For store reviewers

Extension reviewer guide

For Chrome Web Store, Mozilla Add-ons (AMO) and Microsoft Edge Add-ons reviewers: how to inspect, build, validate and test the Sonarr/Radarr/Lidarr/Readarr Auto Search extension. The canonical version of this document lives in the repository.

Read the reviewer guide on GitHub →

On this page
  1. Summary
  2. Repository layout
  3. Build & verification
  4. Permissions
  5. Runtime architecture
  6. Data handling & privacy
  7. Third-party libraries
  8. Functional testing
  9. Security checklist
  10. Automated testing
  11. Implementation details
  12. Tracing code paths
  13. Known limitations
  14. Script audit pointers
  15. Contact

Summary

This extension streamlines searching across user-hosted Servarr applications (Sonarr, Radarr, Lidarr, Readarr) by:

  • Auto-prefilling Servarr search pages when a user navigates to a URL containing /add/new/<term> (or similar).
  • Adding context menu items to search selected text directly in any configured Servarr instance.
  • Injecting small Servarr search icons/links into supported third-party media sites (IMDb, TMDb, TVDb, Trakt, TVmaze, MusicBrainz, Letterboxd, TV Calendar, Rotten Tomatoes, Metacritic, Simkl, IPTorrents, Last.fm, Allociné, SensCritique, Betaseries, Prime Video, Rate Your Music, MyAnimeList, Wikipedia — see the integrations gallery).
  • Providing an options UI where users configure base URLs + API keys (optional) for automatic advanced selector configuration.
  • Backup & restore of user settings via a dedicated options tab: exports a JSON file using a schema-versioned envelope; imports either the new envelope or legacy plain JSON with validation; supports merge or replace modes and a preview-changes diff.

The extension does not collect analytics, ship remote code, or exfiltrate user data. All API calls target only the user's own Servarr instances explicitly configured in settings. See the latest release changes in the changelog.

Repository layout (source vs built artifacts)

PathPurpose
src/Authoritative source (HTML, JS, Tailwind/Sass inputs, manifests).
src/manifest-chromium/manifest.jsonChromium MV3 manifest.
src/manifest-firefox/manifest.jsonFirefox MV2 manifest (kept until MV3 is fully supported on Firefox for parity).
src/content/js/Core runtime scripts (logic, content script, shared helpers, popup & options).
src/content/css/Tailwind entry (tailwind.css).
dist/chromium/Build output for Chromium (produced by grunt release).
dist/firefox/Build output for Firefox.
tests/playwright/Playwright integration tests (site integration smoke tests).
tests/unit-tests/Jest unit tests (minimal).
Gruntfile.jsBuild pipeline definition.
tailwind.config.jsTailwind configuration (purge/content globs + safelist).

All minified/processed assets in dist/ are reproducible by following the build steps below.

Build & verification steps

Perform a clean build from source (commands are cross-platform):

npm ci
grunt release

This produces:

  • dist/chromium (MV3 build using eventPage.chrome.js as service worker + background script bundle pieces).
  • dist/firefox (MV2 build with background.scripts).

Mapping built code to source

Built file (example)Source origin
dist/**/popup.htmlsrc/popup.html (unchanged except path rewriting if any).
dist/**/options.htmlsrc/options.html.
dist/**/content/js/*.jsDirect copies from src/content/js/ (no obfuscation).
dist/**/content/css/tailwind.cssGenerated by Tailwind from utility classes declared across src/**/*.html & src/**/*.js + safelist.
Icon imagesCopied 1:1 from src/content/assets/images/.

There is no custom packer, no dynamic code generation beyond Tailwind's static CSS expansion.

Permissions & justification

manifest-chromium/manifest.json:

"permissions": ["scripting", "storage", "activeTab", "contextMenus"],
"host_permissions": [
    "*://*.allocine.fr/*", "*://*.betaseries.com/*", "*://*.imdb.com/*",
    "*://*.iptorrents.com/*", "*://*.iptorrents.net/*", "*://*.iptorrents.me/*",
    "*://*.iptorrents.eu/*", "*://*.iptorrents.ru/*", "*://*.last.fm/*",
    "*://*.letterboxd.com/*", "*://*.metacritic.com/*", "*://*.musicbrainz.org/*",
    "*://*.myanimelist.net/*", "*://*.primevideo.com/*", "*://*.rateyourmusic.com/*",
    "*://*.rottentomatoes.com/*", "*://*.senscritique.com/*", "*://*.simkl.com/*",
    "*://*.themoviedb.org/*", "*://*.thetvdb.com/*", "*://*.trakt.tv/*",
    "*://*.pogdesign.co.uk/*", "*://*.tvmaze.com/*", "*://*.wikipedia.org/*"
],
"optional_host_permissions": ["<all_urls>"]
PermissionRationale
scriptingInjection of content script on listed domains (MV3 requirement).
storagePersist user configuration (base URLs, API keys, toggles).
activeTabAllow immediate context-menu-initiated search to open the correct tab / access the tab URL when needed.
contextMenusAdd right-click Servarr search items.
Host listIntegration discovery. The site list is explicit for transparency.
<all_urls> (optional)User-specified Servarr domains, unknown at install time (e.g. http://nas.local:8989). Users grant permission to their Servarr instances on a per-domain basis.

Firefox (MV2)

manifest-firefox/manifest.json lists the same host patterns plus storage, activeTab, tabs and contextMenus under permissions (MV2 does not separate host_permissions), with <all_urls> under optional_permissions. The same justifications apply.

Background & runtime architecture

ComponentRole
Content scripts (content_script.js + engine files)Injected two ways: declaratively via the manifest content_scripts entry (run_at: document_idle) on the listed integration domains, and programmatically from the background for user-configured Servarr instance domains (unknown at install time). The same bundled, local files are used for both; the background path skips domains already covered declaratively to avoid double injection.
eventPage.chrome.jsMV3 service worker (message routing, context menu handling, storage change propagation, and programmatic content-script injection for Servarr domains).
eventPage.jsFirefox MV2 background equivalent.
core.jsShared helpers: settings access (storage), API version probing, URL building, small browser polyfill for non-WebExtension contexts (used in tests).
options.js / options_*.jsOptions UI assembly and tab-specific logic (settings load/save, site/integration list management, backup/restore handling).
popup.jsPopup enable/disable toggle + quick settings link.

All logic is synchronous or simple async, with fetch only to user-supplied Servarr endpoints (API base derived from settings). No external analytics endpoints.

Data handling & privacy

AspectDetails
Storage scopebrowser.storage.sync / local (standard WebExtension) for configuration only.
Personal dataNone collected or transmitted externally.
Network callsOnly to user Servarr instances (optional) when an API key is present — to detect version for auto configuration.
Telemetry / tracking / adsNone.
Backup/restoreExport produces a local JSON download only; import reads a local JSON file chosen by the user. No data is transmitted externally.
Firefox data consentbrowser_specific_settings.gecko.data_collection_permissions is declared as required: ["none"], formally stating that no data is collected (Firefox built-in data consent).

Third-party libraries

LibrarySourceUse
jQuerynode_modules/jquerySimple DOM selection & legacy code compatibility.
Font Awesome 7@fortawesome/fontawesome-freeIcons in popup/options UIs.
Tailwind CSS 4 + Forms plugintailwindcss, @tailwindcss/formsUtility-first styling, accessible form defaults. Built into a single CSS file.
Spectrum colour pickerspectrum-colorpicker2Colour picker.
webextension-polyfillwebextension-polyfillUnified browser.* API shim across Chrome/Firefox.

All libraries are installed via npm ci and bundled transparently (no CDN runtime fetches). Unminified sources remain in node_modules/.

How to functionally test

8.1 Load the extension (Chromium)

  1. Build: npm ci && grunt release.
  2. Open chrome://extensions → enable Developer Mode.
  3. Click "Load unpacked" → select dist/chromium.
  4. Open the popup: verify the enable/disable button toggles state (stored in extension storage).

8.2 Load the extension (Firefox)

Option A (temporary install): build, visit about:debugging#/runtime/this-firefox, "Load Temporary Add-on" → pick any file inside dist/firefox (e.g. manifest.json).

Option B (web-ext run):

npm ci
grunt release
npm run firefox

8.3 Configure & test core features

FeatureSteps
Auto-search injectionNavigate to a configured Sonarr/Radarr URL, append /add/new/<term> → search field prefilled & results loaded.
Context menu searchSelect text on any page → right-click → choose a Servarr target → new tab opens to the correct add/new path.
Integration iconsVisit (e.g.) https://www.imdb.com/title/tt0944947/ → Servarr icon(s) should inject near the title region (depends on site layout).
Enable/disable togglePopup → toggle → integration icons & auto-search cease when disabled.
Advanced auto-detectIn options: set base URL + API key for (e.g.) Sonarr → advanced selectors auto-populate; test by removing the key and toggling detection off/on.
Custom icon (if enabled)In options, navigate to the custom icon tab; enable & adjust position/colour; verify the preview updates.

8.4 Storage inspection

In DevTools (background page or service worker):

await browser.storage.sync.get();

Review the structure (only configuration JSON under a single key).

8.5 Backup & restore

Options → "Backup & restore" tab.

  • Backup: "Download backup" produces a pretty-printed JSON file using an envelope format — type ("servarr-autosearch-settings"), schemaVersion (1), exportedAt, appVersion, and a data payload with internal fields (prefixed _/__) stripped.
  • Restore: choose a saved JSON file (envelope or legacy plain JSON — legacy accepted with an informational note). Optional replace mode overwrites fully; the default is a deep merge (arrays replaced; objects merged). "Preview changes" shows a diff of added/removed/changed paths before applying. Files are minimally validated (config object, sites[], integrations[]); a newer schemaVersion warns before import, older schemas are normalised.
  • Reset: "Reset settings" → confirmation prompt → resets to built-in defaults.
  • No network transmission occurs during backup or restore; files are handled locally in the browser context.

Security review checklist

CheckNotes
Remote script injectionNone (all scripts packaged locally).
eval / dynamic functionNot used.
Dangerous DOM APIsOnly standard element creation & attribute manipulation; no unsanitised HTML injection from external sources.
External network endpointsOnly user-configured Servarr base URLs when an API key is present (GET/POST for version test).
Permission minimisationCore set only; <all_urls> justified for unknown self-hosted domains & a consistent injection model.
CSPUses default WebExtension CSP; no inline JS reliance beyond standard HTML event-free patterns.

Testing (automated)

Playwright (site integrations) — not required for functional store acceptance but provided for transparency:

npm ci
grunt release
cd tests/playwright
npm i
npx playwright install --with-deps chromium
npx playwright test

These load integration target pages and assert icon injection or URL logic (requires network access to public sites; may be flaky if site layouts change).

Jest (unit tests) — currently minimal; used as a development safeguard:

npm ci
npm test

Notable implementation details

ConcernHandling
Accessible tabsARIA roles & keyboard navigation (ArrowLeft/Right, Home/End).
Toggle controlsReplaces the legacy Bootstrap toggle plugin with small custom button logic + aria-pressed.
Range inputsNative <input type="range"> styled via the Tailwind forms plugin (no third-party slider).
Status badges (options)Show passive health of Servarr API endpoints; timeouts gracefully degrade to "Unknown/Fail".
Browser polyfillwebextension-polyfill for consistent Promise-based APIs.

How to trace code paths

Typical icon injection path on an integration page:

  1. Content script runs (sonarr_radarr_lidarr_autosearch.js).
  2. Loads settings via helpers in core.js.
  3. Detects page type via site-specific selectors.
  4. Builds search URLs and injects an <a> or <img> element with a click handler.
  5. If disabled (settings.config.enabled === false) it returns early.

Auto-search path:

  1. User navigates to an add/new URL carrying trailing search text.
  2. Content script checks the path & extracts the trailing segment.
  3. Waits for the search input (configurable selectors) up to a timeout.
  4. Sets the value and dispatches an input event.

Known limitations / edge cases

AreaNote
Layout changes on external sitesMay break icon injection; handled by future config updates, not runtime heuristics.
Firefox MV3 migrationPending full parity; the MV2 manifest is currently retained for Android support (see Firefox for Android MV3 compatibility).

Reviewer quick script audit pointers

Search for any suspicious patterns:

  • eval( → not present.
  • new Function( → not present.
  • fetch( / XMLHttpRequest → confined to Servarr API probing in core.js (search for callApi).
  • Event listeners for the context menu: background script only (eventPage.*).
  • Storage key: sonarrRadarrLidarrAutosearchSettings (searchable).

Contact

If clarification is required during review, please raise an issue in the repository or include reviewer notes in the store submission feedback.

Summary assertion: all shipped code is deterministic & build-reproducible with npm ci && grunt release, free of remote executable payloads, limited to user-directed network activity, and structured for least complexity in review (no bundler obfuscation).

Thank you for reviewing this extension. 📡