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
84 changes: 84 additions & 0 deletions pkg/workflow/firewall_args_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,88 @@ Test workflow without custom AWF arguments.
t.Error("Compiled workflow should contain '--log-level' flag")
}
})

t.Run("workflow with ssl-bump and allow-urls compiles correctly", func(t *testing.T) {
// Create temporary directory for test
tmpDir := testutil.TempDir(t, "test-*")
workflowsDir := filepath.Join(tmpDir, ".github", "workflows")
err := os.MkdirAll(workflowsDir, 0755)
if err != nil {
t.Fatalf("Failed to create workflows directory: %v", err)
}

// Create test workflow with ssl-bump and allow-urls
workflowContent := `---
on: workflow_dispatch
permissions:
contents: read
engine: copilot
network:
allowed:
- "github.com"
- "api.github.com"
firewall:
ssl-bump: true
allow-urls:
- "https://github.com/githubnext/*"
- "https://api.github.com/repos/*"
log-level: debug
---

# Test SSL Bump Workflow

Test workflow with SSL bump and allow-urls configuration.
`

workflowPath := filepath.Join(workflowsDir, "test-ssl-bump.md")
err = os.WriteFile(workflowPath, []byte(workflowContent), 0644)
if err != nil {
t.Fatalf("Failed to write workflow file: %v", err)
}

// Compile the workflow
compiler := NewCompilerWithVersion("test-ssl-bump")
compiler.SetSkipValidation(true)

if err := compiler.CompileWorkflow(workflowPath); err != nil {
t.Fatalf("Failed to compile workflow: %v", err)
}

// Read the compiled workflow
lockPath := filepath.Join(workflowsDir, "test-ssl-bump.lock.yml")
lockContent, err := os.ReadFile(lockPath)
if err != nil {
t.Fatalf("Failed to read compiled workflow: %v", err)
}

lockYAML := string(lockContent)

// Verify ssl-bump flag is present
if !strings.Contains(lockYAML, "--ssl-bump") {
t.Error("Compiled workflow should contain '--ssl-bump' flag")
}

// Verify allow-urls flag is present
if !strings.Contains(lockYAML, "--allow-urls") {
t.Error("Compiled workflow should contain '--allow-urls' flag")
}

// Verify the URL patterns are present
if !strings.Contains(lockYAML, "https://github.com/githubnext/*") {
t.Error("Compiled workflow should contain URL pattern 'https://github.com/githubnext/*'")
}

if !strings.Contains(lockYAML, "https://api.github.com/repos/*") {
t.Error("Compiled workflow should contain URL pattern 'https://api.github.com/repos/*'")
}

// Verify standard AWF flags are still present
if !strings.Contains(lockYAML, "--env-all") {
t.Error("Compiled workflow should still contain '--env-all' flag")
}

if !strings.Contains(lockYAML, "--log-level debug") {
t.Error("Compiled workflow should contain '--log-level debug'")
}
})
}
18 changes: 18 additions & 0 deletions pkg/workflow/frontmatter_extraction_security.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,24 @@ func (c *Compiler) extractFirewallConfig(firewall any) *FirewallConfig {
}
}

// Extract ssl-bump if present
if sslBump, hasSslBump := firewallObj["ssl-bump"]; hasSslBump {
if sslBumpBool, ok := sslBump.(bool); ok {
config.SSLBump = sslBumpBool
}
}

// Extract allow-urls if present
if allowUrls, hasAllowUrls := firewallObj["allow-urls"]; hasAllowUrls {
if urlsSlice, ok := allowUrls.([]any); ok {
for _, url := range urlsSlice {
if urlStr, ok := url.(string); ok {
config.AllowURLs = append(config.AllowURLs, urlStr)
}
}
}
}

return config
}

Expand Down
117 changes: 115 additions & 2 deletions pkg/workflow/frontmatter_extraction_security_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,118 @@

package workflow

// Tests for security and network extraction functions
// Placeholder - add specific tests for network, firewall, and sandbox extraction as needed
import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestExtractFirewallConfig tests the extraction of firewall configuration from frontmatter
func TestExtractFirewallConfig(t *testing.T) {
compiler := &Compiler{}

t.Run("extracts ssl-bump boolean field", func(t *testing.T) {
firewallObj := map[string]any{
"ssl-bump": true,
}

config := compiler.extractFirewallConfig(firewallObj)
require.NotNil(t, config, "Should extract firewall config")
assert.True(t, config.Enabled, "Should be enabled")
assert.True(t, config.SSLBump, "Should have ssl-bump enabled")
})

t.Run("extracts allow-urls string array", func(t *testing.T) {
firewallObj := map[string]any{
"ssl-bump": true,
"allow-urls": []any{
"https://github.com/githubnext/*",
"https://api.github.com/repos/*",
},
}

config := compiler.extractFirewallConfig(firewallObj)
require.NotNil(t, config, "Should extract firewall config")
assert.True(t, config.SSLBump, "Should have ssl-bump enabled")
assert.Len(t, config.AllowURLs, 2, "Should have 2 allow-urls")
assert.Equal(t, "https://github.com/githubnext/*", config.AllowURLs[0], "First URL should match")
assert.Equal(t, "https://api.github.com/repos/*", config.AllowURLs[1], "Second URL should match")
})

t.Run("extracts all fields together", func(t *testing.T) {
firewallObj := map[string]any{
"args": []any{"--custom-arg", "value"},
"version": "v1.0.0",
"log-level": "debug",
"ssl-bump": true,
"allow-urls": []any{"https://example.com/*"},
}

config := compiler.extractFirewallConfig(firewallObj)
require.NotNil(t, config, "Should extract firewall config")
assert.True(t, config.Enabled, "Should be enabled")
assert.Len(t, config.Args, 2, "Should have 2 args")
assert.Equal(t, "v1.0.0", config.Version, "Should extract version")
assert.Equal(t, "debug", config.LogLevel, "Should extract log-level")
assert.True(t, config.SSLBump, "Should have ssl-bump enabled")
assert.Len(t, config.AllowURLs, 1, "Should have 1 allow-url")
assert.Equal(t, "https://example.com/*", config.AllowURLs[0], "Should extract allow-url")
})

t.Run("ssl-bump defaults to false when not specified", func(t *testing.T) {
firewallObj := map[string]any{
"version": "v1.0.0",
}

config := compiler.extractFirewallConfig(firewallObj)
require.NotNil(t, config, "Should extract firewall config")
assert.False(t, config.SSLBump, "ssl-bump should default to false")
})

t.Run("allow-urls defaults to empty when not specified", func(t *testing.T) {
firewallObj := map[string]any{
"ssl-bump": true,
}

config := compiler.extractFirewallConfig(firewallObj)
require.NotNil(t, config, "Should extract firewall config")
assert.Nil(t, config.AllowURLs, "allow-urls should be nil when not specified")
})

t.Run("handles non-string values in allow-urls gracefully", func(t *testing.T) {
firewallObj := map[string]any{
"allow-urls": []any{
"https://github.com/*",
123, // Invalid: number instead of string
"https://api.github.com/*",
},
}

config := compiler.extractFirewallConfig(firewallObj)
require.NotNil(t, config, "Should extract firewall config")
assert.Len(t, config.AllowURLs, 2, "Should skip non-string values")
assert.Equal(t, "https://github.com/*", config.AllowURLs[0], "First valid URL should be extracted")
assert.Equal(t, "https://api.github.com/*", config.AllowURLs[1], "Second valid URL should be extracted")
})

t.Run("handles non-boolean ssl-bump gracefully", func(t *testing.T) {
firewallObj := map[string]any{
"ssl-bump": "true", // String instead of boolean
}

config := compiler.extractFirewallConfig(firewallObj)
require.NotNil(t, config, "Should extract firewall config")
assert.False(t, config.SSLBump, "Should ignore non-boolean ssl-bump value")
})

t.Run("handles non-array allow-urls gracefully", func(t *testing.T) {
firewallObj := map[string]any{
"allow-urls": "https://github.com/*", // String instead of array
}

config := compiler.extractFirewallConfig(firewallObj)
require.NotNil(t, config, "Should extract firewall config")
assert.Nil(t, config.AllowURLs, "Should ignore non-array allow-urls value")
})
}
Loading