CI/CD provider
Include ref collides with both a tag and a branch upstream
Control: Includes must not use ambiguous tag/branch refs· Config key: externalRefsMustNotCollide
📋 What is this?
A pipeline include: pins a symbolic ref (an include:project with ref: v1, or a CI/CD component @v1) where the same name exists upstream as both a tag and a branch.
⚠️ Impact
GitLab resolves the tag first, so the pipeline pulls the tagged revision today, but a tag deletion, a rename, or a typo can switch the binding to the mutable branch, which tracks every push. The included template runs in your pipeline with your CI/CD variables and tokens, so the ambiguity is a supply-chain landmine: a reviewer cannot tell from the YAML which of the two revisions will run.
🔧 How to fix
Pin the include to a 40-character commit SHA, which is unambiguous (a commit SHA also takes precedence over a same-named tag). Alternatively, ask the upstream maintainer to remove one of the two colliding refs.
# .gitlab-ci.yml — ❌ Ambiguous refinclude: - project: my-group/ci-templates ref: v1 file: /templates/build.yml # ^ `v1` exists as both a tag AND a branch upstream# .gitlab-ci.yml — ✅ SHA-pinned removes the ambiguityinclude: - project: my-group/ci-templates ref: a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4 file: /templates/build.yml💡 Tips
- API-backed: the collector probes the source project's tag and branch namespaces and fires only on a confirmed double-hit. Without a token (or on a failed probe) the control abstains, with no false positives.
- Applies to CI/CD component
@versionrefs too, since components resolve the version against the same tag and branch namespaces.
⚙️ Configuration
This control is configured in .plumber.yaml under the gitlab section:
gitlab:
controls:
externalRefsMustNotCollide:
enabled: trueSee the CLI documentation for the full configuration reference.
Include ref collides with both a tag and a branch upstream
Control: Actions must not use ambiguous tag/branch refs· Config key: externalRefsMustNotCollide
📋 What is this?
A workflow uses a symbolic action ref (e.g. uses: owner/repo@v1) where the same name exists upstream as both a tag and a branch.
⚠️ Impact
GitHub's ref-resolution order makes the outcome implementation-defined and timing-dependent. An attacker who can push to a branch named v1 upstream may be able to have CI pick the branch over the tag under specific conditions, swapping a release for arbitrary code.
🔧 How to fix
Pin the action to a 40-character commit SHA, which is unambiguous. Alternatively, ask the upstream maintainer to remove one of the two colliding refs.
# .github/workflows/test.yml — ❌ Ambiguous refjobs: test: runs-on: ubuntu-latest steps: - uses: owner/repo@v1 # ^ `v1` exists as both a tag AND a branch upstream# .github/workflows/test.yml — ✅ SHA-pinned removes the ambiguityjobs: test: runs-on: ubuntu-latest steps: - uses: owner/repo@a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4 # v1.0.0💡 Tips
- This rule fires regardless of the
actionsMustBePinnedByCommitShaopt-in, since collisions are a clear bug. - Upstream maintainers should delete the branch (or rename the tag) to clear the collision for all consumers.
⚙️ Configuration
This control is configured in .plumber.yaml under the github section:
github:
controls:
externalRefsMustNotCollide:
enabled: trueSee the CLI documentation for the full configuration reference.