mirror of
https://github.com/garrytan/gstack.git
synced 2026-07-05 07:37:55 +02:00
81645b29e5
Two new test files pinning the four shell-script invariants from the external audit: regression-pr1169-build-app-sed.test.ts — bugs #2 + #3 - Runtime isolation: extracts the sed-escape sequence from build-app.sh and runs it against hostile $APP_NAME values ("Foo/Bar&Baz", "Cool\App", "A/B\C&D"). Asserts the literal hostile name round-trips through a real `sed s///` invocation, locking the metachar safety end-to-end. - Static check: the rebrand block must contain both the escape line AND the sed line referencing $APP_NAME_SED_ESCAPED; bare $APP_NAME interpolation directly into the s/// replacement is rejected. - Static check: DMG_TMP=$(mktemp -d) is followed by an explicit `|| { ... exit }` failure handler AND a `[ -z "$DMG_TMP" ] || [ ! -d "$DMG_TMP" ]` validation AND the cp -a appears AFTER both guards. - Runtime fake-bin: extracts the guard shape, runs with a fake mktemp that exits 1, asserts the script exits non-zero before any cp block can reach. regression-pr1169-mktemp-fallbacks.test.ts — bugs #4 + #5 - Per codex pushback, the invariant is "no `mktemp ... || echo <path>` fallback shape" — not just "no $$ token." That's a stronger invariant that catches future swaps to $RANDOM or hardcoded paths. - For each of bin/gstack-telemetry-sync and supabase/verify-rls.sh: - no echo-based fallback after mktemp - no $$ inside any /tmp path literal - mktemp failure path explicitly exits / returns non-zero - telemetry-sync also pins the `trap rm -f $RESP_FILE EXIT` cleanup so success paths don't leak the tmp on normal exit. All seven new test files are gate-tier (deterministic, sub-second, no LLM, no network). Runtime shell tests use fake-bin PATH stubs in temp dirs; no $HOME mutation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
83 lines
3.2 KiB
TypeScript
83 lines
3.2 KiB
TypeScript
/**
|
|
* Regression tests for PR #1169 bugs #4 + #5 — predictable `$$`-based tmp
|
|
* file fallbacks on mktemp failure.
|
|
*
|
|
* Per codex's pushback, the real invariant is not just "no `$$` token" — it's
|
|
* "no `mktemp ... || echo <fallback-path>` shape at all, AND mktemp failure
|
|
* exits cleanly." A future cleanup could swap `$$` for `$RANDOM` or a
|
|
* hardcoded path and silently keep the foot-gun. The static checks below
|
|
* lock the broader invariant.
|
|
*
|
|
* Runtime fake-bin tests for these two scripts would require setting up
|
|
* SUPABASE_URL, JSONL fixtures, rate files, and config state — disproportionate
|
|
* for the invariant. The static checks pin the actual shape of the bug.
|
|
*/
|
|
import { describe, expect, test } from "bun:test";
|
|
import * as fs from "node:fs";
|
|
import * as path from "node:path";
|
|
|
|
const ROOT = path.resolve(import.meta.dir, "..");
|
|
|
|
function readScript(rel: string): string {
|
|
return fs.readFileSync(path.join(ROOT, rel), "utf-8");
|
|
}
|
|
|
|
describe("PR #1169 bug #4: gstack-telemetry-sync mktemp fallback", () => {
|
|
const SCRIPT = "bin/gstack-telemetry-sync";
|
|
|
|
test("no `mktemp ... || echo <path>` fallback shape anywhere in the script", () => {
|
|
const body = readScript(SCRIPT);
|
|
// Match: mktemp call, optional pipe, then `|| echo <quoted-or-bare-path>`
|
|
// The fallback shape regardless of what the fallback path looks like
|
|
// ($$, $RANDOM, hardcoded — all predictable).
|
|
const fallback = body.match(/mktemp[^|\n]*\|\|\s*echo\s+["']?[^"'\n]*/);
|
|
expect(fallback).toBeNull();
|
|
});
|
|
|
|
test("no `$$` PID interpolation appears anywhere in a /tmp path literal", () => {
|
|
const body = readScript(SCRIPT);
|
|
// Catches any /tmp-style path that uses the PID as part of the name.
|
|
expect(body).not.toMatch(/\/tmp\/[^"'\s]*\$\$/);
|
|
});
|
|
|
|
test("mktemp failure path exits or skips this run", () => {
|
|
const body = readScript(SCRIPT);
|
|
// The mktemp invocation must be guarded by `|| { ... exit 0; }` or
|
|
// equivalent. Match the multi-line guard immediately after `mktemp`.
|
|
const guard = body.match(
|
|
/mktemp\s+[^\n]+\)["']\s*\|\|\s*\{[^}]*exit\s+\d/
|
|
);
|
|
expect(guard).not.toBeNull();
|
|
});
|
|
|
|
test("trap cleans up the response file on EXIT (no leftover tmp on success)", () => {
|
|
const body = readScript(SCRIPT);
|
|
expect(body).toMatch(/trap\s+['"]rm\s+-f\s+"?\$RESP_FILE/);
|
|
});
|
|
});
|
|
|
|
describe("PR #1169 bug #5: supabase/verify-rls.sh mktemp fallback", () => {
|
|
const SCRIPT = "supabase/verify-rls.sh";
|
|
|
|
test("no `mktemp ... || echo <path>` fallback shape", () => {
|
|
const body = readScript(SCRIPT);
|
|
const fallback = body.match(/mktemp[^|\n]*\|\|\s*echo\s+["']?[^"'\n]*/);
|
|
expect(fallback).toBeNull();
|
|
});
|
|
|
|
test("no `$$` PID interpolation in /tmp path literals", () => {
|
|
const body = readScript(SCRIPT);
|
|
expect(body).not.toMatch(/\/tmp\/[^"'\s]*\$\$/);
|
|
});
|
|
|
|
test("mktemp failure path returns non-zero from check()", () => {
|
|
const body = readScript(SCRIPT);
|
|
// The check function must fail loudly — `return 1` (or `exit`) inside
|
|
// the mktemp error handler. Same multi-line guard shape.
|
|
const guard = body.match(
|
|
/mktemp\s+[^\n]+\)["']\s*\|\|\s*\{[^}]*(?:return|exit)\s+\d/
|
|
);
|
|
expect(guard).not.toBeNull();
|
|
});
|
|
});
|