Publish a browser extension without a store rejection: Manifest V3 package checks, least-privilege permission review with written justifications, listing copy and screenshot rules, privacy disclosures, and a rejection fix playbook keyed to real store violation IDs. Use when submitting or updating an extension on the Chrome Web Store, Firefox Add-ons (AMO), Microsoft Edge Add-ons, or Safari, when a submission was rejected, or when planning permissions before a build.
./install.sh browser-extension-publishingname: "browser_extension_publishing" description: "Publish a browser extension without a store rejection: Manifest V3 package checks, least-privilege permission review with written justifications, listing copy and screenshot rules, privacy disclosures, and a rejection fix playbook keyed to real store violation IDs. Use when submitting or updating an extension on the Chrome Web Store, Firefox Add-ons (AMO), Microsoft Edge Add-ons, or Safari, when a submission was rejected, or when planning permissions before a build."
Get an extension through store review on the first submission, and fix a rejection fast when one lands. The work is mostly decided before upload: a Manifest V3 package with no remotely hosted code, the smallest permission set that still does the job, a listing that matches the code exactly, and reviewer notes that let a stranger test the whole product. This skill covers Chrome Web Store, Firefox Add-ons, Microsoft Edge Add-ons, and the Safari App Store route, plus the pre-submission check that catches the common rejection reasons while they are still cheap to fix.
manifest_version: 3, a service worker under background.service_worker (no background scripts, page, or persistent flag), action instead of browser_action or page_action, host access in host_permissions, and network blocking done with declarativeNetRequest rules shipped in the package instead of blocking webRequest. State that must survive the service worker sleeping goes in chrome.storage, never in a variable that dies with the worker.permissions, host_permissions, optional_permissions, and optional_host_permissions, write one sentence: permission, user-facing feature it serves, why a narrower option will not work. Then apply the cuts in order:activeTab when the extension only acts on the current page after a click, a context menu pick, or a keyboard command. activeTab is temporary and ends on navigation or tab close, which is why reviewers prefer it.optional_permissions and optional_host_permissions, and request them at runtime with chrome.permissions.request from a user gesture, with the reason shown in the UI at that moment. Some permissions cannot be optional in Chrome, including debugger, declarativeNetRequest, devtools, geolocation, proxy, and tts, so core features built on those stay required and get a stronger justification.tabs only reads URL, title, and favicon fields, cookies only covers the chrome.cookies API (not document.cookie), and storage only covers chrome.storage (not localStorage or IndexedDB). Requesting one for the wrong API is a standard excessive-permission rejection.http source, eval(, new Function(, and any fetch that returns text which then gets executed or interpreted as commands. Bundle every library into the package. Remote calls may carry data only: JSON config where every selectable behavior already exists in the shipped code, images, and sync payloads. Remove obfuscation. Minification for file size is accepted across stores, code that hides its purpose is not, and AMO can demand original source for any bundled output.python3 bin/preflight.py <extension_dir> from this skill folder against the unpacked package that will be zipped. It checks MV3 shape, MV2 leftovers, files named in the manifest that are missing on disk, a CSP that tries to allow eval, broad host patterns, high-risk permissions, permissions with no matching API call in the source scan, and remote script tags. Fix every FAIL. For every WARN, either fix it or keep the written justification from step 3, because a reviewer will ask the same question. The API-use scan is a heuristic: code reached through a wrapper library or WebAssembly can scan as unused, so a WARN there means prepare the proof, not blindly delete a live permission.manifest.json at the root of the archive. Unzip into a clean folder, load that copy, and run the core flow start to finish: fresh install, first run with no account, the main job, options or settings, and behavior on a slow or failed network. Confirm case-sensitive file paths match the manifest exactly. A file named in the manifest and missing from the package is the top packaging rejection. Confirm the backend the extension needs will stay up for the whole review window, and prepare a test account with seed data if any feature sits behind a login.references/listing-and-privacy.md: what it does in the first two lines, one bullet per real feature, how it works, a plain data section, and honest limits. Screenshots show the real UI doing the real task on a real page, in description order, at the exact store size, with test account data only. Full per-store asset specs and account steps are in references/store-matrix.md.browser_specific_settings.gecko.data_collection_permissions in the manifest, including an explicit none declaration when nothing is collected. Any store: if the code collects nothing and calls no analytics, say so in the policy and in the listing, and keep the code that clean.xcrun safari-web-extension-converter, Xcode signing, and App Store review, so it is a separate release track. Details per store are in references/store-matrix.md.references/rejection-playbook.md, match the violation ID or policy number, fix only that issue, and resubmit with a short note naming the violation, the change, and the files touched. Appeal instead of resubmitting only when the reviewer premise is factually wrong and the proof sits inside the submitted package, for example a permission flagged as unused that is called through a wrapper. Never bundle unrelated changes into a fix resubmission, AMO says so directly and it slows every store.version, re-run the preflight, re-check every new permission as if it were new (new required permissions can disable an extension for existing users on upgrade, optional ones do not), update the privacy policy before shipping a collection change, and keep release notes factual. A percentage rollout on Chrome applies to existing users, only moves up, and does not trigger a new review.A submission-ready run produces:
activeTab over standing host access, optional over required, local storage over a server call. Each narrowing is one less reviewer question.publisher key 2e43c913833850e6d80f939677e3de76e806d47785c0f1caaca90f9d18c98e1e
No ratings yet — be the first to rate it.