Action comes from an unauthorized source
Control: Actions must come from authorized sources · Config key: githubActionMustComeFromAuthorizedSources
📋 What is this?
A workflow references a GitHub Action whose source is not authorized. The control checks every step-level uses: owner/repo@ref and every job-level reusable-workflow uses:, and flags any reference that is not trusted by one of:
- a GitHub-official owner (
actions/*,github/*, trusted by default) - the same org/user as the scanned repository (
trustSameOrgActions, trusted by default) - the
trustedGithubActionsallowlist (exactowner/repoorowner/*wildcard) - a
minimumStarsfloor — when set, the action's upstream repository has at least that many stars
⚠️ Impact
Every third-party action runs inside the caller workflow with its GITHUB_TOKEN and secrets, so an unvetted owner is a direct supply-chain entry point — the vector behind the tj-actions/changed-files compromise (CVE-2025-30066). The minimumStars floor additionally catches the rename/re-creation squat, where an attacker re-registers a once-trusted repository name with no history.
🔧 How to fix
Use an action from an authorized source. Keep trustGithubOfficialActions on for actions/* and github/*, add trusted owners or actions to trustedGithubActions (exact owner/repo or owner/*), and optionally set minimumStars to require a popularity threshold. Vendoring the action into a local ./.github/actions/… directory removes the external dependency entirely.
# .github/workflows/ci.yml — ❌ Actions from unvetted ownersjobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # official — trusted - uses: random-dev/handy-action@v1 # unknown owner — flagged - uses: tj-actions/changed-files@v45 # not on the allowlist — flagged deploy: # Job-level reusable workflow from an unauthorized owner — flagged. uses: acme/shared-workflows/.github/workflows/deploy.yml@main# .github/workflows/ci.yml — ✅ Only authorized sourcesjobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 # official - uses: mycompany/handy-action@v1 # allowed via mycompany/* - uses: jdx/mise-action@v2 # allowed by exact entry
# .plumber.yaml — opt in and define the trusted setgithub: controls: githubActionMustComeFromAuthorizedSources: enabled: true trustGithubOfficialActions: true # trust actions/* and github/* trustSameOrgActions: true # trust your org's own actions minimumStars: 0 # > 0 to require a popularity floor (API) trustedGithubActions: - mycompany/* # whole-org wildcard - jdx/mise-action # exact owner/repo💡 Tips
- Trust is satisfied by ANY of: a GitHub-official owner (
actions/*,github/*, on by default), the scanned repo's own org (trustSameOrgActions, on by default), atrustedGithubActionsmatch, or — whenminimumStars > 0— enough stars on the action repo. -
trustSameOrgActionstrusts actions whose owner matches the scanned repository's owner (case-insensitive). It abstains when the repo owner is unknown (e.g. no GitHub remote), so it never grants trust on missing data. -
trustedGithubActionsaccepts an exactowner/repo(jdx/mise-action) or anowner/*whole-org wildcard (mycompany/*). Bare owners and arbitrary globs are not supported. - Job-level reusable-workflow calls (
jobs.<id>.uses) are checked too, but only against the official-owner and allowlist rules — they carry no star metadata. -
minimumStarsreads star counts from the GitHub API and needsgh/GH_TOKEN. Without it, a reference falls back to the allowlist rather than being flagged on missing data. - Local actions (
uses: ./.github/actions/foo) anddocker://image steps are always exempt.
⚙️ Configuration
This control is configured in .plumber.yaml under the github section:
github:
controls:
githubActionMustComeFromAuthorizedSources:
enabled: trueSee the CLI documentation for the full configuration reference.