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
9 changes: 9 additions & 0 deletions internal/handlers/egress_allowlist_defaults.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
40 changes: 40 additions & 0 deletions internal/handlers/egress_allowlist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
Loading