@pan-sec/notebooklm-mcp 2026.2.11 → 2026.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/README.md +62 -19
  2. package/SECURITY.md +31 -61
  3. package/dist/auth/auth-manager.d.ts +2 -1
  4. package/dist/auth/auth-manager.d.ts.map +1 -1
  5. package/dist/auth/auth-manager.js +117 -44
  6. package/dist/auth/auth-manager.js.map +1 -1
  7. package/dist/auth/mcp-auth.d.ts +24 -4
  8. package/dist/auth/mcp-auth.d.ts.map +1 -1
  9. package/dist/auth/mcp-auth.js +149 -19
  10. package/dist/auth/mcp-auth.js.map +1 -1
  11. package/dist/compliance/alert-manager.d.ts.map +1 -1
  12. package/dist/compliance/alert-manager.js +7 -4
  13. package/dist/compliance/alert-manager.js.map +1 -1
  14. package/dist/compliance/breach-detection.d.ts.map +1 -1
  15. package/dist/compliance/breach-detection.js +14 -7
  16. package/dist/compliance/breach-detection.js.map +1 -1
  17. package/dist/compliance/change-log.d.ts.map +1 -1
  18. package/dist/compliance/change-log.js +7 -4
  19. package/dist/compliance/change-log.js.map +1 -1
  20. package/dist/compliance/compliance-logger.d.ts.map +1 -1
  21. package/dist/compliance/compliance-logger.js +11 -6
  22. package/dist/compliance/compliance-logger.js.map +1 -1
  23. package/dist/compliance/consent-manager.d.ts.map +1 -1
  24. package/dist/compliance/consent-manager.js +5 -3
  25. package/dist/compliance/consent-manager.js.map +1 -1
  26. package/dist/compliance/data-erasure.d.ts +1 -1
  27. package/dist/compliance/data-erasure.d.ts.map +1 -1
  28. package/dist/compliance/data-erasure.js +142 -83
  29. package/dist/compliance/data-erasure.js.map +1 -1
  30. package/dist/compliance/data-export.d.ts.map +1 -1
  31. package/dist/compliance/data-export.js +23 -12
  32. package/dist/compliance/data-export.js.map +1 -1
  33. package/dist/compliance/data-inventory.d.ts.map +1 -1
  34. package/dist/compliance/data-inventory.js +7 -6
  35. package/dist/compliance/data-inventory.js.map +1 -1
  36. package/dist/compliance/dsar-handler.d.ts +7 -1
  37. package/dist/compliance/dsar-handler.d.ts.map +1 -1
  38. package/dist/compliance/dsar-handler.js +74 -61
  39. package/dist/compliance/dsar-handler.js.map +1 -1
  40. package/dist/compliance/evidence-collector.d.ts.map +1 -1
  41. package/dist/compliance/evidence-collector.js +10 -6
  42. package/dist/compliance/evidence-collector.js.map +1 -1
  43. package/dist/compliance/health-monitor.d.ts.map +1 -1
  44. package/dist/compliance/health-monitor.js +15 -9
  45. package/dist/compliance/health-monitor.js.map +1 -1
  46. package/dist/compliance/incident-manager.d.ts.map +1 -1
  47. package/dist/compliance/incident-manager.js +5 -3
  48. package/dist/compliance/incident-manager.js.map +1 -1
  49. package/dist/compliance/policy-docs.d.ts.map +1 -1
  50. package/dist/compliance/policy-docs.js +14 -11
  51. package/dist/compliance/policy-docs.js.map +1 -1
  52. package/dist/compliance/privacy-notice-text.d.ts.map +1 -1
  53. package/dist/compliance/privacy-notice-text.js +3 -4
  54. package/dist/compliance/privacy-notice-text.js.map +1 -1
  55. package/dist/compliance/privacy-notice.d.ts.map +1 -1
  56. package/dist/compliance/privacy-notice.js +5 -3
  57. package/dist/compliance/privacy-notice.js.map +1 -1
  58. package/dist/compliance/report-generator.d.ts.map +1 -1
  59. package/dist/compliance/report-generator.js +5 -3
  60. package/dist/compliance/report-generator.js.map +1 -1
  61. package/dist/compliance/retention-engine.d.ts.map +1 -1
  62. package/dist/compliance/retention-engine.js +24 -10
  63. package/dist/compliance/retention-engine.js.map +1 -1
  64. package/dist/compliance/siem-exporter.d.ts.map +1 -1
  65. package/dist/compliance/siem-exporter.js +40 -16
  66. package/dist/compliance/siem-exporter.js.map +1 -1
  67. package/dist/config.d.ts +8 -31
  68. package/dist/config.d.ts.map +1 -1
  69. package/dist/config.js +26 -64
  70. package/dist/config.js.map +1 -1
  71. package/dist/errors.d.ts +22 -2
  72. package/dist/errors.d.ts.map +1 -1
  73. package/dist/errors.js +55 -4
  74. package/dist/errors.js.map +1 -1
  75. package/dist/gemini/gemini-client.d.ts +1 -0
  76. package/dist/gemini/gemini-client.d.ts.map +1 -1
  77. package/dist/gemini/gemini-client.js +50 -49
  78. package/dist/gemini/gemini-client.js.map +1 -1
  79. package/dist/gemini/types.d.ts +3 -1
  80. package/dist/gemini/types.d.ts.map +1 -1
  81. package/dist/gemini/types.js.map +1 -1
  82. package/dist/index.d.ts +52 -1
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +412 -89
  85. package/dist/index.js.map +1 -1
  86. package/dist/library/notebook-library.d.ts.map +1 -1
  87. package/dist/library/notebook-library.js +2 -1
  88. package/dist/library/notebook-library.js.map +1 -1
  89. package/dist/logging/query-logger.d.ts +13 -1
  90. package/dist/logging/query-logger.d.ts.map +1 -1
  91. package/dist/logging/query-logger.js +62 -10
  92. package/dist/logging/query-logger.js.map +1 -1
  93. package/dist/notebook-creation/audio-manager.d.ts.map +1 -1
  94. package/dist/notebook-creation/audio-manager.js +19 -24
  95. package/dist/notebook-creation/audio-manager.js.map +1 -1
  96. package/dist/notebook-creation/browser-options.d.ts +28 -0
  97. package/dist/notebook-creation/browser-options.d.ts.map +1 -0
  98. package/dist/notebook-creation/browser-options.js +75 -0
  99. package/dist/notebook-creation/browser-options.js.map +1 -0
  100. package/dist/notebook-creation/data-table-manager.d.ts.map +1 -1
  101. package/dist/notebook-creation/data-table-manager.js +20 -21
  102. package/dist/notebook-creation/data-table-manager.js.map +1 -1
  103. package/dist/notebook-creation/discover-creation-flow.d.ts +0 -6
  104. package/dist/notebook-creation/discover-creation-flow.d.ts.map +1 -1
  105. package/dist/notebook-creation/discover-creation-flow.js +10 -10
  106. package/dist/notebook-creation/discover-creation-flow.js.map +1 -1
  107. package/dist/notebook-creation/discover-quota.d.ts +0 -6
  108. package/dist/notebook-creation/discover-quota.d.ts.map +1 -1
  109. package/dist/notebook-creation/discover-quota.js +12 -13
  110. package/dist/notebook-creation/discover-quota.js.map +1 -1
  111. package/dist/notebook-creation/discover-sources.js +15 -16
  112. package/dist/notebook-creation/discover-sources.js.map +1 -1
  113. package/dist/notebook-creation/dom-scripts.d.ts +10 -0
  114. package/dist/notebook-creation/dom-scripts.d.ts.map +1 -0
  115. package/dist/notebook-creation/dom-scripts.js +58 -0
  116. package/dist/notebook-creation/dom-scripts.js.map +1 -0
  117. package/dist/notebook-creation/errors.d.ts +18 -0
  118. package/dist/notebook-creation/errors.d.ts.map +1 -0
  119. package/dist/notebook-creation/errors.js +20 -0
  120. package/dist/notebook-creation/errors.js.map +1 -0
  121. package/dist/notebook-creation/index.d.ts +2 -1
  122. package/dist/notebook-creation/index.d.ts.map +1 -1
  123. package/dist/notebook-creation/index.js +2 -1
  124. package/dist/notebook-creation/index.js.map +1 -1
  125. package/dist/notebook-creation/notebook-creator.d.ts +6 -82
  126. package/dist/notebook-creation/notebook-creator.d.ts.map +1 -1
  127. package/dist/notebook-creation/notebook-creator.js +49 -835
  128. package/dist/notebook-creation/notebook-creator.js.map +1 -1
  129. package/dist/notebook-creation/notebook-nav.d.ts +19 -0
  130. package/dist/notebook-creation/notebook-nav.d.ts.map +1 -0
  131. package/dist/notebook-creation/notebook-nav.js +240 -0
  132. package/dist/notebook-creation/notebook-nav.js.map +1 -0
  133. package/dist/notebook-creation/notebook-sync.d.ts.map +1 -1
  134. package/dist/notebook-creation/notebook-sync.js +36 -38
  135. package/dist/notebook-creation/notebook-sync.js.map +1 -1
  136. package/dist/notebook-creation/selector-discovery.d.ts.map +1 -1
  137. package/dist/notebook-creation/selector-discovery.js +17 -24
  138. package/dist/notebook-creation/selector-discovery.js.map +1 -1
  139. package/dist/notebook-creation/selectors.d.ts +23 -37
  140. package/dist/notebook-creation/selectors.d.ts.map +1 -1
  141. package/dist/notebook-creation/selectors.js +56 -60
  142. package/dist/notebook-creation/selectors.js.map +1 -1
  143. package/dist/notebook-creation/source-manager.d.ts +25 -0
  144. package/dist/notebook-creation/source-manager.d.ts.map +1 -1
  145. package/dist/notebook-creation/source-manager.js +689 -50
  146. package/dist/notebook-creation/source-manager.js.map +1 -1
  147. package/dist/notebook-creation/types.d.ts +4 -0
  148. package/dist/notebook-creation/types.d.ts.map +1 -1
  149. package/dist/notebook-creation/video-manager.d.ts.map +1 -1
  150. package/dist/notebook-creation/video-manager.js +33 -35
  151. package/dist/notebook-creation/video-manager.js.map +1 -1
  152. package/dist/observability/metrics.d.ts +19 -0
  153. package/dist/observability/metrics.d.ts.map +1 -0
  154. package/dist/observability/metrics.js +35 -0
  155. package/dist/observability/metrics.js.map +1 -0
  156. package/dist/quota/quota-manager.d.ts +11 -3
  157. package/dist/quota/quota-manager.d.ts.map +1 -1
  158. package/dist/quota/quota-manager.js +139 -47
  159. package/dist/quota/quota-manager.js.map +1 -1
  160. package/dist/resources/resource-handlers.d.ts.map +1 -1
  161. package/dist/resources/resource-handlers.js +39 -17
  162. package/dist/resources/resource-handlers.js.map +1 -1
  163. package/dist/session/browser-session.d.ts.map +1 -1
  164. package/dist/session/browser-session.js +22 -22
  165. package/dist/session/browser-session.js.map +1 -1
  166. package/dist/session/session-timeout.d.ts.map +1 -1
  167. package/dist/session/session-timeout.js +4 -2
  168. package/dist/session/session-timeout.js.map +1 -1
  169. package/dist/session/shared-context-manager.d.ts.map +1 -1
  170. package/dist/session/shared-context-manager.js +31 -30
  171. package/dist/session/shared-context-manager.js.map +1 -1
  172. package/dist/tools/annotations.d.ts.map +1 -1
  173. package/dist/tools/annotations.js +9 -56
  174. package/dist/tools/annotations.js.map +1 -1
  175. package/dist/tools/definitions/ask-question.d.ts.map +1 -1
  176. package/dist/tools/definitions/ask-question.js +35 -100
  177. package/dist/tools/definitions/ask-question.js.map +1 -1
  178. package/dist/tools/definitions/chat-history.d.ts +47 -1
  179. package/dist/tools/definitions/chat-history.d.ts.map +1 -1
  180. package/dist/tools/definitions/chat-history.js +10 -1
  181. package/dist/tools/definitions/chat-history.js.map +1 -1
  182. package/dist/tools/definitions/data-tables.d.ts.map +1 -1
  183. package/dist/tools/definitions/data-tables.js +2 -0
  184. package/dist/tools/definitions/data-tables.js.map +1 -1
  185. package/dist/tools/definitions/gemini.d.ts.map +1 -1
  186. package/dist/tools/definitions/gemini.js +54 -11
  187. package/dist/tools/definitions/gemini.js.map +1 -1
  188. package/dist/tools/definitions/notebook-management.d.ts.map +1 -1
  189. package/dist/tools/definitions/notebook-management.js +100 -70
  190. package/dist/tools/definitions/notebook-management.js.map +1 -1
  191. package/dist/tools/definitions/query-history.d.ts +47 -1
  192. package/dist/tools/definitions/query-history.d.ts.map +1 -1
  193. package/dist/tools/definitions/query-history.js +7 -0
  194. package/dist/tools/definitions/query-history.js.map +1 -1
  195. package/dist/tools/definitions/session-management.d.ts.map +1 -1
  196. package/dist/tools/definitions/session-management.js +5 -0
  197. package/dist/tools/definitions/session-management.js.map +1 -1
  198. package/dist/tools/definitions/system.d.ts.map +1 -1
  199. package/dist/tools/definitions/system.js +71 -100
  200. package/dist/tools/definitions/system.js.map +1 -1
  201. package/dist/tools/definitions/video.d.ts.map +1 -1
  202. package/dist/tools/definitions/video.js +4 -1
  203. package/dist/tools/definitions/video.js.map +1 -1
  204. package/dist/tools/definitions.d.ts.map +1 -1
  205. package/dist/tools/definitions.js +4 -0
  206. package/dist/tools/definitions.js.map +1 -1
  207. package/dist/tools/handlers/ask-question.d.ts +1 -1
  208. package/dist/tools/handlers/ask-question.d.ts.map +1 -1
  209. package/dist/tools/handlers/ask-question.js +57 -13
  210. package/dist/tools/handlers/ask-question.js.map +1 -1
  211. package/dist/tools/handlers/audio-video.d.ts.map +1 -1
  212. package/dist/tools/handlers/audio-video.js +22 -161
  213. package/dist/tools/handlers/audio-video.js.map +1 -1
  214. package/dist/tools/handlers/auth.d.ts +14 -19
  215. package/dist/tools/handlers/auth.d.ts.map +1 -1
  216. package/dist/tools/handlers/auth.js +77 -121
  217. package/dist/tools/handlers/auth.js.map +1 -1
  218. package/dist/tools/handlers/error-utils.d.ts +16 -0
  219. package/dist/tools/handlers/error-utils.d.ts.map +1 -0
  220. package/dist/tools/handlers/error-utils.js +39 -0
  221. package/dist/tools/handlers/error-utils.js.map +1 -0
  222. package/dist/tools/handlers/gemini.d.ts +2 -0
  223. package/dist/tools/handlers/gemini.d.ts.map +1 -1
  224. package/dist/tools/handlers/gemini.js +88 -51
  225. package/dist/tools/handlers/gemini.js.map +1 -1
  226. package/dist/tools/handlers/index.d.ts +39 -47
  227. package/dist/tools/handlers/index.d.ts.map +1 -1
  228. package/dist/tools/handlers/index.js +15 -4
  229. package/dist/tools/handlers/index.js.map +1 -1
  230. package/dist/tools/handlers/notebook-creation.d.ts.map +1 -1
  231. package/dist/tools/handlers/notebook-creation.js +102 -86
  232. package/dist/tools/handlers/notebook-creation.js.map +1 -1
  233. package/dist/tools/handlers/notebook-management.d.ts +8 -8
  234. package/dist/tools/handlers/notebook-management.d.ts.map +1 -1
  235. package/dist/tools/handlers/notebook-management.js +34 -80
  236. package/dist/tools/handlers/notebook-management.js.map +1 -1
  237. package/dist/tools/handlers/session-management.d.ts +8 -10
  238. package/dist/tools/handlers/session-management.d.ts.map +1 -1
  239. package/dist/tools/handlers/session-management.js +34 -63
  240. package/dist/tools/handlers/session-management.js.map +1 -1
  241. package/dist/tools/handlers/system.d.ts.map +1 -1
  242. package/dist/tools/handlers/system.js +45 -10
  243. package/dist/tools/handlers/system.js.map +1 -1
  244. package/dist/tools/handlers/types.d.ts +1 -1
  245. package/dist/tools/handlers/types.d.ts.map +1 -1
  246. package/dist/tools/handlers/webhooks.d.ts.map +1 -1
  247. package/dist/tools/handlers/webhooks.js +15 -13
  248. package/dist/tools/handlers/webhooks.js.map +1 -1
  249. package/dist/types.d.ts +7 -17
  250. package/dist/types.d.ts.map +1 -1
  251. package/dist/utils/audit-logger.d.ts +19 -1
  252. package/dist/utils/audit-logger.d.ts.map +1 -1
  253. package/dist/utils/audit-logger.js +198 -30
  254. package/dist/utils/audit-logger.js.map +1 -1
  255. package/dist/utils/cleanup-manager.d.ts.map +1 -1
  256. package/dist/utils/cleanup-manager.js +6 -3
  257. package/dist/utils/cleanup-manager.js.map +1 -1
  258. package/dist/utils/crypto.d.ts +4 -1
  259. package/dist/utils/crypto.d.ts.map +1 -1
  260. package/dist/utils/crypto.js +32 -21
  261. package/dist/utils/crypto.js.map +1 -1
  262. package/dist/utils/file-lock.d.ts.map +1 -1
  263. package/dist/utils/file-lock.js +87 -16
  264. package/dist/utils/file-lock.js.map +1 -1
  265. package/dist/utils/file-permissions.d.ts +2 -0
  266. package/dist/utils/file-permissions.d.ts.map +1 -1
  267. package/dist/utils/file-permissions.js +2 -1
  268. package/dist/utils/file-permissions.js.map +1 -1
  269. package/dist/utils/logger.d.ts +4 -0
  270. package/dist/utils/logger.d.ts.map +1 -1
  271. package/dist/utils/logger.js +16 -0
  272. package/dist/utils/logger.js.map +1 -1
  273. package/dist/utils/page-utils.d.ts +13 -0
  274. package/dist/utils/page-utils.d.ts.map +1 -1
  275. package/dist/utils/page-utils.js +61 -39
  276. package/dist/utils/page-utils.js.map +1 -1
  277. package/dist/utils/response-validator.d.ts.map +1 -1
  278. package/dist/utils/response-validator.js +27 -22
  279. package/dist/utils/response-validator.js.map +1 -1
  280. package/dist/utils/secrets-scanner.d.ts +11 -0
  281. package/dist/utils/secrets-scanner.d.ts.map +1 -1
  282. package/dist/utils/secrets-scanner.js +65 -17
  283. package/dist/utils/secrets-scanner.js.map +1 -1
  284. package/dist/utils/secure-memory.d.ts +9 -31
  285. package/dist/utils/secure-memory.d.ts.map +1 -1
  286. package/dist/utils/secure-memory.js +17 -102
  287. package/dist/utils/secure-memory.js.map +1 -1
  288. package/dist/utils/security.d.ts +4 -3
  289. package/dist/utils/security.d.ts.map +1 -1
  290. package/dist/utils/security.js +43 -13
  291. package/dist/utils/security.js.map +1 -1
  292. package/dist/utils/stealth-utils.d.ts.map +1 -1
  293. package/dist/utils/stealth-utils.js +4 -4
  294. package/dist/utils/stealth-utils.js.map +1 -1
  295. package/dist/webhooks/types.d.ts +4 -0
  296. package/dist/webhooks/types.d.ts.map +1 -1
  297. package/dist/webhooks/webhook-dispatcher.d.ts +80 -12
  298. package/dist/webhooks/webhook-dispatcher.d.ts.map +1 -1
  299. package/dist/webhooks/webhook-dispatcher.js +497 -74
  300. package/dist/webhooks/webhook-dispatcher.js.map +1 -1
  301. package/docs/archive/ISSUES-legacy-2026-04-24.md +644 -0
  302. package/docs/dependency-risk.md +25 -0
  303. package/docs/testing-runbook.md +166 -0
  304. package/docs/usage-guide.md +2 -1
  305. package/package.json +34 -16
@@ -0,0 +1,644 @@
1
+ # NotebookLM-MCP-Secure — Master Issue List
2
+
3
+ > Consolidated from 4 parallel reviewers (MCP Developer, Architect, Skeptic, Sentinel)
4
+ > Generated 2026-04-17 against `main` @ 2973097 (v2026.2.11)
5
+ > Organized by area so you can work file-by-file. Severity inline.
6
+
7
+ ## Status — 2026-04-21 (Day 3 complete)
8
+
9
+ **Closed this session (38 issues):**
10
+
11
+ Highs — security hardening + browser reliability:
12
+ - Auth type safety + token salt: I007, I110
13
+ - Test coverage (browser-session, shared-context-manager, prompt-injection): I128, I215
14
+ - Auth/webhook hardening: I123, I274, I276
15
+ - Notebook creator reliability + typed errors: I159, I161
16
+ - Session headless guard: I131
17
+ - Selector timeout budget: I145
18
+ - Advanced tools env flag + notebook-creator split: I069, I154
19
+
20
+ Mediums — MCP protocol + schema correctness:
21
+ - Response shape (structuredContent, isError, transport tags, shutdown flush): I010, I011, I012, I013, I014
22
+ - Annotation correctness (readOnlyHint, idempotentHint): I035, I036, I037, I038, I039
23
+ - Schema bounds (deep_research, list_documents, query history, chat history, browser timeout, batch): I050, I051, I052, I053, I054, I055, I056, I057
24
+ - Config + types cleanup (getConfig alias, followUpReminder, BrowserOptions, Tool re-export): I024, I025, I026, I029
25
+ - Source tool contracts + as-any reduction: I063, I064, I015
26
+
27
+ **Running totals after Day 3:**
28
+ - 565 tests passing (up from 259 after Day 2)
29
+ - tsc --noEmit clean
30
+
31
+ ---
32
+
33
+ ## Status — 2026-04-18 (Day 2 complete)
34
+
35
+ **Closed across two days of work (42 issues):**
36
+
37
+ Day 2 (2026-04-18) — 14 remaining crits closed:
38
+ - Webhook SSRF: I269
39
+ - Audit integrity cluster: I216, I217, I218
40
+ - Compliance wiring: I243, I244
41
+ - PQ claim honesty: I191
42
+ - Test coverage (6 new test files, +120 tests, 139 → 259): I121, I122, I251, I256, I268, I284
43
+ - Coverage aggregate + CI: I297
44
+
45
+ **Baseline test coverage after Day 2** (vitest --coverage, security-critical modules):
46
+ - mcp-auth.ts: 75.7% lines
47
+ - webhook-dispatcher.ts: 71.4% lines
48
+ - data-erasure.ts: 72.0% lines
49
+ - dsar-handler.ts: 59.0% lines
50
+ - quota-manager.ts: 31.9% lines (patchright-dependent scrapers pending integration tests)
51
+ - auth-manager.ts: 13.1% lines (browser paths pending integration tests)
52
+
53
+ 4 of 6 security-critical modules exceed the 45% target. auth-manager and quota-manager have large patchright-dependent surfaces that require real browser contexts; those belong in an integration harness rather than unit coverage.
54
+
55
+ ---
56
+
57
+ ## Status — 2026-04-17 (evening session)
58
+
59
+ **Closed this session (35 issues):**
60
+
61
+ - Protocol & transport: I001, I002, I003, I004, I012, I135
62
+ - Annotations: I030, I031, I032, I033, I034, I035, I036, I037, I038
63
+ - Selectors: I138, I139, I140, I153, I162
64
+ - Query logger / secrets scanner: I181, I230, I231
65
+ - Tool handlers / file I/O: I315, I316, I317
66
+ - Hygiene / dead code: I068, I198, I199, I200, I201, I309, I310
67
+ - Cert pinning retraction: I174, I175, I176, I177, I178, I179, I180, I331 (via D2 delete)
68
+ - Claims alignment: I075 (privacy-notice cert claim), I248, I250 (policy-docs + privacy claims)
69
+ - Audit retention default: I220, I348 (default flipped to 2555 days)
70
+ - MEDUSA CI gate: I308, I333 (CI workflow added)
71
+ - Compliance scaffolding: I237, I238, I239, I240, I241, I242, I246, I247, I343 (tools now registered; deeper integration for I243/I244 pending)
72
+
73
+ **D1 follow-ups (deeper compliance integration — next session):**
74
+ - I243 — wire `ChangeLog.recordChange` into config mutation sites
75
+ - I244 — subscribe `BreachDetector.checkEvent` to the audit event bus
76
+ - I247 — pipe every `audit.*` call through `siem-exporter`
77
+ - I245 — surface `HealthMonitor` metrics in a periodic audit event
78
+
79
+ ## Legend
80
+ - **SEV**: critical / high / medium / low / nit
81
+ - **Src**: which reviewer surfaced it — M=MCP, A=Architect, S=Skeptic, Sn=Sentinel
82
+ - **ID**: `I###` — stable for tracking
83
+
84
+ ## Summary totals
85
+ - critical: 33
86
+ - high: 88
87
+ - medium: 119
88
+ - low: 76
89
+ - nit: 18
90
+ - **Total unique issues: 334**
91
+
92
+ ## Closeout staging — 2026-04-21
93
+
94
+ **Verification baseline:**
95
+ - `npx tsc --noEmit` passes.
96
+ - `npx vitest run` passes: 48 files, 596 tests.
97
+ - Worktree contains uncommitted closeout edits plus existing `CODEX_BATCH*.md` notes.
98
+
99
+ **Closeout loop progress:**
100
+ - 2026-04-21 loop 1: closed I005, I009, I062, I068, I088, I089, I270. Evidence: `ServerState` now uses concrete types; `deep_research.thinking_level` is accepted and forwarded with regression coverage in `tests/gemini-handler.test.ts`; `tools.yaml` is absent; `ask_question` error audit paths use sanitized error-only payloads; `configure_webhook` is auth-gated and create/update URLs are validated in `WebhookDispatcher`.
101
+ - 2026-04-21 loop 2: closed I070. Evidence: `buildAskQuestionDescription` is trimmed to a compact operational description and `tests/ask-question-definition.test.ts` verifies it stays short and does not include active notebook identifiers or private use-case text.
102
+ - 2026-04-21 loop 3: closed I197. Evidence: `SecureString` now documents the V8 heap caveat directly above the class; `npx tsc --noEmit` passes.
103
+ - 2026-04-21 loop 4: closed I308. Evidence: `.github/workflows/ci.yml` has a `security-scan` job that installs Medusa and runs `medusa scan . --fail-on high`.
104
+ - 2026-04-21 critical loop: closed remaining criticals I138, I139, I140, I153, I174, I175, I237, I238, I239, I240, I241, I242, I315, I316. Evidence: selectors are scoped; add-source DOM enumeration is debug-gated; cert-pinning code/claim path is absent; compliance tools and privacy notice startup are wired; `export_library` and `add_folder` path safety are covered by `tests/tool-file-safety.test.ts`, and blocked `add_folder` paths now return sanitized `ToolResult` failures.
105
+ - 2026-04-21 high loop: closed remaining highs I069, I111, I112, I137, I143, I144, I147, I160, I162, I198, I202, I245, I246, I247, I259. Evidence: advanced tools are gated; MCP auth failed-attempt tracking is bounded and skips the shared unknown bucket; timeout reuse checks exist and periodic polling is tightened; selector errors are logged; response selectors are centralized in `selectors.ts`; partial notebook source failures are surfaced via `partial`; add-source fallback logs no longer include text/aria content; dead/theatrical secure-memory constructs are absent and `secureCompare` pads to a fixed length; health/retention/compliance tools are wired; audit events are bridged to SIEM; browser-profile erasure refuses `SingletonLock`. Focused tests cover timeout polling, selector centralization, partial creation, and browser-profile lock refusal.
106
+ - 2026-04-21 medium loop 1: closed I065, I066, I072, I073, I075, I076, I083, I117, I141, I142, I155, I176, I177, I178, I180, I199, I200, I201, I203, I229, I312, I319. Evidence: `list_sources` documents active-notebook fallback; auth handlers return failures inside `data`/`error` rather than top-level ad-hoc fields; verbose tool descriptions are trimmed; video download gap is documented; `get_notebook_chat_history` exists; direct unused `zod` dependency is removed; singleton MCP auth is tested; non-standard selector syntax and generic chat textarea fallback are removed; NOTEBOOKLM_URL is centralized; cert-pinning files/tests are absent after claim removal; dead secure-memory constructs are absent; compliance audit events are wired; tool definitions rebuild per `list_tools`; failed-attempt tracking is bounded.
107
+ - 2026-04-21 medium loop 2: closed I091, I101, I134, I135, I169, I172, I185, I248, I249, I250, I278, I288, I323, I326. Evidence: `ask_question` rate-limit keys no longer collapse missing session IDs into one literal global bucket; indentation in `handleAskQuestion` is fixed; notebook creation revalidates auth before each source add; `upload_document`/`download_audio` are already auth-gated; stealth utility tests cover auth-path direct typing/click fallbacks; selector discovery uses a local `cssEscape` helper rather than an inline `CSS.escape` polyfill object; high-entropy scanning exempts known local crypto/CSRF contexts; policy/privacy text scopes PQ claims to local at-rest secrets; compliance logging masks actor IPs when present; webhook deliveries now include monotonic `sequence`; quota tier labels are trusted only in account chrome; notebook creation no longer logs and rethrows duplicate top-level failures; failed source stacks are included only under `DEBUG=true`. Focused tests: `tests/secrets-scanner.test.ts`, `tests/quota-manager.test.ts`, `tests/webhook-dispatcher.test.ts`, `tests/policy-docs.test.ts`, `tests/notebook-creator.test.ts`, `tests/stealth-utils.test.ts`; `npx tsc --noEmit` passes.
108
+ - 2026-04-21 medium loop 3: closed I158, I165, I168, I266. Evidence: source-processing timeouts are named constants and bounded; NotebookLM navigation now waits for a create-notebook anchor after DOMContentLoaded/network readiness before relying on selectors; SIEM env parsing is centralized through typed choice/list/integer helpers with invalid values falling back safely. Focused tests: `tests/siem-exporter.test.ts`; `npx tsc --noEmit` passes.
109
+ - 2026-04-21 medium loop 4: closed I074, I092, I156, I157. Evidence: `setup_auth`/`re_auth` now share one internal `authenticate(clearExisting)` implementation and their definitions share one auth-tool schema builder; notebook-management handlers use shared `withNotebookHandler` boilerplate and typed `NotebookEntry`/`LibraryStats` returns; copied-text source DOM traversal is extracted to `dom-scripts.ts` with unit coverage; source processing now uses one waiter with explicit `strict`/`lenient` modes. Focused tests: `tests/tool-descriptions.test.ts`, `tests/auth-manager.test.ts`, `tests/mcp-auth.test.ts`, `tests/notebook-library.test.ts`, `tests/settings-manager.test.ts`, `tests/dom-scripts.test.ts`, `tests/notebook-creator.test.ts`; `npx tsc --noEmit` passes.
110
+ - 2026-04-21 medium loop 5: closed I164, I306, I307. Evidence: quota UI scraping wraps `page.evaluate` in a 30s timeout helper; direct dependencies/devDependencies are exact-pinned and CI uses `npm ci --ignore-scripts`; `docs/dependency-risk.md` documents Patchright fork/postinstall/CVE-response risk and container/sandbox deployment guidance. Focused tests: `tests/quota-manager.test.ts`; `npx tsc --noEmit` passes. Note: `npm install --package-lock-only --ignore-scripts` hung locally and was stopped; package and package-lock root specs are aligned by inspection.
111
+ - 2026-04-21 medium loop 6: closed I120, I234, I235, I261. Evidence: MCP auth now supports scoped `read`/`admin` validation, preserves the existing token as admin, and accepts optional `NLMCP_AUTH_READONLY_TOKEN` for non-admin tools only; admin-only tools request admin scope. Logger supports structured JSON logs via `NLMCP_LOG_FORMAT=json`, and tool calls run in a correlation context with `correlation_id` and `tool`. A lightweight metrics registry records auth failures/lockouts, quota query usage/denials, and webhook delivery results. Data erasure file deletion now opens/stats/deletes directly and treats `ENOENT` as idempotent success instead of checking existence first. Focused tests: `tests/mcp-auth.test.ts`, `tests/logger.test.ts`, `tests/metrics.test.ts`, `tests/quota-manager.test.ts`, `tests/webhook-dispatcher.test.ts`, `tests/data-erasure.test.ts`; `npx tsc --noEmit` passes.
112
+ - 2026-04-24 medium loop 7: closed I301. Evidence: added deterministic property-style invariant coverage for the remaining candidate surfaces without introducing flaky harnesses: generated allowed NotebookLM URLs are accepted and normalized, `sanitizeForLogging` is idempotent across varied secret-bearing inputs, `deriveKey` stays deterministic across generated salt/passphrase pairs, and `encryptPQ`/`decryptPQ` round-trip generated binary payloads. Focused tests: `tests/security.test.ts`, `tests/crypto.test.ts`; `npx tsc --noEmit` passes.
113
+ - 2026-04-24 medium loop 8: closed I015, I299, I300. Evidence: the stale umbrella type-debt finding I015 was reconciled per the tracker closure rule by replacing broad auth/session/page-utils suppressions with explicit browser-context/storage types, removing remaining `as any`/`@ts-expect-error` from `auth-manager`, `browser-session`, and `page-utils`, and retiring the legacy umbrella in favor of non-legacy follow-up notes for any future notebook-creation DOM typing cleanup. I299 now has real property-based and mutation-test infrastructure: `fast-check` is installed, `tests/property-based.test.ts` exercises `security`, `crypto`, and `secrets-scanner`, `package.json` adds `test:property` and `test:mutation`, and `stryker.conf.json` scopes Stryker to the tested security modules; `npx stryker run --dryRunOnly` succeeds. I300 now has a real integration layer: `tests/mcp-server.integration.test.ts` boots `NotebookLMMCPServer` over stdio transport, performs MCP initialize, `tools/list`, and `list_notebooks`; `src/index.ts` is import-safe and accepts an injected transport for integration testing. Full verification: `npx vitest run` passes (50 files, 606 tests), `npx tsc --noEmit` passes, and `npx stryker run --dryRunOnly` passes.
114
+
115
+ **Important tracker note:** the master issue body below is historical evidence. Many entries still read as open even when the fix is already in commits and tests. Treat this staging section as the current operating queue until individual issue lines are rewritten or removed.
116
+
117
+ **Closure rule for this cleanup phase:**
118
+ - Close an issue when there is either a direct commit reference, a passing focused test, or a code inspection proving the stated fix exists.
119
+ - If an issue is only partially handled, split it into the remaining concrete behavior instead of carrying the original broad finding forward.
120
+ - Do not use `CODEX_BATCH.md`, `CODEX_BATCH_HIGHS.md`, or `CODEX_BATCH_MEDIUMS_1.md` as authoritative open lists; they include stale items.
121
+
122
+ **Ready to mark closed / reconcile from commits and inspection:**
123
+ - Protocol, transport, and dispatch: I001, I002, I003, I004, I006, I007, I008, I010, I011, I012, I013, I014, I022, I023.
124
+ - Config, schema, annotations, resources: I024, I025, I026, I027, I028, I029, I030, I031, I032, I033, I034, I035, I036, I037, I038, I039, I042, I043, I044, I045, I046, I047, I048, I049, I050, I051, I052, I053, I054, I055, I056, I057, I058, I059, I063, I064, I071, I102, I103, I104, I105, I106, I107.
125
+ - Auth, sessions, browser reliability: I110, I111, I112, I113, I114, I115, I116, I118, I121, I122, I123, I124, I125, I126, I127, I128, I129, I130, I131, I132, I133, I145, I154, I159, I161, I163.
126
+ - Logging, secrets, response validation, audit: I016, I087, I090, I097, I098, I181, I182, I183, I184, I186, I187, I188, I191, I192, I193, I194, I195, I196, I204, I205, I206, I207, I208, I209, I210, I211, I212, I213, I214, I215, I216, I217, I218, I219, I220, I221, I222, I223, I224, I225, I226, I227, I228, I230, I231, I232, I233, I236, I321, I322, I324, I325, I328.
127
+ - Compliance, webhooks, quota, file locks, tests: I243, I244, I251, I252, I253, I254, I255, I256, I257, I258, I260, I262, I263, I264, I268, I269, I271, I272, I273, I274, I275, I276, I277, I279, I280, I282, I283, I284, I285, I286, I287, I289, I290, I291, I292, I293, I294, I295, I296, I297, I298, I302, I303, I313, I318.
128
+
129
+ **Best next closure batches:**
130
+ 1. **Reconcile all already-fixed criticals/highs in the issue body.** Start with the IDs above and add `Status: closed` notes or move them to a closed archive section.
131
+ 2. **Remaining likely-open medium items to inspect first:** none. The legacy cleanup queue is closed.
132
+ 3. **Then sweep medium/low/nit cleanup by area.** Good candidates are tool-description hygiene, duplicated handler boilerplate, static prompt args, dependency/build hygiene, and remaining claim-verification entries.
133
+
134
+ **Post-closeout follow-up notes (not part of the legacy issue queue):**
135
+ - Notebook-creation browser-evaluate helpers still have optional future typing cleanup opportunities in `audio-manager`, `data-table-manager`, `source-manager`, `video-manager`, and discovery scripts. These are now maintenance follow-ups, not unresolved legacy issue IDs.
136
+
137
+ ---
138
+
139
+ # AREA 1 — Bootstrap, transport, errors (`src/index.ts`, `errors.ts`, `types.ts`, `config.ts`)
140
+
141
+ - **I001** [critical][M] `src/index.ts:204` — `progressToken` read from `args._meta.progressToken`; per SDK, `_meta` lives on `request.params._meta`. Progress notifications are silently dead. **Fix:** `request.params._meta?.progressToken`.
142
+ - **I002** [critical][M] `src/index.ts:205` — `authToken` pulled from the same wrong path. Only env-var fallback works. **Fix:** `request.params._meta?.authToken`.
143
+ - **I003** [critical][M] `src/index.ts:222-232, 255-262, 281-295` — every failure path returns `{ content: [...] }` without `isError: true`. Hosts cannot distinguish tool errors from successful output. **Fix:** add `isError: true` on every error branch.
144
+ - **I004** [critical][Sn] `src/index.ts:214` + `src/tools/handlers/auth.ts:131-234` — `re_auth` not in `TOOLS_REQUIRING_AUTH`. With `NLMCP_AUTH_DISABLED=true`, any client can trigger `clearAllAuthData()` → full credential wipe DoS. **Fix:** add `re_auth`, `setup_auth`, `configure_webhook`, `remove_webhook`, `delete_document` with `forceAuth=true`.
145
+ - **I005** [high][M] `src/index.ts:88` — `completions: {}` capability declared but declaration shape should be verified; comment says "Required for completion/complete handler" — misleading. **Fix:** align capability declaration with SDK spec.
146
+ - **I006** [high][M] `src/index.ts:88` — `logging: {}` capability declared but no `server.sendLoggingMessage` calls anywhere. Advertising unused capability misleads clients. **Fix:** remove or wire up log forwarding.
147
+ - **I007** [high][A] `src/index.ts:214` — `TOOLS_REQUIRING_AUTH` is inline string array; not type-checked against tool-name union. Silent drift as tools are added. **Fix:** `Set<ToolName>` typed against the tool-name type.
148
+ - **I008** [high][A] `src/index.ts:131-191` — `toolRegistry: Map<string, (a: any, p?: any) => Promise<any>>` — fully untyped dispatch table in hot path. **Fix:** type against `ToolHandlers` union.
149
+ - **I009** [high][A] `src/types.ts:103-107` — `ServerState` declares `playwright: any; sessionManager: any; authManager: any`. Three `any` in a shared type. **Fix:** use real types or delete if unused.
150
+ - **I010** [medium][M] `src/index.ts:268-275` — success path stringifies JSON into a single `text` block; doesn't use SDK v1.13+ `structuredContent`. **Fix:** return `{ content, structuredContent: result }`.
151
+ - **I011** [medium][M] `src/index.ts:194-199` — `list_tools` unpaginated; ~42 tools × ~2 KB = ~80 KB per listing. **Fix:** add optional cursor paging.
152
+ - **I012** [medium][M] `src/index.ts:220-233` — auth-failure response is a success-looking object without `isError: true`. **Fix:** set `isError: true`.
153
+ - **I013** [medium][M] `src/index.ts:276-296` — transport/protocol errors indistinguishable from domain errors in JSON body. **Fix:** tag transport errors separately.
154
+ - **I014** [medium][M] `src/index.ts:336-346` — `uncaughtException`/`unhandledRejection` call `requestShutdown` but don't flush in-flight MCP responses. Clients see hung stream. **Fix:** send final error frame before exit.
155
+ - **I015** [medium][A] `src/index.ts` — 301 `as any`/`@ts-expect-error` across 21 files (concentrated in notebook-creation/, session/). With `strict: true` these are real type gaps. **Fix:** audit each; annotate reason; replace with typed alternatives.
156
+ - **I016** [medium][M] `src/errors.ts` — only two error classes (`RateLimitError`, `AuthenticationError`). Handlers resort to stringly-typed `Error` + `.includes()`. **Fix:** add `ValidationError`, `QuotaError`, `NotFoundError`, `UpstreamError`, `BrowserError`, `SessionExpiredError`.
157
+ - **I017** [low][M] `src/errors.ts:33` — `AuthenticationError.suggestCleanup` flag unused. **Fix:** surface in payload or drop.
158
+ - **I018** [low][A] `src/index.ts:404-408` — startup logs truncate descriptions to 80 chars including emoji. Wastes budget on decoration. **Fix:** strip emoji from truncation.
159
+ - **I019** [low][M] `src/index.ts:411` — banner refers to `MCP_INFOS.md` which doesn't exist. **Fix:** remove or create.
160
+ - **I020** [low][M] `src/index.ts:131-191` — `this.toolRegistry!` non-null assertion awkward. **Fix:** initialize in field or use definite-assignment.
161
+ - **I021** [nit][A] `src/index.ts:116` — emoji-prefixed startup `🚀` on stderr. Noisy for clients. **Fix:** gate behind `--verbose`.
162
+ - **I022** [medium][M] `src/tools/definitions.ts:51-72` + `src/index.ts:108` — `buildToolDefinitions` built once at construction; `ask_question` description uses active notebook state but never updates after switching. **Fix:** rebuild on `list_tools` or send `notifications/tools/list_changed`.
163
+ - **I023** [high][A] `src/config.ts:418` — `ensureDirectories()` runs as import side effect. Tests importing config create filesystem state. **Fix:** explicit `initialize()` in server startup.
164
+ - **I024** [medium][A] `src/config.ts:323` — `getConfig()` is a pure alias for `CONFIG`; adds indirection with no encapsulation. **Fix:** delete.
165
+ - **I025** [medium][A] `src/config.ts:179` — 230-char ALLCAPS `followUpReminder` default embedded inline. **Fix:** extract as named constant.
166
+ - **I026** [medium][A] `src/config.ts` — `BrowserOptions`/`applyBrowserOptions` mixed with app config. **Fix:** move to `src/notebook-creation/browser-options.ts`.
167
+ - **I027** [medium][S] `src/config.ts` `parseInteger` — `parseInteger("1e9", 0) === 1` (parseInt stops at `e`). **Fix:** `Number.parseFloat` + `Math.trunc` with reject on non-integer.
168
+ - **I028** [low][S] `src/config.ts` `parseArray` — comma-only delimiter; `;`-separated input is a single element. **Fix:** document or support both.
169
+ - **I029** [medium][A] `src/types.ts:60-70` — `Tool` interface re-declares SDK types. Parallel divergence risk. **Fix:** re-export SDK types.
170
+
171
+ ---
172
+
173
+ # AREA 2 — Tool definitions & annotations (`src/tools/definitions/*`, `annotations.ts`)
174
+
175
+ ### Annotations wrong
176
+ - **I030** [high][M] `annotations.ts:124-133` — `export_library` has `readOnlyHint:true` but writes to disk. **Fix:** `readOnlyHint:false`.
177
+ - **I031** [high][M] `annotations.ts:222-231` — `download_audio` has `readOnlyHint:true` but writes file. **Fix:** `readOnlyHint:false`.
178
+ - **I032** [high][M] `annotations.ts:328-337` — `setup_auth` has `destructiveHint:false` but clears all saved credentials. **Fix:** `destructiveHint:true`.
179
+ - **I033** [high][M] `annotations.ts:358-366` — `get_quota` has `openWorldHint:false` but `sync=true` navigates to notebooklm.google.com. **Fix:** `openWorldHint:true` or split into two tools.
180
+ - **I034** [high][M] `annotations.ts:412-421` — `test_webhook` has `readOnlyHint:true` despite sending HTTP to third-party. **Fix:** `readOnlyHint:false`.
181
+ - **I035** [medium][M] `annotations.ts:30-39` — `ask_question` marked `readOnlyHint:true` but increments `use_count`, mutates quota, writes query log. **Fix:** `readOnlyHint:false`.
182
+ - **I036** [medium][M] `annotations.ts:44-53` — `add_notebook` claims `idempotentHint:true`; `NotebookLibrary.addNotebook` creates new entries per call. **Fix:** verify dedupe or `idempotentHint:false`.
183
+ - **I037** [medium][M] `annotations.ts:470-479` — `upload_document.idempotentHint:true` false; Files API creates new file per upload. **Fix:** `idempotentHint:false`.
184
+ - **I038** [medium][M] `annotations.ts:179-186` — `add_source.idempotentHint:true` false; adds duplicate entries. **Fix:** `idempotentHint:false`.
185
+ - **I039** [medium][M] `annotations.ts:155-162` — `batch_create_notebooks` annotations need review (destructiveHint judgement). **Fix:** clarify.
186
+ - **I040** [nit][M] `annotations.ts:260-269` — `generate_data_table` missing `execution.taskSupport`; takes 1–3 min. **Fix:** add on all long-running tools.
187
+ - **I041** [low][M] `annotations.ts` — every annotation has both top-level `title` and `annotations.title`; already drifted. **Fix:** single source of truth.
188
+
189
+ ### Schema quality — universally missing constraints
190
+ - **I042** [high][M] All `inputSchema` in `src/tools/definitions/*.ts` lack `additionalProperties:false`. Clients can pass arbitrary extras. **Fix:** add universally.
191
+ - **I043** [high][M] `ask-question.ts:124-127` — `question` unbounded; handler validates. **Fix:** `minLength:1, maxLength:N`.
192
+ - **I044** [high][M] `gemini.ts:39-42` — `deep_research.query` unbounded; handler enforces 10000. **Fix:** `maxLength:10000`.
193
+ - **I045** [high][M] `gemini.ts:91-94` — `gemini_query.query` unbounded; handler enforces 30000. **Fix:** `maxLength:30000`.
194
+ - **I046** [high][M] `system.ts:274-276` — `configure_webhook.url` accepts any string. SSRF vector (see I168). **Fix:** `format:"uri"`, scheme+host allowlist.
195
+ - **I047** [high][M] `notebook-management.ts:46-49` — `add_notebook.url`/`update_notebook.url` unconstrained. **Fix:** `format:"uri"`, pattern anchored at notebooklm.google.com.
196
+ - **I048** [high][M] `notebook-management.ts:536-539` — `add_folder.folder_path` unconstrained; path traversal possible. **Fix:** constrain to allowlist; reject `..`/`~`.
197
+ - **I049** [medium][M] `gemini.ts:200-210` — `upload_document.file_path` unconstrained. **Fix:** allowlist upload dirs.
198
+ - **I050** [medium][M] `gemini.ts:48-51` — `deep_research.max_wait_seconds` no bounds; handler caps 600. **Fix:** `minimum:10, maximum:600`.
199
+ - **I051** [medium][M] `gemini.ts:288-296` — `list_documents.page_size` unconstrained. **Fix:** `minimum:1, maximum:1000`.
200
+ - **I052** [medium][M] `query-history.ts:40-43` — `limit` unconstrained. **Fix:** `minimum:1, maximum:500`.
201
+ - **I053** [medium][M] `chat-history.ts:58-65` — `limit`/`offset` unconstrained. **Fix:** add bounds.
202
+ - **I054** [medium][M] `ask-question.ts:163-167` — `browser_options.timeout_ms` unconstrained; MAX_SAFE_INTEGER accepted. **Fix:** `minimum:1000, maximum:300000`.
203
+ - **I055** [medium][M] `ask-question.ts:188-202` — typing/delay fields unconstrained, no cross-field min≤max. **Fix:** per-field bounds + handler cross-check.
204
+ - **I056** [medium][M] `notebook-management.ts:58-76` — topics/tags/content_types arrays unconstrained. **Fix:** `maxItems:50, items:{maxLength:100}`.
205
+ - **I057** [medium][M] `notebook-management.ts:688-731` — `batch_create_notebooks.notebooks` has `maxItems:10` but inner `sources` unbounded. **Fix:** add `maxItems` on nested arrays.
206
+ - **I058** [medium][M] `query-history.ts:31-34` — `date` string no pattern/format. **Fix:** `pattern:"^\\d{4}-\\d{2}-\\d{2}$"`.
207
+ - **I059** [low][M] `gemini.ts:97-100, 375-380` — `model` enum includes gemini-2.5-* retired March 2026; today is 2026-04-17. **Fix:** remove retired values; extract to shared constant.
208
+ - **I060** [low][M] `gemini.ts:122-127` — `response_schema` typed `object` with no sub-schema. **Fix:** document shape or mark `additionalProperties:true`.
209
+ - **I061** [low][M] `video.ts:58-62` — `style` enum doesn't match example at line 45 (`"documentary"` not in enum). **Fix:** align.
210
+
211
+ ### Schema vs handler drift
212
+ - **I062** [high][M] `gemini.ts:52-58` — `deep_research.thinking_level` defined in schema but handler at `gemini.ts:36-41` doesn't accept or forward it. Silently dropped. **Fix:** thread through.
213
+ - **I063** [medium][M] `notebook-management.ts:509` — `add_source` requires `source` only; active-notebook fallback undocumented. **Fix:** `oneOf` for explicit contract.
214
+ - **I064** [medium][M] `notebook-management.ts:601` — `remove_source` same fallback ambiguity. **Fix:** as above.
215
+ - **I065** [medium][M] `notebook-management.ts:438-450` — `list_sources` no required fields; handler throws without active notebook. **Fix:** describe `anyOf`.
216
+ - **I066** [medium][M] `tools/handlers/auth.ts:49-54,157-164` — returns `authenticated:false` via `as any`; `ToolResult` has no such top-level field. **Fix:** place inside `data` or broaden type.
217
+ - **I067** [nit][M] `tools/handlers/notebook-management.ts:18,44,70,105,132,158,206,232` — handlers declared `Promise<ToolResult<{ notebook: any }>>`. **Fix:** use real `Notebook` type.
218
+
219
+ ### Tool surface, descriptions, discoverability
220
+ - **I068** [critical][M,A] `/home/ross/Documents/projects/notebooklm-mcp-secure/tools.yaml` (repo root, untracked) — 935 lines of Portainer MCP tools, not this project. Looks authoritative. **Fix:** delete.
221
+ - **I069** [high][M] 42 total tools in `src/index.ts:132-191`; exceeds 30–40 sweet spot. **Fix:** merge generation-status checks, webhook CRUD, etc.
222
+ - **I070** [high][M] `ask-question.ts:15-99` — `buildAskQuestionDescription` produces ~80 lines per call with emojis and code fences, emitted every `list_tools`. **Fix:** trim to 10–15 lines; move examples to prompt template.
223
+ - **I071** [high][M] `ask-question.ts:11-13,97` — description interpolates `active.name/description/topics` verbatim. Confidential notebook names exposed to every `list_tools` call including proxies/log aggregators. **Fix:** use placeholder; expose details via resource.
224
+ - **I072** [medium][M] `system.ts:127-146` — `cleanup_data` description ~20 lines with 8 enumerated categories. **Fix:** trim.
225
+ - **I073** [medium][M] `notebook-management.ts:6-42` — `add_notebook` description ~35 lines including signup tutorial. **Fix:** trim; move to prompt.
226
+ - **I074** [medium][M] `system.ts:29-124` — `setup_auth` and `re_auth` are ~95% overlapping. **Fix:** merge into `authenticate(clear_existing?:boolean)`.
227
+ - **I075** [medium][M] `download_audio` exists but no `download_video` despite `generate_video_overview`. **Fix:** add or document gap.
228
+ - **I076** [medium][M] No tool to retrieve prior session answer without new query (costs quota). **Fix:** add or promote `get_notebook_chat_history`.
229
+ - **I077** [low][M] `session-management.ts:3-44` — `close_session`/`reset_session` near duplicates. **Fix:** optional merge.
230
+ - **I078** [low][M] `gemini.ts:298-328` — `delete_document` destructive but no `confirm` param. **Fix:** add optional `confirm:boolean`.
231
+ - **I079** [nit][M] Tool titles mix gerunds and imperatives. **Fix:** pick one.
232
+ - **I080** [nit][M] `gemini.ts:41` — `⚠️ REQUIRES GEMINI_API_KEY` prefix in several descriptions. **Fix:** use structured error code instead.
233
+ - **I081** [nit][M] `system.ts:131` — `⚠️ CRITICAL:` warning about Chrome is behaviour, not hint. **Fix:** detect Chrome + fail fast.
234
+ - **I082** [nit][M] `system.ts:135-146` — "ULTRATHINK Deep Cleanup" marketing copy in description. **Fix:** remove.
235
+
236
+ ### Zod / schemas
237
+ - **I083** [medium][M] `package.json:66` declares `zod ^4.3.6` as runtime dep, but zero tool definitions use Zod. All inputSchemas are hand-written JSON. **Fix:** adopt Zod schemas OR remove dep if only SDK-transitive.
238
+ - **I084** [low][M] If Zod kept for SDK use, comment accordingly. **Fix:** add explanatory comment.
239
+
240
+ ### Misc tool wiring
241
+ - **I085** [nit][M] `tools/handlers/system.ts:239,280`; `webhooks.ts:14,70,95,130`; `gemini.ts:530` — unused `_ctx: HandlerContext` parameters. **Fix:** drop or absorb.
242
+ - **I086** [nit][M] `tools/handlers/ask-question.ts:189-190` — `followUpReminder` appended to every answer invisibly. **Fix:** declare in description or expose as field.
243
+
244
+ ---
245
+
246
+ # AREA 3 — Tool handlers (`src/tools/handlers/*`)
247
+
248
+ - **I087** [high][M] All handlers — raw `error.message` serialized back to client. fs errors leak absolute paths; Playwright errors leak CSS selectors; quota errors leak state. **Fix:** error mapper strips paths/selectors/stack fragments.
249
+ - **I088** [high][M] `ask-question.ts:84,96,109,271,285` — `audit.tool("ask_question", args, ...)` passes full args incl. raw question on failure; success path at 251-255 correctly redacts to `question_length`. PII leaks to audit on error paths. **Fix:** mirror redaction on all branches.
250
+ - **I089** [high][Sn] `tools/handlers/webhooks.ts:13-67` — `handleConfigureWebhook` passes url/secret/events/headers to dispatcher with no validation. **Fix:** validate URL (see I046, I168).
251
+ - **I090** [medium][M] `ask-question.ts:266` — heuristic `errorMessage.toLowerCase().includes("rate limit")` triggers for unrelated errors mentioning it. **Fix:** rely on `instanceof RateLimitError`.
252
+ - **I091** [medium][Sn] `tools/handlers/ask-question.ts:76-88` — only tool rate-limited; key falls back to literal `'global'` when no session_id. **Fix:** apply globally in dispatcher; key by auth-identity hash.
253
+ - **I092** [medium][A] `src/tools/handlers/notebook-management.ts` — every handler repeats identical try/catch boilerplate across 10+ handlers. **Fix:** `withErrorHandling(fn)` helper.
254
+ - **I093** [low][M] `audio-video.ts:30-58,91-119,147-175,209-239,276-304,332-360,393-421` — identical notebook-URL resolution duplicated 7×. **Fix:** extract `resolveNotebookUrl(ctx, args)`.
255
+ - **I094** [low][M] `notebook-creation.ts:344-365,414-431,572-589,724-740` — same resolution pattern duplicated in source-manager handlers. **Fix:** same helper.
256
+ - **I095** [low][M] `tools/handlers/auth.ts:49` — error path missing `data` field that success returns. **Fix:** consistent shape.
257
+ - **I096** [low][M] `tools/handlers/notebook-creation.ts:671-674` — loop catches + `log.warning` with raw message (path leakage). **Fix:** sanitise first.
258
+ - **I097** [medium][A] `src/tools/handlers/index.ts:99` — `GeminiClient` unconditionally instantiated at construction even without API key. **Fix:** lazy init behind config check.
259
+ - **I098** [medium][A] `src/tools/handlers/index.ts:99` — `new RateLimiter(100, 60000)` magic numbers. **Fix:** read from CONFIG.
260
+ - **I099** [low][A] `src/tools/handlers/index.ts:289` — `cleanup()` logs with emoji while others don't. **Fix:** consistent tone.
261
+ - **I100** [nit][A] `src/tools/handlers/index.ts:87,289` — facade class delegates except `cleanup()` which contains its own logic. **Fix:** extract `handleCleanup`.
262
+ - **I101** [medium][A] `src/tools/handlers/ask-question.ts:222-226` — progress block indented 6 spaces amid 4-space surroundings. **Fix:** fix indent.
263
+
264
+ ---
265
+
266
+ # AREA 4 — Resources & prompts (`src/resources/*`)
267
+
268
+ - **I102** [high][M] `resources/resource-handlers.ts:182-251` — `throw new Error(...)` in `ReadResourceRequestSchema` interpolates user-provided URI into message. Log-injection vector. **Fix:** sanitise before interpolating.
269
+ - **I103** [medium][M] `resource-handlers.ts:47-111` — resource listing unpaginated. **Fix:** cursor pagination.
270
+ - **I104** [medium][M] `resource-handlers.ts:73-86` — per-notebook `mimeType:"application/json"` but `description` embeds pipe-delimited human text. Neither valid JSON nor structured. **Fix:** keep prose in description; move topics to content.
271
+ - **I105** [medium][M] `resource-handlers.ts:91-108` — `notebooklm://metadata` marked DEPRECATED but still listed. Non-deterministic resource listings. **Fix:** remove or set removal date.
272
+ - **I106** [low][M] `resource-handlers.ts:256-270` — `CompleteRequestSchema` casts `request.params as any` and returns `as any`. **Fix:** narrow with typed discriminant.
273
+ - **I107** [low][M] `resource-handlers.ts:195` — notebook-id regex `^[a-z0-9][a-z0-9-]{0,62}$` caps at 63; library may allow otherwise. **Fix:** centralise validator.
274
+ - **I108** [low][M] `resource-handlers.ts:179-215` — URI template doesn't enforce single path segment; fragile. **Fix:** explicit parse.
275
+ - **I109** [low][M] `resource-handlers.ts:283-308` — prompts declared `arguments:[]`; no parameterisation. **Fix:** add optional args or document static.
276
+
277
+ ---
278
+
279
+ # AREA 5 — Auth & sessions (`src/auth/*`, `src/session/*`)
280
+
281
+ ### Credential storage / token handling
282
+ - **I110** [high][Sn] `src/auth/mcp-auth.ts:189-207` — `hashToken = SHA256(token)` no salt, no PBKDF2/argon2. `auth-token.hash` allows offline brute of 24-byte token. **Fix:** scrypt/argon2id with per-token salt, or HMAC with server secret.
283
+ - **I111** [high][S] `src/auth/mcp-auth.ts:96` — `failedAttempts: Map` unbounded; memory DoS via per-IP/clientId spam. **Fix:** LRU-evict older than `lockoutDuration * 2`.
284
+ - **I112** [high][S] `src/auth/mcp-auth.ts:292` — default `clientId="unknown"`; all unauthenticated traffic shares one lockout tracker. One attacker locks everyone out. **Fix:** require clientId; fall back to client IP.
285
+ - **I113** [medium][Sn] `src/auth/mcp-auth.ts:243-275` — `clientId` defaults to tool name; not a network identity. Shared lockout bucket. **Fix:** derive from auth-token hash.
286
+ - **I114** [high][S] `src/auth/mcp-auth.ts:219-235` — after lockout expiry `tracker.count` reset but `lockoutCount` retained; legit user returning a week later gets escalated on first mistype. **Fix:** decay `lockoutCount` over time.
287
+ - **I115** [medium][S] `src/auth/mcp-auth.ts:324` — `secureCompare(providedHash, this.tokenHash ?? "")` compares hex with empty string (finding I215). **Fix:** initialise `tokenHash` to fixed-length random hex.
288
+ - **I116** [medium][S] `src/auth/mcp-auth.ts:126-137` — token-file validates only length 64; no hex regex. **Fix:** `/^[0-9a-f]{64}$/`.
289
+ - **I117** [medium][S] `src/auth/mcp-auth.ts:99-101` — multiple `MCPAuthenticator` instantiations create divergent tracker state. **Fix:** enforce singleton.
290
+ - **I118** [low][S] `src/auth/mcp-auth.ts:63-86` — dual env flags (`NLMCP_AUTH_DISABLED`/`_ENABLED`) with nuanced precedence; misconfig is silent. **Fix:** startup log with effective state + driving env var.
291
+ - **I119** [low][Sn] `src/auth/mcp-auth.ts:360-371` — `rotateToken()` only via CLI; no automatic rotation. **Fix:** optional periodic rotation.
292
+ - **I120** [medium][Sn] `src/index.ts:202-233` — single MCP token grants all tools incl. destructive. No RBAC. **Fix:** scoped tokens (read-only / admin).
293
+
294
+ ### Auth Manager / login flow
295
+ - **I121** [critical][S] `src/auth/auth-manager.ts:1294 LoC` — ZERO unit tests on entire Google-login automation, cookie expiry, race-retry. **Fix:** unit-test `validateWithRetry`, `validateCookiesExpiry`, `isStateExpired`.
296
+ - **I122** [critical][S] `src/auth/mcp-auth.ts:493 LoC` — ZERO tests on token validation, lockout, exponential backoff. **Fix:** add tests.
297
+ - **I123** [high][S] `src/auth/auth-manager.ts:500-519` — `performLogin` has 10-min fixed upper bound; client disconnect leaves browser open. **Fix:** accept `AbortSignal`.
298
+ - **I124** [high][S] `src/auth/auth-manager.ts:466-506` — 600-iter poll with `waitForTimeout` but no page-closed detection. **Fix:** detect `page.isClosed()` and break.
299
+ - **I125** [medium][S] `src/auth/auth-manager.ts:632-641` — failure screenshot saved to `CONFIG.dataDir` with unrestricted mode; no retention. **Fix:** 0600 perms, crop fields, wire to retention.
300
+ - **I126** [low][S] `src/auth/auth-manager.ts:557-559` — `page.goto` timeout warning only; proceeds regardless. **Fix:** retry once then fail.
301
+ - **I127** [low][S] `src/auth/auth-manager.ts:165-178` `loadSessionStorage` — JSON.parse errors return null, indistinguishable from "no file". **Fix:** typed error.
302
+
303
+ ### Session management
304
+ - **I128** [high][S] `src/session/session-manager.ts` + `shared-context-manager.ts` + `session-timeout.ts` — ZERO tests on session lifecycle, timeouts, context sharing. **Fix:** add tests.
305
+ - **I129** [high][A] `src/session/browser-session.ts:191-204` — `isPageClosedSafe()` uses `void this.page.url()` as side-effectful liveness check; nav error could be misread as "page closed". **Fix:** `page.isClosed()` direct.
306
+ - **I130** [high][S] `src/session/shared-context-manager.ts:258-263` — `close` handler unconditionally nulls `this.globalContext`; if `recreateContext` replaced it first, handler nulls the new live context (use-after-replace). **Fix:** capture ref in closure, compare before null.
307
+ - **I131** [high][S] `src/session/shared-context-manager.ts:76-101` — no lock around headless-change check; two concurrent `getOrCreateContext` can both close & recreate under the file lock. **Fix:** move inside `withLock`.
308
+ - **I132** [medium][Sn] `src/session/shared-context-manager.ts:203-208` — browser launched with `--disable-blink-features=AutomationControlled`, no `--disable-extensions`, no `--site-per-process`. **Fix:** add isolation flags.
309
+ - **I133** [medium][Sn] `src/session/shared-context-manager.ts:192` — `channel:"chrome"` uses system Chrome; if run as root the sandbox self-disables. **Fix:** prefer bundled chromium; detect root.
310
+ - **I134** [medium][S] `src/auth/auth-manager.ts` — `validateWithRetry` called only at start of `initialize`; long source upload may span cookie refresh by parallel process. **Fix:** periodic cookie-validity check in multi-step flows.
311
+ - **I135** [medium][M] `src/index.ts:214-233` — `upload_document` not in force-auth list; unauthenticated clients could upload arbitrary local files if auth disabled. **Fix:** add `upload_document`, `download_audio` to force-auth.
312
+ - **I136** [low][A] `src/session/browser-session.ts` — `SharedContextManager` co-located with browser-session; meaningful seam. **Fix:** give own file.
313
+
314
+ ### Session timeout / lifecycle
315
+ - **I137** [high][Sn] `src/session/session-timeout.ts:262-265` — check runs every 30s with `unref()`; session can run up to 30s past expiry. Controlled by env flag (easy to disable). **Fix:** check on each tool invocation; tighten interval.
316
+
317
+ ---
318
+
319
+ # AREA 6 — Browser automation (`src/notebook-creation/*`)
320
+
321
+ ### Selector fragility
322
+ - **I138** [critical][A] `src/notebook-creation/selectors.ts:148` — `insertButton.primary:'button'` matches every button on the page. **Fix:** text-content or aria-label scoped.
323
+ - **I139** [critical][A] `src/notebook-creation/selectors.ts:29` — `notebookNameInput.primary:'input[type="text"]'` matches first text input anywhere. **Fix:** scope with container/aria-label.
324
+ - **I140** [critical][A] `src/notebook-creation/selectors.ts:137` — `submitButton.primary:'button:has-text("Insert")'` may not work in patchright; falls through to `button[type="submit"]` also non-specific. **Fix:** remove broken primary, promote reliable fallback.
325
+ - **I141** [medium][S] `src/notebook-creation/selectors.ts:122-132` — `chooseFileButton` includes `'a:text("choose file")'` — Playwright-extension syntax that may not exist in patchright. **Fix:** verify and drop.
326
+ - **I142** [medium][S] `src/notebook-creation/selectors.ts:205` — `chatInput.fallbacks:'textarea[aria-label]'` matches any textarea with aria-label. **Fix:** combine with role or container class.
327
+ - **I143** [high][S] `src/notebook-creation/selectors.ts:22-208` — 14 entries dated "confirmed: true, December 2025"; today 2026-04-17 (~4 months stale); no auto-freshness check. **Fix:** scheduled CI job using `run-discovery` + telemetry when fallback succeeds.
328
+ - **I144** [high][S] `src/notebook-creation/selectors.ts:224-242` `findElement` — `catch{}` swallows every error, indistinguishable from "not present". **Fix:** propagate error class, log counts per site.
329
+ - **I145** [high][S] `src/notebook-creation/selectors.ts:247-276` `waitForElement` — divides total timeout by selector count; on slow networks all get too little. **Fix:** full timeout on primary, short on fallbacks, or `Promise.any`.
330
+ - **I146** [low][S] `src/notebook-creation/selectors.ts` — `confirmed:false` selectors used in production with no runtime warning when used. **Fix:** debug log.
331
+ - **I147** [high][A] `src/utils/page-utils.ts` — 13 `RESPONSE_SELECTORS` defined outside `selectors.ts`. Selector knowledge split. **Fix:** move into `selectors.ts` as `responseContent`.
332
+ - **I148** [low][A] `src/notebook-creation/selectors.ts:137,151` — `submitButton` and `insertButton` overlap; comment says "use insertButton for text". **Fix:** merge with usage comment.
333
+ - **I149** [low][A] `src/notebook-creation/selectors.ts:216-276` — `findElement`/`waitForElement` belong in `src/utils/page-utils.ts`. **Fix:** move; keep selectors file data-only.
334
+ - **I150** [nit][A] `src/notebook-creation/selectors.ts:47` — locale note only on some selectors that mix locale-dependent/-independent. **Fix:** add to all.
335
+ - **I151** [nit][A] `src/notebook-creation/selectors.ts:156` — `closeDialogButton` uses British "dialogue"; primary should be class-based. **Fix:** audit.
336
+ - **I152** [low][S] `src/notebook-creation/selectors.ts:209` — `fallbacks: 'as const'` readonly tuple; fragile. **Fix:** ensure non-empty for unconfirmed selectors.
337
+
338
+ ### Notebook creator (god file)
339
+ - **I153** [critical][A] `src/notebook-creation/notebook-creator.ts:365-383` — `clickAddSource()` iterates all buttons on page and logs each on every call. Debug DOM enumeration in production hot path. **Fix:** gate behind `DEBUG`.
340
+ - **I154** [high][A] `src/notebook-creation/notebook-creator.ts` — 1029-line god file with 8 responsibilities. **Fix:** extract `SourceAdder`, `ProcessingWatcher`, `NotebookNavigator`.
341
+ - **I155** [medium][A] `src/notebook-creation/notebook-creator.ts:24` + 4 peers — `NOTEBOOKLM_URL` duplicated in 5 files. **Fix:** import from `src/config.ts`.
342
+ - **I156** [medium][A] `src/notebook-creation/notebook-creator.ts:536-636` — `addTextSource()` inline `page.evaluate()` string doing DOM traversal; no types, no tests. **Fix:** extract to typed `dom-scripts.ts`.
343
+ - **I157** [medium][A] `src/notebook-creation/notebook-creator.ts` — two parallel `waitForSourceProcessing` methods, called inconsistently. **Fix:** document or merge with `lenient:boolean`.
344
+ - **I158** [medium][A] `src/notebook-creation/notebook-creator.ts:876` — magic `60000` timeout. **Fix:** name it.
345
+ - **I159** [high][S] `src/notebook-creation/notebook-creator.ts:151` — `waitForLoadState("networkidle").catch(()=>{})` swallows. **Fix:** log + bounded polling fallback.
346
+ - **I160** [high][S] `src/notebook-creation/notebook-creator.ts:40-116` — partial source failure leaves notebook half-populated; no rollback. **Fix:** atomic delete on failure OR `partial:true` flag + surface.
347
+ - **I161** [high][S] `src/notebook-creation/notebook-creator.ts:159-210` — `clickNewNotebook` throws generic error; doesn't capture tier, URL, screenshot. **Fix:** rich error context on throw.
348
+ - **I162** [high][S] `src/notebook-creation/notebook-creator.ts:365-383` — debug log serializes button text/aria-label; leaks notebook/source titles. **Fix:** gate + sanitize.
349
+ - **I163** [medium][S] `src/notebook-creation/notebook-creator.ts:252-297` — concurrent tool calls on same session interleave operations on shared page. **Fix:** per-page mutex; reject concurrent with BUSY or queue.
350
+ - **I164** [medium][S] Patchright hang — no per-method timeout on `page.evaluate` (`quota-manager.ts:139-182`, `notebook-creator.ts:306-343`). **Fix:** `Promise.race` with 30s timeout.
351
+ - **I165** [medium][S] Network-drop during source add — `waitForSourceProcessing` has no bounded upper-limit; can hang indefinitely. **Fix:** bound max duration.
352
+ - **I166** [low][A] `src/notebook-creation/notebook-creator.ts:352-496` — `clickAddSource()` 145 lines, 3 retry strategies inline. **Fix:** extract named methods.
353
+ - **I167** [low][A] `src/notebook-creation/notebook-creator.ts:644-759` — `addFileSource()` 116 lines, 3 upload strategies. **Fix:** extract.
354
+ - **I168** [medium][S] Default `waitUntil:"domcontentloaded"` returns before Angular hydrates; selectors race. **Fix:** wait on anchor element after DCL.
355
+ - **I169** [medium][S] `src/utils/stealth-utils.ts` — `humanType`/`realisticClick` on every auth path; no tests. Regression breaks 2FA. **Fix:** tests with fake timers.
356
+ - **I170** [nit][S] Timing bias in `humanType` could fingerprint individual user to NotebookLM's anti-abuse. **Fix:** document.
357
+ - **I171** [low][A] `src/notebook-creation/selector-discovery.ts:1` — 548 lines of dev tooling in `src/`, inflating production bundle. **Fix:** move to `scripts/` or `tools/`.
358
+ - **I172** [medium][A] `src/notebook-creation/selector-discovery.ts:535-539` — inline `CSS.escape` polyfill reimplements browser API available in chromium. **Fix:** remove polyfill.
359
+ - **I173** [low][Sn] `src/notebook-creation/notebook-creator.ts:66` — throws `Failed to create notebook - unexpected URL: ${url}` back to MCP client; URL may contain IDs. **Fix:** sanitise.
360
+
361
+ ---
362
+
363
+ # AREA 7 — Crypto, secrets, secure memory
364
+
365
+ ### Cert pinning (CRITICAL — theater + vulnerable)
366
+ - **I174** [critical][Sn] `src/utils/cert-pinning.ts` entire file — `CertificatePinningManager`/`createPinnedAgent`/`validateConnection` never imported from any production code outside tests. Browser launched via patchright `launchPersistentContext` bypasses pin check. **Fix:** wire into every `fetch` (Gemini, webhooks) or delete the claim.
367
+ - **I175** [critical][Sn] `src/utils/cert-pinning.ts:33-67` — pinned SPKI hashes are stale vs real Google cert; `reportOnly:true` means even if wired, everything passes. **Fix:** refresh pins; set `reportOnly:false` by default.
368
+ - **I176** [medium][S] `src/utils/cert-pinning.ts:33-67` — no pin expiry, no kill-switch. **Fix:** annotate pins with `notAfter`, warn 30 days before.
369
+ - **I177** [medium][S] `src/utils/cert-pinning.ts:148-178` — `validateCertificatePin` silently passes when no pins found for host. **Fix:** log debug line.
370
+ - **I178** [medium][Sn] `src/utils/cert-pinning.ts:113-143` — `getCertificateChainHashes` loops break on seen-fingerprint; truncated chains never reach the real root. **Fix:** use `tls.rootCertificates` + pin leaf/intermediate/root.
371
+ - **I179** [low][S] `tests/cert-pinning.test.ts:158-164` — no direct assertion on internal pin array length. **Fix:** expose `getPins(host)`.
372
+ - **I180** [medium][S] `tests/cert-pinning.test.ts:204-249` — tests pass a mocked `getPeerCertificate`; tests validate the mock, not real socket behaviour. **Fix:** integration test against local TLS server.
373
+
374
+ ### Secrets scanner (CRITICAL — unused)
375
+ - **I181** [critical][Sn] `src/utils/secrets-scanner.ts` entire file — `SecretsScanner` defined but never imported by any caller. No output, log, or export goes through it. **Fix:** invoke in ask-question before query-log write and before returning to client.
376
+ - **I182** [high][S] `src/utils/secrets-scanner.ts:443-447` — `clean.replace(match, redacted)` replaces first occurrence only; second copy survives. **Fix:** global replace.
377
+ - **I183** [high][S] `src/utils/secrets-scanner.ts:61-66` — AWS Secret Access Key lookahead `(?=.*aws|.*secret|.*key)` only matches hint AFTER key; misses `AWS_SECRET = "..."`. **Fix:** either-side alternation or entropy scoring.
378
+ - **I184** [high][S] `src/utils/secrets-scanner.ts:245-251` — "High Entropy String" pattern triggers on JWT payloads, PNG data-URIs, GCS object names. At default `minSeverity=low` every response with an image data-URI gets flagged. **Fix:** Shannon entropy >4.5 + context heuristics.
379
+ - **I185** [medium][Sn] `src/utils/secrets-scanner.ts:246-251` — same pattern would flag legitimate ML-KEM `encapsulatedKey`, JWT payloads, CSRF tokens. **Fix:** tighten threshold; exempt known-safe contexts.
380
+ - **I186** [medium][S] `src/utils/secrets-scanner.ts:335-384` — no bound on input length; 100MB regex with catastrophic-backtracking risk. **Fix:** `text.length > maxInputBytes` short-circuit, pattern timeouts.
381
+ - **I187** [medium][S] `src/utils/secrets-scanner.ts:437-441` — `indexOf` on mutated `clean` recomputes offsets against shrunk string; positions wrong. **Fix:** use match `.index` captured at scan time.
382
+ - **I188** [medium][S] `src/utils/secrets-scanner.ts:299` — `NLMCP_SECRETS_IGNORE` split on comma no trim; `"foo, bar"` → `["foo"," bar"]`. **Fix:** trim.
383
+ - **I189** [low][S] `src/utils/secrets-scanner.ts:233-241` — Bearer token requires `A.B.C` JWT shape; opaque tokens missed. **Fix:** second pattern for opaque ≥20 chars.
384
+ - **I190** [low][S] `tests/secrets-scanner.test.ts:295-301` — only asserts critical absence; medium/low matches unchecked. **Fix:** assert exact match set.
385
+
386
+ ### Crypto / key derivation
387
+ - **I191** [critical][Sn] `src/utils/crypto.ts:454-524` — PQ keypair generated locally; PQ secret key saved to disk encrypted with classical key (PBKDF2 from machine-derived). Attacker with local read wraps classical key first and unwraps PQ. No remote PQ recipient. **Fix:** document honestly as "local at-rest hybrid" or integrate remote KMS.
388
+ - **I192** [medium][Sn] `src/utils/crypto.ts:183-186,232-235` — KDF is `SHA256(sharedSecret ‖ salt)` — not HKDF. Weak domain separation. **Fix:** HKDF-SHA256 per RFC 5869.
389
+ - **I193** [medium][Sn] `src/utils/crypto.ts:135-149` — machine-derived key from hostname/platform/arch/CPU/homedir is <30-bit entropy. PBKDF2 doesn't rescue. **Fix:** persist random 32-byte key on first run with 0600.
390
+ - **I194** [medium][S] `src/utils/crypto.ts:125-127` — default PBKDF2 100 000 iters; OWASP 2023 ≥600 000. **Fix:** raise default.
391
+ - **I195** [medium][S] `tests/crypto.test.ts` — no negative test for v1/v2/v3 version mismatch; legacy migration path has no fixture. **Fix:** add frozen v1/v2 fixture + migration assertion.
392
+ - **I196** [medium][S] `tests/crypto.test.ts` — never writes a `.pqenc` file and decrypts on a second module instance. In-memory only. **Fix:** add disk round-trip test.
393
+
394
+ ### Secure memory (theater)
395
+ - **I197** [high][S] `src/utils/secure-memory.ts:47-98` — `new SecureString(value:string)` copies into Buffer but caller's immutable JS string stays in V8 heap (internalised). Wipe zeroes Buffer copy only; real credential persists. **Fix:** document; accept `Buffer` in hot paths.
396
+ - **I198** [high][S] `src/utils/secure-memory.ts:260-287` — `FinalizationRegistry` callback cannot zero Buffer because memory already GC'd. Theater. **Fix:** remove or wire to explicit wipe via ref-counted wrapper.
397
+ - **I199** [medium][A] `src/utils/secure-memory.ts:264` — registry inline comment acknowledges "no-op". **Fix:** remove.
398
+ - **I200** [medium][A] `src/utils/secure-memory.ts:169-229` — `SecureObject<T>` never instantiated. Dead code. **Fix:** remove.
399
+ - **I201** [medium][A] `src/utils/secure-memory.ts:249-287` — `withSecureBuffer`/`createSecureBuffer` never called. Dead code. **Fix:** remove.
400
+ - **I202** [high][S] `src/utils/secure-memory.ts:292-303` — `secureCompare` when lengths differ calls `timingSafeEqual(bufA, Buffer.alloc(bufA.length))`; leaks length via branch and allocation timing. **Fix:** constant-time length pad.
401
+ - **I203** [medium][S] `tests/secure-memory.test.ts:276-293` — test acknowledges "can't easily test GC" and skips asserting FinalizationRegistry. Feature unvalidated. **Fix:** remove theater OR `--expose-gc` test.
402
+ - **I204** [high][Sn] `package.json:89` — `memoryScubbing` typo + only wraps `loginPassword`/`geminiApiKey`. Cookies, session responses, query log entries never scrubbed. **Fix:** rename flag; extend scrubbing.
403
+
404
+ ### General security.ts / RateLimiter
405
+ - **I205** [medium][S] `src/utils/security.ts:234-292` `RateLimiter` — `isAllowed` only deletes keys when zero requests survive; dormant keys leak forever. **Fix:** periodic sweep + max key count.
406
+ - **I206** [low][S] `src/utils/security.ts:89` — path-traversal check `.includes("..")` case-sensitive; `%2E%2E` bypasses. **Fix:** `.toLowerCase()` and rely on `pathname` normalisation.
407
+ - **I207** [low][S] `src/utils/security.ts:203-210` `maskEmail` — 1-char local part reveals full domain. **Fix:** always mask portion of TLD-minus-one.
408
+ - **I208** [low][Sn] `src/utils/security.ts:12-18` — sanitizer has no pattern for `smb://user:pass@host`, `ftp://...`, UNC Windows paths. **Fix:** protocol-agnostic credential-in-URL regex.
409
+ - **I209** [low][Sn] `src/utils/security.ts:316-338` `checkSecurityContext` — warns about visible browser + dev mode but NOT `NLMCP_AUTH_DISABLED=true`. **Fix:** add to warnings.
410
+
411
+ ### Response validator
412
+ - **I210** [medium][Sn] `src/utils/response-validator.ts:65-143` — prompt-injection regex has unbounded `[^\n]{20,}` groups (ReDoS) and catches only English. Non-English/homoglyph/line-broken bypass. **Fix:** size cap; ML/Semgrep-style detectors.
413
+ - **I211** [medium][Sn] `src/utils/response-validator.ts:172` — base64 entropy detection strips legitimate images/JSON base64. High FP rate. **Fix:** entropy-gated, not length-only.
414
+ - **I212** [high][Sn] `src/utils/response-validator.ts:172` — minimum length 100; real secrets <100 slip through. **Fix:** entropy + shorter min + known prefix.
415
+ - **I213** [low][Sn] `src/utils/response-validator.ts:223-225` — sanitization uses `.replace(match,...)`; first match only. **Fix:** `replaceAll`.
416
+ - **I214** [medium][Sn] `src/utils/response-validator.ts:327-336` — pattern rebuilt with `"gi"` but reuses original pattern elsewhere; stateful `lastIndex` skips. **Fix:** fresh-compile or `matchAll`.
417
+ - **I215** [high][S] `src/utils/response-validator.ts` — ZERO tests on prompt-injection patterns. **Fix:** corpus of real injections + benign prompts; measure FP/FN.
418
+
419
+ ---
420
+
421
+ # AREA 8 — Audit, logging, observability
422
+
423
+ ### Audit logger
424
+ - **I216** [critical][S] `src/utils/audit-logger.ts:219-243` — `writeEvent` when `isWriting=true` enqueues and returns immediately; caller sees resolution before durability. Crash loses audit records. **Fix:** per-event promise resolved in flush loop.
425
+ - **I217** [critical][S,Sn] `src/utils/audit-logger.ts:127-145` — on corrupted line `try/catch` silently resets `previousHash="GENESIS"`. Attacker tampers any byte → subsequent events validate fresh chain. **Fix:** refuse start or persist `chain_reset` sentinel event.
426
+ - **I218** [critical][Sn] `src/utils/audit-logger.ts` — `verifyIntegrity()` never invoked at runtime. Chain written but never verified. **Fix:** call at startup + schedule; refuse on mismatch.
427
+ - **I219** [high][S] `src/utils/audit-logger.ts:150-170` `cleanOldLogs` — bare `catch{}`; silent failure on retention cleanup, disk fills. **Fix:** log warning + metric.
428
+ - **I220** [high][Sn] `src/utils/audit-logger.ts:48` — default retention 30 days; contradicts CSSF `sevenYearRetention` claim; hard-deletes before retention engine could archive. **Fix:** default 2555d; honor RetentionPolicy action="archive" first.
429
+ - **I221** [high][S] `src/utils/audit-logger.ts:128` + `quota-manager.ts:105,468,575,655` — day-boundary `new Date().toISOString().split("T")[0]` (UTC). Operators see rotation at non-local hours. **Fix:** document UTC or `NLMCP_TIMEZONE` env.
430
+ - **I222** [high][S] `src/utils/audit-logger.ts:218-243` — only in-process `isWriting` flag; two Node procs interleave bytes; hash-chain divergent across writers. **Fix:** `withLock(currentLogFile)` or per-PID files.
431
+ - **I223** [high][Sn] `src/utils/audit-logger.ts:186` — hash truncated to `slice(0,32)` = 128 bits; halves SHA-256 collision resistance. **Fix:** keep full 64 hex.
432
+ - **I224** [medium][S] `src/utils/audit-logger.ts:509-515` `flush` — busy-waits `setTimeout(...,10)`; non-deterministic shutdown latency. **Fix:** promise chained from flush loop.
433
+ - **I225** [medium][S,Sn] `src/utils/audit-logger.ts:190-214` `sanitizeDetails` — regex over-matches (`public_key_hash`, `authorization_level`) and under-matches (`bearer`); doesn't sanitize values when key is `user_input`/`query`/`url`. **Fix:** allowlist + secrets-scanner on values.
434
+ - **I226** [medium][Sn] `src/utils/audit-logger.ts:150-170` — parses filename with `slice(6,16)`; symlink/arbitrary-date file in dir gets deleted. **Fix:** strict regex `^audit-(\d{4}-\d{2}-\d{2})\.jsonl$`.
435
+ - **I227** [medium][Sn] `src/utils/audit-logger.ts:219-243` + `src/logging/query-logger.ts:162-186` — async write queue in-memory; SIGKILL loses pending events. **Fix:** fsync critical events or WAL journal.
436
+ - **I228** [low][S] `src/utils/audit-logger.ts:137` — `lastEvent.hash` — if schema drifts and event missing hash, `previousHash=undefined`, chain silently disabled. **Fix:** type guard validation.
437
+ - **I229** [medium][A] `src/utils/audit-logger.ts` — audit event types include `"compliance"` but compliance module orphaned. **Fix:** remove until wired.
438
+
439
+ ### Query logger (PII leak)
440
+ - **I230** [critical][Sn] `src/logging/query-logger.ts:191-208` — persists full question AND answer plaintext to daily JSONL. No sanitization, no secret-scanning pass. Contradicts `logSanitization`/`credentialMasking` claims. **Fix:** route through `sanitizeForLogging` + `scanAndRedactSecrets`; or hash/truncate + store length.
441
+ - **I231** [high][Sn] `src/logging/query-logger.ts:206` — debug log leaks first 50 chars of question to stderr unsanitized. **Fix:** pipe through sanitizer or drop preview.
442
+ - **I232** [medium][Sn] `src/logging/query-logger.ts:162-186` — no per-file/per-day size cap; adversarial huge questions fill disk. **Fix:** enforce caps; truncate/reject oversize.
443
+ - **I233** [medium][Sn] `src/logging/query-logger.ts:325-346` `getAllQueries` — loads every JSONL to memory per call; multi-GB DoS on `search_queries`. **Fix:** stream + incremental filter.
444
+
445
+ ### Observability / metrics
446
+ - **I234** [medium][S] No structured log format — `log.info(...)` string interpolation; no correlation IDs across multi-step flows. **Fix:** JSON logs with `{ts, level, correlation_id, event, ...}`.
447
+ - **I235** [medium][S] No metrics emission (no Prometheus/OTel) — rate-limit counters, quota %, webhook-delivery success opaque. **Fix:** OTel counters on dispatch/lockout/session/chaff.
448
+
449
+ ### Audit/config leaks in logs
450
+ - **I236** [medium][Sn] `src/utils/security.ts` + `src/config.ts` — `checkSecurityContext` warns about `LOGIN_PASSWORD` in env but logs the var name; applyEnvOverrides leaves env vars in `process.env` (except password/gemini). **Fix:** blank all sensitive env after read; redact env dumps.
451
+
452
+ ---
453
+
454
+ # AREA 9 — Compliance module (`src/compliance/*`)
455
+
456
+ ### Entire module orphaned
457
+ - **I237** [critical][A,Sn] `src/compliance/index.ts` — `getComplianceTools`/`handleComplianceToolCall` never imported outside `compliance/`. Entire module (18 files, ~11k LoC) produces zero runtime effect. **Fix:** wire into `src/tools/definitions.ts` + `src/index.ts`, OR delete the module entirely.
458
+ - **I238** [critical][Sn] `src/compliance/consent-manager.ts` — `grant_consent`/`revoke_consent` tools unreachable. Users cannot record consent via MCP. **Fix:** register.
459
+ - **I239** [critical][Sn] `src/compliance/dsar-handler.ts` — `submit_dsar_request` unreachable. **Fix:** register.
460
+ - **I240** [critical][Sn] `src/compliance/data-export.ts` — `DataExporter.exportAll` unreachable. **Fix:** register.
461
+ - **I241** [critical][Sn] `src/compliance/data-erasure.ts` — scoped erasure unreachable. **Fix:** register.
462
+ - **I242** [critical][Sn] `src/compliance/privacy-notice.ts` — `needsDisplay()`/`acknowledge()` never invoked at server start. **Fix:** call at startup before data processing.
463
+ - **I243** [critical][Sn] `src/compliance/change-log.ts` — `ChangeLog.recordChange` never invoked from any mutation site. No change audit. **Fix:** invoke from config/settings/token-rotation/quota-tier/webhook-CRUD sites.
464
+ - **I244** [critical][Sn] `src/compliance/incident-manager.ts` + `breach-detection.ts` — `BreachDetector.checkEvent` never fed events; `report_incident` unreachable. No automatic detection. **Fix:** subscribe detector to audit/event bus; auto-escalate high-severity.
465
+ - **I245** [high][Sn] `src/compliance/health-monitor.ts` — runs in constructor but results not exported anywhere. **Fix:** wire to audit events or SIEM.
466
+ - **I246** [high][Sn] `src/compliance/retention-engine.ts:602` — `runRetentionPolicies()` never invoked. No scheduler. **Fix:** `setInterval` on startup.
467
+ - **I247** [high][Sn] `src/compliance/siem-exporter.ts:23,158` — opt-in + `exportToSIEM`/`queueEvent` never called from production audit/tool paths. Even enabled, no events flow. **Fix:** pipe every `audit.*` through.
468
+ - **I248** [medium][Sn] `src/compliance/policy-docs.ts` — default policies claim cert pinning + PQ encryption (false per I174, I191). **Fix:** align text with reality.
469
+ - **I249** [medium][Sn] `src/compliance/compliance-logger.ts:41-63` — `maskIP` defined but no caller passes IP (stdio transport has none). Theater. **Fix:** remove.
470
+ - **I250** [medium][Sn] `src/compliance/privacy-notice-text.ts:82,110,194` — notice text claims PQ + cert pinning protect user; false. **Fix:** align.
471
+
472
+ ### DSAR safety
473
+ - **I251** [critical][S] `src/compliance/dsar-handler.ts` — ZERO tests on GDPR Article 15 handler. **Fix:** add tests.
474
+ - **I252** [high][Sn] `src/compliance/dsar-handler.ts:175-229` — `generateResponse` hardcodes `subject_verified:true`. Any MCP caller (if wired) exfils full data inventory. **Fix:** require signed verification claim from authenticated identity; cooldown.
475
+ - **I253** [high][S] `src/compliance/dsar-handler.ts:66-80` `load` — guard `if (this.loaded) return` not async-safe; two concurrent `submitRequest` both load + save, losing entries. **Fix:** `this.loadPromise = this.loadPromise ?? this._load()`.
476
+ - **I254** [high][S] `src/compliance/dsar-handler.ts:85-96` `save` — no file lock; concurrent DSAR submissions race. **Fix:** `withLock(this.requestsFile, ...)`.
477
+ - **I255** [medium][S] `src/compliance/dsar-handler.ts:251-290` `getDataSample` — reads raw file <10KB into response; may include PII. **Fix:** pipe through `secretsScanner.scanAndRedact`.
478
+
479
+ ### Data erasure
480
+ - **I256** [critical][S] `src/compliance/data-erasure.ts` — ZERO tests on GDPR Article 17. **Fix:** assert files deleted, secureOverwrite passes, verification flag truthful.
481
+ - **I257** [high][S,Sn] `src/compliance/data-erasure.ts:43-60` — multi-pass overwrite ineffective on SSDs (wear leveling), COW (btrfs/ZFS/APFS), journaled FS. GDPR Article 17 claim not honourable. **Fix:** document caveats; prefer crypto-shredding.
482
+ - **I258** [high][S] `src/compliance/data-erasure.ts:49-56` — `fs.writeFileSync` no `fsync`; buffers may never reach disk before unlink. **Fix:** openSync + write + fsyncSync + close + unlink.
483
+ - **I259** [high][S] `src/compliance/data-erasure.ts:365-405` `eraseBrowserData` — deletes running Chrome profile without stopping Chrome; on Linux Chrome writes back on close. **Fix:** `SharedContextManager.closeContext()` first; verify no `SingletonLock`.
484
+ - **I260** [high][S] `src/compliance/data-erasure.ts:57-60,94-97,102-104,145-148,355-356,462-464` — every `catch` swallows; `verified=false` with no `error` field. **Fix:** capture `err.message` in `result.error`.
485
+ - **I261** [medium][S] TOCTOU — `fs.existsSync` check + later read/delete races. **Fix:** open with exclusive flags.
486
+ - **I262** [medium][S] `src/compliance/data-erasure.ts:30-38` — `DEFAULT_SCOPE.audit_logs:false`; user may forget to tick, tool invocations remain. **Fix:** print explicit note.
487
+ - **I263** [low][S] `src/compliance/data-erasure.ts:42` — `passes:number=3` allows 0; zero-pass "success". **Fix:** `Math.max(1, passes)`.
488
+ - **I264** [high][S] `src/compliance/consent-manager.ts`, `privacy-notice.ts`, `breach-detection.ts`, `retention-engine.ts`, `compliance-logger.ts`, `incident-manager.ts` — ZERO tests. **Fix:** per-module persistence round-trip + state transitions.
489
+ - **I265** [low][A] `src/compliance/` — marketed as SOC2/CSSF compliance for an MCP wrapping a web app. Genuine SOC2 needs org controls. **Fix:** README should distinguish code-enforced vs process-required.
490
+ - **I266** [medium][A] `src/compliance/siem-exporter.ts` — reads 12 env vars with inline `parseInt`/casts; duplicates config.ts pattern. **Fix:** consolidate into `applyEnvOverrides`.
491
+ - **I267** [low][Sn] `src/compliance/retention-engine.ts:29-73,125-127` — mutable singleton; custom policy with same id silently overrides CSSF default. **Fix:** freeze defaults, forbid id collisions.
492
+
493
+ ---
494
+
495
+ # AREA 10 — Webhooks (`src/webhooks/*`)
496
+
497
+ - **I268** [critical][S] `src/webhooks/webhook-dispatcher.ts` — ZERO tests on outbound HTTP + HMAC. **Fix:** retry/sig/format tests.
498
+ - **I269** [critical][Sn] `src/webhooks/webhook-dispatcher.ts:82-121` — env-initialised webhooks (`NLMCP_WEBHOOK_URL`, `NLMCP_SLACK_WEBHOOK_URL`, etc.) passed to `addWebhook` with zero validation. Attacker with env-write → `http://169.254.169.254/latest/meta-data/` SSRF. **Fix:** HTTPS-only allowlist, block RFC1918/link-local/metadata.
499
+ - **I270** [critical][Sn] `src/webhooks/webhook-dispatcher.ts` + `tools/handlers/webhooks.ts` — `configure_webhook` accepts arbitrary URL, not in TOOLS_REQUIRING_AUTH. Unauthenticated SSRF if auth disabled. **Fix:** require auth; validate host.
500
+ - **I271** [high][S,Sn] `src/webhooks/webhook-dispatcher.ts:430-437,433-437` — HMAC signs payload only; no timestamp/nonce. Replay indefinite. **Fix:** `X-Webhook-Timestamp` + reject >5 min old; include timestamp in signed payload.
501
+ - **I272** [high][Sn] `src/webhooks/webhook-dispatcher.ts:69-76` — `webhooks.json` written plain JSON; `secret` field plaintext, bypasses PQ/ChaCha20. **Fix:** route through `getSecureStorage().save()`.
502
+ - **I273** [high][Sn] `src/webhooks/webhook-dispatcher.ts:169-205` — payload JSON.stringified without secret-scanning; `security_incident` event dumps payload unfiltered. **Fix:** run `scanAndRedactSecrets` on outbound.
503
+ - **I274** [high][S] `src/webhooks/webhook-dispatcher.ts:156-244` `sendWithRetry` — records only final attempt; attempts 1..N-1 have no delivery record. **Fix:** record on every attempt with `attempt` field.
504
+ - **I275** [high][S] `src/webhooks/webhook-dispatcher.ts:122-130,137-143` — subscribes to `*`; serial `await` in `for` loop. One slow webhook blocks all deliveries. **Fix:** `Promise.allSettled`.
505
+ - **I276** [high][S] Retry bomb — 10 webhooks × one flaky endpoint × 100 events → 7000 retry-seconds per webhook. **Fix:** circuit-breaker disabling after N failures.
506
+ - **I277** [medium][S] `src/webhooks/webhook-dispatcher.ts:69-76` `saveStore` — no file lock; concurrent `addWebhook` loses updates. **Fix:** `withLock`.
507
+ - **I278** [medium][S] `src/webhooks/webhook-dispatcher.ts:137-143` — no cross-process ordering guarantee. **Fix:** document; include `sequence` field.
508
+ - **I279** [medium][S] `src/webhooks/webhook-dispatcher.ts:442-447` — `deliveryHistory` in-memory capped 100; no disk persistence. Can't audit yesterday. **Fix:** rotating log file.
509
+ - **I280** [medium][S] `src/webhooks/webhook-dispatcher.ts:181` — UA `"notebooklm-mcp/1.7.0"` hardcoded; ships as v2026.2.11. **Fix:** import from generated `version.ts`.
510
+ - **I281** [low][S] `src/webhooks/webhook-dispatcher.ts:175-189` — `AbortController` timeout doesn't distinguish "slow server" from "DNS failure". **Fix:** catch `AbortError` specifically.
511
+ - **I282** [medium][Sn] `src/webhooks/webhook-dispatcher.ts:183` — `webhook.headers` spread to outbound fetch; no filter. Attacker sets `Host`, `X-Forwarded-For`, `Authorization`. **Fix:** deny CRLF; strip disallowed header names.
512
+ - **I283** [low][Sn] `src/webhooks/webhook-dispatcher.ts:177` — default follow-redirects; pre-allowlisted host redirects to IMDS. **Fix:** `redirect:"error"` or manual re-validation.
513
+
514
+ ---
515
+
516
+ # AREA 11 — Quota & rate limiting (`src/quota/*`)
517
+
518
+ - **I284** [critical][S] `src/quota/quota-manager.ts:724 LoC` — ZERO tests. **Fix:** day-rollover, concurrent atomic increment, tier detection tests.
519
+ - **I285** [high][S] `src/quota/quota-manager.ts:574-599` `canMakeQuery` — mutates settings (reset-on-new-day) during read-only check; two concurrent callers race. **Fix:** move reset to atomic increment only; make canMakeQuery pure.
520
+ - **I286** [high][S] `src/quota/quota-manager.ts:457-461` `incrementNotebookCount` — sync no lock; two procs lose increment. **Fix:** mirror `incrementNotebookCountAtomic`.
521
+ - **I287** [high][S] `src/quota/quota-manager.ts:654-713` `getDetailedStatus` — mutates settings on rollover but doesn't persist. **Fix:** persist reset.
522
+ - **I288** [medium][S] `src/quota/quota-manager.ts:138-182` — tier detection uses `document.body.innerText.toUpperCase()`; notebook source containing "ULTRA" mis-detects tier. **Fix:** scope query to account chrome.
523
+ - **I289** [medium][S] `src/quota/quota-manager.ts:486-519` `incrementQueryCountAtomic` — inside lock, `fs.writeFileSync` errors caught + logged only; caller thinks atomic succeeded. **Fix:** rethrow.
524
+ - **I290** [medium][S] `src/quota/quota-manager.ts` — no upper clamp on `queriesUsedToday` from UI scrape; huge UI integer permanently locks user. **Fix:** clamp to limits.
525
+
526
+ ---
527
+
528
+ # AREA 12 — File I/O, locks, permissions
529
+
530
+ - **I291** [high][S] `src/utils/file-lock.ts` — ZERO tests. Cross-process advisory lock w/ stale detection unverified. **Fix:** spawned child procs hold/release tests.
531
+ - **I292** [high][S] `src/utils/file-lock.ts:38-42` — default stale-threshold 30s; `performSetup` holds auth lock up to 10 min and other callers lack overrides. Stale-reaper steals lock mid-write. **Fix:** 60s minimum; callers must override up-front.
532
+ - **I293** [medium][S] `src/utils/file-lock.ts:127-130` `wx` — advisory on NFS, unreliable on network shares. **Fix:** document local-FS-only; detect NFS + refuse.
533
+ - **I294** [medium][S] `src/utils/file-lock.ts:155-178` `release` — verifies lockId then unlinks; narrow race. **Fix:** atomic rename-to-unique + unlink.
534
+ - **I295** [medium][S] `src/utils/file-lock.ts:225-243` `isLocked` — returns false on JSON parse error; corrupted lock → "unlocked". **Fix:** treat corrupted as locked.
535
+ - **I296** [low][S] `src/utils/file-lock.ts:250-263` `forceUnlock` — exported; no liveness check on other owners. **Fix:** CLI-only with `--force` + warning.
536
+
537
+ ---
538
+
539
+ # AREA 13 — Tests & coverage
540
+
541
+ - **I297** [critical][S] Aggregate coverage — 6 test files cover ~2000/22900 LoC = **<10%**. Zero-test modules (critical): auth/, compliance/, events/, gemini/, library/, logging/, notebook-creation/, quota/, resources/, session/, tools/, webhooks/, audit-logger, cleanup-manager, cli-handler, file-lock, file-permissions, logger, page-utils, response-validator, settings-manager, stealth-utils, tool-validation, errors, index. **Fix:** tests for every security-critical module (individual findings above reference specific files).
542
+ - **I298** [medium][S] No `--random-order`; `secrets-scanner.test.ts` shares scanner state via module-level `beforeEach`. **Fix:** `vitest --sequence.shuffle` in CI.
543
+ - **I299** [medium][S] No mutation testing (Stryker), no property-based (fast-check). **Fix:** add Stryker for tested modules; fast-check for `secrets-scanner`, `security`, `crypto` round-trips.
544
+ - **I300** [medium][S] `tests/` has no `.integration.test.ts` layer; no smoke test booting the MCP server. **Fix:** integration test spawning server + `list_notebooks`.
545
+ - **I301** [medium][S] Property-test candidates — `validateNotebookUrl`, `sanitizeForLogging` (idempotence), `encryptPQ/decryptPQ` round-trip, `deriveKey` determinism. **Fix:** fast-check generators.
546
+ - **I302** [low][S] No test for log rotation/retention — `audit-logger.cleanOldLogs`, `logging/query-logger`. **Fix:** mock fs + advance "now" 31 days, assert unlink.
547
+ - **I303** [low][S] No test for `RateLimiter` memory boundedness under distinct keys. **Fix:** 100k distinct keys, assert bounded Map size (requires sweeper I205).
548
+ - **I304** [low][S] `tests/config.test.ts:113` — range-clamping tests observe post-hoc state; don't reload with env. **Fix:** set env, reload module, assert clamped.
549
+ - **I305** [low][A] Test strategy — no tests for handlers/session/notebook-creation even with mocked patchright. **Fix:** at minimum smoke tests with fixtures.
550
+
551
+ ---
552
+
553
+ # AREA 14 — Build, deps, repo hygiene
554
+
555
+ - **I306** [medium][Sn] `package.json:57-67` — all deps caret-ranged (`^`); no `npm ci --ignore-scripts`, no `overrides`, no integrity enforcement. **Fix:** exact pins; `npm ci --ignore-scripts` in CI.
556
+ - **I307** [medium][Sn] `package.json:64` — `patchright` is a single-maintainer stealth fork of playwright; postinstall scripts download system chrome drivers; CVE response lags upstream. **Fix:** document; isolate via container.
557
+ - **I308** [high][Sn] `package.json:19` — `security-scan` script `medusa scan` exists but CI workflow only runs `npm ci/build/test`. Never gated. **Fix:** add CI step `medusa scan . --fail-on high` or drop claim.
558
+ - **I309** [low][Sn] `medusa-env/` committed — entire Python virtualenv (binaries, site-packages) in repo; CVE surface. **Fix:** gitignore; remove from publish (package.json `files` allowlist should prevent leakage — verify).
559
+ - **I310** [low][Sn] `mcp-publisher.tar.gz` at repo root. **Fix:** gitignore.
560
+ - **I311** [low][A] `tsconfig.json` — `skipLibCheck:true` suppresses patchright type regressions. **Fix:** set false + fix remaining, or scope to `skipDefaultLibCheck`.
561
+ - **I312** [medium][M] `src/tools/definitions.ts:51-72` — definitions built once but `ask_question` description uses active notebook state. **Fix:** rebuild per `list_tools` or `tools/list_changed`.
562
+ - **I313** [low][A] `src/index.ts:214` — no build-time assertion that every tool is in TOOLS_REQUIRING_AUTH or explicit opt-out list. New tool silently unauthenticated. **Fix:** startup check.
563
+ - **I314** [low][Sn] MCP token file — no log when present vs absent. Startup logs not verbose enough re: auth state. **Fix:** consolidated startup summary.
564
+
565
+ ---
566
+
567
+ # AREA 15 — Additional security
568
+
569
+ - **I315** [critical][Sn] `src/tools/handlers/system.ts:37-72` `export_library` — `fs.writeFileSync(outputPath, content, {mode:0o600})`; `outputPath` user-supplied zero validation. Arbitrary write to `/etc/cron.d/*`, `~/.ssh/authorized_keys`, shell rc files if perms allow. **Fix:** resolve under base dir; reject escape.
570
+ - **I316** [critical][Sn] `src/tools/handlers/notebook-creation.ts:490-534` `add_folder` — arbitrary `folder_path` recursively scanned + uploaded to Google NotebookLM. Exfiltrates `~/.ssh`, `~/.aws/credentials`, secrets via legit-looking user action. **Fix:** allowlist base dir; user confirmation with file list.
571
+ - **I317** [low][Sn] `src/tools/handlers/system.ts:41-52` — CSV export doesn't escape leading formulas. Notebook name `=cmd|...` → DDE RCE when opened in Excel (CWE-1236). **Fix:** prefix with `'`.
572
+ - **I318** [low][Sn] `src/config.ts:287` — `NLMCP_FOLLOW_UP_REMINDER` env appended to every response; prompt-injection vector if env attacker-controllable. **Fix:** validate/escape.
573
+ - **I319** [medium][Sn] `src/auth/mcp-auth.ts:243-275` `recordFailedAttempt` — `lockoutCount` unbounded in Map; memory DoS. **Fix:** cap Map size; evict after `lockedUntil + grace`.
574
+ - **I320** [low][Sn] `src/auth/mcp-auth.ts:182-184` `generateToken` — 24 bytes (192 bits) OK. **Fix:** noted for completeness.
575
+ - **I321** [high][Sn] `src/config.ts:216-228,295-304` — only `LOGIN_PASSWORD`/`GEMINI_API_KEY` wrapped in `SecureCredential`; MCP auth token, webhook secrets, SIEM API key plaintext in env for lifetime. **Fix:** wrap all consistently.
576
+
577
+ ---
578
+
579
+ # AREA 16 — Error handling (cross-cutting)
580
+
581
+ - **I322** [high][S] 145 bare `catch{}` across codebase. Notable: `notebook-creation/selectors.ts:236,270`, `notebook-sync.ts:133,256`, `video-manager.ts:103,233,468`, `compliance/data-erasure.ts:*`, `utils/file-permissions.ts:222`. Swallow root causes. **Fix:** minimum `catch(e){ log.debug(\`${ctx}: ${e}\`); }`; ESLint rule.
582
+ - **I323** [medium][S] `src/notebook-creation/notebook-creator.ts:108-115` — catch logs + rethrows; caller logs too. Duplicate traces. **Fix:** wrap in custom error or drop log.
583
+ - **I324** [medium][S] `src/utils/secrets-scanner.ts:400-421` — `await audit.security(...)` inside `scanAndRedact`; audit failure breaks main flow. **Fix:** wrap audit call.
584
+ - **I325** [medium][S] `src/session/shared-context-manager.ts:57`, `src/index.ts:330` — unawaited `void` promises; errors never surface. **Fix:** `.catch(err => log.warning(...))`.
585
+ - **I326** [medium][S] `src/notebook-creation/notebook-creator.ts:85-89` — `error.message` stripped from `failedSources`; debugging needs stack. **Fix:** persist stack behind debug flag.
586
+ - **I327** [low][S] Info disclosure in throws — URLs with identifiers interpolated into error messages. **Fix:** sanitise before throwing to MCP.
587
+ - **I328** [low][Sn] `src/index.ts:276-296` — errors propagated to client with full stack/path/URL. **Fix:** log server-side; return generic + correlation ID.
588
+
589
+ ---
590
+
591
+ # AREA 17 — Claims verification summary
592
+
593
+ Cross-reference all findings above against `package.json` claims. Each claim is overclaimed unless code proof exists.
594
+
595
+ - **I329** [claim] `securityHardening.logSanitization` — overclaimed (see I230, I231, I225)
596
+ - **I330** [claim] `securityHardening.credentialMasking` — partial (see I225)
597
+ - **I331** [claim] `securityHardening.certificatePinning` — theater (see I174–I180)
598
+ - **I332** [claim] `securityHardening.secretsScanning` — theater (see I181)
599
+ - **I333** [claim] `securityHardening.medusaIntegration` — theater (see I308)
600
+ - **I334** [claim] `securityHardening.postQuantumEncryption` — overclaimed (see I191–I194)
601
+ - **I335** [claim] `securityHardening.memoryScubbing` — typo + partial (see I204)
602
+ - **I336** [claim] `securityHardening.auditLogging` — partial (see I217, I218, I220, I222, I223)
603
+ - **I337** [claim] `securityHardening.rateLimiting` — partial (see I091)
604
+ - **I338** [claim] `securityHardening.sessionTimeout` — partial (see I137)
605
+ - **I339** [claim] `securityHardening.inputValidation` / `urlWhitelisting` — partial (see I089, I046, I270)
606
+ - **I340** [claim] `securityHardening.exponentialBackoffLockout` — partial (see I113, I319)
607
+ - **I341** [claim] `securityHardening.credentialIsolation` — partial (see I321)
608
+ - **I342** [claim] `securityHardening.responseValidation` — partial (see I210–I215)
609
+ - **I343** [claim] `gdpr.consentManagement`/`dataSubjectRights`/`dataPortability`/`rightToErasure`/`privacyNotice` — ALL theater (see I237–I242)
610
+ - **I344** [claim] `soc2.hashChainedAuditLogs` — partial (I217, I218, I223)
611
+ - **I345** [claim] `soc2.changeManagement` — theater (I243)
612
+ - **I346** [claim] `soc2.incidentResponse` — theater (I244)
613
+ - **I347** [claim] `soc2.availabilityMonitoring` — theater (I245)
614
+ - **I348** [claim] `cssf.sevenYearRetention` — overclaimed (I220) + scheduler never runs (I246)
615
+ - **I349** [claim] `cssf.siemIntegration` — overclaimed (I247)
616
+ - **I350** [claim] `cssf.policyDocumentation` — partial (I248, I250)
617
+
618
+ ---
619
+
620
+ # Suggested work ordering
621
+
622
+ 1. **Delete the wrong `tools.yaml` from repo root** (I068) — 1 min, removes confusion
623
+ 2. **Critical protocol/wiring bugs** (I001, I002, I003) — silently broken since shipping
624
+ 3. **Auth-bypass DoS** (I004) — unauth clients can wipe all credentials if auth disabled
625
+ 4. **Claims vs code gap** — either wire up or retract:
626
+ - Compliance module (I237 — decide: wire or delete?)
627
+ - Secrets scanner (I181)
628
+ - Cert pinning (I174)
629
+ - `medusaIntegration` CI gate (I308)
630
+ - `sevenYearRetention` default (I220)
631
+ 5. **Selector fragility** (I138, I139, I140, I153) — one DOM change breaks everything
632
+ 6. **SSRF + arbitrary file I/O via tools** (I269, I270, I315, I316)
633
+ 7. **Query logger PII leak** (I230)
634
+ 8. **Audit log tamper/durability** (I216, I217, I218, I222, I223)
635
+ 9. **Annotation correctness** (I030–I041) — one pass through annotations.ts
636
+ 10. **Schema constraints** (I042 + I043–I058) — one pass adding `additionalProperties:false`, bounds, patterns
637
+ 11. **Tool surface trimming** (I069, I070, I071)
638
+ 12. **Test coverage** — start with I121, I122, I128, I251, I256, I268, I284 (security-critical modules with ZERO tests)
639
+ 13. Everything else, ordered by file
640
+
641
+ ## Per-area triage priority
642
+
643
+ - **Highest-impact files:** `src/index.ts`, `src/utils/audit-logger.ts`, `src/logging/query-logger.ts`, `src/utils/cert-pinning.ts`, `src/utils/secrets-scanner.ts`, `src/compliance/index.ts`, `src/webhooks/webhook-dispatcher.ts`, `src/notebook-creation/selectors.ts`, `src/tools/annotations.ts`, `src/tools/handlers/webhooks.ts`, `package.json`.
644
+ - **Most-likely silent wins:** delete `tools.yaml`, delete `mcp-publisher.tar.gz`, gitignore `medusa-env/`, delete `src/utils/secure-memory.ts:169-287` dead code.