Refinement Audit — Cycle 2

Summary

Cycle 1 cleaned up the highest-friction IPC gaps, and the remaining issues are concentrated in desktop edge cases rather than broad architectural faults. The most serious problem is a production security regression where the CSP hook does not cover the packaged file:// renderer, with two additional reliability bugs in crash recovery and Word document creation follow-up behavior.

Critical Findings

C1: CSP registration skips packaged file:// renderer responses

  • File: src/main/security/csp.ts:121
  • Issue: applyCsp registers onHeadersReceived only for *://*/*, which excludes the packaged renderer’s file:// navigations. In production builds the main window therefore misses the app’s custom CSP header entirely, weakening the intended renderer hardening.
  • Evidence: The hook is registered with { urls: ['*://*/*'] } at src/main/security/csp.ts:121, even though this module’s own contract says it should cover the packaged renderer and the packaged app loads local files rather than HTTP URLs.
  • Fix: Expand the filter to include file://*/* (or an equivalent all-URLs filter), and update CSP tests to assert that file-scheme responses are actually covered by the registered filter.

Important Findings

I1: Crash marker writes can fail on a fresh profile before the userData folder exists

  • File: src/main/app/crashGuard.ts:97
  • Issue: writePreviousCrashMarker writes PREVIOUS_CRASH directly under userData without creating the directory first. If the first fatal crash happens before Electron or the app has created that folder, the marker write fails and the next-launch recovery signal is lost.
  • Evidence: The function builds markerPath and immediately calls deps.fs.writeFileSync(...) at src/main/app/crashGuard.ts:99-101, while CrashGuardDeps.fs already exposes mkdirSync and no directory creation happens on this path.
  • Fix: Create the userData directory with mkdirSync(..., { recursive: true }) before writing the marker, and add a regression test that exercises the fresh-profile crash path.

I2: shell.openPath rejection turns a completed Word save into a reported failure

  • File: src/main/services/word/WordDocumentService.ts:440
  • Issue: After the service has already verified that Word created the .docx file successfully, it awaits shell.openPath(dest) inside the main try. If Electron rejects instead of returning a non-empty failure string, the outer catch reports POWERSHELL_FAILED, even though the file already exists on disk.
  • Evidence: File existence is confirmed at src/main/services/word/WordDocumentService.ts:423-437, then shell.openPath runs at src/main/services/word/WordDocumentService.ts:440. Any thrown error falls through to the generic catch at src/main/services/word/WordDocumentService.ts:452-459, which returns a full operation failure.
  • Fix: Isolate shell.openPath in its own try/catch, log the rejection as a warning, and keep the result successful once the saved file has been confirmed.

Minor Findings

None.

Design Improvements

None.

Regressions

None detected.

TOTAL_FINDINGS: 3