Skip to content

Commit e9d3465

Browse files
authored
Add support for setting source trust level (#4216)
1 parent a3bb538 commit e9d3465

File tree

26 files changed

+440
-32
lines changed

26 files changed

+440
-32
lines changed

.github/actions/spelling/expect.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ srs
423423
startswith
424424
STARTUPINFOW
425425
STDMETHODCALLTYPE
426+
storeorigin
426427
STRRET
427428
stylecop
428429
subdir

src/AppInstallerCLICore/Argument.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ namespace AppInstaller::CLI
115115
return { type, "arg"_liv, 'a' };
116116
case Execution::Args::Type::ForceSourceReset:
117117
return { type, "force"_liv };
118+
case Execution::Args::Type::SourceExplicit:
119+
return { type, "explicit"_liv };
120+
case Execution::Args::Type::SourceTrustLevel:
121+
return { type, "trust-level"_liv };
118122

119123
//Hash Command
120124
case Execution::Args::Type::HashFile:
@@ -344,6 +348,10 @@ namespace AppInstaller::CLI
344348
return Argument{ type, Resource::String::SourceArgArgumentDescription, ArgumentType::Positional, true };
345349
case Args::Type::SourceType:
346350
return Argument{ type, Resource::String::SourceTypeArgumentDescription, ArgumentType::Positional };
351+
case Args::Type::SourceExplicit:
352+
return Argument{ type, Resource::String::SourceExplicitArgumentDescription, ArgumentType::Flag };
353+
case Args::Type::SourceTrustLevel:
354+
return Argument{ type, Resource::String::SourceTrustLevelArgumentDescription, ArgumentType::Standard, Argument::Visibility::Help };
347355
case Args::Type::ValidateManifest:
348356
return Argument{ type, Resource::String::ValidateManifestArgumentDescription, ArgumentType::Positional, true };
349357
case Args::Type::IgnoreWarnings:

src/AppInstallerCLICore/Commands/SourceCommand.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,10 @@ namespace AppInstaller::CLI
5252
Argument::ForType(Args::Type::SourceName).SetRequired(true),
5353
Argument::ForType(Args::Type::SourceArg),
5454
Argument::ForType(Args::Type::SourceType),
55+
Argument::ForType(Args::Type::SourceTrustLevel),
5556
Argument::ForType(Args::Type::CustomHeader),
5657
Argument::ForType(Args::Type::AcceptSourceAgreements),
58+
Argument::ForType(Args::Type::SourceExplicit),
5759
};
5860
}
5961

@@ -72,6 +74,29 @@ namespace AppInstaller::CLI
7274
return s_SourceCommand_HelpLink;
7375
}
7476

77+
void SourceAddCommand::ValidateArgumentsInternal(Args& execArgs) const
78+
{
79+
if (execArgs.Contains(Execution::Args::Type::SourceTrustLevel))
80+
{
81+
try
82+
{
83+
std::string trustLevelArg = std::string{ execArgs.GetArg(Execution::Args::Type::SourceTrustLevel) };
84+
85+
for (auto trustLevel : Utility::Split(trustLevelArg, '|', true))
86+
{
87+
Repository::ConvertToSourceTrustLevelEnum(trustLevel);
88+
}
89+
}
90+
catch (...)
91+
{
92+
auto validOptions = std::vector<Utility::LocIndString>{
93+
Utility::LocIndString{ Repository::SourceTrustLevelEnumToString(Repository::SourceTrustLevel::None) },
94+
Utility::LocIndString{ Repository::SourceTrustLevelEnumToString(Repository::SourceTrustLevel::Trusted) } };
95+
throw CommandException(Resource::String::InvalidArgumentValueError(ArgumentCommon::ForType(Execution::Args::Type::SourceTrustLevel).Name, Utility::Join(","_liv, validOptions)));
96+
}
97+
}
98+
}
99+
75100
void SourceAddCommand::ExecuteInternal(Context& context) const
76101
{
77102
// Note: Group Policy for allowed sources is enforced at the RepositoryCore level

src/AppInstallerCLICore/Commands/SourceCommand.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ namespace AppInstaller::CLI
3232
Utility::LocIndView HelpLink() const override;
3333

3434
protected:
35+
void ValidateArgumentsInternal(Execution::Args& execArgs) const override;
3536
void ExecuteInternal(Execution::Context& context) const override;
3637
};
3738

src/AppInstallerCLICore/ExecutionArgs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ namespace AppInstaller::CLI::Execution
6262
SourceType,
6363
SourceArg,
6464
ForceSourceReset,
65+
SourceExplicit,
66+
SourceTrustLevel,
6567

6668
//Hash Command
6769
HashFile,

src/AppInstallerCLICore/Resources.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,8 @@ namespace AppInstaller::CLI::Resource
545545
WINGET_DEFINE_RESOURCE_STRINGID(SourceListName);
546546
WINGET_DEFINE_RESOURCE_STRINGID(SourceListNoneFound);
547547
WINGET_DEFINE_RESOURCE_STRINGID(SourceListNoSources);
548+
WINGET_DEFINE_RESOURCE_STRINGID(SourceListExplicit);
549+
WINGET_DEFINE_RESOURCE_STRINGID(SourceListTrustLevel);
548550
WINGET_DEFINE_RESOURCE_STRINGID(SourceListType);
549551
WINGET_DEFINE_RESOURCE_STRINGID(SourceListUpdated);
550552
WINGET_DEFINE_RESOURCE_STRINGID(SourceListUpdatedNever);
@@ -558,12 +560,14 @@ namespace AppInstaller::CLI::Resource
558560
WINGET_DEFINE_RESOURCE_STRINGID(SourceRemoveCommandShortDescription);
559561
WINGET_DEFINE_RESOURCE_STRINGID(SourceRemoveOne);
560562
WINGET_DEFINE_RESOURCE_STRINGID(SourceRequiresAuthentication);
563+
WINGET_DEFINE_RESOURCE_STRINGID(SourceExplicitArgumentDescription);
561564
WINGET_DEFINE_RESOURCE_STRINGID(SourceResetAll);
562565
WINGET_DEFINE_RESOURCE_STRINGID(SourceResetCommandLongDescription);
563566
WINGET_DEFINE_RESOURCE_STRINGID(SourceResetCommandShortDescription);
564567
WINGET_DEFINE_RESOURCE_STRINGID(SourceResetForceArgumentDescription);
565568
WINGET_DEFINE_RESOURCE_STRINGID(SourceResetListAndOverridePreamble);
566569
WINGET_DEFINE_RESOURCE_STRINGID(SourceResetOne);
570+
WINGET_DEFINE_RESOURCE_STRINGID(SourceTrustLevelArgumentDescription);
567571
WINGET_DEFINE_RESOURCE_STRINGID(SourceTypeArgumentDescription);
568572
WINGET_DEFINE_RESOURCE_STRINGID(SourceUpdateAll);
569573
WINGET_DEFINE_RESOURCE_STRINGID(SourceUpdateCommandLongDescription);

src/AppInstallerCLICore/Workflows/SourceFlow.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,16 @@ namespace AppInstaller::CLI::Workflow
107107
std::string_view name = context.Args.GetArg(Args::Type::SourceName);
108108
std::string_view arg = context.Args.GetArg(Args::Type::SourceArg);
109109
std::string_view type = context.Args.GetArg(Args::Type::SourceType);
110+
bool isExplicit = context.Args.Contains(Args::Type::SourceExplicit);
110111

111-
Repository::Source sourceToAdd{ name, arg, type };
112+
Repository::SourceTrustLevel trustLevel = Repository::SourceTrustLevel::None;
113+
if (context.Args.Contains(Execution::Args::Type::SourceTrustLevel))
114+
{
115+
std::vector<std::string> trustLevelArgs = Utility::Split(std::string{ context.Args.GetArg(Execution::Args::Type::SourceTrustLevel) }, '|', true);
116+
trustLevel = Repository::ConvertToSourceTrustLevelFlag(trustLevelArgs);
117+
}
118+
119+
Repository::Source sourceToAdd{ name, arg, type, trustLevel, isExplicit};
112120

113121
if (context.Args.Contains(Execution::Args::Type::CustomHeader))
114122
{
@@ -156,6 +164,8 @@ namespace AppInstaller::CLI::Workflow
156164
table.OutputLine({ Resource::LocString(Resource::String::SourceListArg), source.Arg });
157165
table.OutputLine({ Resource::LocString(Resource::String::SourceListData), source.Data });
158166
table.OutputLine({ Resource::LocString(Resource::String::SourceListIdentifier), source.Identifier });
167+
table.OutputLine({ Resource::LocString(Resource::String::SourceListTrustLevel), Repository::GetSourceTrustLevelForDisplay(source.TrustLevel)});
168+
table.OutputLine({ Resource::LocString(Resource::String::SourceListExplicit), std::string{ Utility::ConvertBoolToString(source.Explicit) }});
159169

160170
if (source.LastUpdateTime == Utility::ConvertUnixEpochToSystemClock(0))
161171
{
@@ -181,10 +191,10 @@ namespace AppInstaller::CLI::Workflow
181191
}
182192
else
183193
{
184-
Execution::TableOutput<2> table(context.Reporter, { Resource::String::SourceListName, Resource::String::SourceListArg });
194+
Execution::TableOutput<3> table(context.Reporter, { Resource::String::SourceListName, Resource::String::SourceListArg, Resource::String::SourceListExplicit });
185195
for (const auto& source : sources)
186196
{
187-
table.OutputLine({ source.Name, source.Arg });
197+
table.OutputLine({ source.Name, source.Arg, std::string{ Utility::ConvertBoolToString(source.Explicit) }});
188198
}
189199
table.Complete();
190200
}
@@ -199,6 +209,7 @@ namespace AppInstaller::CLI::Workflow
199209
}
200210

201211
const std::vector<Repository::SourceDetails>& sources = context.Get<Data::SourceList>();
212+
202213
for (const auto& sd : sources)
203214
{
204215
Repository::Source source{ sd.Name };
@@ -303,6 +314,10 @@ namespace AppInstaller::CLI::Workflow
303314
s.Arg = source.Arg;
304315
s.Data = source.Data;
305316
s.Identifier = source.Identifier;
317+
318+
std::vector<std::string_view> sourceTrustLevels = Repository::SourceTrustLevelFlagToList(source.TrustLevel);
319+
s.TrustLevel = std::vector<std::string>(sourceTrustLevels.begin(), sourceTrustLevels.end());
320+
s.Explicit = source.Explicit;
306321
context.Reporter.Info() << s.ToJsonString() << std::endl;
307322
}
308323
}

src/AppInstallerCLIE2ETests/Constants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// -----------------------------------------------------------------------------
1+
// -----------------------------------------------------------------------------
22
// <copyright file="Constants.cs" company="Microsoft Corporation">
33
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
44
// </copyright>

src/AppInstallerCLIE2ETests/GroupPolicy.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// -----------------------------------------------------------------------------
1+
// -----------------------------------------------------------------------------
22
// <copyright file="GroupPolicy.cs" company="Microsoft Corporation">
33
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
44
// </copyright>
@@ -180,6 +180,32 @@ public void EnableAdditionalSources()
180180
Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode);
181181
}
182182

183+
/// <summary>
184+
/// Test additional sources with trust levels and explicit are enabled by policy.
185+
/// </summary>
186+
[Test]
187+
public void EnableAdditionalSources_TrustLevel_Explicit()
188+
{
189+
// Remove the test source, then add it with policy.
190+
TestCommon.RunAICLICommand("source remove", "TestSource");
191+
var result = TestCommon.RunAICLICommand("source list", "TestSource");
192+
Assert.AreEqual(Constants.ErrorCode.ERROR_SOURCE_NAME_DOES_NOT_EXIST, result.ExitCode);
193+
194+
GroupPolicyHelper.EnableAdditionalSources.SetEnabledList(new string[]
195+
{
196+
"{\"Arg\":\"https://localhost:5001/TestKit\",\"Data\":\"WingetE2E.Tests_8wekyb3d8bbwe\",\"Identifier\":\"WingetE2E.Tests_8wekyb3d8bbwe\",\"Name\":\"TestSource\",\"Type\":\"Microsoft.PreIndexed.Package\",\"TrustLevel\":[\"Trusted\"],\"Explicit\":true}",
197+
});
198+
199+
result = TestCommon.RunAICLICommand("source list", "TestSource");
200+
Assert.AreEqual(Constants.ErrorCode.S_OK, result.ExitCode);
201+
Assert.True(result.StdOut.Contains("Trust Level"));
202+
Assert.True(result.StdOut.Contains("Trusted"));
203+
204+
var searchResult = TestCommon.RunAICLICommand("search", "TestExampleInstaller");
205+
Assert.AreEqual(Constants.ErrorCode.ERROR_NO_SOURCES_DEFINED, searchResult.ExitCode);
206+
Assert.True(searchResult.StdOut.Contains("No sources defined; add one with 'source add' or reset to defaults with 'source reset'"));
207+
}
208+
183209
/// <summary>
184210
/// Test enable allowed sources.
185211
/// </summary>

src/AppInstallerCLIE2ETests/GroupPolicyHelper.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// -----------------------------------------------------------------------------
1+
// -----------------------------------------------------------------------------
22
// <copyright file="GroupPolicyHelper.cs" company="Microsoft Corporation">
33
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
44
// </copyright>
@@ -385,6 +385,16 @@ public class GroupPolicySource
385385
/// Gets or sets certificate pinning.
386386
/// </summary>
387387
public GroupPolicyCertificatePinning CertificatePinning { get; set; }
388+
389+
/// <summary>
390+
/// Gets or sets the source trust levels.
391+
/// </summary>
392+
public string[] TrustLevel { get; set; }
393+
394+
/// <summary>
395+
/// Gets or sets a value indicating whether the source is explicit.
396+
/// </summary>
397+
public bool Explicit { get; set; }
388398
}
389399

390400
/// <summary>

0 commit comments

Comments
 (0)