lostindark/DriverStoreExplorer · error · InvalidOperationException

Download URL is not from a trusted GitHub domain.

Error message

Download URL is not from a trusted GitHub domain.

What it means

A security pre-check inside UpdateManager.ApplyUpdateAsync. Before any network I/O, IsGitHubUrl verifies the DownloadUrl scheme is HTTPS and the host is github.com, *.github.com, or *.githubusercontent.com. This blocks SSRF, supply-chain swaps, and accidental fetches from attacker-controlled mirrors. The check fails closed: any release whose browser_download_url leaves the GitHub domain set aborts the whole update.

Source

Thrown at Rapr/UpdateManager.cs:80

                        DownloadUrl = new Uri(downloadUrl),
                        Sha256 = sha256
                    };
                }

                return null;
            }
        }

        public async Task ApplyUpdateAsync(VersionInfo versionInfo, IProgress<float> progress)
        {
            if (versionInfo == null)
            {
                throw new ArgumentNullException(nameof(versionInfo));
            }

            if (!IsGitHubUrl(versionInfo.DownloadUrl))
            {
                throw new InvalidOperationException("Download URL is not from a trusted GitHub domain.");
            }

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            string tempBaseDir = Path.Combine(Path.GetTempPath(), "DriverStoreExplorer");
            string downloadFileName = Path.GetFileName(versionInfo.DownloadUrl.LocalPath);
            string tempZipPath = Path.Combine(tempBaseDir, downloadFileName);
            string tempExtractPath = Path.Combine(tempBaseDir, "Update");

            if (!Directory.Exists(tempBaseDir))
            {
                Directory.CreateDirectory(tempBaseDir);
            }

            // Clean up any previous update artifacts
            if (File.Exists(tempZipPath))
            {
                File.Delete(tempZipPath);

View on GitHub (pinned to 958fcd481b)

Solutions

  1. Keep release assets on GitHub so browser_download_url stays on objects.githubusercontent.com — the supported, default path.
  2. If you must allow an additional trusted host, extend IsGitHubUrl's host whitelist rather than disabling the check.
  3. Inspect the release JSON: curl -sSL https://api.github.com/repos/<owner>/<repo>/releases/latest | jq '.assets[].browser_download_url'.
  4. For GitHub Enterprise, add the enterprise host (e.g. github.<company>.com) to the host check.
  5. In tests, build VersionInfo with a Uri("https://github.com/owner/repo/releases/download/v1/x.zip").

Example fix

// before
private static bool IsGitHubUrl(Uri url)
{
    return url.Scheme == Uri.UriSchemeHttps
        && (url.Host.Equals("github.com", StringComparison.OrdinalIgnoreCase)
            || url.Host.EndsWith(".github.com", StringComparison.OrdinalIgnoreCase)
            || url.Host.EndsWith(".githubusercontent.com", StringComparison.OrdinalIgnoreCase));
}

// after — allow a configurable allowlist for GitHub Enterprise / mirrors
private static readonly string[] TrustedHostSuffixes =
{
    "github.com",
    "githubusercontent.com",
    "github.mycompany.com",
};

private static bool IsTrustedUrl(Uri url)
{
    if (url == null || url.Scheme != Uri.UriSchemeHttps) return false;
    foreach (var suffix in TrustedHostSuffixes)
    {
        if (url.Host.Equals(suffix, StringComparison.OrdinalIgnoreCase)
            || url.Host.EndsWith("." + suffix, StringComparison.OrdinalIgnoreCase))
            return true;
    }
    return false;
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate before invoking ApplyUpdateAsync
private static bool TryGetSafeDownloadUrl(VersionInfo info, out Uri url, out string error)
{
    url = null;
    error = null;
    if (info?.DownloadUrl == null) { error = "No download URL present."; return false; }
    if (info.DownloadUrl.Scheme != Uri.UriSchemeHttps) { error = "Download URL must use HTTPS."; return false; }

    string host = info.DownloadUrl.Host;
    bool trusted = host.Equals("github.com", StringComparison.OrdinalIgnoreCase)
        || host.EndsWith(".github.com", StringComparison.OrdinalIgnoreCase)
        || host.EndsWith(".githubusercontent.com", StringComparison.OrdinalIgnoreCase);
    if (!trusted) { error = "Download URL host is not a trusted GitHub domain: " + host; return false; }

    url = info.DownloadUrl;
    return true;
}

// Usage:
if (!TryGetSafeDownloadUrl(this.latestVersionInfo, out var url, out var err))
{
    MessageBox.Show(err, Language.Product_Name, MessageBoxButtons.OK, MessageBoxIcon.Warning);
    return;
}

Type guard

private static bool IsTrustedGitHubReleaseUrl(VersionInfo info)
    => info?.DownloadUrl is Uri u
       && u.Scheme == Uri.UriSchemeHttps
       && (u.Host.Equals("github.com", StringComparison.OrdinalIgnoreCase)
           || u.Host.EndsWith(".github.com", StringComparison.OrdinalIgnoreCase)
           || u.Host.EndsWith(".githubusercontent.com", StringComparison.OrdinalIgnoreCase));

Prevention

When it happens

Trigger: versionInfo.DownloadUrl is built in GetLatestVersionInfo from releaseInfo["assets[0].browser_download_url"]. If a release attaches an asset whose URL is HTTPS but on a CDN outside *.githubusercontent.com, or a caller hand-constructs a VersionInfo pointing elsewhere, IsGitHubUrl (line 199) returns false and line 80 throws.

Common situations: A fork re-hosts the release zip on a private server; a GitHub Enterprise release points to an enterprise host (not *.github.com); a test VersionInfo built in a unit test uses localhost or example.com; a proxy rewrites the asset URL; the GitHub API response is mocked with a non-GitHub asset URL.

Related errors


AI-assisted analysis of lostindark/DriverStoreExplorer@958fcd481b (2026-08-13). Data as JSON: /api/errors/92e12af6466cb164. Report an issue: GitHub.