This pipeline's security stage passes on every run. Why, and how do you fix it?
A CI workflow is supposed to gate merges on security findings, but the security job has been green for three months while the scanner clearly reports criticals.
Constraints: keep the scan on every pull request, and make the fix enforceable by the platform team rather than by convention.
name: ci
on: [pull_request]
jobs:
security:
runs-on: ubuntu-latest
permissions: write-all
steps:
- uses: actions/checkout@main
- uses: some-vendor/sast-action@v2
continue-on-error: true
with:
api-token: "${{ secrets.SAST_TOKEN }}"
- name: Report
run: echo "scan token ${{ secrets.SAST_TOKEN }} finished" >> report.log
- name: Gate
run: exit 0
Find and fix the flaws that make this gate meaningless.
The scan cannot fail the build — continue-on-error: true swallows its exit code and Gate hardcodes exit 0. It also echoes the token into a log and runs write-all on an unpinned action. Gate on the real exit code, drop the echo, rotate the token, scope permissions, pin by SHA.
- ✗Reading a green job as a passing gate without checking the exit path
- ✗Treating
continue-on-erroras a responsiveness setting rather than a bypass - ✗Deleting the leaked token from the log instead of rotating the credential
- →Why does pinning an action to a commit SHA matter more than pinning to a tag?
- →How would you stop a team re-adding
continue-on-errorto the security job?
The gate is meaningless for four reasons, each fixed separately.
continue-on-error: true swallows the scanner's non-zero exit code, so the step always "succeeds". The final Gate step with run: exit 0 then hardcodes success regardless of findings. Together they make the job green unconditionally.
The SAST_TOKEN secret is echoed into report.log. That token is now compromised — the artifact may have been downloaded and the log retained. Deleting the line is not enough; the token must be rotated.
permissions: write-all grants the job write access across the repository, and actions/checkout@main plus sast-action@v2 are floating references: an upstream change executes with those permissions.
name: ci
on: [pull_request]
jobs:
security:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@8f4b7f8 # pinned by SHA
- uses: some-vendor/sast-action@3a1c9de # pinned by SHA
with:
api-token: "${{ secrets.SAST_TOKEN_ROTATED }}"
- name: Report
run: echo "scan finished" >> report.log
The scan now fails the job on its own exit code, so no separate Gate step is needed. To stop teams re-adding continue-on-error, the blocking check should live in a platform-owned reusable workflow and be required by a branch-protection rule.