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
16 changes: 10 additions & 6 deletions pkg/cli/firewall_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ func enrichWithPolicyRules(entries []AuditLogEntry, manifest *PolicyManifest) *P
// 2. agent/sandbox/firewall/audit/ — non-flattened unified agent artifact (new structure)
// 3. agent/tmp/gh-aw/sandbox/firewall/audit/ — non-flattened unified agent artifact (old structure)
// 4. firewall-audit*/ — legacy separate firewall-audit-logs artifact (backward compat)
func detectFirewallAuditArtifacts(runDir string) (manifestPath, auditJSONLPath string) {
func detectFirewallAuditArtifacts(runDir string) (manifestPath, auditJSONLPath string, err error) {
firewallPolicyLog.Printf("Detecting firewall audit artifacts in: %s", runDir)

// checkDir probes dir for policy-manifest.json and audit.jsonl, populating the
Expand Down Expand Up @@ -456,15 +456,16 @@ func detectFirewallAuditArtifacts(runDir string) (manifestPath, auditJSONLPath s
checkDir(filepath.Join(agentDir, "tmp", "gh-aw", "sandbox", "firewall", "audit"), agentBase+"/tmp/gh-aw/sandbox/firewall/audit")
}
if manifestPath != "" && auditJSONLPath != "" {
return
return manifestPath, auditJSONLPath, nil
}
}
}

// 4. Legacy separate firewall-audit-logs artifact (backward compat for older runs that
// uploaded the audit directory as a standalone artifact named firewall-audit-logs).
entries, err := os.ReadDir(runDir)
if err != nil {
entries, readErr := os.ReadDir(runDir)
if readErr != nil {
err = fmt.Errorf("detectFirewallAuditArtifacts: reading run dir %s: %w", runDir, readErr)
return
}
for _, entry := range entries {
Expand All @@ -477,13 +478,16 @@ func detectFirewallAuditArtifacts(runDir string) (manifestPath, auditJSONLPath s
}
}

return manifestPath, auditJSONLPath
return manifestPath, auditJSONLPath, nil
}

// analyzeFirewallPolicy loads policy manifest and audit JSONL, then enriches with rule attribution.
// Returns nil if no policy artifacts are found (graceful fallback to basic analysis).
func analyzeFirewallPolicy(runDir string, verbose bool) (*PolicyAnalysis, error) {
manifestPath, auditJSONLPath := detectFirewallAuditArtifacts(runDir)
manifestPath, auditJSONLPath, err := detectFirewallAuditArtifacts(runDir)
if err != nil {
return nil, fmt.Errorf("failed to detect firewall audit artifacts: %w", err)
}

if manifestPath == "" {
firewallPolicyLog.Print("No policy manifest found, skipping policy analysis")
Expand Down
37 changes: 29 additions & 8 deletions pkg/cli/firewall_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,8 @@ func TestDetectFirewallAuditArtifacts(t *testing.T) {
require.NoError(t, os.WriteFile(manifestPath, []byte("{}"), 0644))
require.NoError(t, os.WriteFile(auditPath, []byte(""), 0644))

foundManifest, foundAudit := detectFirewallAuditArtifacts(dir)
foundManifest, foundAudit, err := detectFirewallAuditArtifacts(dir)
require.NoError(t, err, "Should not error with valid run dir")
assert.Equal(t, manifestPath, foundManifest, "Should find policy manifest")
assert.Equal(t, auditPath, foundAudit, "Should find audit JSONL")
})
Expand All @@ -579,7 +580,8 @@ func TestDetectFirewallAuditArtifacts(t *testing.T) {
require.NoError(t, os.WriteFile(manifestPath, []byte("{}"), 0644))
require.NoError(t, os.WriteFile(auditPath, []byte(""), 0644))

foundManifest, foundAudit := detectFirewallAuditArtifacts(dir)
foundManifest, foundAudit, err := detectFirewallAuditArtifacts(dir)
require.NoError(t, err, "Should not error with valid run dir")
assert.Equal(t, manifestPath, foundManifest, "Should find policy manifest in agent/sandbox/firewall/audit")
assert.Equal(t, auditPath, foundAudit, "Should find audit JSONL in agent/sandbox/firewall/audit")
})
Expand All @@ -596,7 +598,8 @@ func TestDetectFirewallAuditArtifacts(t *testing.T) {
require.NoError(t, os.WriteFile(manifestPath, []byte("{}"), 0644))
require.NoError(t, os.WriteFile(auditPath, []byte(""), 0644))

foundManifest, foundAudit := detectFirewallAuditArtifacts(dir)
foundManifest, foundAudit, err := detectFirewallAuditArtifacts(dir)
require.NoError(t, err, "Should not error with valid run dir")
assert.Equal(t, manifestPath, foundManifest, "Should find policy manifest in agent/tmp/gh-aw/sandbox/firewall/audit")
assert.Equal(t, auditPath, foundAudit, "Should find audit JSONL in agent/tmp/gh-aw/sandbox/firewall/audit")
})
Expand All @@ -612,7 +615,8 @@ func TestDetectFirewallAuditArtifacts(t *testing.T) {
require.NoError(t, os.WriteFile(manifestPath, []byte("{}"), 0644))
require.NoError(t, os.WriteFile(auditPath, []byte(""), 0644))

foundManifest, foundAudit := detectFirewallAuditArtifacts(dir)
foundManifest, foundAudit, err := detectFirewallAuditArtifacts(dir)
require.NoError(t, err, "Should not error with valid run dir")
assert.Equal(t, manifestPath, foundManifest, "Should find policy manifest in agent-artifacts/sandbox/firewall/audit")
assert.Equal(t, auditPath, foundAudit, "Should find audit JSONL in agent-artifacts/sandbox/firewall/audit")
})
Expand All @@ -629,7 +633,8 @@ func TestDetectFirewallAuditArtifacts(t *testing.T) {
require.NoError(t, os.WriteFile(manifestPath, []byte("{}"), 0644))
require.NoError(t, os.WriteFile(auditPath, []byte(""), 0644))

foundManifest, foundAudit := detectFirewallAuditArtifacts(dir)
foundManifest, foundAudit, err := detectFirewallAuditArtifacts(dir)
require.NoError(t, err, "Should not error with valid run dir")
assert.Equal(t, manifestPath, foundManifest, "Should find policy manifest in prefixed-agent/sandbox/firewall/audit")
assert.Equal(t, auditPath, foundAudit, "Should find audit JSONL in prefixed-agent/sandbox/firewall/audit")
})
Expand All @@ -644,14 +649,16 @@ func TestDetectFirewallAuditArtifacts(t *testing.T) {
require.NoError(t, os.WriteFile(manifestPath, []byte("{}"), 0644))
require.NoError(t, os.WriteFile(auditPath, []byte(""), 0644))

foundManifest, foundAudit := detectFirewallAuditArtifacts(dir)
foundManifest, foundAudit, err := detectFirewallAuditArtifacts(dir)
require.NoError(t, err, "Should not error with valid run dir")
assert.Equal(t, manifestPath, foundManifest, "Should find policy manifest in firewall-audit-logs")
assert.Equal(t, auditPath, foundAudit, "Should find audit JSONL in firewall-audit-logs")
})

t.Run("no artifacts", func(t *testing.T) {
dir := t.TempDir()
foundManifest, foundAudit := detectFirewallAuditArtifacts(dir)
foundManifest, foundAudit, err := detectFirewallAuditArtifacts(dir)
require.NoError(t, err, "Should not error with empty run dir")
assert.Empty(t, foundManifest, "Should not find manifest")
assert.Empty(t, foundAudit, "Should not find audit JSONL")
})
Expand All @@ -662,10 +669,24 @@ func TestDetectFirewallAuditArtifacts(t *testing.T) {
dir := t.TempDir()
require.NoError(t, os.WriteFile(filepath.Join(dir, "agent"), []byte("not a directory"), 0644))

foundManifest, foundAudit := detectFirewallAuditArtifacts(dir)
foundManifest, foundAudit, err := detectFirewallAuditArtifacts(dir)
require.NoError(t, err, "Should not error when 'agent' is a file")
assert.Empty(t, foundManifest, "Should not find manifest when 'agent' is a file")
assert.Empty(t, foundAudit, "Should not find audit JSONL when 'agent' is a file")
})

t.Run("unreadable run directory returns error", func(t *testing.T) {
if os.Getuid() == 0 {
t.Skip("root can read any directory; skipping permission test")
}
dir := t.TempDir()
require.NoError(t, os.Chmod(dir, 0000), "Should be able to remove read permission")
t.Cleanup(func() { _ = os.Chmod(dir, 0755) })

_, _, err := detectFirewallAuditArtifacts(dir)
require.Error(t, err, "Should return an error when run dir is unreadable")
Comment on lines +678 to +687
assert.Contains(t, err.Error(), "detectFirewallAuditArtifacts", "Error should identify the function")
})
}

func TestAnalyzeFirewallPolicy(t *testing.T) {
Expand Down
Loading