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
17 changes: 14 additions & 3 deletions pkg/workflow/data/ecosystem_domains.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@
"archive.apache.org",
"download.java.net",
"api.foojay.io",
"cdn.azul.com"
"cdn.azul.com",
"central.sonatype.com",
"maven.google.com",
"dl.google.com",
"repo.gradle.org"
],
"linux-distros": [
"deb.debian.org",
Expand Down Expand Up @@ -133,7 +137,11 @@
"deno.land",
"jsr.io",
"*.jsr.io",
"registry.bower.io"
"registry.bower.io",
"esm.sh",
"googleapis.deno.dev",
"googlechromelabs.github.io",
"cdn.jsdelivr.net"
],
Comment on lines +141 to 145
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding cdn.jsdelivr.net to the node ecosystem creates an overlap with the existing node-cdns ecosystem. GetDomainEcosystem currently iterates over a Go map (for ecosystem := range ecosystemDomains), so when a domain exists in multiple ecosystems the returned ecosystem becomes nondeterministic (and can break/flap tests like TestGetDomainEcosystem which expects cdn.jsdelivr.netnode-cdns, and can also make strict-mode ecosystem suggestions unstable). Consider either (a) keeping cdn.jsdelivr.net in only one ecosystem, or (b) making GetDomainEcosystem deterministic by iterating ecosystems in a fixed priority order and explicitly preferring node-cdns over node for this domain.

This issue also appears on line 163 of the same file.

Copilot uses AI. Check for mistakes.
"node-cdns": ["cdn.jsdelivr.net"],
"perl": ["cpan.org", "www.cpan.org", "metacpan.org", "cpan.metacpan.org"],
Expand All @@ -151,7 +159,10 @@
"binstar.org",
"anaconda.org",
"repo.continuum.io",
"repo.anaconda.com"
"repo.anaconda.com",
"crates.io",
"index.crates.io",
"static.crates.io"
],
"ruby": ["rubygems.org", "api.rubygems.org", "rubygems.pkg.github.com", "bundler.rubygems.org", "gems.rubyforge.org", "gems.rubyonrails.org", "index.rubygems.org", "cache.ruby-lang.org", "*.rvm.io"],
"rust": ["crates.io", "index.crates.io", "static.crates.io", "sh.rustup.rs", "static.rust-lang.org"],
Expand Down
56 changes: 54 additions & 2 deletions pkg/workflow/domains.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,62 @@ func GetAllowedDomains(network *NetworkPermissions) []string {
return expandedDomains
}

// GetDomainEcosystem returns the ecosystem identifier for a given domain, or empty string if not found
// ecosystemPriority defines the order in which ecosystems are checked by GetDomainEcosystem.
// More specific sub-ecosystems are listed before their parent ecosystems so that domains
// shared between multiple ecosystems resolve deterministically to the most specific one.
// For example, "node-cdns" is listed before "node" so that cdn.jsdelivr.net returns "node-cdns".
// All known ecosystems are enumerated here; any ecosystem not in this list is checked last
// in sorted order (for forward-compatibility with new entries).
var ecosystemPriority = []string{
"node-cdns", // before "node" — more specific CDN sub-ecosystem
"rust", // before "python" — crates.io/index.crates.io/static.crates.io are native Rust domains
"containers",
"dart",
"defaults",
"dotnet",
"fonts",
"github",
"github-actions",
"go",
"haskell",
"java",
"linux-distros",
"node",
"perl",
"php",
"playwright",
"python",
"ruby",
"swift",
"terraform",
}

// GetDomainEcosystem returns the ecosystem identifier for a given domain, or empty string if not found.
// Ecosystems are checked in ecosystemPriority order so that the result is deterministic even when
// a domain appears in multiple ecosystems (e.g. cdn.jsdelivr.net is in both "node" and "node-cdns").
func GetDomainEcosystem(domain string) string {
// Check each ecosystem for domain match
checked := make(map[string]bool, len(ecosystemPriority))

// Check ecosystems in priority order first
for _, ecosystem := range ecosystemPriority {
checked[ecosystem] = true
domains := getEcosystemDomains(ecosystem)
for _, ecosystemDomain := range domains {
if matchesDomain(domain, ecosystemDomain) {
return ecosystem
}
}
}

// Fall back to any ecosystems not in the priority list, sorted for determinism
remaining := make([]string, 0)
for ecosystem := range ecosystemDomains {
if !checked[ecosystem] {
remaining = append(remaining, ecosystem)
}
}
SortStrings(remaining)
for _, ecosystem := range remaining {
domains := getEcosystemDomains(ecosystem)
for _, ecosystemDomain := range domains {
if matchesDomain(domain, ecosystemDomain) {
Expand Down
23 changes: 23 additions & 0 deletions pkg/workflow/domains_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,29 @@ func TestGetDomainEcosystem(t *testing.T) {
}
}

// TestGetDomainEcosystem_Determinism verifies that GetDomainEcosystem returns the same result
// across repeated calls for domains that exist in multiple ecosystems (e.g. cdn.jsdelivr.net
// is in both "node" and "node-cdns" and must always resolve to "node-cdns").
func TestGetDomainEcosystem_Determinism(t *testing.T) {
cases := []struct {
domain string
expected string
}{
{"cdn.jsdelivr.net", "node-cdns"},
{"crates.io", "rust"}, // also appears in python ecosystem
{"index.crates.io", "rust"},
{"static.crates.io", "rust"},
}
for _, c := range cases {
for i := 0; i < 20; i++ {
got := GetDomainEcosystem(c.domain)
if got != c.expected {
t.Errorf("call %d: GetDomainEcosystem(%q) = %q, want %q", i, c.domain, got, c.expected)
}
}
}
}

func TestMatchesDomain(t *testing.T) {
tests := []struct {
name string
Expand Down
38 changes: 38 additions & 0 deletions pkg/workflow/ecosystem_domains_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,19 @@ func TestEcosystemDomainExpansion(t *testing.T) {

expectedDomains := []string{
"repo.maven.apache.org",
"repo1.maven.org",
"services.gradle.org",
"plugins.gradle.org",
"download.oracle.com",
"dlcdn.apache.org",
"archive.apache.org",
"download.java.net",
"api.foojay.io",
"cdn.azul.com",
"central.sonatype.com",
"maven.google.com",
"dl.google.com",
"repo.gradle.org",
}

for _, expectedDomain := range expectedDomains {
Expand Down Expand Up @@ -218,6 +224,10 @@ func TestEcosystemDomainExpansion(t *testing.T) {
"deno.land",
"jsr.io",
"*.jsr.io",
"esm.sh",
"googleapis.deno.dev",
"googlechromelabs.github.io",
"cdn.jsdelivr.net",
}

for _, expectedDomain := range expectedDomains {
Expand All @@ -234,6 +244,34 @@ func TestEcosystemDomainExpansion(t *testing.T) {
}
})

t.Run("python ecosystem includes Rust FFI domains for native packages", func(t *testing.T) {
permissions := &NetworkPermissions{
Allowed: []string{"python"},
}
domains := GetAllowedDomains(permissions)

expectedDomains := []string{
"pypi.org",
"pip.pypa.io",
"crates.io",
"index.crates.io",
"static.crates.io",
}

for _, expectedDomain := range expectedDomains {
found := false
for _, domain := range domains {
if domain == expectedDomain {
found = true
break
}
}
if !found {
t.Errorf("Expected domain '%s' to be included in python ecosystem, but it was not found", expectedDomain)
}
}
})

t.Run("github ecosystem includes GitHub domains", func(t *testing.T) {
permissions := &NetworkPermissions{
Allowed: []string{"github"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ jobs:
timeout-minutes: 15
run: |
set -o pipefail
sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains '*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.playwright.dev,codeload.github.com,conda.anaconda.org,conda.binstar.org,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.20.1 --skip-pull --enable-api-proxy \
sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains '*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,anaconda.org,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,codeload.github.com,conda.anaconda.org,conda.binstar.org,crates.io,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,esm.sh,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,index.crates.io,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,static.crates.io,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.20.1 --skip-pull --enable-api-proxy \
-- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools Bash,BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_CLAUDE:+ --model "$GH_AW_MODEL_DETECTION_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ jobs:
timeout-minutes: 15
run: |
set -o pipefail
sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains '*.githubusercontent.com,*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.20.1 --skip-pull --enable-api-proxy \
sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains '*.githubusercontent.com,*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,cdn.playwright.dev,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,esm.sh,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,googleapis.deno.dev,googlechromelabs.github.io,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.20.1 --skip-pull --enable-api-proxy \
-- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_COPILOT:+ --model "$GH_AW_MODEL_DETECTION_COPILOT"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
Expand Down
Loading
Loading