Skip to content

github/workflows: add scoped permissions blocks#5967

Open
eriknordmark wants to merge 5 commits into
lf-edge:masterfrom
eriknordmark:excessive-perms-fix
Open

github/workflows: add scoped permissions blocks#5967
eriknordmark wants to merge 5 commits into
lf-edge:masterfrom
eriknordmark:excessive-perms-fix

Conversation

@eriknordmark
Copy link
Copy Markdown
Contributor

Description

zizmor's excessive-permissions audit fires on every job whose
GITHUB_TOKEN scope is whatever the repository default grants (typically
broader than the job needs). Add a least-privilege permissions: block
to each workflow that lacked one.

Most workflows only read the repo and call out to external services
(DockerHub, codecov, …) with their own secrets, so contents: read at
the top level is enough. The exceptions:

  • close-master-pr.yml uses pull_request_target to close PRs and
    post an explainer comment, so it gets pull-requests: write and
    issues: write at the job level over a permissions: {} workflow
    default.
  • cve-scan.yml delegates to shjala/eve-cvewatch's reusable scanner;
    pass contents: read to the callee and deny everything else.
  • pr-gate.yml's eden-gate job uses gh CLI to read PR review
    state and the upstream build run; grant actions: read and
    pull-requests: read over permissions: {}.
  • eden-trusted.yml previously set statuses: write workflow-wide;
    move that to status_ui, finalize, and subjob_statuses, the
    three jobs that actually write commit statuses.
  • osv-scanner.yml previously set security-events: write
    workflow-wide; move to per-job alongside actions: read and
    contents: read for the two reusable-workflow callers.

PR dependencies

Depends on #5966. That PR carries the artipacked and
template-injection-error fixes for the same workflow files, plus a
small actionlint config adjustment that lets the Workflow Lint job
stay green when many workflows are touched in one PR. The three
commits at the bottom of this branch are #5966; rebase out after it
merges.

How to test and validate this PR

  • The Zizmor workflow on this PR should report 0 open
    excessive-permissions findings.
  • Confirm the PR's auto_request_review job (run by
    request_codeowners_review.yml) still posts reviewers — it now runs
    under the explicit permissions: pull-requests: write block that's
    already in master.
  • After merge, watch the next workflow_run-triggered cycle of
    eden-trusted.yml: status_ui, finalize, and subjob_statuses
    must still successfully write commit statuses on the PR head SHA
    under their per-job statuses: write.
  • After merge, watch the next OSV scheduled scan and PR scan; both
    reusable-workflow callers must still upload SARIF.

Changelog notes

None. CI hardening only.

PR Backports

  • 16.0-stable: No, master-only CI hygiene.
  • 14.5-stable: No, master-only CI hygiene.
  • 13.4-stable: No, master-only CI hygiene.

Checklist

  • I've provided a proper description
  • I've added the proper documentation
  • I've tested my PR on amd64 device — n/a, workflow-only change
  • I've tested my PR on arm64 device — n/a, workflow-only change
  • I've written the test verification instructions
  • I've set the proper labels to this PR
  • I've checked the boxes above, or I've provided a good reason

eriknordmark and others added 3 commits May 15, 2026 20:43
zizmor's artipacked audit warns that actions/checkout writes
GITHUB_TOKEN into .git/config by default, which can be exfiltrated
by a later step that uploads the workspace as an artifact. Set
persist-credentials: false on every checkout so the token is
discarded immediately after the clone.

The exception is build-alpine-base.yml's commit-hash job, which
pushes the updated alpine-base image hash back to master and needs
the token to remain. Mark that checkout with an inline zizmor
ignore plus a comment explaining the dependency.

Signed-off-by: eriknordmark <erik@zededa.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
actionlint (run via reviewdog with reporter: github-check) flags any
${{ github.event.pull_request.head.* }} or ${{ github.event.comment.body }}
expression interpolated directly into a run: script because the PR
branch name and comment body are attacker-influenced.

Move HEAD_SHA, HEAD_REF, and BODY into step env: blocks in
ascii-check.yml, commit-messages.yml, request_codeowners_review.yml,
rerun-ci.yml, and spdx.yml. The shell scripts now read "$HEAD_SHA",
"$HEAD_REF", "$BODY" — the values are still attacker-influenced but
shell quoting prevents script injection.

Also quote the reviewers_array expansion in
request_codeowners_review.yml so shellcheck SC2068 stops firing.

Signed-off-by: eriknordmark <erik@zededa.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
A composite action's inputs metadata only accepts name, description,
required, default, and deprecationMessage; the type key is reserved
for reusable-workflow inputs (workflow_call). actionlint correctly
rejects "type: string" and "type: boolean" here, which has been
failing the Workflow Lint job on every PR touching .github/workflows.

Strip the type keys. The clean default switches from the YAML
boolean true to the string 'true' so the unchanged
inputs.clean == 'true' comparison in this file still resolves the
same way; the only existing caller that overrides clean uses
clean: false (publish.yml), which coerces to the string "false"
exactly as before.

Signed-off-by: eriknordmark <erik@zededa.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
reviewdog/action-actionlint submits every shellcheck finding as a
separate GitHub annotation, including info-level ones. PRs that touch
enough workflow files push reviewdog past GitHub's per-check
annotation cap and the Workflow Lint job fails on "Too many results
(annotations) in diff" rather than on an actual finding. The level
input on the action only sets a default severity for findings that
don't carry their own; it does not filter the stream.

Pass actionlint_flags so shellcheck stops emitting SC2086, SC2046,
and SC2035 entirely (boilerplate quoting and glob suggestions that
were the bulk of the noise). All actionlint rules and any
error-level shellcheck rules still run.

Signed-off-by: eriknordmark <erik@zededa.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@eriknordmark eriknordmark force-pushed the excessive-perms-fix branch from 242b06d to 6e7fe3f Compare May 15, 2026 22:40
The GITHUB_TOKEN handed to a job inherits whichever scope the
repository default grants when a workflow does not declare its own
permissions:. zizmor's excessive-permissions audit flags every job
that runs under that wider default. Add a least-privilege
permissions: block to each workflow that lacked one.

Most workflows only read the repo and call out to external services
(DockerHub, codecov, …) with their own secrets — contents: read at
the top level is enough. The exceptions:

  - close-master-pr.yml uses pull_request_target to close PRs from
    master and post an explainer comment, so it gets
    pull-requests: write and issues: write at the job level over a
    permissions: {} workflow default.
  - cve-scan.yml delegates to shjala/eve-cvewatch's reusable scanner;
    pass contents: read to the callee, deny everything else.
  - pr-gate.yml's eden-gate job uses gh CLI to read PR review state
    and the upstream build run; grant actions: read and
    pull-requests: read over a permissions: {} default.
  - eden-trusted.yml previously set statuses: write workflow-wide;
    move that to status_ui, finalize, and subjob_statuses, which are
    the only jobs that actually write commit statuses.
  - osv-scanner.yml previously set security-events: write
    workflow-wide; move to per-job alongside actions: read and
    contents: read for the two reusable-workflow callers.

Signed-off-by: eriknordmark <erik@zededa.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@eriknordmark eriknordmark force-pushed the excessive-perms-fix branch from 6e7fe3f to 86e3a5e Compare May 15, 2026 22:41
@eriknordmark eriknordmark marked this pull request as ready for review May 16, 2026 05:03
inputs:
command:
required: true
type: string
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason to remove type?

with:
reporter: github-check
fail_level: error
# Silence shellcheck info/warning codes that produce
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants