Files
gstack/test/redact-span-binding.test.ts
T
Garry Tan b9706f3635 v1.88.1.0 fix: harden credential boundaries and owned state (#2942)
* fix(settings): preserve symlinked settings targets

Resolve the selected target for locking, mutation, backup, and rollback; refuse target changes and preserve private modes. Addresses #2830.

* fix(redact): bind masking to original detected spans

Inspired by #2929's anchored-span diagnosis; independently implemented using normalization offsets. Addresses #2930 and the relocation portion of #2912 without changing detection sensitivity.

* fix(evals): exclude operator credentials from prefix admission

Adapts the credential-suffix screen proposed in #2636, with real launched-child regression coverage and deliberate provider-auth exceptions.

* fix(artifacts): retain custom allowlist rules on reinitialization

Preserve the exact user-owned suffix and publish only a successfully assembled replacement. Independently implements the repair reported in #2907.

* test(cso): verify exact masked reads and unmaskable payload refusal

* fix(cso): preserve exact filesystem identities through lease recovery

Preserve 64-bit device/inode identity and nanosecond race checks. Add native NTFS lifecycle coverage for #2927; retain ambiguous legacy-state refusal without claiming Windows PID-reuse recovery is resolved.

* fix(redact): bind pre-push scans to destination and preserve seam context

Uses #2935 (bd07318) as source evidence for push-target range and slice-overlap defects. Independently implemented; no cherry-pick or release metadata adoption.

* test(ci): gate native agent ownership and settings links on macOS

* fix(browse): bind agent lifetimes and cleanup to owned generations

Uses #2931 by Chris Hutton / Claude Fable 5.1 as attributed design input; independently implemented without broad sweeps or copied code. Keep uncertain children and locks rather than deleting foreign state.

* test(ci): include concurrent shutdown controls in the native macOS gate

* v1.88.1.0 fix: harden credential boundaries and owned state

* fix(redact): preserve target provenance and scan boundary semantics

* test(artifacts): read managed rules from atomic allowlist assembly

* fix: preserve native exit observations and fixture prerequisites

* fix: preserve UTF-16 offsets through redaction normalization
2026-09-23 08:54:53 -04:00

73 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, test } from "bun:test";
import { redactFindingSpans, scan } from "../lib/redact-engine";
import { redact, sanitizeForJson } from "../lib/cso/process";
const secret = ["8Fk2pQ9vXz4wL7mN", "3rT6yB1cD5eG0hJq"].join("");
const marker = "<REDACTED-env.kv>";
describe("redaction binds each finding to its original span", () => {
test("never masks a later unflagged assignment instead of the flagged value", () => {
const input = `DB_PASSWORD=${secret}\nOTHER_API_KEY="your-api-key-here"`;
expect(scan(input).findings.map((f) => f.id)).toEqual(["env.kv"]);
expect(redactFindingSpans(input)).toBe(`DB_PASSWORD=${marker}\nOTHER_API_KEY="your-api-key-here"`);
});
test("literal example values are suppressed before redaction", () => {
const input = `DB_PASSWORD=synthetic-example-secret\nOTHER_API_KEY="your-api-key-here"`;
expect(scan(input).findings).toEqual([]);
expect(redactFindingSpans(input)).toBe(input);
});
for (const [name, prefix, suffix] of [
["first line", "", ""],
["later line", "unrelated line\n", "\nend"],
["indented", "header\n ", ""],
["CRLF", "header\r\n", "\r\nend"],
]) {
test(`${name}: masks the captured value and keeps surrounding text`, () => {
const input = `${prefix}DB_PASSWORD=${secret}${suffix}`;
expect(scan(input).findings.map((f) => f.id)).toEqual(["env.kv"]);
expect(redactFindingSpans(input)).toBe(`${prefix}DB_PASSWORD=${marker}${suffix}`);
});
}
test("repeated identical values map independently, preserving unflagged neighbors", () => {
const input = `DB_PASSWORD=${secret}\nOTHER_API_KEY="your-api-key-here"\nAPI_KEY=${secret}`;
expect(scan(input).findings.map((f) => f.id)).toEqual(["env.kv", "env.kv"]);
expect(redactFindingSpans(input)).toBe(`DB_PASSWORD=${marker}\nOTHER_API_KEY="your-api-key-here"\nAPI_KEY=${marker}`);
});
test("many findings retain exact order without per-finding raw rescans", () => {
const input = Array.from({ length: 300 }, (_, i) => `API_KEY=${secret}${i.toString(36)}`).join("\n");
const output = redactFindingSpans(input);
expect(scan(input).findings).toHaveLength(300);
expect(output).toBe(Array.from({ length: 300 }, () => `API_KEY=${marker}`).join("\n"));
});
test("normalization maps fullwidth Unicode, entity text, and zero-width bytes back to original span", () => {
const encoded = `${secret.slice(0, 9)}&${secret.slice(9)}`;
const entity = `${secret.slice(0, 9)}&amp;${secret.slice(9)}`;
const invisible = `${secret.slice(0, 9)}\u200b${secret.slice(9)}`;
for (const value of [encoded, entity, invisible]) {
const input = `DB_PASSWORD=${value}\nOTHER_API_KEY="your-api-key-here"`;
expect(scan(input).findings.map((f) => f.id)).toEqual(["env.kv"]);
expect(redactFindingSpans(input)).toBe(`DB_PASSWORD=${marker}\nOTHER_API_KEY="your-api-key-here"`);
}
});
test("overlapping JWT and Bearer findings coalesce; marker-only and unlocated oversize finding still withhold", () => {
const part = "Ab3dE6fGh8Ij9Kl0Mn1O";
const jwt = `eyJ${part}.eyJ${part}.${part}`;
expect(scan(`Authorization: Bearer ${jwt}`).findings.map((f) => f.id)).toEqual(["auth.bearer", "jwt"]);
expect(redactFindingSpans(`Authorization: Bearer ${jwt}`)).toMatch(/^Authorization: Bearer <REDACTED-[a-z.+]+>$/);
expect(redactFindingSpans("-----BEGIN " + "PRIVATE KEY-----\nbody")).toBeNull();
expect(redactFindingSpans(`DB_PASSWORD=${secret}`, { maxBytes: 10 })).toBeNull();
});
test("CSO process and JSON output use the exact span, without dropping safe context", () => {
const input = `DB_PASSWORD=${secret}\nOTHER_API_KEY="your-api-key-here"`;
expect(redact(input)).toBe(`DB_PASSWORD=${marker}\nOTHER_API_KEY="your-api-key-here"`);
expect(sanitizeForJson({ output: input })).toEqual({ output: `DB_PASSWORD=${marker}\nOTHER_API_KEY="your-api-key-here"` });
});
});