From 7399fb657782de4750437c20d5b22f4f3c93cbc2 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 4 Aug 2026 00:20:39 +0000 Subject: [PATCH] fix(policy): reject uses block scalar action refs Co-authored-by: Adam Moussa --- .github/workflows/callable-pr-policy.yaml | 10 ++++++++-- test/pr-policy.test.mjs | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/.github/workflows/callable-pr-policy.yaml b/.github/workflows/callable-pr-policy.yaml index 406941e..b6859a5 100644 --- a/.github/workflows/callable-pr-policy.yaml +++ b/.github/workflows/callable-pr-policy.yaml @@ -236,8 +236,10 @@ jobs: // starts a block scalar. Lines inside ANY block scalar are not parsed // as structural YAML keys — they are content. For run: block scalars, // the content is still scanned for expression injection (expressions - // must flow through env:). For non-run block scalars (e.g. script:, - // name:), the content is skipped entirely — no uses: or run: detection. + // must flow through env:). uses: block scalars are rejected because an + // action ref must be an inline scalar to validate its immutable pin. + // For other non-run block scalars (e.g. script:, name:), the content + // is skipped entirely — no uses: or run: detection. // // Quoted keys: "uses", 'uses', "run", 'run', "permissions" are all // recognized in addition to their unquoted forms. @@ -373,6 +375,10 @@ jobs: const blockM = line.match(/^([ \t]*)(?:-[ \t]+)?("([^"]*)"|'([^']*)'|([\w-]+)):[ \t]*([|>](?:[1-9][-+]?|[-+][1-9]?)?)[ \t]*(?:#.*)?$/); if (blockM) { const keyName = blockM[3] !== undefined ? blockM[3] : (blockM[4] !== undefined ? blockM[4] : (blockM[5] || '')); + if (keyName === 'uses') { + errs.push(filename + ':' + (i + 1) + ': uses: block scalar is not supported — action refs must be inline and pinned to an immutable SHA [fnv:' + fnv1a32(line.trim()) + ']'); + continue; + } inBlock = true; blockIndent = blockM[1].length; blockIsRun = (keyName === 'run'); diff --git a/test/pr-policy.test.mjs b/test/pr-policy.test.mjs index ef39cf2..d16f676 100644 --- a/test/pr-policy.test.mjs +++ b/test/pr-policy.test.mjs @@ -1089,6 +1089,26 @@ describe('validateWorkflowContent — quoted keys and block-scalar tracking', () assert.deepEqual(v.validateWorkflowContent(content, BASE), [], "single-quoted 'uses' with valid SHA should pass"); }); + it('rejects uses block scalar before opaque block handling can hide it', () => { + const content = [ + ...wfHeader().split('\n'), + ' - uses: >-', + ' actions/checkout@v4', + ].join('\n'); + const errs = v.validateWorkflowContent(content, BASE); + assert.ok(errs.some(e => e.includes('uses: block scalar')), 'uses: folded block scalar must fail: ' + errs.join('; ')); + }); + + it('rejects quoted uses block scalar before pin validation can be bypassed', () => { + const content = [ + ...wfHeader().split('\n'), + ' - "uses": |-', + ' actions/checkout@v4', + ].join('\n'); + const errs = v.validateWorkflowContent(content, BASE); + assert.ok(errs.some(e => e.includes('uses: block scalar')), 'quoted uses: literal block scalar must fail: ' + errs.join('; ')); + }); + it('recognises sequence-form "- run: |" and does not flag uses: inside as action ref', () => { // The sequence item `- run: |` opens a run block scalar. // uses: lines inside must not be treated as action references.