mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-02 23:14:33 +01:00
* fix(windows): shell bins pass NUL as Bun's empty config when MSYS path conversion is off (#3415) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(windows): detect Git Bash/MSYS2/Cygwin from OSTYPE, as gstack-doctor does, so uname stubs in setup fixtures stay untouched Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (#3415) - Choose NUL only when cygpath is also on PATH, so an OSTYPE=msys inherited by a POSIX bash keeps /dev/null instead of a cwd-relative NUL file. - gstack-developer-profile --log-session reports a Bun failure instead of skipping it as invalid input, like gstack-timeline-log. - learnings-injection tests check that their anchored bun -e block exists. - CHANGELOG names the callers this fix does not cover; curation reason fixed. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes, cycle 2 (#3415) - timeline-log and developer-profile validators print their verdict; any non-zero Bun exit is a reported failure (a Bun crash on Windows exits 3). - timeline-log keeps the original event when ts stamping fails. - The definition ratchet scopes a local GSTACK_BUN_CONFIG to its function. - The shell-bins fixture clears its preload marker per test. - CHANGELOG states the callers that discard the report and the Bun 1.3.x case. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CHANGELOG notes Bun 1.3.10 evidence and remaining literal /dev/null callers (#3415) 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>
68 lines
3.1 KiB
TypeScript
68 lines
3.1 KiB
TypeScript
import { describe, test, expect } from "bun:test";
|
|
import { readFileSync } from "fs";
|
|
import path from "path";
|
|
|
|
const SCRIPT = path.join(import.meta.dir, "..", "bin", "gstack-learnings-search");
|
|
|
|
describe("gstack-learnings-search injection prevention", () => {
|
|
const script = readFileSync(SCRIPT, "utf-8");
|
|
const bunBlockAt = script.indexOf('bun --no-env-file --config="${GSTACK_BUN_CONFIG:-/dev/null}" -e "');
|
|
|
|
test("the bun -e block the checks below read is present", () => {
|
|
expect(bunBlockAt).toBeGreaterThanOrEqual(0);
|
|
});
|
|
|
|
test("no shell interpolation inside bun -e string", () => {
|
|
// Extract the bun -e block (everything between `bun -e "` and the closing `"`)
|
|
const bunBlock = script.slice(bunBlockAt);
|
|
|
|
// Should NOT contain ${VAR} patterns (shell interpolation)
|
|
// These are RCE vectors: a malicious learnings entry with '; rm -rf / ;' in the
|
|
// query field would execute arbitrary commands via shell interpolation.
|
|
const shellInterpolations = bunBlock.match(/'\$\{[A-Z_]+\}'/g) || [];
|
|
const bareInterpolations = bunBlock.match(/\$\{[A-Z_]+\}/g) || [];
|
|
|
|
// Filter out any that are inside process.env references (those are safe)
|
|
const unsafeInterpolations = [
|
|
...shellInterpolations,
|
|
...bareInterpolations,
|
|
].filter((m) => !m.includes("process.env"));
|
|
|
|
expect(unsafeInterpolations).toEqual([]);
|
|
});
|
|
|
|
test("uses process.env for all user-controlled values", () => {
|
|
const bunBlock = script.slice(bunBlockAt);
|
|
|
|
// Must use process.env for TYPE, QUERY, LIMIT.
|
|
// SLUG and CROSS are no longer threaded as env vars inside the bun
|
|
// block since PR #1619 — current vs cross-project rows are now
|
|
// distinguished by inline tags in the piped input (`current\t<line>`
|
|
// vs `cross\t<line>`), removing the need for env-var filters inside
|
|
// the bun block. CROSS is still set on the bash command line (it
|
|
// controls whether the cross-project find runs at all), but the bun
|
|
// block reads the tag, not the env var.
|
|
expect(bunBlock).toContain("process.env.GSTACK_SEARCH_TYPE");
|
|
expect(bunBlock).toContain("process.env.GSTACK_SEARCH_QUERY");
|
|
expect(bunBlock).toContain("process.env.GSTACK_SEARCH_LIMIT");
|
|
});
|
|
|
|
test("env vars are set on the bun command line", () => {
|
|
// The env vars must be passed to bun, not just set in the shell.
|
|
// SLUG removed by PR #1619 — see above.
|
|
expect(script).toContain("GSTACK_SEARCH_TYPE=");
|
|
expect(script).toContain("GSTACK_SEARCH_QUERY=");
|
|
expect(script).toContain("GSTACK_SEARCH_LIMIT=");
|
|
expect(script).toContain("GSTACK_SEARCH_CROSS=");
|
|
});
|
|
|
|
test("current vs cross-project rows distinguished by inline tags, not SLUG env (#1619)", () => {
|
|
const bunBlock = script.slice(bunBlockAt);
|
|
// The bun block must inspect the per-line tag to mark cross-project rows.
|
|
// The current shape emits `current\t<json>` or `cross\t<json>` from the
|
|
// upstream pipe (via emit_tagged_file). Inside the bun block, the script
|
|
// parses out the leading tag and sets a per-entry flag.
|
|
expect(bunBlock).toMatch(/sourceTag|tabIndex|crossProject/);
|
|
});
|
|
});
|