Skip to content
Merged
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
24 changes: 12 additions & 12 deletions .github/workflows/stale-pr-cleanup.lock.yml

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

18 changes: 5 additions & 13 deletions pkg/workflow/strict_mode_sandbox_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
package workflow

import (
"errors"
"fmt"
)

Expand All @@ -29,8 +28,7 @@ func internalSandboxFieldError(fieldPath string) error {
// - sandbox.mcp.container, sandbox.mcp.version, sandbox.mcp.entrypoint,
// sandbox.mcp.args, sandbox.mcp.entrypointArgs (MCP gateway customization)
//
// Additionally, a sandbox.agent object without an explicit 'id' field is rejected in
// strict mode: users must be unambiguous about which sandbox they are enabling.
// A sandbox.agent object without an explicit 'id' is explicitly set to AWF in strict mode.
func (c *Compiler) validateStrictSandboxCustomization(sandboxConfig *SandboxConfig) error {
if !c.strictMode {
strictModeValidationLog.Printf("Strict mode disabled, skipping sandbox customization validation")
Expand All @@ -43,17 +41,11 @@ func (c *Compiler) validateStrictSandboxCustomization(sandboxConfig *SandboxConf

// Check agent sandbox internal fields
if agent := sandboxConfig.Agent; agent != nil {
// In strict mode, sandbox.agent must carry an explicit type/id so the sandbox
// configuration is unambiguous. A bare object (e.g. { version: "v0.25.29" }
// with no id) would silently default to AWF in non-strict builds but that
// implicit defaulting is not acceptable in strict mode.
// In strict mode, if sandbox.agent has no id/type set, explicitly default it to AWF
// so the sandbox configuration is always unambiguous.
if !agent.Disabled && !isSupportedSandboxType(getAgentType(agent)) {
return errors.New(
"strict mode: 'sandbox.agent' must specify an explicit 'id' (e.g., id: awf). " +
"A sandbox agent without an 'id' is ambiguous and not allowed in strict mode. " +
"Add 'id: awf' to your sandbox.agent configuration. " +
"See: https://github.github.com/gh-aw/reference/sandbox/",
)
strictModeValidationLog.Printf("sandbox.agent has no id/type in strict mode, defaulting to awf")
agent.Type = SandboxTypeAWF
}

if agent.Command != "" {
Expand Down
50 changes: 40 additions & 10 deletions pkg/workflow/strict_mode_sandbox_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,26 +134,22 @@ func TestValidateStrictSandboxCustomization(t *testing.T) {
expectError: false,
},
{
// A bare sandbox.agent object with no id/type is ambiguous and must be
// rejected in strict mode. Users must write id: awf explicitly.
name: "sandbox.agent without id is rejected in strict mode",
// A bare sandbox.agent object with no id/type is explicitly set to AWF in strict mode.
name: "sandbox.agent without id is allowed in strict mode (defaults to awf)",
sandbox: &SandboxConfig{
Agent: &AgentSandboxConfig{
Version: "v0.25.29",
},
},
expectError: true,
errorMsg: "strict mode: 'sandbox.agent' must specify an explicit 'id'",
expectError: false,
},
{
// An empty AgentSandboxConfig (no id, no type, no version) is equally
// ambiguous and must be rejected in strict mode.
name: "empty sandbox.agent is rejected in strict mode",
// An empty AgentSandboxConfig (no id, no type, no version) is explicitly set to AWF in strict mode.
name: "empty sandbox.agent is allowed in strict mode (defaults to awf)",
sandbox: &SandboxConfig{
Agent: &AgentSandboxConfig{},
},
expectError: true,
errorMsg: "strict mode: 'sandbox.agent' must specify an explicit 'id'",
expectError: false,
},
{
// sandbox.agent: false (Disabled) is handled by validateStrictFirewall, not here.
Expand Down Expand Up @@ -215,3 +211,37 @@ func TestValidateStrictSandboxCustomizationNonStrictMode(t *testing.T) {
t.Errorf("Expected non-strict mode to allow all sandbox fields, got error: %v", err)
}
}

// TestValidateStrictSandboxCustomizationSetsAWFDefault verifies that in strict mode
// a sandbox.agent with no id/type is explicitly set to AWF.
func TestValidateStrictSandboxCustomizationSetsAWFDefault(t *testing.T) {
tests := []struct {
name string
agent *AgentSandboxConfig
}{
{
name: "version-only agent gets AWF type",
agent: &AgentSandboxConfig{Version: "v0.25.29"},
},
{
name: "empty agent gets AWF type",
agent: &AgentSandboxConfig{},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
compiler := NewCompiler()
compiler.strictMode = true

sandbox := &SandboxConfig{Agent: tt.agent}
err := compiler.validateStrictSandboxCustomization(sandbox)
if err != nil {
t.Errorf("Expected validation to succeed but it failed: %v", err)
}
if sandbox.Agent.Type != SandboxTypeAWF {
t.Errorf("Expected sandbox.agent.Type to be %q after strict mode validation, got %q", SandboxTypeAWF, sandbox.Agent.Type)
Comment on lines +215 to +243
}
})
}
}