Summary
.github/workflows/ci_cd.yml does not declare a top-level permissions: block, and 8 of its 11 jobs declare no permissions: of their own. Those jobs run with the repository's default GITHUB_TOKEN scope rather than an explicit least-privilege scope.
OpenSSF Scorecard currently reports Token-Permissions: 0 for this repository (overall score 6.2, scan dated 2026-07-27).
Current state
| Job |
Declared permissions |
pre-commit |
none |
code-ql |
security-events: write |
run-tests |
none |
build-package |
none |
test-build-package |
none |
test-docker-image |
none |
test-docker-image-build |
none |
publish-nettacker-dev-to-docker-registry |
none |
publish-nettacker-latest-to-docker-registry |
none |
publish-to-test-pypi |
contents: read, id-token: write |
publish-to-pypi |
contents: read, id-token: write |
The two PyPI publishing jobs already follow least privilege. The pattern was simply never extended to the rest of the file.
Why this is worth fixing
The practical impact depends on the organisation and repository default token setting, which is not visible externally, so this is defence in depth rather than a directly exploitable issue. Two things make it worth doing anyway:
- Two of the unscoped jobs (
publish-nettacker-dev-to-docker-registry and publish-nettacker-latest-to-docker-registry) handle DOCKER_HUB_ACCESS_TOKEN and push owasp/nettacker:latest. These are the jobs where a broadly scoped ambient token is least desirable.
- An explicit
permissions: {} default means any future job added to this workflow starts from zero rather than inheriting whatever the repository default happens to be at that time.
Proposed change
- Add
permissions: {} at the workflow level as a deny-by-default.
- Add
permissions: contents: read to the eight jobs that currently declare none.
- Add the missing
contents: read to code-ql alongside its existing security-events: write.
No job gains a permission it does not already have in practice, and no job logic changes. The diff is additive: 19 added lines, 0 removed.
Verification
- Workflow still parses;
check-yaml from the existing pre-commit config passes.
- All 11 jobs end up with an explicit, minimal scope.
- Only
code-ql (security-events: write) and the two PyPI jobs (id-token: write) retain any write scope, which they require.
Note on the rest of the Scorecard report
Scorecard also reports Dangerous-Workflow: 0, but that scan predates ca200a1 (2026-08-01), which migrated the PR check to a SHA-pinned action with scoped permissions. That finding appears to be already resolved and should clear on the next scan. This issue is only about Token-Permissions.
I would like to work on this. @securestep9 could you assign it to me? I understand PRs are auto-closed without an assigned issue reference.
Summary
.github/workflows/ci_cd.ymldoes not declare a top-levelpermissions:block, and 8 of its 11 jobs declare nopermissions:of their own. Those jobs run with the repository's defaultGITHUB_TOKENscope rather than an explicit least-privilege scope.OpenSSF Scorecard currently reports
Token-Permissions: 0for this repository (overall score 6.2, scan dated 2026-07-27).Current state
pre-commitcode-qlsecurity-events: writerun-testsbuild-packagetest-build-packagetest-docker-imagetest-docker-image-buildpublish-nettacker-dev-to-docker-registrypublish-nettacker-latest-to-docker-registrypublish-to-test-pypicontents: read,id-token: writepublish-to-pypicontents: read,id-token: writeThe two PyPI publishing jobs already follow least privilege. The pattern was simply never extended to the rest of the file.
Why this is worth fixing
The practical impact depends on the organisation and repository default token setting, which is not visible externally, so this is defence in depth rather than a directly exploitable issue. Two things make it worth doing anyway:
publish-nettacker-dev-to-docker-registryandpublish-nettacker-latest-to-docker-registry) handleDOCKER_HUB_ACCESS_TOKENand pushowasp/nettacker:latest. These are the jobs where a broadly scoped ambient token is least desirable.permissions: {}default means any future job added to this workflow starts from zero rather than inheriting whatever the repository default happens to be at that time.Proposed change
permissions: {}at the workflow level as a deny-by-default.permissions: contents: readto the eight jobs that currently declare none.contents: readtocode-qlalongside its existingsecurity-events: write.No job gains a permission it does not already have in practice, and no job logic changes. The diff is additive: 19 added lines, 0 removed.
Verification
check-yamlfrom the existing pre-commit config passes.code-ql(security-events: write) and the two PyPI jobs (id-token: write) retain any write scope, which they require.Note on the rest of the Scorecard report
Scorecard also reports
Dangerous-Workflow: 0, but that scan predates ca200a1 (2026-08-01), which migrated the PR check to a SHA-pinned action with scoped permissions. That finding appears to be already resolved and should clear on the next scan. This issue is only aboutToken-Permissions.I would like to work on this. @securestep9 could you assign it to me? I understand PRs are auto-closed without an assigned issue reference.