Is Just-One-Page-PDF: An awesome web page to PDF tool safe?
Just-One-Page-PDF: An awesome web page to PDF tool is medium risk. Starting a page-to-PDF capture uses the manage-extensions permission. The worker scans the page for extension iframes, matches IDs against enabled installed extensions, disables matches during capture, stores IDs, and later…
Who publishes itsuziwen1 - 4 other listings from the same operator, none carrying a finding
suziwen1 - 4 other listings from the same operator, none carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
4 other listings published from this account, 903k+ users between them, none of them carrying a finding.
Shared hosts - 2 hostnames
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Disables Other Extensions During PDF Capture
Starting a page-to-PDF capture uses the manage-extensions permission.
The worker scans the page for extension iframes, matches IDs against enabled installed extensions, disables matches during capture, stores IDs, and later re-enables them.
What actually happens
You start a page-to-PDF capture.
The extension scans the page for extension iframes, turns matching enabled extensions off during capture, stores their IDs locally, and later tries to turn them back on.
Concrete values used by the extension-management path
- Permission to manage extensionsmanifest permissions includes "management"
This permission lets the extension turn other installed extensions on or off.
- Extension iframe URLchrome-extension://cjpalhdlnbpafiamejdnhcphjbkeiagm/frame.html (illustrative)
This is how the page scan identifies that another browser extension has injected a frame into the page.
- Matched extension IDcjpalhdlnbpafiamejdnhcphjbkeiagm (illustrative)
The ID is compared with enabled installed extensions before the extension is turned off.
- Local restore listchrome.storage.local disableExtensionIds
This local value records which extensions were turned off so the extension can try to restore them after capture.
- Disable and restore valuessetEnabled(id, false) then setEnabled(id, true)
The first value turns the matched extension off; the later value attempts to turn it back on.
Where the capture flow invokes extension disable and restore
before capture
You didThe page-capture script sends a create request.
The extension didThe service worker enters the PDF creation path and scans for extension iframes before capture.
during setup
You didThe scan returns IDs that match enabled installed extensions.
The extension didEach matching extension is turned off and the IDs are saved locally.
after capture
You didThe capture path finishes or reaches a later frame-ready path.
The extension didThe saved IDs are read back and the extension attempts to turn those extensions on again.
The code that does this
Shipped code that scans, disables, stores, and restores extension IDs
Readable scanner function
js/background.js_w = function(e) { const t = [], n = (r, i = 1, o) => { if (i > 10) return "function" == typeof r.remove && e && r.remove(), void(o && "function" == typeof o.remove && o.remove()); i++; let a = !1; r.querySelectorAll("*").forEach((function(r) { if (!a) if ("IFRAME" === r.tagName)(function(n) { if (n.src.startsWith("chrome-extension://")) { let r = n.src; if (r = r.substring(19), r = r.split("/")[0], r && t.push(r), e) return n.remove(), !0 } })(r) && o && "function" == typeof o.remove && (o.remove(), a = !0); else if (r.shadowRoot) n(r.shadowRoot, i); else try { const e = chrome.dom.openOrClosedShadowRoot(r); e && n(e, i, r) } catch (e) {} })) }; return n(document), t}Readable disable function
js/background.jsSw = async function(e) { if (!pb.checkCanExecuteScript(e)) return []; const t = e.id, n = await chrome.scripting.executeScript({ target: { tabId: t, allFrames: !0 }, injectImmediately: !0, func: _w }), r = [], i = (await chrome.management.getAll()).filter((e => "extension" === e.type && (e.id !== chrome.runtime.id && !!e.enabled))).map((e => e.id)); if (n && n.forEach((e => { e.result?.forEach((e => { e != chrome.runtime.id && i.indexOf(e) >= 0 && r.push(e) })) })), r.length > 0) { const e = Array.from(new Set(r)); for (let t = 0; t < e.length; t++) { const n = e[t]; try { await chrome.management.setEnabled(n, !1) } catch (e) {} } const n = (await chrome.storage.local.get("disableExtensionIds")).disableExtensionIds || [], i = Array.from(new Set(e.concat(n))); return await chrome.storage.local.set({ disableExtensionIds: i }), await chrome.scripting.executeScript({ target: { tabId: t, allFrames: !0 }, func: _w, args: [!0] }), e } return []}Readable restore function
js/background.jskw = async function(e, t) { const n = (await chrome.storage.local.get("disableExtensionIds")).disableExtensionIds || []; t = Array.from(new Set(n.concat(t))); for (let e = 0; e < t.length; e++) { const n = t[e]; try { await chrome.management.setEnabled(n, !0) } catch (e) {} } await chrome.storage.local.set({ disableExtensionIds: [] })}The code that does this
Manifest permission that enables the behavior
"permissions": [ "activeTab", "contextMenus", "scripting", "debugger", "downloads", "storage", "management", "tabs", "notifications", "history"]