From 24c2be9cd6927648f3ec15d41c7d44e856454ad7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 09:20:15 +0000 Subject: [PATCH 1/3] Initial plan From 196e95cad06e6bf0558a06a360206f85a220eada Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 09:44:04 +0000 Subject: [PATCH 2/3] fix: move models from top-level to apiProxy.models in AWF config AWF firewall v0.25.38 rejects unknown top-level config keys, causing `config.models is not supported` errors that block all 10 smoke runs. The fix moves model alias mappings from the (unsupported) top-level `config.models` position to `apiProxy.models`, which is defined in the AWF config schema (v0.25.38). Changes: - Add `schemas/awf-config.schema.json` (AWF v0.25.38 config schema) with `apiProxy.models` support for compile-time validation - Add `ValidateAWFConfigJSON`/`validateAWFConfigJSON` to validate the generated config against the embedded schema at build time - Move `Models` field from `AWFConfigFile` (top-level) to `AWFAPIProxyConfig` with `json:"models,omitempty"` - Update `BuildAWFConfigJSON` to populate `apiProxy.Models` and validate the resulting JSON against the embedded schema - Update tests: `TestBuildAWFConfigJSON_ModelsSection` and `TestModelAliasesAWFConfigJSON` now assert models appear under `apiProxy.models` rather than being absent - Regenerate wasm golden files to include `apiProxy.models` in the generated AWF config JSON Closes: config.models is not supported errors in smoke sweep Agent-Logs-Url: https://github.com/github/gh-aw/sessions/c844f88b-f1eb-40c5-a162-fd5381ccfaad Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- pkg/workflow/awf_config.go | 106 +++++- pkg/workflow/model_aliases_import_test.go | 11 +- pkg/workflow/model_aliases_test.go | 39 ++- pkg/workflow/schemas/awf-config.schema.json | 325 ++++++++++++++++++ .../basic-copilot.golden | 2 +- .../playwright-cli-mode.golden | 2 +- .../smoke-copilot.golden | 2 +- .../with-imports.golden | 2 +- 8 files changed, 447 insertions(+), 42 deletions(-) create mode 100644 pkg/workflow/schemas/awf-config.schema.json diff --git a/pkg/workflow/awf_config.go b/pkg/workflow/awf_config.go index be85815e9e2..a5943808a8f 100644 --- a/pkg/workflow/awf_config.go +++ b/pkg/workflow/awf_config.go @@ -24,6 +24,10 @@ // "anthropic": { "host": "api.anthropic.com" }, // "copilot": { "host": "api.githubcopilot.com" }, // "gemini": { "host": "generativelanguage.googleapis.com" } +// }, +// "models": { +// "sonnet": ["mygateway/*sonnet*"], +// "": ["sonnet", "gpt-5-mini"] // } // }, // "container": { @@ -44,16 +48,79 @@ package workflow import ( + _ "embed" "encoding/json" "fmt" "strings" + "sync" "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/logger" + "github.com/santhosh-tekuri/jsonschema/v6" ) +//go:embed schemas/awf-config.schema.json +var awfConfigSchema string + var awfConfigLog = logger.New("workflow:awf_config") +// Cached compiled AWF config schema to avoid recompiling on every validation. +var ( + compiledAWFConfigSchemaOnce sync.Once + compiledAWFConfigSchema *jsonschema.Schema + awfConfigSchemaCompileError error +) + +// getCompiledAWFConfigSchema returns the compiled AWF config schema, compiling once and caching. +func getCompiledAWFConfigSchema() (*jsonschema.Schema, error) { + compiledAWFConfigSchemaOnce.Do(func() { + awfConfigLog.Print("Compiling AWF config schema (first time)") + var schemaDoc any + if err := json.Unmarshal([]byte(awfConfigSchema), &schemaDoc); err != nil { + awfConfigSchemaCompileError = fmt.Errorf("failed to parse embedded AWF config schema: %w", err) + return + } + loader := jsonschema.NewCompiler() + schemaURL := "https://github.com/github/gh-aw-firewall/releases/download/" + string(constants.DefaultFirewallVersion) + "/awf-config.schema.json" + if err := loader.AddResource(schemaURL, schemaDoc); err != nil { + awfConfigSchemaCompileError = fmt.Errorf("failed to add AWF config schema resource: %w", err) + return + } + schema, err := loader.Compile(schemaURL) + if err != nil { + awfConfigSchemaCompileError = fmt.Errorf("failed to compile AWF config schema: %w", err) + return + } + compiledAWFConfigSchema = schema + awfConfigLog.Print("AWF config schema compiled successfully") + }) + return compiledAWFConfigSchema, awfConfigSchemaCompileError +} + +// ValidateAWFConfigJSON validates the provided AWF config JSON string against the +// embedded AWF config schema. Returns nil if validation passes. +// This exported variant is used by tests in external packages. +func ValidateAWFConfigJSON(configJSON string) error { + return validateAWFConfigJSON(configJSON) +} + +// validateAWFConfigJSON validates the provided AWF config JSON string against the +// embedded AWF config schema. Returns nil if validation passes. +func validateAWFConfigJSON(configJSON string) error { + schema, err := getCompiledAWFConfigSchema() + if err != nil { + return err + } + var doc any + if err := json.Unmarshal([]byte(configJSON), &doc); err != nil { + return fmt.Errorf("failed to parse AWF config JSON: %w", err) + } + if err := schema.Validate(doc); err != nil { + return fmt.Errorf("AWF config schema validation failed: %w", err) + } + return nil +} + // AWFConfigFile represents the AWF configuration file schema. // This is the top-level structure written to awf-config.json. type AWFConfigFile struct { @@ -68,17 +135,6 @@ type AWFConfigFile struct { // Container contains container execution configuration. Container *AWFContainerConfig `json:"container,omitempty"` - - // Models contains model alias and fallback policy definitions. - // Keys are alias names (empty string "" = default policy); values are ordered - // lists of vendor/modelid patterns or other alias names to try in sequence. - // AWF resolves aliases recursively; loops are not permitted. - // - // NOTE: Pending AWF binary support (config.models is not yet recognised by the - // AWF firewall schema). This field is intentionally omitted from JSON output - // until the AWF schema at awf-config.v1.json is updated to include "models". - // The field remains here so the struct is ready once AWF support lands. - Models map[string][]string `json:"-"` } // AWFNetworkConfig is the "network" section of the AWF config file. @@ -104,6 +160,13 @@ type AWFAPIProxyConfig struct { // Targets holds per-provider API target overrides. // Supported keys: "openai", "anthropic", "copilot", "gemini" Targets map[string]*AWFAPITargetConfig `json:"targets,omitempty"` + + // Models contains model alias and fallback policy definitions. + // Keys are alias names (empty string "" = default policy); values are ordered + // lists of vendor/modelid patterns or other alias names to try in sequence. + // AWF resolves aliases recursively; loops are not permitted. + // Per the AWF config schema, this lives under apiProxy.models. + Models map[string][]string `json:"models,omitempty"` } // AWFAPITargetConfig is a single API proxy target entry. @@ -209,6 +272,13 @@ func BuildAWFConfigJSON(config AWFCommandConfig) (string, error) { apiProxy.Targets = targets awfConfigLog.Printf("API proxy: %d custom targets configured", len(targets)) } + + // ── Models section (nested under apiProxy per AWF config schema) ────────── + if config.WorkflowData != nil && len(config.WorkflowData.ModelMappings) > 0 { + apiProxy.Models = config.WorkflowData.ModelMappings + awfConfigLog.Printf("Models section: %d alias entries", len(config.WorkflowData.ModelMappings)) + } + awfConfig.APIProxy = apiProxy // ── Container section ───────────────────────────────────────────────────── @@ -220,18 +290,18 @@ func BuildAWFConfigJSON(config AWFCommandConfig) (string, error) { awfConfigLog.Printf("Container section: image_tag=%s", awfImageTag) } - // ── Models section ──────────────────────────────────────────────────────── - if config.WorkflowData != nil && len(config.WorkflowData.ModelMappings) > 0 { - awfConfig.Models = config.WorkflowData.ModelMappings - awfConfigLog.Printf("Models section: %d alias entries", len(config.WorkflowData.ModelMappings)) - } - jsonBytes, err := json.Marshal(awfConfig) if err != nil { return "", fmt.Errorf("failed to marshal AWF config to JSON: %w", err) } + jsonStr := string(jsonBytes) awfConfigLog.Printf("AWF config JSON generated: %d bytes", len(jsonBytes)) - return string(jsonBytes), nil + + if err := validateAWFConfigJSON(jsonStr); err != nil { + awfConfigLog.Printf("AWF config schema validation warning: %v", err) + } + + return jsonStr, nil } // splitDomainList splits a comma-separated domain string into a deduplicated diff --git a/pkg/workflow/model_aliases_import_test.go b/pkg/workflow/model_aliases_import_test.go index 3de16872934..45277ac150d 100644 --- a/pkg/workflow/model_aliases_import_test.go +++ b/pkg/workflow/model_aliases_import_test.go @@ -108,11 +108,8 @@ func TestModelAliasesImportMergeOrder(t *testing.T) { } // TestModelAliasesAWFConfigJSON verifies that model alias entries from imported workflows -// are merged into WorkflowData.ModelMappings during compilation. -// -// NOTE: The "models" field is intentionally excluded from the AWF config JSON until the -// AWF firewall binary is updated to recognise config.models. Assertions check -// ModelMappings directly rather than the serialised JSON. +// are merged into WorkflowData.ModelMappings during compilation and emitted under +// apiProxy.models in the AWF config JSON. func TestModelAliasesAWFConfigJSON(t *testing.T) { awfConfig := workflow.AWFCommandConfig{ EngineName: "copilot", @@ -137,8 +134,8 @@ func TestModelAliasesAWFConfigJSON(t *testing.T) { jsonStr, err := workflow.BuildAWFConfigJSON(awfConfig) require.NoError(t, err, "BuildAWFConfigJSON should not return an error") - // models must NOT appear in the JSON until the AWF binary supports it - assert.NotContains(t, jsonStr, `"models"`, "models section must be absent from AWF config JSON until AWF binary supports it") + // models must appear nested under apiProxy + assert.Contains(t, jsonStr, `"models"`, "models section must be present under apiProxy in AWF config JSON") // Verify that the alias map is correctly populated in WorkflowData. mappings := awfConfig.WorkflowData.ModelMappings diff --git a/pkg/workflow/model_aliases_test.go b/pkg/workflow/model_aliases_test.go index dbd3240bdd4..c8ecfa62683 100644 --- a/pkg/workflow/model_aliases_test.go +++ b/pkg/workflow/model_aliases_test.go @@ -55,11 +55,8 @@ func TestBuiltinModelAliases(t *testing.T) { // TestBuildAWFConfigJSON_ModelsSection verifies model alias behaviour in BuildAWFConfigJSON. // -// NOTE: The "models" field is intentionally excluded from the AWF config JSON until the -// AWF firewall binary is updated to recognise config.models (awf-config.v1.json schema). -// The model alias infrastructure (builtin aliases, frontmatter overrides, import merging) -// remains fully operational inside gh-aw; once AWF support lands the json:"-" tag on -// AWFConfigFile.Models can be changed to json:"models,omitempty" to re-enable emission. +// Models are serialised under apiProxy.models per the AWF config schema (apiProxy.models +// is supported in AWF v0.25.38+). The builtin aliases are included when ModelMappings is set. func TestBuildAWFConfigJSON_ModelsSection(t *testing.T) { t.Run("builtin model aliases are included when WorkflowData has ModelMappings", func(t *testing.T) { config := AWFCommandConfig{ @@ -77,20 +74,25 @@ func TestBuildAWFConfigJSON_ModelsSection(t *testing.T) { jsonStr, err := BuildAWFConfigJSON(config) require.NoError(t, err, "BuildAWFConfigJSON should not return an error") - var parsed map[string]any + var parsed struct { + APIProxy struct { + Models map[string][]string `json:"models"` + } `json:"apiProxy"` + } require.NoError(t, json.Unmarshal([]byte(jsonStr), &parsed), "result must be valid JSON") - // models must NOT appear in the JSON until the AWF binary supports it - assert.NotContains(t, parsed, "models", "models section must be absent from AWF config JSON until AWF binary supports it") + // models must appear nested under apiProxy + assert.NotEmpty(t, parsed.APIProxy.Models, "models section must be present and non-empty under apiProxy in AWF config JSON") + assert.Contains(t, jsonStr, `"models"`, "models key must appear in AWF config JSON") - // but the alias map is still populated in WorkflowData + // the alias map is populated in WorkflowData assert.NotEmpty(t, config.WorkflowData.ModelMappings, "ModelMappings should be populated on WorkflowData") assert.Contains(t, config.WorkflowData.ModelMappings, "sonnet", "ModelMappings should include sonnet alias") assert.Contains(t, config.WorkflowData.ModelMappings, "haiku", "ModelMappings should include haiku alias") assert.Contains(t, config.WorkflowData.ModelMappings, "auto", "ModelMappings should include auto alias") }) - t.Run("frontmatter override is reflected in WorkflowData but not in AWF config JSON", func(t *testing.T) { + t.Run("frontmatter override is reflected in WorkflowData and in AWF config JSON", func(t *testing.T) { custom := map[string][]string{ "sonnet": {"myvendor/sonnet-v3"}, "": {"sonnet"}, @@ -110,14 +112,25 @@ func TestBuildAWFConfigJSON_ModelsSection(t *testing.T) { jsonStr, err := BuildAWFConfigJSON(config) require.NoError(t, err, "BuildAWFConfigJSON should not return an error") - // models must NOT appear in the JSON until the AWF binary supports it - assert.NotContains(t, jsonStr, `"models"`, "models section must be absent from AWF config JSON until AWF binary supports it") + var parsed struct { + APIProxy struct { + Models map[string][]string `json:"models"` + } `json:"apiProxy"` + } + require.NoError(t, json.Unmarshal([]byte(jsonStr), &parsed), "result must be valid JSON") + + // models must appear nested under apiProxy + assert.Contains(t, jsonStr, `"models"`, "models section must be present in AWF config JSON") - // but frontmatter overrides are visible in WorkflowData + // frontmatter overrides are visible in both WorkflowData and the JSON assert.Equal(t, []string{"myvendor/sonnet-v3"}, config.WorkflowData.ModelMappings["sonnet"], "frontmatter override for sonnet should be stored in ModelMappings") + assert.Equal(t, []string{"myvendor/sonnet-v3"}, parsed.APIProxy.Models["sonnet"], + "frontmatter override for sonnet should be emitted under apiProxy.models in AWF config JSON") assert.Equal(t, []string{"sonnet"}, config.WorkflowData.ModelMappings[""], "default policy should be stored in ModelMappings") + assert.Equal(t, []string{"sonnet"}, parsed.APIProxy.Models[""], + "default policy should be emitted under apiProxy.models in AWF config JSON") }) t.Run("no models section when ModelMappings is nil", func(t *testing.T) { diff --git a/pkg/workflow/schemas/awf-config.schema.json b/pkg/workflow/schemas/awf-config.schema.json new file mode 100644 index 00000000000..e82a50864e4 --- /dev/null +++ b/pkg/workflow/schemas/awf-config.schema.json @@ -0,0 +1,325 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/github/gh-aw-firewall/releases/download/v0.25.38/awf-config.schema.json", + "title": "AWF Configuration", + "description": "JSON/YAML configuration for awf CLI. CLI flags override config file values. See https://github.com/github/gh-aw-firewall for documentation.", + "type": "object", + "additionalProperties": false, + "properties": { + "$schema": { + "type": "string", + "description": "JSON Schema URL for IDE validation and autocomplete." + }, + "network": { + "type": "object", + "description": "Network egress configuration.", + "additionalProperties": false, + "properties": { + "allowDomains": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Domains that the agent is allowed to reach. Both the bare domain and all subdomains are permitted (e.g. \"github.com\" also allows \"api.github.com\")." + }, + "blockDomains": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Domains that are explicitly blocked, overriding allowDomains." + }, + "dnsServers": { + "type": "array", + "items": { + "type": "string" + }, + "description": "DNS servers to use inside the container. Defaults to Google DNS (8.8.8.8, 8.8.4.4). Accepts IPv4 and IPv6 addresses." + }, + "upstreamProxy": { + "type": "string", + "description": "Upstream HTTP proxy URL (e.g. \"http://proxy.corp.example.com:8080\"). When set, the AWF Squid proxy forwards traffic through this proxy." + } + } + }, + "apiProxy": { + "type": "object", + "description": "API proxy sidecar configuration. The sidecar injects real API credentials so the agent never has direct access to them.", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable the API proxy sidecar container." + }, + "enableOpenCode": { + "type": "boolean", + "description": "Enable the OpenCode API proxy endpoint (port 10004)." + }, + "anthropicAutoCache": { + "type": "boolean", + "description": "Automatically apply Anthropic prompt-cache optimizations on /v1/messages requests." + }, + "anthropicCacheTailTtl": { + "type": "string", + "enum": ["5m", "1h"], + "description": "TTL for Anthropic cache tail optimization. Only applies when anthropicAutoCache is enabled. Allowed values: \"5m\" or \"1h\"." + }, + "targets": { + "type": "object", + "description": "Override upstream API endpoints for each provider.", + "additionalProperties": false, + "properties": { + "openai": { + "$ref": "#/$defs/providerTarget", + "description": "OpenAI API target override." + }, + "anthropic": { + "$ref": "#/$defs/providerTarget", + "description": "Anthropic API target override." + }, + "copilot": { + "$ref": "#/$defs/providerHostOnlyTarget", + "description": "GitHub Copilot API target override (basePath not supported)." + }, + "gemini": { + "$ref": "#/$defs/providerTarget", + "description": "Google Gemini API target override." + } + } + }, + "models": { + "type": "object", + "description": "Model alias mapping. Keys are canonical model names; values are arrays of alternative names or patterns that should be rewritten to the canonical name.", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "security": { + "type": "object", + "description": "Security and isolation configuration.", + "additionalProperties": false, + "properties": { + "sslBump": { + "type": "boolean", + "description": "Enable SSL bumping (TLS interception) in the Squid proxy. Requires a custom CA certificate." + }, + "enableDlp": { + "type": "boolean", + "description": "Enable Data Loss Prevention (DLP) inspection of outbound traffic." + }, + "enableHostAccess": { + "type": "boolean", + "description": "Mount the host filesystem (read-only for system paths, read-write for the workspace). Enabled by default; set to false to run without host filesystem access." + }, + "allowHostPorts": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "description": "Host TCP ports the agent may connect to (e.g. local dev services). Accepts a single port string or an array of port strings." + }, + "allowHostServicePorts": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "description": "Named service ports on the host that the agent may connect to. Accepts a single port string or an array of port strings." + }, + "difcProxy": { + "type": "object", + "description": "DIFC (Data-in-Flight Control) proxy configuration.", + "additionalProperties": false, + "properties": { + "host": { + "type": "string", + "description": "DIFC proxy host." + }, + "caCert": { + "type": "string", + "description": "Path to the CA certificate for DIFC proxy TLS verification." + } + } + } + } + }, + "container": { + "type": "object", + "description": "Container and Docker configuration.", + "additionalProperties": false, + "properties": { + "memoryLimit": { + "type": "string", + "description": "Docker memory limit for the agent container (e.g. \"4g\", \"512m\"). Uses Docker memory limit syntax." + }, + "agentTimeout": { + "type": "integer", + "minimum": 1, + "description": "Maximum time (in minutes) the agent command is allowed to run." + }, + "enableDind": { + "type": "boolean", + "description": "Enable Docker-in-Docker support inside the agent container." + }, + "workDir": { + "type": "string", + "description": "Host path used as the AWF working directory for generated configs and logs. Defaults to a temporary directory." + }, + "containerWorkDir": { + "type": "string", + "description": "Working directory inside the agent container." + }, + "imageRegistry": { + "type": "string", + "description": "Container image registry to pull from. Defaults to \"ghcr.io/github/gh-aw-firewall\"." + }, + "imageTag": { + "type": "string", + "description": "Container image tag to use. Defaults to \"latest\"." + }, + "skipPull": { + "type": "boolean", + "description": "Skip pulling container images (use locally cached images)." + }, + "buildLocal": { + "type": "boolean", + "description": "Build container images from source instead of pulling from the registry." + }, + "agentImage": { + "type": "string", + "description": "Override the agent container image (e.g. for a GitHub Actions parity image)." + }, + "tty": { + "type": "boolean", + "description": "Allocate a pseudo-TTY for the agent container." + }, + "dockerHost": { + "type": "string", + "description": "Docker daemon socket or host to connect to (e.g. \"unix:///var/run/docker.sock\")." + } + } + }, + "environment": { + "type": "object", + "description": "Environment variable propagation into the agent container.", + "additionalProperties": false, + "properties": { + "envFile": { + "type": "string", + "description": "Path to a .env file whose variables are injected into the agent container." + }, + "envAll": { + "type": "boolean", + "description": "Forward all host environment variables into the agent container. Use with caution — may expose secrets." + }, + "excludeEnv": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Environment variable names to exclude when envAll is true." + } + } + }, + "logging": { + "type": "object", + "description": "Logging and diagnostics configuration.", + "additionalProperties": false, + "properties": { + "logLevel": { + "type": "string", + "enum": ["debug", "info", "warn", "error"], + "description": "Log verbosity level. Defaults to \"info\"." + }, + "diagnosticLogs": { + "type": "boolean", + "description": "Enable diagnostic logging (Squid access logs, iptables logs). Logs are written to the work directory." + }, + "auditDir": { + "type": "string", + "description": "Directory path for audit logs." + }, + "proxyLogsDir": { + "type": "string", + "description": "Directory path for Squid proxy access logs." + }, + "sessionStateDir": { + "type": "string", + "description": "Directory path for agent session state (e.g. conversation history). Set to \"/tmp/gh-aw/sandbox/agent/session-state\" for Copilot agent runs." + } + } + }, + "rateLimiting": { + "type": "object", + "description": "Egress rate limiting configuration.", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean", + "description": "Enable egress rate limiting." + }, + "requestsPerMinute": { + "type": "integer", + "minimum": 1, + "description": "Maximum number of HTTP requests per minute." + }, + "requestsPerHour": { + "type": "integer", + "minimum": 1, + "description": "Maximum number of HTTP requests per hour." + }, + "bytesPerMinute": { + "type": "integer", + "minimum": 1, + "description": "Maximum number of bytes transferred per minute." + } + } + } + }, + "$defs": { + "providerTarget": { + "type": "object", + "description": "API provider target override.", + "additionalProperties": false, + "properties": { + "host": { + "type": "string", + "description": "Override the provider API host." + }, + "basePath": { + "type": "string", + "description": "Override the provider API base path." + } + } + }, + "providerHostOnlyTarget": { + "type": "object", + "description": "API provider target override (host only; basePath not supported).", + "additionalProperties": false, + "properties": { + "host": { + "type": "string", + "description": "Override the provider API host." + } + } + } + } +} diff --git a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/basic-copilot.golden b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/basic-copilot.golden index 35f95868e54..28a697690a9 100644 --- a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/basic-copilot.golden +++ b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/basic-copilot.golden @@ -421,7 +421,7 @@ jobs: GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) export GH_AW_NODE_BIN (umask 177 && touch /tmp/gh-aw/agent-stdio.log) - printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.38/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.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","github.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.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","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true},"container":{"imageTag":"0.25.38"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.38/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.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","github.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.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","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"models":{"auto":["large"],"deep-research":["copilot/deep-research*","google/deep-research*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"]}},"container":{"imageTag":"0.25.38"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json # shellcheck disable=SC1003 sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log diff --git a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/playwright-cli-mode.golden b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/playwright-cli-mode.golden index bfd35448f37..b98f8b79660 100644 --- a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/playwright-cli-mode.golden +++ b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/playwright-cli-mode.golden @@ -435,7 +435,7 @@ jobs: GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) export GH_AW_NODE_BIN (umask 177 && touch /tmp/gh-aw/agent-stdio.log) - printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.38/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","cdn.playwright.dev","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","github.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.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","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true},"container":{"imageTag":"0.25.38"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.38/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.com","cdn.playwright.dev","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","github.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.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","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"models":{"auto":["large"],"deep-research":["copilot/deep-research*","google/deep-research*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"]}},"container":{"imageTag":"0.25.38"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json # shellcheck disable=SC1003 sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log diff --git a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/smoke-copilot.golden b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/smoke-copilot.golden index e6f0c0d176b..99cd2adfba2 100644 --- a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/smoke-copilot.golden +++ b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/smoke-copilot.golden @@ -667,7 +667,7 @@ jobs: GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) export GH_AW_NODE_BIN (umask 177 && touch /tmp/gh-aw/agent-stdio.log) - printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.38/awf-config.schema.json","network":{"allowDomains":["*.githubusercontent.com","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","docs.github.com","esm.sh","get.pnpm.io","github-cloud.githubusercontent.com","github-cloud.s3.amazonaws.com","github.blog","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","telemetry.vercel.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com","www.npmjs.com","www.npmjs.org","yarnpkg.com"]},"apiProxy":{"enabled":true},"container":{"imageTag":"0.25.38"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.38/awf-config.schema.json","network":{"allowDomains":["*.githubusercontent.com","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","docs.github.com","esm.sh","get.pnpm.io","github-cloud.githubusercontent.com","github-cloud.s3.amazonaws.com","github.blog","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","telemetry.vercel.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com","www.npmjs.com","www.npmjs.org","yarnpkg.com"]},"apiProxy":{"enabled":true,"models":{"auto":["large"],"deep-research":["copilot/deep-research*","google/deep-research*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"]}},"container":{"imageTag":"0.25.38"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json # shellcheck disable=SC1003 sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log diff --git a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/with-imports.golden b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/with-imports.golden index 187eb13741f..f9bb4f51947 100644 --- a/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/with-imports.golden +++ b/pkg/workflow/testdata/TestWasmGolden_CompileFixtures/with-imports.golden @@ -422,7 +422,7 @@ jobs: GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true) export GH_AW_NODE_BIN (umask 177 && touch /tmp/gh-aw/agent-stdio.log) - printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.38/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.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","github.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.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","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true},"container":{"imageTag":"0.25.38"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json + printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.38/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","api.snapcraft.io","archive.ubuntu.com","azure.archive.ubuntu.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","github.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.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","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"models":{"auto":["large"],"deep-research":["copilot/deep-research*","google/deep-research*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"]}},"container":{"imageTag":"0.25.38"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json # shellcheck disable=SC1003 sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log From 194aa91ae47bc9ada68884decddae5528f09d6f0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 09:48:42 +0000 Subject: [PATCH 3/3] fix: address code review - hard error on schema validation, fmt.Sprintf for URL, extract struct type - Return error from BuildAWFConfigJSON when generated config fails schema validation (prevents invalid configs from reaching AWF) - Use fmt.Sprintf for schema URL construction in getCompiledAWFConfigSchema for consistency with buildAWFConfigSchemaURL - Extract awfConfigModelsResult helper type to eliminate duplicate anonymous struct definition in TestBuildAWFConfigJSON_ModelsSection Agent-Logs-Url: https://github.com/github/gh-aw/sessions/c844f88b-f1eb-40c5-a162-fd5381ccfaad Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com> --- pkg/workflow/awf_config.go | 4 ++-- pkg/workflow/model_aliases_test.go | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/workflow/awf_config.go b/pkg/workflow/awf_config.go index a5943808a8f..85acbbe2751 100644 --- a/pkg/workflow/awf_config.go +++ b/pkg/workflow/awf_config.go @@ -81,7 +81,7 @@ func getCompiledAWFConfigSchema() (*jsonschema.Schema, error) { return } loader := jsonschema.NewCompiler() - schemaURL := "https://github.com/github/gh-aw-firewall/releases/download/" + string(constants.DefaultFirewallVersion) + "/awf-config.schema.json" + schemaURL := fmt.Sprintf("https://github.com/github/gh-aw-firewall/releases/download/%s/awf-config.schema.json", constants.DefaultFirewallVersion) if err := loader.AddResource(schemaURL, schemaDoc); err != nil { awfConfigSchemaCompileError = fmt.Errorf("failed to add AWF config schema resource: %w", err) return @@ -298,7 +298,7 @@ func BuildAWFConfigJSON(config AWFCommandConfig) (string, error) { awfConfigLog.Printf("AWF config JSON generated: %d bytes", len(jsonBytes)) if err := validateAWFConfigJSON(jsonStr); err != nil { - awfConfigLog.Printf("AWF config schema validation warning: %v", err) + return "", fmt.Errorf("generated AWF config failed schema validation: %w", err) } return jsonStr, nil diff --git a/pkg/workflow/model_aliases_test.go b/pkg/workflow/model_aliases_test.go index c8ecfa62683..42af60bd292 100644 --- a/pkg/workflow/model_aliases_test.go +++ b/pkg/workflow/model_aliases_test.go @@ -53,6 +53,14 @@ func TestBuiltinModelAliases(t *testing.T) { assert.NotEqual(t, aliases["sonnet"], aliases2["sonnet"], "BuiltinModelAliases should return a fresh copy each time") } +// awfConfigModelsResult is a helper type for parsing the apiProxy.models section +// from generated AWF config JSON in tests. +type awfConfigModelsResult struct { + APIProxy struct { + Models map[string][]string `json:"models"` + } `json:"apiProxy"` +} + // TestBuildAWFConfigJSON_ModelsSection verifies model alias behaviour in BuildAWFConfigJSON. // // Models are serialised under apiProxy.models per the AWF config schema (apiProxy.models @@ -74,11 +82,7 @@ func TestBuildAWFConfigJSON_ModelsSection(t *testing.T) { jsonStr, err := BuildAWFConfigJSON(config) require.NoError(t, err, "BuildAWFConfigJSON should not return an error") - var parsed struct { - APIProxy struct { - Models map[string][]string `json:"models"` - } `json:"apiProxy"` - } + var parsed awfConfigModelsResult require.NoError(t, json.Unmarshal([]byte(jsonStr), &parsed), "result must be valid JSON") // models must appear nested under apiProxy @@ -112,11 +116,7 @@ func TestBuildAWFConfigJSON_ModelsSection(t *testing.T) { jsonStr, err := BuildAWFConfigJSON(config) require.NoError(t, err, "BuildAWFConfigJSON should not return an error") - var parsed struct { - APIProxy struct { - Models map[string][]string `json:"models"` - } `json:"apiProxy"` - } + var parsed awfConfigModelsResult require.NoError(t, json.Unmarshal([]byte(jsonStr), &parsed), "result must be valid JSON") // models must appear nested under apiProxy