fix(llm-security): YAML/workflow parser divergence — block scalars + bare if: (#32,#33,#43)
#33 the frontmatter parser collected a block-scalar body (description: |) but did not skip it, so an indented name:/allowed_tools: inside the body re-matched as a top-level key and overrode the real values TRG-shadow and permission checks depend on; the parser now consumes block-scalar bodies as opaque content. #32 block-scalar headers carrying indentation/chomping indicators (|2, >-, |-2) were not recognized, so their bodies never reached the run: injection sink; now matched via a proper indicator/chomping regex. #43 the B4 actor auth-bypass detector inspected only braced ${{ }} expressions, missing the canonical bare 'if: github.actor == ...' form (Synacktiv Dependabot-spoof false negative); bare if: expressions now emit a synthetic event the detector reads. Suite 2004/0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcQyMTQfyrsAapaCMPxTtQ
This commit is contained in:
parent
207385fbbe
commit
b224e18b42
8 changed files with 244 additions and 7 deletions
|
|
@ -63,6 +63,13 @@ describe('workflow-scanner — true-positive cases', () => {
|
|||
assert.equal(fs[0].severity, 'high');
|
||||
assert.match(fs[0].evidence, /issue\.body/);
|
||||
});
|
||||
|
||||
it('flags issue.title inside `run: |2` (indentation indicator) as HIGH (#32)', () => {
|
||||
const fs = findingsByFile(result.findings, 'tp-block-scalar-indent.yml');
|
||||
assert.equal(fs.length, 1);
|
||||
assert.equal(fs[0].severity, 'high');
|
||||
assert.match(fs[0].evidence, /issue\.title/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('workflow-scanner — false-positive suppression', () => {
|
||||
|
|
@ -184,6 +191,18 @@ describe('workflow-scanner — B4 auth-bypass', () => {
|
|||
assert.match(auth.recommendation, /pull_request\.user\.login/);
|
||||
});
|
||||
|
||||
it('flags bare `if: github.actor == \'dependabot[bot]\'` (no ${{ }}) as MEDIUM auth-bypass (#43)', async () => {
|
||||
resetCounter();
|
||||
const r = await scan(FIXTURE_DIR);
|
||||
const fs = findingsByFile(r.findings, 'auth-bypass-bare-if.yml');
|
||||
const auth = fs.find(f => /Actor auth-bypass/i.test(f.title));
|
||||
assert.ok(auth, `expected an Actor auth-bypass finding in ${JSON.stringify(fs)}`);
|
||||
assert.equal(auth.severity, 'medium');
|
||||
assert.equal(auth.owasp, 'LLM06');
|
||||
assert.match(auth.evidence, /github\.actor == 'dependabot\[bot\]'/);
|
||||
assert.match(auth.recommendation, /pull_request\.user\.login/);
|
||||
});
|
||||
|
||||
it('does NOT flag plain `if: ${{ startsWith(github.head_ref, …) }}` as auth-bypass', async () => {
|
||||
resetCounter();
|
||||
const r = await scan(FIXTURE_DIR);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue