Skip to content
1 change: 1 addition & 0 deletions doc/ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ The PowerShell module now automatically uses `GH_TOKEN` or `GITHUB_TOKEN` enviro

* `SignFile` in `WinGetSourceCreator` now supports an optional RFC 3161 timestamp server via the new `TimestampServer` property on the `Signature` model. When set, `signtool.exe` is called with `/tr <url> /td sha256`, embedding a countersignature timestamp so that signed packages remain valid after the signing certificate expires.
* File and directory paths passed to `signtool.exe` and `makeappx.exe` are now quoted, fixing failures when paths contain spaces.
* DSC export now correctly exports WinGet Admin Settings
37 changes: 37 additions & 0 deletions src/AppInstallerCLITests/AdminSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "TestCommon.h"
#include "TestSettings.h"
#include <winget/AdminSettings.h>
#include <AppInstallerRuntime.h>
#include <fstream>

using namespace AppInstaller::Settings;
using namespace TestCommon;
Expand Down Expand Up @@ -81,4 +83,39 @@ TEST_CASE("AdminSetting_AllSettingsAreImplemented", "[adminSettings]")
REQUIRE(DisableAdminSetting(adminSetting));
REQUIRE_FALSE(IsAdminSettingEnabled(adminSetting));
}
}

TEST_CASE("AdminSetting_CorruptedVerificationFile", "[adminSettings]")
{
GroupPolicyTestOverride policies;
policies.SetState(TogglePolicy::Policy::LocalManifestFiles, PolicyState::NotConfigured);

// Clean up any existing admin settings
ResetAllAdminSettings();

// Enable the setting to create both the data and verification files
REQUIRE(EnableAdminSetting(BoolAdminSetting::LocalManifestFiles));
REQUIRE(IsAdminSettingEnabled(BoolAdminSetting::LocalManifestFiles));

// Get the path to the verification file.
// Note: The data file may be stored in ApplicationData (packaged context) rather than the filesystem,
// so we only verify the verification file, which is always on the filesystem.
std::filesystem::path secureSettingsDir = AppInstaller::Runtime::GetPathTo(AppInstaller::Runtime::PathName::SecureSettingsForRead);
std::filesystem::path verificationFilePath = secureSettingsDir / Stream::AdminSettings.Name;

// Verify the verification file exists
REQUIRE(std::filesystem::exists(verificationFilePath));

// Corrupt the verification file by writing invalid content to it
{
std::ofstream corruptFile(verificationFilePath);
corruptFile << "corrupted data that is not valid YAML or a valid hash";
}

// Now when we try to read the setting, it should fall back to the default value (false)
// instead of throwing an exception
REQUIRE_FALSE(IsAdminSettingEnabled(BoolAdminSetting::LocalManifestFiles));

// Clean up
ResetAllAdminSettings();
}
19 changes: 18 additions & 1 deletion src/AppInstallerCommonCore/AdminSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,22 @@ namespace AppInstaller::Settings

void AdminSettingsInternal::LoadAdminSettings()
{
auto stream = m_settingStream.Get();
std::unique_ptr<std::istream> stream;
try
{
stream = m_settingStream.Get();
}
catch (const std::exception& e)
{
AICLI_LOG(Core, Error, << "Failed to read admin settings: " << e.what() << ". Falling back to default values.");
return;
}
catch (...)
{
AICLI_LOG(Core, Error, << "Failed to read admin settings due to unknown exception. Falling back to default values.");
return;
}

if (!stream)
{
AICLI_LOG(Core, Verbose, << "Admin settings was not found");
Expand Down Expand Up @@ -223,6 +238,8 @@ namespace AppInstaller::Settings
{
m_settingValues.DefaultProxy.emplace(std::move(defaultProxy));
}

AICLI_LOG(Core, Verbose, << "Admin settings loaded successfully");
}

bool AdminSettingsInternal::SaveAdminSettings()
Expand Down
19 changes: 16 additions & 3 deletions src/AppInstallerCommonCore/Settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,14 +293,19 @@ namespace AppInstaller::Settings
return result;
}

void SetVerificationData(VerificationData data)
bool SetVerificationData(VerificationData data)
{
YAML::Emitter out;
out << YAML::BeginMap;
out << YAML::Key << NodeName_Sha256 << YAML::Value << SHA256::ConvertToString(data.Hash);
out << YAML::EndMap;

m_secure.Set(out.str());
bool result = m_secure.Set(out.str());
if (!result)
{
AICLI_LOG(Core, Warning, << "Failed to write verification data for '" << m_name << "'");
}
return result;
}

public:
Expand Down Expand Up @@ -338,7 +343,15 @@ namespace AppInstaller::Settings
VerificationData verData;
verData.Hash = m_hash.value();

SetVerificationData(verData);
if (!SetVerificationData(verData))
{
AICLI_LOG(Core, Error, << "Failed to write verification data for '" << m_name << "'. Reverting setting write to maintain consistency.");
AICLI_LOG(Core, Warning, << "This failure may be caused by insufficient permissions or disk space issues");
// Verification data write failed, so we need to revert the main write
// to maintain consistency
Remove();
return false;
}
}

return exchangeResult;
Expand Down
Loading