Skip to content

Unpinned third-party MkDocs plugin from a personal feat/* branch is executed in docs deploy with contents:write #168

Description

@nouraellm

master publishes the project site from .github/workflows/ci.yml. That job installs a Python package from a mutable git ref on a personal fork, then force-deploys Pages with a write-capable GITHUB_TOKEN. The same ref is installed in Dockerfile so CI and local/preview image builds share one untrusted dependency.

# .github/workflows/ci.yml
permissions:
  contents: write
#
- run: pip install git+https://github.com/PauloASilva/mkdocs-monorepo-plugin@feat/i18n
- run: mkdocs gh-deploy --force

Dockerfile

ENV MKDOCS_MONOREPO_REPO=https://github.com/PauloASilva/mkdocs-monorepo-plugin
ENV MKDOCS_MONOREPO_BRANCH="feat/i18n"
RUN python -m pip install git+$MKDOCS_MONOREPO_REPO@$MKDOCS_MONOREPO_BRANCH

@feat/i18n is not a tag or a release or a commit SHA. Thus, each pip install (Actions and docker build) resolves whatever commit is currently at that branch tip, and package installation executes the distribution’s build backend. In CI, the next step can rewrite gh-pages

The official plugin is backstage/mkdocs-monorepo-plugin / PyPI mkdocs-monorepo-plugin. Neither the workflow nor the Dockerfile uses that artifact.

actions/checkout@v3, actions/setup-python@v4, and actions/cache@v3 are unpinned by SHA and are not current majors. Secondary to the git-ref install.

In scope of SECURITY.md (CI/CD / repository automation)

Steps to reproduce

  1. Open .github/workflows/ci.yml and Dockerfile on master.
  2. Confirm both install git+https://github.com/PauloASilva/mkdocs-monorepo-plugin@feat/i18n with no commit SHA.
  3. Confirm the workflow has permissions: contents: write and runs mkdocs gh-deploy --force.
  4. Resolve https://github.com/PauloASilva/mkdocs-monorepo-plugin @ feat/i18n and note it is a moving branch tip.

No exploit payload included.

Expected behavior

Third-party Python used to publish this site is a pinned PyPI release with hashes or git+https://…@<full commit SHA>. Pages deploy does not use repo-wide contents: write plus force push.

Actual behavior

CI and Docker install a floating feat/* branch from a personal fork then CI force-deploys the site.

Impact

Compromise or unilateral update of PauloASilva/mkdocs-monorepo-plugin@feat/i18n yields:

  • arbitrary code execution in OWASP/API-Security GitHub Actions on the next push to master and in any docker build of this tree
  • integrity failure of the published API Security Top 10 site via mkdocs gh-deploy --force

No application runtime or user data is involved. The asset is the official documentation origin.

Suggested remediation

  • Pin one artifact in both the workflow and the Dockerfile (lockfile with hashes, or an immutable commit SHA)
  • Build with contents: read. Deploy via actions/upload-pages-artifact + actions/deploy-pages (pages: write, id-token: write only on the deploy job)
  • Remove --force unless a documented recovery case requires it
  • Pin Actions by commit SHA

Happy to send a PR that pins the SHA and splits the deploy job.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions