Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/dev.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/issue-classifier.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions .github/workflows/smoke-copilot-sdk.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .github/workflows/smoke-opencode.lock.yml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions pkg/workflow/claude_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions pkg/workflow/codex_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down
53 changes: 39 additions & 14 deletions pkg/workflow/compiler_safe_outputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
24 changes: 14 additions & 10 deletions pkg/workflow/compiler_safe_outputs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -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")
})
}
Expand Down Expand Up @@ -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
Expand All @@ -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")
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/compiler_yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions pkg/workflow/compiler_yaml_main_job.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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")
Expand All @@ -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")
Expand Down
6 changes: 3 additions & 3 deletions pkg/workflow/copilot_engine_execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/copilot_engine_installation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading
Loading