mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-02 03:35:09 +02:00
fix: adversarial review fixes for ux-audit and heatmap
Security: - Remove live form value extraction from ux-audit (leaked input field values) - Add ux-audit to PAGE_CONTENT_COMMANDS (untrusted content wrapping) Correctness: - Scope youAreHere selector to nav containers (was matching animation classes) - Validate heatmap JSON is a plain object (string/array/null produced garbage) - Use textContent instead of innerText for word count (avoids layout computation) - Remove dead url variable and unused LINK_CAP constant Found by Codex + Claude adversarial review. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,7 @@ export const PAGE_CONTENT_COMMANDS = new Set([
|
||||
'text', 'html', 'links', 'forms', 'accessibility', 'attrs',
|
||||
'console', 'dialog',
|
||||
'media', 'data',
|
||||
'ux-audit',
|
||||
]);
|
||||
|
||||
/** Wrap output from untrusted-content commands with trust boundary markers */
|
||||
|
||||
@@ -656,13 +656,12 @@ export async function handleMetaCommand(
|
||||
// ─── UX Audit ─────────────────────────────────────
|
||||
case 'ux-audit': {
|
||||
const page = bm.getPage();
|
||||
const url = page.url();
|
||||
|
||||
// Extract page structure for UX behavioral analysis
|
||||
// Agent interprets the data and applies Krug's 6 usability tests
|
||||
// Uses textContent (not innerText) to avoid layout computation on large DOMs
|
||||
const data = await page.evaluate(() => {
|
||||
const HEADING_CAP = 50;
|
||||
const LINK_CAP = 100;
|
||||
const INTERACTIVE_CAP = 200;
|
||||
const TEXT_BLOCK_CAP = 50;
|
||||
|
||||
@@ -695,7 +694,8 @@ export async function handleMetaCommand(
|
||||
});
|
||||
|
||||
// "You are here" indicator: current/active nav items
|
||||
const activeNavItems = document.querySelectorAll('[aria-current], .active, .current, [class*="active"], [class*="current"]');
|
||||
// Scoped to nav containers to avoid false positives from animation classes
|
||||
const activeNavItems = document.querySelectorAll('nav [aria-current], nav .active, nav .current, [role="navigation"] [aria-current], [role="navigation"] .active, [role="navigation"] .current');
|
||||
const youAreHere = Array.from(activeNavItems).slice(0, 5).map(el => ({
|
||||
text: (el.textContent || '').trim().slice(0, 50),
|
||||
tag: el.tagName,
|
||||
@@ -725,7 +725,7 @@ export async function handleMetaCommand(
|
||||
const rect = el.getBoundingClientRect();
|
||||
return {
|
||||
tag: el.tagName,
|
||||
text: (el.textContent || (el as HTMLInputElement).placeholder || (el as HTMLInputElement).value || '').trim().slice(0, 50),
|
||||
text: (el.textContent || (el as HTMLInputElement).placeholder || '').trim().slice(0, 50),
|
||||
type: (el as HTMLInputElement).type || null,
|
||||
role: el.getAttribute('role'),
|
||||
w: Math.round(rect.width),
|
||||
@@ -740,8 +740,8 @@ export async function handleMetaCommand(
|
||||
wordCount: (el.textContent || '').trim().split(/\s+/).filter(Boolean).length,
|
||||
}));
|
||||
|
||||
// Total visible text word count
|
||||
const bodyText = (document.body?.innerText || '').trim();
|
||||
// Total visible text word count (textContent avoids layout computation)
|
||||
const bodyText = (document.body?.textContent || '').trim();
|
||||
const totalWords = bodyText.split(/\s+/).filter(Boolean).length;
|
||||
|
||||
return {
|
||||
|
||||
@@ -482,9 +482,13 @@ export async function handleSnapshot(
|
||||
|
||||
let colorAssignments: Record<string, string>;
|
||||
try {
|
||||
colorAssignments = JSON.parse(opts.heatmap);
|
||||
const parsed = JSON.parse(opts.heatmap);
|
||||
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
||||
throw new Error('not an object');
|
||||
}
|
||||
colorAssignments = parsed;
|
||||
} catch {
|
||||
throw new Error('Invalid heatmap JSON. Expected: \'{"@e1":"green","@e3":"red"}\'');
|
||||
throw new Error('Invalid heatmap JSON. Expected object: \'{"@e1":"green","@e3":"red"}\'');
|
||||
}
|
||||
|
||||
// Validate colors
|
||||
|
||||
Reference in New Issue
Block a user