diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 42d540c5..176b1899 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -347,8 +347,17 @@ jobs: - name: Verify rafter agent scan detects secrets run: | + # Copy the fixture to a tmpdir OUTSIDE the repo before scanning. The + # repo's own .rafter.yml declassifies **/fixtures/** (triaged FPs for + # the dogfooding security gate); policy discovery walks cwd -> git + # root, so scanning the in-repo path would hit that policy and + # suppress the finding (exit 0). This step verifies the PUBLISHED + # engine detects secrets, independent of this repo's self-scan policy. + SMOKE_DIR=$(mktemp -d) + cp .github/fixtures/fake-secret.txt "$SMOKE_DIR/fake-secret.txt" + cd "$SMOKE_DIR" # Scan fixture file with fake secret — must exit 1 (secrets found) - if rafter agent scan .github/fixtures/fake-secret.txt --engine patterns; then + if rafter agent scan fake-secret.txt --engine patterns; then echo "FAIL: scan should have exited non-zero (secrets found)" exit 1 fi @@ -398,8 +407,17 @@ jobs: - name: Verify rafter agent scan detects secrets run: | + # Copy the fixture to a tmpdir OUTSIDE the repo before scanning. The + # repo's own .rafter.yml declassifies **/fixtures/** (triaged FPs for + # the dogfooding security gate); policy discovery walks cwd -> git + # root, so scanning the in-repo path would hit that policy and + # suppress the finding (exit 0). This step verifies the PUBLISHED + # engine detects secrets, independent of this repo's self-scan policy. + SMOKE_DIR=$(mktemp -d) + cp .github/fixtures/fake-secret.txt "$SMOKE_DIR/fake-secret.txt" + cd "$SMOKE_DIR" # Scan fixture file with fake secret — must exit 1 (secrets found) - if /tmp/rafter-smoke/bin/rafter agent scan .github/fixtures/fake-secret.txt --engine patterns; then + if /tmp/rafter-smoke/bin/rafter agent scan fake-secret.txt --engine patterns; then echo "FAIL: scan should have exited non-zero (secrets found)" exit 1 fi diff --git a/.github/workflows/test-action.yml b/.github/workflows/test-action.yml index b9cdb5e2..11c7e88b 100644 --- a/.github/workflows/test-action.yml +++ b/.github/workflows/test-action.yml @@ -20,11 +20,22 @@ jobs: steps: - uses: actions/checkout@v4 + # Stage the fixture in a neutral temp dir OUTSIDE the fixtures/tests + # tree. The action runs `rafter secrets` from the workspace root, where + # this repo's own .rafter.yml declassifies **/fixtures/** (triaged FPs + # for the dogfooding gate) — scanning the in-repo path would suppress the + # finding. This job verifies the ENGINE detects the secret, so we scan a + # copy at a path the policy's globs don't match. + - name: Stage fixture in a clean temp dir + run: | + mkdir -p /tmp/rafter-detect + cp .github/fixtures/fake-secret.txt /tmp/rafter-detect/fake-secret.txt + - name: Run Rafter action on fixture with known secret id: scan uses: ./ # test the local action.yml with: - scan-path: '.github/fixtures' + scan-path: '/tmp/rafter-detect' format: json continue-on-error: true @@ -88,11 +99,18 @@ jobs: with: python-version: '3.12' + # Scan a copy outside the fixtures tree so the repo's .rafter.yml does + # not suppress the finding (see "detect secrets" job for rationale). + - name: Stage fixture in a clean temp dir + run: | + mkdir -p /tmp/rafter-detect + cp .github/fixtures/fake-secret.txt /tmp/rafter-detect/fake-secret.txt + - name: Run Rafter action via pip id: scan uses: ./ with: - scan-path: '.github/fixtures' + scan-path: '/tmp/rafter-detect' install-method: pip format: json continue-on-error: true @@ -112,11 +130,18 @@ jobs: steps: - uses: actions/checkout@v4 + # Scan a copy outside the fixtures tree so the repo's .rafter.yml does + # not suppress the finding (see "detect secrets" job for rationale). + - name: Stage fixture in a clean temp dir + run: | + mkdir -p /tmp/rafter-detect + cp .github/fixtures/fake-secret.txt /tmp/rafter-detect/fake-secret.txt + - name: Run published Rafter action @v1 id: scan uses: raftersecurity/rafter-cli@v1 with: - scan-path: '.github/fixtures' + scan-path: '/tmp/rafter-detect' format: json continue-on-error: true diff --git a/node/tests/github-action.test.ts b/node/tests/github-action.test.ts index 415db730..7b65062f 100644 --- a/node/tests/github-action.test.ts +++ b/node/tests/github-action.test.ts @@ -563,14 +563,31 @@ describe("fixture files for action testing", () => { }); it("CLI detects secrets in fixture file", () => { - const r = rafter( - ["scan", "local", FIXTURES_DIR, "--engine", "patterns", "--format", "json"], - ); - expect(r.exitCode).toBe(1); - // stdout or stderr should contain JSON scan results with findings - const combined = r.stdout + r.stderr; - expect(combined).toContain("AWS"); - expect(combined).toContain("matches"); + // Scan an isolated copy OUTSIDE the repo. This repo's own .rafter.yml + // declassifies **/fixtures/** (its fixtures are triaged FPs for the + // dogfooding security gate), and policy discovery walks cwd -> git root, + // so scanning the in-repo fixture would hit that policy and suppress the + // finding (exit 0). This test verifies the detection ENGINE, which must be + // decoupled from the repo's self-scan policy — so we copy the fixture to a + // tmpdir (no .rafter.yml above it) and scan there. + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "rafter-fixture-")); + try { + fs.copyFileSync( + path.join(FIXTURES_DIR, "fake-secret.txt"), + path.join(tmp, "fake-secret.txt"), + ); + const r = rafter( + ["scan", "local", tmp, "--engine", "patterns", "--format", "json"], + { cwd: tmp }, + ); + expect(r.exitCode).toBe(1); + // stdout or stderr should contain JSON scan results with findings + const combined = r.stdout + r.stderr; + expect(combined).toContain("AWS"); + expect(combined).toContain("matches"); + } finally { + fs.rmSync(tmp, { recursive: true, force: true }); + } }, 15000); });