How JustZix injects code on CSP-strict pages
Some pages have a Content-Security-Policy so strict they will not run any script their author did not write. And yet JustZix can inject your JS there and hook the Output Console into the page's console.log. This article explains how — and why the first version could not.
What CSP is and why it gets in the way
Content-Security-Policy is an HTTP header by which a page declares where scripts may be loaded from. A typical strict policy is script-src 'self' — "only run scripts from my own domain". Such a policy blocks inline <script>: if you inject a <script>code...</script> tag into the DOM, the browser will not run it. This is how GitHub, banking dashboards and many corporate sites work.
The first approach — and a regression
An early version of the Output Console hook (v2.13.72–73) injected code exactly via an inline <script> appended to <head>. On ordinary pages it worked. On pages with script-src 'self' the browser silently rejected the script — the hook never attached, the Output Console stayed empty. That was a regression: a feature that worked "in the demo" failed exactly where a developer needs it most.
The fix — chrome.scripting.executeScript
The solution (v2.13.74) no longer injects a <script> tag. Instead the extension's service worker calls chrome.scripting.executeScript. That is the crucial difference: this script runs from the extension's privilege, not from page level. The page's CSP does not apply to it — the browser treats it as code from a trusted extension the user consented to at install time.
MAIN world versus ISOLATED world
An extension's content scripts live by default in an "isolated world" (ISOLATED) — they have their own window, separated from the page. That is safe, but useless when you want to hook into the page's real console.log or touch its global variables.
So the hook lands in the MAIN world — the same JavaScript context the page's own code lives in. There it can wrap console.log/warn/error, attach error and unhandledrejection listeners, and see exactly what the page sees.
A bridge between worlds — postMessage
The hook in the MAIN world captures logs, but the Output Console window itself is rendered by a content script in the ISOLATED world. The two worlds do not share variables — a bridge is needed. That bridge is window.postMessage:
- The MAIN-world hook intercepts a
console.logentry. - It serializes the arguments and calls
window.postMessagewith a tagged payload. - The ISOLATED content script listens for
message, filters by tag, and renders the entry in the Output Console window.
postMessage is one of the few channels that crosses the world boundary — and it does so without violating CSP, because it is not script execution, just message passing.
What this means in practice
The Output Console and JS injection work on pages where the classic inline-script trick would fail — GitHub, banking dashboards, corporate intranets with a hard CSP. You do not configure anything; the extension picks this path itself.
Limits — what CSP still will not allow
- Loading external scripts from your code. If your injected JS tries to append a
<script src="https://cdn...">, that new tag is still subject to the page's CSP. - fetch to blocked domains. The
connect-srcdirective still limits where the page's code may send requests. - This is not a security bypass. It works because the user knowingly installed the extension and granted it permissions — it is a privilege you granted, not a hole.
Update v3.2.0 — rules, actions and TEMP windows too
The mechanism above was about the Output Console hook — how the extension listens to the page's console.log. Executing your JS rules, actions and TEMP JS windows is a separate path — and it too used to work only on pages whose CSP allowed 'unsafe-eval'. In v3.2.0 we ported the same "layered strategy" to that second path: the order of attempts is chrome.userScripts.execute → new Function → <script src="blob:…">. Rules, actions and the JS pane / JS Console now run even on Facebook (via the blob fallback) and on fully CSP-strict sites (via userScripts after a one-time "Allow user scripts" opt-in). Full write-up: JustZix JavaScript rules now work even on Facebook, X and GitHub.
See also
- Output Console catches errors — what the hook sees
- Output Console — the window the logs land in
- window.JZ helpers — the programmatic API
Install JustZix — and inject code even where the page says "no".
Rate this post
No ratings yet — be the first.