mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-19 02:42:29 +08:00
fix: security audit round 2 (v0.13.4.0) (#640)
* fix: chrome-cdp localhost-only binding Restrict Chrome CDP to localhost by adding --remote-debugging-address=127.0.0.1 and --remote-allow-origins to prevent network-accessible debugging sessions. Clears 1 Socket anomaly (Chrome CDP session exposure). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: extension sender validation + message type allowlist Add sender.id check and ALLOWED_TYPES allowlist to the Chrome extension's message handler. Defense-in-depth against message spoofing from external extensions or future externally_connectable changes. Clears 2 Socket anomalies (extension permissions). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: checksum-verified bun install Replace unverified curl|bash bun installation with checksum-verified download-then-execute pattern. The install script is downloaded, sha256 verified against a known hash, then executed. Preserves the Bun-native install path without adding a Node/npm dependency. Clears Snyk W012 + 3 Socket anomalies. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: content trust boundary markers in browse output Wrap page-content commands (text, html, links, forms, accessibility, console, dialog, snapshot) with --- BEGIN/END UNTRUSTED EXTERNAL CONTENT --- markers. Covers direct commands (server.ts), chain sub-commands, and snapshot output (meta-commands.ts). Adds PAGE_CONTENT_COMMANDS set and wrapUntrustedContent() helper in commands.ts (single source of truth, DRY). Expands the SKILL.md trust warning with explicit processing rules for agents. Clears Snyk W011 (third-party content exposure). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: harden trust boundary markers against escape attacks - Sanitize URLs in markers (remove newlines, cap at 200 chars) to prevent marker injection via history.pushState - Escape marker strings in content (zero-width space) so malicious pages can't forge the END marker to break out of the untrusted block - Wrap resume command snapshot with trust boundary markers - Wrap diff command output with trust boundary markers - Wrap watch stop last snapshot with trust boundary markers Found by cross-model adversarial review (Claude + Codex). * chore: bump version and changelog (v0.13.4.0) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: gitignore .factory/ and remove from tracking Factory Droid support was removed in this branch. The .factory/ directory was re-added by merging main (which had v0.13.5.0 Factory support). Gitignore it so it stays out. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
import type { BrowserManager } from './browser-manager';
|
||||
import { handleSnapshot } from './snapshot';
|
||||
import { getCleanText } from './read-commands';
|
||||
import { READ_COMMANDS, WRITE_COMMANDS, META_COMMANDS } from './commands';
|
||||
import { READ_COMMANDS, WRITE_COMMANDS, META_COMMANDS, PAGE_CONTENT_COMMANDS, wrapUntrustedContent } from './commands';
|
||||
import { validateNavigationUrl } from './url-validation';
|
||||
import * as Diff from 'diff';
|
||||
import * as fs from 'fs';
|
||||
@@ -242,6 +242,9 @@ export async function handleMetaCommand(
|
||||
lastWasWrite = true;
|
||||
} else if (READ_COMMANDS.has(name)) {
|
||||
result = await handleReadCommand(name, cmdArgs, bm);
|
||||
if (PAGE_CONTENT_COMMANDS.has(name)) {
|
||||
result = wrapUntrustedContent(result, bm.getCurrentUrl());
|
||||
}
|
||||
lastWasWrite = false;
|
||||
} else if (META_COMMANDS.has(name)) {
|
||||
result = await handleMetaCommand(name, cmdArgs, bm, shutdown);
|
||||
@@ -288,12 +291,13 @@ export async function handleMetaCommand(
|
||||
}
|
||||
}
|
||||
|
||||
return output.join('\n');
|
||||
return wrapUntrustedContent(output.join('\n'), `diff: ${url1} vs ${url2}`);
|
||||
}
|
||||
|
||||
// ─── Snapshot ─────────────────────────────────────
|
||||
case 'snapshot': {
|
||||
return await handleSnapshot(args, bm);
|
||||
const snapshotResult = await handleSnapshot(args, bm);
|
||||
return wrapUntrustedContent(snapshotResult, bm.getCurrentUrl());
|
||||
}
|
||||
|
||||
// ─── Handoff ────────────────────────────────────
|
||||
@@ -306,7 +310,7 @@ export async function handleMetaCommand(
|
||||
bm.resume();
|
||||
// Re-snapshot to capture current page state after human interaction
|
||||
const snapshot = await handleSnapshot(['-i'], bm);
|
||||
return `RESUMED\n${snapshot}`;
|
||||
return `RESUMED\n${wrapUntrustedContent(snapshot, bm.getCurrentUrl())}`;
|
||||
}
|
||||
|
||||
// ─── Headed Mode ──────────────────────────────────────
|
||||
@@ -377,11 +381,14 @@ export async function handleMetaCommand(
|
||||
if (!bm.isWatching()) return 'Not currently watching.';
|
||||
const result = bm.stopWatch();
|
||||
const durationSec = Math.round(result.duration / 1000);
|
||||
const lastSnapshot = result.snapshots.length > 0
|
||||
? wrapUntrustedContent(result.snapshots[result.snapshots.length - 1], bm.getCurrentUrl())
|
||||
: '(none)';
|
||||
return [
|
||||
`WATCH STOPPED (${durationSec}s, ${result.snapshots.length} snapshots)`,
|
||||
'',
|
||||
'Last snapshot:',
|
||||
result.snapshots.length > 0 ? result.snapshots[result.snapshots.length - 1] : '(none)',
|
||||
lastSnapshot,
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user