Repository navigation
Centralize engine default domain sets #55461
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
03724be
7dc9cba
3cd8e22
a099902
03a3bcb
d671c0c
5e44cf7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -116,6 +116,21 @@ Mix ecosystem identifiers with specific domains for fine-grained control: | |
| | `swift` | Swift packages (`swift.org`, `cocoapods.org`) | | ||
| | `zig` | Zig packages (`ziglang.org`) | | ||
|
|
||
| ## Automatic Engine Domain Sets | ||
|
|
||
| Each engine automatically receives the domain set it requires in addition to | ||
| `network.allowed`. These named sets are maintained by the compiler for analysis | ||
| and reporting; they are not valid `network.allowed` identifiers. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in |
||
|
|
||
| | Engine set | Included domains | | ||
| |---|---| | ||
| | `copilot` | `api.github.com`, `api.githubcopilot.com`, `github.com`, `host.docker.internal`, `raw.githubusercontent.com` | | ||
| | `claude` | Anthropic APIs, GitHub transport, certificate/OCSP services, Ubuntu package metadata, Playwright downloads, and `host.docker.internal` | | ||
| | `codex` | `172.30.0.1`, `api.github.com`, `api.openai.com`, `chatgpt.com`, `github.com`, `host.docker.internal`, `openai.com` | | ||
| | `gemini` | `*.googleapis.com`, `generativelanguage.googleapis.com`, `github.com`, `host.docker.internal`, `raw.githubusercontent.com` | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/grill-with-docs] The 💡 Suggested additionAdd a row after the | `pi-base` | `host.docker.internal`, `github.com`, `raw.githubusercontent.com`; applied as the provider-independent baseline before a provider prefix is resolved |@copilot please address this.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in |
||
| | `pi` | `api.githubcopilot.com`, `github.com`, `host.docker.internal`, `raw.githubusercontent.com`; provider-scoped models replace the API host with the selected provider endpoint | | ||
| | `threat-detection` | Copilot API and telemetry hosts, `api.github.com`, `github.com`, `host.docker.internal`, and `registry.npmjs.org` for read-only lockfile validation | | ||
|
|
||
| ### Ecosystem Identifier Validation | ||
|
|
||
| Single-word entries in `network.allowed` that match the ecosystem identifier pattern (`[a-z][a-z0-9-]*`) are validated against the known ecosystem list at compile time. An unrecognized identifier produces a compilation error with the full list of valid options: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -257,16 +257,5 @@ | |
| "scala": ["repo.scala-sbt.org", "scala-ci.typesafe.com", "repo.typesafe.com", "jitpack.io", "dl.bintray.com", "scala.jfrog.io"], | ||
| "swift": ["download.swift.org", "swift.org", "cocoapods.org", "cdn.cocoapods.org"], | ||
| "terraform": ["releases.hashicorp.com", "apt.releases.hashicorp.com", "yum.releases.hashicorp.com", "registry.terraform.io"], | ||
| "threat-detection": [ | ||
| "api.business.githubcopilot.com", | ||
| "api.enterprise.githubcopilot.com", | ||
| "api.github.com", | ||
| "api.githubcopilot.com", | ||
| "api.individual.githubcopilot.com", | ||
| "github.com", | ||
| "host.docker.internal", | ||
| "registry.npmjs.org", | ||
| "telemetry.enterprise.githubcopilot.com" | ||
| ], | ||
| "zig": ["ziglang.org", "pkg.machengine.org", "deps.files.ghostty.org"] | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -65,104 +65,150 @@ func getLoadedEcosystemDomains() map[string][]string { | |
| // to an engine default and that test starts failing, the domain belongs behind an explicit | ||
| // ecosystem/runtime opt-in instead, not in the unconditional default list. | ||
|
|
||
| // engineDefaultDomainSets centralizes every unconditional engine allow-list. These | ||
| // sets are intentionally distinct from user-selectable ecosystem domain lists: the | ||
| // compiler includes them automatically for the matching engine, rather than allowing | ||
| // users to select them through network.allowed. | ||
| // | ||
| // Engine default domain lists intentionally exclude package registries (npm, PyPI, | ||
| // and similar). See the package-registry invariant above. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in |
||
| var engineDefaultDomainSets = map[string][]string{ | ||
| "claude": { | ||
| "*.githubusercontent.com", | ||
| "anthropic.com", | ||
| "api.anthropic.com", | ||
| "api.github.com", | ||
| "api.snapcraft.io", | ||
| "archive.ubuntu.com", | ||
| "azure.archive.ubuntu.com", | ||
| "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", | ||
| "ghcr.io", | ||
| "github-cloud.githubusercontent.com", | ||
| "github-cloud.s3.amazonaws.com", | ||
| "github.com", | ||
| "host.docker.internal", | ||
| "json-schema.org", | ||
| "json.schemastore.org", | ||
| "keyserver.ubuntu.com", | ||
| "lfs.github.com", | ||
| "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", | ||
| "playwright.download.prss.microsoft.com", | ||
| "ppa.launchpad.net", | ||
| "raw.githubusercontent.com", | ||
| "s.symcb.com", | ||
| "s.symcd.com", | ||
| "security.ubuntu.com", | ||
| "sentry.io", | ||
| "statsig.anthropic.com", | ||
| "ts-crl.ws.symantec.com", | ||
| "ts-ocsp.ws.symantec.com", | ||
| }, | ||
| "codex": { | ||
| "172.30.0.1", // AWF gateway IP - Codex resolves host.docker.internal to this IP for Rust DNS compatibility | ||
| "api.github.com", // Codex startup performs GitHub plugin sync requests against the GitHub API | ||
| "api.openai.com", | ||
| "chatgpt.com", // Codex CLI connects to chatgpt.com (and subdomains e.g. ab.chatgpt.com) for auth/telemetry | ||
| "github.com", // Codex startup accesses GitHub-hosted plugin metadata pages | ||
| "host.docker.internal", | ||
| "openai.com", | ||
| }, | ||
| "copilot": { | ||
| "api.github.com", | ||
| "api.githubcopilot.com", | ||
| "github.com", | ||
| "host.docker.internal", | ||
| "raw.githubusercontent.com", | ||
| }, | ||
| "gemini": { | ||
| "*.googleapis.com", | ||
| "generativelanguage.googleapis.com", | ||
| "github.com", | ||
| "host.docker.internal", | ||
| "raw.githubusercontent.com", | ||
| }, | ||
| "pi": { | ||
| "api.githubcopilot.com", // Default provider (Copilot routing) | ||
| "host.docker.internal", | ||
| "github.com", | ||
| "raw.githubusercontent.com", | ||
| }, | ||
| "pi-base": { | ||
| "host.docker.internal", // MCP gateway / API proxy access | ||
| "github.com", | ||
| "raw.githubusercontent.com", | ||
| }, | ||
| "threat-detection": { | ||
| "api.business.githubcopilot.com", | ||
| "api.enterprise.githubcopilot.com", | ||
| "api.github.com", | ||
| "api.githubcopilot.com", | ||
| "api.individual.githubcopilot.com", | ||
| "github.com", | ||
| "host.docker.internal", | ||
| "registry.npmjs.org", | ||
| "telemetry.enterprise.githubcopilot.com", | ||
| }, | ||
| } | ||
|
|
||
| // GetEngineDefaultDomainSets returns copies of the named engine domain sets for | ||
| // analysis and reporting. Engine domain sets are not valid network.allowed values. | ||
| func GetEngineDefaultDomainSets() map[string][]string { | ||
| sets := make(map[string][]string, len(engineDefaultDomainSets)) | ||
| for name, domains := range engineDefaultDomainSets { | ||
| sets[name] = copyEngineDefaultDomainSet(domains) | ||
| } | ||
| return sets | ||
| } | ||
|
|
||
| func copyEngineDefaultDomainSet(domains []string) []string { | ||
| return append([]string(nil), domains...) | ||
| } | ||
|
|
||
| // CopilotDefaultDomains are the default domains required for GitHub Copilot CLI authentication and operation. | ||
| // | ||
| // This list is limited to the shared gateway/GitHub transport baseline: the MCP/API gateway | ||
| // (host.docker.internal), the GitHub API and web hosts, and the Copilot routing hub used for | ||
| // inference. Plan-specific Copilot API hosts (business/enterprise/individual) and Copilot | ||
| // telemetry are *not* part of the default set: agents route inference through the AWF api-proxy, | ||
| // so those vendor hosts require an explicit `network: { allowed: [copilot-vendor] }` opt-in. | ||
| var CopilotDefaultDomains = []string{ | ||
| "api.github.com", | ||
| "api.githubcopilot.com", | ||
| "github.com", | ||
| "host.docker.internal", | ||
| "raw.githubusercontent.com", | ||
| } | ||
| var CopilotDefaultDomains = copyEngineDefaultDomainSet(engineDefaultDomainSets["copilot"]) | ||
|
|
||
| // CodexDefaultDomains are the minimal default domains required for Codex CLI operation | ||
| var CodexDefaultDomains = []string{ | ||
| "172.30.0.1", // AWF gateway IP - Codex resolves host.docker.internal to this IP for Rust DNS compatibility | ||
| "api.github.com", // Codex startup performs GitHub plugin sync requests against the GitHub API | ||
| "api.openai.com", | ||
| "chatgpt.com", // Codex CLI connects to chatgpt.com (and subdomains e.g. ab.chatgpt.com) for auth/telemetry | ||
| "github.com", // Codex startup accesses GitHub-hosted plugin metadata pages | ||
| "host.docker.internal", | ||
| "openai.com", | ||
| } | ||
| // CodexDefaultDomains are the minimal default domains required for Codex CLI operation. | ||
| var CodexDefaultDomains = copyEngineDefaultDomainSet(engineDefaultDomainSets["codex"]) | ||
|
|
||
| // ClaudeDefaultDomains are the default domains required for Claude Code CLI authentication and operation | ||
| var ClaudeDefaultDomains = []string{ | ||
| "*.githubusercontent.com", | ||
| "anthropic.com", | ||
| "api.anthropic.com", | ||
| "api.github.com", | ||
| "api.snapcraft.io", | ||
| "archive.ubuntu.com", | ||
| "azure.archive.ubuntu.com", | ||
| "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", | ||
| "ghcr.io", | ||
| "github-cloud.githubusercontent.com", | ||
| "github-cloud.s3.amazonaws.com", | ||
| "github.com", | ||
| "host.docker.internal", | ||
| "json-schema.org", | ||
| "json.schemastore.org", | ||
| "keyserver.ubuntu.com", | ||
| "lfs.github.com", | ||
| "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", | ||
| "playwright.download.prss.microsoft.com", | ||
| "ppa.launchpad.net", | ||
| "raw.githubusercontent.com", | ||
| "s.symcb.com", | ||
| "s.symcd.com", | ||
| "security.ubuntu.com", | ||
| "sentry.io", | ||
| "statsig.anthropic.com", | ||
| "ts-crl.ws.symantec.com", | ||
| "ts-ocsp.ws.symantec.com", | ||
| } | ||
| // ClaudeDefaultDomains are the default domains required for Claude Code CLI authentication and operation. | ||
| var ClaudeDefaultDomains = copyEngineDefaultDomainSet(engineDefaultDomainSets["claude"]) | ||
|
|
||
| // GeminiDefaultDomains are the default domains required for Google Gemini CLI authentication and operation. | ||
| var GeminiDefaultDomains = []string{ | ||
| "*.googleapis.com", | ||
| "generativelanguage.googleapis.com", | ||
| "github.com", | ||
| "host.docker.internal", | ||
| "raw.githubusercontent.com", | ||
| } | ||
| var GeminiDefaultDomains = copyEngineDefaultDomainSet(engineDefaultDomainSets["gemini"]) | ||
|
|
||
| // PiBaseDefaultDomains are the base domains required for the Pi CLI to operate, | ||
| // independent of the chosen LLM provider. When a model uses provider/model format, | ||
| // provider-specific API domains are added on top via GetDefaultDomainsForEngine. | ||
| var PiBaseDefaultDomains = []string{ | ||
| "host.docker.internal", // MCP gateway / API proxy access | ||
| "github.com", | ||
| "raw.githubusercontent.com", | ||
| } | ||
| var PiBaseDefaultDomains = copyEngineDefaultDomainSet(engineDefaultDomainSets["pi-base"]) | ||
|
|
||
| // piProviderDomains maps provider prefixes to their API domains. | ||
| // Covers the same set of providers that Pi can route through via the AWF LLM gateway. | ||
|
|
@@ -180,12 +226,7 @@ var piProviderDomains = map[string]string{ | |
| // PiDefaultDomains are the static default domains for backward compatibility when | ||
| // no model provider prefix is given. When a provider/model format is used, the | ||
| // dynamic path (GetDefaultDomainsForEngine) resolves provider-specific domains instead. | ||
| var PiDefaultDomains = []string{ | ||
| "api.githubcopilot.com", // Default provider (Copilot routing) | ||
| "host.docker.internal", | ||
| "github.com", | ||
| "raw.githubusercontent.com", | ||
| } | ||
| var PiDefaultDomains = copyEngineDefaultDomainSet(engineDefaultDomainSets["pi"]) | ||
|
|
||
| // extractProviderFromModel parses "provider/model" format and returns the | ||
| // lowercase provider prefix. Returns ("", nil) when no model is given or the | ||
|
|
@@ -752,16 +793,16 @@ func GetAllowedDomainsForEngine(engine constants.EngineName, network *NetworkPer | |
| } | ||
|
|
||
| // GetThreatDetectionAllowedDomains returns the minimal set of domains allowed for a Copilot | ||
| // detection run. It loads the "threat-detection" ecosystem from ecosystem_domains.json, which | ||
| // includes the Copilot API endpoints needed for read-only threat analysis plus registry.npmjs.org | ||
| // detection run. The "threat-detection" engine domain set includes the Copilot API endpoints | ||
| // needed for read-only threat analysis plus registry.npmjs.org | ||
| // for read-only npm package validation (e.g. verifying lockfile integrity hashes). It intentionally | ||
| // excludes raw.githubusercontent.com (not needed when MCP servers are disabled and the CLI binary | ||
| // is pre-installed). npm registry access is read-only metadata lookup only — installs are not | ||
| // permitted during detection runs. | ||
| // Any additional user-specified network.allowed entries are merged in (typically empty for detection). | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [/codebase-design] Direct map access hands the internal slice to 💡 Suggested fixUse the same defensive copy pattern used everywhere else in this PR: detectionDomains := copyEngineDefaultDomainSet(engineDefaultDomainSets["threat-detection"])This is a one-liner and keeps the access pattern consistent with @copilot please address this.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in |
||
| // Returns a deduplicated, sorted, comma-separated string suitable for AWF's --allow-domains flag. | ||
| func GetThreatDetectionAllowedDomains(network *NetworkPermissions) string { | ||
| detectionDomains := getEcosystemDomains("threat-detection") | ||
| detectionDomains := engineDefaultDomainSets["threat-detection"] | ||
| // Pass nil tools and runtimes: detection runs with no npm/runtime ecosystem, so | ||
| // ecosystem domain expansion is intentionally skipped. | ||
| return mergeDomainsWithNetworkToolsAndRuntimes(detectionDomains, network, nil, nil) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -409,7 +409,7 @@ func TestCopilotVendorDomainsRequireOptIn(t *testing.T) { | |
| } | ||
|
|
||
| func TestThreatDetectionDomains(t *testing.T) { | ||
| detectionDomains := getEcosystemDomains("threat-detection") | ||
| detectionDomains := GetEngineDefaultDomainSets()["threat-detection"] | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in |
||
|
|
||
| // Detection domains must include every required Copilot API domain | ||
| requiredDomains := []string{ | ||
|
|
@@ -423,6 +423,7 @@ func TestThreatDetectionDomains(t *testing.T) { | |
| "registry.npmjs.org", | ||
| "telemetry.enterprise.githubcopilot.com", | ||
| } | ||
|
|
||
| detectionMap := make(map[string]bool) | ||
| for _, d := range detectionDomains { | ||
| detectionMap[d] = true | ||
|
|
@@ -444,6 +445,30 @@ func TestThreatDetectionDomains(t *testing.T) { | |
| "threat-detection ecosystem should have exactly %d entries", len(requiredDomains)) | ||
| } | ||
|
|
||
| func TestGetEngineDefaultDomainSets(t *testing.T) { | ||
| sets := GetEngineDefaultDomainSets() | ||
|
|
||
| assert.Equal(t, CopilotDefaultDomains, sets["copilot"]) | ||
| assert.Equal(t, ClaudeDefaultDomains, sets["claude"]) | ||
| assert.Equal(t, CodexDefaultDomains, sets["codex"]) | ||
| assert.Equal(t, GeminiDefaultDomains, sets["gemini"]) | ||
| assert.Equal(t, PiBaseDefaultDomains, sets["pi-base"]) | ||
| assert.Equal(t, PiDefaultDomains, sets["pi"]) | ||
| assert.NotEmpty(t, sets["threat-detection"]) | ||
|
|
||
| sets["copilot"][0] = "modified.example.com" | ||
| assert.NotEqual(t, "modified.example.com", CopilotDefaultDomains[0], | ||
| "callers must not be able to modify registered domain sets") | ||
|
|
||
| original := CopilotDefaultDomains[0] | ||
| t.Cleanup(func() { | ||
| CopilotDefaultDomains[0] = original | ||
| }) | ||
| CopilotDefaultDomains[0] = "modified.example.com" | ||
| assert.Equal(t, original, GetEngineDefaultDomainSets()["copilot"][0], | ||
| "exported compatibility variables must not modify registered domain sets") | ||
| } | ||
|
|
||
| func TestGetThreatDetectionAllowedDomains(t *testing.T) { | ||
| // With empty network permissions the result equals the sorted detection domains | ||
| result := GetThreatDetectionAllowedDomains(&NetworkPermissions{Allowed: []string{}}) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in
03a3bcb: updated.github/aw/network.mdwith the same Copilot-only automatic threat-detection scoping and clarified other detection engines use their own defaults.