Files
gstack/test/redact-context-exemptions.test.ts
T
Garry Tanandcapy-ai[bot] a8e36b9815 Fewer false privacy holds when posting PRs and running pre-merge checks (GST-2047) (#4198)
* Stop flagging test run numbers in PR descriptions as phone numbers (GST-2047)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Let approval import record autoplan run ids that look like phone or card numbers (GST-2047)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Read versions beside a VERSION file or a semver as versions, not public IPs (GST-2047)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Say pre-merge checks in the changelog entry (GST-2047)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Keep the phone-number check on run-id fields that hold other text and on run id names (GST-2047)

Review-Finding: p1/red-team#1 reader=red-team
Review-Finding: p1/specialist-batched#1 reader=security
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Exempt only 11 to 20 digit run numbers in a ship summary block from the phone-number check (GST-2047)

Review-Finding: v1/adversarial-native#2 reader=adversarial-native
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-11-09 08:55:27 +00:00

536 lines
22 KiB
TypeScript

/**
* Redaction false positives that blocked pushes and decision logs, each paired
* with true-positive controls that must still report at the same tier.
*
* Every exemption below is decided by context, so each table carries the
* benign vector AND the same (or same-shaped) value in a sensitive context.
* A guard that passes the benign half by gutting the pattern fails the
* control half.
*/
import { describe, test, expect } from "bun:test";
import * as fs from "node:fs";
import * as path from "node:path";
import { scan, type Finding } from "../lib/redact-engine";
const FIXTURES = path.join(import.meta.dir, "fixtures", "redact-diagram");
function findings(text: string): Finding[] {
return scan(text, { repoVisibility: "private" }).findings;
}
function tierOf(text: string, id: string): string | undefined {
return findings(text).find((f) => f.id === id)?.severity;
}
function table(id: string, tier: string, benign: string[], controls: string[]): void {
for (const input of benign) {
test(`clean: ${input.slice(0, 70)}`, () => {
expect(findings(input).map((f) => f.id)).not.toContain(id);
});
}
for (const input of controls) {
test(`still ${tier}: ${input.slice(0, 70)}`, () => {
expect(tierOf(input, id)).toBe(tier);
});
}
}
describe("pii.phone.e164 / pii.cc: vector geometry is not PII (#2885, #2827)", () => {
table(
"pii.phone.e164",
"MEDIUM",
[
"M 37.6188 101.694 L 100 64.5326 100",
"M50 1C1 2 37.6188 101.694 51.6863 99.4363Z",
'viewBox="0 0 581.66796875 695.65625"',
'transform="translate(296.1484375, 298.796875)"',
"fill:hsl(0, 0%, 98.9215686275%)",
"v 12.3456 100.125 7.5",
'{"type":"rectangle","x":250.9140625,"y":8}',
'{"seed":1808177121,"version":3,"versionNonce":1365644783}',
'"updated": 1791059590857,',
],
[
"call me at +1 415 555 0123 tomorrow",
"tel: +14155550123",
"p 415.555.0123",
"tel +1 415.555.0123",
"ring +44 20 7946 0958",
"phone: (415) 555-0123",
"+37.6188 101.694",
'{"phone":1808177121}',
'"contact": 14155550123,',
"seed 1808177121",
],
);
table(
"pii.cc",
"MEDIUM",
['"height":492.34399999999994,', '"gap":3.266375000000039}', "0.4111111111111111"],
["card 4111 1111 1111 1111", "card 4111111111111111.", "4111111111111111"],
);
test("a rendered /diagram triplet produces no PII findings", () => {
for (const name of ["flow.svg", "flow.excalidraw"]) {
const text = fs.readFileSync(path.join(FIXTURES, name), "utf8");
const pii = findings(text).filter((f) => f.id === "pii.phone.e164" || f.id === "pii.cc");
expect({ name, pii: pii.map((f) => f.id) }).toEqual({ name, pii: [] });
}
});
});
describe("env.kv: a function call assigned to a credential name is code (#2899)", () => {
table(
"env.kv",
"MEDIUM",
[
' session = _FlakySession(requests.ReadTimeout("Read timed out"))',
" session = _FlakySession(responses=[1])",
" session = requests.Session()",
" session = find_session(client, args.session_id, now=now, state=state)",
' token = make_token(user, scopes=["read"])',
" password = getpass.getpass()",
" session = build_http_session(",
],
[
'session = "Xk9pQ2mLr7Tz4vBn"',
"SESSION_SECRET=9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c",
'password = "Tr0ub4dor&3!xyz"',
"PASSWORD=Tr0ub4dor&3!xyz#",
"password = correct.horse.battery.staple",
'api_key: "Zx9Qw8Er7Ty6Ui5Op4As3Df2"',
"API_SECRET=Abc123xyz(9Qm",
'password = "getPassword(x)Q9z"',
'token = decrypt("Zx9Qw8Er7Ty6Ui5Op4As3Df2")',
],
);
});
describe("env.kv: a name-shaped literal inside a call is a file or dict key, not a secret (GST-1064)", () => {
const FAKE_KEY = "Zx9Qw8Er7Ty6Ui5Op4As3Df2";
const FAKE_STRIPE_TAIL = "4eC39HqLyjWDarjtT1zdp7dc";
table(
"env.kv",
"MEDIUM",
[
String.raw`key = json.loads(files.get("key.json", ""))`,
String.raw`token = resp.json().get("access_token")`,
String.raw` session = store.load('session.cookie')`,
String.raw`secret = vault.read("client_secret")`,
],
[
String.raw`key = "${FAKE_KEY}"`,
String.raw`KEY=${FAKE_KEY}`,
String.raw`key = json.loads("sk_live_${FAKE_STRIPE_TAIL}")`,
String.raw`token = resp.json().get("access_token", "${FAKE_KEY}")`,
String.raw`password = os.getenv("DB_PASSWORD", "hunter2.Zx9Qw8")`,
String.raw`key = json.loads(files.get("key.json")) or "${FAKE_KEY}"`,
String.raw`password = os.getenv("DB_PASSWORD", "correct-horse-battery-staple")`,
String.raw`api_key = decrypt("prod-signing-key")`,
String.raw`token = decrypt("correct_horse_battery_staple")`,
String.raw`key = decrypt("qwhzxkvbnmplor")`,
String.raw`SECRET_KEY = os.getenv("SECRET_KEY", "orange_monkey_pass")`,
String.raw`password = os.environ.get("DB_PASSWORD", "purple_walrus")`,
String.raw`token = resp.json().get("access_token", "orange_monkey_pass")`,
String.raw`key = derive_secret("tigers.horse")`,
String.raw`password = os.getenv("DB_PASSWORD", "qwhzxkvbnmplor.txt")`,
String.raw`password = make_password("orange_monkey_pass")`,
String.raw`password = bcrypt.hashpw("orange_monkey_pass", salt)`,
String.raw`token = jwt.encode("purple_walrus_pie", payload)`,
String.raw`api_key = settings.Secret("correct_horse_battery")`,
String.raw`password = hash_password("purple_walrus")`,
String.raw`password=hash_password("purple_walrus")`,
String.raw`+password = hash_password( "purple_walrus" )`,
String.raw`password = hash_password("purple_walrus").encode()`,
String.raw`password = hash_password("purple_walrus") # seed user`,
String.raw`password = config.get("db_password") or ("orange_monkey_pass")`,
String.raw`password = config.get("db_password", ("orange_monkey_pass"))`,
String.raw`password = login(user, ("purple_walrus"))`,
String.raw`password = load_cfg("app.yaml"); login("purple_walrus")`,
String.raw`password = login(user, str("purple_walrus"))`,
String.raw`password = encrypt(str("orange_monkey_pass"))`,
],
);
});
describe("env.kv: reading an environment variable in code names a secret without holding one (GST-1894)", () => {
const FAKE_KEY = "Zx9Qw8Er7Ty6Ui5Op4As3Df2";
const GH = ["ghp", "_", "Zx9Qw8Er7Ty6Ui5Op4As3Df2Gh7Jk8Lm9Np0"].join("");
table(
"env.kv",
"MEDIUM",
[
String.raw` token = opts?.env?.AGENT_API_TOKEN;`,
String.raw`token = ctx.env.AGENT_API_TOKEN;`,
String.raw`token = env?.AGENT_API_TOKEN;`,
String.raw`token = env.AGENT_API_TOKEN,`,
String.raw`token = import.meta.env.VITE_API_TOKEN;`,
String.raw`token = Bun.env.AGENT_API_TOKEN;`,
String.raw`token = this.env!.AGENT_API_TOKEN;`,
String.raw`token = process.env.AGENT_API_TOKEN;`,
String.raw`token = process.env["AGENT_API_TOKEN"];`,
String.raw`token = opts?.env?.["AGENT_API_TOKEN"];`,
String.raw`token = os.environ["OPENROUTER_API_KEY_V2"]`,
String.raw`token = os.getenv("OPENROUTER_API_KEY_V2")`,
String.raw`token = System.getenv("AGENT_API_TOKEN");`,
],
[
String.raw`token = "${FAKE_KEY}"`,
`AGENT_API_TOKEN=${FAKE_KEY}`,
`export AGENT_API_TOKEN=${FAKE_KEY}`,
String.raw`token = opts?.env?.AGENT_API_TOKEN ?? "${FAKE_KEY}"`,
String.raw`token = env.AGENT_API_TOKEN||"${FAKE_KEY}"`,
String.raw`token = process.env["AGENT_API_TOKEN"] || "${FAKE_KEY}"`,
String.raw`token = os.getenv("AGENT_API_TOKEN", "${FAKE_KEY}")`,
String.raw`token = process.env["${FAKE_KEY}"]`,
String.raw`token = os.environ["${FAKE_KEY}"]`,
`token = env.${FAKE_KEY}`,
String.raw`token = myenv.AGENT_API_TOKEN;`,
],
);
test("a known key prefix beside an env name still reports HIGH", () => {
for (const input of [`AGENT_API_TOKEN=${GH}`, String.raw`token = opts?.env?.AGENT_API_TOKEN ?? "${GH}"`]) {
expect(findings(input).some((f) => f.severity === "HIGH")).toBe(true);
}
});
});
describe("env.kv: the env-read exemption needs a finished expression with no fallback; otherwise the line reads as on main (GST-1894)", () => {
const FAKE_KEY = "Zx9Qw8Er7Ty6Ui5Op4As3Df2";
const kv = (text: string) => findings(text).some((f) => f.id === "env.kv");
test.each([
["comment after ??", String.raw`token = opts?.env?.AGENT_API_TOKEN ?? // fallback` + "\n" + String.raw` "${FAKE_KEY}";`],
["blank line after ??", String.raw`token = opts?.env?.AGENT_API_TOKEN ??` + "\n\n" + String.raw` "${FAKE_KEY}";`],
["fallback wrapped after ??", String.raw`token = opts?.env?.AGENT_API_TOKEN ??` + "\n" + String.raw` "${FAKE_KEY}";`],
["fallback on the next line", String.raw`token = opts?.env?.AGENT_API_TOKEN` + "\n" + String.raw` ?? "${FAKE_KEY}";`],
["|| on the next line", String.raw`token = env.AGENT_API_TOKEN` + "\n" + String.raw` || "${FAKE_KEY}"`],
["backtick fallback", "token = opts?.env?.AGENT_API_TOKEN ?? `" + FAKE_KEY + "`;"],
["backtick concatenation", "token = ctx.env.AGENT_API_TOKEN + `" + FAKE_KEY + "`;"],
["ternary", "token = opts?.env?.AGENT_API_TOKEN ? opts.env.AGENT_API_TOKEN : `" + FAKE_KEY + "`;"],
["python or", String.raw`token = ctx.env.AGENT_API_TOKEN or FALLBACK_TOKEN_VALUE`],
["getenv default", String.raw`token = System.getenv("AGENT_API_TOKEN", "${FAKE_KEY}");`],
["call left open", String.raw`token = System.getenv("AGENT_API_TOKEN")`],
])("fires as on main: %s", (_, input) => {
expect(kv(input)).toBe(true);
});
test.each([
["multi-line call", ["client = OpenAI(", String.raw` api_key=os.getenv("OPENAI_API_KEY"),`, String.raw` base_url="https://api.openai.com/v1",`, ")"].join("\n")],
["object property", String.raw` token: opts?.env?.AGENT_API_TOKEN,`],
["code span in a comment", "token = make_token(user) // `process.env.AGENT_API_TOKEN` fallback"],
["getenv with an empty default", String.raw`token = os.getenv("OPENROUTER_API_KEY_V2", "")`],
])("still exempt, as on main: %s", (_, input) => {
expect(kv(input)).toBe(false);
});
});
describe("env.kv: a lowercase fixture label that names itself a secret, in a test file only (GST-1894)", () => {
const kv = (text: string, sourcePath?: string) => scan(text, { repoVisibility: "private", sourcePath }).findings.some((f) => f.id === "env.kv");
test.each([
["memory-registry-pg.test.ts", ' clientSecret: "personal-secret",'],
["memory-registry-pg.test.ts", ' signingSecret: "memory-authority-signing-secret",'],
["auth.spec.tsx", " apiKey: 'staging-api-key',"],
["test_auth.py", ' password = "admin_password_fixture"'],
["auth_test.go", ' token: "refresh-token-value",'],
])("clean in %s: %s", (file, input) => {
expect(kv(input, file)).toBe(false);
});
test.each([
["memory-registry.ts", ' clientSecret: "personal-secret",'],
[undefined, ' clientSecret: "personal-secret",'],
["deploy.env", "CLIENT_SECRET=personal-secret"],
["memory-registry-pg.test.ts", ' clientSecret: "correct-horse-battery-staple",'],
["memory-registry-pg.test.ts", ' clientSecret: "Personal-Secret-9x",'],
["memory-registry-pg.test.ts", ' clientSecret: "secretive-horse",'],
["memory-registry-pg.test.ts", ' clientSecret: "Zx9Qw8Er7Ty6Ui5Op4As3Df2",'],
["memory-registry-pg.test.ts.bak", ' clientSecret: "personal-secret",'],
["src/ab-test.ts", ' clientSecret: "personal-secret",'],
["scripts/load-test.ts", ' clientSecret: "personal-secret",'],
["src/test_helpers.ts", ' clientSecret: "personal-secret",'],
["spec/auth_spec.rb", ' clientSecret: "personal-secret",'],
])("still MEDIUM in %s: %s", (file, input) => {
expect(kv(input, file)).toBe(true);
});
});
describe("internal.hostname: per-machine config filenames are not hosts (#2962, #2740)", () => {
table(
"internal.hostname",
"MEDIUM",
[
"Edit CLAUDE.local.md and settings.local.json",
"no `CLAUDE.local.md`, no @import",
"edit .claude/settings.local.json",
"helm -f values.staging.yaml",
"config.prod.toml is read first",
"npm run dev -- --env-file=.env.local",
"copy .env.local to .env.staging",
],
[
"ssh build.local",
"ping printer.local.",
"http://printer.local/md",
"printer.local.mdx is odd",
"curl http://settings.local:8080/health",
"the api.corp gateway",
"connect to build-7.internal",
],
);
});
describe("pii.ip_public: a four-part version needs a version declaration (#2784)", () => {
table(
"pii.ip_public",
"MEDIUM",
[
' "version": "1.128.1.0",',
"app_version = 1.128.1.0",
"schema-version: 1.128.1.0",
"APP_VERSION=1.128.1.0",
"Released version 1.128.1.0 today",
"## [1.128.1.0] - 2026-09-02",
"<Version>1.128.1.0</Version>",
'[assembly: AssemblyVersion("1.0.0.0")]',
"released v1.128.1.0 today",
],
[
"1.128.1.0",
"host: 1.128.1.0",
"server = 1.128.1.0",
'"upstream": "1.128.1.0"',
"resolver = 8.8.8.8",
"version: 1.2.3.4 via 8.8.8.8",
"the version endpoint moved to 1.128.1.0",
"conversion 1.128.1.0",
"## 1.128.1.0 is our egress",
"## [1.128.1.0 mirror]",
],
);
});
describe("pii.ip_public: a four-part version beside a VERSION file or a semver is not an address (GST-2047)", () => {
table(
"pii.ip_public",
"MEDIUM",
[
'fs.writeFileSync(path.join(dir, "VERSION"), "1.98.0.0\\n");',
"echo 1.98.0.0 > VERSION",
"cat pkg/VERSION -> 1.98.0.0",
"write('1.98.0.0', 'VERSION')",
"bumps 1.98.0.0 (1.98.0 on npm)",
"from v1.97.2 to 1.98.0.0",
"1.98.0.0 / 1.98.0-rc.1",
],
[
"resolver = 8.8.8.8",
'fs.writeFileSync(path.join(dir, "HOSTS"), "8.8.8.8\\n");',
"VERSION bumped; the DNS server 8.8.8.8 answered",
"8.8.8.8 is in VERSION notes",
"echo 8.8.8.8 > VERSIONS",
"cat MY_VERSION_HOST 8.8.8.8",
"node 22.11.0 talks to 8.8.8.8 over the egress proxy",
"1.1.1.1, 8.8.8.8",
],
);
test("a VERSION file holding only a quad is a version; the same quad in any other file, or in a VERSION file with other lines, still reports", () => {
const ids = (text: string, sourcePath: string) => scan(text, { repoVisibility: "private", sourcePath }).findings.map((f) => f.id);
expect(ids("1.98.0.0\n", "VERSION")).not.toContain("pii.ip_public");
expect(ids("1.98.0.0\n", "/repo/pkg/VERSION")).not.toContain("pii.ip_public");
expect(ids("1.98.0.0\n", "HOSTS")).toContain("pii.ip_public");
expect(ids("1.98.0.0\n", "NOT_VERSION")).toContain("pii.ip_public");
expect(ids("host 8.8.8.8\n", "VERSION")).toContain("pii.ip_public");
expect(scan("1.98.0.0\nupstream 8.8.8.8\n", { repoVisibility: "private", sourcePath: "VERSION" }).findings.filter((f) => f.id === "pii.ip_public").map((f) => f.line)).toEqual([1, 2]);
});
});
describe("pii.ip_public: a four-part version gstack names is not an address (GST-1204)", () => {
table(
"pii.ip_public",
"MEDIUM",
[
"gstack 1.94.4.0",
"gstack: ok 1.94.4.0 for=ship",
"gstack: fail claude,codex (1.93.37.0)",
"upgraded gstack from 1.93.14.0 to 1.94.4.0",
"seen on gstack 1.94.4.0 to 1.94.8.0",
"(gstack 1.94.4.0-1.94.8.0)",
],
[
"gstack talks to 8.8.8.8",
"gstack-proxy 1.128.1.0",
"landed in 1.94.4.0",
"gstack 1.94.4.0 via 8.8.8.8",
"from 1.128.1.0 to 1.128.1.1",
"gstack 8.8.8.8",
"gstack: fail 8.8.8.8",
"upgraded gstack from 8.8.8.8",
"my-gstack 1.94.4.0",
"/opt/gstack 1.94.4.0",
"gstack 1.94.4.0 - 8.8.8.8",
"VERSION 1.2.3.4 to 8.8.8.8",
],
);
});
describe("db.url_with_password: postgres:postgres only on a loopback host (#2913)", () => {
// Assembled from parts so this file's own source never carries a
// credential-bearing URL for the repo's secret scan to block.
const url = (userinfo: string, rest: string, scheme = "postgresql") => `${scheme}://${userinfo}@${rest}`;
const pg = ["postgres", "postgres"].join(":");
table(
"db.url_with_password",
"HIGH",
[
`DATABASE_URL=${url(pg, "localhost:5432/app_test")}`,
`DATABASE_URL=${url(pg, "127.0.0.1:5432/app", "postgres")}`,
`DIRECT_URL="${url(pg, "localhost/app")}"`,
`url: ${url(pg, "localhost", "postgres")}`,
url(pg, "localhost:5432?sslmode=disable"),
],
[
`DATABASE_URL=${url(pg, "db:5432/app")}`,
`DATABASE_URL=${url(pg, "postgres:5432/app")}`,
`DATABASE_URL=${url(pg, "prod-host:5432/app")}`,
`DATABASE_URL=${url(pg, "localhost.evil.example.net/app")}`,
`DATABASE_URL=${url(pg, "127.0.0.1.nip.io/app")}`,
`DATABASE_URL=${url(pg, "localhost@prod-db/app")}`,
`DATABASE_URL=${url(["user", "s3cretPassw0rd"].join(":"), "prod-host:5432/app")}`,
`DATABASE_URL=${url(["postgres", "hunter2x"].join(":"), "localhost:5432/app")}`,
`DATABASE_URL=${url(["admin", "postgres"].join(":"), "localhost:5432/app")}`,
`DATABASE_URL=${url(["postgres", "Postgres"].join(":"), "localhost:5432/app")}`,
`DATABASE_URL=${url(pg, "localhost:3306/app", "mysql")}`,
],
);
});
// Free Tests flaked test/capy-captain-flow.test.ts in 7 of 57 runs (2026-10-21..23): the autoplan run id
// run-YYYYMMDDHHMMSS put a 14-digit stamp in the approval row, and one stamp in ten passes Luhn.
describe("pii.cc: a separator-free YYYYMMDDHHMMSS stamp is not a card number", () => {
table(
"pii.cc",
"MEDIUM",
[
'Approve the plan "Retry backoff" (autoplan run run-20261024131504, 2 decisions)',
"autoplan:run-20261024131504@gate_rev=1",
"backup stamp 20261024131512 ran late",
],
[
"card 30569309025904",
"ref 20261324131501",
"stamp 202610241315045",
"card 2026 1024 1315 04",
"card 4111111111111111",
],
);
});
describe("pii.phone.e164: GitHub Actions run and job ids in their id positions", () => {
table(
"pii.phone.e164",
"MEDIUM",
[
"bun run eval:pass-rates --run 37235771700 --case context-recovery-artifacts",
"bun run eval:pass-rates --run=37235771700",
"gh run view 37235771700 --job 111534615007 --log",
"gh run download 37235771700 -n paid-slice-10-a1",
"https://github.com/garrytan/gstack/actions/runs/37235771700/job/111534615007",
"GITHUB_RUN_ID=37235771700",
'{"run_id": 37235771700}',
// An 11-digit id after a run word, as a READY block, a ship receipt or an approval record names one (GSTA-121's `screen <id> GREEN`).
"citing screen 40477638211 GREEN at 5619199800a2",
"gate=40477638211",
"rerun passed 40477638211",
"run #40477638211",
"Screens: 40477638211",
"dispatched 40477638211 at the pin",
"cleared by run 19368712345",
],
[
"call 37235771700 tomorrow",
"--phone 37235771700",
"https://example.com/contact/37235771700",
'{"phone_id": 37235771700}',
"run 4155550123 laps",
// A run word before a 10-digit national number, a bare 11-digit number and a generic word exempt nothing.
"gate 4155550123",
"the id is 40477638211",
"job 40477638211",
"workflow 40477638211",
"40477638211 failed",
],
);
});
describe("pii.phone.e164: run ids in a runs= list or an actions/runs/ path (GST-1227)", () => {
table(
"pii.phone.e164",
"MEDIUM",
[
// The run list a CI carry or run clear prints (lib/run-evidence.ts runReceiptFields): every id, not only the first.
"CI_CARRY: cleared pregate.json/remote_gate run tests=412 runs=41629861819/1,41629861820/2 profiles=free-tests heads=2f9d11a0c3e4",
'{"remote_gate": "carried runs=41629861819/1,41629861820/1,41629861821/3 profiles=free-tests,windows-smoke"}',
"run=41629861819/1,416298618201/1",
"see actions/runs/41629861820 for the log",
"garrytan/gstack actions/runs/4162986182/attempts/2",
],
[
"call +1 415 555 0123 tomorrow",
"call +1 415 555 0123 about runs=41629861819/1,41629861820/2",
"runs=+14155550123/1",
"runs=1/4155550123",
"runs=4155550123",
"reruns=4155550123/1",
"factions/runs/4155550123",
"sales-actions/runs/4155550123",
"runs=4162986182/1",
"runs=1234567890123/1",
],
);
});
describe("pii.phone.e164: a short sha after review_loop_grant or review_loop_exception (Free Tests 41667825418, 41766865679)", () => {
table(
"pii.phone.e164",
"MEDIUM",
[
// The ask gstack-approvals records for the owner's grant, here with a stop head whose 12-character sha is all digits.
"Owner grant: one scoped fix-check after round 2 (review_loop_grant 367120488513)",
"Owner accepts the stop after the fix-check (review_loop_exception 367120488513)",
"review_loop_grant=367120488513",
"review_loop_grant: 3671204885",
],
[
"review_loop_grant +14155552671",
"review_loop_grant 415-555-2671",
"review_loop_grant (415) 555-2671",
"call 367120488513 about the review_loop_grant",
"grant 367120488513",
"my_review_loop_grant 367120488513",
"review_loop_grant for 367120488513",
],
);
});
describe("pii.wallet: a bc1 address needs the bech32 alphabet and checksum (Free Tests 41715139988)", () => {
table(
"pii.wallet",
"MEDIUM",
[
// The sha the CSO history range printed as <REDACTED-pii.wallet>..<REDACTED-pii.wallet>; it holds 1 and b, outside bech32's alphabet.
"bc1f7576f1eaf9d548ddb6f742b85ec11b4a3e79..bc1f7576f1eaf9d548ddb6f742b85ec11b4a3e79",
// A sha inside the alphabet (no 1 or b after the prefix) fails the checksum.
"commit bc1f7576f2eaf9d548ddf6f742c85ec22d4a3e79",
// A real address with its last character changed fails the checksum too.
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5",
],
[
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
"send it to bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq today",
"wallet: bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"0x52908400098527886E0F7030069857D2E4169EE7",
"1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2",
],
);
});