diff --git a/docs/src/content/docs/rules.md b/docs/src/content/docs/rules.md index ba4e71d..a689253 100644 --- a/docs/src/content/docs/rules.md +++ b/docs/src/content/docs/rules.md @@ -251,10 +251,11 @@ HTML metadata is enumerated among the non-rendered carriers in Greshake et al. - **Default:** on Walk every element and, for a small allowlist of agent-readable attributes — -`aria-label`, `aria-description`, `alt`, `title`, `placeholder`, `data-tooltip`, -and `value` on `` elements the user cannot reach (`disabled` or -`type="hidden"`) — remove the attribute outright when its value matches the -prompt-injection pattern set (the same regex bundle used by +`aria-label`, `aria-description`, `aria-roledescription`, `aria-placeholder`, +`aria-valuetext`, `aria-keyshortcuts`, `alt`, `title`, `placeholder`, +`data-tooltip`, and `value` on `` elements the user cannot reach +(`disabled` or `type="hidden"`) — remove the attribute outright when its value +matches the prompt-injection pattern set (the same regex bundle used by `prompt-injection-redact`). Clean attributes are preserved. Attributes outside the allowlist are not inspected. We remove the whole attribute rather than blank it because an empty `aria-label` actively hides an element from diff --git a/extension/src/rules/__tests__/attribute-injection-sanitize.property.test.ts b/extension/src/rules/__tests__/attribute-injection-sanitize.property.test.ts index db6b690..2b6d9bd 100644 --- a/extension/src/rules/__tests__/attribute-injection-sanitize.property.test.ts +++ b/extension/src/rules/__tests__/attribute-injection-sanitize.property.test.ts @@ -17,6 +17,10 @@ import { FIXTURES } from "./injection-fixtures"; const ALLOWLISTED_ATTRS = [ "aria-label", "aria-description", + "aria-roledescription", + "aria-placeholder", + "aria-valuetext", + "aria-keyshortcuts", "alt", "title", "placeholder", @@ -26,6 +30,10 @@ const ALLOWLISTED_ATTRS = [ const TAG_FOR_ATTR: Record<(typeof ALLOWLISTED_ATTRS)[number], string> = { "aria-label": "button", "aria-description": "div", + "aria-roledescription": "div", + "aria-placeholder": "input", + "aria-valuetext": "div", + "aria-keyshortcuts": "button", alt: "img", title: "span", placeholder: "input", diff --git a/extension/src/rules/__tests__/attribute-injection-sanitize.test.ts b/extension/src/rules/__tests__/attribute-injection-sanitize.test.ts index 4b5d3de..aa512f4 100644 --- a/extension/src/rules/__tests__/attribute-injection-sanitize.test.ts +++ b/extension/src/rules/__tests__/attribute-injection-sanitize.test.ts @@ -21,6 +21,10 @@ describe("attribute-injection-sanitize candidate attributes", () => { it.each([ ["aria-label", "button"], ["aria-description", "div"], + ["aria-roledescription", "div"], + ["aria-placeholder", "input"], + ["aria-valuetext", "div"], + ["aria-keyshortcuts", "button"], ["alt", "img"], ["title", "span"], ["placeholder", "input"], @@ -39,6 +43,10 @@ describe("attribute-injection-sanitize candidate attributes", () => { ["alt", "img", "Product photo"], ["title", "span", "Posted 3 days ago"], ["placeholder", "input", "Search products"], + ["aria-roledescription", "div", "carousel"], + ["aria-valuetext", "div", "$129.99"], + ["aria-keyshortcuts", "button", "Control+S"], + ["aria-placeholder", "input", "Search products"], ])("preserves clean %s values", (attribute, tag, value) => { document.body.innerHTML = `<${tag} ${attribute}="${value}">`; diff --git a/extension/src/rules/attribute-injection-sanitize.ts b/extension/src/rules/attribute-injection-sanitize.ts index 8642698..fa1d581 100644 --- a/extension/src/rules/attribute-injection-sanitize.ts +++ b/extension/src/rules/attribute-injection-sanitize.ts @@ -3,13 +3,15 @@ // Apply the prompt-injection pattern set to a small allowlist of // agent-readable element attributes. Sighted users typically don't see -// these strings — `aria-label`, `aria-description`, `title`, -// `placeholder`, `alt`, and `data-tooltip` surface in screen readers, -// hover popups, or empty-state hints, not as the main visible label. -// Browser-use agents, in contrast, read the accessibility tree where -// these values are first-class names and descriptions for every -// element. That asymmetry makes attributes a clean carrier for -// instruction-shaped text the page operator never has to render. +// these strings — `aria-label`, `aria-description`, +// `aria-roledescription`, `aria-placeholder`, `aria-valuetext`, +// `aria-keyshortcuts`, `title`, `placeholder`, `alt`, and `data-tooltip` +// surface in screen readers, hover popups, or empty-state hints, not as +// the main visible label. Browser-use agents, in contrast, read the +// accessibility tree where these values are first-class names, +// descriptions, value text, and shortcut hints for every element. That +// asymmetry makes attributes a clean carrier for instruction-shaped +// text the page operator never has to render. // // We also scrub `value` on `` elements the user cannot reach: // `disabled` inputs render to humans but cannot be edited, and @@ -44,6 +46,10 @@ const RULE_ID = "attribute-injection-sanitize" as const; const CANDIDATE_ATTRIBUTES = [ "aria-label", "aria-description", + "aria-roledescription", + "aria-placeholder", + "aria-valuetext", + "aria-keyshortcuts", "alt", "title", "placeholder",