From 0e4f50799fd72c226b6d101f684e2b8529e059e8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:00:23 +0000 Subject: [PATCH 1/5] Initial plan From e963117e08e9f6af8a909406c6a3ce9fb8b6ac63 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:10:43 +0000 Subject: [PATCH 2/5] Implement strict mode support for zizmor security scanner Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/compile_command.go | 4 +- pkg/cli/zizmor.go | 26 ++++++---- pkg/cli/zizmor_test.go | 7 ++- pkg/workflow/strict_mode_zizmor_test.go | 66 +++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 pkg/workflow/strict_mode_zizmor_test.go diff --git a/pkg/cli/compile_command.go b/pkg/cli/compile_command.go index a6d14ad8f96..3c2d687530d 100644 --- a/pkg/cli/compile_command.go +++ b/pkg/cli/compile_command.go @@ -317,7 +317,7 @@ func CompileWorkflows(config CompileConfig) ([]*workflow.WorkflowData, error) { } } - if err := runZizmor(absWorkflowDir, verbose); err != nil { + if err := runZizmor(absWorkflowDir, verbose, strict); err != nil { return workflowDataList, fmt.Errorf("zizmor security scan failed: %w", err) } } @@ -484,7 +484,7 @@ func CompileWorkflows(config CompileConfig) ([]*workflow.WorkflowData, error) { // Run zizmor security scanner if requested and compilation was successful if zizmor && !noEmit { - if err := runZizmor(workflowsDir, verbose); err != nil { + if err := runZizmor(workflowsDir, verbose, strict); err != nil { return workflowDataList, fmt.Errorf("zizmor security scan failed: %w", err) } } diff --git a/pkg/cli/zizmor.go b/pkg/cli/zizmor.go index f4e9b86b208..e383e4fef5d 100644 --- a/pkg/cli/zizmor.go +++ b/pkg/cli/zizmor.go @@ -31,7 +31,7 @@ type zizmorFinding struct { } // runZizmor runs the zizmor security scanner on generated .lock.yml files using Docker -func runZizmor(workflowsDir string, verbose bool) error { +func runZizmor(workflowsDir string, verbose bool, strict bool) error { compileLog.Print("Running zizmor security scanner") if verbose { @@ -75,9 +75,10 @@ func runZizmor(workflowsDir string, verbose bool) error { // Run the command err = cmd.Run() - // Parse and reformat the output - if err := parseAndDisplayZizmorOutput(stdout.String(), stderr.String(), verbose); err != nil { - compileLog.Printf("Failed to parse zizmor output: %v", err) + // Parse and reformat the output, get total warning count + totalWarnings, parseErr := parseAndDisplayZizmorOutput(stdout.String(), stderr.String(), verbose) + if parseErr != nil { + compileLog.Printf("Failed to parse zizmor output: %v", parseErr) // Fall back to showing raw output if stdout.Len() > 0 { fmt.Fprint(os.Stderr, stdout.String()) @@ -97,9 +98,13 @@ func runZizmor(workflowsDir string, verbose bool) error { if exitErr, ok := err.(*exec.ExitError); ok { exitCode := exitErr.ExitCode() compileLog.Printf("Zizmor exited with code %d", exitCode) - // Exit codes 10-14 indicate findings, not failures - // Treat these as success but log them + // Exit codes 10-14 indicate findings if exitCode >= 10 && exitCode <= 14 { + // In strict mode, findings are treated as errors + if strict { + return fmt.Errorf("strict mode: zizmor found %d security warnings/errors - workflows must have no zizmor findings in strict mode", totalWarnings) + } + // In non-strict mode, findings are logged but not treated as errors return nil } // Other exit codes are actual errors @@ -117,7 +122,8 @@ func runZizmor(workflowsDir string, verbose bool) error { } // parseAndDisplayZizmorOutput parses zizmor JSON output and displays it in the desired format -func parseAndDisplayZizmorOutput(stdout, stderr string, verbose bool) error { +// Returns the total number of warnings found +func parseAndDisplayZizmorOutput(stdout, stderr string, verbose bool) (int, error) { // Count findings per file fileFindings := make(map[string]int) @@ -140,9 +146,10 @@ func parseAndDisplayZizmorOutput(stdout, stderr string, verbose bool) error { // Parse JSON findings from stdout var findings []zizmorFinding + totalWarnings := 0 if stdout != "" && strings.HasPrefix(strings.TrimSpace(stdout), "[") { if err := json.Unmarshal([]byte(stdout), &findings); err != nil { - return fmt.Errorf("failed to parse zizmor JSON output: %w", err) + return 0, fmt.Errorf("failed to parse zizmor JSON output: %w", err) } // Count findings per file - each finding counts as 1 regardless of how many locations it has @@ -154,6 +161,7 @@ func parseAndDisplayZizmorOutput(stdout, stderr string, verbose bool) error { if filePath != "" && !affectedFiles[filePath] { affectedFiles[filePath] = true fileFindings[filePath]++ + totalWarnings++ } } } @@ -170,5 +178,5 @@ func parseAndDisplayZizmorOutput(stdout, stderr string, verbose bool) error { fmt.Fprintf(os.Stderr, "🌈 zizmor %d %s in %s\n", count, warningText, filePath) } - return nil + return totalWarnings, nil } diff --git a/pkg/cli/zizmor_test.go b/pkg/cli/zizmor_test.go index a8a675213ce..b289ff75d23 100644 --- a/pkg/cli/zizmor_test.go +++ b/pkg/cli/zizmor_test.go @@ -186,7 +186,7 @@ func TestParseAndDisplayZizmorOutput(t *testing.T) { r, w, _ := os.Pipe() os.Stderr = w - err := parseAndDisplayZizmorOutput(tt.stdout, tt.stderr, tt.verbose) + warningCount, err := parseAndDisplayZizmorOutput(tt.stdout, tt.stderr, tt.verbose) // Restore stderr w.Close() @@ -205,6 +205,11 @@ func TestParseAndDisplayZizmorOutput(t *testing.T) { t.Errorf("Unexpected error: %v", err) } + // Verify warning count is non-negative + if warningCount < 0 { + t.Errorf("Warning count should be non-negative, got: %d", warningCount) + } + // Check expected output for _, expected := range tt.expectedOutput { if !strings.Contains(output, expected) { diff --git a/pkg/workflow/strict_mode_zizmor_test.go b/pkg/workflow/strict_mode_zizmor_test.go new file mode 100644 index 00000000000..0359818db76 --- /dev/null +++ b/pkg/workflow/strict_mode_zizmor_test.go @@ -0,0 +1,66 @@ +package workflow + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestStrictModeWithZizmor(t *testing.T) { + // Note: This test documents the expected behavior but doesn't actually run zizmor + // since that would require Docker. The actual zizmor integration is tested in + // pkg/cli/zizmor_test.go + tests := []struct { + name string + content string + expectError bool + errorMsg string + }{ + { + name: "strict mode should be compatible with zizmor flag", + content: `--- +on: push +strict: true +permissions: + contents: read +engine: copilot +network: + allowed: + - "api.example.com" +--- + +# Test Workflow`, + expectError: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpDir, err := os.MkdirTemp("", "strict-zizmor-test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + testFile := filepath.Join(tmpDir, "test-workflow.md") + if err := os.WriteFile(testFile, []byte(tt.content), 0644); err != nil { + t.Fatal(err) + } + + compiler := NewCompiler(false, "", "") + compiler.SetStrictMode(true) + err = compiler.CompileWorkflow(testFile) + + if tt.expectError && err == nil { + t.Error("Expected compilation to fail but it succeeded") + } else if !tt.expectError && err != nil { + t.Errorf("Expected compilation to succeed but it failed: %v", err) + } else if tt.expectError && err != nil && tt.errorMsg != "" { + if !strings.Contains(err.Error(), tt.errorMsg) { + t.Errorf("Expected error containing '%s', got '%s'", tt.errorMsg, err.Error()) + } + } + }) + } +} From 97caf4099f200dc59cf0cee68b68b0de1e5a2178 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:14:32 +0000 Subject: [PATCH 3/5] Add documentation for strict mode zizmor integration Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/strict_mode.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/workflow/strict_mode.go b/pkg/workflow/strict_mode.go index 8b54f35234c..d8cbb8c528a 100644 --- a/pkg/workflow/strict_mode.go +++ b/pkg/workflow/strict_mode.go @@ -5,6 +5,9 @@ import ( ) // validateStrictMode performs strict mode validations on the workflow +// Note: Strict mode also affects zizmor security scanner behavior (see pkg/cli/zizmor.go) +// When zizmor is enabled with --zizmor flag, strict mode will treat any security +// findings as compilation errors rather than warnings. func (c *Compiler) validateStrictMode(frontmatter map[string]any, networkPermissions *NetworkPermissions) error { if !c.strictMode { return nil From af6767a532dd4578e2f9e995eae524eb7713b54d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:18:44 +0000 Subject: [PATCH 4/5] Complete strict mode zizmor integration - all tests pass Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- docs/src/content/docs/reference/frontmatter-full.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/src/content/docs/reference/frontmatter-full.md b/docs/src/content/docs/reference/frontmatter-full.md index 7e40221d9f4..624fe141703 100644 --- a/docs/src/content/docs/reference/frontmatter-full.md +++ b/docs/src/content/docs/reference/frontmatter-full.md @@ -985,11 +985,7 @@ tools: github-token: "${{ secrets.GITHUB_TOKEN }}" # Array of GitHub MCP server toolset names to enable specific groups of GitHub API - # functionalities. Prefer using toolset over listing individual tools with 'allowed'. - # Available toolsets: context, repos, issues, pull_requests, actions, code_security, - # dependabot, discussions, experiments, gists, labels, notifications, orgs, projects, - # secret_protection, security_advisories, stargazers, users, search - # Use [default] for recommended toolsets or [all] for everything. + # functionalities # (optional) toolsets: [] # Array of Toolset name From cd0b6be9ea226a6a7ff889d44f21b8817246a339 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 28 Oct 2025 01:59:12 +0000 Subject: [PATCH 5/5] Merge main and fix test workflows after github.actor removal Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/cli/workflows/test-all.md | 3 +-- pkg/cli/workflows/test-template-github-actions-syntax.md | 2 -- pkg/cli/workflows/test-template-issue-context.md | 2 -- pkg/cli/workflows/test-template-pr-context.md | 2 -- 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/pkg/cli/workflows/test-all.md b/pkg/cli/workflows/test-all.md index 29866649a9b..c6b9b36505d 100644 --- a/pkg/cli/workflows/test-all.md +++ b/pkg/cli/workflows/test-all.md @@ -138,7 +138,7 @@ safe-outputs: timeout_minutes: 15 # Custom run name -run-name: "Poem Bot triggered by ${{ github.actor }}" +run-name: "Poem Bot triggered by repository workflow" # Environment variables env: @@ -159,7 +159,6 @@ You are the **Poem Bot**, a creative AI agent that responds to various GitHub ev ### Current Context - **Repository**: ${{ github.repository }} - **Triggered by**: GitHub workflow -- **Actor**: ${{ github.actor }} - **Poem Theme**: ${{ env.POEM_THEME }} - **Action Type**: ${{ env.ACTION_TYPE }} - **Content**: "${{ needs.activation.outputs.text }}" diff --git a/pkg/cli/workflows/test-template-github-actions-syntax.md b/pkg/cli/workflows/test-template-github-actions-syntax.md index 7b3f09c691a..776221bdb5c 100644 --- a/pkg/cli/workflows/test-template-github-actions-syntax.md +++ b/pkg/cli/workflows/test-template-github-actions-syntax.md @@ -12,7 +12,6 @@ engine: This workflow tests template rendering with GitHub Actions expressions in conditions. Repository: ${{ github.repository }} -Actor: ${{ github.actor }} {{#if true}} ## Standard Analysis @@ -31,7 +30,6 @@ This section is hidden and won't be included in the prompt. ## Workflow Information -- Workflow: ${{ github.workflow }} - Run ID: ${{ github.run_id }} - Run Number: ${{ github.run_number }} diff --git a/pkg/cli/workflows/test-template-issue-context.md b/pkg/cli/workflows/test-template-issue-context.md index 53d9b3d8c17..78bbcd94ad2 100644 --- a/pkg/cli/workflows/test-template-issue-context.md +++ b/pkg/cli/workflows/test-template-issue-context.md @@ -16,8 +16,6 @@ tools: Analyze issue #${{ github.event.issue.number }} in repository ${{ github.repository }}. -Created by: ${{ github.actor }} - {{#if ${{ github.event.issue.number }}}} ## Standard Analysis diff --git a/pkg/cli/workflows/test-template-pr-context.md b/pkg/cli/workflows/test-template-pr-context.md index e9785432bad..5322737108c 100644 --- a/pkg/cli/workflows/test-template-pr-context.md +++ b/pkg/cli/workflows/test-template-pr-context.md @@ -16,8 +16,6 @@ tools: Review PR #${{ github.event.pull_request.number }} in repository ${{ github.repository }}. -Opened by: ${{ github.actor }} - {{#if true}} ## Standard Review