mirror of
https://github.com/garrytan/gstack.git
synced 2026-06-22 01:30:03 +02:00
Merge remote-tracking branch 'origin/main' into garrytan/dont-change-gstack-source
This commit is contained in:
+17
-1
@@ -83,7 +83,23 @@ function loadMeta(scope: 'cross-project' | 'per-project', projectSlug: string |
|
||||
return { schema_version: GSTACK_SCHEMA_PACK_VERSION, endpoint_hash: detectEndpointHash(), last_refresh: {}, last_attempt: {} };
|
||||
}
|
||||
try {
|
||||
return JSON.parse(readFileSync(path, 'utf-8')) as CacheMeta;
|
||||
const parsed = JSON.parse(readFileSync(path, 'utf-8')) as unknown;
|
||||
// #1879: a valid JSON file can still be the wrong shape. JSON.parse can return
|
||||
// null/array/string/number, and a partial object can omit last_refresh — three
|
||||
// consumers (isStale, cmdInvalidate, refreshEntity) dereference meta.last_refresh
|
||||
// unguarded and crash with a TypeError.
|
||||
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
||||
return { schema_version: GSTACK_SCHEMA_PACK_VERSION, endpoint_hash: detectEndpointHash(), last_refresh: {}, last_attempt: {} };
|
||||
}
|
||||
const meta = parsed as CacheMeta;
|
||||
// Normalize ONLY the dereferenced maps. Do NOT default schema_version /
|
||||
// endpoint_hash — leaving them absent makes schemaVersionMismatch() /
|
||||
// endpointSwitched() correctly force a rebuild (missing identity = mismatch =
|
||||
// safe). Defaulting them to current values would suppress invalidation and
|
||||
// trust a stale file of unknown provenance.
|
||||
meta.last_refresh = meta.last_refresh ?? {};
|
||||
meta.last_attempt = meta.last_attempt ?? {};
|
||||
return meta;
|
||||
} catch {
|
||||
// Corrupt _meta — start fresh (entries will refresh on next access).
|
||||
return { schema_version: GSTACK_SCHEMA_PACK_VERSION, endpoint_hash: detectEndpointHash(), last_refresh: {}, last_attempt: {} };
|
||||
|
||||
@@ -75,7 +75,10 @@ while IFS= read -r f; do
|
||||
|
||||
# Backend: everything else that's code (excluding views/components already matched)
|
||||
*.rb|*.py|*.go|*.rs|*.java|*.php|*.ex|*.exs) BACKEND=true ;;
|
||||
*.ts|*.js) BACKEND=true ;; # Non-component TS/JS is backend
|
||||
# Non-component TS/JS is backend. Include ESM/CJS (.mjs/.cjs) and
|
||||
# explicit-module TS (.mts/.cts) — #1810: these matched no category, so an
|
||||
# ESM/CJS-only PR skipped the backend reviewer entirely.
|
||||
*.ts|*.js|*.mjs|*.cjs|*.mts|*.cts) BACKEND=true ;;
|
||||
esac
|
||||
done <<< "$FILES"
|
||||
|
||||
|
||||
@@ -90,10 +90,16 @@ for (const taggedLine of lines) {
|
||||
const isCrossProject = sourceTag === 'cross';
|
||||
e._crossProject = isCrossProject;
|
||||
|
||||
// Trust gate: cross-project learnings only loaded if trusted (user-stated)
|
||||
// Trust gate: cross-project learnings only loaded if trusted (user-stated).
|
||||
// This prevents prompt injection from one project's AI-generated learnings
|
||||
// silently influencing reviews in another project.
|
||||
if (isCrossProject && e.trusted === false) continue;
|
||||
// #1745: this is an ALLOWLIST, not a denylist. The old equals-false check
|
||||
// admitted any row where trusted is missing/undefined (legacy rows written
|
||||
// before the field existed, hand-edited rows, rows from other tools).
|
||||
// Require trusted to be exactly true. NOTE: this whole block is a
|
||||
// double-quoted bun -e string, so bash still does command substitution
|
||||
// inside it. Keep backticks and dollar-paren out of these comments.
|
||||
if (isCrossProject && e.trusted !== true) continue;
|
||||
|
||||
entries.push(e);
|
||||
} catch {}
|
||||
|
||||
+14
-1
@@ -161,12 +161,25 @@ function readLines(path: string | undefined): string[] | undefined {
|
||||
function buildOpts(): ScanOptions {
|
||||
const vis = (arg("--repo-visibility") as RepoVisibility) || "unknown";
|
||||
const maxBytes = arg("--max-bytes");
|
||||
// #1824: validate the RAW string, not the parse result. parseInt("123abc")
|
||||
// is 123 and parseInt("foo") is NaN — both silently corrupt the fail-closed
|
||||
// oversize guard. Require a clean positive integer or reject before scanning.
|
||||
let maxBytesOpt: number | undefined;
|
||||
if (maxBytes !== undefined) {
|
||||
if (!/^\d+$/.test(maxBytes) || Number(maxBytes) <= 0) {
|
||||
process.stderr.write(
|
||||
`gstack-redact: --max-bytes must be a positive integer (got "${maxBytes}")\n`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
maxBytesOpt = Number(maxBytes);
|
||||
}
|
||||
return {
|
||||
repoVisibility: ["public", "private", "unknown"].includes(vis) ? vis : "unknown",
|
||||
allowlist: readLines(arg("--allowlist")),
|
||||
selfEmail: arg("--self-email"),
|
||||
repoPublicEmails: readLines(arg("--repo-public-emails")),
|
||||
...(maxBytes ? { maxBytes: parseInt(maxBytes, 10) } : {}),
|
||||
...(maxBytesOpt !== undefined ? { maxBytes: maxBytesOpt } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user