Skip to content
Closed
17 changes: 12 additions & 5 deletions bin/gstack-global-discover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,15 +274,22 @@ function resolveClaudeCodeCwd(
}

function extractCwdFromJsonl(filePath: string): string | null {
// Read a capped prefix so huge JSONL files don't blow up memory. 64KB
// comfortably fits the largest observed session headers; the old 8KB cap
// would sometimes fall inside a single long line and silently drop the
// project (JSON.parse failure on the truncated tail).
const MAX_BYTES = 64 * 1024;
const MAX_LINES = 30;
try {
// Read only the first 8KB to avoid loading huge JSONL files into memory
const fd = openSync(filePath, "r");
const buf = Buffer.alloc(8192);
const bytesRead = readSync(fd, buf, 0, 8192, 0);
const buf = Buffer.alloc(MAX_BYTES);
const bytesRead = readSync(fd, buf, 0, MAX_BYTES, 0);
closeSync(fd);
const text = buf.toString("utf-8", 0, bytesRead);
const lines = text.split("\n").slice(0, 15);
for (const line of lines) {
// Drop the final segment — it may be an incomplete line at the cap boundary.
const parts = text.split("\n");
const completeLines = parts.length > 1 ? parts.slice(0, -1) : parts;
for (const line of completeLines.slice(0, MAX_LINES)) {
if (!line.trim()) continue;
try {
const obj = JSON.parse(line);
Expand Down
8 changes: 7 additions & 1 deletion bin/gstack-telemetry-sync
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,13 @@ BATCH="$BATCH]"
[ "$COUNT" -eq 0 ] && exit 0

# ─── POST to edge function ───────────────────────────────────
RESP_FILE="$(mktemp /tmp/gstack-sync-XXXXXX 2>/dev/null || echo "/tmp/gstack-sync-$$")"
# Create response file atomically. If mktemp fails, refuse to continue rather
# than fall back to a predictable $$-based path (race + overwrite footgun).
RESP_FILE="$(mktemp "${TMPDIR:-/tmp}/gstack-sync-XXXXXX")" || {
echo "gstack-telemetry-sync: mktemp failed — skipping this run" >&2
exit 0
}
trap 'rm -f "$RESP_FILE"' EXIT
HTTP_CODE="$(curl -s -w '%{http_code}' --max-time 10 \
-X POST "${SUPABASE_URL}/functions/v1/telemetry-ingest" \
-H "Content-Type: application/json" \
Expand Down
4 changes: 1 addition & 3 deletions browse/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -957,9 +957,7 @@ Refs: After 'snapshot', use @e1, @e2... as selectors:
'Content-Type': 'application/json',
'Authorization': `Bearer ${existingState.token}`,
},
body: JSON.stringify({
domains,
command: 'disconnect', args: [] }),
body: JSON.stringify({ command: 'disconnect', args: [] }),
signal: AbortSignal.timeout(3000),
});
if (resp.ok) {
Expand Down
11 changes: 10 additions & 1 deletion browse/src/meta-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,16 @@ function parsePdfFromFile(payloadPath: string): ParsedPdfArgs {
);
}
const raw = fs.readFileSync(payloadPath, 'utf8');
const json = JSON.parse(raw);
let json: any;
try {
json = JSON.parse(raw);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
throw new Error(`pdf: --from-file ${payloadPath} is not valid JSON (${msg}).`);
}
if (json === null || typeof json !== 'object' || Array.isArray(json)) {
throw new Error(`pdf: --from-file ${payloadPath} must be a JSON object, got ${Array.isArray(json) ? 'array' : typeof json}.`);
}
const out: ParsedPdfArgs = {
output: json.output || `${TEMP_DIR}/browse-page.pdf`,
format: json.format,
Expand Down
26 changes: 17 additions & 9 deletions browse/src/security-classifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,16 +142,24 @@ async function downloadFile(url: string, dest: string): Promise<void> {
const writer = fs.createWriteStream(tmp);
// @ts-ignore — Node stream compat
const reader = res.body.getReader();
let done = false;
while (!done) {
const chunk = await reader.read();
if (chunk.done) { done = true; break; }
writer.write(chunk.value);
try {
let done = false;
while (!done) {
const chunk = await reader.read();
if (chunk.done) { done = true; break; }
writer.write(chunk.value);
}
await new Promise<void>((resolve, reject) => {
writer.end((err?: Error | null) => (err ? reject(err) : resolve()));
});
fs.renameSync(tmp, dest);
} catch (err) {
// Close the writer and drop the half-written tmp so we don't leak an FD
// or ship a truncated model file to the renameSync path on retry.
try { writer.destroy(); } catch { /* already destroyed */ }
try { fs.unlinkSync(tmp); } catch { /* nothing to clean */ }
throw err;
}
await new Promise<void>((resolve, reject) => {
writer.end((err?: Error | null) => (err ? reject(err) : resolve()));
});
fs.renameSync(tmp, dest);
}

async function ensureTestsavantStaged(onProgress?: (msg: string) => void): Promise<void> {
Expand Down
10 changes: 8 additions & 2 deletions scripts/build-app.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ if [ -f "$CHROMIUM_PLIST" ]; then
if [ -f "$CHROMIUM_STRINGS" ]; then
# InfoPlist.strings may be binary plist, convert to xml first
plutil -convert xml1 "$CHROMIUM_STRINGS" 2>/dev/null || true
sed -i '' "s/Google Chrome for Testing/$APP_NAME/g" "$CHROMIUM_STRINGS" 2>/dev/null || true
# Escape sed replacement metachars (& / \) in $APP_NAME so unusual names can't break or inject into the s/// command.
APP_NAME_SED_ESCAPED=$(printf '%s' "$APP_NAME" | sed 's/[&/\]/\\&/g')
sed -i '' "s/Google Chrome for Testing/${APP_NAME_SED_ESCAPED}/g" "$CHROMIUM_STRINGS" 2>/dev/null || true
fi
# Replace Chromium's icon with ours so the Dock shows the GStack icon
# (Chromium's process owns the Dock icon, not our launcher)
Expand Down Expand Up @@ -177,7 +179,11 @@ echo " Creating DMG..."
rm -f "$DMG_PATH"

# Create a temporary directory for DMG contents
DMG_TMP=$(mktemp -d)
DMG_TMP=$(mktemp -d) || { echo "ERROR: mktemp -d failed — refusing to continue so we don't cp into the filesystem root." >&2; exit 1; }
if [ -z "$DMG_TMP" ] || [ ! -d "$DMG_TMP" ]; then
echo "ERROR: mktemp -d returned an invalid path ('$DMG_TMP')." >&2
exit 1
fi
cp -a "$APP_DIR" "$DMG_TMP/"
ln -s /Applications "$DMG_TMP/Applications"

Expand Down
7 changes: 6 additions & 1 deletion supabase/verify-rls.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ check() {
TOTAL=$(( TOTAL + 1 ))

local resp_file
resp_file="$(mktemp 2>/dev/null || echo "/tmp/verify-rls-$$-$TOTAL")"
# Use mktemp strictly. Don't fall back to a predictable $$-based path —
# that's a race/overwrite footgun on shared machines.
resp_file="$(mktemp "${TMPDIR:-/tmp}/verify-rls-XXXXXX")" || {
echo "verify-rls: mktemp failed, aborting" >&2
return 1
}

local http_code
if [ "$method" = "GET" ]; then
Expand Down