Fix the supply-chain audit of a deploy workflow

Review the AI's codeEasy10 minMicrosoft style

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: every uses: 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's permissions: block as scope=level pairs 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_refs adds 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 =write anywhere, add "write-permissions". It is a plain text search and case sensitive, so contents = write and contents=WRITE do not count.
  • Return an empty list when nothing is wrong.

Examples

audit_supply_chain(uses_refs: list[str], permissions: str) -> list[str]
Example 1
Inputaudit_supply_chain(["actions/checkout@main", "some-vendor/deploy-action@v3"], "")
Output["unpinned:actions/checkout", "unpinned:some-vendor/deploy-action", "missing-permissions"]
Example 2
Inputaudit_supply_chain(["actions/checkout@a1b2c3d4e5f60718293a4b5c6d7e8f9012345678"], "contents=read")
Output[]