Skip to content

Commit 29350fa

Browse files
authored
Configuration processor auditing improvements (microsoft#6193)
## Change When a processor path is provided, inform the user about that fact in the output. Ensure that the server agrees on the hash of the processor that is provided in its CLI arguments. When the target processor is an app execution alias, the hash is of the contents of the reparse point data. Otherwise, it is a hash of the contents of the file itself. ``` Custom processor path: Path: C:\Program Files\WindowsApps\Microsoft.DesiredStateConfiguration_3.2.0.0_x64__8wekyb3d8bbwe\dsc.exe Hash: 6ee88bd4c93c4a94539a0af0667ace8ffba48f5b8732930e1421721621ca19de Signed By: CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US ```
1 parent b0bc0d1 commit 29350fa

36 files changed

Lines changed: 1783 additions & 253 deletions

.github/actions/spelling/allow.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ testsettingname
357357
TEXTFORMAT
358358
TEXTINCLUDE
359359
threadpool
360+
TOCTOU
360361
tpl
361362
TRACELOGGING
362363
triaged

.github/actions/spelling/expect.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ apfn
2626
apicontract
2727
apiset
2828
appdata
29+
APPEXECLINK
2930
appinstallertest
3031
applic
3132
appname
@@ -192,6 +193,7 @@ FONTHASH
192193
FORPARSING
193194
foundfr
194195
fsanitize
196+
Fsctl
195197
FULLMUTEX
196198
FULLWIDTH
197199
fundraiser
@@ -531,6 +533,7 @@ SETTINGMAPPING
531533
sev
532534
sfs
533535
sfsclient
536+
SGNR
534537
SHCONTF
535538
shellapi
536539
SHGDN
@@ -639,6 +642,7 @@ vns
639642
vscodeignore
640643
vsconfig
641644
vstest
645+
vswhere
642646
waitable
643647
wal
644648
wcex
@@ -666,6 +670,7 @@ wingetutil
666670
winreg
667671
winrtact
668672
winstring
673+
wintrust
669674
WMI
670675
wmmc
671676
wnd
@@ -681,6 +686,7 @@ wsb
681686
wsl
682687
wsv
683688
wto
689+
WVT
684690
wwinmain
685691
WZDNCRFJ
686692
xcopy

doc/windows/package-manager/winget/returnCodes.md

Lines changed: 239 additions & 235 deletions
Large diffs are not rendered by default.

src/AppInstallerCLI.sln

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
33
# Visual Studio Version 17
4-
VisualStudioVersion = 17.14.36915.13 d17.14
4+
VisualStudioVersion = 17.14.36915.13
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "AppInstallerCLIPackage", "AppInstallerCLIPackage\AppInstallerCLIPackage.wapproj", "{6AA3791A-0713-4548-A357-87A323E7AC3A}"
77
ProjectSection(ProjectDependencies) = postProject
@@ -40,7 +40,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Project", "Project", "{8D53
4040
Get-VcxprojNugetPackageVersions.ps1 = Get-VcxprojNugetPackageVersions.ps1
4141
..\README.md = ..\README.md
4242
..\doc\ReleaseNotes.md = ..\doc\ReleaseNotes.md
43-
..\doc\Settings.md = ..\doc\Settings.md
4443
Update-VcxprojNugetPackageVersions.ps1 = Update-VcxprojNugetPackageVersions.ps1
4544
..\WinGetInProcCom.nuspec = ..\WinGetInProcCom.nuspec
4645
..\WinGetUtil.nuspec = ..\WinGetUtil.nuspec
@@ -242,6 +241,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "copilot", "copilot", "{B978
242241
..\.github\copilot-instructions.md = ..\.github\copilot-instructions.md
243242
EndProjectSection
244243
EndProject
244+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{3FF6C881-2548-486E-8D70-7555A90030F5}"
245+
ProjectSection(SolutionItems) = preProject
246+
..\doc\windows\package-manager\winget\returnCodes.md = ..\doc\windows\package-manager\winget\returnCodes.md
247+
..\doc\Settings.md = ..\doc\Settings.md
248+
EndProjectSection
249+
EndProject
245250
Global
246251
GlobalSection(SolutionConfigurationPlatforms) = preSolution
247252
Debug|ARM64 = Debug|ARM64
@@ -1114,6 +1119,7 @@ Global
11141119
{40D7CA7F-EB86-4345-9641-AD27180C559D} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9}
11151120
{5421394F-5619-4E4B-8923-F3FB30D5EFAD} = {7C218A3E-9BC8-48FF-B91B-BCACD828C0C9}
11161121
{B978E358-D2BE-4FA7-A21A-6661F3744DD7} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7}
1122+
{3FF6C881-2548-486E-8D70-7555A90030F5} = {8D53D749-D51C-46F8-A162-9371AAA6C2E7}
11171123
EndGlobalSection
11181124
GlobalSection(ExtensibilityGlobals) = postSolution
11191125
SolutionGuid = {B6FDB70C-A751-422C-ACD1-E35419495857}

src/AppInstallerCLICore/Commands/DebugCommand.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <winrt/Microsoft.Management.Configuration.SetProcessorFactory.h>
1111
#include "AppInstallerDownloader.h"
1212
#include "Sixel.h"
13+
#include <winget/Certificates.h>
1314

1415
using namespace AppInstaller::CLI::Execution;
1516

@@ -64,6 +65,7 @@ namespace AppInstaller::CLI
6465
std::make_unique<DumpErrorResourceCommand>(FullName()),
6566
std::make_unique<ShowSixelCommand>(FullName()),
6667
std::make_unique<ProgressCommand>(FullName()),
68+
std::make_unique<GetSignerCommand>(FullName()),
6769
std::make_unique<LogViewerTestCommand>(FullName()),
6870
std::make_unique<DebugDscResourceCommand>(FullName()),
6971
});
@@ -370,6 +372,30 @@ namespace AppInstaller::CLI
370372
}
371373
}
372374

375+
std::vector<Argument> GetSignerCommand::GetArguments() const
376+
{
377+
return {
378+
Argument{ "file", 'f', Args::Type::Manifest, Resource::String::SourceListUpdatedNever, ArgumentType::Positional },
379+
};
380+
}
381+
382+
Resource::LocString GetSignerCommand::ShortDescription() const
383+
{
384+
return Utility::LocIndString("Get signer information"sv);
385+
}
386+
387+
Resource::LocString GetSignerCommand::LongDescription() const
388+
{
389+
return Utility::LocIndString("Gets the signing information for a given path."sv);
390+
}
391+
392+
void GetSignerCommand::ExecuteInternal(Execution::Context& context) const
393+
{
394+
std::string subject = Certificates::GetAuthenticodeSubject(context.Args.GetArg(Args::Type::Manifest));
395+
396+
context.Reporter.Info() << "Subject: " << subject << std::endl;
397+
}
398+
373399
// ── LogViewerTestCommand ─────────────────────────────────────────────────────
374400

375401
#define WINGET_DEBUG_LOG_VIEWER_FOLLOW Args::Type::Force

src/AppInstallerCLICore/Commands/DebugCommand.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,20 @@ namespace AppInstaller::CLI
100100
void ExecuteInternal(Execution::Context& context) const override;
101101
};
102102

103+
// Invokes signing information collection for a path.
104+
struct GetSignerCommand final : public Command
105+
{
106+
GetSignerCommand(std::string_view parent) : Command("get-signer", {}, parent) {}
107+
108+
std::vector<Argument> GetArguments() const override;
109+
110+
Resource::LocString ShortDescription() const override;
111+
Resource::LocString LongDescription() const override;
112+
113+
protected:
114+
void ExecuteInternal(Execution::Context& context) const override;
115+
};
116+
103117
// Tests the log viewer extension by emitting logs that exercise all channels, levels, subchannels,
104118
// continuation lines, long lines, and optionally a streaming follow mode.
105119
struct LogViewerTestCommand final : public Command

src/AppInstallerCLICore/Commands/ErrorCommand.cpp

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include "AppInstallerErrors.h"
77
#include "VTSupport.h"
88

9+
#include <filesystem>
10+
911
namespace AppInstaller::CLI
1012
{
1113
namespace
@@ -23,12 +25,123 @@ namespace AppInstaller::CLI
2325
info << std::endl;
2426
info << error.GetDescription() << std::endl;
2527
}
28+
29+
struct ErrorGroup
30+
{
31+
std::string_view Name;
32+
WORD MinCode; // HRESULT_CODE, inclusive
33+
WORD MaxCode; // HRESULT_CODE, inclusive
34+
};
35+
36+
// Groups matching the sections in the published returnCodes.md documentation.
37+
constexpr ErrorGroup s_errorGroups[] =
38+
{
39+
{ "General Errors", 0x0001, 0x00FF },
40+
{ "Install errors.", 0x0100, 0x01FF },
41+
{ "Check for package installed status", 0x0200, 0x02FF },
42+
{ "Configuration Errors", 0xC000, 0xC0FF },
43+
{ "Configuration Processor Errors", 0xC100, 0xC1FF },
44+
};
45+
46+
constexpr std::string_view s_uncategorizedGroupName = "Uncategorized";
47+
48+
// Writes the markdown table for a group of errors to the given stream.
49+
void WriteMarkdownGroup(std::ostream& out, std::string_view groupName, std::vector<const Errors::HResultInformation*>& errors)
50+
{
51+
if (errors.empty())
52+
{
53+
return;
54+
}
55+
56+
std::sort(errors.begin(), errors.end(),
57+
[](const Errors::HResultInformation* a, const Errors::HResultInformation* b) { return HRESULT_CODE(a->Value()) < HRESULT_CODE(b->Value()); });
58+
59+
out << "\n## " << groupName << "\n\n";
60+
out << "| Hex | Decimal | Symbol | Description |\n";
61+
out << "|-------------|-------------|-------------|-------------|\n";
62+
63+
for (const auto* error : errors)
64+
{
65+
HRESULT hr = error->Value();
66+
char hexBuf[11];
67+
sprintf_s(hexBuf, "0x%08X", static_cast<uint32_t>(hr));
68+
69+
out << "| " << hexBuf
70+
<< " | " << static_cast<int32_t>(hr)
71+
<< " | " << error->Symbol()
72+
<< " | " << error->GetDescription()
73+
<< " |\n";
74+
}
75+
}
76+
77+
void OutputErrorMarkdown(Execution::Context& context)
78+
{
79+
auto allErrors = Errors::GetWinGetErrors();
80+
81+
// Categorize each error into its group; the last slot holds uncategorized errors.
82+
static constexpr size_t s_errorGroupsCount = ARRAYSIZE(s_errorGroups);
83+
std::vector<std::vector<const Errors::HResultInformation*>> groupedErrors(s_errorGroupsCount + 1);
84+
85+
for (const auto& error : allErrors)
86+
{
87+
HRESULT hr = error->Value();
88+
WORD code = static_cast<WORD>(HRESULT_CODE(hr));
89+
90+
bool placed = false;
91+
for (size_t i = 0; i < s_errorGroupsCount; ++i)
92+
{
93+
if (code >= s_errorGroups[i].MinCode && code <= s_errorGroups[i].MaxCode)
94+
{
95+
groupedErrors[i].push_back(error.get());
96+
placed = true;
97+
break;
98+
}
99+
}
100+
101+
if (!placed)
102+
{
103+
groupedErrors[s_errorGroupsCount].push_back(error.get());
104+
}
105+
}
106+
107+
std::filesystem::path outputPath{ Utility::ConvertToUTF16(context.Args.GetArg(Execution::Args::Type::OutputFile)) };
108+
std::ofstream out{ outputPath };
109+
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_OPEN_FAILED), !out);
110+
111+
std::time_t now = std::time(nullptr);
112+
std::tm tm{};
113+
localtime_s(&tm, &now);
114+
char dateBuf[11];
115+
strftime(dateBuf, sizeof(dateBuf), "%m/%d/%Y", &tm);
116+
117+
out <<
118+
"---\n"
119+
"title: WinGet HRESULT Codes\n"
120+
"description: WinGet return codes and their meanings\n"
121+
"ms.date: " << dateBuf << "\n"
122+
"ms.topic: article\n"
123+
"ms.localizationpriority: low\n"
124+
"---\n"
125+
"\n"
126+
"> [!NOTE]\n"
127+
"> This file is generated by running: winget error --output <file>\n"
128+
"\n"
129+
"# Return Codes\n";
130+
131+
for (size_t i = 0; i < ARRAYSIZE(s_errorGroups); ++i)
132+
{
133+
WriteMarkdownGroup(out, s_errorGroups[i].Name, groupedErrors[i]);
134+
}
135+
136+
WriteMarkdownGroup(out, s_uncategorizedGroupName, groupedErrors[s_errorGroupsCount]);
137+
}
26138
}
27139

28140
std::vector<Argument> ErrorCommand::GetArguments() const
29141
{
30142
return {
31-
Argument{ Execution::Args::Type::ErrorInput, Resource::String::ErrorInputArgumentDescription, ArgumentType::Positional, true },
143+
Argument{ Execution::Args::Type::ErrorInput, Resource::String::ErrorInputArgumentDescription, ArgumentType::Positional },
144+
Argument{ Execution::Args::Type::OutputFile, Resource::String::ErrorOutputFileArgumentDescription, ArgumentType::Standard },
32145
};
33146
}
34147

@@ -42,8 +155,27 @@ namespace AppInstaller::CLI
42155
return { Resource::String::ErrorCommandLongDescription };
43156
}
44157

158+
void ErrorCommand::ValidateArgumentsInternal(Execution::Args& execArgs) const
159+
{
160+
if (execArgs.Contains(Execution::Args::Type::OutputFile) && execArgs.Contains(Execution::Args::Type::ErrorInput))
161+
{
162+
throw CommandException(Resource::String::ErrorOutputFileConflictsWithInput);
163+
}
164+
165+
if (!execArgs.Contains(Execution::Args::Type::OutputFile) && !execArgs.Contains(Execution::Args::Type::ErrorInput))
166+
{
167+
throw CommandException(Resource::String::ErrorRequiresInputOrOutputFile);
168+
}
169+
}
170+
45171
void ErrorCommand::ExecuteInternal(Execution::Context& context) const
46172
{
173+
if (context.Args.Contains(Execution::Args::Type::OutputFile))
174+
{
175+
OutputErrorMarkdown(context);
176+
return;
177+
}
178+
47179
std::string input{ context.Args.GetArg(Execution::Args::Type::ErrorInput) };
48180

49181
const char* begin = input.c_str();

src/AppInstallerCLICore/Commands/ErrorCommand.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace AppInstaller::CLI
1616
Resource::LocString LongDescription() const override;
1717

1818
protected:
19+
void ValidateArgumentsInternal(Execution::Args& execArgs) const override;
1920
void ExecuteInternal(Execution::Context& context) const override;
2021
};
2122
}

src/AppInstallerCLICore/ConfigurationDynamicRuntimeFactory.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,10 +341,12 @@ namespace AppInstaller::CLI::ConfigurationRemoting
341341
{
342342
winrt::hstring dscExecutablePathPropertyName = ToHString(PropertyName::DscExecutablePath);
343343
std::optional<winrt::hstring> dscExecutablePath = m_dynamicFactory->GetFactoryMapValue(dscExecutablePathPropertyName);
344+
bool usingFoundPath = false;
344345

345346
if (!dscExecutablePath)
346347
{
347348
dscExecutablePath = m_dynamicFactory->Lookup(ToHString(PropertyName::FoundDscExecutablePath));
349+
usingFoundPath = true;
348350
}
349351

350352
if (dscExecutablePath->empty())
@@ -355,6 +357,37 @@ namespace AppInstaller::CLI::ConfigurationRemoting
355357
}
356358

357359
json["processorPath"] = Utility::ConvertToUTF8(dscExecutablePath.value());
360+
361+
if (usingFoundPath)
362+
{
363+
// FoundDscExecutablePathHash/IsAlias are computed on the remote side alongside FoundDscExecutablePath.
364+
winrt::hstring pathHash = m_dynamicFactory->Lookup(ToHString(PropertyName::FoundDscExecutablePathHash));
365+
if (!pathHash.empty())
366+
{
367+
json["processorPathHash"] = Utility::ConvertToUTF8(pathHash);
368+
}
369+
370+
winrt::hstring pathIsAlias = m_dynamicFactory->Lookup(ToHString(PropertyName::FoundDscExecutablePathIsAlias));
371+
if (!pathIsAlias.empty())
372+
{
373+
json["processorPathIsAlias"] = (pathIsAlias == L"true");
374+
}
375+
}
376+
else
377+
{
378+
// DscExecutablePathHash/IsAlias are set locally via the audit block.
379+
auto pathHash = m_dynamicFactory->GetFactoryMapValue(ToHString(PropertyName::DscExecutablePathHash));
380+
if (pathHash)
381+
{
382+
json["processorPathHash"] = Utility::ConvertToUTF8(pathHash.value());
383+
}
384+
385+
auto pathIsAlias = m_dynamicFactory->GetFactoryMapValue(ToHString(PropertyName::DscExecutablePathIsAlias));
386+
if (pathIsAlias)
387+
{
388+
json["processorPathIsAlias"] = (pathIsAlias.value() == L"true");
389+
}
390+
}
358391
}
359392

360393
Json::StreamWriterBuilder writerBuilder;

src/AppInstallerCLICore/ConfigurationSetProcessorFactoryRemoting.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,8 +381,11 @@ namespace AppInstaller::CLI::ConfigurationRemoting
381381
case PropertyName::FoundDscExecutablePath: return L"FoundDscExecutablePath";
382382
case PropertyName::DiagnosticTraceEnabled: return L"DiagnosticTraceEnabled";
383383
case PropertyName::FindDscStateMachine: return L"FindDscStateMachine";
384+
case PropertyName::DscExecutablePathHash: return L"DscExecutablePathHash";
385+
case PropertyName::DscExecutablePathIsAlias: return L"DscExecutablePathIsAlias";
386+
case PropertyName::FoundDscExecutablePathHash: return L"FoundDscExecutablePathHash";
387+
case PropertyName::FoundDscExecutablePathIsAlias: return L"FoundDscExecutablePathIsAlias";
384388
}
385-
386389
THROW_HR(E_UNEXPECTED);
387390
}
388391
}

0 commit comments

Comments
 (0)