Our Chrome extension was rejected for "remotely hosted code". Here's the build guard we added
I'm the founder of Flawsnap, a visual bug-reporting tool. This week our Chrome extension went live, but only on the second try. Here's why it was rejected, in case you're about to submit an extension that shares code with a website.
The rejection
Version 0.2.1 was rejected for "Including remotely hosted code in a Manifest V3 item".
Why
The extension shows the same report form as our website widget, so its content script bundles the widget's code. The widget, on a website, can add a <script src="{api}/widget-recorder.js"> to load an rrweb session recorder.
The extension never runs that path. It creates the form with replay turned off and records the tab as video itself, with chrome.tabCapture and an offscreen document. But the code that could inject the script was in the package, and that's what review looks at.
The fix (0.2.2)
- Strip it at build time. An esbuild plugin replaces the widget's
loadRecorder()with a version that loads nothing and drops the recorder URL. The build stops if the widget's code changes shape, so the swap can't silently stop matching. - Scan the output. After building, a script walks
dist/and fails the build on a<script>element,import(),eval,new Function,importScripts, a remote.jsURL, or the recorder file. - Tests for both guards.
- A note to the reviewer explaining what changed.
The scan is just a list of patterns, checked against every built .js and .html file:
/** Patterns that load or run code from outside the package. */ export const REMOTE_CODE = [ { what: 'creates a <script> element', re: /createElement\(\s*["'`]script["'`]\s*\)/ }, { what: 'importScripts()', re: /\bimportScripts\s*\(/ }, { what: 'dynamic import()', re: /(?<![\w$.])import\s*\(/ }, { what: 'new Function()', re: /\bnew\s+Function\s*\(/ }, { what: 'eval()', re: /(?<![\w$.])eval\s*\(/ }, { what: 'a remote .js URL', re: /https?:\/\/[^\s"'`<>]+\.m?js\b/ }, { what: 'the widget recorder file', re: /widget-recorder\.js/ }, { what: 'a remote <script src>', re: /<script[^>]+src\s*=\s*["']?(https?:)?\/\//i }, ] Takeaways
- Review checks what your package could do, not just what it does.
- If you share UI code between a site and an extension, look for dynamic script loading, lazy
import()and CDN URLs in the bundle. - A build-time check is cheaper than another review round.
The extension
Report bugs on any site, no code: mark up a screenshot or record the tab, and it lands on your Flawsnap board with the page, the browser and, on sites you allow, console errors and failed requests. Chrome, Edge and Brave. It needs a Flawsnap account (7-day trial).
Has the Web Store rejected one of yours? What for?
Drafted with AI assistance and edited by me.