From f75aac6a84eb6d51a98ee6c63728de24bc5ba05e Mon Sep 17 00:00:00 2001 From: v-abhishekbhaskar Date: Fri, 25 Sep 2026 23:11:02 -0500 Subject: [PATCH] add azure blob redirect urls to egress allowlist --- .../handlers/egress_allowlist_defaults.yaml | 9 +++++ internal/handlers/egress_allowlist_test.go | 40 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/internal/handlers/egress_allowlist_defaults.yaml b/internal/handlers/egress_allowlist_defaults.yaml index 4018efb..896d368 100644 --- a/internal/handlers/egress_allowlist_defaults.yaml +++ b/internal/handlers/egress_allowlist_defaults.yaml @@ -188,6 +188,9 @@ ecosystem_default_domains: - cdn.sheetjs.com - npm.jsr.io - dl.fontawesome.com + # GitHub Packages npm content host; npm.pkg.github.com 302-redirects here. + # Exact only; listed in api.github.com/meta domains.packages. + - npmregistryv2prod.blob.core.windows.net bun: *npm_registries pip: &python_registries - pypi.org @@ -206,6 +209,9 @@ ecosystem_default_domains: - gems.rubygems.org - rails-assets.org - gem.coop + # GitHub Packages RubyGems content host; rubygems.pkg.github.com redirects here. + # Exact only; listed in api.github.com/meta domains.packages. + - rubygemsregistryv2prod.blob.core.windows.net maven: &jvm_registries - repo.maven.apache.org - repo1.maven.org @@ -214,6 +220,9 @@ ecosystem_default_domains: - maven.google.com - repo.broadcom.com - packages.confluent.io + # GitHub Packages Maven content host; maven.pkg.github.com 302-redirects here. + # Exact only; listed in api.github.com/meta domains.packages. + - mavenregistryv2prod.blob.core.windows.net gradle: *jvm_registries sbt: - repo1.maven.org diff --git a/internal/handlers/egress_allowlist_test.go b/internal/handlers/egress_allowlist_test.go index 178815a..0f16861 100644 --- a/internal/handlers/egress_allowlist_test.go +++ b/internal/handlers/egress_allowlist_test.go @@ -200,6 +200,46 @@ func TestEgressAllowlist_NuGetStorageBackendsAllowed(t *testing.T) { } } +func TestEgressAllowlist_GitHubPackagesContentHostsAllowed(t *testing.T) { + // GitHub Packages serves registry metadata from *.pkg.github.com but + // 302-redirects the actual package download to a per-ecosystem Azure Blob + // content host carrying a short-lived SAS token. Blocking the redirect + // target fails the download even though the registry request succeeded. + // These four hosts are published under domains.packages in + // https://api.github.com/meta. They are allowed as EXACT hosts only: an + // Azure storage account name is globally unique and these are already + // registered to GitHub, so no attacker can claim them. + h := newEgressHandler(false, true, "bundler") + + for _, allowed := range []string{ + "https://rubygems.pkg.github.com/github/gems/github-kredz-0.0.4.gem", + "https://rubygemsregistryv2prod.blob.core.windows.net/gems/x.gem?sig=redacted", + "https://npmregistryv2prod.blob.core.windows.net/npm/x.tgz?sig=redacted", + "https://mavenregistryv2prod.blob.core.windows.net/maven/x.jar?sig=redacted", + "https://nugetregistryv2prod.blob.core.windows.net/nuget/x.nupkg?sig=redacted", + } { + assert.Nil(t, egressResult(t, h, allowed), "github packages content host allowed: "+allowed) + } + + for _, blocked := range []string{ + // Child hosts: these start passing the moment an entry is widened to a + // leading-dot suffix, so they pin the exact-host semantics. + "https://evil.rubygemsregistryv2prod.blob.core.windows.net/loot", + "https://evil.npmregistryv2prod.blob.core.windows.net/loot", + "https://evil.mavenregistryv2prod.blob.core.windows.net/loot", + // Lookalike account names must not match. + "https://rubygemsregistryv2prodx.blob.core.windows.net/loot", + "https://myrubygemsregistryv2prod.blob.core.windows.net/loot", + // The shared multi-tenant parent stays closed. + "https://attacker.blob.core.windows.net/loot", + } { + resp := egressResult(t, h, blocked) + if assert.NotNil(t, resp, "packages entries must not widen to: "+blocked) { + assert.Equal(t, http.StatusForbidden, resp.StatusCode) + } + } +} + func TestEgressAllowlist_MultiTenantAWSNamespacesNotGloballyAllowed(t *testing.T) { // A 12-digit AWS account id matches every AWS tenant, so ECR and CodeArtifact // are NOT globally allowlisted (an attacker could use their own account).