security: scope reusable-workflow secrets (least privilege) over secrets: inherit
The swarm (reviewing the mort/executus rollout PRs) correctly flagged that `secrets: inherit` forwards EVERY caller secret to the reusable review workflow — registry/deploy/db creds the reviewer never touches. Fix: - review-reusable.yml: declare workflow_call.secrets (all optional) so a caller can forward only what the reviewer needs. - adversarial-review.yml (gadfly's own caller) + examples/reusable.yml: replace `secrets: inherit` with an explicit forward of just OLLAMA_CLOUD_API_KEY / CLAUDE_CODE_OAUTH_TOKEN / findings tokens. GITEA_TOKEN stays automatic. - Docs (README, examples) updated; also advise pinning consumers to an immutable @<sha> instead of @main (supply-chain, the other finding). gadfly's own review on this PR exercises the explicit-secrets path (local reusable ref) — validating it on the act_runner before mort/executus adopt it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,19 +6,25 @@
|
||||
# jobs:
|
||||
# review:
|
||||
# if: ... # actor gate for the comment trigger
|
||||
# uses: steve/gadfly/.gitea/workflows/review-reusable.yml@main
|
||||
# secrets: inherit # passes OLLAMA_CLOUD_API_KEY etc. through
|
||||
# uses: steve/gadfly/.gitea/workflows/review-reusable.yml@<sha>
|
||||
# secrets: # forward ONLY what the reviewer needs
|
||||
# OLLAMA_CLOUD_API_KEY: ${{ secrets.OLLAMA_CLOUD_API_KEY }}
|
||||
# CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
# with: { models: "...", allowed_users: "..." } # all optional
|
||||
#
|
||||
# Inputs are all optional and default to "" — an empty env value makes the
|
||||
# image/entrypoint use its own built-in default, so the caller only sets what it
|
||||
# wants to override. Secrets come via `secrets: inherit` (verified working on
|
||||
# this Gitea's act_runner); an undefined secret resolves to empty, so optional
|
||||
# ones (Claude Code token, foreman endpoints, findings store) are harmless when
|
||||
# the consumer hasn't set them.
|
||||
# wants to override. Secrets are DECLARED below (workflow_call.secrets) so a
|
||||
# caller forwards only the credentials the reviewer actually uses — least
|
||||
# privilege — rather than `secrets: inherit`, which leaks every caller secret
|
||||
# (registry/deploy/db creds) into this workflow. `secrets: inherit` still works
|
||||
# if you accept that exposure; the explicit form is recommended. GITEA_TOKEN is
|
||||
# the automatic job token (no need to forward it).
|
||||
#
|
||||
# Advisory only — never blocks a merge. The image is pinned to an immutable
|
||||
# :sha- tag here (act_runner caches :latest); bump it per Gadfly release.
|
||||
# Consumers should likewise pin `uses: ...@<sha>` (not @main) so a push to this
|
||||
# repo can't silently change the code that runs with their forwarded secrets.
|
||||
|
||||
name: Gadfly review (reusable)
|
||||
|
||||
@@ -39,6 +45,24 @@ on:
|
||||
# Job wall-clock cap. 45 > 30 as a default: a multi-model swarm or a slow
|
||||
# lens (e.g. claude-code with extended thinking) can exceed 30 minutes.
|
||||
timeout_minutes: { type: number, default: 45 }
|
||||
# Declared so callers can forward ONLY the secrets the reviewer needs
|
||||
# (least privilege) instead of `secrets: inherit`, which would hand this
|
||||
# workflow every secret in the caller's repo (registry/deploy/db creds the
|
||||
# review never touches). All optional — an unset/unpassed secret resolves to
|
||||
# empty, harmless for the providers a given consumer doesn't use. GITEA_TOKEN
|
||||
# is the automatic job token and need not be declared/forwarded. Consumers
|
||||
# with bespoke GADFLY_ENDPOINT_<NAME>s beyond M1/M5 need the full stub.
|
||||
secrets:
|
||||
OLLAMA_CLOUD_API_KEY: { required: false }
|
||||
OPENAI_API_KEY: { required: false }
|
||||
ANTHROPIC_API_KEY: { required: false }
|
||||
GOOGLE_API_KEY: { required: false }
|
||||
GADFLY_API_KEY: { required: false }
|
||||
CLAUDE_CODE_OAUTH_TOKEN: { required: false }
|
||||
GADFLY_ENDPOINT_M1: { required: false }
|
||||
GADFLY_ENDPOINT_M5: { required: false }
|
||||
GADFLY_FINDINGS_URL: { required: false }
|
||||
GADFLY_FINDINGS_TOKEN: { required: false }
|
||||
|
||||
# The reusable job posts the review comment, so it needs issues/PR write. Gitea
|
||||
# caps these by the caller's granted permissions; declaring them here is explicit.
|
||||
|
||||
Reference in New Issue
Block a user