Fix the supply-chain audit of a deploy workflow
Your task
An assistant scaffolded a deploy workflow that calls its actions by tag, @main and @v3, with no permissions: block. Whoever controls a tag can point it at new code, and that code runs with your deploy token. The team now audits every workflow before it ships.
An assistant wrote audit_supply_chain(uses_refs, permissions). It is in the editor. It has a bug. Find it and fix it.
Input and output:
uses_refs: everyuses:value in the workflow, in order, each meant to be"action@ref", such as"actions/checkout@v4". The ref is a tag, a branch or a commit SHA (the hash that names one exact commit). The list can be empty.permissions: the workflow'spermissions:block asscope=levelpairs joined by commas, such as"contents=read". It is empty when the workflow has no block.- Return the findings, as a list of strings.
Rules the tests check:
- Each entry in
uses_refsadds at most one finding, in input order. Repeats are kept: the same entry twice gives two findings. - An entry with no
@adds"unpinned:"followed by the whole entry. - Otherwise split the entry at its last
@. The text before it is the action and the text after it is the ref. - The ref is pinned only when it is exactly 40 characters, each a lowercase hex digit (0 to 9 or a to f). Uppercase letters do not count.
- Any other ref adds
"unpinned:"followed by the action. The action can be empty. - After all the entry findings, add at most one permissions finding. Trim whitespace at both ends of
permissions. If nothing is left, add"missing-permissions". - Otherwise, if the trimmed text contains
=writeanywhere, add"write-permissions". It is a plain text search and case sensitive, socontents = writeandcontents=WRITEdo not count. - Return an empty list when nothing is wrong.
Examples
audit_supply_chain(uses_refs: list[str], permissions: str) -> list[str]Example 1
Input
audit_supply_chain(["actions/checkout@main", "some-vendor/deploy-action@v3"], "")Output
["unpinned:actions/checkout", "unpinned:some-vendor/deploy-action", "missing-permissions"]Example 2
Input
audit_supply_chain(["actions/checkout@a1b2c3d4e5f60718293a4b5c6d7e8f9012345678"], "contents=read")Output
[]