From 6c7cd09ac8a95950f262c3d10932a2cbb0f37132 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Apr 2026 11:28:04 +0000 Subject: [PATCH 1/3] Add run-scripts flag to disable npm pre/post install scripts by default Agent-Logs-Url: https://github.com/github/gh-aw/sessions/744e4164-27d0-4991-b0d7-594f812a7c14 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/ai-moderator.lock.yml | 2 +- .github/workflows/audit-workflows.lock.yml | 4 +- .github/workflows/blog-auditor.lock.yml | 4 +- .github/workflows/changeset.lock.yml | 2 +- .github/workflows/ci-doctor.lock.yml | 4 +- .../claude-code-user-docs-review.lock.yml | 4 +- .../workflows/cli-version-checker.lock.yml | 4 +- .github/workflows/cloclo.lock.yml | 4 +- .../codex-github-remote-mcp-test.lock.yml | 2 +- .../commit-changes-analyzer.lock.yml | 4 +- .../workflows/copilot-agent-analysis.lock.yml | 4 +- .../copilot-session-insights.lock.yml | 4 +- .github/workflows/daily-choice-test.lock.yml | 4 +- .github/workflows/daily-code-metrics.lock.yml | 4 +- .github/workflows/daily-doc-healer.lock.yml | 6 +- .github/workflows/daily-doc-updater.lock.yml | 6 +- .github/workflows/daily-fact.lock.yml | 4 +- .../workflows/daily-function-namer.lock.yml | 4 +- .../daily-multi-device-docs-tester.lock.yml | 4 +- .../daily-observability-report.lock.yml | 4 +- ...aily-otel-instrumentation-advisor.lock.yml | 4 +- .../daily-rendering-scripts-verifier.lock.yml | 4 +- .../daily-safe-output-optimizer.lock.yml | 4 +- .../daily-safe-outputs-conformance.lock.yml | 4 +- .../daily-security-red-team.lock.yml | 4 +- .../daily-team-evolution-insights.lock.yml | 4 +- .github/workflows/deep-report.lock.yml | 4 +- .github/workflows/dev.lock.yml | 2 +- .../developer-docs-consolidator.lock.yml | 6 +- .github/workflows/dictation-prompt.lock.yml | 2 +- .../duplicate-code-detector.lock.yml | 4 +- .../example-workflow-analyzer.lock.yml | 4 +- .../github-mcp-structural-analysis.lock.yml | 4 +- .../github-mcp-tools-report.lock.yml | 4 +- .../workflows/glossary-maintainer.lock.yml | 2 +- .github/workflows/go-fan.lock.yml | 4 +- .github/workflows/go-logger.lock.yml | 4 +- .../workflows/go-pattern-detector.lock.yml | 4 +- .github/workflows/grumpy-reviewer.lock.yml | 4 +- .github/workflows/hourly-ci-cleaner.lock.yml | 4 +- .../workflows/instructions-janitor.lock.yml | 4 +- .github/workflows/issue-arborist.lock.yml | 4 +- .github/workflows/lockfile-stats.lock.yml | 4 +- .../prompt-clustering-analysis.lock.yml | 4 +- .github/workflows/safe-output-health.lock.yml | 4 +- .../schema-consistency-checker.lock.yml | 4 +- .../schema-feature-coverage.lock.yml | 4 +- .github/workflows/scout.lock.yml | 4 +- .../semantic-function-refactor.lock.yml | 4 +- .github/workflows/sergo.lock.yml | 4 +- .../workflows/smoke-agent-all-merged.lock.yml | 4 +- .../workflows/smoke-agent-all-none.lock.yml | 4 +- .../smoke-agent-public-approved.lock.yml | 4 +- .../smoke-agent-public-none.lock.yml | 4 +- .../smoke-agent-scoped-approved.lock.yml | 4 +- .../workflows/smoke-call-workflow.lock.yml | 4 +- .github/workflows/smoke-claude.lock.yml | 4 +- .github/workflows/smoke-codex.lock.yml | 6 +- .github/workflows/smoke-gemini.lock.yml | 4 +- .../workflows/static-analysis-report.lock.yml | 4 +- .../workflows/step-name-alignment.lock.yml | 4 +- .../workflows/technical-doc-writer.lock.yml | 2 +- .../test-create-pr-error-handling.lock.yml | 4 +- .github/workflows/typist.lock.yml | 4 +- .github/workflows/unbloat-docs.lock.yml | 6 +- .../weekly-blog-post-writer.lock.yml | 2 +- actions/setup/setup.sh | 2 +- pkg/parser/import_field_extractor.go | 29 +++ pkg/parser/import_processor.go | 1 + pkg/parser/schemas/main_workflow_schema.json | 12 + pkg/workflow/claude_engine_test.go | 4 +- pkg/workflow/compiler_orchestrator_tools.go | 6 + .../compiler_orchestrator_workflow.go | 6 + pkg/workflow/compiler_types.go | 1 + pkg/workflow/engine_helpers.go | 1 + pkg/workflow/engine_includes_test.go | 2 +- pkg/workflow/frontmatter_types.go | 15 +- pkg/workflow/nodejs.go | 28 ++- pkg/workflow/qmd.go | 2 +- pkg/workflow/run_scripts_validation.go | 103 +++++++++ pkg/workflow/run_scripts_validation_test.go | 205 ++++++++++++++++++ 81 files changed, 531 insertions(+), 144 deletions(-) create mode 100644 pkg/workflow/run_scripts_validation.go create mode 100644 pkg/workflow/run_scripts_validation_test.go diff --git a/.github/workflows/ai-moderator.lock.yml b/.github/workflows/ai-moderator.lock.yml index 523ce69fbc5..7df1723d9f8 100644 --- a/.github/workflows/ai-moderator.lock.yml +++ b/.github/workflows/ai-moderator.lock.yml @@ -403,7 +403,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Determine automatic lockdown mode for GitHub MCP Server diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index 97b12a28bd9..dc0f244b7ad 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -475,7 +475,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1303,7 +1303,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/blog-auditor.lock.yml b/.github/workflows/blog-auditor.lock.yml index c7ec37f07ae..6f5c7fdadb9 100644 --- a/.github/workflows/blog-auditor.lock.yml +++ b/.github/workflows/blog-auditor.lock.yml @@ -363,7 +363,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1156,7 +1156,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml index 039bbdc4e93..298249c039f 100644 --- a/.github/workflows/changeset.lock.yml +++ b/.github/workflows/changeset.lock.yml @@ -413,7 +413,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Determine automatic lockdown mode for GitHub MCP Server diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index f08012385dd..a4e5d385565 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -480,7 +480,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -1283,7 +1283,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI diff --git a/.github/workflows/claude-code-user-docs-review.lock.yml b/.github/workflows/claude-code-user-docs-review.lock.yml index d64c79c0e54..df3be3a80c6 100644 --- a/.github/workflows/claude-code-user-docs-review.lock.yml +++ b/.github/workflows/claude-code-user-docs-review.lock.yml @@ -385,7 +385,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1130,7 +1130,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index 8b2571a5337..5db4c999323 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -386,7 +386,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1131,7 +1131,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index e2385df019d..f514cad592d 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -568,7 +568,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1497,7 +1497,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/codex-github-remote-mcp-test.lock.yml b/.github/workflows/codex-github-remote-mcp-test.lock.yml index 60209846cb2..13126491860 100644 --- a/.github/workflows/codex-github-remote-mcp-test.lock.yml +++ b/.github/workflows/codex-github-remote-mcp-test.lock.yml @@ -326,7 +326,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Determine automatic lockdown mode for GitHub MCP Server diff --git a/.github/workflows/commit-changes-analyzer.lock.yml b/.github/workflows/commit-changes-analyzer.lock.yml index daa0e1655a3..9533a1cba5f 100644 --- a/.github/workflows/commit-changes-analyzer.lock.yml +++ b/.github/workflows/commit-changes-analyzer.lock.yml @@ -360,7 +360,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1085,7 +1085,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index e27ecfc2fb6..816b6c08d67 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -424,7 +424,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1178,7 +1178,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml index bdf7027d5ba..bdc7102c154 100644 --- a/.github/workflows/copilot-session-insights.lock.yml +++ b/.github/workflows/copilot-session-insights.lock.yml @@ -462,7 +462,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1242,7 +1242,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/daily-choice-test.lock.yml b/.github/workflows/daily-choice-test.lock.yml index 295919cdfa4..9b8335c64cf 100644 --- a/.github/workflows/daily-choice-test.lock.yml +++ b/.github/workflows/daily-choice-test.lock.yml @@ -366,7 +366,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1116,7 +1116,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml index 5d6654e140f..f1ed5bddab1 100644 --- a/.github/workflows/daily-code-metrics.lock.yml +++ b/.github/workflows/daily-code-metrics.lock.yml @@ -451,7 +451,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1256,7 +1256,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/daily-doc-healer.lock.yml b/.github/workflows/daily-doc-healer.lock.yml index 75d9e29886c..202a5ac508c 100644 --- a/.github/workflows/daily-doc-healer.lock.yml +++ b/.github/workflows/daily-doc-healer.lock.yml @@ -419,7 +419,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Restore qmd index from cache uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: @@ -1312,7 +1312,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution @@ -1426,7 +1426,7 @@ jobs: - name: Install @tobilu/qmd SDK if: steps.qmd-cache-restore.outputs.cache-hit != 'true' run: | - npm install --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github + npm install --ignore-scripts --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github - name: Build qmd index if: steps.qmd-cache-restore.outputs.cache-hit != 'true' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index 8d2ece36fa9..3316695316c 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -418,7 +418,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Restore qmd index from cache uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: @@ -1277,7 +1277,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution @@ -1399,7 +1399,7 @@ jobs: - name: Install @tobilu/qmd SDK if: steps.qmd-cache-restore.outputs.cache-hit != 'true' run: | - npm install --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github + npm install --ignore-scripts --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github - name: Build qmd index if: steps.qmd-cache-restore.outputs.cache-hit != 'true' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/.github/workflows/daily-fact.lock.yml b/.github/workflows/daily-fact.lock.yml index 7962be5f25d..1442f2185e6 100644 --- a/.github/workflows/daily-fact.lock.yml +++ b/.github/workflows/daily-fact.lock.yml @@ -438,7 +438,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -1138,7 +1138,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI diff --git a/.github/workflows/daily-function-namer.lock.yml b/.github/workflows/daily-function-namer.lock.yml index b73bb6ae483..b652baebb67 100644 --- a/.github/workflows/daily-function-namer.lock.yml +++ b/.github/workflows/daily-function-namer.lock.yml @@ -429,7 +429,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1224,7 +1224,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml index c40a399a27f..24b4918c942 100644 --- a/.github/workflows/daily-multi-device-docs-tester.lock.yml +++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml @@ -390,7 +390,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1238,7 +1238,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/daily-observability-report.lock.yml b/.github/workflows/daily-observability-report.lock.yml index d384df387f2..86c3776d44d 100644 --- a/.github/workflows/daily-observability-report.lock.yml +++ b/.github/workflows/daily-observability-report.lock.yml @@ -412,7 +412,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -1173,7 +1173,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI diff --git a/.github/workflows/daily-otel-instrumentation-advisor.lock.yml b/.github/workflows/daily-otel-instrumentation-advisor.lock.yml index 9eabbd6de50..a23695e45cd 100644 --- a/.github/workflows/daily-otel-instrumentation-advisor.lock.yml +++ b/.github/workflows/daily-otel-instrumentation-advisor.lock.yml @@ -374,7 +374,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1146,7 +1146,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/daily-rendering-scripts-verifier.lock.yml b/.github/workflows/daily-rendering-scripts-verifier.lock.yml index bc42ef7e4fd..1da09757422 100644 --- a/.github/workflows/daily-rendering-scripts-verifier.lock.yml +++ b/.github/workflows/daily-rendering-scripts-verifier.lock.yml @@ -441,7 +441,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1289,7 +1289,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/daily-safe-output-optimizer.lock.yml b/.github/workflows/daily-safe-output-optimizer.lock.yml index 3e169f0433e..4ab685af3a4 100644 --- a/.github/workflows/daily-safe-output-optimizer.lock.yml +++ b/.github/workflows/daily-safe-output-optimizer.lock.yml @@ -447,7 +447,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1270,7 +1270,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/daily-safe-outputs-conformance.lock.yml b/.github/workflows/daily-safe-outputs-conformance.lock.yml index 8d95a81a7e4..14df2927359 100644 --- a/.github/workflows/daily-safe-outputs-conformance.lock.yml +++ b/.github/workflows/daily-safe-outputs-conformance.lock.yml @@ -369,7 +369,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1122,7 +1122,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/daily-security-red-team.lock.yml b/.github/workflows/daily-security-red-team.lock.yml index 2e3ca0abad5..f59e10c7e94 100644 --- a/.github/workflows/daily-security-red-team.lock.yml +++ b/.github/workflows/daily-security-red-team.lock.yml @@ -373,7 +373,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1126,7 +1126,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/daily-team-evolution-insights.lock.yml b/.github/workflows/daily-team-evolution-insights.lock.yml index 645919c018f..fd5dbf2cd55 100644 --- a/.github/workflows/daily-team-evolution-insights.lock.yml +++ b/.github/workflows/daily-team-evolution-insights.lock.yml @@ -373,7 +373,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1122,7 +1122,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml index 59bb69a8f35..c7d95606d33 100644 --- a/.github/workflows/deep-report.lock.yml +++ b/.github/workflows/deep-report.lock.yml @@ -469,7 +469,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1330,7 +1330,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index 7fdc8b1ff89..d85ec1806e3 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -1277,7 +1277,7 @@ jobs: - name: Install @tobilu/qmd SDK if: steps.qmd-cache-restore.outputs.cache-hit != 'true' run: | - npm install --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github + npm install --ignore-scripts --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github - name: Build qmd index if: steps.qmd-cache-restore.outputs.cache-hit != 'true' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml index 3c807a56ff5..9fd48968ba6 100644 --- a/.github/workflows/developer-docs-consolidator.lock.yml +++ b/.github/workflows/developer-docs-consolidator.lock.yml @@ -464,7 +464,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Restore qmd index from cache uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: @@ -1366,7 +1366,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution @@ -1480,7 +1480,7 @@ jobs: - name: Install @tobilu/qmd SDK if: steps.qmd-cache-restore.outputs.cache-hit != 'true' run: | - npm install --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github + npm install --ignore-scripts --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github - name: Build qmd index if: steps.qmd-cache-restore.outputs.cache-hit != 'true' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/.github/workflows/dictation-prompt.lock.yml b/.github/workflows/dictation-prompt.lock.yml index f3a5e7cdd33..a8b74dc29db 100644 --- a/.github/workflows/dictation-prompt.lock.yml +++ b/.github/workflows/dictation-prompt.lock.yml @@ -1211,7 +1211,7 @@ jobs: - name: Install @tobilu/qmd SDK if: steps.qmd-cache-restore.outputs.cache-hit != 'true' run: | - npm install --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github + npm install --ignore-scripts --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github - name: Build qmd index if: steps.qmd-cache-restore.outputs.cache-hit != 'true' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index e61b67ac9cd..a597f31a225 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -399,7 +399,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -1129,7 +1129,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml index d99df3b854f..91fc123baa7 100644 --- a/.github/workflows/example-workflow-analyzer.lock.yml +++ b/.github/workflows/example-workflow-analyzer.lock.yml @@ -391,7 +391,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1158,7 +1158,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/github-mcp-structural-analysis.lock.yml b/.github/workflows/github-mcp-structural-analysis.lock.yml index 1cfe380ae01..8d7e078ae45 100644 --- a/.github/workflows/github-mcp-structural-analysis.lock.yml +++ b/.github/workflows/github-mcp-structural-analysis.lock.yml @@ -417,7 +417,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1183,7 +1183,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index a7397e79238..8ce29326f8b 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -388,7 +388,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1168,7 +1168,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml index 6160786272c..fff70dc0906 100644 --- a/.github/workflows/glossary-maintainer.lock.yml +++ b/.github/workflows/glossary-maintainer.lock.yml @@ -1364,7 +1364,7 @@ jobs: - name: Install @tobilu/qmd SDK if: steps.qmd-cache-restore.outputs.cache-hit != 'true' run: | - npm install --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github + npm install --ignore-scripts --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github - name: Build qmd index if: steps.qmd-cache-restore.outputs.cache-hit != 'true' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml index 2d0c1141635..16c91fc66c5 100644 --- a/.github/workflows/go-fan.lock.yml +++ b/.github/workflows/go-fan.lock.yml @@ -420,7 +420,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1216,7 +1216,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/go-logger.lock.yml b/.github/workflows/go-logger.lock.yml index 81879d31904..eadc82bc004 100644 --- a/.github/workflows/go-logger.lock.yml +++ b/.github/workflows/go-logger.lock.yml @@ -400,7 +400,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1329,7 +1329,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index 23acecf3762..a34072f1d47 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -366,7 +366,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1153,7 +1153,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index eec9de856b7..cd17436b729 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -441,7 +441,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Parse integrity filter lists @@ -1185,7 +1185,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI diff --git a/.github/workflows/hourly-ci-cleaner.lock.yml b/.github/workflows/hourly-ci-cleaner.lock.yml index cc514578c6a..6d30e68cbb1 100644 --- a/.github/workflows/hourly-ci-cleaner.lock.yml +++ b/.github/workflows/hourly-ci-cleaner.lock.yml @@ -411,7 +411,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1212,7 +1212,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/instructions-janitor.lock.yml b/.github/workflows/instructions-janitor.lock.yml index d6f3283cbaf..71f9d1b6748 100644 --- a/.github/workflows/instructions-janitor.lock.yml +++ b/.github/workflows/instructions-janitor.lock.yml @@ -378,7 +378,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1150,7 +1150,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml index 5683cf4d10c..5febf77e43f 100644 --- a/.github/workflows/issue-arborist.lock.yml +++ b/.github/workflows/issue-arborist.lock.yml @@ -378,7 +378,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Parse integrity filter lists @@ -1119,7 +1119,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index 36667addc3f..7b5f97ad17d 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -381,7 +381,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1121,7 +1121,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml index 995d1ab089c..c7f42de23e5 100644 --- a/.github/workflows/prompt-clustering-analysis.lock.yml +++ b/.github/workflows/prompt-clustering-analysis.lock.yml @@ -482,7 +482,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1265,7 +1265,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml index e6d27dd059e..6cbb6266aef 100644 --- a/.github/workflows/safe-output-health.lock.yml +++ b/.github/workflows/safe-output-health.lock.yml @@ -427,7 +427,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1225,7 +1225,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml index 020a87fd2f8..0ff315c288f 100644 --- a/.github/workflows/schema-consistency-checker.lock.yml +++ b/.github/workflows/schema-consistency-checker.lock.yml @@ -381,7 +381,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1121,7 +1121,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/schema-feature-coverage.lock.yml b/.github/workflows/schema-feature-coverage.lock.yml index fad03bb0e32..8cf97357d53 100644 --- a/.github/workflows/schema-feature-coverage.lock.yml +++ b/.github/workflows/schema-feature-coverage.lock.yml @@ -362,7 +362,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -1054,7 +1054,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index e689341edc4..5a204c5c356 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -505,7 +505,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Parse integrity filter lists id: parse-guard-vars env: @@ -1382,7 +1382,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml index 05783a2ad5b..1e58434a2ca 100644 --- a/.github/workflows/semantic-function-refactor.lock.yml +++ b/.github/workflows/semantic-function-refactor.lock.yml @@ -393,7 +393,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1185,7 +1185,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/sergo.lock.yml b/.github/workflows/sergo.lock.yml index 4f4e9fdeed7..ed7cef825bf 100644 --- a/.github/workflows/sergo.lock.yml +++ b/.github/workflows/sergo.lock.yml @@ -418,7 +418,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1205,7 +1205,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/smoke-agent-all-merged.lock.yml b/.github/workflows/smoke-agent-all-merged.lock.yml index f74fb79b9d6..a51c2242448 100644 --- a/.github/workflows/smoke-agent-all-merged.lock.yml +++ b/.github/workflows/smoke-agent-all-merged.lock.yml @@ -404,7 +404,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Parse integrity filter lists @@ -1119,7 +1119,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI diff --git a/.github/workflows/smoke-agent-all-none.lock.yml b/.github/workflows/smoke-agent-all-none.lock.yml index 5b3adee8369..c4636833191 100644 --- a/.github/workflows/smoke-agent-all-none.lock.yml +++ b/.github/workflows/smoke-agent-all-none.lock.yml @@ -404,7 +404,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Parse integrity filter lists @@ -1119,7 +1119,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI diff --git a/.github/workflows/smoke-agent-public-approved.lock.yml b/.github/workflows/smoke-agent-public-approved.lock.yml index ab658e69146..a0cfc6a8f70 100644 --- a/.github/workflows/smoke-agent-public-approved.lock.yml +++ b/.github/workflows/smoke-agent-public-approved.lock.yml @@ -407,7 +407,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Parse integrity filter lists @@ -1152,7 +1152,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI diff --git a/.github/workflows/smoke-agent-public-none.lock.yml b/.github/workflows/smoke-agent-public-none.lock.yml index 12066117b5e..6b71de4efa6 100644 --- a/.github/workflows/smoke-agent-public-none.lock.yml +++ b/.github/workflows/smoke-agent-public-none.lock.yml @@ -404,7 +404,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Parse integrity filter lists @@ -1119,7 +1119,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI diff --git a/.github/workflows/smoke-agent-scoped-approved.lock.yml b/.github/workflows/smoke-agent-scoped-approved.lock.yml index aba42a7b609..20f20f7bf75 100644 --- a/.github/workflows/smoke-agent-scoped-approved.lock.yml +++ b/.github/workflows/smoke-agent-scoped-approved.lock.yml @@ -406,7 +406,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Parse integrity filter lists @@ -1126,7 +1126,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI diff --git a/.github/workflows/smoke-call-workflow.lock.yml b/.github/workflows/smoke-call-workflow.lock.yml index 96e2a1ac9c0..8bad22a81a6 100644 --- a/.github/workflows/smoke-call-workflow.lock.yml +++ b/.github/workflows/smoke-call-workflow.lock.yml @@ -385,7 +385,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Determine automatic lockdown mode for GitHub MCP Server @@ -1092,7 +1092,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index e5d5742bdf4..fb1b7d1e33e 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -907,7 +907,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -2734,7 +2734,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 88dd6348d85..286ec0d71af 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -522,7 +522,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Restore qmd index from cache @@ -1686,7 +1686,7 @@ jobs: node-version: '24' package-manager-cache: false - name: Install Codex CLI - run: npm install -g @openai/codex@latest + run: npm install --ignore-scripts -g @openai/codex@latest - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Execute Codex CLI @@ -1785,7 +1785,7 @@ jobs: - name: Install @tobilu/qmd SDK if: steps.qmd-cache-restore.outputs.cache-hit != 'true' run: | - npm install --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github + npm install --ignore-scripts --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github - name: Build qmd index if: steps.qmd-cache-restore.outputs.cache-hit != 'true' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/.github/workflows/smoke-gemini.lock.yml b/.github/workflows/smoke-gemini.lock.yml index 958b3682153..7a32c530c66 100644 --- a/.github/workflows/smoke-gemini.lock.yml +++ b/.github/workflows/smoke-gemini.lock.yml @@ -452,7 +452,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Gemini CLI - run: npm install -g @google/gemini-cli@latest + run: npm install --ignore-scripts -g @google/gemini-cli@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1346,7 +1346,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Gemini CLI - run: npm install -g @google/gemini-cli@latest + run: npm install --ignore-scripts -g @google/gemini-cli@latest - name: Write Gemini Settings if: always() && steps.detection_guard.outputs.run_detection == 'true' run: | diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml index 2d712082e2a..f8775f4714b 100644 --- a/.github/workflows/static-analysis-report.lock.yml +++ b/.github/workflows/static-analysis-report.lock.yml @@ -444,7 +444,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1263,7 +1263,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/step-name-alignment.lock.yml b/.github/workflows/step-name-alignment.lock.yml index 17c7f8127ec..6cf5563d99b 100644 --- a/.github/workflows/step-name-alignment.lock.yml +++ b/.github/workflows/step-name-alignment.lock.yml @@ -374,7 +374,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1135,7 +1135,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index c333a3a8dff..e705a41c453 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -1369,7 +1369,7 @@ jobs: - name: Install @tobilu/qmd SDK if: steps.qmd-cache-restore.outputs.cache-hit != 'true' run: | - npm install --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github + npm install --ignore-scripts --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github - name: Build qmd index if: steps.qmd-cache-restore.outputs.cache-hit != 'true' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/.github/workflows/test-create-pr-error-handling.lock.yml b/.github/workflows/test-create-pr-error-handling.lock.yml index 4207628e6cc..ea6237eaf10 100644 --- a/.github/workflows/test-create-pr-error-handling.lock.yml +++ b/.github/workflows/test-create-pr-error-handling.lock.yml @@ -373,7 +373,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1123,7 +1123,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/typist.lock.yml b/.github/workflows/typist.lock.yml index a3ec2d4eb62..705ec3876b1 100644 --- a/.github/workflows/typist.lock.yml +++ b/.github/workflows/typist.lock.yml @@ -392,7 +392,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 @@ -1161,7 +1161,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index e1f5cf2fd7a..94bd4a80137 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -479,7 +479,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Restore qmd index from cache uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 with: @@ -1439,7 +1439,7 @@ jobs: - name: Install AWF binary run: bash ${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh v0.25.13 - name: Install Claude Code CLI - run: npm install -g @anthropic-ai/claude-code@latest + run: npm install --ignore-scripts -g @anthropic-ai/claude-code@latest - name: Execute Claude Code CLI if: always() && steps.detection_guard.outputs.run_detection == 'true' id: detection_agentic_execution @@ -1553,7 +1553,7 @@ jobs: - name: Install @tobilu/qmd SDK if: steps.qmd-cache-restore.outputs.cache-hit != 'true' run: | - npm install --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github + npm install --ignore-scripts --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github - name: Build qmd index if: steps.qmd-cache-restore.outputs.cache-hit != 'true' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/.github/workflows/weekly-blog-post-writer.lock.yml b/.github/workflows/weekly-blog-post-writer.lock.yml index 313bab7683d..e000cba7001 100644 --- a/.github/workflows/weekly-blog-post-writer.lock.yml +++ b/.github/workflows/weekly-blog-post-writer.lock.yml @@ -1352,7 +1352,7 @@ jobs: - name: Install @tobilu/qmd SDK if: steps.qmd-cache-restore.outputs.cache-hit != 'true' run: | - npm install --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github + npm install --ignore-scripts --prefix "${{ runner.temp }}/gh-aw/actions" --legacy-peer-deps @tobilu/qmd@2.0.1 @actions/github - name: Build qmd index if: steps.qmd-cache-restore.outputs.cache-hit != 'true' uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/actions/setup/setup.sh b/actions/setup/setup.sh index 19858cb72d0..41b5f10cf35 100755 --- a/actions/setup/setup.sh +++ b/actions/setup/setup.sh @@ -387,7 +387,7 @@ if [ "${SAFE_OUTPUT_CUSTOM_TOKENS_ENABLED}" = "true" ]; then fi # Install @actions/github package - npm install --no-save --loglevel=error @actions/github@^7.0.0 2>&1 | grep -v "npm WARN" || true + npm install --ignore-scripts --no-save --loglevel=error @actions/github@^7.0.0 2>&1 | grep -v "npm WARN" || true if [ -d "node_modules/@actions/github" ]; then echo "✓ Successfully installed @actions/github package" else diff --git a/pkg/parser/import_field_extractor.go b/pkg/parser/import_field_extractor.go index 30ed258fd9e..c1a7c712459 100644 --- a/pkg/parser/import_field_extractor.go +++ b/pkg/parser/import_field_extractor.go @@ -44,6 +44,7 @@ type importAccumulator struct { skipBotsSet map[string]bool caches []string features []map[string]any + runScripts bool // true if any imported workflow sets run-scripts: true (global or node-level) agentFile string agentImportSpec string repositoryImports []string @@ -351,6 +352,33 @@ func (acc *importAccumulator) extractAllImportFields(content []byte, item import } } + // Extract run-scripts flag from imported file. + // If global run-scripts: true is set OR if runtimes.node.run-scripts: true is set, + // propagate to the accumulator (OR semantics: any import enabling it enables it overall). + if !acc.runScripts { + if rsAny, hasRS := fm["run-scripts"]; hasRS { + if rsBool, ok := rsAny.(bool); ok && rsBool { + acc.runScripts = true + log.Printf("Extracted run-scripts: true from import: %s", item.fullPath) + } + } + // Also check runtimes.node.run-scripts + if runtimesAny, hasRuntimes := fm["runtimes"]; hasRuntimes { + if runtimesMap, ok := runtimesAny.(map[string]any); ok { + if nodeAny, hasNode := runtimesMap["node"]; hasNode { + if nodeMap, ok := nodeAny.(map[string]any); ok { + if rsAny, hasRS := nodeMap["run-scripts"]; hasRS { + if rsBool, ok := rsAny.(bool); ok && rsBool { + acc.runScripts = true + log.Printf("Extracted runtimes.node.run-scripts: true from import: %s", item.fullPath) + } + } + } + } + } + } + } + // Extract observability from imported file (first-wins: only set if not yet populated). // This ensures an imported workflow's OTLP config is visible to injectOTLPConfig even // when the main workflow's frontmatter does not contain an observability section. @@ -381,6 +409,7 @@ func (acc *importAccumulator) toImportsResult(topologicalOrder []string) *Import MergedSteps: acc.stepsBuilder.String(), CopilotSetupSteps: acc.copilotSetupStepsBuilder.String(), MergedRuntimes: acc.runtimesBuilder.String(), + MergedRunScripts: acc.runScripts, MergedServices: acc.servicesBuilder.String(), MergedNetwork: acc.networkBuilder.String(), MergedPermissions: acc.permissionsBuilder.String(), diff --git a/pkg/parser/import_processor.go b/pkg/parser/import_processor.go index cc623d2225f..754e88e12fa 100644 --- a/pkg/parser/import_processor.go +++ b/pkg/parser/import_processor.go @@ -24,6 +24,7 @@ type ImportsResult struct { MergedSteps string // Merged steps configuration from all imports (excluding copilot-setup-steps) CopilotSetupSteps string // Steps from copilot-setup-steps.yml (inserted at start) MergedRuntimes string // Merged runtimes configuration from all imports + MergedRunScripts bool // true if any imported workflow sets run-scripts: true (global or node-level) MergedServices string // Merged services configuration from all imports MergedNetwork string // Merged network configuration from all imports MergedPermissions string // Merged permissions configuration from all imports diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 089af1ad3fd..631bc359667 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -8497,6 +8497,12 @@ "description": "Control whether the compile-agentic version update check runs in the activation job. When true (default), the activation job downloads config.json from the gh-aw repository and verifies the compiled version is not blocked and meets the minimum supported version. Set to false to disable the check (not allowed in strict mode). See: https://github.github.com/gh-aw/reference/frontmatter/#check-for-updates", "examples": [true, false] }, + "run-scripts": { + "type": "boolean", + "default": false, + "description": "Allow npm pre/post install scripts to execute during package installation. By default, --ignore-scripts is added to all generated npm install commands to prevent supply chain attacks via malicious install hooks. Setting run-scripts: true disables this protection globally (all runtimes). A supply chain security warning is emitted at compile time; in strict mode this is an error. Per-runtime control is also available via runtimes..run-scripts. See: https://github.github.com/gh-aw/reference/frontmatter/#run-scripts", + "examples": [false, true] + }, "mcp-scripts": { "type": "object", "description": "MCP Scripts configuration for defining custom lightweight MCP tools as JavaScript, shell scripts, or Python scripts. Tools are mounted in an MCP server and have access to secrets specified by the user. Only one of 'script' (JavaScript), 'run' (shell), or 'py' (Python) must be specified per tool.", @@ -8710,6 +8716,12 @@ "type": "string", "description": "Optional GitHub Actions if condition to control when the runtime setup step runs. Supports standard GitHub Actions expression syntax. Useful for conditionally installing runtimes based on file presence (e.g., \"hashFiles('go.mod') != ''\" to install Go only when go.mod exists).", "examples": ["hashFiles('go.mod') != ''", "hashFiles('package.json') != ''", "hashFiles('requirements.txt') != '' || hashFiles('pyproject.toml') != ''", "hashFiles('uv.lock') != ''", "github.event_name == 'workflow_dispatch'"] + }, + "run-scripts": { + "type": "boolean", + "default": false, + "description": "Allow npm pre/post install scripts to execute for this runtime during package installation. Overrides the global run-scripts setting for this specific runtime. Only affects runtimes that generate npm install commands (node). A supply chain security warning is emitted at compile time; in strict mode this is an error.", + "examples": [false, true] } }, "additionalProperties": false diff --git a/pkg/workflow/claude_engine_test.go b/pkg/workflow/claude_engine_test.go index 79e997687e2..8791d76dde1 100644 --- a/pkg/workflow/claude_engine_test.go +++ b/pkg/workflow/claude_engine_test.go @@ -55,7 +55,7 @@ func TestClaudeEngine(t *testing.T) { if !strings.Contains(installStep, "Install Claude Code CLI") { t.Errorf("Expected 'Install Claude Code CLI' in installation step, got: %s", installStep) } - expectedInstallCommand := fmt.Sprintf("npm install -g @anthropic-ai/claude-code@%s", constants.DefaultClaudeCodeVersion) + expectedInstallCommand := fmt.Sprintf("npm install --ignore-scripts -g @anthropic-ai/claude-code@%s", constants.DefaultClaudeCodeVersion) if !strings.Contains(installStep, expectedInstallCommand) { t.Errorf("Expected '%s' in install step, got: %s", expectedInstallCommand, installStep) } @@ -247,7 +247,7 @@ func TestClaudeEngineWithVersion(t *testing.T) { // Check that install step uses the custom version (second step, index 1) installStep := strings.Join([]string(installSteps[1]), "\n") - if !strings.Contains(installStep, "npm install -g @anthropic-ai/claude-code@v1.2.3") { + if !strings.Contains(installStep, "npm install --ignore-scripts -g @anthropic-ai/claude-code@v1.2.3") { t.Errorf("Expected npm install with custom version v1.2.3 in install step:\n%s", installStep) } diff --git a/pkg/workflow/compiler_orchestrator_tools.go b/pkg/workflow/compiler_orchestrator_tools.go index e62906fdb4d..61e0cab6fd2 100644 --- a/pkg/workflow/compiler_orchestrator_tools.go +++ b/pkg/workflow/compiler_orchestrator_tools.go @@ -18,6 +18,7 @@ type toolsProcessingResult struct { tools map[string]any resolvedMCPServers map[string]any // fully merged mcp-servers from main workflow and all imports runtimes map[string]any + runScripts bool // true when run-scripts: true is set (globally or per node runtime, from main + imports) toolsTimeout string toolsStartupTimeout string markdownContent string @@ -156,6 +157,10 @@ func (c *Compiler) processToolsAndMarkdown(result *parser.FrontmatterResult, cle return nil, fmt.Errorf("failed to merge runtimes: %w", err) } + // Resolve run-scripts setting: true if global run-scripts is set, or if the node runtime + // has run-scripts: true, or if any imported workflow sets run-scripts (global or node-level). + runScripts := resolveRunScripts(result.Frontmatter, runtimes, importsResult.MergedRunScripts) + // Warn on deprecated APM configuration fields that are now ignored if _, hasDependencies := result.Frontmatter["dependencies"]; hasDependencies { fmt.Fprintln(os.Stderr, console.FormatWarningMessage("The 'dependencies' field is deprecated and no longer supported. Migrate to 'imports: - uses: shared/apm.md' to configure APM packages.")) @@ -298,6 +303,7 @@ func (c *Compiler) processToolsAndMarkdown(result *parser.FrontmatterResult, cle tools: tools, resolvedMCPServers: allMCPServers, runtimes: runtimes, + runScripts: runScripts, toolsTimeout: toolsTimeout, toolsStartupTimeout: toolsStartupTimeout, markdownContent: markdownContent, diff --git a/pkg/workflow/compiler_orchestrator_workflow.go b/pkg/workflow/compiler_orchestrator_workflow.go index c8fd0c570e4..1b9b1e11a5d 100644 --- a/pkg/workflow/compiler_orchestrator_workflow.go +++ b/pkg/workflow/compiler_orchestrator_workflow.go @@ -69,6 +69,11 @@ func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error) // Store a stable workflow identifier derived from the file name. workflowData.WorkflowID = GetWorkflowIDFromPath(cleanPath) + // Validate run-scripts setting (warning in non-strict mode, error in strict mode) + if err := c.validateRunScripts(workflowData); err != nil { + return nil, fmt.Errorf("%s: %w", cleanPath, err) + } + // Validate that inlined-imports is not used with agent file imports. // Agent files require runtime access and cannot be resolved without sources. if workflowData.InlinedImports && engineSetup.importsResult.AgentFile != "" { @@ -204,6 +209,7 @@ func (c *Compiler) buildInitialWorkflowData( Tools: toolsResult.tools, ParsedTools: NewTools(toolsResult.tools), Runtimes: toolsResult.runtimes, + RunScripts: toolsResult.runScripts, MarkdownContent: toolsResult.markdownContent, AI: engineSetup.engineSetting, EngineConfig: engineSetup.engineConfig, diff --git a/pkg/workflow/compiler_types.go b/pkg/workflow/compiler_types.go index d1cb2d663d3..6ccc70dc1b3 100644 --- a/pkg/workflow/compiler_types.go +++ b/pkg/workflow/compiler_types.go @@ -435,6 +435,7 @@ type WorkflowData struct { StaleCheckDisabled bool // true when on.stale-check: false is set in frontmatter (disables frontmatter hash check step in activation job) EngineConfigSteps []map[string]any // steps returned by engine.RenderConfig — prepended before execution steps ServicePortExpressions string // comma-separated ${{ job.services[''].ports[''] }} expressions for AWF --allow-host-service-ports + RunScripts bool // true when run-scripts: true is set (globally or per node runtime); disables --ignore-scripts on generated npm install steps } // BaseSafeOutputConfig holds common configuration fields for all safe output types diff --git a/pkg/workflow/engine_helpers.go b/pkg/workflow/engine_helpers.go index 78562ac7706..f471919331b 100644 --- a/pkg/workflow/engine_helpers.go +++ b/pkg/workflow/engine_helpers.go @@ -178,6 +178,7 @@ func BuildStandardNpmEngineInstallSteps( stepName, cacheKeyPrefix, true, // Include Node.js setup + workflowData.RunScripts, ) } diff --git a/pkg/workflow/engine_includes_test.go b/pkg/workflow/engine_includes_test.go index 4aa17093af9..19b88341ae1 100644 --- a/pkg/workflow/engine_includes_test.go +++ b/pkg/workflow/engine_includes_test.go @@ -305,7 +305,7 @@ This workflow specifies claude engine directly without any includes. if !strings.Contains(lockStr, "claude --print") { t.Error("Expected lock file to contain claude command reference") } - if !strings.Contains(lockStr, "npm install -g @anthropic-ai/claude-code") { + if !strings.Contains(lockStr, "npm install --ignore-scripts -g @anthropic-ai/claude-code") { t.Error("Expected lock file to contain npm install command") } } diff --git a/pkg/workflow/frontmatter_types.go b/pkg/workflow/frontmatter_types.go index 8fc0a0ea234..268841d8738 100644 --- a/pkg/workflow/frontmatter_types.go +++ b/pkg/workflow/frontmatter_types.go @@ -16,6 +16,7 @@ type RuntimeConfig struct { If string `json:"if,omitempty"` // Optional GitHub Actions if condition (e.g., "hashFiles('go.mod') != ''") ActionRepo string `json:"action-repo,omitempty"` // Override the GitHub Actions repository (e.g., "actions/setup-node") ActionVersion string `json:"action-version,omitempty"` // Override the action version (e.g., "v4") + RunScripts *bool `json:"run-scripts,omitempty"` // If true, allow pre/post install scripts for this runtime (supply chain risk; emits warning or error in strict mode) } // RuntimesConfig represents the configuration for all runtime environments @@ -152,8 +153,9 @@ type FrontmatterConfig struct { TrackerID string `json:"tracker-id,omitempty"` Version string `json:"version,omitempty"` TimeoutMinutes *TemplatableInt32 `json:"timeout-minutes,omitempty"` - Strict *bool `json:"strict,omitempty"` // Pointer to distinguish unset from false - Private *bool `json:"private,omitempty"` // If true, workflow cannot be added to other repositories + Strict *bool `json:"strict,omitempty"` // Pointer to distinguish unset from false + Private *bool `json:"private,omitempty"` // If true, workflow cannot be added to other repositories + RunScripts *bool `json:"run-scripts,omitempty"` // If true, allow pre/post install scripts globally (supply chain risk; emits warning or error in strict mode) Labels []string `json:"labels,omitempty"` // Configuration sections - using strongly-typed structs @@ -321,12 +323,21 @@ func parseRuntimesConfig(runtimes map[string]any) (*RuntimesConfig, error) { actionRepo, _ := configMap["action-repo"].(string) actionVersion, _ := configMap["action-version"].(string) + // Extract run-scripts flag (optional) + var runScripts *bool + if rsAny, hasRS := configMap["run-scripts"]; hasRS { + if rsBool, ok := rsAny.(bool); ok { + runScripts = &rsBool + } + } + // Create runtime config with all fields runtimeConfig := &RuntimeConfig{ Version: version, If: ifCondition, ActionRepo: actionRepo, ActionVersion: actionVersion, + RunScripts: runScripts, } // Map to specific runtime field diff --git a/pkg/workflow/nodejs.go b/pkg/workflow/nodejs.go index 1e656510123..692d41c2ee5 100644 --- a/pkg/workflow/nodejs.go +++ b/pkg/workflow/nodejs.go @@ -22,22 +22,27 @@ func GenerateNodeJsSetupStep() GitHubActionStep { } } -// GenerateNpmInstallSteps creates GitHub Actions steps for installing an npm package globally +// GenerateNpmInstallSteps creates GitHub Actions steps for installing an npm package globally. +// By default, --ignore-scripts is added to the install command to prevent pre/post install +// scripts from executing (supply chain security). Pass runScripts=true to allow scripts. // Parameters: // - packageName: The npm package name (e.g., "@anthropic-ai/claude-code") // - version: The package version to install // - stepName: The name to display for the install step (e.g., "Install Claude Code CLI") // - cacheKeyPrefix: The prefix for the cache key (unused, kept for API compatibility) // - includeNodeSetup: If true, includes Node.js setup step before npm install +// - runScripts: If true, allow pre/post install scripts (omits --ignore-scripts) // // Returns steps for installing the npm package (optionally with Node.js setup) -func GenerateNpmInstallSteps(packageName, version, stepName, cacheKeyPrefix string, includeNodeSetup bool) []GitHubActionStep { - return GenerateNpmInstallStepsWithScope(packageName, version, stepName, cacheKeyPrefix, includeNodeSetup, true) +func GenerateNpmInstallSteps(packageName, version, stepName, cacheKeyPrefix string, includeNodeSetup bool, runScripts bool) []GitHubActionStep { + return GenerateNpmInstallStepsWithScope(packageName, version, stepName, cacheKeyPrefix, includeNodeSetup, true, runScripts) } -// GenerateNpmInstallStepsWithScope generates npm installation steps with control over global vs local installation -func GenerateNpmInstallStepsWithScope(packageName, version, stepName, cacheKeyPrefix string, includeNodeSetup bool, isGlobal bool) []GitHubActionStep { - nodejsLog.Printf("Generating npm install steps: package=%s, version=%s, includeNodeSetup=%v, isGlobal=%v", packageName, version, includeNodeSetup, isGlobal) +// GenerateNpmInstallStepsWithScope generates npm installation steps with control over global vs local installation. +// By default, --ignore-scripts is added to the install command to prevent pre/post install +// scripts from executing (supply chain security). Pass runScripts=true to allow scripts. +func GenerateNpmInstallStepsWithScope(packageName, version, stepName, cacheKeyPrefix string, includeNodeSetup bool, isGlobal bool, runScripts bool) []GitHubActionStep { + nodejsLog.Printf("Generating npm install steps: package=%s, version=%s, includeNodeSetup=%v, isGlobal=%v, runScripts=%v", packageName, version, includeNodeSetup, isGlobal, runScripts) var steps []GitHubActionStep @@ -53,6 +58,13 @@ func GenerateNpmInstallStepsWithScope(packageName, version, stepName, cacheKeyPr globalFlag = "-g " } + // Add --ignore-scripts by default to prevent pre/post install scripts (supply chain security). + // runScripts=true disables this protection (emits a warning at compile time). + ignoreScriptsFlag := "--ignore-scripts " + if runScripts { + ignoreScriptsFlag = "" + } + var installStep GitHubActionStep if ExpressionPattern.MatchString(version) { // Version is a GitHub Actions expression (e.g. ${{ inputs.engine-version }}). @@ -60,7 +72,7 @@ func GenerateNpmInstallStepsWithScope(packageName, version, stepName, cacheKeyPr // if the expression evaluates to a malicious string, it would otherwise be // substituted verbatim into the shell command before the shell parses it. nodejsLog.Printf("Version contains GitHub Actions expression, using env var for injection safety: %s", version) - installCmd := fmt.Sprintf(`npm install %s%s@"${ENGINE_VERSION}"`, globalFlag, packageName) + installCmd := fmt.Sprintf(`npm install %s%s%s@"${ENGINE_VERSION}"`, ignoreScriptsFlag, globalFlag, packageName) installStep = GitHubActionStep{ " - name: " + stepName, " run: " + installCmd, @@ -68,7 +80,7 @@ func GenerateNpmInstallStepsWithScope(packageName, version, stepName, cacheKeyPr " ENGINE_VERSION: " + version, } } else { - installCmd := fmt.Sprintf("npm install %s%s@%s", globalFlag, packageName, version) + installCmd := fmt.Sprintf("npm install %s%s%s@%s", ignoreScriptsFlag, globalFlag, packageName, version) installStep = GitHubActionStep{ " - name: " + stepName, " run: " + installCmd, diff --git a/pkg/workflow/qmd.go b/pkg/workflow/qmd.go index 284a28862a1..1263af520b7 100644 --- a/pkg/workflow/qmd.go +++ b/pkg/workflow/qmd.go @@ -497,7 +497,7 @@ func generateQmdIndexSteps(qmdConfig *QmdToolConfig) []string { npmInstall := " - name: Install @tobilu/qmd SDK\n" npmInstall += ifCacheMiss npmInstall += " run: |\n" - npmInstall += fmt.Sprintf(" npm install --prefix \"${{ runner.temp }}/gh-aw/actions\" --legacy-peer-deps @tobilu/qmd@%s @actions/github\n", version) + npmInstall += fmt.Sprintf(" npm install --ignore-scripts --prefix \"${{ runner.temp }}/gh-aw/actions\" --legacy-peer-deps @tobilu/qmd@%s @actions/github\n", version) steps = append(steps, npmInstall) // Emit a checkout step for each collection that targets a non-default repository diff --git a/pkg/workflow/run_scripts_validation.go b/pkg/workflow/run_scripts_validation.go new file mode 100644 index 00000000000..c483a565525 --- /dev/null +++ b/pkg/workflow/run_scripts_validation.go @@ -0,0 +1,103 @@ +// This file implements run-scripts validation for agentic workflows. +// +// # Run Scripts +// +// By default, the runtime manager adds --ignore-scripts to generated npm install +// commands to prevent pre/post install scripts from executing. This is a supply +// chain security measure: malicious packages can use install hooks to exfiltrate +// secrets or corrupt the runner environment. +// +// Users can opt in to install scripts by setting run-scripts: true in their +// workflow frontmatter. This emits a security warning in non-strict mode and +// is rejected as an error in strict mode. +// +// # Supported Flags (by package manager) +// +// - npm / yarn / pnpm: --ignore-scripts +// - pip / uv: no pre/post install lifecycle scripts (N/A) +// - go / gem / bundle / dotnet / elixir / haskell / java / ruby: N/A +// +// # Configuration +// +// Global (all runtimes): +// +// run-scripts: true +// +// Per-runtime (node only, since it is the only runtime that generates install commands): +// +// runtimes: +// node: +// run-scripts: true + +package workflow + +import ( + "fmt" + "os" + + "github.com/github/gh-aw/pkg/console" +) + +var runScriptsLog = newValidationLogger("run_scripts") + +// resolveRunScripts determines whether install scripts should be allowed based on +// the workflow frontmatter and any merged settings from imported shared workflows. +// +// Returns true (allow scripts) when any of the following is set: +// - Global run-scripts: true in the top-level frontmatter +// - runtimes.node.run-scripts: true in the frontmatter +// - mergedRunScripts is true (any imported shared workflow enables run-scripts) +func resolveRunScripts(frontmatter map[string]any, runtimes map[string]any, mergedRunScripts bool) bool { + // Already enabled by an imported shared workflow + if mergedRunScripts { + runScriptsLog.Print("run-scripts enabled by imported shared workflow") + return true + } + + // Check global run-scripts field + if rsAny, ok := frontmatter["run-scripts"]; ok { + if rsBool, ok := rsAny.(bool); ok && rsBool { + runScriptsLog.Print("run-scripts enabled globally via run-scripts: true") + return true + } + } + + // Check per-runtime run-scripts for node (the only runtime that generates npm install commands) + if nodeAny, ok := runtimes["node"]; ok { + if nodeMap, ok := nodeAny.(map[string]any); ok { + if rsAny, ok := nodeMap["run-scripts"]; ok { + if rsBool, ok := rsAny.(bool); ok && rsBool { + runScriptsLog.Print("run-scripts enabled via runtimes.node.run-scripts: true") + return true + } + } + } + } + + return false +} + +// validateRunScripts emits a warning (non-strict mode) or returns an error (strict mode) +// when run-scripts is enabled in the workflow. This alerts users to the supply chain +// attack risk introduced by allowing npm pre/post install scripts. +func (c *Compiler) validateRunScripts(workflowData *WorkflowData) error { + if !workflowData.RunScripts { + runScriptsLog.Print("run-scripts not enabled, skipping validation") + return nil + } + + runScriptsLog.Print("run-scripts is enabled, emitting supply chain warning") + + warningMsg := "run-scripts: true is set – npm pre/post install scripts will execute during package installation. " + + "This is a supply chain security risk: malicious or compromised packages can use install hooks to " + + "exfiltrate secrets or tamper with the runner environment. " + + "Remove run-scripts: true unless you fully trust all installed packages and their transitive dependencies." + + if c.strictMode { + return fmt.Errorf("strict mode: %s", warningMsg) + } + + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(warningMsg)) + c.IncrementWarningCount() + return nil +} diff --git a/pkg/workflow/run_scripts_validation_test.go b/pkg/workflow/run_scripts_validation_test.go new file mode 100644 index 00000000000..50833326f82 --- /dev/null +++ b/pkg/workflow/run_scripts_validation_test.go @@ -0,0 +1,205 @@ +//go:build !integration + +package workflow + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestResolveRunScripts(t *testing.T) { + tests := []struct { + name string + frontmatter map[string]any + runtimes map[string]any + mergedRunScripts bool + expectedRunScript bool + }{ + { + name: "default: run_scripts is false", + frontmatter: map[string]any{}, + runtimes: map[string]any{}, + mergedRunScripts: false, + expectedRunScript: false, + }, + { + name: "global run-scripts: true", + frontmatter: map[string]any{ + "run-scripts": true, + }, + runtimes: map[string]any{}, + mergedRunScripts: false, + expectedRunScript: true, + }, + { + name: "global run-scripts: false", + frontmatter: map[string]any{"run-scripts": false}, + runtimes: map[string]any{}, + + mergedRunScripts: false, + expectedRunScript: false, + }, + { + name: "per-runtime node run-scripts: true", + frontmatter: map[string]any{}, + runtimes: map[string]any{ + "node": map[string]any{ + "run-scripts": true, + }, + }, + mergedRunScripts: false, + expectedRunScript: true, + }, + { + name: "per-runtime python run-scripts: true (no effect for npm installs)", + frontmatter: map[string]any{}, + runtimes: map[string]any{ + "python": map[string]any{ + "run-scripts": true, + }, + }, + mergedRunScripts: false, + expectedRunScript: false, + }, + { + name: "merged run-scripts from imported shared workflow", + frontmatter: map[string]any{}, + runtimes: map[string]any{}, + mergedRunScripts: true, + expectedRunScript: true, + }, + { + name: "merged run-scripts OR global: both true", + frontmatter: map[string]any{ + "run-scripts": true, + }, + runtimes: map[string]any{}, + mergedRunScripts: true, + expectedRunScript: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := resolveRunScripts(tt.frontmatter, tt.runtimes, tt.mergedRunScripts) + assert.Equal(t, tt.expectedRunScript, result, "resolveRunScripts() result mismatch") + }) + } +} + +func TestGenerateNpmInstallSteps_IgnoreScriptsByDefault(t *testing.T) { + steps := GenerateNpmInstallSteps( + "@anthropic-ai/claude-code", + "latest", + "Install Claude Code CLI", + "claude", + false, // no Node.js setup step + false, // runScripts=false (default) + ) + + require.Len(t, steps, 1, "Expected 1 install step") + installStep := strings.Join([]string(steps[0]), "\n") + + assert.Contains(t, installStep, "--ignore-scripts", "Expected --ignore-scripts flag by default") + assert.Contains(t, installStep, "npm install --ignore-scripts -g @anthropic-ai/claude-code@latest") +} + +func TestGenerateNpmInstallSteps_RunScriptsEnabled(t *testing.T) { + steps := GenerateNpmInstallSteps( + "@anthropic-ai/claude-code", + "latest", + "Install Claude Code CLI", + "claude", + false, // no Node.js setup step + true, // runScripts=true + ) + + require.Len(t, steps, 1, "Expected 1 install step") + installStep := strings.Join([]string(steps[0]), "\n") + + assert.NotContains(t, installStep, "--ignore-scripts", "Expected no --ignore-scripts flag when runScripts=true") + assert.Contains(t, installStep, "npm install -g @anthropic-ai/claude-code@latest") +} + +func TestGenerateNpmInstallStepsWithScope_LocalInstall(t *testing.T) { + steps := GenerateNpmInstallStepsWithScope( + "@tobilu/qmd", + "2.0.1", + "Install qmd", + "qmd", + false, // no Node.js setup + false, // local install (not global) + false, // runScripts=false + ) + + require.Len(t, steps, 1, "Expected 1 install step") + installStep := strings.Join([]string(steps[0]), "\n") + + assert.Contains(t, installStep, "--ignore-scripts", "Expected --ignore-scripts flag") + assert.NotContains(t, installStep, " -g ", "Expected local install (no -g flag)") +} + +func TestValidateRunScripts_Warning(t *testing.T) { + c := &Compiler{strictMode: false} + c.ResetWarningCount() + + workflowData := &WorkflowData{RunScripts: true} + err := c.validateRunScripts(workflowData) + + require.NoError(t, err, "Should not return error in non-strict mode") + assert.Equal(t, 1, c.GetWarningCount(), "Should increment warning count") +} + +func TestValidateRunScripts_StrictModeError(t *testing.T) { + c := &Compiler{strictMode: true} + + workflowData := &WorkflowData{RunScripts: true} + err := c.validateRunScripts(workflowData) + + require.Error(t, err, "Should return error in strict mode") + assert.Contains(t, err.Error(), "strict mode", "Error should mention strict mode") + assert.Contains(t, err.Error(), "supply chain", "Error should mention supply chain risk") +} + +func TestValidateRunScripts_NotSet(t *testing.T) { + c := &Compiler{strictMode: false} + c.ResetWarningCount() + + workflowData := &WorkflowData{RunScripts: false} + err := c.validateRunScripts(workflowData) + + require.NoError(t, err, "Should not return error when run-scripts is not set") + assert.Equal(t, 0, c.GetWarningCount(), "Should not increment warning count") +} + +func TestFrontmatterConfig_RunScripts(t *testing.T) { + frontmatter := map[string]any{ + "run-scripts": true, + "engine": "claude", + } + + config, err := ParseFrontmatterConfig(frontmatter) + require.NoError(t, err, "Should parse frontmatter without error") + require.NotNil(t, config, "Config should not be nil") + + require.NotNil(t, config.RunScripts, "RunScripts should be set") + assert.True(t, *config.RunScripts, "RunScripts should be true") +} + +func TestRuntimeConfig_RunScripts(t *testing.T) { + runtimes := map[string]any{ + "node": map[string]any{ + "version": "20", + "run-scripts": true, + }, + } + + config, err := parseRuntimesConfig(runtimes) + require.NoError(t, err, "Should parse runtimes config without error") + require.NotNil(t, config.Node, "Node config should be set") + require.NotNil(t, config.Node.RunScripts, "Node RunScripts should be set") + assert.True(t, *config.Node.RunScripts, "Node RunScripts should be true") +} From 321c53ad366616de577ef68651fda4f7065286a7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Apr 2026 11:32:43 +0000 Subject: [PATCH 2/3] Fix test file: remove unnecessary type conversions per code review Agent-Logs-Url: https://github.com/github/gh-aw/sessions/744e4164-27d0-4991-b0d7-594f812a7c14 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/run_scripts_validation_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/workflow/run_scripts_validation_test.go b/pkg/workflow/run_scripts_validation_test.go index 50833326f82..ec85c850d20 100644 --- a/pkg/workflow/run_scripts_validation_test.go +++ b/pkg/workflow/run_scripts_validation_test.go @@ -101,7 +101,7 @@ func TestGenerateNpmInstallSteps_IgnoreScriptsByDefault(t *testing.T) { ) require.Len(t, steps, 1, "Expected 1 install step") - installStep := strings.Join([]string(steps[0]), "\n") + installStep := strings.Join(steps[0], "\n") assert.Contains(t, installStep, "--ignore-scripts", "Expected --ignore-scripts flag by default") assert.Contains(t, installStep, "npm install --ignore-scripts -g @anthropic-ai/claude-code@latest") @@ -118,7 +118,7 @@ func TestGenerateNpmInstallSteps_RunScriptsEnabled(t *testing.T) { ) require.Len(t, steps, 1, "Expected 1 install step") - installStep := strings.Join([]string(steps[0]), "\n") + installStep := strings.Join(steps[0], "\n") assert.NotContains(t, installStep, "--ignore-scripts", "Expected no --ignore-scripts flag when runScripts=true") assert.Contains(t, installStep, "npm install -g @anthropic-ai/claude-code@latest") @@ -136,7 +136,7 @@ func TestGenerateNpmInstallStepsWithScope_LocalInstall(t *testing.T) { ) require.Len(t, steps, 1, "Expected 1 install step") - installStep := strings.Join([]string(steps[0]), "\n") + installStep := strings.Join(steps[0], "\n") assert.Contains(t, installStep, "--ignore-scripts", "Expected --ignore-scripts flag") assert.NotContains(t, installStep, " -g ", "Expected local install (no -g flag)") From 75d5400fade861081753f02f89515e92087f5d96 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Apr 2026 11:45:21 +0000 Subject: [PATCH 3/3] Rename run-scripts to run-install-scripts Agent-Logs-Url: https://github.com/github/gh-aw/sessions/eaec605c-e5b6-4781-936e-4744f3448919 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/parser/import_field_extractor.go | 24 +- pkg/parser/import_processor.go | 2 +- pkg/parser/schemas/main_workflow_schema.json | 8 +- pkg/workflow/compiler_orchestrator_tools.go | 10 +- .../compiler_orchestrator_workflow.go | 6 +- pkg/workflow/compiler_types.go | 2 +- pkg/workflow/engine_helpers.go | 2 +- pkg/workflow/frontmatter_types.go | 46 ++-- pkg/workflow/nodejs.go | 18 +- .../run_install_scripts_validation.go | 103 +++++++++ .../run_install_scripts_validation_test.go | 205 ++++++++++++++++++ pkg/workflow/run_scripts_validation.go | 103 --------- pkg/workflow/run_scripts_validation_test.go | 205 ------------------ 13 files changed, 367 insertions(+), 367 deletions(-) create mode 100644 pkg/workflow/run_install_scripts_validation.go create mode 100644 pkg/workflow/run_install_scripts_validation_test.go delete mode 100644 pkg/workflow/run_scripts_validation.go delete mode 100644 pkg/workflow/run_scripts_validation_test.go diff --git a/pkg/parser/import_field_extractor.go b/pkg/parser/import_field_extractor.go index c1a7c712459..b051e0a6984 100644 --- a/pkg/parser/import_field_extractor.go +++ b/pkg/parser/import_field_extractor.go @@ -44,7 +44,7 @@ type importAccumulator struct { skipBotsSet map[string]bool caches []string features []map[string]any - runScripts bool // true if any imported workflow sets run-scripts: true (global or node-level) + runInstallScripts bool // true if any imported workflow sets run-install-scripts: true (global or node-level) agentFile string agentImportSpec string repositoryImports []string @@ -352,25 +352,25 @@ func (acc *importAccumulator) extractAllImportFields(content []byte, item import } } - // Extract run-scripts flag from imported file. - // If global run-scripts: true is set OR if runtimes.node.run-scripts: true is set, + // Extract run-install-scripts flag from imported file. + // If global run-install-scripts: true is set OR if runtimes.node.run-install-scripts: true is set, // propagate to the accumulator (OR semantics: any import enabling it enables it overall). - if !acc.runScripts { - if rsAny, hasRS := fm["run-scripts"]; hasRS { + if !acc.runInstallScripts { + if rsAny, hasRS := fm["run-install-scripts"]; hasRS { if rsBool, ok := rsAny.(bool); ok && rsBool { - acc.runScripts = true - log.Printf("Extracted run-scripts: true from import: %s", item.fullPath) + acc.runInstallScripts = true + log.Printf("Extracted run-install-scripts: true from import: %s", item.fullPath) } } - // Also check runtimes.node.run-scripts + // Also check runtimes.node.run-install-scripts if runtimesAny, hasRuntimes := fm["runtimes"]; hasRuntimes { if runtimesMap, ok := runtimesAny.(map[string]any); ok { if nodeAny, hasNode := runtimesMap["node"]; hasNode { if nodeMap, ok := nodeAny.(map[string]any); ok { - if rsAny, hasRS := nodeMap["run-scripts"]; hasRS { + if rsAny, hasRS := nodeMap["run-install-scripts"]; hasRS { if rsBool, ok := rsAny.(bool); ok && rsBool { - acc.runScripts = true - log.Printf("Extracted runtimes.node.run-scripts: true from import: %s", item.fullPath) + acc.runInstallScripts = true + log.Printf("Extracted runtimes.node.run-install-scripts: true from import: %s", item.fullPath) } } } @@ -409,7 +409,7 @@ func (acc *importAccumulator) toImportsResult(topologicalOrder []string) *Import MergedSteps: acc.stepsBuilder.String(), CopilotSetupSteps: acc.copilotSetupStepsBuilder.String(), MergedRuntimes: acc.runtimesBuilder.String(), - MergedRunScripts: acc.runScripts, + MergedRunInstallScripts: acc.runInstallScripts, MergedServices: acc.servicesBuilder.String(), MergedNetwork: acc.networkBuilder.String(), MergedPermissions: acc.permissionsBuilder.String(), diff --git a/pkg/parser/import_processor.go b/pkg/parser/import_processor.go index 754e88e12fa..f1ab1ba9e2a 100644 --- a/pkg/parser/import_processor.go +++ b/pkg/parser/import_processor.go @@ -24,7 +24,7 @@ type ImportsResult struct { MergedSteps string // Merged steps configuration from all imports (excluding copilot-setup-steps) CopilotSetupSteps string // Steps from copilot-setup-steps.yml (inserted at start) MergedRuntimes string // Merged runtimes configuration from all imports - MergedRunScripts bool // true if any imported workflow sets run-scripts: true (global or node-level) + MergedRunInstallScripts bool // true if any imported workflow sets run-install-scripts: true (global or node-level) MergedServices string // Merged services configuration from all imports MergedNetwork string // Merged network configuration from all imports MergedPermissions string // Merged permissions configuration from all imports diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index 631bc359667..8f6c5c896b7 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -8497,10 +8497,10 @@ "description": "Control whether the compile-agentic version update check runs in the activation job. When true (default), the activation job downloads config.json from the gh-aw repository and verifies the compiled version is not blocked and meets the minimum supported version. Set to false to disable the check (not allowed in strict mode). See: https://github.github.com/gh-aw/reference/frontmatter/#check-for-updates", "examples": [true, false] }, - "run-scripts": { + "run-install-scripts": { "type": "boolean", "default": false, - "description": "Allow npm pre/post install scripts to execute during package installation. By default, --ignore-scripts is added to all generated npm install commands to prevent supply chain attacks via malicious install hooks. Setting run-scripts: true disables this protection globally (all runtimes). A supply chain security warning is emitted at compile time; in strict mode this is an error. Per-runtime control is also available via runtimes..run-scripts. See: https://github.github.com/gh-aw/reference/frontmatter/#run-scripts", + "description": "Allow npm pre/post install scripts to execute during package installation. By default, --ignore-scripts is added to all generated npm install commands to prevent supply chain attacks via malicious install hooks. Setting run-install-scripts: true disables this protection globally (all runtimes). A supply chain security warning is emitted at compile time; in strict mode this is an error. Per-runtime control is also available via runtimes..run-install-scripts. See: https://github.github.com/gh-aw/reference/frontmatter/#run-install-scripts", "examples": [false, true] }, "mcp-scripts": { @@ -8717,10 +8717,10 @@ "description": "Optional GitHub Actions if condition to control when the runtime setup step runs. Supports standard GitHub Actions expression syntax. Useful for conditionally installing runtimes based on file presence (e.g., \"hashFiles('go.mod') != ''\" to install Go only when go.mod exists).", "examples": ["hashFiles('go.mod') != ''", "hashFiles('package.json') != ''", "hashFiles('requirements.txt') != '' || hashFiles('pyproject.toml') != ''", "hashFiles('uv.lock') != ''", "github.event_name == 'workflow_dispatch'"] }, - "run-scripts": { + "run-install-scripts": { "type": "boolean", "default": false, - "description": "Allow npm pre/post install scripts to execute for this runtime during package installation. Overrides the global run-scripts setting for this specific runtime. Only affects runtimes that generate npm install commands (node). A supply chain security warning is emitted at compile time; in strict mode this is an error.", + "description": "Allow npm pre/post install scripts to execute for this runtime during package installation. Overrides the global run-install-scripts setting for this specific runtime. Only affects runtimes that generate npm install commands (node). A supply chain security warning is emitted at compile time; in strict mode this is an error.", "examples": [false, true] } }, diff --git a/pkg/workflow/compiler_orchestrator_tools.go b/pkg/workflow/compiler_orchestrator_tools.go index 61e0cab6fd2..ed889a3795e 100644 --- a/pkg/workflow/compiler_orchestrator_tools.go +++ b/pkg/workflow/compiler_orchestrator_tools.go @@ -18,7 +18,7 @@ type toolsProcessingResult struct { tools map[string]any resolvedMCPServers map[string]any // fully merged mcp-servers from main workflow and all imports runtimes map[string]any - runScripts bool // true when run-scripts: true is set (globally or per node runtime, from main + imports) + runInstallScripts bool // true when run-install-scripts: true is set (globally or per node runtime, from main + imports) toolsTimeout string toolsStartupTimeout string markdownContent string @@ -157,9 +157,9 @@ func (c *Compiler) processToolsAndMarkdown(result *parser.FrontmatterResult, cle return nil, fmt.Errorf("failed to merge runtimes: %w", err) } - // Resolve run-scripts setting: true if global run-scripts is set, or if the node runtime - // has run-scripts: true, or if any imported workflow sets run-scripts (global or node-level). - runScripts := resolveRunScripts(result.Frontmatter, runtimes, importsResult.MergedRunScripts) + // Resolve run-install-scripts setting: true if global run-install-scripts is set, or if the node runtime + // has run-install-scripts: true, or if any imported workflow sets run-install-scripts (global or node-level). + runInstallScripts := resolveRunInstallScripts(result.Frontmatter, runtimes, importsResult.MergedRunInstallScripts) // Warn on deprecated APM configuration fields that are now ignored if _, hasDependencies := result.Frontmatter["dependencies"]; hasDependencies { @@ -303,7 +303,7 @@ func (c *Compiler) processToolsAndMarkdown(result *parser.FrontmatterResult, cle tools: tools, resolvedMCPServers: allMCPServers, runtimes: runtimes, - runScripts: runScripts, + runInstallScripts: runInstallScripts, toolsTimeout: toolsTimeout, toolsStartupTimeout: toolsStartupTimeout, markdownContent: markdownContent, diff --git a/pkg/workflow/compiler_orchestrator_workflow.go b/pkg/workflow/compiler_orchestrator_workflow.go index 1b9b1e11a5d..7848db9c110 100644 --- a/pkg/workflow/compiler_orchestrator_workflow.go +++ b/pkg/workflow/compiler_orchestrator_workflow.go @@ -69,8 +69,8 @@ func (c *Compiler) ParseWorkflowFile(markdownPath string) (*WorkflowData, error) // Store a stable workflow identifier derived from the file name. workflowData.WorkflowID = GetWorkflowIDFromPath(cleanPath) - // Validate run-scripts setting (warning in non-strict mode, error in strict mode) - if err := c.validateRunScripts(workflowData); err != nil { + // Validate run-install-scripts setting (warning in non-strict mode, error in strict mode) + if err := c.validateRunInstallScripts(workflowData); err != nil { return nil, fmt.Errorf("%s: %w", cleanPath, err) } @@ -209,7 +209,7 @@ func (c *Compiler) buildInitialWorkflowData( Tools: toolsResult.tools, ParsedTools: NewTools(toolsResult.tools), Runtimes: toolsResult.runtimes, - RunScripts: toolsResult.runScripts, + RunInstallScripts: toolsResult.runInstallScripts, MarkdownContent: toolsResult.markdownContent, AI: engineSetup.engineSetting, EngineConfig: engineSetup.engineConfig, diff --git a/pkg/workflow/compiler_types.go b/pkg/workflow/compiler_types.go index 6ccc70dc1b3..1763d9f2606 100644 --- a/pkg/workflow/compiler_types.go +++ b/pkg/workflow/compiler_types.go @@ -435,7 +435,7 @@ type WorkflowData struct { StaleCheckDisabled bool // true when on.stale-check: false is set in frontmatter (disables frontmatter hash check step in activation job) EngineConfigSteps []map[string]any // steps returned by engine.RenderConfig — prepended before execution steps ServicePortExpressions string // comma-separated ${{ job.services[''].ports[''] }} expressions for AWF --allow-host-service-ports - RunScripts bool // true when run-scripts: true is set (globally or per node runtime); disables --ignore-scripts on generated npm install steps + RunInstallScripts bool // true when run-install-scripts: true is set (globally or per node runtime); disables --ignore-scripts on generated npm install steps } // BaseSafeOutputConfig holds common configuration fields for all safe output types diff --git a/pkg/workflow/engine_helpers.go b/pkg/workflow/engine_helpers.go index f471919331b..3a2162af356 100644 --- a/pkg/workflow/engine_helpers.go +++ b/pkg/workflow/engine_helpers.go @@ -178,7 +178,7 @@ func BuildStandardNpmEngineInstallSteps( stepName, cacheKeyPrefix, true, // Include Node.js setup - workflowData.RunScripts, + workflowData.RunInstallScripts, ) } diff --git a/pkg/workflow/frontmatter_types.go b/pkg/workflow/frontmatter_types.go index 268841d8738..e0d8a38f0fe 100644 --- a/pkg/workflow/frontmatter_types.go +++ b/pkg/workflow/frontmatter_types.go @@ -12,11 +12,11 @@ var frontmatterTypesLog = logger.New("workflow:frontmatter_types") // RuntimeConfig represents the configuration for a single runtime type RuntimeConfig struct { - Version string `json:"version,omitempty"` // Version of the runtime (e.g., "20" for Node, "3.11" for Python) - If string `json:"if,omitempty"` // Optional GitHub Actions if condition (e.g., "hashFiles('go.mod') != ''") - ActionRepo string `json:"action-repo,omitempty"` // Override the GitHub Actions repository (e.g., "actions/setup-node") - ActionVersion string `json:"action-version,omitempty"` // Override the action version (e.g., "v4") - RunScripts *bool `json:"run-scripts,omitempty"` // If true, allow pre/post install scripts for this runtime (supply chain risk; emits warning or error in strict mode) + Version string `json:"version,omitempty"` // Version of the runtime (e.g., "20" for Node, "3.11" for Python) + If string `json:"if,omitempty"` // Optional GitHub Actions if condition (e.g., "hashFiles('go.mod') != ''") + ActionRepo string `json:"action-repo,omitempty"` // Override the GitHub Actions repository (e.g., "actions/setup-node") + ActionVersion string `json:"action-version,omitempty"` // Override the action version (e.g., "v4") + RunInstallScripts *bool `json:"run-install-scripts,omitempty"` // If true, allow pre/post install scripts for this runtime (supply chain risk; emits warning or error in strict mode) } // RuntimesConfig represents the configuration for all runtime environments @@ -148,15 +148,15 @@ type FrontmatterConfig struct { // configuration (e.g. {id: copilot, max-continuations: 2}). Using any prevents // JSON unmarshal failures when the engine is an object, which would otherwise cause // ParseFrontmatterConfig to return nil and break features that depend on it (e.g. OTLP). - Engine any `json:"engine,omitempty"` - Source string `json:"source,omitempty"` - TrackerID string `json:"tracker-id,omitempty"` - Version string `json:"version,omitempty"` - TimeoutMinutes *TemplatableInt32 `json:"timeout-minutes,omitempty"` - Strict *bool `json:"strict,omitempty"` // Pointer to distinguish unset from false - Private *bool `json:"private,omitempty"` // If true, workflow cannot be added to other repositories - RunScripts *bool `json:"run-scripts,omitempty"` // If true, allow pre/post install scripts globally (supply chain risk; emits warning or error in strict mode) - Labels []string `json:"labels,omitempty"` + Engine any `json:"engine,omitempty"` + Source string `json:"source,omitempty"` + TrackerID string `json:"tracker-id,omitempty"` + Version string `json:"version,omitempty"` + TimeoutMinutes *TemplatableInt32 `json:"timeout-minutes,omitempty"` + Strict *bool `json:"strict,omitempty"` // Pointer to distinguish unset from false + Private *bool `json:"private,omitempty"` // If true, workflow cannot be added to other repositories + RunInstallScripts *bool `json:"run-install-scripts,omitempty"` // If true, allow pre/post install scripts globally (supply chain risk; emits warning or error in strict mode) + Labels []string `json:"labels,omitempty"` // Configuration sections - using strongly-typed structs Tools *ToolsConfig `json:"tools,omitempty"` @@ -323,21 +323,21 @@ func parseRuntimesConfig(runtimes map[string]any) (*RuntimesConfig, error) { actionRepo, _ := configMap["action-repo"].(string) actionVersion, _ := configMap["action-version"].(string) - // Extract run-scripts flag (optional) - var runScripts *bool - if rsAny, hasRS := configMap["run-scripts"]; hasRS { + // Extract run-install-scripts flag (optional) + var runInstallScripts *bool + if rsAny, hasRS := configMap["run-install-scripts"]; hasRS { if rsBool, ok := rsAny.(bool); ok { - runScripts = &rsBool + runInstallScripts = &rsBool } } // Create runtime config with all fields runtimeConfig := &RuntimeConfig{ - Version: version, - If: ifCondition, - ActionRepo: actionRepo, - ActionVersion: actionVersion, - RunScripts: runScripts, + Version: version, + If: ifCondition, + ActionRepo: actionRepo, + ActionVersion: actionVersion, + RunInstallScripts: runInstallScripts, } // Map to specific runtime field diff --git a/pkg/workflow/nodejs.go b/pkg/workflow/nodejs.go index 692d41c2ee5..1161fbe55f9 100644 --- a/pkg/workflow/nodejs.go +++ b/pkg/workflow/nodejs.go @@ -24,25 +24,25 @@ func GenerateNodeJsSetupStep() GitHubActionStep { // GenerateNpmInstallSteps creates GitHub Actions steps for installing an npm package globally. // By default, --ignore-scripts is added to the install command to prevent pre/post install -// scripts from executing (supply chain security). Pass runScripts=true to allow scripts. +// scripts from executing (supply chain security). Pass runInstallScripts=true to allow scripts. // Parameters: // - packageName: The npm package name (e.g., "@anthropic-ai/claude-code") // - version: The package version to install // - stepName: The name to display for the install step (e.g., "Install Claude Code CLI") // - cacheKeyPrefix: The prefix for the cache key (unused, kept for API compatibility) // - includeNodeSetup: If true, includes Node.js setup step before npm install -// - runScripts: If true, allow pre/post install scripts (omits --ignore-scripts) +// - runInstallScripts: If true, allow pre/post install scripts (omits --ignore-scripts) // // Returns steps for installing the npm package (optionally with Node.js setup) -func GenerateNpmInstallSteps(packageName, version, stepName, cacheKeyPrefix string, includeNodeSetup bool, runScripts bool) []GitHubActionStep { - return GenerateNpmInstallStepsWithScope(packageName, version, stepName, cacheKeyPrefix, includeNodeSetup, true, runScripts) +func GenerateNpmInstallSteps(packageName, version, stepName, cacheKeyPrefix string, includeNodeSetup bool, runInstallScripts bool) []GitHubActionStep { + return GenerateNpmInstallStepsWithScope(packageName, version, stepName, cacheKeyPrefix, includeNodeSetup, true, runInstallScripts) } // GenerateNpmInstallStepsWithScope generates npm installation steps with control over global vs local installation. // By default, --ignore-scripts is added to the install command to prevent pre/post install -// scripts from executing (supply chain security). Pass runScripts=true to allow scripts. -func GenerateNpmInstallStepsWithScope(packageName, version, stepName, cacheKeyPrefix string, includeNodeSetup bool, isGlobal bool, runScripts bool) []GitHubActionStep { - nodejsLog.Printf("Generating npm install steps: package=%s, version=%s, includeNodeSetup=%v, isGlobal=%v, runScripts=%v", packageName, version, includeNodeSetup, isGlobal, runScripts) +// scripts from executing (supply chain security). Pass runInstallScripts=true to allow scripts. +func GenerateNpmInstallStepsWithScope(packageName, version, stepName, cacheKeyPrefix string, includeNodeSetup bool, isGlobal bool, runInstallScripts bool) []GitHubActionStep { + nodejsLog.Printf("Generating npm install steps: package=%s, version=%s, includeNodeSetup=%v, isGlobal=%v, runInstallScripts=%v", packageName, version, includeNodeSetup, isGlobal, runInstallScripts) var steps []GitHubActionStep @@ -59,9 +59,9 @@ func GenerateNpmInstallStepsWithScope(packageName, version, stepName, cacheKeyPr } // Add --ignore-scripts by default to prevent pre/post install scripts (supply chain security). - // runScripts=true disables this protection (emits a warning at compile time). + // runInstallScripts=true disables this protection (emits a warning at compile time). ignoreScriptsFlag := "--ignore-scripts " - if runScripts { + if runInstallScripts { ignoreScriptsFlag = "" } diff --git a/pkg/workflow/run_install_scripts_validation.go b/pkg/workflow/run_install_scripts_validation.go new file mode 100644 index 00000000000..af5e900c3e1 --- /dev/null +++ b/pkg/workflow/run_install_scripts_validation.go @@ -0,0 +1,103 @@ +// This file implements run-install-scripts validation for agentic workflows. +// +// # Run Scripts +// +// By default, the runtime manager adds --ignore-scripts to generated npm install +// commands to prevent pre/post install scripts from executing. This is a supply +// chain security measure: malicious packages can use install hooks to exfiltrate +// secrets or corrupt the runner environment. +// +// Users can opt in to install scripts by setting run-install-scripts: true in their +// workflow frontmatter. This emits a security warning in non-strict mode and +// is rejected as an error in strict mode. +// +// # Supported Flags (by package manager) +// +// - npm / yarn / pnpm: --ignore-scripts +// - pip / uv: no pre/post install lifecycle scripts (N/A) +// - go / gem / bundle / dotnet / elixir / haskell / java / ruby: N/A +// +// # Configuration +// +// Global (all runtimes): +// +// run-install-scripts: true +// +// Per-runtime (node only, since it is the only runtime that generates install commands): +// +// runtimes: +// node: +// run-install-scripts: true + +package workflow + +import ( + "fmt" + "os" + + "github.com/github/gh-aw/pkg/console" +) + +var runInstallScriptsLog = newValidationLogger("run_install_scripts") + +// resolveRunInstallScripts determines whether install scripts should be allowed based on +// the workflow frontmatter and any merged settings from imported shared workflows. +// +// Returns true (allow scripts) when any of the following is set: +// - Global run-install-scripts: true in the top-level frontmatter +// - runtimes.node.run-install-scripts: true in the frontmatter +// - mergedRunInstallScripts is true (any imported shared workflow enables run-install-scripts) +func resolveRunInstallScripts(frontmatter map[string]any, runtimes map[string]any, mergedRunInstallScripts bool) bool { + // Already enabled by an imported shared workflow + if mergedRunInstallScripts { + runInstallScriptsLog.Print("run-install-scripts enabled by imported shared workflow") + return true + } + + // Check global run-install-scripts field + if rsAny, ok := frontmatter["run-install-scripts"]; ok { + if rsBool, ok := rsAny.(bool); ok && rsBool { + runInstallScriptsLog.Print("run-install-scripts enabled globally via run-install-scripts: true") + return true + } + } + + // Check per-runtime run-install-scripts for node (the only runtime that generates npm install commands) + if nodeAny, ok := runtimes["node"]; ok { + if nodeMap, ok := nodeAny.(map[string]any); ok { + if rsAny, ok := nodeMap["run-install-scripts"]; ok { + if rsBool, ok := rsAny.(bool); ok && rsBool { + runInstallScriptsLog.Print("run-install-scripts enabled via runtimes.node.run-install-scripts: true") + return true + } + } + } + } + + return false +} + +// validateRunInstallScripts emits a warning (non-strict mode) or returns an error (strict mode) +// when run-install-scripts is enabled in the workflow. This alerts users to the supply chain +// attack risk introduced by allowing npm pre/post install scripts. +func (c *Compiler) validateRunInstallScripts(workflowData *WorkflowData) error { + if !workflowData.RunInstallScripts { + runInstallScriptsLog.Print("run-install-scripts not enabled, skipping validation") + return nil + } + + runInstallScriptsLog.Print("run-install-scripts is enabled, emitting supply chain warning") + + warningMsg := "run-install-scripts: true is set – npm pre/post install scripts will execute during package installation. " + + "This is a supply chain security risk: malicious or compromised packages can use install hooks to " + + "exfiltrate secrets or tamper with the runner environment. " + + "Remove run-install-scripts: true unless you fully trust all installed packages and their transitive dependencies." + + if c.strictMode { + return fmt.Errorf("strict mode: %s", warningMsg) + } + + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(warningMsg)) + c.IncrementWarningCount() + return nil +} diff --git a/pkg/workflow/run_install_scripts_validation_test.go b/pkg/workflow/run_install_scripts_validation_test.go new file mode 100644 index 00000000000..8a4569f3226 --- /dev/null +++ b/pkg/workflow/run_install_scripts_validation_test.go @@ -0,0 +1,205 @@ +//go:build !integration + +package workflow + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestResolveRunInstallScripts(t *testing.T) { + tests := []struct { + name string + frontmatter map[string]any + runtimes map[string]any + mergedRunInstallScripts bool + expectedRunScript bool + }{ + { + name: "default: run_scripts is false", + frontmatter: map[string]any{}, + runtimes: map[string]any{}, + mergedRunInstallScripts: false, + expectedRunScript: false, + }, + { + name: "global run-install-scripts: true", + frontmatter: map[string]any{ + "run-install-scripts": true, + }, + runtimes: map[string]any{}, + mergedRunInstallScripts: false, + expectedRunScript: true, + }, + { + name: "global run-install-scripts: false", + frontmatter: map[string]any{"run-install-scripts": false}, + runtimes: map[string]any{}, + + mergedRunInstallScripts: false, + expectedRunScript: false, + }, + { + name: "per-runtime node run-install-scripts: true", + frontmatter: map[string]any{}, + runtimes: map[string]any{ + "node": map[string]any{ + "run-install-scripts": true, + }, + }, + mergedRunInstallScripts: false, + expectedRunScript: true, + }, + { + name: "per-runtime python run-install-scripts: true (no effect for npm installs)", + frontmatter: map[string]any{}, + runtimes: map[string]any{ + "python": map[string]any{ + "run-install-scripts": true, + }, + }, + mergedRunInstallScripts: false, + expectedRunScript: false, + }, + { + name: "merged run-install-scripts from imported shared workflow", + frontmatter: map[string]any{}, + runtimes: map[string]any{}, + mergedRunInstallScripts: true, + expectedRunScript: true, + }, + { + name: "merged run-install-scripts OR global: both true", + frontmatter: map[string]any{ + "run-install-scripts": true, + }, + runtimes: map[string]any{}, + mergedRunInstallScripts: true, + expectedRunScript: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := resolveRunInstallScripts(tt.frontmatter, tt.runtimes, tt.mergedRunInstallScripts) + assert.Equal(t, tt.expectedRunScript, result, "resolveRunInstallScripts() result mismatch") + }) + } +} + +func TestGenerateNpmInstallSteps_IgnoreScriptsByDefault(t *testing.T) { + steps := GenerateNpmInstallSteps( + "@anthropic-ai/claude-code", + "latest", + "Install Claude Code CLI", + "claude", + false, // no Node.js setup step + false, // runInstallScripts=false (default) + ) + + require.Len(t, steps, 1, "Expected 1 install step") + installStep := strings.Join(steps[0], "\n") + + assert.Contains(t, installStep, "--ignore-scripts", "Expected --ignore-scripts flag by default") + assert.Contains(t, installStep, "npm install --ignore-scripts -g @anthropic-ai/claude-code@latest") +} + +func TestGenerateNpmInstallSteps_RunInstallScriptsEnabled(t *testing.T) { + steps := GenerateNpmInstallSteps( + "@anthropic-ai/claude-code", + "latest", + "Install Claude Code CLI", + "claude", + false, // no Node.js setup step + true, // runInstallScripts=true + ) + + require.Len(t, steps, 1, "Expected 1 install step") + installStep := strings.Join(steps[0], "\n") + + assert.NotContains(t, installStep, "--ignore-scripts", "Expected no --ignore-scripts flag when runInstallScripts=true") + assert.Contains(t, installStep, "npm install -g @anthropic-ai/claude-code@latest") +} + +func TestGenerateNpmInstallStepsWithScope_LocalInstall(t *testing.T) { + steps := GenerateNpmInstallStepsWithScope( + "@tobilu/qmd", + "2.0.1", + "Install qmd", + "qmd", + false, // no Node.js setup + false, // local install (not global) + false, // runInstallScripts=false + ) + + require.Len(t, steps, 1, "Expected 1 install step") + installStep := strings.Join(steps[0], "\n") + + assert.Contains(t, installStep, "--ignore-scripts", "Expected --ignore-scripts flag") + assert.NotContains(t, installStep, " -g ", "Expected local install (no -g flag)") +} + +func TestValidateRunInstallScripts_Warning(t *testing.T) { + c := &Compiler{strictMode: false} + c.ResetWarningCount() + + workflowData := &WorkflowData{RunInstallScripts: true} + err := c.validateRunInstallScripts(workflowData) + + require.NoError(t, err, "Should not return error in non-strict mode") + assert.Equal(t, 1, c.GetWarningCount(), "Should increment warning count") +} + +func TestValidateRunInstallScripts_StrictModeError(t *testing.T) { + c := &Compiler{strictMode: true} + + workflowData := &WorkflowData{RunInstallScripts: true} + err := c.validateRunInstallScripts(workflowData) + + require.Error(t, err, "Should return error in strict mode") + assert.Contains(t, err.Error(), "strict mode", "Error should mention strict mode") + assert.Contains(t, err.Error(), "supply chain", "Error should mention supply chain risk") +} + +func TestValidateRunInstallScripts_NotSet(t *testing.T) { + c := &Compiler{strictMode: false} + c.ResetWarningCount() + + workflowData := &WorkflowData{RunInstallScripts: false} + err := c.validateRunInstallScripts(workflowData) + + require.NoError(t, err, "Should not return error when run-install-scripts is not set") + assert.Equal(t, 0, c.GetWarningCount(), "Should not increment warning count") +} + +func TestFrontmatterConfig_RunInstallScripts(t *testing.T) { + frontmatter := map[string]any{ + "run-install-scripts": true, + "engine": "claude", + } + + config, err := ParseFrontmatterConfig(frontmatter) + require.NoError(t, err, "Should parse frontmatter without error") + require.NotNil(t, config, "Config should not be nil") + + require.NotNil(t, config.RunInstallScripts, "RunInstallScripts should be set") + assert.True(t, *config.RunInstallScripts, "RunInstallScripts should be true") +} + +func TestRuntimeConfig_RunInstallScripts(t *testing.T) { + runtimes := map[string]any{ + "node": map[string]any{ + "version": "20", + "run-install-scripts": true, + }, + } + + config, err := parseRuntimesConfig(runtimes) + require.NoError(t, err, "Should parse runtimes config without error") + require.NotNil(t, config.Node, "Node config should be set") + require.NotNil(t, config.Node.RunInstallScripts, "Node RunInstallScripts should be set") + assert.True(t, *config.Node.RunInstallScripts, "Node RunInstallScripts should be true") +} diff --git a/pkg/workflow/run_scripts_validation.go b/pkg/workflow/run_scripts_validation.go deleted file mode 100644 index c483a565525..00000000000 --- a/pkg/workflow/run_scripts_validation.go +++ /dev/null @@ -1,103 +0,0 @@ -// This file implements run-scripts validation for agentic workflows. -// -// # Run Scripts -// -// By default, the runtime manager adds --ignore-scripts to generated npm install -// commands to prevent pre/post install scripts from executing. This is a supply -// chain security measure: malicious packages can use install hooks to exfiltrate -// secrets or corrupt the runner environment. -// -// Users can opt in to install scripts by setting run-scripts: true in their -// workflow frontmatter. This emits a security warning in non-strict mode and -// is rejected as an error in strict mode. -// -// # Supported Flags (by package manager) -// -// - npm / yarn / pnpm: --ignore-scripts -// - pip / uv: no pre/post install lifecycle scripts (N/A) -// - go / gem / bundle / dotnet / elixir / haskell / java / ruby: N/A -// -// # Configuration -// -// Global (all runtimes): -// -// run-scripts: true -// -// Per-runtime (node only, since it is the only runtime that generates install commands): -// -// runtimes: -// node: -// run-scripts: true - -package workflow - -import ( - "fmt" - "os" - - "github.com/github/gh-aw/pkg/console" -) - -var runScriptsLog = newValidationLogger("run_scripts") - -// resolveRunScripts determines whether install scripts should be allowed based on -// the workflow frontmatter and any merged settings from imported shared workflows. -// -// Returns true (allow scripts) when any of the following is set: -// - Global run-scripts: true in the top-level frontmatter -// - runtimes.node.run-scripts: true in the frontmatter -// - mergedRunScripts is true (any imported shared workflow enables run-scripts) -func resolveRunScripts(frontmatter map[string]any, runtimes map[string]any, mergedRunScripts bool) bool { - // Already enabled by an imported shared workflow - if mergedRunScripts { - runScriptsLog.Print("run-scripts enabled by imported shared workflow") - return true - } - - // Check global run-scripts field - if rsAny, ok := frontmatter["run-scripts"]; ok { - if rsBool, ok := rsAny.(bool); ok && rsBool { - runScriptsLog.Print("run-scripts enabled globally via run-scripts: true") - return true - } - } - - // Check per-runtime run-scripts for node (the only runtime that generates npm install commands) - if nodeAny, ok := runtimes["node"]; ok { - if nodeMap, ok := nodeAny.(map[string]any); ok { - if rsAny, ok := nodeMap["run-scripts"]; ok { - if rsBool, ok := rsAny.(bool); ok && rsBool { - runScriptsLog.Print("run-scripts enabled via runtimes.node.run-scripts: true") - return true - } - } - } - } - - return false -} - -// validateRunScripts emits a warning (non-strict mode) or returns an error (strict mode) -// when run-scripts is enabled in the workflow. This alerts users to the supply chain -// attack risk introduced by allowing npm pre/post install scripts. -func (c *Compiler) validateRunScripts(workflowData *WorkflowData) error { - if !workflowData.RunScripts { - runScriptsLog.Print("run-scripts not enabled, skipping validation") - return nil - } - - runScriptsLog.Print("run-scripts is enabled, emitting supply chain warning") - - warningMsg := "run-scripts: true is set – npm pre/post install scripts will execute during package installation. " + - "This is a supply chain security risk: malicious or compromised packages can use install hooks to " + - "exfiltrate secrets or tamper with the runner environment. " + - "Remove run-scripts: true unless you fully trust all installed packages and their transitive dependencies." - - if c.strictMode { - return fmt.Errorf("strict mode: %s", warningMsg) - } - - fmt.Fprintln(os.Stderr, console.FormatWarningMessage(warningMsg)) - c.IncrementWarningCount() - return nil -} diff --git a/pkg/workflow/run_scripts_validation_test.go b/pkg/workflow/run_scripts_validation_test.go deleted file mode 100644 index ec85c850d20..00000000000 --- a/pkg/workflow/run_scripts_validation_test.go +++ /dev/null @@ -1,205 +0,0 @@ -//go:build !integration - -package workflow - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestResolveRunScripts(t *testing.T) { - tests := []struct { - name string - frontmatter map[string]any - runtimes map[string]any - mergedRunScripts bool - expectedRunScript bool - }{ - { - name: "default: run_scripts is false", - frontmatter: map[string]any{}, - runtimes: map[string]any{}, - mergedRunScripts: false, - expectedRunScript: false, - }, - { - name: "global run-scripts: true", - frontmatter: map[string]any{ - "run-scripts": true, - }, - runtimes: map[string]any{}, - mergedRunScripts: false, - expectedRunScript: true, - }, - { - name: "global run-scripts: false", - frontmatter: map[string]any{"run-scripts": false}, - runtimes: map[string]any{}, - - mergedRunScripts: false, - expectedRunScript: false, - }, - { - name: "per-runtime node run-scripts: true", - frontmatter: map[string]any{}, - runtimes: map[string]any{ - "node": map[string]any{ - "run-scripts": true, - }, - }, - mergedRunScripts: false, - expectedRunScript: true, - }, - { - name: "per-runtime python run-scripts: true (no effect for npm installs)", - frontmatter: map[string]any{}, - runtimes: map[string]any{ - "python": map[string]any{ - "run-scripts": true, - }, - }, - mergedRunScripts: false, - expectedRunScript: false, - }, - { - name: "merged run-scripts from imported shared workflow", - frontmatter: map[string]any{}, - runtimes: map[string]any{}, - mergedRunScripts: true, - expectedRunScript: true, - }, - { - name: "merged run-scripts OR global: both true", - frontmatter: map[string]any{ - "run-scripts": true, - }, - runtimes: map[string]any{}, - mergedRunScripts: true, - expectedRunScript: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := resolveRunScripts(tt.frontmatter, tt.runtimes, tt.mergedRunScripts) - assert.Equal(t, tt.expectedRunScript, result, "resolveRunScripts() result mismatch") - }) - } -} - -func TestGenerateNpmInstallSteps_IgnoreScriptsByDefault(t *testing.T) { - steps := GenerateNpmInstallSteps( - "@anthropic-ai/claude-code", - "latest", - "Install Claude Code CLI", - "claude", - false, // no Node.js setup step - false, // runScripts=false (default) - ) - - require.Len(t, steps, 1, "Expected 1 install step") - installStep := strings.Join(steps[0], "\n") - - assert.Contains(t, installStep, "--ignore-scripts", "Expected --ignore-scripts flag by default") - assert.Contains(t, installStep, "npm install --ignore-scripts -g @anthropic-ai/claude-code@latest") -} - -func TestGenerateNpmInstallSteps_RunScriptsEnabled(t *testing.T) { - steps := GenerateNpmInstallSteps( - "@anthropic-ai/claude-code", - "latest", - "Install Claude Code CLI", - "claude", - false, // no Node.js setup step - true, // runScripts=true - ) - - require.Len(t, steps, 1, "Expected 1 install step") - installStep := strings.Join(steps[0], "\n") - - assert.NotContains(t, installStep, "--ignore-scripts", "Expected no --ignore-scripts flag when runScripts=true") - assert.Contains(t, installStep, "npm install -g @anthropic-ai/claude-code@latest") -} - -func TestGenerateNpmInstallStepsWithScope_LocalInstall(t *testing.T) { - steps := GenerateNpmInstallStepsWithScope( - "@tobilu/qmd", - "2.0.1", - "Install qmd", - "qmd", - false, // no Node.js setup - false, // local install (not global) - false, // runScripts=false - ) - - require.Len(t, steps, 1, "Expected 1 install step") - installStep := strings.Join(steps[0], "\n") - - assert.Contains(t, installStep, "--ignore-scripts", "Expected --ignore-scripts flag") - assert.NotContains(t, installStep, " -g ", "Expected local install (no -g flag)") -} - -func TestValidateRunScripts_Warning(t *testing.T) { - c := &Compiler{strictMode: false} - c.ResetWarningCount() - - workflowData := &WorkflowData{RunScripts: true} - err := c.validateRunScripts(workflowData) - - require.NoError(t, err, "Should not return error in non-strict mode") - assert.Equal(t, 1, c.GetWarningCount(), "Should increment warning count") -} - -func TestValidateRunScripts_StrictModeError(t *testing.T) { - c := &Compiler{strictMode: true} - - workflowData := &WorkflowData{RunScripts: true} - err := c.validateRunScripts(workflowData) - - require.Error(t, err, "Should return error in strict mode") - assert.Contains(t, err.Error(), "strict mode", "Error should mention strict mode") - assert.Contains(t, err.Error(), "supply chain", "Error should mention supply chain risk") -} - -func TestValidateRunScripts_NotSet(t *testing.T) { - c := &Compiler{strictMode: false} - c.ResetWarningCount() - - workflowData := &WorkflowData{RunScripts: false} - err := c.validateRunScripts(workflowData) - - require.NoError(t, err, "Should not return error when run-scripts is not set") - assert.Equal(t, 0, c.GetWarningCount(), "Should not increment warning count") -} - -func TestFrontmatterConfig_RunScripts(t *testing.T) { - frontmatter := map[string]any{ - "run-scripts": true, - "engine": "claude", - } - - config, err := ParseFrontmatterConfig(frontmatter) - require.NoError(t, err, "Should parse frontmatter without error") - require.NotNil(t, config, "Config should not be nil") - - require.NotNil(t, config.RunScripts, "RunScripts should be set") - assert.True(t, *config.RunScripts, "RunScripts should be true") -} - -func TestRuntimeConfig_RunScripts(t *testing.T) { - runtimes := map[string]any{ - "node": map[string]any{ - "version": "20", - "run-scripts": true, - }, - } - - config, err := parseRuntimesConfig(runtimes) - require.NoError(t, err, "Should parse runtimes config without error") - require.NotNil(t, config.Node, "Node config should be set") - require.NotNil(t, config.Node.RunScripts, "Node RunScripts should be set") - assert.True(t, *config.Node.RunScripts, "Node RunScripts should be true") -}