
A workflow ran against the client's repository. Tonight, in a repository you or the client can open, three things. Name each third-party action the workflow uses, and name the ref: a tag, a branch, or a full commit SHA. Show that a tag or a branch is not the commit the last run executed, or show that a policy refuses a use that is not pinned while you can still read the refusal. Show a different step, pinned to a full-length commit SHA, still running. If any of the three requires the person who copied the YAML and then left, you have not named the action. You have a green check and a file that will fetch whatever that name points at tomorrow.
This is the ref on a uses line. It is not the identity of the release the client is running. Name the commit the client is running is the digest of the image, or the commit that image was built from, set next to the commit you believe you shipped. A digest you can read does not tell you which third-party action the workflow fetched in order to build it. It is not who merged. Name who merged their change is the account that landed a pull request. A merge by a person you trust does not pin the action that pull request's workflow will execute. It is not a secret sitting in history. Name the secret the history holds is a value a fresh clone can still resolve. A secret you have rotated can still be printed by an action whose ref moved yesterday. And it is not a role on a product account. Name the role that account holds is a membership in the product. A workflow is not that membership.
None of what follows is a finding that this client's workflows are unsafe, and none of it is legal advice. The counts below are public GitHub, collected from a bot's events, not a private repository in 2026. A recommendation in a 2022 paper is not a checkbox that fails a run. A green check is not a pin.
The paper that counted the refs did not count one thing. Koishybayev, Nahapetyan, Zachariah, Muralee, Reaves, Kapravelos, and Machiry, USENIX Security 2022, analyzed 447,238 workflows across 213,854 repositories. They queried GHArchive for events from the github_bot user, from 2019 through July 2021. The set leaves out workflows that do not update the repository and do not call GitHub's APIs. It is public GitHub in that window. It is not this client's private repository. The abstract says 99.8% of workflows are overprivileged, with read-write access to the repository instead of read-only. That 99.8 is the abstract. It is not a count of unpinned actions. The abstract also says 23.7% of workflows are triggerable by pull_request and use code from the underlying repository. The trigger table is a different cut: pull_request is 94,962 repositories (44.4%) and 146,803 workflows (32.8%). 23.7 is not 32.8 is not 44.4. 146,803 is not 94,962. "And use code from the underlying repository" is the abstract's narrower set. Do not replace it with the trigger count (Koishybayev and colleagues, Characterizing the Security of Github CI Workflows).
The same paper's action counts have to stay in their own sentences. The keyword uses appeared 1,623,413 times, in 99.7% of repositories. 0.75% of those uses were local actions. 6,082 (0.3%) referenced a Docker image directly. Of the rest, 62% were first-party and 38% were third-party. 0.75 is not 0.3. 62 is not 38. 38% of the rest is not 99.7% of repositories, and 1,623,413 is not the third-party total. They found 11,438 unique third-party actions, or 19,033 if versions count as distinct. 19,033 is not 11,438. 213,209 repositories (99.7%) reference at least one third-party action. 213,209 is not 213,854. As of 15 November 2021, GitHub had 75 verified-creator organizations in the marketplace. In the dataset, 335 of 11,438 actions were from verified creators, which they call 3%. 335 is not 75, and 3% of actions is not the abstract's 97% of repositories that execute at least one action from a creator who is not verified. Do not subtract 3 from 100 and call it 97. Among the top 20 actions, ignoring GitHub's own, they count one from a verified creator and nine from creators who are not. 1 is not 9 is not 20.
How those actions are referenced is three columns, and the paper prints three different shares for the commit column. Table 5, third-party actions only, with the non-verified subset in parentheses: tag 474,166 (410,054), 78.8% (68.2%); branch 120,633 (109,400), 20% (18.1%); commit hash 6,539 (5,687), 1% (0.9%); total 601,338 (525,141), 100% (88.2%). 474,166 is not 410,054. 6,539 is not 5,687. 1% is not 0.9%. The caption under that table says "only 0.1% of references use commit hash." The paragraph above the table says "less than 1%." 0.1 is not 1, and neither sentence is the cell. Do not pick the caption over the cell, or the cell over the caption, or average them. The three distribution cells are printed as 78.8, 20, and 1, and the total cell is printed as 100. Do not add the three and replace the 100. The parenthetical total is printed as 88.2%. Do not add 68.2, 18.1, and 0.9 and replace 88.2. Of the 213,209 repositories that use third-party actions, the paper says "only 1.7% (i.e., 3,248 repositories)" reference them by commit hash. The sentence gives you both numbers. Dividing 3,248 by 213,209 does not produce 1.7. Do not replace either figure with the quotient. A contributions bullet says "less than 2% of all repositories" follow the commit-hash guidance. Less than 2 is not 1.7, and "all repositories" is not the 213,209. A commit hash, the paper adds, still does not guarantee the action is immutable if that action itself uses a mutable ref for a dependency. They reported a proof of that to GitHub. The hash on your line is not a hash on the action's own dependencies.
Two further clusters are easy to slide under the pin, and they are not the pin. The abstract says 18% of repositories execute at least one action with missing security updates. The body says 38,315, or 17.9%, use at least one potentially vulnerable third-party action. 18 is not 17.9, and 38,315 is the body's count, not a rounding you are invited to do. Table 6 splits severity: high, 26 actions and 582 repositories; medium, 56 and 28,870; low, 577 and 10,922. The action column is the 659 actions they kept after review, which they call 5% of the actions they cloned. 5.4K is the count of candidate fixing commits before that review. 5.4K is not 659. 5% is their word for the actions, not a share of the 5.4K. Do not add 582, 28,870, and 10,922 and call the sum 38,315. The paper does not say those repository rows are disjoint. They opened issues for 542 of the 582 high-severity repositories and could not for 40. 542 and 40 are that split. 582 is not 38,315. Secrets are a third cluster. The dataset has 245K cases of a secret passed through GitHub's mechanism. Almost half, 49.7%, of repositories pass a secret at least once. 245K is not 49.7%. Secrets were passed directly to 4.5K third-party actions, of which 359 were from verified creators, "less than 10%." 5.7K actions had indirect access, of which 53 were from verified creators. 4.5K is not 5.7K. 359 is not 53. Less than 10 is not an integer you should compute from 359. Plaintext candidates were 2,240, then 333 possible leaks, of which 11 maintainers confirmed. 11 is not 333 is not 2,240. Self-hosted runners: at least 565 public repositories, of which 292 (51.7%) are triggered by a pull request, a lower bound because custom labels hide runners. Of those 292, about 20% (53) have more than 100 stars. The 292 average 744 stars and 133 forks. 51.7 is not 20. 292 is not 565. 744 and 133 are averages, not repository counts. None of these is the share of your workflow file that is pinned.
GitHub's current hardening page is a different document from that paper, and it is the one that tells you what to write on the line. Pinning an action to a full-length commit SHA "is currently the only way to use an action as an immutable release." A tag is more convenient. The page says to use a tag only if you trust the creator, and then says there is still risk, "because a tag can be moved or deleted if a bad actor gains access to the repository storing the action." The verified-creator badge is "a useful signal." A signal is not a pin. The SHA you select has to be from the action's repository, not from a fork. Policies at the repository and at the organization can require the full-length SHA. A separate sentence on the same guide: Dependabot only creates alerts for vulnerable actions that use semantic versioning, and "will not create alerts for actions pinned to SHA values." A pin and an alert are different features. Pinning does not subscribe you to the alert, and an alert does not pin the line (GitHub, Secure use reference).
The enforcement that fails a run is newer than the paper, and it is not on until someone turns it on. On 15 August 2025 GitHub's changelog says administrators can require SHA pinning in the allowed-actions policy. A checkbox appears under each choice, except where actions are already disabled at the enterprise, the organization, or the repository. "The policy will check for a full commit SHA, and any workflow that attempts to use an action that isn't pinned will fail." A blocklist entry prefixed with an exclamation mark is evaluated last and overrides an allow. The same post says GitHub "is introducing immutable releases," and that once a release is marked immutable its assets and its tag cannot be changed or deleted. "Is introducing" is not the checkbox. The checkbox fails an unpinned uses line. It does not, by that paragraph, freeze a tag you are still allowed to name. The 2022 paper's sentence that GitHub's documentation suggests a commit hash is the suggestion. The 2025 checkbox is a failure. A repository where the checkbox is off is still on the suggestion. Do not cite 1% as the share of a repository whose policy you have not opened (GitHub, changelog, 15 August 2025).
Do the three proofs on one workflow file you can open. List every uses line that points outside the repository. For each, write the owner, the action, and the ref, and write which of the three kinds the ref is. A tag that looks like a version is still a tag. A branch name is still a branch. A full SHA is forty hexadecimal characters, not a short prefix and not a tag that happens to point at a SHA today. If the ref is a tag or a branch, open the last successful run and write the SHA that run resolved, then open the ref now. If those two SHAs differ, you have the second proof: the name moved. If they match, write that they match today, and write that a tag can still move. Matching today is not a pin. If a policy is on, push a branch whose workflow uses a floating tag and show the run fail, then show the pinned step on the real workflow still succeed. The failure is the second proof. The success is the third. A policy you have not switched on cannot be the failure. Say that it is off. If every line is already a full SHA, the first proof is the list, the second proof is the sentence "there is no floating ref in this file," and the third is one run that used one of those SHAs. That sentence is allowed. A screenshot of a green check, with the YAML offscreen, is not.
What opening the file looks like, when the work is a search across repositories rather than a tour of one laptop, is ordinary. Haystack is a search layer across documents, data lakes, and repositories. Nothing in that description is a headcount, a duration, or a result figure. The shape is the point: the workflow file is in a repository you can search, so the uses line is something you can read after the author has closed the editor. That case is not a pin ledger, and it is not a finding about any action's ref. A workflow that exists only in a copied snippet, in a tool the client cannot open, is the object this proof refuses.
There is a version of this where hiring the pin is the wrong purchase. The work is not sold, so there is no client repository and no workflow a build could have added. The client asked to meet the people who will write the code, and the conversation you are in is that introduction, not a uses line. The room only has a slide. Or the buyer wants a template policy and does not have a repository to open. A policy that names a commit SHA and has never been turned on is a brochure. The criteria for choosing a firm, when that is the actual decision, are the ones on the page for a top saas development company. They do not rank anyone, including us. Or the buyer wants a legal opinion about a license. Name the license the build added is the grant. A grant is not a ref.
When the client is already asking what ran, the missing object is specific. The action, the ref, and either a moved tag, a refused run, or a written sentence that no line floats, plus one pinned step that still succeeds. A white label software development partner that expects this will have the workflow in the client's repository, not in an account the client cannot see. One that needs a call in order for you to read a uses line is telling you who holds the file. If the first slice is a fixed capability, an AI integration sprint is the shape where the acceptance sentence can include one workflow whose refs you can read yourself. If nothing has been wired and the question is whether a pipeline could name its actions at all, a two-week AI readiness assessment hands you the findings either way. The findings are yours. They are not a SHA, and they are not a 1%.
A model that called a tool you cannot name is a different missing record, name the tool the model called. Pinning an action does not record that call, and recording the call does not pin the action. Before you tell the client the workflow is safe because the check is green, write the three lines and the time you read them. The action. The ref. Whether that ref is a full SHA, and what happened when it was not. Do not ask a model which commit a tag means tonight. The file, and the run, are the page you can defend.
Which environment that run was allowed to touch is a different line: which environment got that change. A ref is not an environment. Do not record the deploy target on this page.
The runner that job used is a different line from the action ref: which runner did that job use?. A uses SHA is not a runs-on label. Do not record the machine on this page.
Find this useful? Tell Google to show you more of it.
