From 1aaf8d6aa2b73d015f9f95a488ae7610e3f99ec1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Jun 2026 20:30:41 +0000 Subject: [PATCH 1/2] Require dangerously-disable-sandbox-agent feature flag for sandbox.agent: false Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/constants/feature_constants.go | 10 ++++ pkg/workflow/compiler_validators_test.go | 3 ++ pkg/workflow/importable_tools_test.go | 6 +++ .../pull_request_target_validation_test.go | 8 ++++ pkg/workflow/sandbox_agent_false_test.go | 47 +++++++++++++++++-- pkg/workflow/sandbox_validation.go | 16 +++++-- pkg/workflow/workflow_run_validation_test.go | 14 ++++++ 7 files changed, 95 insertions(+), 9 deletions(-) diff --git a/pkg/constants/feature_constants.go b/pkg/constants/feature_constants.go index 5c8ce42b9d6..59587f0f792 100644 --- a/pkg/constants/feature_constants.go +++ b/pkg/constants/feature_constants.go @@ -76,4 +76,14 @@ const ( // features: // group-concurrency-queue: false GroupConcurrencyQueueFeatureFlag FeatureFlag = "group-concurrency-queue" + // DangerouslyDisableSandboxAgentFeatureFlag is required to allow sandbox.agent: false. + // Without this flag, setting sandbox.agent to false raises a validation error. + // This flag is intentionally named with "dangerously" to make the security + // implications explicit and visible in the workflow frontmatter. + // + // Workflow frontmatter usage: + // + // features: + // dangerously-disable-sandbox-agent: true + DangerouslyDisableSandboxAgentFeatureFlag FeatureFlag = "dangerously-disable-sandbox-agent" ) diff --git a/pkg/workflow/compiler_validators_test.go b/pkg/workflow/compiler_validators_test.go index 656ec2417ad..340b24f3729 100644 --- a/pkg/workflow/compiler_validators_test.go +++ b/pkg/workflow/compiler_validators_test.go @@ -384,6 +384,9 @@ func TestValidateToolConfiguration_EmitsSandboxWarningBeforeThreatDetectionError workflowData := &WorkflowData{ Name: "Test", + Features: map[string]any{ + "dangerously-disable-sandbox-agent": true, + }, SandboxConfig: &SandboxConfig{ Agent: &AgentSandboxConfig{Disabled: true}, }, diff --git a/pkg/workflow/importable_tools_test.go b/pkg/workflow/importable_tools_test.go index 211c3c751ca..364f380da4e 100644 --- a/pkg/workflow/importable_tools_test.go +++ b/pkg/workflow/importable_tools_test.go @@ -801,6 +801,8 @@ permissions: issues: read tools: bash: true +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false imports: @@ -890,6 +892,8 @@ strict: false permissions: contents: read issues: read +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false imports: @@ -962,6 +966,8 @@ permissions: issues: read tools: github: false +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false imports: diff --git a/pkg/workflow/pull_request_target_validation_test.go b/pkg/workflow/pull_request_target_validation_test.go index bcd5bd051b1..3668bdc4a4e 100644 --- a/pkg/workflow/pull_request_target_validation_test.go +++ b/pkg/workflow/pull_request_target_validation_test.go @@ -36,6 +36,8 @@ on: types: [opened] tools: github: false +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false checkout: false @@ -58,6 +60,8 @@ on: types: [opened] tools: github: false +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false --- @@ -103,6 +107,8 @@ on: types: [opened] tools: github: false +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false --- @@ -124,6 +130,8 @@ on: branches: [main] tools: github: false +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false --- diff --git a/pkg/workflow/sandbox_agent_false_test.go b/pkg/workflow/sandbox_agent_false_test.go index bd8a914dc0e..dbd03d29802 100644 --- a/pkg/workflow/sandbox_agent_false_test.go +++ b/pkg/workflow/sandbox_agent_false_test.go @@ -10,7 +10,7 @@ import ( ) func TestSandboxAgentMandatory(t *testing.T) { - t.Run("sandbox.agent: false is accepted and disables agent sandbox", func(t *testing.T) { + t.Run("sandbox.agent: false with feature flag is accepted and disables agent sandbox", func(t *testing.T) { // Create temp directory for test workflows workflowsDir := t.TempDir() @@ -20,13 +20,15 @@ network: allowed: - defaults - github.com +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false strict: false on: workflow_dispatch --- -Test workflow to verify sandbox.agent: false is accepted and disables agent sandbox. +Test workflow to verify sandbox.agent: false is accepted when the feature flag is set. ` workflowPath := filepath.Join(workflowsDir, "test-agent-false.md") @@ -39,9 +41,9 @@ Test workflow to verify sandbox.agent: false is accepted and disables agent sand compiler := NewCompiler() compiler.SetSkipValidation(true) - // Should succeed in non-strict mode + // Should succeed when the feature flag is set if err := compiler.CompileWorkflow(workflowPath); err != nil { - t.Fatalf("Expected compilation to succeed with sandbox.agent: false in non-strict mode, but got error: %v", err) + t.Fatalf("Expected compilation to succeed with sandbox.agent: false and feature flag, but got error: %v", err) } // Read the compiled workflow @@ -157,6 +159,43 @@ Test workflow to verify default sandbox.agent behavior (awf). }) } +func TestSandboxAgentFalseRequiresFeatureFlag(t *testing.T) { + t.Run("sandbox.agent: false without feature flag is rejected", func(t *testing.T) { + workflowsDir := t.TempDir() + + markdown := `--- +engine: copilot +network: + allowed: + - defaults + - github.com +sandbox: + agent: false +strict: false +on: workflow_dispatch +--- + +Test workflow to verify sandbox.agent: false is rejected without the feature flag. +` + + workflowPath := filepath.Join(workflowsDir, "test-agent-false-no-flag.md") + err := os.WriteFile(workflowPath, []byte(markdown), 0644) + if err != nil { + t.Fatalf("Failed to write workflow file: %v", err) + } + + compiler := NewCompiler() + + err = compiler.CompileWorkflow(workflowPath) + if err == nil { + t.Fatal("Expected compilation to fail when sandbox.agent: false without feature flag, but got nil error") + } + if !strings.Contains(err.Error(), "dangerously-disable-sandbox-agent") { + t.Fatalf("Expected error to reference 'dangerously-disable-sandbox-agent', got: %v", err) + } + }) +} + func TestNetworkFirewallFrontmatterRejected(t *testing.T) { t.Run("network.firewall is rejected by schema", func(t *testing.T) { // Create temp directory for test workflows diff --git a/pkg/workflow/sandbox_validation.go b/pkg/workflow/sandbox_validation.go index 07dd7ee7b47..0a8e26af9ce 100644 --- a/pkg/workflow/sandbox_validation.go +++ b/pkg/workflow/sandbox_validation.go @@ -76,12 +76,18 @@ func validateSandboxConfig(workflowData *WorkflowData) error { sandboxConfig := workflowData.SandboxConfig // Check if sandbox.agent: false was specified - // In non-strict mode, this is allowed (with a warning shown at compile time) - // The strict mode check happens in validateStrictFirewall() + // This requires the "dangerously-disable-sandbox-agent" feature flag to be enabled. + // Without the feature flag, setting sandbox.agent: false is a validation error. if sandboxConfig.Agent != nil && sandboxConfig.Agent.Disabled { - // sandbox.agent: false is allowed in non-strict mode, so we don't error here - // The warning is emitted in compiler.go - sandboxValidationLog.Print("sandbox.agent: false detected, will be validated by strict mode check") + if !isFeatureEnabled(constants.DangerouslyDisableSandboxAgentFeatureFlag, workflowData) { + return NewValidationError( + "sandbox.agent", + "false", + "disabling the agent sandbox requires the 'dangerously-disable-sandbox-agent' feature flag", + fmt.Sprintf("Add the feature flag to your workflow frontmatter:\n\nfeatures:\n dangerously-disable-sandbox-agent: true\nsandbox:\n agent: false\n\nSee: %s", constants.DocsSandboxURL), + ) + } + sandboxValidationLog.Print("sandbox.agent: false permitted by dangerously-disable-sandbox-agent feature flag") } // Validate mounts syntax if specified in agent config diff --git a/pkg/workflow/workflow_run_validation_test.go b/pkg/workflow/workflow_run_validation_test.go index 37a3dfa2750..e200ba46d71 100644 --- a/pkg/workflow/workflow_run_validation_test.go +++ b/pkg/workflow/workflow_run_validation_test.go @@ -36,6 +36,8 @@ on: types: [completed] tools: github: false +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false --- @@ -81,6 +83,8 @@ on: - develop tools: github: false +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false --- @@ -186,6 +190,8 @@ on: branches: [main] tools: github: false +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false --- @@ -210,6 +216,8 @@ on: types: [completed] tools: github: false +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false --- @@ -233,6 +241,8 @@ on: branches: [] tools: github: false +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false --- @@ -303,6 +313,8 @@ strict: false on: push tools: github: false +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false --- @@ -323,6 +335,8 @@ on: types: [completed] tools: github: false +features: + dangerously-disable-sandbox-agent: true sandbox: agent: false --- From b24b4df66b905622486763976280e45fe14bb2d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 9 Jun 2026 21:08:26 +0000 Subject: [PATCH 2/2] Use DangerouslyDisableSandboxAgentFeatureFlag constant in error messages; remove misleading SetSkipValidation(true) from feature-flag test Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/sandbox_agent_false_test.go | 4 ++-- pkg/workflow/sandbox_validation.go | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/workflow/sandbox_agent_false_test.go b/pkg/workflow/sandbox_agent_false_test.go index dbd03d29802..7fc26de1a23 100644 --- a/pkg/workflow/sandbox_agent_false_test.go +++ b/pkg/workflow/sandbox_agent_false_test.go @@ -37,9 +37,9 @@ Test workflow to verify sandbox.agent: false is accepted when the feature flag i t.Fatalf("Failed to write workflow file: %v", err) } - // Compile the workflow + // Compile the workflow (validation runs unconditionally; validateSandboxConfig + // is not gated by skipValidation, so this exercises the feature-flag check) compiler := NewCompiler() - compiler.SetSkipValidation(true) // Should succeed when the feature flag is set if err := compiler.CompileWorkflow(workflowPath); err != nil { diff --git a/pkg/workflow/sandbox_validation.go b/pkg/workflow/sandbox_validation.go index 0a8e26af9ce..c606fe276ac 100644 --- a/pkg/workflow/sandbox_validation.go +++ b/pkg/workflow/sandbox_validation.go @@ -80,14 +80,15 @@ func validateSandboxConfig(workflowData *WorkflowData) error { // Without the feature flag, setting sandbox.agent: false is a validation error. if sandboxConfig.Agent != nil && sandboxConfig.Agent.Disabled { if !isFeatureEnabled(constants.DangerouslyDisableSandboxAgentFeatureFlag, workflowData) { + flag := string(constants.DangerouslyDisableSandboxAgentFeatureFlag) return NewValidationError( "sandbox.agent", "false", - "disabling the agent sandbox requires the 'dangerously-disable-sandbox-agent' feature flag", - fmt.Sprintf("Add the feature flag to your workflow frontmatter:\n\nfeatures:\n dangerously-disable-sandbox-agent: true\nsandbox:\n agent: false\n\nSee: %s", constants.DocsSandboxURL), + fmt.Sprintf("disabling the agent sandbox requires the '%s' feature flag", flag), + fmt.Sprintf("Add the feature flag to your workflow frontmatter:\n\nfeatures:\n %s: true\nsandbox:\n agent: false\n\nSee: %s", flag, constants.DocsSandboxURL), ) } - sandboxValidationLog.Print("sandbox.agent: false permitted by dangerously-disable-sandbox-agent feature flag") + sandboxValidationLog.Printf("sandbox.agent: false permitted by %s feature flag", constants.DangerouslyDisableSandboxAgentFeatureFlag) } // Validate mounts syntax if specified in agent config