Skip to content
Merged
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
38 changes: 36 additions & 2 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,23 @@ class Program

// Version in YYYY.MM.DD.HHMM format - injected at build time via MSBuild
private static readonly string Version = GetBuildVersion();

// Host of the manifest URL actually used this run. The policy
// AuthorizationHeader exists to authenticate against the manifest
// server; it must be scoped to that host only. Sending it cross-host
// both leaks the credential to whatever hosts package URLs point at
// and breaks Azure blob storage, which returns 403 for public blobs
// when a request carries an Authorization header it can't validate
// (the fleet-visible symptom: every package "Download failed:
// Forbidden" on devices that receive the header via CSP).
private static string? _activeManifestHost;

private static bool ShouldAttachAuthHeader(string requestUrl)
{
return _activeManifestHost != null
&& Uri.TryCreate(requestUrl, UriKind.Absolute, out var uri)
&& string.Equals(uri.Host, _activeManifestHost, StringComparison.OrdinalIgnoreCase);
}

private static string GetBuildVersion()
{
Expand Down Expand Up @@ -679,7 +696,14 @@ private static int SaveSettingsFromArgs(string[] args)
Logger.Debug($"Initialized status tracking with RunId: {StatusManager.GetCurrentRunId()}");

Logger.Info($"Downloading manifest from: {manifestUrl}");


// Record the manifest host so the policy Authorization header can be
// scoped to it - packages hosted elsewhere (Azure blob) must never
// receive it (see ShouldAttachAuthHeader).
_activeManifestHost = Uri.TryCreate(manifestUrl, UriKind.Absolute, out var manifestUri)
? manifestUri.Host
: null;

using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("User-Agent", $"BootstrapMate/{Version}");
var authHeader = ConfigManager.Instance.Config.AuthorizationHeader;
Expand Down Expand Up @@ -1019,9 +1043,19 @@ static async Task DownloadAndInstallPackage(string displayName, string url, stri
DialogManager.Instance.NotifyDownloadStarted(displayName);

using var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("User-Agent", $"BootstrapMate/{Version}");
var authHeader = ConfigManager.Instance.Config.AuthorizationHeader;
if (!string.IsNullOrEmpty(authHeader))
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", authHeader);
{
// Scope the policy credential to the manifest host. Azure blob
// storage 403s public-blob requests carrying a foreign
// Authorization header, and third-party hosts must not see the
// org's token at all.
if (ShouldAttachAuthHeader(url))
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", authHeader);
else
Logger.Debug($"Authorization header withheld for cross-host download: {url}");
}
using var response = await httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
{
Expand Down
Loading