diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index e2c43ac795..3ae66badca 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -171,7 +171,7 @@ jobs: awf_version: "", awmg_version: "v0.1.4", steps: { - firewall: "" + firewall: "squid" }, created_at: new Date().toISOString() }; @@ -191,6 +191,8 @@ jobs: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.410 + - name: Install awf binary + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.18.0 - name: Determine automatic lockdown mode for GitHub MCP server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -202,7 +204,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-mcpg:v0.1.4 ghcr.io/github/github-mcp-server:v0.30.3 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.18.0 ghcr.io/github/gh-aw-firewall/squid:0.18.0 ghcr.io/github/gh-aw-mcpg:v0.1.4 ghcr.io/github/github-mcp-server:v0.30.3 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index 6879309bd6..06f97cb7eb 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -199,7 +199,7 @@ jobs: awf_version: "", awmg_version: "v0.1.4", steps: { - firewall: "" + firewall: "squid" }, created_at: new Date().toISOString() }; @@ -223,7 +223,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-mcpg:v0.1.4 ghcr.io/github/github-mcp-server:v0.30.3 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.18.0 ghcr.io/github/gh-aw-firewall/squid:0.18.0 ghcr.io/github/gh-aw-mcpg:v0.1.4 ghcr.io/github/github-mcp-server:v0.30.3 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs diff --git a/.github/workflows/smoke-copilot-sdk.lock.yml b/.github/workflows/smoke-copilot-sdk.lock.yml index d4a5e6c2ad..e0960f3529 100644 --- a/.github/workflows/smoke-copilot-sdk.lock.yml +++ b/.github/workflows/smoke-copilot-sdk.lock.yml @@ -236,7 +236,7 @@ jobs: awf_version: "", awmg_version: "v0.1.4", steps: { - firewall: "" + firewall: "squid" }, created_at: new Date().toISOString() }; @@ -256,6 +256,8 @@ jobs: COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - name: Install GitHub Copilot CLI run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.410 + - name: Install awf binary + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.18.0 - name: Determine automatic lockdown mode for GitHub MCP server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -267,7 +269,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-mcpg:v0.1.4 ghcr.io/github/github-mcp-server:v0.30.3 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.18.0 ghcr.io/github/gh-aw-firewall/squid:0.18.0 ghcr.io/github/gh-aw-mcpg:v0.1.4 ghcr.io/github/github-mcp-server:v0.30.3 node:lts-alpine - name: Install gh-aw extension env: GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/smoke-opencode.lock.yml b/.github/workflows/smoke-opencode.lock.yml index 45fb9c1284..e95bcdaf4d 100644 --- a/.github/workflows/smoke-opencode.lock.yml +++ b/.github/workflows/smoke-opencode.lock.yml @@ -209,7 +209,7 @@ jobs: awf_version: "", awmg_version: "v0.1.4", steps: { - firewall: "" + firewall: "squid" }, created_at: new Date().toISOString() }; @@ -233,7 +233,7 @@ jobs: const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-mcpg:v0.1.4 ghcr.io/github/github-mcp-server:v0.30.3 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.18.0 ghcr.io/github/gh-aw-firewall/squid:0.18.0 ghcr.io/github/gh-aw-mcpg:v0.1.4 ghcr.io/github/github-mcp-server:v0.30.3 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs diff --git a/pkg/workflow/claude_engine.go b/pkg/workflow/claude_engine.go index 4a4416054d..91b21dc853 100644 --- a/pkg/workflow/claude_engine.go +++ b/pkg/workflow/claude_engine.go @@ -110,7 +110,7 @@ func (e *ClaudeEngine) GetInstallationSteps(workflowData *WorkflowData) []GitHub } // Add AWF installation if firewall is enabled - if isFirewallEnabled(workflowData) { + if isSandboxEnabled(workflowData) { // Install AWF after Node.js setup but before Claude CLI installation firewallConfig := getFirewallConfig(workflowData) agentConfig := getAgentConfig(workflowData) @@ -141,7 +141,7 @@ func (e *ClaudeEngine) GetDeclaredOutputFiles() []string { // GetExecutionSteps returns the GitHub Actions steps for executing Claude func (e *ClaudeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile string) []GitHubActionStep { - claudeLog.Printf("Generating execution steps for Claude engine: workflow=%s, firewall=%v", workflowData.Name, isFirewallEnabled(workflowData)) + claudeLog.Printf("Generating execution steps for Claude engine: workflow=%s, firewall=%v", workflowData.Name, isSandboxEnabled(workflowData)) // Handle custom steps if they exist in engine config steps := InjectCustomEngineSteps(workflowData, e.convertStepToYAML) @@ -263,7 +263,7 @@ func (e *ClaudeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str // Build the full command based on whether firewall is enabled var command string - if isFirewallEnabled(workflowData) { + if isSandboxEnabled(workflowData) { // Build the AWF-wrapped command using helper function // Get allowed domains (Claude defaults + network permissions + HTTP MCP server URLs + runtime ecosystem domains) allowedDomains := GetClaudeAllowedDomainsWithToolsAndRuntimes(workflowData.NetworkPermissions, workflowData.Tools, workflowData.Runtimes) @@ -457,7 +457,7 @@ func (e *ClaudeEngine) GetSquidLogsSteps(workflowData *WorkflowData) []GitHubAct var steps []GitHubActionStep // Only add upload and parsing steps if firewall is enabled - if isFirewallEnabled(workflowData) { + if isSandboxEnabled(workflowData) { claudeLog.Printf("Adding Squid logs upload and parsing steps for workflow: %s", workflowData.Name) squidLogsUpload := generateSquidLogsUploadStep(workflowData.Name) diff --git a/pkg/workflow/codex_engine.go b/pkg/workflow/codex_engine.go index ee704318c8..7510d2bc59 100644 --- a/pkg/workflow/codex_engine.go +++ b/pkg/workflow/codex_engine.go @@ -92,7 +92,7 @@ func (e *CodexEngine) GetInstallationSteps(workflowData *WorkflowData) []GitHubA }, workflowData) // Add AWF installation step if firewall is enabled - if isFirewallEnabled(workflowData) { + if isSandboxEnabled(workflowData) { firewallConfig := getFirewallConfig(workflowData) agentConfig := getAgentConfig(workflowData) var awfVersion string @@ -127,7 +127,7 @@ func (e *CodexEngine) GetExecutionSteps(workflowData *WorkflowData, logFile stri if modelConfigured { model = workflowData.EngineConfig.Model } - firewallEnabled := isFirewallEnabled(workflowData) + firewallEnabled := isSandboxEnabled(workflowData) codexEngineLog.Printf("Building Codex execution steps: workflow=%s, model=%s, has_agent_file=%v, firewall=%v", workflowData.Name, model, workflowData.AgentFile != "", firewallEnabled) @@ -348,7 +348,7 @@ func (e *CodexEngine) GetSquidLogsSteps(workflowData *WorkflowData) []GitHubActi var steps []GitHubActionStep // Only add upload and parsing steps if firewall is enabled - if isFirewallEnabled(workflowData) { + if isSandboxEnabled(workflowData) { codexEngineLog.Printf("Adding Squid logs upload and parsing steps for workflow: %s", workflowData.Name) squidLogsUpload := generateSquidLogsUploadStep(workflowData.Name) diff --git a/pkg/workflow/compiler_safe_outputs.go b/pkg/workflow/compiler_safe_outputs.go index a915c415fb..fa169eacf1 100644 --- a/pkg/workflow/compiler_safe_outputs.go +++ b/pkg/workflow/compiler_safe_outputs.go @@ -315,10 +315,8 @@ func (c *Compiler) applyDefaultTools(tools map[string]any, safeOutputs *SafeOutp } // Enable edit and bash tools by default when sandbox is enabled - // The sandbox is enabled when: - // 1. Explicitly configured via sandbox.agent (awf/srt) - // 2. Auto-enabled by firewall default enablement (when network restrictions are present) - if isSandboxEnabled(sandboxConfig, networkPermissions) { + // The sandbox is enabled when explicitly configured via sandbox.agent + if isSandboxEnabledFromConfigs(sandboxConfig, networkPermissions) { compilerSafeOutputsLog.Print("Sandbox enabled, applying default edit and bash tools") // Add edit tool if not present @@ -455,26 +453,53 @@ func needsGitCommands(safeOutputs *SafeOutputsConfig) bool { return safeOutputs.CreatePullRequests != nil || safeOutputs.PushToPullRequestBranch != nil } -// isSandboxEnabled checks if the sandbox is enabled (either explicitly or auto-enabled) +// isSandboxEnabled checks if the sandbox/firewall is enabled for the workflow // Returns true when: -// - sandbox.agent is explicitly set to a sandbox type (awf, srt, etc.) -// - Firewall is auto-enabled (networkPermissions.Firewall is set and enabled) -// - SRT sandbox is enabled +// - sandbox.agent is explicitly set +// - Firewall is auto-enabled (network.firewall is set and enabled) // Returns false when: // - sandbox.agent is false (explicitly disabled) // - No sandbox configuration and no auto-enabled firewall -func isSandboxEnabled(sandboxConfig *SandboxConfig, networkPermissions *NetworkPermissions) bool { +func isSandboxEnabled(workflowData *WorkflowData) bool { + // Check if sandbox.agent: false (explicitly disabled) + if workflowData != nil && + workflowData.SandboxConfig != nil && + workflowData.SandboxConfig.Agent != nil && + workflowData.SandboxConfig.Agent.Disabled { + return false + } + + // Extract sandbox and network configs + var sandboxConfig *SandboxConfig + var networkPermissions *NetworkPermissions + if workflowData != nil { + sandboxConfig = workflowData.SandboxConfig + networkPermissions = workflowData.NetworkPermissions + } + + // Check if sandbox.agent is explicitly configured (AWF is the only sandbox type) + if sandboxConfig != nil && sandboxConfig.Agent != nil { + return true + } + + // Check if firewall is auto-enabled (AWF) via legacy network.firewall config + if networkPermissions != nil && networkPermissions.Firewall != nil && networkPermissions.Firewall.Enabled { + return true + } + + return false +} + +// isSandboxEnabledFromConfigs is a helper for code that has separate config parameters +func isSandboxEnabledFromConfigs(sandboxConfig *SandboxConfig, networkPermissions *NetworkPermissions) bool { // Check if sandbox.agent is explicitly disabled if sandboxConfig != nil && sandboxConfig.Agent != nil && sandboxConfig.Agent.Disabled { return false } - // Check if sandbox.agent is explicitly configured with a type + // Check if sandbox.agent is explicitly configured (AWF is the only sandbox type) if sandboxConfig != nil && sandboxConfig.Agent != nil { - agentType := getAgentType(sandboxConfig.Agent) - if isSupportedSandboxType(agentType) { - return true - } + return true } // Check if firewall is auto-enabled (AWF) diff --git a/pkg/workflow/compiler_safe_outputs_test.go b/pkg/workflow/compiler_safe_outputs_test.go index 0bcbb70edc..7b3d5ee560 100644 --- a/pkg/workflow/compiler_safe_outputs_test.go +++ b/pkg/workflow/compiler_safe_outputs_test.go @@ -905,16 +905,20 @@ func TestCompilerIsSandboxEnabled(t *testing.T) { expected: true, }, { - name: "legacy type field SRT", + name: "legacy type field converted to agent", sandboxConfig: &SandboxConfig{ - Type: SandboxTypeAWF, + Agent: &AgentSandboxConfig{ + Type: SandboxTypeAWF, + }, }, expected: true, }, { - name: "legacy type field runtime", + name: "legacy type field runtime converted to agent", sandboxConfig: &SandboxConfig{ - Type: SandboxTypeAWF, + Agent: &AgentSandboxConfig{ + Type: SandboxTypeAWF, + }, }, expected: true, }, @@ -952,19 +956,19 @@ func TestCompilerIsSandboxEnabled(t *testing.T) { expected: false, }, { - name: "unsupported sandbox type", + name: "agent configured without specific type", sandboxConfig: &SandboxConfig{ Agent: &AgentSandboxConfig{ - ID: "unknown", + Command: "custom-awf", }, }, - expected: false, + expected: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := isSandboxEnabled(tt.sandboxConfig, tt.networkPermissions) + result := isSandboxEnabledFromConfigs(tt.sandboxConfig, tt.networkPermissions) assert.Equal(t, tt.expected, result, "Sandbox detection mismatch") }) } @@ -1412,7 +1416,7 @@ func TestCompilerIsSandboxEnabledPrecedence(t *testing.T) { Firewall: &FirewallConfig{Enabled: true}, } - result := isSandboxEnabled(config, networkPerms) + result := isSandboxEnabledFromConfigs(config, networkPerms) assert.False(t, result, "Disabled flag should take precedence over all other settings") // Test that ID field takes precedence over Type field @@ -1423,7 +1427,7 @@ func TestCompilerIsSandboxEnabledPrecedence(t *testing.T) { }, } - result = isSandboxEnabled(config2, nil) + result = isSandboxEnabledFromConfigs(config2, nil) assert.True(t, result, "ID field should take precedence over Type field") } diff --git a/pkg/workflow/compiler_yaml.go b/pkg/workflow/compiler_yaml.go index 30b20cdbe7..eca9e012eb 100644 --- a/pkg/workflow/compiler_yaml.go +++ b/pkg/workflow/compiler_yaml.go @@ -525,7 +525,7 @@ func (c *Compiler) generateCreateAwInfo(yaml *strings.Builder, data *WorkflowDat // Determine firewall type firewallType := "" - if isFirewallEnabled(data) { + if isSandboxEnabled(data) { firewallType = "squid" } fmt.Fprintf(yaml, " firewall: \"%s\"\n", firewallType) diff --git a/pkg/workflow/compiler_yaml_main_job.go b/pkg/workflow/compiler_yaml_main_job.go index 633ff56262..e8dad49bde 100644 --- a/pkg/workflow/compiler_yaml_main_job.go +++ b/pkg/workflow/compiler_yaml_main_job.go @@ -347,7 +347,7 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat // Add firewall log parsing steps (but not upload - collected for unified upload) // For Copilot, Codex, and Claude engines if _, ok := engine.(*CopilotEngine); ok { - if isFirewallEnabled(data) { + if isSandboxEnabled(data) { firewallLogParsing := generateFirewallLogParsingStep(data.Name) for _, line := range firewallLogParsing { yaml.WriteString(line + "\n") @@ -357,7 +357,7 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat } } if _, ok := engine.(*CodexEngine); ok { - if isFirewallEnabled(data) { + if isSandboxEnabled(data) { firewallLogParsing := generateFirewallLogParsingStep(data.Name) for _, line := range firewallLogParsing { yaml.WriteString(line + "\n") @@ -367,7 +367,7 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat } } if _, ok := engine.(*ClaudeEngine); ok { - if isFirewallEnabled(data) { + if isSandboxEnabled(data) { firewallLogParsing := generateFirewallLogParsingStep(data.Name) for _, line := range firewallLogParsing { yaml.WriteString(line + "\n") diff --git a/pkg/workflow/copilot_engine_execution.go b/pkg/workflow/copilot_engine_execution.go index a6edbebf5b..8d838659a5 100644 --- a/pkg/workflow/copilot_engine_execution.go +++ b/pkg/workflow/copilot_engine_execution.go @@ -34,14 +34,14 @@ var copilotExecLog = logger.New("workflow:copilot_engine_execution") // GetExecutionSteps returns the GitHub Actions steps for executing GitHub Copilot CLI func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile string) []GitHubActionStep { - copilotExecLog.Printf("Generating execution steps for Copilot: workflow=%s, firewall=%v", workflowData.Name, isFirewallEnabled(workflowData)) + copilotExecLog.Printf("Generating execution steps for Copilot: workflow=%s, firewall=%v", workflowData.Name, isSandboxEnabled(workflowData)) // Handle custom steps if they exist in engine config steps := InjectCustomEngineSteps(workflowData, e.convertStepToYAML) // Build copilot CLI arguments based on configuration var copilotArgs []string - sandboxEnabled := isFirewallEnabled(workflowData) + sandboxEnabled := isSandboxEnabled(workflowData) if sandboxEnabled { // Simplified args for sandbox mode (AWF) copilotArgs = []string{"--add-dir", "/tmp/gh-aw/", "--log-level", "all", "--log-dir", logsFolder} @@ -195,7 +195,7 @@ func (e *CopilotEngine) GetExecutionSteps(workflowData *WorkflowData, logFile st // Conditionally wrap with sandbox (AWF only) var command string - if isFirewallEnabled(workflowData) { + if isSandboxEnabled(workflowData) { // Build AWF-wrapped command using helper function - no mkdir needed, AWF handles it // Get allowed domains (copilot defaults + network permissions + HTTP MCP server URLs + runtime ecosystem domains) allowedDomains := GetCopilotAllowedDomainsWithToolsAndRuntimes(workflowData.NetworkPermissions, workflowData.Tools, workflowData.Runtimes) diff --git a/pkg/workflow/copilot_engine_installation.go b/pkg/workflow/copilot_engine_installation.go index 7139c51388..8d03edc19c 100644 --- a/pkg/workflow/copilot_engine_installation.go +++ b/pkg/workflow/copilot_engine_installation.go @@ -89,7 +89,7 @@ func (e *CopilotEngine) GetInstallationSteps(workflowData *WorkflowData) []GitHu } // Add sandbox installation steps (AWF only) - if isFirewallEnabled(workflowData) { + if isSandboxEnabled(workflowData) { // Install AWF after Node.js setup but before Copilot CLI installation firewallConfig := getFirewallConfig(workflowData) agentConfig := getAgentConfig(workflowData) diff --git a/pkg/workflow/copilot_engine_test.go b/pkg/workflow/copilot_engine_test.go index 3cfd545a64..500a925f2c 100644 --- a/pkg/workflow/copilot_engine_test.go +++ b/pkg/workflow/copilot_engine_test.go @@ -1462,17 +1462,19 @@ func TestCopilotEnginePluginDiscoveryInSandboxMode(t *testing.T) { } } -func TestCopilotEnginePluginDiscoveryWithSRT(t *testing.T) { +func TestCopilotEnginePluginDiscoveryWithSandbox(t *testing.T) { engine := NewCopilotEngine() - // Test with SRT enabled (via sandbox config) + // Test with sandbox enabled workflowData := &WorkflowData{ Name: "test-workflow", PluginInfo: &PluginInfo{ Plugins: []string{"github/auto-agentics"}, }, SandboxConfig: &SandboxConfig{ - Type: "awf", + Agent: &AgentSandboxConfig{ + Type: "awf", + }, }, } steps := engine.GetExecutionSteps(workflowData, "/tmp/gh-aw/test.log") @@ -1484,8 +1486,8 @@ func TestCopilotEnginePluginDiscoveryWithSRT(t *testing.T) { stepContent := strings.Join([]string(steps[0]), "\n") - // Should include --add-dir /home/runner/.copilot/ when SRT is enabled with plugins + // Should include --add-dir /home/runner/.copilot/ when sandbox is enabled with plugins if !strings.Contains(stepContent, "--add-dir /home/runner/.copilot/") { - t.Errorf("Expected step to contain '--add-dir /home/runner/.copilot/' when plugins are declared with SRT enabled, but it was missing:\n%s", stepContent) + t.Errorf("Expected step to contain '--add-dir /home/runner/.copilot/' when plugins are declared with sandbox enabled, but it was missing:\n%s", stepContent) } } diff --git a/pkg/workflow/copilot_logs.go b/pkg/workflow/copilot_logs.go index c635bb1df8..8ae9d07b20 100644 --- a/pkg/workflow/copilot_logs.go +++ b/pkg/workflow/copilot_logs.go @@ -451,7 +451,7 @@ func (e *CopilotEngine) GetSquidLogsSteps(workflowData *WorkflowData) []GitHubAc var steps []GitHubActionStep // Only add upload and parsing steps if firewall is enabled - if isFirewallEnabled(workflowData) { + if isSandboxEnabled(workflowData) { copilotLogsLog.Printf("Adding Squid logs upload and parsing steps for workflow: %s", workflowData.Name) squidLogsUpload := generateSquidLogsUploadStep(workflowData.Name) diff --git a/pkg/workflow/copilot_sdk_engine.go b/pkg/workflow/copilot_sdk_engine.go index c7d21caff5..400e1cbac2 100644 --- a/pkg/workflow/copilot_sdk_engine.go +++ b/pkg/workflow/copilot_sdk_engine.go @@ -53,8 +53,9 @@ func NewCopilotSDKEngine() *CopilotSDKEngine { } // SupportsLLMGateway returns the LLM gateway port for Copilot SDK engine +// SupportsLLMGateway returns 0 because Copilot does not support LLM gateway func (e *CopilotSDKEngine) SupportsLLMGateway() int { - return constants.CopilotSDKLLMGatewayPort + return 0 } // GetDefaultDetectionModel returns the default model for threat detection diff --git a/pkg/workflow/copilot_sdk_engine_test.go b/pkg/workflow/copilot_sdk_engine_test.go index 2068bf25b8..69bf353ab6 100644 --- a/pkg/workflow/copilot_sdk_engine_test.go +++ b/pkg/workflow/copilot_sdk_engine_test.go @@ -32,7 +32,7 @@ func TestCopilotSDKEngineCapabilities(t *testing.T) { assert.False(t, engine.SupportsWebSearch()) assert.False(t, engine.SupportsFirewall(), "SDK mode doesn't use firewall") assert.False(t, engine.SupportsPlugins(), "SDK mode doesn't support plugins yet") - assert.Equal(t, constants.CopilotSDKLLMGatewayPort, engine.SupportsLLMGateway(), "Copilot SDK uses dedicated port for LLM gateway") + assert.Equal(t, 0, engine.SupportsLLMGateway(), "Copilot SDK does not support LLM gateway") } func TestCopilotSDKEngineGetRequiredSecretNames(t *testing.T) { diff --git a/pkg/workflow/docker.go b/pkg/workflow/docker.go index e206a72fcf..a8c97b021c 100644 --- a/pkg/workflow/docker.go +++ b/pkg/workflow/docker.go @@ -84,7 +84,7 @@ func collectDockerImages(tools map[string]any, workflowData *WorkflowData, actio // Collect AWF (firewall) container images when firewall is enabled // AWF uses three containers: squid (proxy), agent, and api-proxy (for engines with LLM gateway support) - if isFirewallEnabled(workflowData) { + if isSandboxEnabled(workflowData) { // Get the firewall version for image tags firewallConfig := getFirewallConfig(workflowData) awfImageTag := getAWFImageTag(firewallConfig) diff --git a/pkg/workflow/firewall.go b/pkg/workflow/firewall.go index 627208fd38..b93630d183 100644 --- a/pkg/workflow/firewall.go +++ b/pkg/workflow/firewall.go @@ -10,7 +10,6 @@ import ( var firewallLog = logger.New("workflow:firewall") // FirewallConfig represents AWF (gh-aw-firewall) configuration for network egress control. -// These settings are specific to the AWF sandbox and do not apply to Sandbox Runtime (SRT). type FirewallConfig struct { Enabled bool `yaml:"enabled,omitempty"` // Enable/disable AWF (default: true for copilot when network restrictions present) Version string `yaml:"version,omitempty"` // AWF version (empty = latest) @@ -21,35 +20,6 @@ type FirewallConfig struct { AllowURLs []string `yaml:"allow_urls,omitempty"` // AWF-only: URL patterns to allow for HTTPS (requires SSLBump), e.g., "https://github.com/githubnext/*" } -// isFirewallDisabledBySandboxAgent checks if the firewall is disabled via sandbox.agent: false -func isFirewallDisabledBySandboxAgent(workflowData *WorkflowData) bool { - return workflowData != nil && - workflowData.SandboxConfig != nil && - workflowData.SandboxConfig.Agent != nil && - workflowData.SandboxConfig.Agent.Disabled -} - -// isFirewallEnabled checks if AWF firewall is enabled for the workflow -// Firewall is enabled if network.firewall is explicitly set to true or an object -// Firewall is disabled if sandbox.agent is explicitly set to false -func isFirewallEnabled(workflowData *WorkflowData) bool { - // Check if sandbox.agent: false (new way to disable firewall) - if isFirewallDisabledBySandboxAgent(workflowData) { - firewallLog.Print("Firewall disabled via sandbox.agent: false") - return false - } - - // Check network.firewall configuration (deprecated) - if workflowData != nil && workflowData.NetworkPermissions != nil && workflowData.NetworkPermissions.Firewall != nil { - enabled := workflowData.NetworkPermissions.Firewall.Enabled - firewallLog.Printf("Firewall enabled check: %v", enabled) - return enabled - } - - firewallLog.Print("Firewall not configured, returning false") - return false -} - // getFirewallConfig returns the firewall configuration from network permissions func getFirewallConfig(workflowData *WorkflowData) *FirewallConfig { if workflowData == nil { @@ -80,13 +50,11 @@ func getAgentConfig(workflowData *WorkflowData) *AgentSandboxConfig { // enableFirewallByDefaultForCopilot enables firewall by default for copilot and codex engines // when network restrictions are present but no explicit firewall configuration exists -// and no SRT sandbox is configured (SRT and AWF are mutually exclusive) // and sandbox.agent is not explicitly set to false // // The firewall is enabled by default for copilot and codex UNLESS: // - allowed contains "*" (unrestricted network access) // - sandbox.agent is explicitly set to false -// - SRT sandbox is configured func enableFirewallByDefaultForCopilot(engineID string, networkPermissions *NetworkPermissions, sandboxConfig *SandboxConfig) { // Only apply to copilot and codex engines if engineID != "copilot" && engineID != "codex" { @@ -114,13 +82,11 @@ func enableFirewallByDefaultForClaude(engineID string, networkPermissions *Netwo // enableFirewallByDefaultForEngine enables firewall by default for a given engine // when network restrictions are present but no explicit firewall configuration exists -// and no SRT sandbox is configured (SRT and AWF are mutually exclusive) // and sandbox.agent is not explicitly set to false // // The firewall is enabled by default for the engine UNLESS: // - allowed contains "*" (unrestricted network access) // - sandbox.agent is explicitly set to false -// - SRT sandbox is configured (Copilot only) func enableFirewallByDefaultForEngine(engineID string, networkPermissions *NetworkPermissions, sandboxConfig *SandboxConfig) { // Check if network permissions exist if networkPermissions == nil { @@ -134,7 +100,7 @@ func enableFirewallByDefaultForEngine(engineID string, networkPermissions *Netwo return } - // SRT has been removed, all sandboxes should use AWF now + // All sandboxes use AWF now // This section is no longer needed // Check if firewall is already configured @@ -182,8 +148,7 @@ func getAWFImageTag(firewallConfig *FirewallConfig) string { // SSL Bump enables HTTPS content inspection (v0.9.0+), allowing URL path filtering // instead of domain-only filtering. // -// Note: These features are specific to AWF (Agent Workflow Firewall) and do not -// apply to Sandbox Runtime (SRT) or other sandbox configurations. +// Note: These features are specific to AWF (Agent Workflow Firewall). func getSSLBumpArgs(firewallConfig *FirewallConfig) []string { if firewallConfig == nil || !firewallConfig.SSLBump { return nil diff --git a/pkg/workflow/firewall_default_enablement_test.go b/pkg/workflow/firewall_default_enablement_test.go index a957edc1d7..e3df153282 100644 --- a/pkg/workflow/firewall_default_enablement_test.go +++ b/pkg/workflow/firewall_default_enablement_test.go @@ -473,7 +473,7 @@ func TestStrictModeFirewallValidation(t *testing.T) { } }) - t.Run("strict mode skips validation when SRT is enabled", func(t *testing.T) { + t.Run("strict mode skips validation when sandbox is enabled", func(t *testing.T) { compiler := NewCompiler() compiler.SetStrictMode(true) @@ -484,12 +484,14 @@ func TestStrictModeFirewallValidation(t *testing.T) { } sandboxConfig := &SandboxConfig{ - Type: SandboxTypeAWF, + Agent: &AgentSandboxConfig{ + Type: SandboxTypeAWF, + }, } err := compiler.validateStrictFirewall("copilot", networkPerms, sandboxConfig) if err != nil { - t.Errorf("Expected no error when SRT is enabled, got: %v", err) + t.Errorf("Expected no error when sandbox is enabled, got: %v", err) } }) diff --git a/pkg/workflow/sandbox.go b/pkg/workflow/sandbox.go index ac2b7e83f0..df0cef864d 100644 --- a/pkg/workflow/sandbox.go +++ b/pkg/workflow/sandbox.go @@ -1,7 +1,7 @@ // This file provides sandbox configuration for agentic workflows. // // This file handles: -// - Sandbox type definitions (AWF, SRT) +// - Sandbox type definitions (AWF) // - Sandbox configuration structures and parsing // - Sandbox runtime config generation // @@ -28,25 +28,25 @@ const ( ) // SandboxConfig represents the top-level sandbox configuration from front matter -// New format: { agent: "awf"|"srt"|{type, config}, mcp: {port, command, ...} } -// Legacy format: "default"|"sandbox-runtime" or { type, config } +// New format: { agent: "awf"|{id, config}, mcp: {port, command, ...} } +// Legacy format: "default"|"awf" or { type, config } type SandboxConfig struct { // New fields Agent *AgentSandboxConfig `yaml:"agent,omitempty"` // Agent sandbox configuration MCP *MCPGatewayRuntimeConfig `yaml:"mcp,omitempty"` // MCP gateway configuration // Legacy fields (for backward compatibility) - Type SandboxType `yaml:"type,omitempty"` // Sandbox type: "default" or "sandbox-runtime" + Type SandboxType `yaml:"type,omitempty"` // Sandbox type: "default" or "awf" Config *SandboxRuntimeConfig `yaml:"config,omitempty"` // Custom SRT config (optional) } // AgentSandboxConfig represents the agent sandbox configuration type AgentSandboxConfig struct { - ID string `yaml:"id,omitempty"` // Agent ID: "awf" or "srt" (replaces Type in new object format) - Type SandboxType `yaml:"type,omitempty"` // Sandbox type: "awf" or "srt" (legacy, use ID instead) + ID string `yaml:"id,omitempty"` // Agent ID: "awf" (AWF is the only supported sandbox) + Type SandboxType `yaml:"type,omitempty"` // Sandbox type: "awf" (legacy field, use ID instead) Disabled bool `yaml:"-"` // True when agent is explicitly set to false (disables firewall). This is a runtime flag, not serialized to YAML. - Config *SandboxRuntimeConfig `yaml:"config,omitempty"` // Custom SRT config (optional) - Command string `yaml:"command,omitempty"` // Custom command to replace AWF or SRT installation + Config *SandboxRuntimeConfig `yaml:"config,omitempty"` // Deprecated: Custom sandbox config (no longer used) + Command string `yaml:"command,omitempty"` // Custom command to replace AWF installation Args []string `yaml:"args,omitempty"` // Additional arguments to append to the command Env map[string]string `yaml:"env,omitempty"` // Environment variables to set on the step Mounts []string `yaml:"mounts,omitempty"` // Container mounts to add for AWF (format: "source:dest:mode") @@ -83,6 +83,7 @@ type SRTFilesystemConfig struct { // getAgentType returns the effective agent type from AgentSandboxConfig // Prefers ID field (new format) over Type field (legacy) +// Returns "awf" for AWF sandbox, "default" for default, or "" if not set func getAgentType(agent *AgentSandboxConfig) SandboxType { if agent == nil { return "" @@ -96,31 +97,32 @@ func getAgentType(agent *AgentSandboxConfig) SandboxType { } // isSupportedSandboxType checks if a sandbox type is valid/supported +// Only "awf" and "default" (alias for awf) are supported func isSupportedSandboxType(sandboxType SandboxType) bool { return sandboxType == SandboxTypeAWF || sandboxType == SandboxTypeDefault } -// migrateSRTToAWF converts any SRT sandbox configuration to AWF -// This is a codemod that automatically migrates workflows from the deprecated SRT to AWF +// migrateSRTToAWF converts any deprecated sandbox configuration to AWF +// This is a codemod that automatically migrates workflows to use AWF func migrateSRTToAWF(sandboxConfig *SandboxConfig) *SandboxConfig { if sandboxConfig == nil { return nil } - // Migrate legacy Type field from SRT/sandbox-runtime to AWF/default + // Migrate legacy Type field from deprecated values to AWF if sandboxConfig.Type == "srt" || sandboxConfig.Type == "sandbox-runtime" { sandboxLog.Printf("Migrating legacy sandbox type from %s to awf", sandboxConfig.Type) sandboxConfig.Type = SandboxTypeAWF } - // Migrate Agent.Type field from SRT to AWF + // Migrate Agent.Type field from deprecated values to AWF if sandboxConfig.Agent != nil { if sandboxConfig.Agent.Type == "srt" || sandboxConfig.Agent.Type == "sandbox-runtime" { sandboxLog.Printf("Migrating agent type from %s to awf", sandboxConfig.Agent.Type) sandboxConfig.Agent.Type = SandboxTypeAWF } - // Migrate Agent.ID field from SRT to AWF + // Migrate Agent.ID field from deprecated values to AWF if sandboxConfig.Agent.ID == "srt" || sandboxConfig.Agent.ID == "sandbox-runtime" { sandboxLog.Printf("Migrating agent ID from %s to awf", sandboxConfig.Agent.ID) sandboxConfig.Agent.ID = "awf" @@ -131,10 +133,10 @@ func migrateSRTToAWF(sandboxConfig *SandboxConfig) *SandboxConfig { } // applySandboxDefaults applies default values to sandbox configuration -// If no sandbox config exists, creates one with awf as default agent -// If sandbox config exists but has no agent, sets agent to awf (unless agent is explicitly disabled) +// If no sandbox config exists, creates one with AWF as default agent +// If sandbox config exists but has no agent, sets agent to AWF (unless agent is explicitly disabled) func applySandboxDefaults(sandboxConfig *SandboxConfig, engineConfig *EngineConfig) *SandboxConfig { - // First, migrate any SRT references to AWF (codemod) + // First, migrate any deprecated references to AWF (codemod) sandboxConfig = migrateSRTToAWF(sandboxConfig) // If agent sandbox is explicitly disabled (sandbox.agent: false), preserve that setting @@ -143,7 +145,7 @@ func applySandboxDefaults(sandboxConfig *SandboxConfig, engineConfig *EngineConf return sandboxConfig } - // If no sandbox config exists, create one with awf as default + // If no sandbox config exists, create one with AWF as default if sandboxConfig == nil { sandboxLog.Print("No sandbox config found, creating default with agent: awf") return &SandboxConfig{ @@ -153,14 +155,14 @@ func applySandboxDefaults(sandboxConfig *SandboxConfig, engineConfig *EngineConf } } - // If sandbox config exists with legacy Type field set, don't override with awf default + // If sandbox config exists with legacy Type field set, don't override with AWF default // The legacy Type field indicates explicit sandbox configuration if sandboxConfig.Type != "" { sandboxLog.Printf("Sandbox config uses legacy Type field: %s, preserving it", sandboxConfig.Type) return sandboxConfig } - // If sandbox config exists but has no agent, set agent to awf + // If sandbox config exists but has no agent, set agent to AWF if sandboxConfig.Agent == nil { sandboxLog.Print("Sandbox config exists without agent, setting default agent: awf") sandboxConfig.Agent = &AgentSandboxConfig{ diff --git a/pkg/workflow/sandbox_custom_agent_test.go b/pkg/workflow/sandbox_custom_agent_test.go index 7e37745e7f..028969facc 100644 --- a/pkg/workflow/sandbox_custom_agent_test.go +++ b/pkg/workflow/sandbox_custom_agent_test.go @@ -248,9 +248,9 @@ sandbox: } }) - t.Run("custom command and args for SRT", func(t *testing.T) { + t.Run("custom command and args for AWF", func(t *testing.T) { // Create temp directory for test - tmpDir, err := os.MkdirTemp("", "custom-srt-test") + tmpDir, err := os.MkdirTemp("", "custom-awf-test") if err != nil { t.Fatal(err) } @@ -260,21 +260,19 @@ sandbox: on: workflow_dispatch: engine: copilot -features: - sandbox-runtime: true sandbox: agent: - id: srt - command: "custom-srt-wrapper" + id: awf + command: "custom-awf-wrapper" args: - - "--custom-srt-arg" + - "--custom-awf-arg" - "--debug" env: - SRT_CUSTOM_VAR: "test_value" - SRT_DEBUG: "true" + AWF_CUSTOM_VAR: "test_value" + AWF_DEBUG: "true" --- -# Test Custom SRT +# Test Custom AWF ` testFile := filepath.Join(tmpDir, "test-workflow.md") @@ -300,30 +298,30 @@ sandbox: } lockStr := string(lockContent) - // Verify custom SRT command is used - if !strings.Contains(lockStr, "custom-srt-wrapper") { - t.Error("Expected custom SRT command 'custom-srt-wrapper' in compiled workflow") + // Verify custom AWF command is used + if !strings.Contains(lockStr, "custom-awf-wrapper") { + t.Error("Expected custom AWF command 'custom-awf-wrapper' in compiled workflow") } // Verify custom args are included - if !strings.Contains(lockStr, "--custom-srt-arg") { - t.Error("Expected custom arg '--custom-srt-arg' in compiled workflow") + if !strings.Contains(lockStr, "--custom-awf-arg") { + t.Error("Expected custom arg '--custom-awf-arg' in compiled workflow") } if !strings.Contains(lockStr, "--debug") { t.Error("Expected custom arg '--debug' in compiled workflow") } // Verify custom env is included - if !strings.Contains(lockStr, "SRT_CUSTOM_VAR: test_value") { - t.Error("Expected custom env 'SRT_CUSTOM_VAR: test_value' in compiled workflow") + if !strings.Contains(lockStr, "AWF_CUSTOM_VAR: test_value") { + t.Error("Expected custom env 'AWF_CUSTOM_VAR: test_value' in compiled workflow") } - if !strings.Contains(lockStr, "SRT_DEBUG: true") { - t.Error("Expected custom env 'SRT_DEBUG: true' in compiled workflow") + if !strings.Contains(lockStr, "AWF_DEBUG: true") { + t.Error("Expected custom env 'AWF_DEBUG: true' in compiled workflow") } // Verify installation steps were skipped - if strings.Contains(lockStr, "Install Sandbox Runtime") { - t.Error("Expected SRT installation step to be skipped when custom command is specified") + if strings.Contains(lockStr, "Install AWF") { + t.Error("Expected AWF installation step to be skipped when custom command is specified") } }) } diff --git a/pkg/workflow/sandbox_test.go b/pkg/workflow/sandbox_test.go index 591c6009e0..41de09faa5 100644 --- a/pkg/workflow/sandbox_test.go +++ b/pkg/workflow/sandbox_test.go @@ -34,6 +34,11 @@ func TestValidateSandboxConfig(t *testing.T) { Type: SandboxTypeAWF, }, }, + Tools: map[string]any{ + "github": map[string]any{ + "mode": "remote", + }, + }, }, }, } diff --git a/pkg/workflow/strict_mode_llm_gateway_test.go b/pkg/workflow/strict_mode_llm_gateway_test.go index b59f02df3b..d3cf6222e7 100644 --- a/pkg/workflow/strict_mode_llm_gateway_test.go +++ b/pkg/workflow/strict_mode_llm_gateway_test.go @@ -315,8 +315,8 @@ func TestSupportsLLMGateway(t *testing.T) { }, { engineID: "copilot-sdk", - expectedPort: constants.CopilotSDKLLMGatewayPort, - description: "Copilot SDK engine uses dedicated port for LLM gateway", + expectedPort: 0, + description: "Copilot SDK engine does not support LLM gateway", }, { engineID: "copilot", diff --git a/pkg/workflow/strict_mode_validation.go b/pkg/workflow/strict_mode_validation.go index 6ffdb15050..0d5c7d54f8 100644 --- a/pkg/workflow/strict_mode_validation.go +++ b/pkg/workflow/strict_mode_validation.go @@ -408,7 +408,7 @@ func (c *Compiler) validateStrictFirewall(engineID string, networkPermissions *N return nil } - // SRT has been removed - all sandboxes use AWF now + // All sandboxes use AWF now // This check is no longer needed // If network permissions don't exist, that's fine (will default to "defaults") @@ -426,6 +426,12 @@ func (c *Compiler) validateStrictFirewall(engineID string, networkPermissions *N } } + // Check if sandbox is enabled - if so, firewall will be auto-enabled + if isSandboxEnabledFromConfigs(sandboxConfig, networkPermissions) { + strictModeValidationLog.Printf("Sandbox enabled, firewall validation passed") + return nil + } + // At this point, we have network domains (or defaults) and copilot/codex engine // In strict mode, firewall MUST be enabled if networkPermissions.Firewall == nil || !networkPermissions.Firewall.Enabled {