mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
b4c834f3cd
* ci: run Codex review on fork PRs when a maintainer triggers it The fork skip in codex-pr-review.yml unconditionally bailed on cross-repository PRs, so even a maintainer's /codex or /review comment (routed through pr-review-commands.yml via workflow_call, gated by check-write-access) skipped external PRs. Gate the skip on the automatic pull_request trigger only, detected via an empty INPUT_PR_NUMBER (the metadata step already branches on this at the same step). The workflow_call path now reviews fork PRs; the auto pull_request trigger still skips them. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: run Pi review on fork PRs when a maintainer triggers it Apply the same fork-skip gating as the Codex review: skip fork PRs only on the automatic pull_request trigger (empty INPUT_PR_NUMBER), so a maintainer's /pi or /review comment (workflow_call, gated by check-write-access) reviews external PRs. Claude's pr-ready-review.yml needs no change: it has no fork skip, checks out main (not the fork ref), and reviews via gh pr diff/view with a restricted tool allowlist, so it already handles fork PRs on the command path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: harden fork-review path against secret exfiltration Addresses the CI review of the fork-review enablement. On the fork path (maintainer-triggered workflow_call for a cross-repository PR), the reviewer ran an autonomous agent over the attacker-controlled merge checkout with the EE token present, full-access sandbox, and the review prompt itself read from that untrusted checkout — so a malicious fork could rewrite the reviewer's own instructions to exfiltrate secrets. For fork PRs only (detected via the is_fork step output): - withhold WINDMILL_EE_PRIVATE_ACCESS: skip the EE access/checkout/ substitution steps, so the private-repo token is never in the env. - read REVIEW.md and the prompt file from the trusted base ref (git show origin/<base>:...) instead of the merge checkout. - restrict the agent: Codex runs with -s workspace-write (network off) instead of danger-full-access; Pi drops the bash tool. Non-fork PRs are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: redact provider credentials from fork review comments The model call needs the provider credential in its environment/config, so a network-disabled sandbox alone can't stop a prompt-injected fork review from reading the key (Codex: $HOME/.codex/auth.json; Pi: /proc/self/environ) and emitting it in the final message, which both workflows post verbatim. GitHub Actions log masking does not cover comments posted via the API. Strip the known credential values (OpenAI key + raw Codex auth JSON and its nested tokens; DeepSeek key) from the review body before posting, closing the comment as an exfiltration channel. Applied unconditionally since a credential should never appear in a review comment regardless of trigger. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: don't persist github.token in fork review checkout actions/checkout writes github.token into .git/config (http.extraheader) by default. The review agent can read the checked-out tree, so on the fork path a prompt injection could exfiltrate that token (issue/PR write) via .git/config — the provider-credential redaction added earlier didn't cover it. Set persist-credentials: false on the merge-ref checkout so the token is never written to disk. Safe on both paths: the only later git op is an unauthenticated fetch from the public origin, EE checkout uses its own token, and gh uses GH_TOKEN. Also redact github.token from the posted comment as defense-in-depth. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: disable Pi project-local discovery on fork reviews Pi auto-discovers and executes project-local .pi extensions (.ts/.js) at startup with DEEPSEEK_API_KEY in its environment — before the --tools allowlist applies — so a fork could add an extension that exfiltrates the key over the network, which output redaction can't catch. On the fork path (cwd is the fork checkout), pass --no-extensions to disable extension discovery, plus --no-skills/--no-prompt-templates/--no-themes/ --no-context-files so fork-controlled skills, templates, themes, and AGENTS.md/CLAUDE.md aren't auto-loaded into the reviewer's prompt as an injection vector. Non-fork behavior unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: use unguessable delimiter for untrusted PR metadata outputs The PR title/body were written to $GITHUB_OUTPUT with a fixed heredoc terminator (PR_BODY_EOF). A fork author could embed that terminator in their PR body to close the heredoc early and append their own output lines — e.g. is_fork=false, which (last-write-wins) overrides the real is_fork=true and puts fork code back on the trusted path (EE checkout + substitute_ee_code.sh with the private token, full-access agent). Generate a per-run random delimiter (128 bits from /dev/urandom) for the title and body heredocs so the terminator can't be predicted or embedded. Everything else in the block is single-line and newline-free, so this closes the injection. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: set PI_OFFLINE=1 on fork Pi reviews to block package resolution --no-extensions only filters which resources are *loaded*; Pi still resolves packages declared in a fork's .pi/settings.json first, running `npm install` / the configured npmCommand and lifecycle scripts with DEEPSEEK_API_KEY in env and network available — before the extension filter applies. Set PI_OFFLINE=1 on the fork path so the resolver's installMissing() short- circuits (returns false) for every missing package, skipping all install/clone/ lifecycle execution. It gates only startup network ops (installs, helper-binary downloads), not the provider inference call, so the review still runs. Verified: a fork .pi/settings.json with a malicious npmCommand does not execute under the flag. Non-fork path unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: run fork Pi review from an isolated dir to cut off project config Root cause of the recurring fork-review exposure: Pi resolves every project config from <cwd>/.pi — settings/packages, extensions, skills, themes, prompts, SYSTEM.md, APPEND_SYSTEM.md — so running inside the fork checkout let a fork inject any of them to execute code or rewrite the reviewer's system prompt with DEEPSEEK_API_KEY in env. Per-flag opt-outs (--no-extensions, PI_OFFLINE, ...) only covered discovered vectors one at a time (SYSTEM.md wasn't covered). Discovery is cwd-based (single level, no walk-up; global fallback is the trusted runner home), so run Pi from a fresh mktemp dir where no fork .pi/* is on the path. The fork agent has no shell, so pre-compute the diff (base...head SHAs are trusted) into the context file it reads; it may still read fork files by absolute path for extra context — reads are safe, only config discovery and code execution were the risk. Outputs now use absolute workspace paths since cwd moved. The --no-* flags and PI_OFFLINE stay as belt-and-suspenders. Non-fork path unchanged. Verified: a fork .pi/SYSTEM.md sentinel is not discovered from the isolated cwd. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: keep review artifacts outside the checkout to defeat symlink writes Both workflows wrote generated files (final message, event stream, review context, prior-comments) into $GITHUB_WORKSPACE. On the fork path the merge tree is attacker-controlled, so a fork could commit any of those paths as a symlink (e.g. codex-final-message.md -> ../../_actions/actions/github-script/v7/dist/ index.js). Our write would follow it and overwrite the next action's code, which then executes with the provider credential and the write-capable GitHub token — no prompt injection required. Route every generated file through $RUNNER_TEMP, which is runner-created and outside the checkout, so no fork-committed symlink is on the path: - prior-comments.json and pr-review-context.md are written to RUNNER_TEMP; the context step reads prior-comments from there. - The agent is given the context file's absolute RUNNER_TEMP path (appended to the prompt); prompt files updated to reference it instead of a checkout- relative path. Pi (no shell on forks) gets the diff pre-computed into that context file; the isolated-cwd hardening is retained. - Codex writes -o to RUNNER_TEMP; Pi writes its events/final message there; both post steps read from RUNNER_TEMP. Non-fork behavior is functionally unchanged (trusted checkout; same review inputs, now sourced from RUNNER_TEMP). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: condense fork-review comments to the 4-line limit AGENTS.md requires each invariant stated in <=4 lines. Trim the security comments added in this branch (fork-skip rationale, output delimiter, isolated cwd, RUNNER_TEMP artifacts, credential redaction) to comply without dropping the constraint each one records. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>