Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0-windows10.0.26100.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Platforms>x64;x86;arm64</Platforms>
<!-- Override these to match the platform/configuration you built the solution with. -->
<WinGetBuildPlatform Condition="'$(WinGetBuildPlatform)' == ''">x64</WinGetBuildPlatform>
<WinGetBuildConfiguration Condition="'$(WinGetBuildConfiguration)' == ''">Debug</WinGetBuildConfiguration>
<!-- Platform-specific build output root. -->
<WinGetSrcBinRoot>$([MSBuild]::NormalizePath('$(MSBuildThisFileDirectory)..\..\src\$(WinGetBuildPlatform)\$(WinGetBuildConfiguration)'))</WinGetSrcBinRoot>
</PropertyGroup>

<!--
Generates a CsWinRT projection from the built winmd so this sample needs no published NuGet package.
Build the solution first (src\AppInstallerCLI.sln), then pass a catalog name as the argument.
Override the defaults if needed: -p:WinGetBuildPlatform=x64 -p:WinGetBuildConfiguration=Release
-->
<ItemGroup>
<PackageReference Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
</ItemGroup>

<PropertyGroup>
<CsWinRTWindowsMetadata>10.0.26100.0</CsWinRTWindowsMetadata>
<CsWinRTIncludes>Microsoft.Management.Deployment</CsWinRTIncludes>
</PropertyGroup>

<ItemGroup>
<CsWinRTInputs Include="$(WinGetSrcBinRoot)\Microsoft.Management.Deployment\Microsoft.Management.Deployment.winmd" />
</ItemGroup>

<!-- Copy the native in-proc COM DLL next to the sample so CsWinRT can activate it. -->
<ItemGroup>
<Content Include="$(WinGetSrcBinRoot)\Microsoft.Management.Deployment.InProc\Microsoft.Management.Deployment.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>Microsoft.Management.Deployment.dll</Link>
</Content>
<Content Include="$(WinGetSrcBinRoot)\WindowsPackageManager\WindowsPackageManager.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>WindowsPackageManager.dll</Link>
</Content>
<Content Include="$(WinGetSrcBinRoot)\WindowsPackageManager\WindowsPackageManager.pdb">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>WindowsPackageManager.pdb</Link>
</Content>
</ItemGroup>

</Project>
116 changes: 116 additions & 0 deletions samples/ConnectionValidationSample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// Sample demonstrating the ConnectionValidationHandler on PackageCatalogReference.
//
// This sample retrieves a named package catalog, installs a connection validation
// callback that shows certificate information, prompts the user to accept or reject
// the certificate, then reports the Connect() result.
//
// Usage: ConnectionValidationSample <CatalogName>
// Example: ConnectionValidationSample winget
//
// Requirements:
// - Must run in-process (the ConnectionValidationHandler setter rejects out-of-proc callers).
// - The Microsoft.Management.Deployment COM server must be registered (deploy the dev package
// or install WinGet from the Microsoft Store).

using Microsoft.Management.Deployment;
using Windows.Security.Cryptography.Certificates;

if (args.Length == 0)
{
Console.Error.WriteLine("Usage: ConnectionValidationSample <CatalogName>");
Console.Error.WriteLine("Example: ConnectionValidationSample winget");
return 1;
}

string catalogName = args[0];

// Use in-proc activation so that ConnectionValidationHandler can be set.
// CsWinRT activates PackageManager in-proc via DllGetActivationFactory from
// Microsoft.Management.Deployment.dll placed alongside this executable.
var packageManager = new PackageManager();

var catalogRef = packageManager.GetPackageCatalogByName(catalogName);
if (catalogRef is null)
{
Console.Error.WriteLine($"No catalog named '{catalogName}' found.");
Console.Error.WriteLine("Use 'winget source list' to see available catalogs.");
return 1;
}

Console.WriteLine($"Catalog: {catalogRef.Info.Name} ({catalogRef.Info.Argument})");
Console.WriteLine("Setting connection validation handler...");

catalogRef.ConnectionValidationHandler = (PackageCatalogConnectionValidationEventArgs validationArgs) =>
{
Certificate cert = validationArgs.ServerCertificate;

Console.WriteLine();
Console.WriteLine("Catalog connection validation for: " + catalogRef.Info.Name);
Console.WriteLine(" at: " + catalogRef.Info.Argument);
Console.WriteLine();
Console.WriteLine("=== Server Certificate ===");
Console.WriteLine($" Subject: {cert.Subject}");
Console.WriteLine($" Issuer: {cert.Issuer}");
Console.WriteLine($" Valid from: {cert.ValidFrom}");
Console.WriteLine($" Valid to: {cert.ValidTo}");
Console.WriteLine($" Serial: {cert.SerialNumber}");
Console.WriteLine("==========================");
Console.WriteLine();
Console.Write("Accept this certificate? [Y/N]: ");

while (true)
{
string? input = Console.ReadLine()?.Trim().ToUpperInvariant();
if (input == "Y")
{
Console.WriteLine("Certificate accepted.");
return PackageCatalogConnectionValidationResult.Ok;
}
else if (input == "N")
{
Console.WriteLine("Certificate rejected.");
return PackageCatalogConnectionValidationResult.CertificateRejected;
}
else
{
Console.Write("Please enter Y or N: ");
}
}
};

Console.WriteLine("Connecting...");
ConnectResult result = catalogRef.Connect();

Console.WriteLine();
Console.WriteLine($"Connect result: {result.Status}");

if (result.Status == ConnectResultStatus.Ok)
{
Console.WriteLine($"Successfully connected to '{catalogName}'.");

// Run a simple search to force a live network call, since Connect() may serve a cached response.
Console.WriteLine("Searching to verify live connection...");
var findOptions = new FindPackagesOptions();
findOptions.Selectors.Add(new PackageMatchFilter
{
Field = PackageMatchField.Id,
Option = PackageFieldMatchOption.StartsWithCaseInsensitive,
Value = "Microsoft.",
});
var searchResult = result.PackageCatalog.FindPackages(findOptions);
Console.WriteLine($"Search result: {searchResult.Status} ({searchResult.Matches.Count} match(es))");

return 0;
}
else
{
Console.Error.WriteLine($"Failed to connect to '{catalogName}'.");
if (result.ExtendedErrorCode is not null)
{
Console.Error.WriteLine($" Error code: 0x{result.ExtendedErrorCode.HResult:X8}");
}
return 1;
}
12 changes: 6 additions & 6 deletions src/AppInstallerCLICore/Commands/RootCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,20 +102,20 @@ namespace AppInstaller::CLI
if (groupPolicies.GetState(Settings::TogglePolicy::Policy::AdditionalSources) == Settings::PolicyState::Enabled)
{
info << std::endl;
auto sources = groupPolicies.GetValueRef<Settings::ValuePolicy::AdditionalSources>();
if (sources.has_value() && !sources->get().empty())
auto sources = groupPolicies.GetValue<Settings::ValuePolicy::AdditionalSources>();
if (sources.has_value() && !sources->empty())
{
OutputGroupPolicySourceList(context, sources->get(), Resource::String::SourceListAdditionalSource);
OutputGroupPolicySourceList(context, sources.value(), Resource::String::SourceListAdditionalSource);
}
}

if (groupPolicies.GetState(Settings::TogglePolicy::Policy::AllowedSources) == Settings::PolicyState::Enabled)
{
info << std::endl;
auto sources = groupPolicies.GetValueRef<Settings::ValuePolicy::AllowedSources>();
if (sources.has_value() && !sources->get().empty())
auto sources = groupPolicies.GetValue<Settings::ValuePolicy::AllowedSources>();
if (sources.has_value() && !sources->empty())
{
OutputGroupPolicySourceList(context, sources->get(), Resource::String::SourceListAllowedSource);
OutputGroupPolicySourceList(context, sources.value(), Resource::String::SourceListAllowedSource);
}
}
info << std::endl;
Expand Down
3 changes: 3 additions & 0 deletions src/AppInstallerCLIE2ETests/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ public class Constants
public const string TestSourceUrl = @"https://localhost:5001/TestKit";
public const string TestSourceType = "Microsoft.PreIndexed.Package";
public const string TestSourceIdentifier = @"WingetE2E.Tests_8wekyb3d8bbwe";
public const string RestTestSourceName = @"TestRestSource";
public const string RestTestSourceUrl = @"https://localhost:5001/TestKit/TestData/TestRestSource";
public const string RestTestSourceType = "Microsoft.Rest";

public const string AICLIPackageFamilyName = "WinGetDevCLI_8wekyb3d8bbwe";
public const string AICLIPackageName = "WinGetDevCLI";
Expand Down
31 changes: 31 additions & 0 deletions src/AppInstallerCLIE2ETests/GroupPolicyHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ namespace AppInstallerCLIE2ETests
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Xml.Linq;
using AppInstallerCLIE2ETests.Helpers;
using Microsoft.Win32;
Expand Down Expand Up @@ -133,6 +134,11 @@ private GroupPolicyHelper(string name, string elementId)
/// </summary>
public static GroupPolicyHelper EnableConfigurationProcessorPath { get; private set; } = new GroupPolicyHelper("EnableWindowsPackageManagerConfigurationProcessorPath");

/// <summary>
/// Gets the Bypass certificate pinning for Microsoft Store policy.
/// </summary>
public static GroupPolicyHelper BypassCertificatePinningForMicrosoftStore { get; private set; } = new GroupPolicyHelper("EnableBypassCertificatePinningForMicrosoftStore");

/// <summary>
/// Gets the Enable auto update interval policy.
/// </summary>
Expand Down Expand Up @@ -231,6 +237,8 @@ public void Enable()
{
key.SetValue(this.ValueName, enabledValue);
}

ReloadGroupPolicyIfAvailable();
}

/// <summary>
Expand All @@ -249,6 +257,8 @@ public void Disable()
{
key.SetValue(this.ValueName, disabledValue);
}

ReloadGroupPolicyIfAvailable();
}

/// <summary>
Expand Down Expand Up @@ -283,6 +293,8 @@ public void SetNotConfigured()
}
}
}

ReloadGroupPolicyIfAvailable();
}

/// <summary>
Expand Down Expand Up @@ -339,6 +351,25 @@ public void SetEnabledList(IEnumerable<GroupPolicySource> values)
this.SetEnabledList(values.Select(source => JsonConvert.SerializeObject(source)));
}

[DllImport("WindowsPackageManager.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int WindowsPackageManagerTestHook_ReloadGroupPolicy();

/// <summary>
/// Calls the in-process test hook to reload the GroupPolicy singleton from the current registry state.
/// Silently ignored if the DLL is not loaded in this process (e.g., out-of-process test scenarios).
/// </summary>
private static void ReloadGroupPolicyIfAvailable()
{
try
{
WindowsPackageManagerTestHook_ReloadGroupPolicy();
}
catch (Exception)
{
// The DLL is not loaded in this process (out-of-process scenario); nothing to do.
}
}

/// <summary>
/// Gets the value from a "decimal" child element.
/// </summary>
Expand Down
Loading
Loading