Files
rclone-jav/bugs-candidates-extension-bg.md
T
admin f7fc15b17c Sync working tree before initial Gitea push
Includes:
- cli.py path fix (parents[1]) for config/catalog resolution
- Library cleanup feature design docs (TODO.md, mockup)
- Audit + bug-queue markdowns from May 2026 reliability pass
- .gitignore expanded for transient artifacts
2026-05-26 22:35:42 +02:00

8.5 KiB

Candidate Findings — Extension SW + content + manifest — audit-snapshot-2026-05-24T15-55Z.md

Scope: background.js + content.js + manifest.json Required-reading: ext AGENTS.md / mockup / bug-audit-plan.md / project memory Auditor: fresh Explore agent (read-only Phase 1)


Candidate C-1: Race condition in maybeNotifyHostError rate-limiting

  • File: background.js:188-193
  • Hunch: Concurrent recordRpc calls could trigger multiple notifications within 10min due to get-then-set race.
  • Trace: Two simultaneous host errors invoke recordRpc() → maybeNotifyHostError(). Both read HOST_ALERT_KEY before either writes. Both pass the now-lastTs check and both post.
  • Question for verifier: Does code guarantee only one alert fires per 10min window under concurrent error paths?
  • Contract refs needed: Race condition definition in bug-audit-plan.md; Chrome storage.local atomicity

Candidate C-2: pending Map orphaned on SW eviction mid-call

  • File: background.js:90, 124-148, 307-365
  • Hunch: If SW evicts between request send and response, next instance has empty pending Map. Response arrives with no matching req_id.
  • Trace: Send stores pending.set(reqId, {resolve, reject}). SW evicts. New SW has empty pending. Response at line 124-127 finds no match, dropped.
  • Question for verifier: Does keepalive (20s pulse) reliably prevent SW eviction during full 90s timeout on slow remote?
  • Contract refs needed: MV3 SW eviction timing vs NATIVE_CALL_TIMEOUT_MS (90s)

Candidate C-3: mergeSettings shallow-merge loses missing nested keys

  • File: background.js:62-76
  • Hunch: Deep-merge is one level, but if stored.triggers is incomplete, Object.assign(dv, sv) loses keys not in sv.
  • Trace: dv={autoPageLoad:true, autoKnownSites:false, …7 keys}. sv={autoPageLoad:true}. Result is only {autoPageLoad:true}.
  • Question for verifier: Does incomplete settings blob correctly populate all missing triggers keys from defaults?
  • Contract refs needed: DEFAULT_SETTINGS.triggers shape vs loaded partial settings

Candidate C-4: Discord webhook URL regex insufficient

  • File: background.js:232
  • Hunch: Regex validates only schema+domain, not mandatory ID. URL https://discord.com/api/webhooks/ (no ID) passes validation.
  • Trace: /^https://(?:discord.com|discordapp.com)/api/webhooks//.test(url) matches prefix only.
  • Question for verifier: Should regex enforce numeric ID after /api/webhooks/?
  • Contract refs needed: Discord webhook URL format spec

Candidate C-5: postDiscordAlert silently swallows all errors

  • File: background.js:215, 268-272
  • Hunch: .catch(() => {}) suppresses Discord errors with no logging or diagnostics visibility.
  • Trace: Line 215 swallows all exceptions. Function catches fetch errors at 268 and records in lastDiscordSend, but callers don't see them.
  • Question for verifier: Should Discord errors be logged to native RPC log or is silent swallow intentional?
  • Contract refs needed: AGENTS.md or alerting design docs

Candidate C-6: contextMenu handler doesn't validate tab.id

  • File: background.js:895-905
  • Hunch: Handler checks if (!tab) but not if tab.id is null. Missing tab.id will fail silently in checkTab.
  • Trace: Line 896 checks tab, not tab.id. Line 898 calls checkTab(tab) which calls extractIdFromTab(tab) which calls chrome.tabs.sendMessage(tab.id).
  • Question for verifier: Can contextMenus.onClicked pass tab with null id?
  • Contract refs needed: Chrome contextMenus API contract

Candidate C-12: FC2 ID regex minimum 4 digits too strict

  • File: src/shared/id-extract.js:16-20
  • Hunch: FC2-PPV normalizer requires 4+ digits. Pages with FC2-PPV-123 silently fail to match.
  • Trace: Line 17 regex /\bFC2-?PPV-?(\d{4,})\b/i. Line 19 /\bFC2-(\d{4,})\b/i. Both need 4+ digits minimum.
  • Question for verifier: Is 4-digit minimum intentional or should it be 3+?
  • Contract refs needed: Real FC2 ID formats in AGENTS.md

Candidate C-15: recordRpc read-modify-write race loses entries

  • File: background.js:162-169
  • Hunch: Concurrent recordRpc calls uncoordinated. Two errors get same old log, prepend differently, second write overwrites first.
  • Trace: get(NATIVE_LOG_KEY), prepend entry, set. If two calls concurrent, second set overwrites first's entry.
  • Question for verifier: Does Chrome storage.local.set serialize, or can concurrent calls lose entries?
  • Contract refs needed: Chrome storage.local atomicity guarantees

Candidate C-19: ensureContextMenu not called on SW init

  • File: background.js:766-782
  • Hunch: Context menu may not recreate after SW eviction if only called on settings changes.
  • Trace: ensureContextMenu defined at line 766. Need to verify it's called at SW startup.
  • Question for verifier: Is ensureContextMenu called during SW initialization?
  • Contract refs needed: MV3 context menu persistence after SW eviction

Candidate C-20: escapeOverlay function not found in content.js

  • File: content.js (various lines use escapeOverlay but definition not visible)
  • Hunch: showOverlay calls escapeOverlay at multiple points but function is not defined in the 509-line content.js.
  • Trace: Lines 374-400 call escapeOverlay(...) but no definition found.
  • Question for verifier: Is escapeOverlay defined in content.js or missing?
  • Contract refs needed: content.js full file review; module dependencies

Additional light candidates (lower priority, but noted):

C-7: nativePort assignment race (theory-level, JS atomic in practice) C-9: Silent catch in recordActivity (observability trade-off) C-11: Content message validation (trusted sender, acceptable design trade-off) C-14: keepaliveTimer stale reference (uncommon race, low impact) C-18: badgeSpinners leak under heavy load (unlikely, has onRemoved handler)


Triage Summary

High-priority for verification: C-1, C-2, C-3, C-4, C-5, C-6, C-12, C-15, C-19, C-20 Medium-priority (design review): C-7, C-9, C-11, C-14, C-18

Focus areas for verifier:

  1. Concurrency safety of storage get-then-set patterns
  2. Service worker eviction + pending request handling
  3. Settings merge correctness with partial updates
  4. Input validation in all entry points

VERIFIER NOTES (appended after Phase 1 verification)

C-2 (SW eviction + orphaned pending) — REFUTED

First verifier returned CONFIRMED, but did not consult Chrome connectNative keepalive contract. Re-verifier (with explicit contract ref to https://developer.chrome.com/docs/extensions/develop/concepts/service-workers/lifecycle) returned REFUTED, high confidence. Key finding: an open connectNative port keeps the MV3 SW alive per docs. If port closes, onDisconnect fires and rejects all pending (background.js:139). The orphaned-pending-Map scenario cannot occur under documented Chrome contract. The in-code pulseKeepalive is defensive redundancy, not load-bearing. Caveat: if Brave is observed diverging from this contract, the symptom could manifest as a Brave-specific bug — would need a Brave-observed SW restart trace while a native port stayed active. NOT verified here. Final status: REFUTED. Removed from bugs-extension-bg.md.

C-15 (recordRpc race) — CONFIRMED

Verifier returned CONFIRMED, high confidence. Promoted to bugs-extension-bg.md as S-1.

CHUNK 3 MODERATE VERIFICATION RESULTS (after stricter prompt)

  • C-1 (maybeNotifyHostError rate-limit race) — CONFIRMED, M, promoted as M-1 in bugs-extension-bg.md
  • C-3 (mergeSettings shallow-merge) — REFUTED. Auditor misread Object.assign arg order; defaults fill missing keys correctly. Discarded.
  • C-5 (Discord errors swallowed) — PARTIAL, demoted M→L. lastDiscordSend storage write present; only passive UI display missing. Promoted as L-1.
  • C-6 (contextMenu tab.id null) — REFUTED. Chrome contract guarantees non-null tab.id for registered contexts. extractIdFromTab also has defensive null check. Discarded.
  • C-19 (ensureContextMenu post-eviction) — CONFIRMED, M (very high confidence). Promoted as M-2.
  • C-20 (escapeOverlay undefined) — REFUTED. Function defined at content.js:451. Auditor missed it. Discarded.

CHUNK 3 CALIBRATION SUMMARY:

  • Severe rejection: 1/2 = 50%
  • Moderate rejection: 3/6 = 50%
  • Combined: 4/8 = 50%
  • Stop condition (>30% rejection) TRIGGERED. Chunk 3 audit should be restarted with tighter framing OR Light candidates should NOT be verified per current pass.
  • Calibration learning: auditor over-claimed by reading code in isolation without checking platform API contracts (Chrome lifecycle, contextMenus, storage atomicity). Stricter verifier prompt with explicit contract requirement caught 3 of 4 false positives.