test: coverage-audit additions for the fix wave

Ship Step 7 gap-fill (all passing, 248 tests across the touched suites):
memory + dream stage probe-timeout proceeds, gbrain-detect override paths,
stale-flag passthrough, 200-body-missing-.security fail-closed case,
telemetry redaction edges, and credential-pattern edge cases.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-06-11 23:08:54 -07:00
parent 028cb52ca8
commit 88ca684929
6 changed files with 168 additions and 0 deletions

View File

@@ -136,6 +136,27 @@ describe('gbrain detection override → gen-skill-docs', () => {
}
});
test('with status "timeout" (slow-but-healthy, #1964), brain blocks render like "ok"', () => {
const { tmpHome, cleanup } = makeFixture(
JSON.stringify({ gbrain_local_status: 'timeout', gbrain_on_path: true, gbrain_version: 'test-0.41.0' }),
);
try {
const snap = regenAndSnapshot({
respectDetection: true,
tmpHome,
files: PROBE_FILES,
});
const content = probeUnion(snap);
// A slow engine must not silently suppress brain features — same
// treatment as "ok" (matches gstack-gbrain-detect --is-ok).
expect(content).toContain('## Save Results to Brain');
expect(content).toContain('gbrain put "office-hours/');
} finally {
cleanup();
}
});
test('with detected:false (status != "ok"), brain blocks stay suppressed', () => {
const { tmpHome, cleanup } = makeFixture(
JSON.stringify({ gbrain_local_status: 'no-cli', gbrain_on_path: false, gbrain_version: null }),

View File

@@ -391,6 +391,20 @@ describe("lib/gbrain-local-status — cache behavior", () => {
expect(localEngineStatus({ noCache: false })).toBe("ok");
});
it("invalidates cache when GBRAIN_HOME changes (key invariant, codex D11)", () => {
env = makeEnv({ withGbrain: true, gbrainBehavior: "ok", withConfig: true });
restoreEnv = applyEnv(env);
expect(localEngineStatus({ noCache: false })).toBe("ok");
// Point GBRAIN_HOME at an empty dir: a stale cached "ok" must not win —
// gbrain_home is part of the cache key, so this re-probes and finds no
// config at the new location.
const altHome = join(env.tmp, "alt-gbrain-empty");
mkdirSync(altHome, { recursive: true });
process.env.GBRAIN_HOME = altHome;
expect(localEngineStatus({ noCache: false })).toBe("missing-config");
});
it("invalidates cache when HOME changes (key invariant)", () => {
env = makeEnv({ withGbrain: true, gbrainBehavior: "ok", withConfig: true });
restoreEnv = applyEnv(env);

View File

@@ -152,6 +152,36 @@ describe("gstack-gbrain-sync — split-engine SKIP (plan D12)", () => {
}
}, 30_000); // proceeding runs the real code-import path against the slow fake (~11s)
it("memory stage also PROCEEDS (with warning) on probe timeout (#1964)", () => {
const env = makeEnv({ withGbrain: true, gbrainBehavior: "slow", withConfig: true });
try {
const r = runOrchestrator(env, ["--no-code", "--no-brain-sync"], {
GSTACK_GBRAIN_PROBE_TIMEOUT_MS: "300",
});
const out = r.stdout + r.stderr;
expect(out).not.toContain("local engine timeout");
expect(out).toContain("memory: engine probe timed out");
} finally {
env.cleanup();
}
}, 30_000);
it("dream stage also PROCEEDS (with warning) on probe timeout (#1964)", () => {
const env = makeEnv({ withGbrain: true, gbrainBehavior: "slow", withConfig: true });
try {
const r = runOrchestrator(
env,
["--dream", "--no-code", "--no-memory", "--no-brain-sync"],
{ GSTACK_GBRAIN_PROBE_TIMEOUT_MS: "300" },
);
const out = r.stdout + r.stderr;
expect(out).not.toContain("local engine timeout");
expect(out).toContain("dream: engine probe timed out");
} finally {
env.cleanup();
}
}, 30_000);
it("SKIPs code stage when local engine is broken-db; brain-sync still attempted", () => {
const env = makeEnv({ withGbrain: true, gbrainBehavior: "broken-db", withConfig: true });
try {

View File

@@ -12,6 +12,7 @@ import {
exitCodeFor,
maskPreview,
normalizeWithMap,
redactFindingSpans,
type RepoVisibility,
} from "../lib/redact-engine";
import {
@@ -364,6 +365,52 @@ describe("masking + purity", () => {
});
});
describe("redactFindingSpans — machine-egress masking (#1947)", () => {
test("clean input passes through unchanged", () => {
const text = "push failed: remote rejected the branch";
expect(redactFindingSpans(text, { repoVisibility: "private" })).toBe(text);
});
test("a single finding's span becomes <REDACTED-{id}>, context survives", () => {
const token = "ghp_" + "1234567890abcdefghijklmnopqrstuvwxyz";
const out = redactFindingSpans(`auth ${token} rejected`, { repoVisibility: "private" });
expect(out).toBe("auth <REDACTED-github.pat> rejected");
});
test("multiple findings are all replaced (right-to-left splice keeps offsets valid)", () => {
const pat = "ghp_" + "1234567890abcdefghijklmnopqrstuvwxyz";
const aws = "AKIA1234567890ABCDEF";
const out = redactFindingSpans(`first ${aws} then ${pat} end`, {
repoVisibility: "private",
});
expect(out).toBe("first <REDACTED-aws.access_key> then <REDACTED-github.pat> end");
});
test("fails closed (null) when a span cannot be relocated — never raw passthrough", () => {
// env.kv's span (the value) starts well past the regex match start (the
// var name), so locateSpan's rewind-2 re-exec misses it. The contract is
// null → caller drops the whole payload. The one thing that must never
// happen is the secret surviving in the output.
const secret = "8Fk2pQ9vXz4wL7mN3rT6yB1cD5eG0hJq";
const out = redactFindingSpans(`API_KEY=${secret}`, { repoVisibility: "private" });
if (out !== null) {
// If locateSpan ever learns to find context-prefixed spans, masking
// must actually mask.
expect(out).not.toContain(secret);
} else {
expect(out).toBeNull();
}
});
test("multiline input redacts a finding past the first line (locateSpan line/col path)", () => {
const token = "ghp_" + "1234567890abcdefghijklmnopqrstuvwxyz";
const out = redactFindingSpans(`line one\nline two has ${token}\nline three`, {
repoVisibility: "private",
});
expect(out).toBe("line one\nline two has <REDACTED-github.pat>\nline three");
});
});
describe("taxonomy integrity", () => {
test("every pattern has a unique id", () => {
const set = new Set(PATTERNS.map((p) => p.id));

View File

@@ -174,6 +174,14 @@ describe("gstack-security-dashboard — never reports fake zeros (#1947)", () =>
expect(parsed.security.top_attack_domains[0].domain).toBe("evil.example");
});
it("stale cache responses pass the stale flag through (json mode)", () => {
const staleBody = JSON.stringify({ ...JSON.parse(GOOD_BODY_MARKER), stale: true });
const r = run(SEC_BIN, { mode: "ok", body: staleBody, json: true });
const parsed = JSON.parse(r.stdout.trim());
expect(parsed.status).toBe("ok");
expect(parsed.stale).toBe(true);
});
it("200 without marker (legacy backend) → figures shown with unverified note", () => {
const r = run(SEC_BIN, { mode: "ok", body: GOOD_BODY_LEGACY });
expect(r.stdout).toContain("Attacks detected last 7 days: 3");
@@ -186,6 +194,18 @@ describe("gstack-security-dashboard — never reports fake zeros (#1947)", () =>
expect(parsed.status).toBe("legacy_unverified");
expect(parsed.security.attacks_last_7_days).toBe(3);
});
it("200 with a body missing .security → unknown backend_error, never 0", () => {
const r = run(SEC_BIN, {
mode: "ok",
body: JSON.stringify({ weekly_active: 42, status: "ok" }),
json: true,
});
const parsed = JSON.parse(r.stdout.trim());
expect(parsed.status).toBe("unknown");
expect(parsed.reason).toBe("backend_error");
expect(parsed.security).toBeNull();
});
});
describe("gstack-community-dashboard — never reports fake zeros (#1947)", () => {
@@ -212,4 +232,10 @@ describe("gstack-community-dashboard — never reports fake zeros (#1947)", () =
expect(r.stdout).toContain("Weekly active installs: 42");
expect(r.stdout).toContain("unverified");
});
it("200 with a garbage body (no weekly_active) → unknown, never 0", () => {
const r = run(COMM_BIN, { mode: "ok", body: '{"error":"weird"}' });
expect(r.stdout).toContain("unknown — backend error (HTTP 200)");
expect(r.stdout).not.toContain("Weekly active installs:");
});
});

View File

@@ -236,6 +236,36 @@ describe('gstack-telemetry-log', () => {
expect(lines[0]).not.toContain(token);
});
test('truncates error_message to 200 chars after redaction (#1947)', () => {
setConfig('telemetry', 'anonymous');
const long = 'x'.repeat(300);
run(
`${BIN}/gstack-telemetry-log --skill qa --duration 10 --outcome error --error-message '${long}' --session-id red-3`,
);
const events = parseJsonl();
expect(events).toHaveLength(1);
expect(events[0].error_message.length).toBeLessThanOrEqual(200);
});
test('fails closed: error_message becomes null when the engine cannot relocate a span (#1947)', () => {
setConfig('telemetry', 'anonymous');
const secret = '8Fk2pQ9vXz4wL7mN3rT6yB1cD5eG0hJq';
// env.kv-shaped finding (line-anchored, so the assignment leads the
// message): the span (value) starts past the regex match start,
// locateSpan misses it, redactFindingSpans returns null — the bin must
// drop the whole message, never pass it through raw.
run(
`${BIN}/gstack-telemetry-log --skill qa --duration 10 --outcome error --error-message 'API_KEY=${secret} rejected by daemon' --session-id red-4`,
);
const lines = readJsonl();
expect(lines).toHaveLength(1);
const event = JSON.parse(lines[0]);
expect(event.error_message).toBeNull();
expect(lines[0]).not.toContain(secret);
});
test('creates analytics directory if missing', () => {
// Remove analytics dir
const analyticsDir = path.join(tmpDir, 'analytics');