From 112f9594594b7081860be0cc86471ae7c7e2a5a4 Mon Sep 17 00:00:00 2001 From: Yuriy Andamasov Date: Sun, 10 May 2026 22:47:03 +0300 Subject: fix(ci): idempotent _changed_md + remove App token from process argv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two new findings on PR #1947: 1. Copilot — line 59, mkdir collision after cancellation: `mkdir _changed_md` fails with EEXIST if the directory was left behind by a previous run that concurrency.cancel-in-progress killed before the post-job cleanup step could execute. On a busy PR with rapid synchronize events this is a real non-determinism. Replaced with `rm -rf _changed_md && mkdir -p` so the bundling step is idempotent. 2. CodeRabbit — Major, scripts/ai-validation.yml:244 (mirrored here on the deployed copy): `uv pip install "git+https://x-access-token:${TOKEN}@..."` puts the App token in process argv. On a self-hosted runner anyone able to read /proc//cmdline (any user with the same UID, any root tool, any LSM audit log) sees the secret while uv/git is running. This undercuts the persist-credentials:false hardening on the surrounding checkouts. Replaced the install with a two-step checkout + local-path install: - actions/checkout@v6 with persist-credentials:false fetches the reviewer source into ./reviewer-src using the App token as a transient http extraheader (not argv). - `uv pip install ./reviewer-src` then installs from the local path — no token anywhere on the command line. Net trust boundary: same security posture as the existing sparse-checkout of branches.json (line 100-110); no new attack surface. 🤖 Generated by [robots](https://vyos.io) --- .github/workflows/ai-validation.yml | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) (limited to '.github/workflows/ai-validation.yml') diff --git a/.github/workflows/ai-validation.yml b/.github/workflows/ai-validation.yml index 9820f7d4..e93dc886 100644 --- a/.github/workflows/ai-validation.yml +++ b/.github/workflows/ai-validation.yml @@ -56,7 +56,11 @@ jobs: # input. `git show HEAD:` returns the blob directly from the # object database; for a symlink-mode entry it returns the textual # target path, never the target's content. - mkdir _changed_md + # Idempotent: a previous run cancelled by concurrency.cancel-in-progress + # may have left _changed_md/ behind on the self-hosted runner if the + # job was killed before the post-job cleanup ran. rm -rf + mkdir -p + # guarantees a clean target regardless of prior state. + rm -rf _changed_md && mkdir -p _changed_md while IFS= read -r -d '' path; do # Path-traversal hardening: even though git's tree machinery # rejects `..` segments and absolute paths in committed entries @@ -225,10 +229,28 @@ jobs: python-version: '3.12' activate-environment: true - - name: Install reviewer (pinned to REVIEWER_REF) + # Check out the reviewer source instead of installing via + # `uv pip install git+https://x-access-token:@...` — the URL form + # puts the App token in process argv (visible through + # /proc//cmdline on the self-hosted runner while uv or git is + # running). actions/checkout writes the token as a transient http + # extraheader instead, and persist-credentials:false ensures it does + # not linger in reviewer-src/.git/config where the Pass 2 LLM step + # could read it. + - name: Checkout reviewer package source (pinned to REVIEWER_REF) + if: steps.secrets-check.outputs.skip != 'true' + uses: actions/checkout@v6 + with: + repository: VyOS-Networks/vyos-docs-opus-reviewer + ref: ${{ env.REVIEWER_REF }} + token: ${{ steps.app.outputs.token }} + persist-credentials: false + path: reviewer-src + + - name: Install reviewer (from local checkout) if: steps.secrets-check.outputs.skip != 'true' run: | - uv pip install "git+https://x-access-token:${{ steps.app.outputs.token }}@github.com/VyOS-Networks/vyos-docs-opus-reviewer.git@${{ env.REVIEWER_REF }}" + uv pip install ./reviewer-src # Run from inside _changed_md/ so the diff's relative paths # (`docs/...`) resolve to actual files in the artifact tree. -- cgit v1.2.3