← GitHub Actions secrets: inherit Check
GitHub Actions secrets: inherit Check vs. reviewing workflows by eye
Finding the line secrets: inherit in a workflow file takes no special skill. Deciding
what it actually exposes -- whether the call it sits on reaches outside the repository, and whether the job is
one a stranger's fork can trigger -- is the part that is easy to get wrong under a quick read. This page names
no product or company; "reviewing by eye" is the generic habit every maintainer already has, not a competing
tool.
Where a careful read holds up, and where it slips
secrets: inherit does not hide -- it is one line, and a reviewer who knows to look for it will
find every instance in a file. What is harder to hold in your head while scanning a real workflow is what
each instance actually means. A reusable-workflow call is external only if its uses: value
names another repository (owner/repo/.github/workflows/file.yml@ref) rather than a local path
(./.github/workflows/file.yml) -- easy to conflate when the line wraps or sits several jobs
away from the secrets: inherit it governs. And the fork-reachability question depends on a
trigger declared once at the top of the file, under on:, which has to be carried in mind while
reading down through every job below it -- easy to lose track of in a workflow with several unrelated jobs,
especially when the risky job's name gives no hint that it is the one at risk.
| Case | Reviewing by eye | This tool |
|---|---|---|
Spotting that a job uses secrets: inherit at all | Easy to catch -- one distinctive line | Catches it the same way |
Telling a local reusable-workflow call (./.github/workflows/file.yml) from an external one (owner/repo/…@ref) | Easy to misread when the line wraps or sits jobs away from the secrets: inherit line | Re-parses the exact uses: value for every secrets: inherit line found |
Connecting a job to a pull_request_target/workflow_run trigger declared at the top of a long file | Easy to lose track of while reading down through unrelated jobs | Checks the whole file's trigger against every secrets: inherit line, however far apart they sit |
| A repository with a dozen workflow files, each with its own reusable-workflow calls | Re-reading all of them for both patterns, every time a workflow changes, is tedious and easy to shortcut | Paste as many files as you have, or run check.py across the whole directory |
| Blocking a newly-added finding before it merges | Depends on a human noticing it in review | check.py --fail-on-risk gives a real exit code from the same classification, wired into your own CI |
The two are complementary, not competing: this tool does not decide whether a called workflow
is actually trustworthy, or whether a fork-reachable job's blast radius matters in your specific setup -- it
replaces the tedious, error-prone part, which is re-checking every uses: value and every
trigger declaration each time a workflow file changes.
Common questions
What does reviewing a workflow for secrets: inherit by eye actually catch?
secrets: inherit is one distinctive line, and a reviewer who knows to look for it will find every occurrence in a file without much effort. That recognition is real, and this tool starts from the same line -- it does not find a secrets: inherit call a careful reader would have missed.
Why is telling an external reusable-workflow call from a local one easy to get wrong at a glance?
The difference between uses: ./.github/workflows/build.yml and uses: some-org/shared-workflows/.github/workflows/build.yml@v2 is a leading ./ and an owner/repo prefix -- easy to miss when the uses: line is long, wrapped, or several jobs removed from the secrets: inherit line it governs. A reviewer scanning quickly for the string secrets: inherit can confirm the line exists without re-checking which uses: value the surrounding job actually calls, which is exactly the distinction that decides whether the finding is secrets-inherit-external-workflow at all. This tool re-reads the uses: value for every secrets: inherit line it finds, every time.
Why is the fork-trigger connection easy to miss even when the trigger is declared right there in the file?
pull_request_target and workflow_run are usually declared once, at the top of the file, under on:. The job carrying secrets: inherit can be dozens of lines below, in a workflow with several unrelated jobs. Holding "this file's trigger is untrusted" in mind while scanning down through every job's steps, for every workflow file in a repository, is the kind of bookkeeping that is easy to let slip -- especially on a workflow whose name or first job gives no hint that a later job is the one at risk. This tool checks the whole file's trigger against every secrets: inherit line it finds, regardless of how far apart they sit.
Does a clean result from this tool mean my repository's secret handling is safe?
No. It checks two specific, well-documented secrets: inherit patterns in one workflow file at a time. It does not evaluate whether a called reusable workflow is itself trustworthy, and it does not follow a local reusable workflow to see what it in turn calls -- both real ways a repository's secret handling can still be exposed after a clean scan.