diff --git a/.changeset/patch-compute-safe-output-perms.md b/.changeset/patch-compute-safe-output-perms.md new file mode 100644 index 0000000000..85f8872145 --- /dev/null +++ b/.changeset/patch-compute-safe-output-perms.md @@ -0,0 +1,5 @@ +--- +"gh-aw": patch +--- + +Compute conclusion and consolidated safe-outputs job permissions from the configured safe-outputs so that discussions write permission is only requested when needed. diff --git a/.github/workflows/agent-persona-explorer.lock.yml b/.github/workflows/agent-persona-explorer.lock.yml index 71bf1637d6..f50d132b4c 100644 --- a/.github/workflows/agent-persona-explorer.lock.yml +++ b/.github/workflows/agent-persona-explorer.lock.yml @@ -875,7 +875,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml index 64c5ed0f30..3d617165cc 100644 --- a/.github/workflows/artifacts-summary.lock.yml +++ b/.github/workflows/artifacts-summary.lock.yml @@ -784,7 +784,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index 8f00e60b66..c34888ef80 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -1046,10 +1046,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1362,7 +1361,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/blog-auditor.lock.yml b/.github/workflows/blog-auditor.lock.yml index 082fc6dc41..65f49c6b19 100644 --- a/.github/workflows/blog-auditor.lock.yml +++ b/.github/workflows/blog-auditor.lock.yml @@ -893,7 +893,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/bot-detection.lock.yml b/.github/workflows/bot-detection.lock.yml index 29d9c73460..c0d20bf1fd 100644 --- a/.github/workflows/bot-detection.lock.yml +++ b/.github/workflows/bot-detection.lock.yml @@ -898,9 +898,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/breaking-change-checker.lock.yml b/.github/workflows/breaking-change-checker.lock.yml index bc52a072a3..c2aefcbf84 100644 --- a/.github/workflows/breaking-change-checker.lock.yml +++ b/.github/workflows/breaking-change-checker.lock.yml @@ -824,9 +824,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml index c99cedeb19..17ad48a78a 100644 --- a/.github/workflows/changeset.lock.yml +++ b/.github/workflows/changeset.lock.yml @@ -896,8 +896,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/ci-coach.lock.yml b/.github/workflows/ci-coach.lock.yml index c63e8d407c..df8193ef58 100644 --- a/.github/workflows/ci-coach.lock.yml +++ b/.github/workflows/ci-coach.lock.yml @@ -868,8 +868,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/claude-code-user-docs-review.lock.yml b/.github/workflows/claude-code-user-docs-review.lock.yml index bbe0c6ed33..baa5c54575 100644 --- a/.github/workflows/claude-code-user-docs-review.lock.yml +++ b/.github/workflows/claude-code-user-docs-review.lock.yml @@ -857,7 +857,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/cli-consistency-checker.lock.yml b/.github/workflows/cli-consistency-checker.lock.yml index 3df59a3474..8eb2cb718a 100644 --- a/.github/workflows/cli-consistency-checker.lock.yml +++ b/.github/workflows/cli-consistency-checker.lock.yml @@ -798,9 +798,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index c16af39d18..6fb82896f8 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -887,9 +887,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index 2d3092083d..d29308664d 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -1148,7 +1148,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write pull-requests: write diff --git a/.github/workflows/code-scanning-fixer.lock.yml b/.github/workflows/code-scanning-fixer.lock.yml index f158a61143..468b357d74 100644 --- a/.github/workflows/code-scanning-fixer.lock.yml +++ b/.github/workflows/code-scanning-fixer.lock.yml @@ -890,8 +890,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/code-simplifier.lock.yml b/.github/workflows/code-simplifier.lock.yml index 9b01b43174..bfbf2539e7 100644 --- a/.github/workflows/code-simplifier.lock.yml +++ b/.github/workflows/code-simplifier.lock.yml @@ -793,8 +793,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/commit-changes-analyzer.lock.yml b/.github/workflows/commit-changes-analyzer.lock.yml index 2b9ac6a3fc..45d27537e1 100644 --- a/.github/workflows/commit-changes-analyzer.lock.yml +++ b/.github/workflows/commit-changes-analyzer.lock.yml @@ -838,7 +838,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index 9a7997b57c..2a6815881b 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -948,7 +948,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/copilot-cli-deep-research.lock.yml b/.github/workflows/copilot-cli-deep-research.lock.yml index a676692dab..2ebf1c3509 100644 --- a/.github/workflows/copilot-cli-deep-research.lock.yml +++ b/.github/workflows/copilot-cli-deep-research.lock.yml @@ -855,7 +855,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/copilot-pr-merged-report.lock.yml b/.github/workflows/copilot-pr-merged-report.lock.yml index 1019da2459..8c1a9f3229 100644 --- a/.github/workflows/copilot-pr-merged-report.lock.yml +++ b/.github/workflows/copilot-pr-merged-report.lock.yml @@ -841,7 +841,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml index ca2f14d02c..4dec8f8a4a 100644 --- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml +++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml @@ -942,10 +942,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1238,7 +1237,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml index 41cfeb83f4..de57ab557d 100644 --- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml +++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml @@ -871,7 +871,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml index b50a37d6ec..087c4bb8c7 100644 --- a/.github/workflows/copilot-session-insights.lock.yml +++ b/.github/workflows/copilot-session-insights.lock.yml @@ -1001,10 +1001,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1312,7 +1311,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/craft.lock.yml b/.github/workflows/craft.lock.yml index a289e12b96..9ddcc172fd 100644 --- a/.github/workflows/craft.lock.yml +++ b/.github/workflows/craft.lock.yml @@ -856,7 +856,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write pull-requests: write diff --git a/.github/workflows/daily-choice-test.lock.yml b/.github/workflows/daily-choice-test.lock.yml index 75d2a952dd..5fdc6fe5f1 100644 --- a/.github/workflows/daily-choice-test.lock.yml +++ b/.github/workflows/daily-choice-test.lock.yml @@ -801,11 +801,6 @@ jobs: - test_environment if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim - permissions: - contents: read - discussions: write - issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-cli-tools-tester.lock.yml b/.github/workflows/daily-cli-tools-tester.lock.yml index 6ac26855f0..c63f6434c9 100644 --- a/.github/workflows/daily-cli-tools-tester.lock.yml +++ b/.github/workflows/daily-cli-tools-tester.lock.yml @@ -866,9 +866,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml index cc578c9c67..78b2489b07 100644 --- a/.github/workflows/daily-code-metrics.lock.yml +++ b/.github/workflows/daily-code-metrics.lock.yml @@ -975,10 +975,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1291,7 +1290,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/daily-compiler-quality.lock.yml b/.github/workflows/daily-compiler-quality.lock.yml index d51aa4c966..5a36afd2a2 100644 --- a/.github/workflows/daily-compiler-quality.lock.yml +++ b/.github/workflows/daily-compiler-quality.lock.yml @@ -834,7 +834,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-copilot-token-report.lock.yml b/.github/workflows/daily-copilot-token-report.lock.yml index 119b3a8322..12aa64190a 100644 --- a/.github/workflows/daily-copilot-token-report.lock.yml +++ b/.github/workflows/daily-copilot-token-report.lock.yml @@ -953,10 +953,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1254,7 +1253,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index 8bc33bf250..d99e2029bb 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -885,8 +885,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/daily-file-diet.lock.yml b/.github/workflows/daily-file-diet.lock.yml index db03cf3124..6e36dc5dae 100644 --- a/.github/workflows/daily-file-diet.lock.yml +++ b/.github/workflows/daily-file-diet.lock.yml @@ -837,9 +837,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml index 22a176021c..bc78f805bb 100644 --- a/.github/workflows/daily-firewall-report.lock.yml +++ b/.github/workflows/daily-firewall-report.lock.yml @@ -940,10 +940,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1173,7 +1172,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml index 3ccafb6b3a..457810e0c8 100644 --- a/.github/workflows/daily-issues-report.lock.yml +++ b/.github/workflows/daily-issues-report.lock.yml @@ -961,10 +961,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1216,7 +1215,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/daily-malicious-code-scan.lock.yml b/.github/workflows/daily-malicious-code-scan.lock.yml index 6fe7b76caf..9725dbbd38 100644 --- a/.github/workflows/daily-malicious-code-scan.lock.yml +++ b/.github/workflows/daily-malicious-code-scan.lock.yml @@ -819,9 +819,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write - issues: write - pull-requests: write + security-events: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml index 8d6dfd2dfc..00e924ab18 100644 --- a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml +++ b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml @@ -886,9 +886,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml index 0f2d156779..7848b13673 100644 --- a/.github/workflows/daily-multi-device-docs-tester.lock.yml +++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml @@ -968,10 +968,8 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1215,7 +1213,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write issues: write timeout-minutes: 15 env: diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index a7f63c178f..786e6779ee 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -1015,10 +1015,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1316,7 +1315,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/daily-observability-report.lock.yml b/.github/workflows/daily-observability-report.lock.yml index 0b972579f3..c929ca5a6a 100644 --- a/.github/workflows/daily-observability-report.lock.yml +++ b/.github/workflows/daily-observability-report.lock.yml @@ -923,7 +923,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-performance-summary.lock.yml b/.github/workflows/daily-performance-summary.lock.yml index 10ff2bf856..12760b0d07 100644 --- a/.github/workflows/daily-performance-summary.lock.yml +++ b/.github/workflows/daily-performance-summary.lock.yml @@ -1425,10 +1425,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1650,7 +1649,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/daily-regulatory.lock.yml b/.github/workflows/daily-regulatory.lock.yml index f51b36b391..355fdd6475 100644 --- a/.github/workflows/daily-regulatory.lock.yml +++ b/.github/workflows/daily-regulatory.lock.yml @@ -1321,7 +1321,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml index c6bdc0d322..43cd7517f2 100644 --- a/.github/workflows/daily-repo-chronicle.lock.yml +++ b/.github/workflows/daily-repo-chronicle.lock.yml @@ -875,10 +875,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1108,7 +1107,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/daily-safe-output-optimizer.lock.yml b/.github/workflows/daily-safe-output-optimizer.lock.yml index 157a632bdb..5f90305f34 100644 --- a/.github/workflows/daily-safe-output-optimizer.lock.yml +++ b/.github/workflows/daily-safe-output-optimizer.lock.yml @@ -973,9 +973,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-secrets-analysis.lock.yml b/.github/workflows/daily-secrets-analysis.lock.yml index 8eb8d4db05..269091481d 100644 --- a/.github/workflows/daily-secrets-analysis.lock.yml +++ b/.github/workflows/daily-secrets-analysis.lock.yml @@ -840,7 +840,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-semgrep-scan.lock.yml b/.github/workflows/daily-semgrep-scan.lock.yml index f0e89612dc..6c425d8557 100644 --- a/.github/workflows/daily-semgrep-scan.lock.yml +++ b/.github/workflows/daily-semgrep-scan.lock.yml @@ -833,9 +833,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write - issues: write - pull-requests: write + security-events: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-syntax-error-quality.lock.yml b/.github/workflows/daily-syntax-error-quality.lock.yml index 48e5970787..1af9baf0d3 100644 --- a/.github/workflows/daily-syntax-error-quality.lock.yml +++ b/.github/workflows/daily-syntax-error-quality.lock.yml @@ -836,9 +836,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-team-evolution-insights.lock.yml b/.github/workflows/daily-team-evolution-insights.lock.yml index ca001eb4d5..38b5155fa8 100644 --- a/.github/workflows/daily-team-evolution-insights.lock.yml +++ b/.github/workflows/daily-team-evolution-insights.lock.yml @@ -833,7 +833,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-team-status.lock.yml b/.github/workflows/daily-team-status.lock.yml index ade1166669..7bf890bdfa 100644 --- a/.github/workflows/daily-team-status.lock.yml +++ b/.github/workflows/daily-team-status.lock.yml @@ -809,9 +809,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-testify-uber-super-expert.lock.yml b/.github/workflows/daily-testify-uber-super-expert.lock.yml index 4e46243b3c..ba329fd237 100644 --- a/.github/workflows/daily-testify-uber-super-expert.lock.yml +++ b/.github/workflows/daily-testify-uber-super-expert.lock.yml @@ -885,9 +885,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/daily-workflow-updater.lock.yml b/.github/workflows/daily-workflow-updater.lock.yml index 325a706ebe..32cfd8edde 100644 --- a/.github/workflows/daily-workflow-updater.lock.yml +++ b/.github/workflows/daily-workflow-updater.lock.yml @@ -784,8 +784,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml index be9f4fffe4..e00bdef4c2 100644 --- a/.github/workflows/deep-report.lock.yml +++ b/.github/workflows/deep-report.lock.yml @@ -1063,10 +1063,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1356,7 +1355,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/delight.lock.yml b/.github/workflows/delight.lock.yml index 4a8d36bf8e..1a5275b828 100644 --- a/.github/workflows/delight.lock.yml +++ b/.github/workflows/delight.lock.yml @@ -934,7 +934,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/dependabot-burner.lock.yml b/.github/workflows/dependabot-burner.lock.yml index 8a660fe8dd..33f4399108 100644 --- a/.github/workflows/dependabot-burner.lock.yml +++ b/.github/workflows/dependabot-burner.lock.yml @@ -790,9 +790,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/dependabot-go-checker.lock.yml b/.github/workflows/dependabot-go-checker.lock.yml index cf978cca5c..45d29467c9 100644 --- a/.github/workflows/dependabot-go-checker.lock.yml +++ b/.github/workflows/dependabot-go-checker.lock.yml @@ -836,9 +836,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml index ff6d282ae8..105dce5bf6 100644 --- a/.github/workflows/dev.lock.yml +++ b/.github/workflows/dev.lock.yml @@ -781,8 +781,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml index 6e393b7096..1edd28f7b0 100644 --- a/.github/workflows/developer-docs-consolidator.lock.yml +++ b/.github/workflows/developer-docs-consolidator.lock.yml @@ -960,7 +960,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write pull-requests: write diff --git a/.github/workflows/dictation-prompt.lock.yml b/.github/workflows/dictation-prompt.lock.yml index 8197c4530a..42450718cf 100644 --- a/.github/workflows/dictation-prompt.lock.yml +++ b/.github/workflows/dictation-prompt.lock.yml @@ -787,8 +787,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/docs-noob-tester.lock.yml b/.github/workflows/docs-noob-tester.lock.yml index 6967db5ebf..a80907948e 100644 --- a/.github/workflows/docs-noob-tester.lock.yml +++ b/.github/workflows/docs-noob-tester.lock.yml @@ -829,10 +829,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1057,7 +1056,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml index 1bad88d4d8..b570407782 100644 --- a/.github/workflows/duplicate-code-detector.lock.yml +++ b/.github/workflows/duplicate-code-detector.lock.yml @@ -844,9 +844,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/example-workflow-analyzer.lock.yml b/.github/workflows/example-workflow-analyzer.lock.yml index 5e0998a141..8c0a1ab861 100644 --- a/.github/workflows/example-workflow-analyzer.lock.yml +++ b/.github/workflows/example-workflow-analyzer.lock.yml @@ -896,7 +896,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/firewall-escape.lock.yml b/.github/workflows/firewall-escape.lock.yml index 1c56c45b04..12427db897 100644 --- a/.github/workflows/firewall-escape.lock.yml +++ b/.github/workflows/firewall-escape.lock.yml @@ -858,7 +858,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/functional-pragmatist.lock.yml b/.github/workflows/functional-pragmatist.lock.yml index df2f89019a..ef3c70ee87 100644 --- a/.github/workflows/functional-pragmatist.lock.yml +++ b/.github/workflows/functional-pragmatist.lock.yml @@ -789,8 +789,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/github-mcp-structural-analysis.lock.yml b/.github/workflows/github-mcp-structural-analysis.lock.yml index 7cce08cdb7..e3eaa33b62 100644 --- a/.github/workflows/github-mcp-structural-analysis.lock.yml +++ b/.github/workflows/github-mcp-structural-analysis.lock.yml @@ -927,10 +927,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1170,7 +1169,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index f2fd8f0de9..ffd41f6820 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -922,7 +922,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write pull-requests: write diff --git a/.github/workflows/github-remote-mcp-auth-test.lock.yml b/.github/workflows/github-remote-mcp-auth-test.lock.yml index 3bf369442d..c74d25ffe7 100644 --- a/.github/workflows/github-remote-mcp-auth-test.lock.yml +++ b/.github/workflows/github-remote-mcp-auth-test.lock.yml @@ -790,7 +790,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml index 704d2b1b65..62e09f778f 100644 --- a/.github/workflows/glossary-maintainer.lock.yml +++ b/.github/workflows/glossary-maintainer.lock.yml @@ -863,8 +863,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml index 1d774e3ba5..09cee25482 100644 --- a/.github/workflows/go-fan.lock.yml +++ b/.github/workflows/go-fan.lock.yml @@ -894,7 +894,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/go-logger.lock.yml b/.github/workflows/go-logger.lock.yml index 0726c472b2..a096c492dd 100644 --- a/.github/workflows/go-logger.lock.yml +++ b/.github/workflows/go-logger.lock.yml @@ -1052,8 +1052,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml index ec383373fa..617de540cc 100644 --- a/.github/workflows/go-pattern-detector.lock.yml +++ b/.github/workflows/go-pattern-detector.lock.yml @@ -896,9 +896,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/gpclean.lock.yml b/.github/workflows/gpclean.lock.yml index f7c4eb4fea..05703c0e06 100644 --- a/.github/workflows/gpclean.lock.yml +++ b/.github/workflows/gpclean.lock.yml @@ -827,9 +827,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/hourly-ci-cleaner.lock.yml b/.github/workflows/hourly-ci-cleaner.lock.yml index b7632fce9f..fc44f9c23e 100644 --- a/.github/workflows/hourly-ci-cleaner.lock.yml +++ b/.github/workflows/hourly-ci-cleaner.lock.yml @@ -885,8 +885,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/instructions-janitor.lock.yml b/.github/workflows/instructions-janitor.lock.yml index f3c52fffd3..82ac89fa5d 100644 --- a/.github/workflows/instructions-janitor.lock.yml +++ b/.github/workflows/instructions-janitor.lock.yml @@ -884,8 +884,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml index 41b182bb85..15f4f2768c 100644 --- a/.github/workflows/issue-arborist.lock.yml +++ b/.github/workflows/issue-arborist.lock.yml @@ -913,7 +913,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/issue-classifier.lock.yml b/.github/workflows/issue-classifier.lock.yml index 5da4ec557c..bb12bcabf5 100644 --- a/.github/workflows/issue-classifier.lock.yml +++ b/.github/workflows/issue-classifier.lock.yml @@ -740,7 +740,6 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml index a8f3afd084..30dfac2dde 100644 --- a/.github/workflows/jsweep.lock.yml +++ b/.github/workflows/jsweep.lock.yml @@ -828,8 +828,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/layout-spec-maintainer.lock.yml b/.github/workflows/layout-spec-maintainer.lock.yml index be0e924f41..f9a969fcc9 100644 --- a/.github/workflows/layout-spec-maintainer.lock.yml +++ b/.github/workflows/layout-spec-maintainer.lock.yml @@ -821,8 +821,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index 8405be63ca..7293e496b9 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -858,7 +858,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index 33a8b24554..c34b065267 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -1190,7 +1190,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/mergefest.lock.yml b/.github/workflows/mergefest.lock.yml index 9cd47421af..de1fc362be 100644 --- a/.github/workflows/mergefest.lock.yml +++ b/.github/workflows/mergefest.lock.yml @@ -841,8 +841,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/notion-issue-summary.lock.yml b/.github/workflows/notion-issue-summary.lock.yml index 9aa92b411d..5348209e03 100644 --- a/.github/workflows/notion-issue-summary.lock.yml +++ b/.github/workflows/notion-issue-summary.lock.yml @@ -763,11 +763,6 @@ jobs: - safe_outputs if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim - permissions: - contents: read - discussions: write - issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/org-health-report.lock.yml b/.github/workflows/org-health-report.lock.yml index 6226f3bceb..354163dd2f 100644 --- a/.github/workflows/org-health-report.lock.yml +++ b/.github/workflows/org-health-report.lock.yml @@ -878,10 +878,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1106,7 +1105,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml index 0cedc80be7..0ccafc8b3c 100644 --- a/.github/workflows/plan.lock.yml +++ b/.github/workflows/plan.lock.yml @@ -894,7 +894,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index cee8a32155..cad91ef653 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1439,7 +1439,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write pull-requests: write diff --git a/.github/workflows/portfolio-analyst.lock.yml b/.github/workflows/portfolio-analyst.lock.yml index 547045e7da..98c2af61ec 100644 --- a/.github/workflows/portfolio-analyst.lock.yml +++ b/.github/workflows/portfolio-analyst.lock.yml @@ -951,10 +951,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1184,7 +1183,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml index 27bda2ae92..16de6b40ae 100644 --- a/.github/workflows/prompt-clustering-analysis.lock.yml +++ b/.github/workflows/prompt-clustering-analysis.lock.yml @@ -989,7 +989,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml index 763458211b..2d14154805 100644 --- a/.github/workflows/python-data-charts.lock.yml +++ b/.github/workflows/python-data-charts.lock.yml @@ -941,10 +941,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1169,7 +1168,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index 72de7e2063..efa83030ce 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1001,7 +1001,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write pull-requests: write diff --git a/.github/workflows/refiner.lock.yml b/.github/workflows/refiner.lock.yml index f8f6563121..ef561db308 100644 --- a/.github/workflows/refiner.lock.yml +++ b/.github/workflows/refiner.lock.yml @@ -836,7 +836,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write pull-requests: write diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml index 7b4cc1e920..8986adc61a 100644 --- a/.github/workflows/release.lock.yml +++ b/.github/workflows/release.lock.yml @@ -802,10 +802,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write - issues: write - pull-requests: write + contents: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/repo-audit-analyzer.lock.yml b/.github/workflows/repo-audit-analyzer.lock.yml index ee35dc9b8d..f3a0d18ce1 100644 --- a/.github/workflows/repo-audit-analyzer.lock.yml +++ b/.github/workflows/repo-audit-analyzer.lock.yml @@ -820,7 +820,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/repo-tree-map.lock.yml b/.github/workflows/repo-tree-map.lock.yml index a37d3704c5..940f6e3de5 100644 --- a/.github/workflows/repo-tree-map.lock.yml +++ b/.github/workflows/repo-tree-map.lock.yml @@ -780,7 +780,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/repository-quality-improver.lock.yml b/.github/workflows/repository-quality-improver.lock.yml index fedd2272a9..f72f0cdc40 100644 --- a/.github/workflows/repository-quality-improver.lock.yml +++ b/.github/workflows/repository-quality-improver.lock.yml @@ -821,7 +821,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml index 3831fc13ae..1137d0693e 100644 --- a/.github/workflows/research.lock.yml +++ b/.github/workflows/research.lock.yml @@ -808,7 +808,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml index 1fca47361c..1059b0e27b 100644 --- a/.github/workflows/safe-output-health.lock.yml +++ b/.github/workflows/safe-output-health.lock.yml @@ -950,7 +950,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml index 1b23bcd36d..905f2b0b29 100644 --- a/.github/workflows/schema-consistency-checker.lock.yml +++ b/.github/workflows/schema-consistency-checker.lock.yml @@ -859,7 +859,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/security-compliance.lock.yml b/.github/workflows/security-compliance.lock.yml index fe950f7dd0..4977130387 100644 --- a/.github/workflows/security-compliance.lock.yml +++ b/.github/workflows/security-compliance.lock.yml @@ -863,9 +863,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml index dbcfaad3bc..b89fdcc8ce 100644 --- a/.github/workflows/semantic-function-refactor.lock.yml +++ b/.github/workflows/semantic-function-refactor.lock.yml @@ -924,9 +924,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/sergo.lock.yml b/.github/workflows/sergo.lock.yml index a03d6959d4..9cd00991e9 100644 --- a/.github/workflows/sergo.lock.yml +++ b/.github/workflows/sergo.lock.yml @@ -893,7 +893,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/slide-deck-maintainer.lock.yml b/.github/workflows/slide-deck-maintainer.lock.yml index 9495664e5e..cf869553cd 100644 --- a/.github/workflows/slide-deck-maintainer.lock.yml +++ b/.github/workflows/slide-deck-maintainer.lock.yml @@ -884,8 +884,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index eb32fca602..c2b084cd7a 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -1720,6 +1720,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: + actions: write contents: read discussions: write issues: write diff --git a/.github/workflows/smoke-project.lock.yml b/.github/workflows/smoke-project.lock.yml index 36a8367f33..fc211a5dee 100644 --- a/.github/workflows/smoke-project.lock.yml +++ b/.github/workflows/smoke-project.lock.yml @@ -1231,7 +1231,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write pull-requests: write diff --git a/.github/workflows/stale-repo-identifier.lock.yml b/.github/workflows/stale-repo-identifier.lock.yml index 3c6c5afa2c..1c188386b8 100644 --- a/.github/workflows/stale-repo-identifier.lock.yml +++ b/.github/workflows/stale-repo-identifier.lock.yml @@ -947,10 +947,8 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1175,7 +1173,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write issues: write timeout-minutes: 15 env: diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml index f0a1737806..ad24ef378f 100644 --- a/.github/workflows/static-analysis-report.lock.yml +++ b/.github/workflows/static-analysis-report.lock.yml @@ -933,7 +933,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/step-name-alignment.lock.yml b/.github/workflows/step-name-alignment.lock.yml index 383808fa05..e5c354258d 100644 --- a/.github/workflows/step-name-alignment.lock.yml +++ b/.github/workflows/step-name-alignment.lock.yml @@ -889,9 +889,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/super-linter.lock.yml b/.github/workflows/super-linter.lock.yml index 563929b265..05e720b81e 100644 --- a/.github/workflows/super-linter.lock.yml +++ b/.github/workflows/super-linter.lock.yml @@ -839,9 +839,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index e2f0044b7c..986e075ee0 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -927,7 +927,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write pull-requests: write diff --git a/.github/workflows/terminal-stylist.lock.yml b/.github/workflows/terminal-stylist.lock.yml index 321fc7f61d..b928bbd635 100644 --- a/.github/workflows/terminal-stylist.lock.yml +++ b/.github/workflows/terminal-stylist.lock.yml @@ -784,7 +784,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/test-create-pr-error-handling.lock.yml b/.github/workflows/test-create-pr-error-handling.lock.yml index 8d6276999e..d8b45b900a 100644 --- a/.github/workflows/test-create-pr-error-handling.lock.yml +++ b/.github/workflows/test-create-pr-error-handling.lock.yml @@ -858,8 +858,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/test-dispatcher.lock.yml b/.github/workflows/test-dispatcher.lock.yml index fde58b8145..b59105be5e 100644 --- a/.github/workflows/test-dispatcher.lock.yml +++ b/.github/workflows/test-dispatcher.lock.yml @@ -734,10 +734,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write - issues: write - pull-requests: write + actions: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/test-project-url-default.lock.yml b/.github/workflows/test-project-url-default.lock.yml index fe24bb54c1..6c4694a576 100644 --- a/.github/workflows/test-project-url-default.lock.yml +++ b/.github/workflows/test-project-url-default.lock.yml @@ -978,9 +978,6 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write - issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/tidy.lock.yml b/.github/workflows/tidy.lock.yml index 659e576034..7a6c53a1be 100644 --- a/.github/workflows/tidy.lock.yml +++ b/.github/workflows/tidy.lock.yml @@ -910,8 +910,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/typist.lock.yml b/.github/workflows/typist.lock.yml index 2b9e87003f..4fdf691183 100644 --- a/.github/workflows/typist.lock.yml +++ b/.github/workflows/typist.lock.yml @@ -865,7 +865,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/ubuntu-image-analyzer.lock.yml b/.github/workflows/ubuntu-image-analyzer.lock.yml index da308a6151..1ba4caa1c8 100644 --- a/.github/workflows/ubuntu-image-analyzer.lock.yml +++ b/.github/workflows/ubuntu-image-analyzer.lock.yml @@ -814,8 +814,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read - discussions: write + contents: write issues: write pull-requests: write outputs: diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index 373e8fdca4..88700c5b9c 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1068,7 +1068,7 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write pull-requests: write diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml index 2b2cd4becd..62d4e74d76 100644 --- a/.github/workflows/video-analyzer.lock.yml +++ b/.github/workflows/video-analyzer.lock.yml @@ -833,9 +833,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml index b636af86c4..2cf7e3488e 100644 --- a/.github/workflows/weekly-issue-summary.lock.yml +++ b/.github/workflows/weekly-issue-summary.lock.yml @@ -858,10 +858,9 @@ jobs: if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} @@ -1090,7 +1089,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') runs-on: ubuntu-slim permissions: - contents: read + contents: write discussions: write issues: write timeout-minutes: 15 diff --git a/.github/workflows/workflow-generator.lock.yml b/.github/workflows/workflow-generator.lock.yml index 96ab20991d..97ceac36b2 100644 --- a/.github/workflows/workflow-generator.lock.yml +++ b/.github/workflows/workflow-generator.lock.yml @@ -885,9 +885,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/workflow-normalizer.lock.yml b/.github/workflows/workflow-normalizer.lock.yml index eeb91d4965..5a21e92779 100644 --- a/.github/workflows/workflow-normalizer.lock.yml +++ b/.github/workflows/workflow-normalizer.lock.yml @@ -869,9 +869,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read - discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/.github/workflows/workflow-skill-extractor.lock.yml b/.github/workflows/workflow-skill-extractor.lock.yml index aecf9955aa..1d3220f1a9 100644 --- a/.github/workflows/workflow-skill-extractor.lock.yml +++ b/.github/workflows/workflow-skill-extractor.lock.yml @@ -875,7 +875,6 @@ jobs: contents: read discussions: write issues: write - pull-requests: write outputs: noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} diff --git a/pkg/workflow/compiler_safe_outputs_job.go b/pkg/workflow/compiler_safe_outputs_job.go index b4b21a576b..b88e980d81 100644 --- a/pkg/workflow/compiler_safe_outputs_job.go +++ b/pkg/workflow/compiler_safe_outputs_job.go @@ -27,16 +27,20 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa var steps []string var outputs = make(map[string]string) - var permissions = NewPermissions() var safeOutputStepNames []string + // Compute permissions based on configured safe outputs (principle of least privilege) + permissions := computePermissionsForSafeOutputs(data.SafeOutputs) + // Track whether threat detection job is enabled for step conditions threatDetectionEnabled := data.SafeOutputs.ThreatDetection != nil // Add GitHub App token minting step if app is configured if data.SafeOutputs.App != nil { consolidatedSafeOutputsJobLog.Print("Adding GitHub App token minting step") - // We'll compute permissions after collecting all step requirements + // Prepend GitHub App token step before other steps + appTokenSteps := c.buildGitHubAppTokenMintStep(data.SafeOutputs.App, permissions) + steps = append(steps, appTokenSteps...) } // Add setup action to copy JavaScript files @@ -103,8 +107,7 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa steps = append(steps, " script: |\n") steps = append(steps, generateGitHubScriptWithRequire("unlock-issue.cjs")) - // Add permissions needed for unlocking issues - permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + // Note: Permissions for unlocking issues are computed centrally by computePermissionsForSafeOutputs() } // === Build safe output steps === @@ -162,80 +165,8 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa outputs["create_discussion_errors"] = "${{ steps.process_safe_outputs.outputs.create_discussion_errors }}" outputs["create_discussion_error_count"] = "${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}" - // Merge permissions for all handler-managed types - if data.SafeOutputs.CreateIssues != nil { - permissions.Merge(NewPermissionsContentsReadIssuesWrite()) - } - if data.SafeOutputs.CreateDiscussions != nil { - permissions.Merge(NewPermissionsContentsReadIssuesWriteDiscussionsWrite()) - } - if data.SafeOutputs.AddComments != nil { - permissions.Merge(NewPermissionsContentsReadIssuesWritePRWriteDiscussionsWrite()) - } - if data.SafeOutputs.CloseIssues != nil { - permissions.Merge(NewPermissionsContentsReadIssuesWrite()) - } - if data.SafeOutputs.CloseDiscussions != nil { - permissions.Merge(NewPermissionsContentsReadDiscussionsWrite()) - } - if data.SafeOutputs.AddLabels != nil { - permissions.Merge(NewPermissionsContentsReadIssuesWritePRWrite()) - } - if data.SafeOutputs.RemoveLabels != nil { - permissions.Merge(NewPermissionsContentsReadIssuesWritePRWrite()) - } - if data.SafeOutputs.UpdateIssues != nil { - permissions.Merge(NewPermissionsContentsReadIssuesWrite()) - } - if data.SafeOutputs.UpdateDiscussions != nil { - permissions.Merge(NewPermissionsContentsReadDiscussionsWrite()) - } - if data.SafeOutputs.LinkSubIssue != nil { - permissions.Merge(NewPermissionsContentsReadIssuesWrite()) - } - if data.SafeOutputs.UpdateRelease != nil { - permissions.Merge(NewPermissionsContentsWrite()) - } - if data.SafeOutputs.CreatePullRequestReviewComments != nil || data.SafeOutputs.SubmitPullRequestReview != nil { - permissions.Merge(NewPermissionsContentsReadPRWrite()) - } - if data.SafeOutputs.CreatePullRequests != nil { - // Check fallback-as-issue setting to determine permissions - if getFallbackAsIssue(data.SafeOutputs.CreatePullRequests) { - permissions.Merge(NewPermissionsContentsWriteIssuesWritePRWrite()) - } else { - permissions.Merge(NewPermissionsContentsWritePRWrite()) - } - } - if data.SafeOutputs.PushToPullRequestBranch != nil { - permissions.Merge(NewPermissionsContentsWriteIssuesWritePRWrite()) - } - if data.SafeOutputs.UpdatePullRequests != nil { - permissions.Merge(NewPermissionsContentsReadPRWrite()) - } - if data.SafeOutputs.ClosePullRequests != nil { - permissions.Merge(NewPermissionsContentsReadPRWrite()) - } - if data.SafeOutputs.MarkPullRequestAsReadyForReview != nil { - permissions.Merge(NewPermissionsContentsReadPRWrite()) - } - if data.SafeOutputs.HideComment != nil { - permissions.Merge(NewPermissionsContentsReadIssuesWritePRWriteDiscussionsWrite()) - } - if data.SafeOutputs.DispatchWorkflow != nil { - permissions.Merge(NewPermissionsActionsWrite()) - } - // Project-related types now handled by the unified handler - // (not the separate project handler manager step) - if data.SafeOutputs.CreateProjects != nil { - permissions.Merge(NewPermissionsContentsReadProjectsWrite()) - } - if data.SafeOutputs.UpdateProjects != nil { - permissions.Merge(NewPermissionsContentsReadProjectsWrite()) - } - if data.SafeOutputs.CreateProjectStatusUpdates != nil { - permissions.Merge(NewPermissionsContentsReadProjectsWrite()) - } + // Note: Permissions are now computed centrally by computePermissionsForSafeOutputs() + // at the start of this function to ensure consistent permission calculation // If create-issue is configured with assignees: copilot, run a follow-up step to // assign the Copilot coding agent. The handler manager exports the list via @@ -265,7 +196,7 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa outputs["assign_to_agent_assignment_errors"] = "${{ steps.assign_to_agent.outputs.assignment_errors }}" outputs["assign_to_agent_assignment_error_count"] = "${{ steps.assign_to_agent.outputs.assignment_error_count }}" - permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + // Note: Permissions are computed centrally by computePermissionsForSafeOutputs() } // 4. Create Agent Session step @@ -278,7 +209,7 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa outputs["create_agent_session_session_number"] = "${{ steps.create_agent_session.outputs.session_number }}" outputs["create_agent_session_session_url"] = "${{ steps.create_agent_session.outputs.session_url }}" - permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + // Note: Permissions are computed centrally by computePermissionsForSafeOutputs() } // Note: Create Pull Request is now handled by the handler manager @@ -289,35 +220,31 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa // Note: Create Code Scanning Alert is now handled by the handler manager // The permissions are configured in the handler manager section above - if data.SafeOutputs.CreateCodeScanningAlerts != nil { - permissions.Merge(NewPermissionsContentsReadSecurityEventsWrite()) - } + // Note: Permissions are computed centrally by computePermissionsForSafeOutputs() // Note: Create Project Status Update is now handled by the handler manager // The permissions are configured in the handler manager section above - if data.SafeOutputs.CreateProjectStatusUpdates != nil { - permissions.Merge(NewPermissionsContentsReadProjectsWrite()) - } + // Note: Permissions are computed centrally by computePermissionsForSafeOutputs() // Note: Add Reviewer is now handled by the handler manager // The outputs and permissions are configured in the handler manager section above if data.SafeOutputs.AddReviewer != nil { outputs["add_reviewer_reviewers_added"] = "${{ steps.process_safe_outputs.outputs.reviewers_added }}" - permissions.Merge(NewPermissionsContentsReadPRWrite()) + // Note: Permissions are computed centrally by computePermissionsForSafeOutputs() } // Note: Assign Milestone is now handled by the handler manager // The outputs and permissions are configured in the handler manager section above if data.SafeOutputs.AssignMilestone != nil { outputs["assign_milestone_milestone_assigned"] = "${{ steps.process_safe_outputs.outputs.milestone_assigned }}" - permissions.Merge(NewPermissionsContentsReadIssuesWritePRWrite()) + // Note: Permissions are computed centrally by computePermissionsForSafeOutputs() } // Note: Assign To User is now handled by the handler manager // The outputs and permissions are configured in the handler manager section above if data.SafeOutputs.AssignToUser != nil { outputs["assign_to_user_assigned"] = "${{ steps.process_safe_outputs.outputs.assigned }}" - permissions.Merge(NewPermissionsContentsReadIssuesWritePRWrite()) + // Note: Permissions are computed centrally by computePermissionsForSafeOutputs() } // Note: Update Pull Request step - now handled by handler manager diff --git a/pkg/workflow/notify_comment.go b/pkg/workflow/notify_comment.go index 99faeb77a6..252acdc153 100644 --- a/pkg/workflow/notify_comment.go +++ b/pkg/workflow/notify_comment.go @@ -57,8 +57,8 @@ func (c *Compiler) buildConclusionJob(data *WorkflowData, mainJobName string, sa // Add GitHub App token minting step if app is configured if data.SafeOutputs.App != nil { - // Use permissions for the conclusion job - permissions := NewPermissionsContentsReadIssuesWritePRWriteDiscussionsWrite() + // Compute permissions based on configured safe outputs (principle of least privilege) + permissions := computePermissionsForSafeOutputs(data.SafeOutputs) steps = append(steps, c.buildGitHubAppTokenMintStep(data.SafeOutputs.App, permissions)...) } @@ -409,11 +409,14 @@ func (c *Compiler) buildConclusionJob(data *WorkflowData, mainJobName string, sa outputs["total_count"] = "${{ steps.missing_tool.outputs.total_count }}" } + // Compute permissions based on configured safe outputs (principle of least privilege) + permissions := computePermissionsForSafeOutputs(data.SafeOutputs) + job := &Job{ Name: "conclusion", If: condition.Render(), RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), - Permissions: NewPermissionsContentsReadIssuesWritePRWriteDiscussionsWrite().RenderToYAML(), + Permissions: permissions.RenderToYAML(), Steps: steps, Needs: needs, Outputs: outputs, diff --git a/pkg/workflow/notify_comment_test.go b/pkg/workflow/notify_comment_test.go index 1548b03416..5344421213 100644 --- a/pkg/workflow/notify_comment_test.go +++ b/pkg/workflow/notify_comment_test.go @@ -205,16 +205,23 @@ func TestConclusionJob(t *testing.T) { } } - // Check permissions - if !strings.Contains(job.Permissions, "issues: write") { - t.Error("Expected 'issues: write' permission in conclusion job") - } - if !strings.Contains(job.Permissions, "pull-requests: write") { - t.Error("Expected 'pull-requests: write' permission in conclusion job") - } - if !strings.Contains(job.Permissions, "discussions: write") { - t.Error("Expected 'discussions: write' permission in conclusion job") + // Check permissions based on what safe-outputs are configured + // When add-comment is configured, it requires issues, pull-requests, and discussions permissions + // When only missing_tool/noop is configured, minimal permissions are needed + if tt.addCommentConfig { + // add-comment requires full write permissions + if !strings.Contains(job.Permissions, "issues: write") { + t.Error("Expected 'issues: write' permission when add-comment is configured") + } + if !strings.Contains(job.Permissions, "pull-requests: write") { + t.Error("Expected 'pull-requests: write' permission when add-comment is configured") + } + if !strings.Contains(job.Permissions, "discussions: write") { + t.Error("Expected 'discussions: write' permission when add-comment is configured") + } } + // No need to check for specific permissions when only noop/missing_tool is configured + // as they don't require write permissions on their own // Check that the job has the update reaction step stepsYAML := strings.Join(job.Steps, "") diff --git a/pkg/workflow/safe_outputs_permissions.go b/pkg/workflow/safe_outputs_permissions.go new file mode 100644 index 0000000000..ab047791c3 --- /dev/null +++ b/pkg/workflow/safe_outputs_permissions.go @@ -0,0 +1,160 @@ +package workflow + +import "github.com/github/gh-aw/pkg/logger" + +var safeOutputsPermissionsLog = logger.New("workflow:safe_outputs_permissions") + +// computePermissionsForSafeOutputs computes the minimal required permissions +// based on the configured safe-outputs. This function is used by both the +// consolidated safe outputs job and the conclusion job to ensure they only +// request the permissions they actually need. +// +// This implements the principle of least privilege by only including +// permissions that are required by the configured safe outputs. +func computePermissionsForSafeOutputs(safeOutputs *SafeOutputsConfig) *Permissions { + if safeOutputs == nil { + safeOutputsPermissionsLog.Print("No safe outputs configured, returning empty permissions") + return NewPermissions() + } + + permissions := NewPermissions() + + // Merge permissions for all handler-managed types + if safeOutputs.CreateIssues != nil { + safeOutputsPermissionsLog.Print("Adding permissions for create-issue") + permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + } + if safeOutputs.CreateDiscussions != nil { + safeOutputsPermissionsLog.Print("Adding permissions for create-discussion") + permissions.Merge(NewPermissionsContentsReadIssuesWriteDiscussionsWrite()) + } + if safeOutputs.AddComments != nil { + safeOutputsPermissionsLog.Print("Adding permissions for add-comment") + permissions.Merge(NewPermissionsContentsReadIssuesWritePRWriteDiscussionsWrite()) + } + if safeOutputs.CloseIssues != nil { + safeOutputsPermissionsLog.Print("Adding permissions for close-issue") + permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + } + if safeOutputs.CloseDiscussions != nil { + safeOutputsPermissionsLog.Print("Adding permissions for close-discussion") + permissions.Merge(NewPermissionsContentsReadDiscussionsWrite()) + } + if safeOutputs.AddLabels != nil { + safeOutputsPermissionsLog.Print("Adding permissions for add-labels") + permissions.Merge(NewPermissionsContentsReadIssuesWritePRWrite()) + } + if safeOutputs.RemoveLabels != nil { + safeOutputsPermissionsLog.Print("Adding permissions for remove-labels") + permissions.Merge(NewPermissionsContentsReadIssuesWritePRWrite()) + } + if safeOutputs.UpdateIssues != nil { + safeOutputsPermissionsLog.Print("Adding permissions for update-issue") + permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + } + if safeOutputs.UpdateDiscussions != nil { + safeOutputsPermissionsLog.Print("Adding permissions for update-discussion") + permissions.Merge(NewPermissionsContentsReadDiscussionsWrite()) + } + if safeOutputs.LinkSubIssue != nil { + safeOutputsPermissionsLog.Print("Adding permissions for link-sub-issue") + permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + } + if safeOutputs.UpdateRelease != nil { + safeOutputsPermissionsLog.Print("Adding permissions for update-release") + permissions.Merge(NewPermissionsContentsWrite()) + } + if safeOutputs.CreatePullRequestReviewComments != nil || safeOutputs.SubmitPullRequestReview != nil { + safeOutputsPermissionsLog.Print("Adding permissions for create-pr-review-comment or submit-pr-review") + permissions.Merge(NewPermissionsContentsReadPRWrite()) + } + if safeOutputs.CreatePullRequests != nil { + // Check fallback-as-issue setting to determine permissions + if getFallbackAsIssue(safeOutputs.CreatePullRequests) { + safeOutputsPermissionsLog.Print("Adding permissions for create-pull-request with fallback-as-issue") + permissions.Merge(NewPermissionsContentsWriteIssuesWritePRWrite()) + } else { + safeOutputsPermissionsLog.Print("Adding permissions for create-pull-request") + permissions.Merge(NewPermissionsContentsWritePRWrite()) + } + } + if safeOutputs.PushToPullRequestBranch != nil { + safeOutputsPermissionsLog.Print("Adding permissions for push-to-pull-request-branch") + permissions.Merge(NewPermissionsContentsWriteIssuesWritePRWrite()) + } + if safeOutputs.UpdatePullRequests != nil { + safeOutputsPermissionsLog.Print("Adding permissions for update-pull-request") + permissions.Merge(NewPermissionsContentsReadPRWrite()) + } + if safeOutputs.ClosePullRequests != nil { + safeOutputsPermissionsLog.Print("Adding permissions for close-pull-request") + permissions.Merge(NewPermissionsContentsReadPRWrite()) + } + if safeOutputs.MarkPullRequestAsReadyForReview != nil { + safeOutputsPermissionsLog.Print("Adding permissions for mark-pull-request-as-ready-for-review") + permissions.Merge(NewPermissionsContentsReadPRWrite()) + } + if safeOutputs.HideComment != nil { + safeOutputsPermissionsLog.Print("Adding permissions for hide-comment") + permissions.Merge(NewPermissionsContentsReadIssuesWritePRWriteDiscussionsWrite()) + } + if safeOutputs.DispatchWorkflow != nil { + safeOutputsPermissionsLog.Print("Adding permissions for dispatch-workflow") + permissions.Merge(NewPermissionsActionsWrite()) + } + // Project-related types + if safeOutputs.CreateProjects != nil { + safeOutputsPermissionsLog.Print("Adding permissions for create-project") + permissions.Merge(NewPermissionsContentsReadProjectsWrite()) + } + if safeOutputs.UpdateProjects != nil { + safeOutputsPermissionsLog.Print("Adding permissions for update-project") + permissions.Merge(NewPermissionsContentsReadProjectsWrite()) + } + if safeOutputs.CreateProjectStatusUpdates != nil { + safeOutputsPermissionsLog.Print("Adding permissions for create-project-status-update") + permissions.Merge(NewPermissionsContentsReadProjectsWrite()) + } + if safeOutputs.AssignToAgent != nil { + safeOutputsPermissionsLog.Print("Adding permissions for assign-to-agent") + permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + } + if safeOutputs.CreateAgentSessions != nil { + safeOutputsPermissionsLog.Print("Adding permissions for create-agent-session") + permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + } + if safeOutputs.CreateCodeScanningAlerts != nil { + safeOutputsPermissionsLog.Print("Adding permissions for create-code-scanning-alert") + permissions.Merge(NewPermissionsContentsReadSecurityEventsWrite()) + } + if safeOutputs.AutofixCodeScanningAlert != nil { + safeOutputsPermissionsLog.Print("Adding permissions for autofix-code-scanning-alert") + permissions.Merge(NewPermissionsContentsReadSecurityEventsWriteActionsRead()) + } + if safeOutputs.AssignToUser != nil { + safeOutputsPermissionsLog.Print("Adding permissions for assign-to-user") + permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + } + if safeOutputs.UnassignFromUser != nil { + safeOutputsPermissionsLog.Print("Adding permissions for unassign-from-user") + permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + } + if safeOutputs.AssignMilestone != nil { + safeOutputsPermissionsLog.Print("Adding permissions for assign-milestone") + permissions.Merge(NewPermissionsContentsReadIssuesWrite()) + } + if safeOutputs.AddReviewer != nil { + safeOutputsPermissionsLog.Print("Adding permissions for add-reviewer") + permissions.Merge(NewPermissionsContentsReadPRWrite()) + } + if safeOutputs.UploadAssets != nil { + safeOutputsPermissionsLog.Print("Adding permissions for upload-asset") + permissions.Merge(NewPermissionsContentsWrite()) + } + + // NoOp and MissingTool don't require write permissions beyond what's already included + // They only need to comment if add-comment is already configured + + safeOutputsPermissionsLog.Printf("Computed permissions with %d scopes", len(permissions.permissions)) + return permissions +} diff --git a/pkg/workflow/safe_outputs_permissions_test.go b/pkg/workflow/safe_outputs_permissions_test.go new file mode 100644 index 0000000000..70a572a593 --- /dev/null +++ b/pkg/workflow/safe_outputs_permissions_test.go @@ -0,0 +1,315 @@ +package workflow + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestComputePermissionsForSafeOutputs(t *testing.T) { + tests := []struct { + name string + safeOutputs *SafeOutputsConfig + expected map[PermissionScope]PermissionLevel + }{ + { + name: "nil safe outputs returns empty permissions", + safeOutputs: nil, + expected: map[PermissionScope]PermissionLevel{}, + }, + { + name: "create-issue only - no discussions permission", + safeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionIssues: PermissionWrite, + }, + }, + { + name: "create-discussion requires discussions permission", + safeOutputs: &SafeOutputsConfig{ + CreateDiscussions: &CreateDiscussionsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionIssues: PermissionWrite, + PermissionDiscussions: PermissionWrite, + }, + }, + { + name: "close-discussion requires discussions permission", + safeOutputs: &SafeOutputsConfig{ + CloseDiscussions: &CloseDiscussionsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionDiscussions: PermissionWrite, + }, + }, + { + name: "update-discussion requires discussions permission", + safeOutputs: &SafeOutputsConfig{ + UpdateDiscussions: &UpdateDiscussionsConfig{ + UpdateEntityConfig: UpdateEntityConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionDiscussions: PermissionWrite, + }, + }, + { + name: "add-comment includes all write permissions including discussions", + safeOutputs: &SafeOutputsConfig{ + AddComments: &AddCommentsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionIssues: PermissionWrite, + PermissionPullRequests: PermissionWrite, + PermissionDiscussions: PermissionWrite, + }, + }, + { + name: "hide-comment includes all write permissions including discussions", + safeOutputs: &SafeOutputsConfig{ + HideComment: &HideCommentConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionIssues: PermissionWrite, + PermissionPullRequests: PermissionWrite, + PermissionDiscussions: PermissionWrite, + }, + }, + { + name: "add-labels only - no discussions permission", + safeOutputs: &SafeOutputsConfig{ + AddLabels: &AddLabelsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 5}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionIssues: PermissionWrite, + PermissionPullRequests: PermissionWrite, + }, + }, + { + name: "remove-labels only - no discussions permission", + safeOutputs: &SafeOutputsConfig{ + RemoveLabels: &RemoveLabelsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 2}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionIssues: PermissionWrite, + PermissionPullRequests: PermissionWrite, + }, + }, + { + name: "close-issue only - no discussions permission", + safeOutputs: &SafeOutputsConfig{ + CloseIssues: &CloseIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionIssues: PermissionWrite, + }, + }, + { + name: "close-pull-request only - no discussions permission", + safeOutputs: &SafeOutputsConfig{ + ClosePullRequests: &ClosePullRequestsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionPullRequests: PermissionWrite, + }, + }, + { + name: "create-pull-request with fallback-as-issue (default) - includes issues permission", + safeOutputs: &SafeOutputsConfig{ + CreatePullRequests: &CreatePullRequestsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionWrite, + PermissionIssues: PermissionWrite, + PermissionPullRequests: PermissionWrite, + }, + }, + { + name: "create-pull-request with fallback-as-issue false - no issues permission", + safeOutputs: &SafeOutputsConfig{ + CreatePullRequests: &CreatePullRequestsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + FallbackAsIssue: ptrBool(false), + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionWrite, + PermissionPullRequests: PermissionWrite, + }, + }, + { + name: "multiple safe outputs without discussions - no discussions permission", + safeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + AddLabels: &AddLabelsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 5}, + }, + AssignToUser: &AssignToUserConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionIssues: PermissionWrite, + PermissionPullRequests: PermissionWrite, + }, + }, + { + name: "multiple safe outputs with one discussion - includes discussions permission", + safeOutputs: &SafeOutputsConfig{ + CreateIssues: &CreateIssuesConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + CreateDiscussions: &CreateDiscussionsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + AddLabels: &AddLabelsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 5}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionIssues: PermissionWrite, + PermissionPullRequests: PermissionWrite, + PermissionDiscussions: PermissionWrite, + }, + }, + { + name: "upload-asset requires contents write", + safeOutputs: &SafeOutputsConfig{ + UploadAssets: &UploadAssetsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionWrite, + }, + }, + { + name: "create-code-scanning-alert requires security-events write", + safeOutputs: &SafeOutputsConfig{ + CreateCodeScanningAlerts: &CreateCodeScanningAlertsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionSecurityEvents: PermissionWrite, + }, + }, + { + name: "autofix-code-scanning-alert requires security-events and actions", + safeOutputs: &SafeOutputsConfig{ + AutofixCodeScanningAlert: &AutofixCodeScanningAlertConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionSecurityEvents: PermissionWrite, + PermissionActions: PermissionRead, + }, + }, + { + name: "dispatch-workflow requires actions write", + safeOutputs: &SafeOutputsConfig{ + DispatchWorkflow: &DispatchWorkflowConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionActions: PermissionWrite, + }, + }, + { + name: "create-project requires organization-projects write", + safeOutputs: &SafeOutputsConfig{ + CreateProjects: &CreateProjectsConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 1}, + }, + }, + expected: map[PermissionScope]PermissionLevel{ + PermissionContents: PermissionRead, + PermissionOrganizationProj: PermissionWrite, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + permissions := computePermissionsForSafeOutputs(tt.safeOutputs) + require.NotNil(t, permissions, "Permissions should not be nil") + + // Check that all expected permissions are present + for scope, expectedLevel := range tt.expected { + actualLevel, exists := permissions.Get(scope) + assert.True(t, exists, "Permission scope %s should exist", scope) + assert.Equal(t, expectedLevel, actualLevel, "Permission level for %s should match", scope) + } + + // Check that no unexpected permissions are present + for scope := range permissions.permissions { + _, expected := tt.expected[scope] + assert.True(t, expected, "Unexpected permission scope: %s", scope) + } + }) + } +} + +func TestComputePermissionsForSafeOutputs_NoOpAndMissingTool(t *testing.T) { + // NoOp and MissingTool don't add any permissions on their own + // They rely on add-comment permissions if comments are needed + safeOutputs := &SafeOutputsConfig{ + NoOp: &NoOpConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 5}, + }, + MissingTool: &MissingToolConfig{ + BaseSafeOutputConfig: BaseSafeOutputConfig{Max: 3}, + }, + } + + permissions := computePermissionsForSafeOutputs(safeOutputs) + require.NotNil(t, permissions, "Permissions should not be nil") + + // NoOp and MissingTool alone don't require any permissions + // The conclusion job will handle commenting through add-comment if configured + assert.Empty(t, permissions.permissions, "NoOp and MissingTool alone should not add permissions") +}