Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions extension/src/rules/__tests__/prompt-injection-redact.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,60 @@ describe("prompt-injection-redact", () => {
expect(document.body.textContent).toContain(FIXTURES.IGNORE_ALL);
});

describe("reveal flow", () => {
// The container the rule hides is an ancestor of the matched text
// node, so the reveal stamp lands on the container — not on anything
// walkTextNodes returns. A second apply() stands in for the disable→
// enable cycle (and for the watcher-driven re-scan that would happen
// if a MutationObserver is ever wired in). Same shape as the
// disguised-ad-flag bug fixed in #160.
it("does not re-hide a container the user revealed", () => {
document.body.innerHTML = `<p>${FIXTURES.IGNORE_ALL}</p>`;
promptInjectionRedactRule.apply(document.body);

const placeholder = document.querySelector<HTMLElement>(
`.${PLACEHOLDER_CLASS}`,
);
placeholder?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
expect(document.querySelector("p")).not.toBeNull();

promptInjectionRedactRule.apply(document.body);

expect(document.querySelector(`.${PLACEHOLDER_CLASS}`)).toBeNull();
expect(document.querySelector("p")).not.toBeNull();
expect(document.body.textContent).toContain(FIXTURES.IGNORE_ALL);
});

// Reveal exempts the revealed container (and its descendants) only —
// a sibling paragraph with its own injection must still be hidden.
it("still hides a sibling container after an unrelated reveal", () => {
document.body.innerHTML = `
<p id="first">${FIXTURES.IGNORE_ALL}</p>
<p id="second">${FIXTURES.DISREGARD}</p>
`;
promptInjectionRedactRule.apply(document.body);
expect(document.querySelectorAll(`.${PLACEHOLDER_CLASS}`)).toHaveLength(
2,
);

const [firstPlaceholder] = document.querySelectorAll<HTMLElement>(
`.${PLACEHOLDER_CLASS}`,
);
firstPlaceholder?.dispatchEvent(
new MouseEvent("click", { bubbles: true }),
);

promptInjectionRedactRule.apply(document.body);

// First container revealed and stays revealed; second stays hidden.
expect(document.querySelector("p#first")).not.toBeNull();
expect(document.querySelector("p#second")).toBeNull();
expect(document.querySelectorAll(`.${PLACEHOLDER_CLASS}`)).toHaveLength(
1,
);
});
});

describe("findContainer escalation", () => {
// When injection text lives directly in <body> with no block-level
// ancestor, the rule must not redact — wrapping <body> in a placeholder
Expand Down
11 changes: 11 additions & 0 deletions extension/src/rules/prompt-injection-redact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// shipped extension bundle therefore contains plaintext regexes, not
// `atob` decoding (matters for Chrome Web Store review).

import { REVEALED_ATTR } from "../lib/dom-markers";
import {
filterToOutermost,
isInsidePlaceholder,
Expand All @@ -23,6 +24,7 @@ import { INJECTION_PATTERNS } from "./injection-patterns.generated";
import type { Rule } from "./types";

const RULE_ID = "prompt-injection-redact" as const;
const REVEALED_SELECTOR = `[${REVEALED_ATTR}="${RULE_ID}"]`;
const MIN_TEXT_LENGTH = 8;

// Containers we consider a reasonable unit to hide. Walking up from the text
Expand Down Expand Up @@ -80,6 +82,15 @@ function apply(root: ParentNode): void {
if (isInsidePlaceholder(element)) {
continue;
}
// The container is an ancestor of the matched text node, so the reveal
// stamp lands here — not on the text node we walked from. If a previous
// run hid this container and the user revealed it, a re-apply (rule
// disable→enable, or a future MutationObserver-driven re-scan) would
// otherwise re-hide the same block. Same shape as the disguised-ad-flag
// bug fixed in #160.
if (element.closest(REVEALED_SELECTOR)) {
continue;
}
replaceWithBlockPlaceholder(
element,
RULE_ID,
Expand Down
Loading