ci: the docs deploy builds with its dependencies in a job that can write to the repository #7320
Description
What is the problem?
The docs job in ci.yml calls docs.yaml with contents: write, so the site can be pushed to asf-site. Inside docs.yaml the single build-docs job holds that write access for the whole run:
- Both
actions/checkoutsteps use the defaultpersist-credentials: true, which leaves the job'sGITHUB_TOKENin the checkout's git config. - The job then runs everything that builds the site:
npm installfor mermaid-cli,pip install -r docs/requirements.txt, Sphinx, anddocs/build.sh, which builds four Spark profiles with Maven. - Only the last step, "Copy & push the generated HTML", needs to write.
So a compromised dependency of any of those tools runs in the same job as a token that can push to asf-site. The job only runs on push to main (and a dispatch of main), so a pull request cannot reach it. The weakness is the dependency chain of an authorized build.
This is hardening, not a known compromise.
Proposal
Split it in two:
- A build job with
contents: readandpersist-credentials: falseon both checkouts, which installs, builds, runs the mermaid checks, and uploadsdocs/build/htmlas an artifact. - A small deploy job with
contents: writethat downloads the artifact, checks outasf-site, and does thersyncand push.
This separates the credential from the code that builds. It does not make the built output trustworthy: a compromised builder can still produce malicious pages. It only stops the builder from using the repository's write access directly.
Raised in review of #7311, which does not touch this file.
Activity
ErikBPF commentedon Oct 29, 2026
This proposal preserves the boundary requested in the original review. No additional scope change is needed.
Evidence
For implementation, keep the reusable caller's permission ceiling sufficient for deployment, but explicitly narrow the build job to
contents: read. Keep dependency installation, Maven, Sphinx and Mermaid execution in that read-only job. The fresh writer should consume the generated output without running project build commands or scripts from the artifact.Action: Verify the effective build-job permissions and non-persisted build checkout credentials, and verify that the deploy job performs only the static-output publication path. Include a disposable copy check preserving Git metadata,
.asf.yamland.nojekyll. The current copy step restores the latter two afterrsync --delete, anddocs/build/htmlalone does not supply root.asf.yaml. Keep the trigger guards and Mermaid checks. The stated caveat is important: isolating the writer does not certify the generated pages as trustworthy.This confirms the proposed scope. It does not mark the current workflow as repaired.