docs: harden the Actions example in the CLI smoke-testing guide

Copilot round 7. The copyable job inherited the repository's default
GITHUB_TOKEN scopes and left that token in the runner's git config while
`npx` executed a package downloaded from npm.

Adds `permissions: contents: read` and `persist-credentials: false`,
each with a comment giving its reason.

The review cited `.github/workflows/main.yml:11-18` as precedent for
both. It supports one: main.yml does set `permissions: contents: read`
at workflow level, for exactly the stated reason, and every workflow in
this repo carries a block. It does not support the other —
`persist-credentials` appears nowhere in .github/workflows. That half is
taken on its own merit rather than as convention: the job uses git for
nothing after checkout, so the credential is only attack surface while
third-party code runs.

Both matter more in this example than in a workflow we own, because it
is written to be copied into a repository whose defaults the reader
cannot see — which is now stated in the guide.

The edited block was parsed as YAML to confirm both keys land where
intended rather than eyeballing the indentation.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KsKC3HniVF9dCZtDVbYp5B
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-09-06 14:41:56 -04:00
commit 8dfb2c8c4b

View file

@ -517,8 +517,18 @@ As a GitHub Actions job:
```yaml
smoke:
runs-on: ubuntu-latest
# Least privilege: the job reads the repo and talks to your MCP server. It
# writes nothing back to GitHub, so do not let it inherit the repository's
# default token scopes, which may be far broader.
permissions:
contents: read
steps:
- uses: actions/checkout@v7
with:
# Nothing here uses git after checkout, and the next step runs a package
# downloaded from npm. Leaving GITHUB_TOKEN in the runner's git config
# would be handing that package a credential it has no use for.
persist-credentials: false
- uses: actions/setup-node@v7
with:
node-version: "22.x"
@ -532,6 +542,11 @@ smoke:
MCP_TOKEN: ${{ secrets.MCP_TOKEN }}
```
Both hardening lines matter more here than in a workflow you wrote yourself,
because this one is meant to be **copied into a repository whose defaults you
cannot see**. A `permissions:` block that names what the job needs is the same
discipline this repo applies to its own workflows.
`"${auth[@]+"${auth[@]}"}"` rather than `"${auth[@]}"`: under `set -u` an empty
array is an unbound variable in bash before 4.4, and the runner is not the only
place this script runs — macOS still ships bash 3.2. The guarded form expands to