Skip to content

feat: Browser Extension Integration + System Health/i18n Improvements - #91

Closed
chrisriv10 wants to merge 9 commits into
mainfrom
feature/browser-extension-integration
Closed

feat: Browser Extension Integration + System Health/i18n Improvements#91
chrisriv10 wants to merge 9 commits into
mainfrom
feature/browser-extension-integration

Conversation

@chrisriv10

@chrisriv10 chrisriv10 commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Summary

Complete browser extension ecosystem for credential leak monitoring, major tray dashboard enhancements, and full i18n coverage for the health score system across 14 locales.


Browser Extension (Manifest V3)

browser-extension/ — Complete browser extension including:

  • Background service worker (background.js)
    • HIBP k-anonymity password checks
    • Native messaging bridge
  • Content script (content.js)
    • Password field detection
    • Inline breach badges
    • Automatic checks on paste
  • Popup (popup.html, popup.js)
    • Manual password checking
    • Desktop connection status
  • Options page (options.html, options.js)
    • Settings with sync storage
  • Native messaging host (native-host.js)
    • Bridges the browser extension and desktop app via stdio
  • Install script (tools/install-native-host.js)
    • Registers the Chrome/Edge native messaging host on Windows

Desktop Integration

  • Added soterios:// protocol handler
    • Supports second-instance launch handling
    • Enables tray communication
  • Added IPC handlers:
    • credential-leak:notify
      • Extension → desktop credential leak alerts
    • browserExtension:installNativeHost
      • One-click native host installation from Settings
  • Added Browser Extension Integration feature toggle in Settings
    • Automatically installs the native host when enabled

Tray Dashboard Enhancements

  • Health score (0–100) with color coding
  • Real-time protection (RTP) status indicator
  • Network usage sparkline (24-hour RX/TX history)
  • Quick Scan button
  • Last scan timestamp
  • Threat count display

System Health Score i18n (14 Locales)

Translation Key Structure

Updated to use:

  • health.label.*
  • health.reason.*

This matches the dashboard's expected translation structure.

Added health.reason.* Keys

  • noScan
  • noThreats
  • threatsFound
  • scanToday
  • scanDaysAgo
  • diskLowSpace
  • diskNoVolumes
  • diskHealthy
  • memoryUsage
  • cpuLoad
  • uptimeToday
  • uptimeDays
  • uptimeWeeks
  • uptimeLong
  • rtpActive
  • rtpDisabled
  • firewallActive
  • firewallDisabled

Localization

  • Fixed Spanish translation:
    • health.reason.noThreats
      • "No se encontraron amenazas en el escaneo más reciente"

Completed translations for all 14 locales:

  • English (en)
  • Spanish (es)
  • French (fr)
  • German (de)
  • Italian (it)
  • Turkish (tr)
  • Russian (ru)
  • Portuguese (Brazil) (pt-BR)
  • Korean (ko)
  • Japanese (ja)
  • Chinese (Simplified) (zh-CN)
  • Dutch (nl)
  • Polish (pl)
  • Arabic (ar)

Testing

  • ✅ Extension loads successfully in Chrome and Edge (Manifest V3)
  • ✅ Native host installs via the Settings toggle on Windows
  • ✅ Credential leak alerts appear in the desktop application's alerts panel
  • ✅ Tray dashboard displays:
    • Live health score
    • RTP status
    • Network sparkline
  • ✅ Health score breakdown is translated correctly across all 14 locales

Files Changed

browser-extension/                 # New browser extension (16 files)

tools/
├── install-native-host.js         # Native host installer
└── build-icons.js                 # Icon generator

src/main/
├── healthSummary.js               # Enhanced summary (RTP, firewall, network, last scan)
├── ipcHandlers.js                 # credential-leak:notify, browserExtension:installNativeHost
├── main.js                        # Protocol handler, second-instance logic
└── trayDashboard.js               # Passes enhanced summary

src/ui/
├── pages/
│   ├── trayDashboard.html         # New tray dashboard UI
│   └── trayDashboard.js           # Sparkline rendering and dashboard updates
└── js/pages/
    └── settings.js                # Browser Extension toggle and native host installer

src/i18n/locales/
└── *.json                         # Updated translations for all 14 locales

Summary by CodeRabbit

  • New Features

    • Added browser extension support for password breach checks, password-field indicators, popup checks, configurable settings, and desktop app connectivity.
    • Added native desktop integration for credential leak notifications and extension setup.
    • Enhanced the tray dashboard with real-time protection and firewall status, network activity charts, last-scan details, and Quick Scan.
    • Added single-instance app handling and protocol-link support.
  • Improvements

    • Expanded health and scan status translations across supported languages.

- Add IPC handler 'credential-leak:notify' for browser extension
- Add second-instance protocol handler (soterios://)
- Scaffold browser-extension/ with manifest v3, background, content, popup, options
- Native messaging host (native-host.js) bridges extension <-> desktop app
- Install script tools/install-native-host.js
- Add 'Browser Extension Integration' feature toggle in Settings
- IPC handler 'browserExtension:installNativeHost' installs native messaging host (Windows)
- When enabled, installs native host for Chrome/Edge
- i18n strings for install status
… sparkline

- healthSummary.js: Return RTP, firewall, network stats, last scan
- trayDashboard.html: Health score badge, RTP indicator, sparkline, quick scan button
- trayDashboard.js: Already passes enhanced summary
- Add missing health.label.* keys to match dashboard usage
- Translate English strings in it, tr, ru, pt-BR, ko, ja locales
- Fix key structure (health.label.* vs health.malware.label)
- Add health.reason.* keys (noScan, noThreats, threatsFound, scanToday, scanDaysAgo, diskLowSpace, diskNoVolumes, diskHealthy, memoryUsage, cpuLoad, uptimeToday, uptimeDays, uptimeWeeks, uptimeLong, rtpActive, rtpDisabled, firewallActive, firewallDisabled)
- Translate for: ar, de, es, fr, it, ja, ko, nl, pl, pt-BR, ru, tr
- Fix key structure mismatches (health.label.* vs health.malware.label)
- Spanish 'No threats found' now uses health.reason.noThreats
- Added health.reason.* keys for all locales
- Added health.label.* keys for all locales
- Fixed key structure (health.label.* vs health.malware.label)
- Translated all English strings in it, tr, ru, pt-BR, ko, ja, nl, de, pl, ar, hi, fr
- Spanish now has health.reason.noThreats for 'No threats found in the most recent scan'
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a Manifest V3 browser extension with HIBP checks, password-field indicators, options, and native messaging. Extends tray health data and UI with RTP, firewall, network, scan details, and quick scans. Adds single-instance protocol handling and updates health-related translations.

Changes

Browser extension UI and settings

Layer / File(s) Summary
Extension interfaces and password checks
browser-extension/manifest.json, browser-extension/popup.*, browser-extension/content.js
Adds the Manifest V3 extension, popup password checks through HIBP, password-field icons, breach badges, and desktop connectivity status.
Options and packaging
browser-extension/options.*, browser-extension/package.json, tools/build-icons.js
Adds persisted extension toggles, settings notifications, package scripts, and PNG icon generation.

Native messaging bridge and installation

Layer / File(s) Summary
Framed native hosts
browser-extension-host.js, browser-extension/native-host.js
Adds length-prefixed JSON messaging, desktop pipe forwarding, desktop launching, ping handling, and native-host error responses.
Host registration
browser-extension-host.json, browser-extension/native-host-manifest.json, browser-extension/native-host.bat, tools/install-native-host.js
Adds host manifests, a Windows launcher, extension-origin substitution, and OS-specific host registration.

Desktop event and feature wiring

Layer / File(s) Summary
Browser-extension installation and app activation
src/ui/js/pages/settings.js, src/main/main.js
Adds the browser-extension feature toggle, native-host installation flow, single-instance locking, protocol forwarding, and Windows protocol registration.

Tray health data and dashboard

Layer / File(s) Summary
Health summary data
src/main/healthSummary.js
Adds RTP, firewall, network-history, and latest-scan fields to the tray health summary.
Tray dashboard rendering
src/ui/pages/trayDashboard.html, src/ui/pages/trayDashboard.js
Adds RTP and firewall status, network sparklines, last-scan details, periodic refresh, and a Quick Scan action.

Localization

Layer / File(s) Summary
Health and extension translations
src/i18n/locales/*.json
Adds or updates browser-extension, tray, health, audit, scan-indicator, and health-reason strings across the listed locales.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant BrowserExtension
  participant NativeHost
  participant DesktopApp
  User->>BrowserExtension: Check password or report credential leak
  BrowserExtension->>NativeHost: Send framed native message
  NativeHost->>DesktopApp: Forward leak payload or launch app
  NativeHost-->>BrowserExtension: Return framed response
  BrowserExtension-->>User: Show breach or operation status
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the browser extension integration, system health enhancements, and localization updates in the pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch feature/browser-extension-integration
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/browser-extension-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread src/main/ipcHandlers.js Dismissed
@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Integrate MV3 browser extension via native host + expand tray health dashboard and i18n

✨ Enhancement ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add Manifest V3 browser extension for HIBP password breach checks and leak monitoring UI.
• Bridge extension to desktop via native messaging host, IPC handlers, and soterios:// protocol.
• Enhance tray dashboard with health score, RTP/firewall status, network sparkline, and quick scan.
• Align health score translations to health.label.* and add health.reason.* across locales.
Diagram

graph TD
  EXT["Browser Extension (MV3)"] --> HIBP["HIBP API"]
  EXT --> HOST["Native Messaging Host"] --> DESK["Desktop App (Electron main)"]
  UISET["Settings UI"] --> DESK
  TRAY["Tray Dashboard"] --> DESK
  I18N["Locale JSONs"] --> UISET
  I18N --> TRAY
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Never send raw passwords to the desktop app
  • ➕ Reduces risk if IPC/native-host traffic is intercepted or logged
  • ➕ Keeps the desktop-side alerting path independent of sensitive plaintext handling
  • ➕ Aligns with HIBP k-anonymity design (only hashes/prefixes are needed)
  • ➖ Requires refactoring payload contracts (extension → host → desktop)
  • ➖ May limit some future UX that wants password context (should be avoided anyway)
2. Consolidate native-host implementations into a single host + single manifest
  • ➕ Eliminates duplicated host logic/manifest formats (currently multiple host/manifest files)
  • ➕ Simplifies installer and troubleshooting (one code path)
  • ➕ Reduces packaging/maintenance overhead
  • ➖ May require reworking any existing packaging pipeline that expects separate host entrypoints
  • ➖ Needs careful migration if both hosts were intended for different transports (pipe vs protocol)
3. Use a local authenticated HTTP endpoint instead of native messaging (where feasible)
  • ➕ Easier cross-platform deployment than registry-based native messaging on Windows
  • ➕ Leverages existing desktop HTTP health endpoint patterns (already used for popup connectivity checks)
  • ➕ Potentially simpler debugging and versioning
  • ➖ Requires auth/CSRF hardening to avoid local-web attacks
  • ➖ May be blocked by browser extension host permissions/CORS constraints
  • ➖ Not a drop-in replacement for native messaging install flows

Recommendation: Keep the native-messaging approach (it’s the right primitive for extension↔desktop), but adjust the data contract to avoid transmitting plaintext passwords (send only SHA-1 prefix/suffix or just {count, hashPrefix}). Also consider consolidating the two host scripts/manifests into one to reduce operational complexity and support burden.

Files changed (40) +1824 / -200 · 4 not counted

Enhancement (19) +1152 / -18
browser-extension-host.jsAdd Node-based native host that forwards leak events to desktop pipe +94/-0

Add Node-based native host that forwards leak events to desktop pipe

• Introduces a stdio native messaging host that reads length-prefixed JSON messages from the extension and forwards credential leak notifications to a Windows named pipe. Adds basic PING handling and host-side logging to stderr.

browser-extension-host.js

background.jsInitialize extension default settings on install +3/-0

Initialize extension default settings on install

• Sets a default sync-storage value on extension installation to enable external lookups by default.

browser-extension/background.js

content.jsAdd content script for password field detection and inline results +142/-0

Add content script for password field detection and inline results

• Implements DOM scanning + mutation observing for password inputs, injects an icon into detected fields, and triggers background checks via runtime messaging. Renders temporary inline result badges and responds to settings updates to show/hide UI.

browser-extension/content.js

icon.svgAdd source SVG icon for extension branding +11/-0

Add source SVG icon for extension branding

• Adds an SVG icon used as the source for generating PNG icons at required sizes.

browser-extension/icons/icon.svg

icon128.pngAdd 128px PNG icon asset not counted

Add 128px PNG icon asset

• Adds a 128px PNG icon for the extension manifest and store requirements.

browser-extension/icons/icon128.png

icon16.pngAdd 16px PNG icon asset not counted

Add 16px PNG icon asset

• Adds a 16px PNG icon for the extension toolbar/action UI.

browser-extension/icons/icon16.png

icon32.pngAdd 32px PNG icon asset not counted

Add 32px PNG icon asset

• Adds a 32px PNG icon for extension UI surfaces.

browser-extension/icons/icon32.png

icon48.pngAdd 48px PNG icon asset not counted

Add 48px PNG icon asset

• Adds a 48px PNG icon for extension UI surfaces.

browser-extension/icons/icon48.png

native-host.jsAdd stdio native host that can launch the desktop via soterios:// +113/-0

Add stdio native host that can launch the desktop via soterios://

• Implements a length-prefixed JSON message reader/writer and supports commands (CREDENTIAL_LEAK, PING, OPEN_APP). Launches the desktop app via platform-appropriate mechanisms (cmd/open/xdg-open) and responds with status messages.

browser-extension/native-host.js

options.htmlAdd extension options UI for breach monitoring and desktop notifications +75/-0

Add extension options UI for breach monitoring and desktop notifications

• Adds a styled options page with toggles for HIBP checks, auto-checking, icon display, and desktop leak notifications. Includes user guidance for native host installation.

browser-extension/options.html

options.jsPersist and broadcast extension settings via sync storage +50/-0

Persist and broadcast extension settings via sync storage

• Loads/saves options to chrome.storage.sync and broadcasts SETTINGS_UPDATED to extension components. Provides accessible toggle interaction (click/keyboard).

browser-extension/options.js

popup.htmlAdd extension popup UI for manual password checks and desktop status +63/-0

Add extension popup UI for manual password checks and desktop status

• Implements a popup UI for entering a password to check, showing safe/pwned results, and displaying desktop connectivity status with a link to settings.

browser-extension/popup.html

popup.jsImplement HIBP range checks and periodic desktop connectivity probe +85/-0

Implement HIBP range checks and periodic desktop connectivity probe

• Computes SHA-1 in the browser, queries HIBP range endpoint, and renders results. Periodically checks a local desktop health endpoint and opens the options page on request.

browser-extension/popup.js

healthSummary.jsExpand tray health summary with RTP, firewall, network, and last scan info +48/-2

Expand tray health summary with RTP, firewall, network, and last scan info

• Extends the tray summary payload to include RTP enabled state (from settings), firewall state (via netsh), recent network traffic history for sparklines, and last scan metadata. Keeps score/detail output while enriching the return structure.

src/main/healthSummary.js

ipcHandlers.jsAdd IPC for credential leak notifications and native-host installation +48/-0

Add IPC for credential leak notifications and native-host installation

• Adds an IPC handler to record a 'Credential Leak Detected' alert (including SHA-1 prefix) and emit it on the event bus. Adds a Windows-only IPC handler that registers the native messaging host for Chrome/Edge by writing registry keys and patching allowed_origins with an extension ID.

src/main/ipcHandlers.js

main.jsAdd single-instance handling and register soterios:// protocol (Windows) +20/-0

Add single-instance handling and register soterios:// protocol (Windows)

• Enforces a single-instance lock and focuses the existing window when a second instance is launched, forwarding soterios:// URLs to the renderer. Registers the custom soterios protocol handler on Windows to support extension/native-host app launching.

src/main/main.js

settings.jsAdd Browser Extension Integration feature toggle with installer flow +42/-0

Add Browser Extension Integration feature toggle with installer flow

• Adds a Settings toggle for browser extension integration and triggers native host installation via IPC when enabled, with status text updates. Also appears to duplicate the networkPerimeterMap toggle block in the rendered HTML, which may be unintended.

src/ui/js/pages/settings.js

trayDashboard.htmlRedesign tray dashboard with RTP badge, network sparkline, and quick scan +192/-16

Redesign tray dashboard with RTP badge, network sparkline, and quick scan

• Expands tray dashboard UI styling and layout, adds an RTP status badge, network sparkline canvas, and a Quick Scan button. Updates the embedded script to render the richer summary payload and to handle scan triggering and resizing.

src/ui/pages/trayDashboard.html

trayDashboard.jsAdd standalone tray dashboard renderer script (summary + sparkline + actions) +166/-0

Add standalone tray dashboard renderer script (summary + sparkline + actions)

• Introduces a separate JS module to render tray summary updates including score coloring, RTP/firewall statuses, network sparkline, and last scan display. Adds handlers for quick scan/open actions and periodic summary refresh.

src/ui/pages/trayDashboard.js

Documentation (14) +529 / -182
ar.jsonAdd health.label.* and health.reason.* keys (Arabic) +38/-12

Add health.label.* and health.reason.* keys (Arabic)

• Extends Arabic locale to include new health label keys and the health.reason.* message set required by the dashboard. Also localizes scan indicator strings and fixes punctuation/formatting around firewall detail key.

src/i18n/locales/ar.json

de.jsonAdd health.label.* and health.reason.* keys (German) +33/-7

Add health.label.* and health.reason.* keys (German)

• Adds missing health label keys and health.reason.* strings; updates some scan indicator strings and fixes trailing comma formatting for firewall detail key.

src/i18n/locales/de.json

en.jsonAdd settings + tray strings and health.label.* keys (English) +23/-0

Add settings + tray strings and health.label.* keys (English)

• Adds Settings strings for browser extension integration (including install statuses), tray menu/dashboard strings, and introduces health.label.* keys to match the dashboard’s expected structure.

src/i18n/locales/en.json

es.jsonAdd health.label.* keys and missing health.reason.* strings (Spanish) +17/-1

Add health.label.* keys and missing health.reason.* strings (Spanish)

• Introduces health.label.* keys and fills in missing health.reason.* entries for scan/threat messaging. Also reorganizes/duplicates some health.* label/reason keys to align with dashboard usage.

src/i18n/locales/es.json

fr.jsonAdd health.label.* and health.reason.* keys; translate audit strings (French) +67/-41

Add health.label.* and health.reason.* keys; translate audit strings (French)

• Adds health label keys and the full health.reason.* set. Also replaces many English audit check strings with French translations to improve coverage consistency.

src/i18n/locales/fr.json

hi.jsonAdd health.label.* keys and localized malware strings (Hindi) +13/-1

Add health.label.* keys and localized malware strings (Hindi)

• Adds health label keys and localizes malware scan strings that were previously English. Keeps scan recency keys present for dashboard compatibility.

src/i18n/locales/hi.json

it.jsonTranslate health/audit keys and add health.reason.* (Italian) +95/-70

Translate health/audit keys and add health.reason.* (Italian)

• Replaces prior English health/audit strings with Italian translations, adds health.label.* keys, and appends health.reason.* strings for dashboard explanations.

src/i18n/locales/it.json

ja.jsonAdd health.label.* and health.reason.* keys; localize health strings (Japanese) +46/-20

Add health.label.* and health.reason.* keys; localize health strings (Japanese)

• Adds health.label.* keys and localizes health strings that were previously English. Introduces health.reason.* entries for scan/disk/resource/RTP/firewall explanations.

src/i18n/locales/ja.json

ko.jsonAdd localized health label strings and scan recency strings (Korean) +28/-7

Add localized health label strings and scan recency strings (Korean)

• Adds translations for health label and health detail strings and localizes crack time centuries. Expands scan recency and disk/memory/load/uptime strings needed by the health dashboard.

src/i18n/locales/ko.json

nl.jsonAdd health.label.* and health.reason.* keys; translate some recommendations (Dutch) +41/-11

Add health.label.* and health.reason.* keys; translate some recommendations (Dutch)

• Adds health label keys and health.reason.* strings and translates multiple audit recommendation strings. Also localizes scan indicator strings previously in English.

src/i18n/locales/nl.json

pl.jsonAdd health.label.* and health.reason.* keys (Polish) +32/-2

Add health.label.* and health.reason.* keys (Polish)

• Adds health label keys and health.reason.* strings so the health system can render consistent labels and explanations in Polish.

src/i18n/locales/pl.json

pt-BR.jsonLocalize health strings and add health.label.* keys (pt-BR) +26/-1

Localize health strings and add health.label.* keys (pt-BR)

• Replaces English health strings with Brazilian Portuguese translations and adds missing health label keys expected by the dashboard.

src/i18n/locales/pt-BR.json

ru.jsonLocalize health strings and add health.label.* keys (Russian) +28/-7

Localize health strings and add health.label.* keys (Russian)

• Adds translations for health strings and introduces health.label.* keys for dashboard alignment. Also localizes crack time centuries.

src/i18n/locales/ru.json

tr.jsonAdd health.label.* and health.reason.* keys (Turkish) +42/-2

Add health.label.* and health.reason.* keys (Turkish)

• Adds health label keys, localizes health component strings, and adds health.reason.* explanations. Note: the diff shows a duplicated closing brace which should be validated for JSON correctness.

src/i18n/locales/tr.json

Other (7) +143 / -0
browser-extension-host.jsonAdd native messaging host manifest template (allowed_origins placeholder) +9/-0

Add native messaging host manifest template (allowed_origins placeholder)

• Adds a native messaging host manifest with placeholder extension ID and executable path configuration. Intended to be registered with the browser for native messaging.

browser-extension-host.json

manifest.jsonAdd MV3 extension manifest with popup/options and HIBP permissions +22/-0

Add MV3 extension manifest with popup/options and HIBP permissions

• Defines a Manifest V3 extension with popup and options page, storage permission, and host permissions for the HIBP password range endpoint. Registers the service worker background script and icon assets.

browser-extension/manifest.json

native-host-manifest.jsonAdd native-host manifest used by installer (bat launcher) +9/-0

Add native-host manifest used by installer (bat launcher)

• Adds a manifest pointing at a batch launcher with an <EXTENSION_ID> placeholder for allowed origins, designed for registration by the desktop/installer.

browser-extension/native-host-manifest.json

native-host.batAdd Windows batch launcher for the native host +6/-0

Add Windows batch launcher for the native host

• Adds a batch file that sets NODE_PATH and launches the Node native-host script for Chrome/Edge native messaging.

browser-extension/native-host.bat

package.jsonAdd extension packaging and icon build scripts +14/-0

Add extension packaging and icon build scripts

• Adds build tooling to generate icons and package the extension into a zip, plus a script to install the native host. Introduces svgexport as a dev dependency.

browser-extension/package.json

build-icons.jsAdd script to generate PNG icons from SVG +22/-0

Add script to generate PNG icons from SVG

• Adds a Node script that uses svgexport via npx to generate required icon sizes into the extension icons directory.

tools/build-icons.js

install-native-host.jsAdd cross-platform native host installer (Windows registry + *nix manifest copy) +61/-0

Add cross-platform native host installer (Windows registry + *nix manifest copy)

• Adds an installer script that patches allowed_origins with the configured extension ID and registers the native host. On Windows it writes HKCU registry keys for Chrome/Edge; on macOS/Linux it copies the manifest into the standard NativeMessagingHosts directory.

tools/install-native-host.js

@qodo-code-review

qodo-code-review Bot commented Jul 22, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. WeakMap cleanup crashes ✓ Resolved 🐞 Bug ≡ Correctness
Description
browser-extension/content.js stores icons in a WeakMap but later calls forEach() and clear(), which
do not exist on WeakMap. When SETTINGS_UPDATED disables showIcon, the handler will throw a TypeError
and fail to remove icons / apply the update.
Code

browser-extension/content.js[R133-138]

+chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
+  if (msg.type === 'SETTINGS_UPDATED') {
+    if (!msg.settings.showIcon) {
+      passwordFields.forEach((icon, input) => icon.remove());
+      passwordFields.clear();
+    } else {
Evidence
The file initializes passwordFields as a WeakMap, then later calls passwordFields.forEach(...)
and passwordFields.clear(), which are not WeakMap methods and will throw when executed.

browser-extension/content.js[6-9]
browser-extension/content.js[133-141]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`passwordFields` is a `WeakMap`, but the settings update handler uses `forEach()` and `clear()`, which will throw at runtime.

### Issue Context
You need an iterable collection to remove all injected icons when `showIcon` is disabled.

### Fix
Replace `WeakMap` with `Map`, or keep a separate `Set` of created icon elements/inputs for iteration, and ensure cleanup removes icons and any related listeners.

### Fix Focus Areas
- browser-extension/content.js[7-8]
- browser-extension/content.js[133-140]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Missing CHECK_PASSWORD handler 🐞 Bug ≡ Correctness
Description
browser-extension/content.js sends a CHECK_PASSWORD message to the extension runtime, but
browser-extension/background.js never registers runtime.onMessage to handle it. Inline icon checks
will fail because there is no receiver for the message.
Code

browser-extension/content.js[R43-48]

+  try {
+    const result = await chrome.runtime.sendMessage({ type: 'CHECK_PASSWORD', password });
+    showResult(el, result);
+  } catch (err) {
+    console.error('[Soterios] Check failed:', err);
+  }
Evidence
The sender exists in content.js, but background.js only sets initial storage on install and
provides no onMessage listener, so the CHECK_PASSWORD message has no handler.

browser-extension/content.js[35-48]
browser-extension/background.js[1-3]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`content.js` uses `chrome.runtime.sendMessage({ type: 'CHECK_PASSWORD', ... })`, but the service worker does not handle this message type.

### Issue Context
Without a `chrome.runtime.onMessage` listener in `background.js`, the sendMessage call will reject/fail and no result badge can be shown.

### Fix
Add `chrome.runtime.onMessage.addListener(...)` in `background.js` to handle `CHECK_PASSWORD` (perform the HIBP k-anonymity call and respond), or move the HIBP check into `content.js` / `popup.js` consistently.

### Fix Focus Areas
- browser-extension/background.js[1-3]
- browser-extension/content.js[43-48]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Content script not injected ✓ Resolved 🐞 Bug ≡ Correctness
Description
browser-extension/manifest.json does not declare any content_scripts entry, so
browser-extension/content.js will never run on webpages. Password field detection and inline breach
badges therefore cannot work.
Code

browser-extension/manifest.json[R1-22]

+{
+  "manifest_version": 3,
+  "name": "Soterios Credential Safety",
+  "version": "1.2.1",
+  "description": "Password breach checker and credential safety companion for Soterios",
+  "icons": {
+    "16": "icons/icon16.png",
+    "32": "icons/icon32.png",
+    "48": "icons/icon48.png",
+    "128": "icons/icon128.png"
+  },
+  "action": {
+    "default_popup": "popup.html",
+    "default_title": "Soterios Credential Safety"
+  },
+  "options_page": "options.html",
+  "permissions": ["storage"],
+  "host_permissions": ["https://api.pwnedpasswords.com/*"],
+  "background": {
+    "service_worker": "background.js"
+  }
+}
Evidence
The manifest contains only action/options/storage/host_permissions/background; there is no content
script registration, while content.js is present and expects to run in web pages.

browser-extension/manifest.json[1-22]
browser-extension/content.js[1-142]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The MV3 manifest does not register `content.js` as a content script, so it is never injected.

### Issue Context
The PR adds `browser-extension/content.js` with DOM scanning and UI injection, but the manifest only registers a service worker.

### Fix
Add a `content_scripts` section that injects `content.js` on the intended URL match patterns (likely `<all_urls>`), and ensure any referenced resources (icons) are available.

### Fix Focus Areas
- browser-extension/manifest.json[1-22]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (3)
4. Duplicate toggle IDs rendered ✓ Resolved 🐞 Bug ≡ Correctness
Description
src/ui/js/pages/settings.js renders the Network Perimeter Map toggle twice using the same id
networkPerimeterMapToggle. Duplicate IDs break DOM uniqueness and will cause only the first toggle
to be wired by querySelector(), leaving the second toggle inconsistent/non-functional.
Code

src/ui/js/pages/settings.js[R122-128]

+<div class="toggle-row">
+            <div>
+              <div class="toggle-label">${escapeHtml(t('settings.networkPerimeterMap.label'))}</div>
+              <div class="toggle-desc">${escapeHtml(t('settings.networkPerimeterMap.desc'))}</div>
+            </div>
+            <label class="toggle"><input type="checkbox" id="networkPerimeterMapToggle" ${settings.features.networkPerimeterMap !== false ? 'checked' : ''} /><span class="toggle-slider"></span></label>
+          </div>
Evidence
The HTML template includes two consecutive blocks with the same checkbox id, so only the first
element will be found/wired by subsequent querySelector usage.

src/ui/js/pages/settings.js[115-137]
src/ui/js/pages/settings.js[399-402]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The settings page contains two identical toggle rows for Network Perimeter Map, both using `id="networkPerimeterMapToggle"`.

### Issue Context
Event handlers are attached via `container.querySelector('#networkPerimeterMapToggle')`, which only returns the first match.

### Fix
Remove the duplicated block (or give it the intended distinct setting/id if it was meant to be another feature).

### Fix Focus Areas
- src/ui/js/pages/settings.js[115-136]
- src/ui/js/pages/settings.js[384-401]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Turkish locale JSON broken ✓ Resolved 🐞 Bug ≡ Correctness
Description
src/i18n/locales/tr.json ends with an extra closing brace, making the file invalid JSON.
Parsing/loading the Turkish locale will fail.
Code

src/i18n/locales/tr.json[R917-919]

+  "health.reason.firewallDisabled": "Windows Güvenlik Duvarı devre dışı."
+}
}
Evidence
The file ends with } on line 918 and another } on line 919, which will cause JSON.parse to throw
due to trailing tokens.

src/i18n/locales/tr.json[899-919]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The Turkish locale file has two closing braces at the end, which is invalid JSON.

### Issue Context
Locale JSON files are typically parsed at runtime; a syntax error will break loading for that locale (and may break i18n initialization depending on loader behavior).

### Fix
Remove the extra trailing `}` and run a JSON validation pass across all locale files.

### Fix Focus Areas
- src/i18n/locales/tr.json[914-919]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. Installer doesn't persist extension ID ✓ Resolved 🐞 Bug ≡ Correctness
Description
The Windows native-host install flow replaces <EXTENSION_ID> in the parsed manifest object but never
writes the updated manifest back to disk before registry registration. Chrome/Edge will read the
unchanged file (still containing <EXTENSION_ID>) and reject native host connections.
Code

src/main/ipcHandlers.js[R937-942]

+    const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
+    const extId = process.env.SOTERIOS_EXT_ID || 'YOUR_EXTENSION_ID_HERE';
+    manifest.allowed_origins = [manifest.allowed_origins[0].replace('<EXTENSION_ID>', extId)];
+    const regPath = `HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\${manifest.name}`;
+    const regCmd = `reg add "${regPath}" /ve /t REG_SZ /d "${manifestPath.replace(/\\/g, '\\\\')}" /f`;
+    try {
Evidence
Both installers perform the string replacement on the parsed object but never persist it on Windows;
the checked-in manifest still has <EXTENSION_ID>, yet the registry is pointed at that file path.

src/main/ipcHandlers.js[923-951]
tools/install-native-host.js[25-46]
browser-extension/native-host-manifest.json[1-9]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The installer mutates `manifest.allowed_origins` in memory, but registers the path to the on-disk `native-host-manifest.json` without writing the updated JSON. As a result, the registered manifest still contains the placeholder extension ID.

### Issue Context
This occurs in the desktop IPC installer, and similarly in `tools/install-native-host.js` on Windows.

### Fix
On Windows:
1) Write a generated manifest file with the resolved extension ID (e.g., `native-host-manifest.generated.json`) and register that path, OR overwrite `native-host-manifest.json` with the updated JSON.
2) Ensure both Chrome and Edge registry entries point to the generated/updated manifest.

### Fix Focus Areas
- src/main/ipcHandlers.js[937-946]
- tools/install-native-host.js[25-33]
- browser-extension/native-host-manifest.json[1-9]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

7. Tray sparkline data mismatch ✓ Resolved 🐞 Bug ≡ Correctness
Description
src/ui/pages/trayDashboard.html only draws the sparkline if summary.network.rx/tx arrays exist, but
src/main/healthSummary.js produces summary.network as {rxKBs, txKBs, history}. The network sparkline
will never render with the current backend payload shape.
Code

src/ui/pages/trayDashboard.html[R217-220]

+      // Network sparkline
+      if (summary.network?.rx?.length || summary.network?.tx?.length) {
+        drawSparkline(summary.network.rx, summary.network.tx);
+      }
Evidence
The tray HTML checks for network.rx/network.tx, but healthSummary only fills
rxKBs/txKBs/history, so the condition never becomes true and drawSparkline is never called.

src/ui/pages/trayDashboard.html[195-221]
src/main/healthSummary.js[42-52]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The tray UI expects `summary.network.rx` and `summary.network.tx` arrays, but the backend returns `rxKBs`, `txKBs`, and a combined `history` array.

### Issue Context
This prevents the drawSparkline path from executing, so the sparkline stays empty.

### Fix
Either:
- Change the tray UI to draw using `summary.network.history` (single-series), OR
- Change `getTrayHealthSummary()` to return `{ rx: number[], tx: number[] }` series matching the UI.

### Fix Focus Areas
- src/ui/pages/trayDashboard.html[217-220]
- src/main/healthSummary.js[42-51]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. NativeMessaging permission absent ✓ Resolved 🐞 Bug ≡ Correctness
Description
browser-extension/manifest.json only requests the storage permission and does not include
nativeMessaging. Any future/expected use of chrome.runtime.connectNative/sendNativeMessage for
desktop integration will be blocked by the manifest.
Code

browser-extension/manifest.json[R16-18]

+  "options_page": "options.html",
+  "permissions": ["storage"],
+  "host_permissions": ["https://api.pwnedpasswords.com/*"],
Evidence
The manifest permissions list contains only storage, which is insufficient for native messaging
APIs.

browser-extension/manifest.json[16-21]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The manifest omits the `nativeMessaging` permission.

### Issue Context
The PR introduces a native messaging host and desktop integration flow; those require the extension to have `nativeMessaging` permission to call native messaging APIs.

### Fix
Add `"nativeMessaging"` to `permissions` in `manifest.json` (and ensure the extension actually uses native messaging APIs where intended).

### Fix Focus Areas
- browser-extension/manifest.json[16-19]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Shell-spawn command injection risk ✓ Resolved 🐞 Bug ⛨ Security
Description
browser-extension/native-host.js builds a shell command string using DESKTOP_APP from the
environment and executes it with shell:true. If that environment variable is tampered with, it can
result in arbitrary command execution when the native host launches the desktop app.
Code

browser-extension/native-host.js[R60-68]

+  return new Promise((resolve, reject) => {
+    const url = process.platform === 'win32'
+      ? `cmd /c start "" "${DESKTOP_APP}"`
+      : process.platform === 'darwin'
+        ? `open -a "Soterios"`
+        : `xdg-open "${DESKTOP_APP}"`;
+
+    desktopProc = spawn(url, { shell: true, detached: true });
+    desktopProc.unref();
Evidence
DESKTOP_APP is read from process.env and embedded into a command string that is executed with shell
parsing enabled, which is the classic pattern that enables command injection if the interpolated
value is attacker-controlled.

browser-extension/native-host.js[10-12]
browser-extension/native-host.js[60-69]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The native host constructs a platform-specific command string and runs it via `spawn(..., { shell: true })`, interpolating `DESKTOP_APP` (from env).

### Issue Context
Even if `DESKTOP_APP` is usually benign, treating it as command text expands the attack surface; native hosts should avoid shell parsing where possible.

### Fix
Use `spawn` with an executable + args array (no `shell:true`) and validate/escape the target. On Windows, prefer `cmd.exe` as the executable with fixed args, and pass the URL as a single argument (not embedded in a larger command string).

### Fix Focus Areas
- browser-extension/native-host.js[10-11]
- browser-extension/native-host.js[60-68]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

10. Fetch timeout option ignored ✓ Resolved 🐞 Bug ☼ Reliability
Description
browser-extension/popup.js passes a non-standard timeout option to fetch(), which will be ignored by
browsers. The localhost health check can remain pending longer than intended, delaying popup status
updates.
Code

browser-extension/popup.js[R41-44]

+async function checkConnection() {
+  try {
+    const resp = await fetch('http://localhost:17234/api/health', { method: 'GET', timeout: 1000 });
+    if (resp.ok) {
Evidence
The code passes { timeout: 1000 } to fetch(), which is not a standard fetch option, so no timeout
is actually enforced by this code path.

browser-extension/popup.js[41-52]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`fetch()` does not implement a `timeout` option, so the request is not guaranteed to abort after 1s.

### Issue Context
This affects the popup's app-connection status check.

### Fix
Use `AbortController` + `setTimeout` to abort the fetch after a fixed duration, and clear the timer in `finally`.

### Fix Focus Areas
- browser-extension/popup.js[41-51]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment thread browser-extension/content.js
Comment thread browser-extension/manifest.json
Comment on lines +43 to +48
try {
const result = await chrome.runtime.sendMessage({ type: 'CHECK_PASSWORD', password });
showResult(el, result);
} catch (err) {
console.error('[Soterios] Check failed:', err);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Missing check_password handler 🐞 Bug ≡ Correctness

browser-extension/content.js sends a CHECK_PASSWORD message to the extension runtime, but
browser-extension/background.js never registers runtime.onMessage to handle it. Inline icon checks
will fail because there is no receiver for the message.
Agent Prompt
### Issue description
`content.js` uses `chrome.runtime.sendMessage({ type: 'CHECK_PASSWORD', ... })`, but the service worker does not handle this message type.

### Issue Context
Without a `chrome.runtime.onMessage` listener in `background.js`, the sendMessage call will reject/fail and no result badge can be shown.

### Fix
Add `chrome.runtime.onMessage.addListener(...)` in `background.js` to handle `CHECK_PASSWORD` (perform the HIBP k-anonymity call and respond), or move the HIBP check into `content.js` / `popup.js` consistently.

### Fix Focus Areas
- browser-extension/background.js[1-3]
- browser-extension/content.js[43-48]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread browser-extension/manifest.json
Comment thread src/main/ipcHandlers.js Outdated
Comment thread src/ui/js/pages/settings.js Outdated
Comment thread src/ui/pages/trayDashboard.html
Comment thread src/i18n/locales/tr.json
Comment thread browser-extension/popup.js
Comment thread browser-extension/native-host.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

🟠 Major comments (21)
src/ui/pages/trayDashboard.html-218-220 (1)

218-220: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Network sparkline reads fields that getTrayHealthSummary never emits.

This branch checks summary.network.rx/summary.network.tx and passes them to drawSparkline, but getTrayHealthSummary (src/main/healthSummary.js Lines 43-52) returns network.rxKBs, network.txKBs, and network.history — there is no rx/tx array. As a result this condition is always false and the sparkline never renders. Additionally, drawSparkline's format() treats inputs as B/s while history is already in KB/s, so units would be wrong even if wired correctly.

Align the consumer to the producer's network.history (single series) or extend getTrayHealthSummary to also emit separate rx/tx arrays.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/pages/trayDashboard.html` around lines 218 - 220, Update the network
sparkline branch in the tray dashboard to consume the `network.history` data
emitted by `getTrayHealthSummary` instead of nonexistent `rx`/`tx` fields,
adapting the `drawSparkline` call to its single-series shape. Ensure
`drawSparkline` formats these history values as KB/s rather than B/s, preserving
the producer’s units.
tools/build-icons.js-19-21 (1)

19-21: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fail the build when icon generation fails.

The catch block only logs the error, so the script exits successfully and packaging can emit an extension missing required icon sizes. Propagate the failure or set a non-zero exit code after the loop.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/build-icons.js` around lines 19 - 21, Update the catch block in the
icon generation loop to preserve the failure status by propagating the error or
setting a non-zero process exit code after generation fails. Keep the existing
error logging, and ensure the build cannot exit successfully when any required
icon generation fails.
browser-extension/package.json-7-9 (1)

7-9: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix script paths relative to browser-extension/package.json.

npm runs these scripts with browser-extension as the working directory, but the helpers are tools/build-icons.js and tools/install-native-host.js at the repository root. Lines 7 and 9 therefore resolve to nonexistent paths, while line 8 attempts to enter browser-extension again.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/package.json` around lines 7 - 9, Update the build:icons,
package, and install:host scripts in package.json to resolve helper paths from
the repository root when invoked with browser-extension as the working
directory. Remove the redundant directory change in package and reference the
root-level tools scripts using the appropriate parent-relative paths.
src/i18n/locales/pt-BR.json-807-819 (1)

807-819: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove the duplicate health keys from all affected locales. These additions trigger Biome’s duplicate-object-key errors and make the effective translation depend on declaration order.

  • src/i18n/locales/pt-BR.json#L807-L819: remove the earlier copies or merge these Portuguese values into the existing keys.
  • src/i18n/locales/ru.json#L803-L815: remove the earlier copies or merge these Russian values into the existing keys.
  • src/i18n/locales/tr.json#L803-L815: remove the earlier copies or merge these Turkish values into the existing keys.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/locales/pt-BR.json` around lines 807 - 819, Remove or merge the
duplicate health translation keys in src/i18n/locales/pt-BR.json lines 807-819,
src/i18n/locales/ru.json lines 803-815, and src/i18n/locales/tr.json lines
803-815. Preserve each locale’s intended translations under a single definition
for every health.scanRecency, health.disk, health.memory, health.load,
health.uptime, health.rtp, and health.firewall key.

Source: Linters/SAST tools

browser-extension/package.json-8-8 (1)

8-8: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Keep the release archive limited to extension files.

After fixing the working-directory issue, zip -r ... . will include node_modules created for the build dependency. It also requires a Unix zip executable, which is not available in standard Windows environments. Use an explicit staging/file list and a cross-platform archiver.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/package.json` at line 8, Update the package.json “package”
script to stage only browser-extension release files, explicitly excluding build
dependencies such as node_modules, icons/*.svg, tools, and other non-extension
artifacts. Replace the Unix-specific zip command with a cross-platform archiver
already supported by the project, while preserving the icon build and generated
soterios-extension.zip output.
tools/build-icons.js-17-17 (1)

17-17: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Avoid npx -y in the icon build step
svgexport isn’t declared in package.json, so this can download and execute a registry package during packaging. The catch also lets icon generation fail silently, which can ship an incomplete build. Use a declared local binary and fail the build on conversion errors.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/build-icons.js` at line 17, Update the icon generation flow around the
svgexport execSync call to use a declared local svgexport dependency/binary
instead of npx -y, and make conversion errors propagate rather than being
swallowed by the catch handler. Ensure the build exits unsuccessfully when any
icon conversion fails.

Source: Linters/SAST tools

src/i18n/locales/ja.json-765-778 (1)

765-778: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove duplicate locale-key declarations across both files.

The new translation blocks redeclare keys that already exist, triggering Biome’s duplicate-key errors and making effective values depend on parser behavior.

  • src/i18n/locales/ja.json#L765-L778: remove the duplicate health.malware.high definition and retain one canonical value.
  • src/i18n/locales/ko.json#L804-L816: remove the duplicated health subkeys and retain one canonical definition for each key.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/locales/ja.json` around lines 765 - 778, Remove the duplicate
locale-key declarations from src/i18n/locales/ja.json lines 765-778, keeping one
canonical health.malware.high value. Also remove the duplicated health subkeys
from src/i18n/locales/ko.json lines 804-816, retaining one canonical definition
for each key so both locale files contain no duplicate keys.

Source: Linters/SAST tools

src/ui/js/pages/settings.js-122-128 (1)

122-128: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove the duplicate Network Perimeter Map toggle.

This duplicates #networkPerimeterMapToggle from Lines 115-121. Only the first duplicate receives the event listener at Line 401, so the second visible control does nothing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/js/pages/settings.js` around lines 122 - 128, Remove the duplicate
toggle-row containing `#networkPerimeterMapToggle` from the settings markup,
preserving the earlier instance that receives the event listener. Keep the
existing label, description, and functional toggle behavior represented by the
first control.
browser-extension/manifest.json-18-18 (1)

18-18: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Authorize the localhost health endpoint. browser-extension/popup.js fetches http://localhost:17234/api/health, but browser-extension/manifest.json only grants https://api.pwnedpasswords.com/*. Add http://localhost:17234/* so the popup can detect the desktop app when it is running.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/manifest.json` at line 18, Add the localhost origin pattern
http://localhost:17234/* to the manifest host_permissions alongside the existing
API permission, enabling popup.js to reach the desktop app health endpoint
without changing the current permission.
browser-extension/manifest.json-17-20 (1)

17-20: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Register the content script and add a localhost host permission.

  • browser-extension/content.js is never loaded because manifest.json has no content_scripts entry, so the password-field overlay cannot appear.
  • browser-extension/popup.js calls http://localhost:17234/api/health, but the manifest only grants https://api.pwnedpasswords.com/*; add a matching localhost host permission or move that check behind the background script.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/manifest.json` around lines 17 - 20, Update the manifest
configuration to register browser-extension/content.js as a content script on
the intended pages so the password-field overlay loads, and add host permission
coverage for http://localhost:17234/* to support the health request made by
browser-extension/popup.js. Preserve the existing storage and Have I Been Pwned
permissions.
src/main/ipcHandlers.js-937-941 (1)

937-941: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Persist the resolved native-host manifest before registering it. manifest.allowed_origins is only updated in memory, but reg add still points Chrome/Edge at browser-extension/native-host-manifest.json, which keeps <EXTENSION_ID> on disk. Write the updated manifest to a writable installed-host path and register that file instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/ipcHandlers.js` around lines 937 - 941, Update the native-host
registration flow around manifest parsing in ipcHandlers.js to persist the
resolved manifest, including the substituted extension ID, to a writable
installed-host path before invoking reg add. Use that persisted file path in the
registry command instead of the source
browser-extension/native-host-manifest.json path, while preserving the existing
manifest name and allowed_origins handling.
browser-extension/content.js-94-99 (1)

94-99: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fix the icon cleanup path. passwordFields is a WeakMap, so the SETTINGS_UPDATED handler will throw on forEach/clear. The blur handler also only removes the icon DOM node; it leaves data-soterios-id and the window listeners behind, so the field can’t be re-iconed and the listeners accumulate. Use a shared cleanup helper that removes the listeners, deletes the entry, and clears the dataset key.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/content.js` around lines 94 - 99, Replace the inline blur
cleanup near the passwordFields.set call with a shared cleanup helper that
removes the icon, unregisters the scroll and resize listeners, deletes the input
from the passwordFields WeakMap, and clears its data-soterios-id attribute.
Update the SETTINGS_UPDATED cleanup path to use this helper instead of calling
forEach or clear on passwordFields, while preserving one-time cleanup behavior.
browser-extension/popup.js-13-20 (1)

13-20: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Treat HIBP HTTP errors as failures
A non-OK response still gets parsed and can fall through to return 0, so rate limits or outages show as “Not found in breaches.” Check resp.ok before reading the body.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/popup.js` around lines 13 - 20, Update the HIBP fetch flow
around resp and the response parsing loop to check resp.ok immediately after
fetch and before calling resp.text(). Treat non-OK responses as failures using
the existing error-handling path, rather than parsing the body or returning 0;
preserve the current suffix matching and successful-response behavior.
browser-extension/background.js-1-3 (1)

1-3: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Add a CHECK_PASSWORD message handler in browser-extension/background.js.
browser-extension/content.js sends { type: 'CHECK_PASSWORD' }, but this worker only initializes storage. Return { pwned, count } here so showResult receives the expected payload.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/background.js` around lines 1 - 3, Add a CHECK_PASSWORD
message handler in the background worker alongside the existing onInstalled
listener. Handle messages sent by content.js, perform the password lookup using
the existing project mechanism, and respond asynchronously with an object
containing the expected pwned and count fields so showResult receives the
correct payload.
src/ui/js/pages/settings.js-422-424 (1)

422-424: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Rollback the stored browserExtension flag on rejection

If window.api.invoke('browserExtension:installNativeHost') rejects after browserExtension: true is already saved, the catch only resets the checkbox. Persist browserExtension: false there too so reopening settings doesn’t still show the integration enabled.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/js/pages/settings.js` around lines 422 - 424, Update the catch handler
for the browserExtension:installNativeHost invocation to persist
browserExtension as false when installation is rejected, in addition to
resetting event.target.checked. Keep the existing error status message behavior
unchanged.
browser-extension/native-host.js-83-86 (1)

83-86: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Credential-leak acknowledgements are disconnected from desktop delivery. Both host implementations report success despite not confirming that the desktop application received the event.

  • browser-extension/native-host.js#L83-L86: transmit the leak payload through the desktop IPC/protocol contract and acknowledge only after acceptance.
  • browser-extension-host.js#L76-L80: reconnect or return an explicit delivery failure instead of dropping the event with { ok: true }.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/native-host.js` around lines 83 - 86, Update the
CREDENTIAL_LEAK handling in browser-extension/native-host.js at lines 83-86 to
send the leak payload through the desktop IPC/protocol contract and acknowledge
with LEAK_NOTIFIED only after delivery is accepted; update
browser-extension-host.js at lines 76-80 to reconnect and deliver the event, or
return an explicit delivery failure instead of reporting { ok: true } when
delivery is not confirmed.
browser-extension/native-host.js-57-75 (1)

57-75: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Reset the launch state on exit and surface spawn failures. desktopProc stays truthy after the spawned launcher exits, so later calls are skipped even though the app is gone. Clear the reference on exit/close, and reject on error instead of resolving unconditionally so APP_OPENED isn’t reported after a failed launch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/native-host.js` around lines 57 - 75, Update
launchDesktopApp so desktopProc is cleared when the spawned launcher emits exit
or close, allowing subsequent launches. In the existing error handler, log the
failure, reset desktopProc, and reject the promise; ensure the delayed resolve
only occurs for successful launches so APP_OPENED is not reported after spawn
failure.
tools/install-native-host.js-47-55 (1)

47-55: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Install a platform-appropriate native-host launcher. The non-Windows path still writes browser-extension/native-host-manifest.json unchanged, and that manifest points to native-host.bat, which macOS/Linux can’t execute. Use a platform-specific manifest and point non-Windows installs at an executable launcher such as browser-extension/native-host.js with execute permissions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/install-native-host.js` around lines 47 - 55, Update the non-Windows
installation branch around manifest writing to create a platform-specific
manifest whose native host path points to the executable
browser-extension/native-host.js launcher instead of native-host.bat. Ensure the
launcher has execute permissions before installing it, while preserving the
existing Windows manifest behavior and destination handling.
browser-extension/native-host.js-61-67 (1)

61-67: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Avoid shell-interpolating process.env.SOTERIOS_APP_PATH. spawn(..., { shell: true }) turns that env value into a command string on Windows/Linux, so quotes or shell metacharacters can break out and run arbitrary commands. Use platform-specific binaries with argument arrays and shell: false; desktopProc should also be cleared on child exit so later launches still work.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/native-host.js` around lines 61 - 67, Update the desktop
launch logic around desktopProc to avoid constructing a shell command from
DESKTOP_APP: select platform-specific executables and argument arrays, invoke
spawn with shell: false, and preserve the existing Windows, macOS, and Linux
launch behavior. Attach an exit/close handler that clears desktopProc when the
child terminates so subsequent launches can start a new process.

Source: Linters/SAST tools

tools/install-native-host.js-11-32 (1)

11-32: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Write the resolved manifest before Windows registration. In both tools/install-native-host.js and src/main/ipcHandlers.js, <EXTENSION_ID> is replaced only in memory, but Chrome/Edge is still pointed at browser-extension/native-host-manifest.json, so the placeholder stays on disk. Generate a substituted copy and register that file instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/install-native-host.js` around lines 11 - 32, Write the substituted
manifest containing the resolved allowed_origins to a generated file before
registration, and point the Windows registry command in main() to that generated
file instead of the unresolved native-host-manifest.json. Apply the
corresponding manifest-registration update in browser-extension-host.json (lines
6-8) so it also references the resolved copy; update
tools/install-native-host.js (lines 11-32) directly, while preserving the
existing extension ID substitution.
tools/install-native-host.js-11-11 (1)

11-11: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Write a resolved, platform-specific manifest before registering it. EXTENSION_ID still falls back to YOUR_EXTENSION_ID_HERE, and Windows registers browser-extension/native-host-manifest.json without persisting the updated allowed_origins, so the browser keeps the placeholder origin. The Linux/macOS install path also writes the same template manifest, whose path still points at native-host.bat. Reject placeholder IDs and emit the manifest to an install location per platform.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tools/install-native-host.js` at line 11, Update the install flow around
EXTENSION_ID and platform-specific registration to reject the
YOUR_EXTENSION_ID_HERE placeholder, resolve allowed_origins from the actual
extension ID, and write a platform-specific manifest to the platform’s install
location before registering it. Ensure Windows persists the resolved manifest
instead of registering the template, and Linux/macOS use a native-host
executable path rather than the template’s native-host.bat path.
🟡 Minor comments (11)
src/main/healthSummary.js-45-52 (1)

45-52: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Guard against NaN in sparkline samples and rename the shadowing latest.

h.tx_bytes + h.rx_bytes lacks the || 0 fallback used just above for rxKBs/txKBs; a missing field yields NaN, which will break the sparkline scaling downstream. The inner latest also shadows the outer scan-report latest from Line 9, which is confusing.

🐛 Proposed fix
-    const history = db.getNetworkHistory ? db.getNetworkHistory(24 * 60) : []; // last 24h, 1 sample per min
-    if (history.length) {
-      const latest = history[history.length - 1];
-      network.rxKBs = Math.round((latest.rx_bytes || 0) / 1024);
-      network.txKBs = Math.round((latest.tx_bytes || 0) / 1024);
-      network.history = history.slice(-60).map(h => (h.tx_bytes + h.rx_bytes) / 1024); // last 60 samples for sparkline
+    const history = db.getNetworkHistory ? db.getNetworkHistory(24 * 60) : []; // last 24h, 1 sample per min
+    if (history.length) {
+      const latestSample = history[history.length - 1];
+      network.rxKBs = Math.round((latestSample.rx_bytes || 0) / 1024);
+      network.txKBs = Math.round((latestSample.tx_bytes || 0) / 1024);
+      network.history = history.slice(-60).map(h => ((h.tx_bytes || 0) + (h.rx_bytes || 0)) / 1024); // last 60 samples
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/healthSummary.js` around lines 45 - 52, Update the network history
mapping in the health-summary logic to default both h.tx_bytes and h.rx_bytes to
zero before calculating each sparkline sample, preventing NaN values. Rename the
inner latest variable used for the newest history entry to avoid shadowing the
outer scan-report latest.
src/i18n/locales/tr.json-905-909 (1)

905-909: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Align Turkish health placeholders and percent formatting with the dashboard contract.

src/ui/js/pages/dashboard.js supplies pct, not usage, to the disk reason translations. Also, %{pct}% produces an extra percent sign. Use {pct}% or %{pct} consistently for the target Turkish phrasing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/locales/tr.json` around lines 905 - 909, Update the Turkish health
translation entries around health.reason.diskHealthy and
health.reason.memoryUsage to use the dashboard contract’s pct placeholder
instead of usage, and remove the duplicated percent sign by using one consistent
{pct}% or %{pct} format. Preserve the intended Turkish wording and apply the
corrected placeholder formatting to the affected percentage messages.
src/i18n/locales/en.json-124-124 (1)

124-124: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Pass the error argument or remove the placeholder. settings.browserExtension.installFailed includes {error}, but the call site can invoke it without interpolation args, so the UI may show the placeholder literally.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/locales/en.json` at line 124, Update the
settings.browserExtension.installFailed translation and its call site so they
agree: pass the actual error interpolation argument whenever the message is
used, or remove the {error} placeholder if no error is available. Ensure the UI
never displays the placeholder literally.
src/i18n/locales/nl.json-900-900 (1)

900-900: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate scanIndicator.scanning into Dutch.

"Scanning…" remains English while the other scan-indicator statuses are localized. Use a Dutch equivalent such as "Scannen…".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/locales/nl.json` at line 900, Translate the scanIndicator.scanning
value in the Dutch locale from English to Dutch, using “Scannen…” while
preserving the existing key and ellipsis style.
src/i18n/locales/ja.json-866-866 (1)

866-866: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix the incomplete scan-time phrase.

"過去 1 以内" is missing the unit (), so the UI displays malformed Japanese. It should express “within the last day.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/locales/ja.json` at line 866, Update the Japanese translation for
health.reason.scanToday so the incomplete “過去 1 以内” phrase includes the day unit
and clearly expresses “within the last day.”
src/i18n/locales/ja.json-767-767 (1)

767-767: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use a natural Japanese translation for scan recency.

"スキャン時効性" is not a natural label for recency and can be misunderstood. Use wording such as “スキャンの最新性” or “最後のスキャン” instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/locales/ja.json` at line 767, Update the Japanese translation for
the health.label.scanRecency key from the unnatural “スキャン時効性” to a natural label
such as “スキャンの最新性” or “最後のスキャン”.
src/i18n/locales/it.json-790-804 (1)

790-804: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Polish the Italian health translations.

Several strings have incorrect grammar ("Nessun scansione", "nell'ultimo scansione"), awkward placeholder text ("Trovata/e"), or leave "Recency" untranslated.

Also applies to: 886-890

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/locales/it.json` around lines 790 - 804, Polish the Italian health
translations in the malware and scan-recency entries, including the
corresponding entries around the referenced later section. Correct article and
gender agreement for “scansione,” replace awkward “Trovata/e” wording with
natural Italian while preserving {count} and {days}, and translate “Recency”
consistently in the relevant labels.
src/i18n/locales/de.json-885-885 (1)

885-885: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Translate scanIndicator.threatsFound.

This value is still English ("{count} threat(s) found"), so German users see untranslated text and an awkward literal plural marker.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/i18n/locales/de.json` at line 885, Update the German locale value for
scanIndicator.threatsFound to a natural German translation, replacing the
English text and literal “(s)” marker while preserving the {count} interpolation
placeholder.
browser-extension/popup.js-41-51 (1)

41-51: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Use AbortSignal.timeout() here
fetch ignores the timeout option, so this health check can hang and overlap with the 30s interval. Abort it after 1s instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/popup.js` around lines 41 - 51, Update checkConnection to
replace fetch’s unsupported timeout option with an AbortSignal.timeout(1000)
signal, ensuring the health request aborts after one second while preserving the
existing success and catch handling.
browser-extension/native-host.bat-5-5 (1)

5-5: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Quote the batch variable assignment.

An installation path containing shell metacharacters can alter parsing here. Use the safe assignment form.

Proposed fix
- set NODE_PATH=%~dp0..\..\node_modules
+ set "NODE_PATH=%~dp0..\..\node_modules"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/native-host.bat` at line 5, Update the NODE_PATH assignment
in the batch script to quote the path value safely, preserving the existing
%~dp0 relative-path resolution while preventing shell metacharacters in the
installation path from altering parsing.

Source: Linters/SAST tools

browser-extension/options.html-38-38 (1)

38-38: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add a switch role to these toggle buttons. aria-checked on a plain button won’t expose the on/off state correctly to assistive tech; use role="switch" on all four controls, or replace them with native checkboxes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/options.html` at line 38, Add role="switch" to all four
toggle buttons in the options UI, including the control identified by
id="hibpEnabled", while preserving their existing aria-checked state and labels.
🧹 Nitpick comments (3)
src/ui/pages/trayDashboard.js (1)

61-104: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Deduplicate the summary-rendering logic.

loadSummary (Lines 61-104) re-implements the exact score/RTP/firewall/network/last-scan rendering already present in the tray:summary handler (Lines 1-59). Extract a single renderSummary(summary) and call it from both the event handler and loadSummary to prevent the two paths from drifting.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ui/pages/trayDashboard.js` around lines 61 - 104, Extract the shared
score, detail, RTP, firewall, network, and last-scan DOM update logic from
loadSummary into a renderSummary(summary) function. Replace the duplicated
rendering in both the tray:summary event handler and loadSummary with calls to
renderSummary, while preserving each path’s existing loading and error handling.
src/main/healthSummary.js (2)

34-40: 🚀 Performance & Scalability | 🔵 Trivial

netsh is spawned on every tray summary refresh.

getTrayHealthSummary is invoked on the tray refresh cycle (~15s), so this launches a netsh child process each time. On non-Windows platforms it fails fast (caught, falls back to active:false), but on Windows it's a recurring process spawn just for firewall state. Consider caching the result for a short TTL or reading it less frequently.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/healthSummary.js` around lines 34 - 40, Update the firewall-state
logic in getTrayHealthSummary to avoid invoking netsh on every tray refresh.
Cache the resolved firewall status with a short time-to-live, reuse the cached
value while valid, and only rerun the existing execFileAsync check after the
cache expires; preserve the current active:false fallback when the command
fails.

Source: Linters/SAST tools


24-30: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Drop the unused RealTimeWatcher require
rtp comes entirely from db.getSetting, so this import only adds a silent failure path and can force { enabled: false } if the module load fails.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/healthSummary.js` around lines 24 - 30, Remove the unused
RealTimeWatcher require from the health summary initialization, leaving
rtp.enabled sourced directly from db.getSetting('feature.realtimeProtection',
false). Preserve the existing fallback behavior only for failures in the
settings lookup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Major comments:
In `@browser-extension/background.js`:
- Around line 1-3: Add a CHECK_PASSWORD message handler in the background worker
alongside the existing onInstalled listener. Handle messages sent by content.js,
perform the password lookup using the existing project mechanism, and respond
asynchronously with an object containing the expected pwned and count fields so
showResult receives the correct payload.

In `@browser-extension/content.js`:
- Around line 94-99: Replace the inline blur cleanup near the passwordFields.set
call with a shared cleanup helper that removes the icon, unregisters the scroll
and resize listeners, deletes the input from the passwordFields WeakMap, and
clears its data-soterios-id attribute. Update the SETTINGS_UPDATED cleanup path
to use this helper instead of calling forEach or clear on passwordFields, while
preserving one-time cleanup behavior.

In `@browser-extension/manifest.json`:
- Line 18: Add the localhost origin pattern http://localhost:17234/* to the
manifest host_permissions alongside the existing API permission, enabling
popup.js to reach the desktop app health endpoint without changing the current
permission.
- Around line 17-20: Update the manifest configuration to register
browser-extension/content.js as a content script on the intended pages so the
password-field overlay loads, and add host permission coverage for
http://localhost:17234/* to support the health request made by
browser-extension/popup.js. Preserve the existing storage and Have I Been Pwned
permissions.

In `@browser-extension/native-host.js`:
- Around line 83-86: Update the CREDENTIAL_LEAK handling in
browser-extension/native-host.js at lines 83-86 to send the leak payload through
the desktop IPC/protocol contract and acknowledge with LEAK_NOTIFIED only after
delivery is accepted; update browser-extension-host.js at lines 76-80 to
reconnect and deliver the event, or return an explicit delivery failure instead
of reporting { ok: true } when delivery is not confirmed.
- Around line 57-75: Update launchDesktopApp so desktopProc is cleared when the
spawned launcher emits exit or close, allowing subsequent launches. In the
existing error handler, log the failure, reset desktopProc, and reject the
promise; ensure the delayed resolve only occurs for successful launches so
APP_OPENED is not reported after spawn failure.
- Around line 61-67: Update the desktop launch logic around desktopProc to avoid
constructing a shell command from DESKTOP_APP: select platform-specific
executables and argument arrays, invoke spawn with shell: false, and preserve
the existing Windows, macOS, and Linux launch behavior. Attach an exit/close
handler that clears desktopProc when the child terminates so subsequent launches
can start a new process.

In `@browser-extension/package.json`:
- Around line 7-9: Update the build:icons, package, and install:host scripts in
package.json to resolve helper paths from the repository root when invoked with
browser-extension as the working directory. Remove the redundant directory
change in package and reference the root-level tools scripts using the
appropriate parent-relative paths.
- Line 8: Update the package.json “package” script to stage only
browser-extension release files, explicitly excluding build dependencies such as
node_modules, icons/*.svg, tools, and other non-extension artifacts. Replace the
Unix-specific zip command with a cross-platform archiver already supported by
the project, while preserving the icon build and generated
soterios-extension.zip output.

In `@browser-extension/popup.js`:
- Around line 13-20: Update the HIBP fetch flow around resp and the response
parsing loop to check resp.ok immediately after fetch and before calling
resp.text(). Treat non-OK responses as failures using the existing
error-handling path, rather than parsing the body or returning 0; preserve the
current suffix matching and successful-response behavior.

In `@src/i18n/locales/ja.json`:
- Around line 765-778: Remove the duplicate locale-key declarations from
src/i18n/locales/ja.json lines 765-778, keeping one canonical
health.malware.high value. Also remove the duplicated health subkeys from
src/i18n/locales/ko.json lines 804-816, retaining one canonical definition for
each key so both locale files contain no duplicate keys.

In `@src/i18n/locales/pt-BR.json`:
- Around line 807-819: Remove or merge the duplicate health translation keys in
src/i18n/locales/pt-BR.json lines 807-819, src/i18n/locales/ru.json lines
803-815, and src/i18n/locales/tr.json lines 803-815. Preserve each locale’s
intended translations under a single definition for every health.scanRecency,
health.disk, health.memory, health.load, health.uptime, health.rtp, and
health.firewall key.

In `@src/main/ipcHandlers.js`:
- Around line 937-941: Update the native-host registration flow around manifest
parsing in ipcHandlers.js to persist the resolved manifest, including the
substituted extension ID, to a writable installed-host path before invoking reg
add. Use that persisted file path in the registry command instead of the source
browser-extension/native-host-manifest.json path, while preserving the existing
manifest name and allowed_origins handling.

In `@src/ui/js/pages/settings.js`:
- Around line 122-128: Remove the duplicate toggle-row containing
`#networkPerimeterMapToggle` from the settings markup, preserving the earlier
instance that receives the event listener. Keep the existing label, description,
and functional toggle behavior represented by the first control.
- Around line 422-424: Update the catch handler for the
browserExtension:installNativeHost invocation to persist browserExtension as
false when installation is rejected, in addition to resetting
event.target.checked. Keep the existing error status message behavior unchanged.

In `@src/ui/pages/trayDashboard.html`:
- Around line 218-220: Update the network sparkline branch in the tray dashboard
to consume the `network.history` data emitted by `getTrayHealthSummary` instead
of nonexistent `rx`/`tx` fields, adapting the `drawSparkline` call to its
single-series shape. Ensure `drawSparkline` formats these history values as KB/s
rather than B/s, preserving the producer’s units.

In `@tools/build-icons.js`:
- Around line 19-21: Update the catch block in the icon generation loop to
preserve the failure status by propagating the error or setting a non-zero
process exit code after generation fails. Keep the existing error logging, and
ensure the build cannot exit successfully when any required icon generation
fails.
- Line 17: Update the icon generation flow around the svgexport execSync call to
use a declared local svgexport dependency/binary instead of npx -y, and make
conversion errors propagate rather than being swallowed by the catch handler.
Ensure the build exits unsuccessfully when any icon conversion fails.

In `@tools/install-native-host.js`:
- Around line 47-55: Update the non-Windows installation branch around manifest
writing to create a platform-specific manifest whose native host path points to
the executable browser-extension/native-host.js launcher instead of
native-host.bat. Ensure the launcher has execute permissions before installing
it, while preserving the existing Windows manifest behavior and destination
handling.
- Around line 11-32: Write the substituted manifest containing the resolved
allowed_origins to a generated file before registration, and point the Windows
registry command in main() to that generated file instead of the unresolved
native-host-manifest.json. Apply the corresponding manifest-registration update
in browser-extension-host.json (lines 6-8) so it also references the resolved
copy; update tools/install-native-host.js (lines 11-32) directly, while
preserving the existing extension ID substitution.
- Line 11: Update the install flow around EXTENSION_ID and platform-specific
registration to reject the YOUR_EXTENSION_ID_HERE placeholder, resolve
allowed_origins from the actual extension ID, and write a platform-specific
manifest to the platform’s install location before registering it. Ensure
Windows persists the resolved manifest instead of registering the template, and
Linux/macOS use a native-host executable path rather than the template’s
native-host.bat path.

---

Minor comments:
In `@browser-extension/native-host.bat`:
- Line 5: Update the NODE_PATH assignment in the batch script to quote the path
value safely, preserving the existing %~dp0 relative-path resolution while
preventing shell metacharacters in the installation path from altering parsing.

In `@browser-extension/options.html`:
- Line 38: Add role="switch" to all four toggle buttons in the options UI,
including the control identified by id="hibpEnabled", while preserving their
existing aria-checked state and labels.

In `@browser-extension/popup.js`:
- Around line 41-51: Update checkConnection to replace fetch’s unsupported
timeout option with an AbortSignal.timeout(1000) signal, ensuring the health
request aborts after one second while preserving the existing success and catch
handling.

In `@src/i18n/locales/de.json`:
- Line 885: Update the German locale value for scanIndicator.threatsFound to a
natural German translation, replacing the English text and literal “(s)” marker
while preserving the {count} interpolation placeholder.

In `@src/i18n/locales/en.json`:
- Line 124: Update the settings.browserExtension.installFailed translation and
its call site so they agree: pass the actual error interpolation argument
whenever the message is used, or remove the {error} placeholder if no error is
available. Ensure the UI never displays the placeholder literally.

In `@src/i18n/locales/it.json`:
- Around line 790-804: Polish the Italian health translations in the malware and
scan-recency entries, including the corresponding entries around the referenced
later section. Correct article and gender agreement for “scansione,” replace
awkward “Trovata/e” wording with natural Italian while preserving {count} and
{days}, and translate “Recency” consistently in the relevant labels.

In `@src/i18n/locales/ja.json`:
- Line 866: Update the Japanese translation for health.reason.scanToday so the
incomplete “過去 1 以内” phrase includes the day unit and clearly expresses “within
the last day.”
- Line 767: Update the Japanese translation for the health.label.scanRecency key
from the unnatural “スキャン時効性” to a natural label such as “スキャンの最新性” or “最後のスキャン”.

In `@src/i18n/locales/nl.json`:
- Line 900: Translate the scanIndicator.scanning value in the Dutch locale from
English to Dutch, using “Scannen…” while preserving the existing key and
ellipsis style.

In `@src/i18n/locales/tr.json`:
- Around line 905-909: Update the Turkish health translation entries around
health.reason.diskHealthy and health.reason.memoryUsage to use the dashboard
contract’s pct placeholder instead of usage, and remove the duplicated percent
sign by using one consistent {pct}% or %{pct} format. Preserve the intended
Turkish wording and apply the corrected placeholder formatting to the affected
percentage messages.

In `@src/main/healthSummary.js`:
- Around line 45-52: Update the network history mapping in the health-summary
logic to default both h.tx_bytes and h.rx_bytes to zero before calculating each
sparkline sample, preventing NaN values. Rename the inner latest variable used
for the newest history entry to avoid shadowing the outer scan-report latest.

---

Nitpick comments:
In `@src/main/healthSummary.js`:
- Around line 34-40: Update the firewall-state logic in getTrayHealthSummary to
avoid invoking netsh on every tray refresh. Cache the resolved firewall status
with a short time-to-live, reuse the cached value while valid, and only rerun
the existing execFileAsync check after the cache expires; preserve the current
active:false fallback when the command fails.
- Around line 24-30: Remove the unused RealTimeWatcher require from the health
summary initialization, leaving rtp.enabled sourced directly from
db.getSetting('feature.realtimeProtection', false). Preserve the existing
fallback behavior only for failures in the settings lookup.

In `@src/ui/pages/trayDashboard.js`:
- Around line 61-104: Extract the shared score, detail, RTP, firewall, network,
and last-scan DOM update logic from loadSummary into a renderSummary(summary)
function. Replace the duplicated rendering in both the tray:summary event
handler and loadSummary with calls to renderSummary, while preserving each
path’s existing loading and error handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b0ce2a5-4611-4a41-bd23-cb2a90d330d5

📥 Commits

Reviewing files that changed from the base of the PR and between f9d640a and 8a139be.

⛔ Files ignored due to path filters (5)
  • browser-extension/icons/icon.svg is excluded by !**/*.svg
  • browser-extension/icons/icon128.png is excluded by !**/*.png
  • browser-extension/icons/icon16.png is excluded by !**/*.png
  • browser-extension/icons/icon32.png is excluded by !**/*.png
  • browser-extension/icons/icon48.png is excluded by !**/*.png
📒 Files selected for processing (35)
  • browser-extension-host.js
  • browser-extension-host.json
  • browser-extension/background.js
  • browser-extension/content.js
  • browser-extension/manifest.json
  • browser-extension/native-host-manifest.json
  • browser-extension/native-host.bat
  • browser-extension/native-host.js
  • browser-extension/options.html
  • browser-extension/options.js
  • browser-extension/package.json
  • browser-extension/popup.html
  • browser-extension/popup.js
  • src/i18n/locales/ar.json
  • src/i18n/locales/de.json
  • src/i18n/locales/en.json
  • src/i18n/locales/es.json
  • src/i18n/locales/fr.json
  • src/i18n/locales/hi.json
  • src/i18n/locales/it.json
  • src/i18n/locales/ja.json
  • src/i18n/locales/ko.json
  • src/i18n/locales/nl.json
  • src/i18n/locales/pl.json
  • src/i18n/locales/pt-BR.json
  • src/i18n/locales/ru.json
  • src/i18n/locales/tr.json
  • src/main/healthSummary.js
  • src/main/ipcHandlers.js
  • src/main/main.js
  • src/ui/js/pages/settings.js
  • src/ui/pages/trayDashboard.html
  • src/ui/pages/trayDashboard.js
  • tools/build-icons.js
  • tools/install-native-host.js

- Fix WeakMap -> Map in content.js (forEach/clear support)
- Add content_scripts to manifest.json
- Add CHECK_PASSWORD handler in background.js
- Add nativeMessaging permission to manifest.json
- Fix installer to write updated manifest back to disk
- Fix duplicate toggle IDs in settings.js
- Fix tray sparkline data mismatch (healthSummary.js -> tray)
- Fix Turkish locale JSON (extra brace)
- Fix fetch timeout in popup.js (use AbortController)
- Fix shell injection in native-host.js (no shell, validate env)
- Add CHECK_PASSWORD handler in background.js
- Add nativeMessaging permission
- Fix installer to write manifest back to disk
- Fix duplicate toggle IDs
- Fix tray sparkline data shape
- Fix Turkish locale JSON
- Fix fetch timeout with AbortController
- Fix shell injection in native-host.js
const cmd = isWin ? 'cmd' : resolvedPath;
const options = { shell: false, detached: true };

desktopProc = spawn(cmd, args, options);

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src/main/ipcHandlers.js (1)

769-780: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Reject invalid breach counts before persisting an alert.

payload.count is trusted without validation, so 0, negative, NaN, or non-numeric values still create a danger alert. Normalize it to a finite positive integer before calling db.addAlert.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/ipcHandlers.js` around lines 769 - 780, Validate and normalize
payload.count in the credential-leak:notify handler before constructing or
persisting the alert: accept only finite positive integers, and return an
appropriate failure response for invalid values. Use the normalized count
consistently in the alert message, detail, and metadata, including the
db.addAlert call.
browser-extension/native-host.js (3)

91-113: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

CREDENTIAL_LEAK/OPEN_APP handlers don't guard against launchDesktopApp() rejecting.

Unlike the default branch (which sends an ERROR frame for unknown types), the CREDENTIAL_LEAK/OPEN_APP cases await launchDesktopApp() with no try/catch. If it rejects (missing DESKTOP_APP, missing file, or spawn error once Line 80-89 is fixed to reject), the rejection escapes to the process-level unhandledRejection handler, which only logs — no response frame is ever sent back to the extension, leaving background.js's corresponding message-port wait hanging indefinitely.

case 'CREDENTIAL_LEAK':
  try {
    await launchDesktopApp();
    send({ type: 'LEAK_NOTIFIED' });
  } catch (e) {
    send({ type: 'ERROR', error: e.message, original: msg });
  }
  break;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/native-host.js` around lines 91 - 113, Update the
CREDENTIAL_LEAK and OPEN_APP branches in handleMessage to catch
launchDesktopApp() rejections and send an ERROR response containing the failure
message and original request. Preserve the existing success response types and
ensure each branch completes without allowing the rejection to escape to the
process-level handler.

80-89: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Spawn failures never reject the promise, and desktopProc is never cleared on normal exit.

desktopProc.on('error', ...) only logs and nulls desktopProc; it never calls reject, so the fixed setTimeout(resolve, 1500) always resolves the promise regardless of outcome — callers (handleMessage) will report success (LEAK_NOTIFIED/APP_OPENED) even when the launch failed. There's also no exit/close listener, so once the desktop app terminates normally, desktopProc stays non-null forever and the top-of-function guard (if (desktopProc) return Promise.resolve();) permanently skips relaunching on subsequent CREDENTIAL_LEAK/OPEN_APP messages.

🔧 Proposed fix
     desktopProc.unref();
 
-    desktopProc.on('error', e => {
-      log('Desktop app launch error:', e.message);
-      desktopProc = null;
-    });
-
-    setTimeout(resolve, 1500);
+    const timer = setTimeout(resolve, 1500);
+
+    desktopProc.on('error', e => {
+      log('Desktop app launch error:', e.message);
+      desktopProc = null;
+      clearTimeout(timer);
+      reject(e);
+    });
+
+    desktopProc.on('exit', () => {
+      desktopProc = null;
+    });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/native-host.js` around lines 80 - 89, Update the desktop
process launch promise around desktopProc.on('error') so spawn failures call
reject with the launch error instead of allowing the fixed timeout to resolve
successfully. Add an exit or close listener that clears desktopProc when the
child terminates, while preserving the existing launch-success resolution and
top-of-function guard behavior.

115-123: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Catch rejected handler promises and send an error reply. readMessages() calls handleMessage() without await or .catch(), so a failed launchDesktopApp() only reaches unhandledRejection and the extension gets no response. Handle the promise rejection here and send the ERROR frame instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/native-host.js` around lines 115 - 123, Update
readMessages() so the handleMessage() promise is awaited or has a rejection
handler that logs the failure and sends an ERROR frame containing the error
message, ensuring launchDesktopApp() failures receive a response instead of only
reaching the global unhandledRejection handler.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@browser-extension/background.js`:
- Around line 5-11: Update the CHECK_PASSWORD handler’s
checkPassword(msg.password) promise chain to catch all rejections, including
failures before the internal fetch handling, and call sendResponse with an
appropriate error response so the content script always receives a reply while
preserving the async return behavior.
- Around line 34-63: Add an abort timeout to the fetch request in checkPassword
so stalled HIBP calls terminate and the existing catch path returns an error
instead of leaving CHECK_PASSWORD unresolved. Create and pass an AbortSignal to
fetch, ensure the timer is cleaned up after completion, and preserve the current
response parsing and error handling behavior.

In `@browser-extension/native-host.js`:
- Around line 74-79: Update the desktop launch logic around the spawn call to
invoke resolvedPath directly with no arguments, removing the platform-specific
cmd/start command construction. Preserve detached execution with shell disabled
and add windowsHide: true to the spawn options.

In `@src/main/ipcHandlers.js`:
- Around line 940-941: Update native-host manifest generation in
src/main/ipcHandlers.js lines 940-941 and tools/install-native-host.js lines
28-29 to be idempotent: validate SOTERIOS_EXT_ID, load an immutable template or
replace the extension origin on every run, then persist/register/copy the
freshly generated manifest. Apply the same logic in both installers so stale or
placeholder origins cannot survive subsequent installations.
- Around line 937-941: Move manifest loading, JSON parsing, allowed_origins
validation and replacement, and the writeFileSync call into the existing try
block in the IPC handler. Validate that manifest.allowed_origins contains a
usable first entry before calling replace, and preserve the structured { ok:
false, error } response for read, parse, validation, and write failures.

---

Outside diff comments:
In `@browser-extension/native-host.js`:
- Around line 91-113: Update the CREDENTIAL_LEAK and OPEN_APP branches in
handleMessage to catch launchDesktopApp() rejections and send an ERROR response
containing the failure message and original request. Preserve the existing
success response types and ensure each branch completes without allowing the
rejection to escape to the process-level handler.
- Around line 80-89: Update the desktop process launch promise around
desktopProc.on('error') so spawn failures call reject with the launch error
instead of allowing the fixed timeout to resolve successfully. Add an exit or
close listener that clears desktopProc when the child terminates, while
preserving the existing launch-success resolution and top-of-function guard
behavior.
- Around line 115-123: Update readMessages() so the handleMessage() promise is
awaited or has a rejection handler that logs the failure and sends an ERROR
frame containing the error message, ensuring launchDesktopApp() failures receive
a response instead of only reaching the global unhandledRejection handler.

In `@src/main/ipcHandlers.js`:
- Around line 769-780: Validate and normalize payload.count in the
credential-leak:notify handler before constructing or persisting the alert:
accept only finite positive integers, and return an appropriate failure response
for invalid values. Use the normalized count consistently in the alert message,
detail, and metadata, including the db.addAlert call.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a42f6a0b-acad-4c42-bc42-0ab60733fd30

📥 Commits

Reviewing files that changed from the base of the PR and between 8a139be and dfa0339.

📒 Files selected for processing (10)
  • browser-extension/background.js
  • browser-extension/content.js
  • browser-extension/manifest.json
  • browser-extension/native-host.js
  • browser-extension/popup.js
  • src/i18n/locales/tr.json
  • src/main/healthSummary.js
  • src/main/ipcHandlers.js
  • src/ui/js/pages/settings.js
  • tools/install-native-host.js
💤 Files with no reviewable changes (2)
  • src/i18n/locales/tr.json
  • src/ui/js/pages/settings.js
🚧 Files skipped from review as they are similar to previous changes (3)
  • browser-extension/manifest.json
  • src/main/healthSummary.js
  • browser-extension/content.js

Comment on lines +5 to +11
// Handle CHECK_PASSWORD from content script
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.type === 'CHECK_PASSWORD' && msg.password) {
checkPassword(msg.password).then(sendResponse);
return true; // async response
}
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect relevant files with line numbers
printf '\n--- browser-extension/background.js ---\n'
cat -n browser-extension/background.js | sed -n '1,120p'

printf '\n--- Search for CHECK_PASSWORD and sendResponse usage ---\n'
rg -n "CHECK_PASSWORD|sendResponse|checkPassword\\(" browser-extension -S

Repository: chrisriv10/Soterios

Length of output: 3570


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect surrounding code paths that may catch/reject the promise
printf '\n--- browser-extension/content-script.js ---\n'
if [ -f browser-extension/content-script.js ]; then
  cat -n browser-extension/content-script.js | sed -n '1,220p'
fi

printf '\n--- browser-extension/manifest.json ---\n'
if [ -f browser-extension/manifest.json ]; then
  cat -n browser-extension/manifest.json | sed -n '1,220p'
fi

Repository: chrisriv10/Soterios

Length of output: 1237


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Print the exact background.js slice around the handler and password checker.
sed -n '1,120p' browser-extension/background.js

Repository: chrisriv10/Soterios

Length of output: 2300


Add a rejection handler to this message reply.
checkPassword() can still reject before its internal fetch catch runs (for example, crypto.subtle.digest failures), and checkPassword(...).then(sendResponse) will drop the response in that case. Chain a .catch(...) so the content script always gets a reply.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/background.js` around lines 5 - 11, Update the
CHECK_PASSWORD handler’s checkPassword(msg.password) promise chain to catch all
rejections, including failures before the internal fetch handling, and call
sendResponse with an appropriate error response so the content script always
receives a reply while preserving the async return behavior.

Comment on lines +34 to +63
async function checkPassword(password) {
const HIBP_API = 'https://api.pwnedpasswords.com/range/';
const encoder = new TextEncoder();
const data = encoder.encode(password);
const hashBuffer = await crypto.subtle.digest('SHA-1', data);
const hash = Array.from(new Uint8Array(hashBuffer))
.map(b => b.toString(16).padStart(2, '0'))
.join('')
.toUpperCase();

const prefix = hash.slice(0, 5);
const suffix = hash.slice(5);

try {
const resp = await fetch(`${HIBP_API}${prefix}`);
const text = await resp.text();
const lines = text.trim().split('\n');

for (const line of lines) {
const [suf, count] = line.split(':');
if (suf === suffix) {
return { pwned: true, count: parseInt(count, 10) };
}
}
return { pwned: false, count: 0 };
} catch (e) {
console.error('[Soterios] HIBP check failed:', e);
return { error: e.message };
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant file and surrounding handlers
ast-grep outline browser-extension/background.js --view expanded || true

printf '\n--- file stats ---\n'
wc -l browser-extension/background.js

printf '\n--- relevant lines ---\n'
sed -n '1,220p' browser-extension/background.js

printf '\n--- search for CHECK_PASSWORD / timeout / AbortController ---\n'
rg -n "CHECK_PASSWORD|AbortController|setTimeout|fetch\\(" browser-extension -S

Repository: chrisriv10/Soterios

Length of output: 3749


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check how the response path behaves if checkPassword throws/returns error
rg -n "checkPassword\\(|chrome\\.runtime\\.onMessage|sendResponse|return true|CHECK_PASSWORD" browser-extension/background.js -n -S

Repository: chrisriv10/Soterios

Length of output: 468


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the message handler and response lifecycle around checkPassword
cat -n browser-extension/background.js | sed -n '1,220p'

Repository: chrisriv10/Soterios

Length of output: 2811


Add a timeout to the HIBP request. If api.pwnedpasswords.com stalls, checkPassword() never resolves and the CHECK_PASSWORD caller waits indefinitely.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/background.js` around lines 34 - 63, Add an abort timeout
to the fetch request in checkPassword so stalled HIBP calls terminate and the
existing catch path returns an error instead of leaving CHECK_PASSWORD
unresolved. Create and pass an AbortSignal to fetch, ensure the timer is cleaned
up after completion, and preserve the current response parsing and error
handling behavior.

Comment on lines +74 to +79
const isWin = process.platform === 'win32';
const args = isWin ? ['/c', 'start', '""', resolvedPath] : [resolvedPath];
const cmd = isWin ? 'cmd' : resolvedPath;
const options = { shell: false, detached: true };

desktopProc = spawn(cmd, args, options);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the target file and relevant symbols first.
git ls-files browser-extension/native-host.js
printf '\n--- outline ---\n'
ast-grep outline browser-extension/native-host.js --view expanded || true

printf '\n--- numbered excerpt ---\n'
cat -n browser-extension/native-host.js | sed -n '1,180p'

Repository: chrisriv10/Soterios

Length of output: 4612


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for any other desktop launch or spawn patterns in the extension.
rg -n "spawn\\(|DESKTOP_APP|launchDesktopApp|desktopProc|unhandledRejection|uncaughtException" browser-extension

Repository: chrisriv10/Soterios

Length of output: 1226


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check whether the reviewed launch pattern is a direct executable spawn or uses cmd/start elsewhere.
python3 - <<'PY'
from pathlib import Path
p = Path('browser-extension/native-host.js')
text = p.read_text()
start = text.index('function launchDesktopApp')
print(text[start:start+1200])
PY

Repository: chrisriv10/Soterios

Length of output: 1358


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read just the surrounding lines for the launch and message/error handlers.
sed -n '55,130p' browser-extension/native-host.js | nl -ba -v55

Repository: chrisriv10/Soterios

Length of output: 197


🌐 Web query:

Node.js child_process.spawn Windows cmd.exe /c start quoting semantics detached shell:false argv first argument

💡 Result:

When using child_process.spawn to invoke 'cmd.exe' with '/c' 'start' on Windows with shell: false, you are responsible for manually handling argument quoting to satisfy the command-line parsing rules of cmd.exe [1][2]. Because 'start' is a shell builtin, it interprets its first quoted argument as a window title if it contains spaces [1]. To robustly use 'start' in this context: 1. Pass an empty string '""' as the first argument to 'start'. This serves as a dummy window title, ensuring that any subsequent arguments (like file paths with spaces) are not misinterpreted as the window title [1]. 2. If your arguments contain spaces or special characters, you must ensure they are properly quoted for cmd.exe [2][3]. 3. The option windowsVerbatimArguments: true can be used if you want to bypass Node.js's internal automatic quoting/escaping and pass the argument string exactly as-is to the Windows process [2][3]. This is often useful when you are already manually managing all required quotes for cmd.exe [2]. Example implementation: const { spawn } = require('node:child_process'); // The '""' is an empty window title, which prevents 'start' // from consuming the following path as the title. const child = spawn('cmd.exe', ['/c', 'start', '""', 'path to file.txt'], { windowsVerbatimArguments: true // Optional: use if you are manually handling all quotes }); Be aware that shell: true automatically handles some of these complexities by invoking the shell, but it introduces security risks if the input is not sanitized, as it may allow for arbitrary command injection [4]. Always prioritize explicitly passing arguments as an array rather than a single string when possible [3][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for any code that reads launch-time argv or references the desktop app path as an argument.
rg -n "process\.argv|argv\\[1\\]|argv\\[2\\]|DESKTOP_APP|SOTERIOS_APP_PATH|launchDesktopApp|OPEN_APP|CREDENTIAL_LEAK" .

Repository: chrisriv10/Soterios

Length of output: 1674


Spawn the desktop app directly. spawn(resolvedPath, [resolvedPath], ...) passes the executable path as the app’s first argument on non-Windows, and the cmd /c start branch still routes the launch through cmd.exe’s parser. spawn(resolvedPath, [], { shell: false, detached: true, windowsHide: true }) avoids both issues.

🧰 Tools
🪛 GitHub Check: CodeQL

[warning] 79-79: Shell command built from environment values
This shell command depends on an uncontrolled absolute path.
This shell command depends on an uncontrolled absolute path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@browser-extension/native-host.js` around lines 74 - 79, Update the desktop
launch logic around the spawn call to invoke resolvedPath directly with no
arguments, removing the platform-specific cmd/start command construction.
Preserve detached execution with shell disabled and add windowsHide: true to the
spawn options.

Comment thread src/main/ipcHandlers.js Outdated
Comment on lines +937 to +941
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
const extId = process.env.SOTERIOS_EXT_ID || 'YOUR_EXTENSION_ID_HERE';
manifest.allowed_origins = [manifest.allowed_origins[0].replace('<EXTENSION_ID>', extId)];
// Write updated manifest back to disk so Chrome/Edge reads the correct ID
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Include manifest preparation in the structured error boundary.

JSON.parse, allowed_origins[0].replace(...), and writeFileSync run before the try block. Malformed or read-only manifests therefore reject the IPC call instead of returning { ok: false, error }; validate the manifest and move these operations inside the same try.

🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 940-940: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/ipcHandlers.js` around lines 937 - 941, Move manifest loading, JSON
parsing, allowed_origins validation and replacement, and the writeFileSync call
into the existing try block in the IPC handler. Validate that
manifest.allowed_origins contains a usable first entry before calling replace,
and preserve the structured { ok: false, error } response for read, parse,
validation, and write failures.

Comment thread src/main/ipcHandlers.js Outdated
Comment on lines +940 to +941
// Write updated manifest back to disk so Chrome/Edge reads the correct ID
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Generate native-host manifests idempotently instead of persisting one-time substitutions.

Both installers mutate the template in place, so subsequent installations can retain a stale or placeholder extension origin.

  • src/main/ipcHandlers.js#L940-L941: validate SOTERIOS_EXT_ID and generate the persisted manifest from an immutable template or replace the current origin every run.
  • tools/install-native-host.js#L28-L29: apply the same idempotent generation logic before registering or copying the manifest.
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 940-940: Filesystem path is not a string literal; a request-/variable-derived path can enable path traversal. Validate and normalize the path before use.
Context: fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(detect-non-literal-fs-filename)

📍 Affects 2 files
  • src/main/ipcHandlers.js#L940-L941 (this comment)
  • tools/install-native-host.js#L28-L29
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/ipcHandlers.js` around lines 940 - 941, Update native-host manifest
generation in src/main/ipcHandlers.js lines 940-941 and
tools/install-native-host.js lines 28-29 to be idempotent: validate
SOTERIOS_EXT_ID, load an immutable template or replace the extension origin on
every run, then persist/register/copy the freshly generated manifest. Apply the
same logic in both installers so stale or placeholder origins cannot survive
subsequent installations.

@coderabbitai coderabbitai Bot mentioned this pull request Jul 23, 2026
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@chrisriv10

Copy link
Copy Markdown
Owner Author

Superseded by #96 - clean rebase on main with security fixes and unit tests.

@chrisriv10 chrisriv10 closed this Aug 1, 2026
@chrisriv10
chrisriv10 deleted the feature/browser-extension-integration branch August 1, 2026 02:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants