mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-10 15:09:00 +02:00
The repo owned a redaction engine and had zero CI-side secret scanning. quality-gate.yml now pipes every PR diff's ADDED lines through our own bin/gstack-redact (gate-secret-scan.mjs, taken from the fork — it dogfoods the engine): HIGH findings fail the check, MEDIUM prints an advisory count only (no human in CI to confirm), planted-bug fixtures excluded by pathspec. Live-verified both directions: PEM key fails, clean diff and MEDIUM shapes pass; ShellCheck (errors) covers the setup/build shell boundary and passes today; bun audit gates critical advisories. Trigger is pull_request, never pull_request_target. dependency-review.yml adopts the hardened never-merged prior-art branch (fail-on-severity high, workflow paths watched, tight perms) — verify the dependency graph parses bun.lock with a canary bump before trusting the gate. dependabot: weekly, grouped per ecosystem, capped PR counts; and evals.yml image build/push now skips dependabot actors, whose read-only GITHUB_TOKEN made every lockfile bump a permanently red check. OSV scans weekly with a reasoned ignore file. All new workflow actions SHA-pinned. Scorecard deliberately not taken (no consumer for the score). The PR template front-loads the evidence bar (live proof, liveness screenshot, no-ETHOS/voice-changes checklist); the unenforced DCO line is dropped. bin/gstack-verify-gate ships OPT-IN (never registered by ./setup — a Stop hook running the project's verify command after every turn is the user's call), with the fork's tests adapted to pin exactly that. Ported from time-attack/gstack (GStack 2) + our own prior-art branch. Co-authored-by: Sina Matian <sina@time-attack.dev> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
32 lines
1.1 KiB
JavaScript
32 lines
1.1 KiB
JavaScript
#!/usr/bin/env node
|
|
import { spawn } from "node:child_process";
|
|
|
|
const child = spawn("bun", [
|
|
"bin/gstack-redact",
|
|
"--repo-visibility", "public",
|
|
"--json",
|
|
"--max-bytes", "16000000",
|
|
], { shell: false, windowsHide: true, stdio: ["pipe", "pipe", "inherit"] });
|
|
let diff = "";
|
|
process.stdin.setEncoding("utf8");
|
|
process.stdin.on("data", (chunk) => { diff += chunk; });
|
|
process.stdin.once("end", () => {
|
|
const additions = diff
|
|
.split(/\r?\n/)
|
|
.filter((line) => line.startsWith("+") && !line.startsWith("+++"))
|
|
.map((line) => line.slice(1))
|
|
.join("\n");
|
|
child.stdin.end(additions);
|
|
});
|
|
let stdout = "";
|
|
child.stdout.setEncoding("utf8");
|
|
child.stdout.on("data", (chunk) => { stdout += chunk; });
|
|
child.once("error", (error) => { throw error; });
|
|
child.once("close", (code) => {
|
|
const report = JSON.parse(stdout);
|
|
const high = Number(report.counts?.HIGH ?? 0);
|
|
const medium = Number(report.counts?.MEDIUM ?? 0);
|
|
console.log(`credential scan: ${high} high, ${medium} advisory`);
|
|
process.exitCode = high > 0 || report.oversize || ![0, 2, 3].includes(code) ? 1 : 0;
|
|
});
|