mirror of https://github.com/garrytan/gstack.git
security: remove .svg from load-html extension allowlist
SVG files can contain inline <script> tags, event handlers (onload, onclick), and <foreignObject> elements. When loaded via setContent(), JavaScript executes in an about:blank context with full fetch() access to localhost — including the gstack server itself. Attack vector: a malicious SVG placed in a project directory (via git clone, npm package, or download) gets loaded by the AI agent through load-html. The SVG's JavaScript modifies the DOM to inject prompt injection payloads that the agent reads on subsequent text/snapshot commands. SVG rendering is still available via `goto file://path/to/file.svg` which goes through validateNavigationUrl + validateReadPath with safe-dirs enforcement.
This commit is contained in:
parent
675717e320
commit
3095f39ef5
|
|
@ -247,11 +247,11 @@ export async function handleWriteCommand(
|
|||
if (!filePath) throw new Error('Usage: browse load-html <file> [--wait-until load|domcontentloaded|networkidle] [--tab-id <N>] | load-html --from-file <payload.json> [--tab-id <N>]');
|
||||
|
||||
// Extension allowlist
|
||||
const ALLOWED_EXT = ['.html', '.htm', '.xhtml', '.svg'];
|
||||
const ALLOWED_EXT = ['.html', '.htm', '.xhtml'];
|
||||
const ext = path.extname(filePath).toLowerCase();
|
||||
if (!ALLOWED_EXT.includes(ext)) {
|
||||
throw new Error(
|
||||
`load-html: file does not appear to be HTML. Expected .html/.htm/.xhtml/.svg, got ${ext || '(no extension)'}. Rename the file if it's really HTML.`
|
||||
`load-html: file does not appear to be HTML. Expected .html/.htm/.xhtml, got ${ext || '(no extension)'}. Rename the file if it's really HTML.`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2143,6 +2143,19 @@ describe('load-html', () => {
|
|||
}
|
||||
});
|
||||
|
||||
test('load-html rejects .svg files', async () => {
|
||||
const svgPath = path.join(tmpDir, `load-html-test-${Date.now()}.svg`);
|
||||
fs.writeFileSync(svgPath, '<svg xmlns="http://www.w3.org/2000/svg"><text>hi</text></svg>');
|
||||
try {
|
||||
await handleWriteCommand('load-html', [svgPath], bm);
|
||||
expect(true).toBe(false);
|
||||
} catch (err: any) {
|
||||
expect(err.message).toMatch(/does not appear to be HTML/);
|
||||
} finally {
|
||||
try { fs.unlinkSync(svgPath); } catch {}
|
||||
}
|
||||
});
|
||||
|
||||
test('load-html rejects file outside safe dirs', async () => {
|
||||
try {
|
||||
await handleWriteCommand('load-html', ['/etc/passwd.html'], bm);
|
||||
|
|
|
|||
Loading…
Reference in New Issue