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

Skip to content

Commit ec081a3

Browse files
twschillerclaude
andcommitted
Fix cross-origin-frame-redact watcher missing top-level iframes
The watcher's onSubtrees handed each added subtree root to scan(), which calls querySelectorAll('iframe') on it. querySelectorAll matches descendants only, so a bare cross-origin <iframe> appended directly to document.body slipped past the watcher (the iframe itself is the root, and it has no iframe descendants). Mirror the workaround already documented in selector-hide-rule.ts: drop the per-root loop and rescan from document.body on every batch. The is-connected / is-revealed checks in scan() keep it idempotent, and the watcher's throttle coalesces bursts. Add a regression test that appends a bare iframe directly to body. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
1 parent a158f60 commit ec081a3

2 files changed

Lines changed: 25 additions & 4 deletions

File tree

extension/src/rules/__tests__/cross-origin-frame-redact.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,24 @@ describe("crossOriginFrameRedactRule", () => {
189189
expect(getPlaceholder()).not.toBeNull();
190190
});
191191

192+
// Regression: when an iframe is appended directly (no wrapper), the
193+
// MutationObserver root *is* the iframe, and querySelectorAll on it
194+
// returns nothing. The watcher must rescan from document.body to catch
195+
// this case.
196+
it("replaces a bare cross-origin iframe appended directly to body", async () => {
197+
crossOriginFrameRedactRule.apply(document.body);
198+
199+
const iframe = document.createElement("iframe");
200+
iframe.src = "https://example.com/widget";
201+
document.body.append(iframe);
202+
203+
await flushMutations();
204+
jest.advanceTimersByTime(MUTATION_THROTTLE_MS);
205+
206+
expect(document.querySelector("iframe")).toBeNull();
207+
expect(getPlaceholder()).not.toBeNull();
208+
});
209+
192210
it("teardown stops the observer so later additions are ignored", async () => {
193211
crossOriginFrameRedactRule.apply(document.body);
194212
crossOriginFrameRedactRule.teardown();

extension/src/rules/cross-origin-frame-redact.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,13 @@ function scan(root: ParentNode): void {
7777
}
7878

7979
const watcher = createSubtreeWatcher({
80-
onSubtrees: (roots) => {
81-
for (const root of roots) {
82-
scan(root);
83-
}
80+
// MutationObserver hands us the newly-inserted element itself, but
81+
// querySelectorAll on that element does not match the element itself —
82+
// so a bare cross-origin <iframe> appended directly to document.body
83+
// would be missed. Rescan from document.body on every batch; the
84+
// is-connected / is-revealed checks in scan() keep it idempotent.
85+
onSubtrees: () => {
86+
scan(document.body);
8487
},
8588
skipPlaceholderSubtrees: true,
8689
});

0 commit comments

Comments
 (0)