@wenathlan/devthink 2.0.12 → 2.0.14

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 (444) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/accounts.ts +2087 -0
  3. package/agent.ts +2331 -0
  4. package/antigravity.ts +2139 -0
  5. package/apifreeze.ts +4146 -0
  6. package/auth.ts +642 -0
  7. package/background.ts +16837 -0
  8. package/bridge.ts +2080 -0
  9. package/build.ts +54 -0
  10. package/bun.ts +26 -0
  11. package/capture.ts +1614 -0
  12. package/checksecrets.ts +72 -0
  13. package/cjs.ts +9 -0
  14. package/cli.ts +3500 -0
  15. package/commands.ts +1101 -0
  16. package/companion.ts +450 -0
  17. package/compatibility.ts +37 -0
  18. package/config.ts +3137 -0
  19. package/constants.ts +943 -0
  20. package/core.ts +1473 -0
  21. package/crossbrowser.ts +1359 -0
  22. package/dashdone.ts +605 -0
  23. package/data.ts +943 -0
  24. package/database.ts +133 -0
  25. package/debug.ts +850 -0
  26. package/deno.ts +27 -0
  27. package/devthink.ts +707 -0
  28. package/dist/accounts.d.ts +409 -0
  29. package/dist/agent.d.ts +837 -0
  30. package/dist/antigravity.d.ts +380 -0
  31. package/dist/apifreeze.d.ts +113 -0
  32. package/dist/auth.d.ts +224 -0
  33. package/{bridge.d.ts → dist/bridge.d.ts} +1 -1
  34. package/dist/capture.d.ts +595 -0
  35. package/dist/cjs.d.ts +6 -0
  36. package/{cli.js → dist/cli.js} +2 -2
  37. package/dist/commands.d.ts +310 -0
  38. package/dist/compatibility.d.ts +17 -0
  39. package/dist/config.d.ts +1853 -0
  40. package/dist/constants.d.ts +637 -0
  41. package/dist/core.d.ts +361 -0
  42. package/dist/data.d.ts +330 -0
  43. package/dist/database.d.ts +24 -0
  44. package/dist/debug.d.ts +270 -0
  45. package/dist/engine.d.ts +74 -0
  46. package/dist/environments.d.ts +244 -0
  47. package/dist/evidence.d.ts +277 -0
  48. package/dist/export.d.ts +311 -0
  49. package/dist/fingerprint.d.ts +594 -0
  50. package/dist/flow.d.ts +300 -0
  51. package/dist/gates.d.ts +103 -0
  52. package/dist/identity.d.ts +51 -0
  53. package/dist/ids.d.ts +7 -0
  54. package/dist/llm.d.ts +652 -0
  55. package/dist/models.d.ts +1807 -0
  56. package/dist/net.d.ts +325 -0
  57. package/dist/neutral.d.ts +41 -0
  58. package/dist/oauth.d.ts +703 -0
  59. package/dist/page.d.ts +1166 -0
  60. package/dist/perf.d.ts +481 -0
  61. package/dist/plan.d.ts +134 -0
  62. package/dist/project.d.ts +395 -0
  63. package/dist/providers.d.ts +37 -0
  64. package/dist/quota.d.ts +453 -0
  65. package/dist/recovery.d.ts +621 -0
  66. package/dist/request.d.ts +1492 -0
  67. package/dist/run.d.ts +779 -0
  68. package/dist/runtime.d.ts +212 -0
  69. package/dist/search.d.ts +375 -0
  70. package/dist/security.d.ts +534 -0
  71. package/dist/serve.d.ts +136 -0
  72. package/dist/session.d.ts +296 -0
  73. package/dist/storage.d.ts +53 -0
  74. package/dist/streaming.d.ts +1119 -0
  75. package/dist/swarm.d.ts +328 -0
  76. package/dist/tools.d.ts +170 -0
  77. package/dist/types.d.ts +8357 -0
  78. package/dist/utils.d.ts +82 -0
  79. package/dist/version.d.ts +7 -0
  80. package/dist/versionregistry.d.ts +406 -0
  81. package/dist/views.d.ts +579 -0
  82. package/dist/workbenchsession.d.ts +69 -0
  83. package/dist/workflow.d.ts +649 -0
  84. package/docs/readme.md +92 -0
  85. package/docs/schemas/antigravity.json +192 -0
  86. package/engine.ts +1524 -0
  87. package/environments.ts +1005 -0
  88. package/evidence.ts +643 -0
  89. package/export.ts +889 -0
  90. package/fingerprint.ts +1458 -0
  91. package/flow.ts +1190 -0
  92. package/gates.ts +307 -0
  93. package/gateway.ts +34 -0
  94. package/hardening.ts +191 -0
  95. package/headless.ts +369 -0
  96. package/http.ts +2110 -0
  97. package/identity.ts +246 -0
  98. package/ids.ts +26 -0
  99. package/index.ts +369 -0
  100. package/llm.ts +2682 -0
  101. package/mcp.ts +1835 -0
  102. package/{memory.js → memory.ts} +3834 -2310
  103. package/models.ts +3805 -0
  104. package/modes.ts +43 -0
  105. package/net.ts +1214 -0
  106. package/neutral.ts +362 -0
  107. package/node.ts +26 -0
  108. package/oauth.ts +2216 -0
  109. package/offscreen.ts +170 -0
  110. package/pack.ts +956 -0
  111. package/package.json +175 -82
  112. package/page.ts +7824 -0
  113. package/pagebridge.ts +1116 -0
  114. package/perf.ts +957 -0
  115. package/plan.ts +672 -0
  116. package/plugin.ts +76 -0
  117. package/{policy.js → policy.ts} +5073 -4679
  118. package/prisma.config.ts +27 -0
  119. package/progress.ts +1308 -0
  120. package/project.ts +1291 -0
  121. package/protocol.ts +4187 -0
  122. package/providers.ts +246 -0
  123. package/quota.ts +1887 -0
  124. package/recovery.ts +2433 -0
  125. package/request.ts +4482 -0
  126. package/run.ts +2135 -0
  127. package/runtime.ts +526 -0
  128. package/search.ts +1760 -0
  129. package/security.ts +1673 -0
  130. package/serve.ts +419 -0
  131. package/server.ts +5538 -0
  132. package/session.ts +1162 -0
  133. package/storage.ts +182 -0
  134. package/streaming.ts +4101 -0
  135. package/swarm.ts +884 -0
  136. package/sync.ts +56 -0
  137. package/tests/agentcert.test.ts +128 -0
  138. package/tests/agentfleet.test.ts +214 -0
  139. package/tests/agentreview.test.ts +241 -0
  140. package/tests/agentstream.test.ts +335 -0
  141. package/tests/agentwork.test.ts +356 -0
  142. package/tests/apifreeze.test.ts +290 -0
  143. package/tests/apimap.test.ts +112 -0
  144. package/tests/approvalgate.test.ts +175 -0
  145. package/tests/artifactmanifest.test.ts +204 -0
  146. package/tests/attentionfeed.test.ts +163 -0
  147. package/tests/backgroundruns.test.ts +100 -0
  148. package/tests/batchscheduling.test.ts +131 -0
  149. package/tests/blackboard.test.ts +238 -0
  150. package/tests/bridge.test.ts +1088 -0
  151. package/tests/browsercoverage.test.ts +134 -0
  152. package/tests/browserpolyfills.test.ts +124 -0
  153. package/tests/build.mjs +1260 -0
  154. package/tests/clientauth.test.ts +223 -0
  155. package/tests/clitools.test.ts +578 -0
  156. package/tests/confirmgates.test.ts +190 -0
  157. package/tests/containerpack.test.ts +143 -0
  158. package/tests/controlflow.test.ts +2120 -0
  159. package/tests/coordination.test.ts +345 -0
  160. package/tests/copyscreen.test.ts +139 -0
  161. package/tests/core.test.ts +417 -0
  162. package/tests/costcert.test.ts +95 -0
  163. package/tests/crossbrowser.test.ts +115 -0
  164. package/tests/cspaudit.test.ts +79 -0
  165. package/tests/dashdone.test.ts +615 -0
  166. package/tests/datagrid.test.ts +137 -0
  167. package/tests/doccheck.test.ts +107 -0
  168. package/tests/dombatch.test.ts +109 -0
  169. package/tests/emulation.test.ts +282 -0
  170. package/tests/environments.test.ts +259 -0
  171. package/tests/evidenceviews.test.ts +88 -0
  172. package/tests/exporttools.test.ts +185 -0
  173. package/tests/firefoxprep.test.ts +134 -0
  174. package/tests/flowlibrary.test.ts +242 -0
  175. package/tests/flowrun.test.ts +303 -0
  176. package/tests/forensics.test.ts +439 -0
  177. package/tests/gateway.test.ts +939 -0
  178. package/tests/headlesslib.test.ts +171 -0
  179. package/tests/httpstream.test.ts +277 -0
  180. package/tests/immutablelog.test.ts +157 -0
  181. package/tests/inboundguard.test.ts +177 -0
  182. package/tests/lazyload.test.ts +72 -0
  183. package/tests/librarymodes.test.ts +623 -0
  184. package/tests/llm.test.ts +696 -0
  185. package/tests/manifest.test.ts +226 -0
  186. package/tests/maskinputs.test.ts +150 -0
  187. package/tests/matrixverify.test.ts +111 -0
  188. package/tests/mavenpack.test.ts +75 -0
  189. package/tests/mcpserver.test.ts +704 -0
  190. package/tests/memory.test.ts +4155 -0
  191. package/tests/memorycare.test.ts +230 -0
  192. package/tests/migrateplan.test.ts +349 -0
  193. package/tests/minimization.test.ts +414 -0
  194. package/tests/modelroute.test.ts +112 -0
  195. package/tests/multiagent.test.ts +440 -0
  196. package/tests/nativebridge.test.ts +807 -0
  197. package/tests/navigation.test.ts +284 -0
  198. package/tests/nugetpack.test.ts +83 -0
  199. package/tests/orchestration.test.ts +449 -0
  200. package/tests/originpolicy.test.ts +405 -0
  201. package/tests/outputcompare.test.ts +97 -0
  202. package/tests/package.test.ts +91 -0
  203. package/tests/pagebridge.test.ts +5909 -0
  204. package/tests/pentest.test.ts +75 -0
  205. package/tests/perfrecords.test.ts +257 -0
  206. package/tests/permdiff.test.ts +85 -0
  207. package/tests/phishguard.test.ts +128 -0
  208. package/tests/pickerviews.test.ts +155 -0
  209. package/tests/pipelines.test.ts +381 -0
  210. package/tests/planlint.test.ts +240 -0
  211. package/tests/planreview.test.ts +383 -0
  212. package/tests/platformtargets.test.ts +56 -0
  213. package/tests/policy.test.ts +8284 -0
  214. package/tests/portability.test.ts +96 -0
  215. package/tests/progress.test.ts +1036 -0
  216. package/tests/promptlibrary.test.ts +116 -0
  217. package/tests/protocol.test.ts +4793 -0
  218. package/tests/publishingpipeline.test.ts +314 -0
  219. package/tests/quickactions.test.ts +164 -0
  220. package/tests/readiness.test.ts +78 -0
  221. package/tests/recipes.test.ts +163 -0
  222. package/tests/redactshots.test.ts +216 -0
  223. package/tests/registernvidiakeys.ts +51 -0
  224. package/tests/relayserve.test.ts +304 -0
  225. package/tests/releasenotes.test.ts +276 -0
  226. package/tests/resilience.test.ts +467 -0
  227. package/tests/resourceaware.test.ts +187 -0
  228. package/tests/runbudget.test.ts +125 -0
  229. package/tests/runhistory.test.ts +299 -0
  230. package/tests/runreplay.test.ts +113 -0
  231. package/tests/runresume.test.ts +184 -0
  232. package/tests/runstate.test.ts +288 -0
  233. package/tests/runtimeadapters.test.ts +251 -0
  234. package/tests/safariskeleton.test.ts +115 -0
  235. package/tests/sandboxframe.test.ts +196 -0
  236. package/tests/sbom.test.ts +105 -0
  237. package/tests/secretvault.test.ts +188 -0
  238. package/tests/selcache.test.ts +111 -0
  239. package/tests/sessioninterface.test.ts +791 -0
  240. package/tests/sessions.test.ts +591 -0
  241. package/tests/siteprefs.test.ts +228 -0
  242. package/tests/snapshotdelta.test.ts +165 -0
  243. package/tests/statusviews.test.ts +237 -0
  244. package/tests/stepmeter.test.ts +200 -0
  245. package/tests/streamparse.test.ts +163 -0
  246. package/tests/stylepruning.test.ts +90 -0
  247. package/tests/surfaces.test.ts +282 -0
  248. package/tests/sweep.test.ts +78 -0
  249. package/tests/syncbridge.test.ts +164 -0
  250. package/tests/taskqueue.test.ts +206 -0
  251. package/tests/toolcalls.test.ts +336 -0
  252. package/tests/toolcatalog.test.ts +201 -0
  253. package/tests/torture-agent.test.ts +706 -0
  254. package/tests/torture-auth.test.ts +461 -0
  255. package/tests/torture-commands.test.ts +895 -0
  256. package/tests/torture-data.test.ts +923 -0
  257. package/tests/torture-environments.test.ts +285 -0
  258. package/tests/torture-export.test.ts +263 -0
  259. package/tests/torture-http.test.ts +1379 -0
  260. package/tests/torture-llm.test.ts +808 -0
  261. package/tests/torture-memory.test.ts +1084 -0
  262. package/tests/torture-plan.test.ts +829 -0
  263. package/tests/torture-run.test.ts +626 -0
  264. package/tests/torture-session.test.ts +928 -0
  265. package/tests/torture-swarm.test.ts +1245 -0
  266. package/tests/torture-tools.test.ts +964 -0
  267. package/tests/torture-workflow.test.ts +2354 -0
  268. package/tests/tourviews.test.ts +52 -0
  269. package/tests/transparency.test.ts +147 -0
  270. package/tests/trigger.test.ts +941 -0
  271. package/tests/v1sunset.test.ts +428 -0
  272. package/tests/virtlist.test.ts +94 -0
  273. package/tests/vision.test.ts +540 -0
  274. package/tests/vsixpack.test.ts +145 -0
  275. package/tests/webapi.test.ts +523 -0
  276. package/tests/workflow.test.ts +1785 -0
  277. package/tests/workfloweditor.test.ts +759 -0
  278. package/tests/xpipack.test.ts +128 -0
  279. package/tools.ts +734 -0
  280. package/types.ts +9908 -0
  281. package/umd.ts +9 -0
  282. package/utils.ts +249 -0
  283. package/version.ts +8 -0
  284. package/versionregistry.ts +1310 -0
  285. package/views.ts +2153 -0
  286. package/vitest.config.ts +12 -0
  287. package/web/App.tsx +78 -0
  288. package/web/README.md +47 -0
  289. package/web/capacitor.config.ts +46 -0
  290. package/web/const.ts +19 -0
  291. package/web/control.shell.tsx +66 -0
  292. package/web/dashboardpage.ts +554 -0
  293. package/web/db.ts +493 -0
  294. package/web/design.html +541 -0
  295. package/web/gatewayview/Gateway.tsx +345 -0
  296. package/web/gatewayview/config.ts +1042 -0
  297. package/web/gatewayview/definition.ts +174 -0
  298. package/web/icons.ts +15 -0
  299. package/{manifest.json → web/manifest.json} +1 -1
  300. package/web/optionspage.ts +691 -0
  301. package/web/popup.ts +1651 -0
  302. package/web/schema.prisma +176 -0
  303. package/web/sidepanel.ts +7941 -0
  304. package/web/sitemanifest.json +13 -0
  305. package/web/terminal-ui.tsx +622 -0
  306. package/web/transparencypage.ts +541 -0
  307. package/web/vite.config.ts +39 -0
  308. package/web/workspace.ts +23 -0
  309. package/workbenchmemory.ts +104 -0
  310. package/workbenchsession.ts +304 -0
  311. package/workflow.ts +4495 -0
  312. package/bridge.d.ts.map +0 -1
  313. package/bridge.js +0 -1877
  314. package/bun.d.ts.map +0 -1
  315. package/bun.js +0 -52329
  316. package/caps/background.json +0 -712
  317. package/caps/cli.json +0 -360
  318. package/caps/library.json +0 -3611
  319. package/caps/mcp.json +0 -126
  320. package/caps/pagebridge.json +0 -377
  321. package/caps/popup.json +0 -51
  322. package/caps/sidepanel.json +0 -522
  323. package/checksums.txt +0 -142
  324. package/cli.d.ts.map +0 -1
  325. package/companion.d.ts.map +0 -1
  326. package/companion.js +0 -389
  327. package/crossbrowser.d.ts.map +0 -1
  328. package/crossbrowser.js +0 -1191
  329. package/dashdone.d.ts.map +0 -1
  330. package/dashdone.js +0 -368
  331. package/deno.d.ts.map +0 -1
  332. package/deno.js +0 -52325
  333. package/devthink.umd.js +0 -52346
  334. package/fixtures/bun.ts +0 -7
  335. package/fixtures/cjs.js +0 -6
  336. package/fixtures/deno.ts +0 -6
  337. package/fixtures/example-org-pagestate.json +0 -22
  338. package/fixtures/importers/automa-workflow.json +0 -18
  339. package/fixtures/importers/selenium-side.json +0 -20
  340. package/fixtures/importers/tabular-plan.csv +0 -7
  341. package/fixtures/importers/uivision-macro.json +0 -10
  342. package/fixtures/importers/v1-plan.json +0 -13
  343. package/fixtures/mcp-client.mjs +0 -14
  344. package/fixtures/pages/checkout.html +0 -192
  345. package/fixtures/pages/dashboard.html +0 -110
  346. package/fixtures/pages/feed.html +0 -113
  347. package/fixtures/pages/product-grid.html +0 -150
  348. package/fixtures/plans/form-inventory-plan.json +0 -11
  349. package/fixtures/plans/release-notes-plan.json +0 -11
  350. package/fixtures/recipes/agentscompete.json +0 -11
  351. package/fixtures/recipes/agentsforms.json +0 -11
  352. package/fixtures/recipes/agentsmonitor.json +0 -11
  353. package/fixtures/recipes/agentsreview.json +0 -29
  354. package/fixtures/recipes/agentswarm.json +0 -11
  355. package/fixtures/recipes/fillcalendar.json +0 -12
  356. package/fixtures/recipes/fillcheckout.json +0 -13
  357. package/fixtures/recipes/filldependentdropdowns.json +0 -12
  358. package/fixtures/recipes/fillfromcsv.json +0 -13
  359. package/fixtures/recipes/fillloginform.json +0 -12
  360. package/fixtures/recipes/fillmultistepwizard.json +0 -13
  361. package/fixtures/recipes/fillregistration.json +0 -12
  362. package/fixtures/recipes/fillsearchfilters.json +0 -12
  363. package/fixtures/recipes/filltypeahead.json +0 -11
  364. package/fixtures/recipes/monitoralerts.json +0 -12
  365. package/fixtures/recipes/monitoravailability.json +0 -11
  366. package/fixtures/recipes/monitornetworkquiet.json +0 -12
  367. package/fixtures/recipes/monitorpagechanges.json +0 -11
  368. package/fixtures/recipes/monitorpricechange.json +0 -11
  369. package/fixtures/recipes/monitorschedule.json +0 -12
  370. package/fixtures/recipes/scrapedynamicprices.json +0 -13
  371. package/fixtures/recipes/scrapeimagegallery.json +0 -12
  372. package/fixtures/recipes/scrapeinfinitefeed.json +0 -13
  373. package/fixtures/recipes/scrapenestedlists.json +0 -12
  374. package/fixtures/recipes/scrapeproductgrid.json +0 -12
  375. package/fixtures/recipes/scrapereviews.json +0 -12
  376. package/fixtures/recipes/scrapesearchresults.json +0 -12
  377. package/fixtures/recipes/scrapetablepagination.json +0 -12
  378. package/fixtures/recipes/testaccessibility.json +0 -34
  379. package/fixtures/recipes/testconsoleerrors.json +0 -12
  380. package/fixtures/recipes/testdeeplinks.json +0 -12
  381. package/fixtures/recipes/testformvalidation.json +0 -13
  382. package/fixtures/recipes/testlayoutshift.json +0 -12
  383. package/fixtures/recipes/testlinkcheck.json +0 -12
  384. package/fixtures/recipes/testloadtiming.json +0 -12
  385. package/fixtures/recipes/testvisualdiff.json +0 -12
  386. package/fixtures/workflows/changelog-digest-workflow.json +0 -13
  387. package/fixtures/workflows/form-survey-workflow.json +0 -13
  388. package/gallery.json +0 -706
  389. package/hardening.d.ts.map +0 -1
  390. package/hardening.js +0 -188
  391. package/headless.d.ts.map +0 -1
  392. package/headless.js +0 -11522
  393. package/http.d.ts.map +0 -1
  394. package/http.js +0 -2165
  395. package/index.cjs +0 -54682
  396. package/index.d.ts.map +0 -1
  397. package/index.js +0 -52310
  398. package/index.neutral.js +0 -46343
  399. package/mcp.d.ts.map +0 -1
  400. package/mcp.js +0 -20515
  401. package/memory.d.ts.map +0 -1
  402. package/nativehost.template.json +0 -10
  403. package/node.cjs +0 -54686
  404. package/node.d.ts.map +0 -1
  405. package/pack.d.ts.map +0 -1
  406. package/pack.js +0 -805
  407. package/policy.d.ts.map +0 -1
  408. package/progress.d.ts.map +0 -1
  409. package/progress.js +0 -645
  410. package/protocol.d.ts.map +0 -1
  411. package/protocol.js +0 -8334
  412. package/schemas/audit.schema.json +0 -226
  413. package/schemas/capability.schema.json +0 -113
  414. package/schemas/envelope.schema.json +0 -162
  415. package/schemas/memory.schema.json +0 -51
  416. package/schemas/observation.schema.json +0 -113
  417. package/schemas/plan.schema.json +0 -208
  418. package/schemas/progress.schema.json +0 -126
  419. package/schemas/proposal.schema.json +0 -68
  420. package/schemas/session.schema.json +0 -82
  421. package/schemas/tool.schema.json +0 -94
  422. package/server.d.ts.map +0 -1
  423. package/server.js +0 -28877
  424. package/umd-example.html +0 -18
  425. package/umd.d.ts.map +0 -1
  426. /package/{bun.d.ts → dist/bun.d.ts} +0 -0
  427. /package/{cli.d.ts → dist/cli.d.ts} +0 -0
  428. /package/{companion.d.ts → dist/companion.d.ts} +0 -0
  429. /package/{crossbrowser.d.ts → dist/crossbrowser.d.ts} +0 -0
  430. /package/{dashdone.d.ts → dist/dashdone.d.ts} +0 -0
  431. /package/{deno.d.ts → dist/deno.d.ts} +0 -0
  432. /package/{hardening.d.ts → dist/hardening.d.ts} +0 -0
  433. /package/{headless.d.ts → dist/headless.d.ts} +0 -0
  434. /package/{http.d.ts → dist/http.d.ts} +0 -0
  435. /package/{index.d.ts → dist/index.d.ts} +0 -0
  436. /package/{mcp.d.ts → dist/mcp.d.ts} +0 -0
  437. /package/{memory.d.ts → dist/memory.d.ts} +0 -0
  438. /package/{node.d.ts → dist/node.d.ts} +0 -0
  439. /package/{pack.d.ts → dist/pack.d.ts} +0 -0
  440. /package/{policy.d.ts → dist/policy.d.ts} +0 -0
  441. /package/{progress.d.ts → dist/progress.d.ts} +0 -0
  442. /package/{protocol.d.ts → dist/protocol.d.ts} +0 -0
  443. /package/{server.d.ts → dist/server.d.ts} +0 -0
  444. /package/{umd.d.ts → dist/umd.d.ts} +0 -0
@@ -0,0 +1,75 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { execFile } from "node:child_process";
3
+ import { existsSync } from "node:fs";
4
+ import { readFile } from "node:fs/promises";
5
+ import { promisify } from "node:util";
6
+
7
+ const execute = promisify(execFile);
8
+
9
+ /* ── The 1.1.95 security hardening checklist coverage. ── */
10
+
11
+ describe("the pentest checklist artifact", () => {
12
+ it("declares every automated entry of the release candidate checklist with its family and module", async () => {
13
+ const source = await readFile("tests/pentest.mjs", "utf8");
14
+ const declarations = [
15
+ ...source.matchAll(/await entry\(\{ id: (\d+), title: "([^"]+)", family: "([^"]+)", module: "([^"]+)" \}/g),
16
+ ].map((match) => ({ id: Number(match[1]), title: match[2], family: match[3], module: match[4] }));
17
+ expect(declarations).toHaveLength(21);
18
+ expect(declarations.map((entry) => entry.id)).toEqual(Array.from({ length: 21 }, (_, index) => index + 1));
19
+ const families = new Set(declarations.map((entry) => entry.family));
20
+ for (const family of [
21
+ "trust boundary",
22
+ "inbound guards",
23
+ "emergency stops",
24
+ "transport posture",
25
+ "secrets",
26
+ "audit integrity",
27
+ "untrusted rendering",
28
+ "minimization",
29
+ "phishguard",
30
+ "human gates",
31
+ ])
32
+ expect(families.has(family)).toBe(true);
33
+ for (const entry of declarations) expect((entry.title ?? "").length).toBeGreaterThan(10);
34
+ });
35
+
36
+ it("covers every checklist entry with a passing outcome once the gate has run", async () => {
37
+ const artifactpath = "tests/artifacts/pentest.json";
38
+ const built = existsSync("dist/extension/manifest.json");
39
+ const packagejson = JSON.parse(await readFile("package.json", "utf8")) as { version: string };
40
+ /* a built tree runs the gate itself when no fresh artifact of this release exists, so every lane that builds before the suite — the bun lane, the deno lane, a local two-pass validate — verifies the checklist artifact without depending on the gate having run before the tests */
41
+ if (built && !existsSync(artifactpath)) await execute("node", ["tests/pentest.mjs"]);
42
+ if (built && existsSync(artifactpath)) {
43
+ const stored = JSON.parse(await readFile(artifactpath, "utf8")) as { release: string };
44
+ if (stored.release !== packagejson.version) await execute("node", ["tests/pentest.mjs"]);
45
+ }
46
+ const stored = existsSync(artifactpath);
47
+ if (built) expect(stored).toBe(true);
48
+ if (!stored) return;
49
+ const report = JSON.parse(await readFile(artifactpath, "utf8")) as {
50
+ release: string;
51
+ entries: Array<{ id: number; title: string; outcome: string; detail: string }>;
52
+ summary: { total: number; passed: number; failed: number };
53
+ };
54
+ expect(report.release).toBe(packagejson.version);
55
+ expect(report.summary.total).toBe(21);
56
+ expect(report.summary.failed).toBe(0);
57
+ expect(report.summary.passed).toBe(21);
58
+ expect(report.entries.map((entry) => entry.id)).toEqual(Array.from({ length: 21 }, (_, index) => index + 1));
59
+ for (const entry of report.entries) {
60
+ expect(entry.outcome).toBe("pass");
61
+ expect(entry.detail.length).toBeGreaterThan(10);
62
+ }
63
+ });
64
+
65
+ it("exercises the real compiled modules the checklist names and stays deterministic", async () => {
66
+ const source = await readFile("tests/pentest.mjs", "utf8");
67
+ expect(source).toContain('import("../dist/index.js")');
68
+ expect(source).toContain('import("../dist/policy.js")');
69
+ expect(source).toContain('import("../dist/hardening.js")');
70
+ /* the artifact carries no timestamp so reruns stay byte identical */
71
+ expect(source).not.toContain("new Date()");
72
+ expect(source).toContain("1_800_000_000_000");
73
+ expect(source).toContain("process.exitCode = 1");
74
+ });
75
+ });
@@ -0,0 +1,257 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ abandonedparsetasks,
4
+ captureplanschedule,
5
+ dedupesnapshotrequests,
6
+ perfbundle,
7
+ perfrecordof,
8
+ perfsummary,
9
+ pruneperfrecords,
10
+ queuedepthsampleof,
11
+ queuedepthview,
12
+ snapshotrequestkey,
13
+ stepdurationchart,
14
+ workerbackpressure,
15
+ } from "../perf.js";
16
+ import { perfprovenancegate, workerbackpressuregate } from "../policy.js";
17
+ import { perfreport } from "../protocol.js";
18
+ import { sessionmemory } from "../memory.js";
19
+
20
+ const now = 1_800_000_000_000;
21
+
22
+ class fakeadapter {
23
+ private readonly data = new Map<string, unknown>();
24
+ async get<T>(key: string): Promise<T | undefined> {
25
+ return this.data.get(key) as T | undefined;
26
+ }
27
+ async set<T>(key: string, value: T): Promise<void> {
28
+ this.data.set(key, value);
29
+ }
30
+ }
31
+
32
+ describe("worker queue backpressure", () => {
33
+ it("defers parse tasks past the user configured depth and never refuses them", () => {
34
+ expect(workerbackpressure({ depth: 8, pending: 8, configured: 5 })).toEqual({
35
+ deferred: 3,
36
+ admitted: 5,
37
+ unbounded: false,
38
+ });
39
+ expect(workerbackpressure({ depth: 2, pending: 4, configured: 5 })).toEqual({
40
+ deferred: 0,
41
+ admitted: 4,
42
+ unbounded: false,
43
+ });
44
+ expect(workerbackpressure({ depth: 9, pending: 9 })).toEqual({ deferred: 0, admitted: 9, unbounded: true });
45
+ expect(workerbackpressuregate({ depth: 5, pending: 9 }).allowed).toBe(true);
46
+ expect(workerbackpressuregate({ depth: 5, pending: 9 }).reason).toMatch(/defers and never refuses/i);
47
+ expect(workerbackpressuregate({ pending: 9 }).reason).toMatch(/unbounded/i);
48
+ expect(workerbackpressuregate({ depth: 0, pending: 9 }).allowed).toBe(false);
49
+ });
50
+
51
+ it("records the queue depth over time for tuning", () => {
52
+ const sample = queuedepthsampleof({ depth: 4, deferred: 2, now });
53
+ expect(sample).toEqual({ at: now, depth: 4, deferred: 2 });
54
+ const view = queuedepthview([sample, queuedepthsampleof({ depth: 7, deferred: 0, now: now + 100 })]);
55
+ expect(view).toEqual({ peak: 7, deferred: 2, samples: 2 });
56
+ });
57
+
58
+ it("cancels the abandoned parse tasks of a halted run while the other runs keep theirs", () => {
59
+ const pending = [
60
+ { id: "a", runid: "run1", started: false },
61
+ { id: "b", runid: "run1", started: true },
62
+ { id: "c", runid: "run2", started: false },
63
+ ];
64
+ const halted = abandonedparsetasks({ halted: true, runid: "run1", pending });
65
+ expect(halted.cancelled).toEqual(["a"]);
66
+ expect(halted.kept.sort()).toEqual(["b", "c"]);
67
+ const running = abandonedparsetasks({ halted: false, runid: "run1", pending });
68
+ expect(running.cancelled).toEqual([]);
69
+ expect(running.kept).toHaveLength(3);
70
+ });
71
+ });
72
+
73
+ describe("perf recording per step", () => {
74
+ it("records one perf record per step with its provenance attached", () => {
75
+ const record = perfrecordof({
76
+ runid: "run1",
77
+ stepid: "s1",
78
+ duration: 120,
79
+ queries: 5,
80
+ cachehits: 3,
81
+ delta: true,
82
+ now,
83
+ provenance: { origin: "https://example.com", environment: "offscreenworker", task: "htmlsnapshot" },
84
+ });
85
+ expect(record.delta).toBe(true);
86
+ expect(perfprovenancegate(record).allowed).toBe(true);
87
+ expect(() =>
88
+ perfrecordof({ runid: " ", stepid: "s1", duration: 1, queries: 1, cachehits: 0, delta: false, now }),
89
+ ).toThrow(/run id/i);
90
+ expect(() =>
91
+ perfrecordof({ runid: "run1", stepid: " ", duration: 1, queries: 1, cachehits: 0, delta: false, now }),
92
+ ).toThrow(/step id/i);
93
+ expect(perfprovenancegate({ ...record, provenance: {} }).allowed).toBe(false);
94
+ });
95
+
96
+ it("sums the perf summary and charts the step durations over the run", () => {
97
+ const records = [
98
+ perfrecordof({
99
+ runid: "run1",
100
+ stepid: "s1",
101
+ duration: 100,
102
+ queries: 4,
103
+ cachehits: 2,
104
+ delta: false,
105
+ now: now + 100,
106
+ provenance: { origin: "https://example.com" },
107
+ }),
108
+ perfrecordof({
109
+ runid: "run1",
110
+ stepid: "s2",
111
+ duration: 300,
112
+ queries: 6,
113
+ cachehits: 3,
114
+ delta: true,
115
+ now: now + 50,
116
+ provenance: { environment: "offscreenworker" },
117
+ }),
118
+ ];
119
+ const summary = perfsummary(records);
120
+ expect(summary).toEqual({
121
+ steps: 2,
122
+ duration: 400,
123
+ average: 200,
124
+ queries: 10,
125
+ cachehits: 5,
126
+ hitratio: 0.5,
127
+ deltashare: 0.5,
128
+ });
129
+ expect(perfsummary([]).steps).toBe(0);
130
+ const chart = stepdurationchart(records);
131
+ expect(chart.map((point) => point.stepid)).toEqual(["s2", "s1"]);
132
+ expect(chart[0]?.delta).toBe(true);
133
+ const bundle = perfbundle({ runid: "run1", records });
134
+ expect(bundle.records).toHaveLength(2);
135
+ expect(bundle.summary.steps).toBe(2);
136
+ });
137
+
138
+ it("deduplicates identical snapshot requests within one run", () => {
139
+ const requests = [
140
+ { stepid: "s1", fingerprint: "f1" },
141
+ { stepid: "s1", fingerprint: "f1" },
142
+ { stepid: "s2", fingerprint: "f1" },
143
+ { stepid: "s2", fingerprint: "f2" },
144
+ ];
145
+ const deduped = dedupesnapshotrequests({ runid: "run1", requests });
146
+ expect(deduped.distinct).toHaveLength(3);
147
+ expect(deduped.duplicates).toBe(1);
148
+ expect(snapshotrequestkey({ runid: "run1", stepid: "s1", fingerprint: "f1" })).toBe("run1:s1:f1");
149
+ });
150
+
151
+ it("schedules the heavy capture work behind the user priority choice and never drops a capture", () => {
152
+ const speed = captureplanschedule({ priority: "speed", heavy: ["capturevisible", "captureelement"] });
153
+ expect(speed.beside).toHaveLength(0);
154
+ expect(speed.deferred).toEqual(["capturevisible", "captureelement"]);
155
+ expect(speed.note).toMatch(/still runs inside the reviewed plan/i);
156
+ const evidence = captureplanschedule({ priority: "evidence", heavy: ["capturevisible"] });
157
+ expect(evidence.beside).toEqual(["capturevisible"]);
158
+ expect(evidence.deferred).toHaveLength(0);
159
+ const unset = captureplanschedule({ heavy: ["capturevisible"] });
160
+ expect(unset.beside).toEqual(["capturevisible"]);
161
+ });
162
+
163
+ it("prunes the perf records past the user configured retention window and exports the bundle", async () => {
164
+ const store = new sessionmemory(new fakeadapter());
165
+ const fresh = perfrecordof({
166
+ runid: "run1",
167
+ stepid: "s1",
168
+ duration: 10,
169
+ queries: 1,
170
+ cachehits: 0,
171
+ delta: false,
172
+ now,
173
+ provenance: { origin: "https://example.com" },
174
+ });
175
+ const old = perfrecordof({
176
+ runid: "run1",
177
+ stepid: "s2",
178
+ duration: 20,
179
+ queries: 2,
180
+ cachehits: 1,
181
+ delta: false,
182
+ now: now - 10_000,
183
+ provenance: { environment: "pagecontext" },
184
+ });
185
+ await store.addperfrecord(fresh);
186
+ await store.addperfrecord(old);
187
+ await store.recordqueuedepth(queuedepthsampleof({ depth: 3, deferred: 1, now }));
188
+ expect(await store.getperfrecords()).toHaveLength(2);
189
+ const pruned = await store.pruneperfrecords(5000, now);
190
+ expect(pruned.pruned).toBe(1);
191
+ expect(pruned.kept).toHaveLength(1);
192
+ const unpruned = await store.pruneperfrecords(undefined, now);
193
+ expect(unpruned.pruned).toBe(0);
194
+ const bundle = await store.exportperfbundle("run1");
195
+ expect(bundle.records).toHaveLength(1);
196
+ expect(await store.getqueuedepths()).toHaveLength(1);
197
+ });
198
+
199
+ it("builds the perf report envelope for the dashboardpage", () => {
200
+ const report = perfreport({
201
+ runid: "run1",
202
+ summary: { steps: 2, duration: 400, average: 200, queries: 10, cachehits: 5, hitratio: 0.5, deltashare: 0.5 },
203
+ chart: [{ stepid: "s1", duration: 100, delta: false }],
204
+ selcache: { generation: 3, entries: 4 },
205
+ queue: { depth: 2, deferred: 1, peak: 6 },
206
+ chunk: { tableid: "table1", extracted: 10, total: 25, complete: false },
207
+ });
208
+ expect(report.runid).toBe("run1");
209
+ expect(report.chart[0]?.stepid).toBe("s1");
210
+ expect(report.chunk?.tableid).toBe("table1");
211
+ const minimal = perfreport({
212
+ runid: "run2",
213
+ summary: { steps: 0, duration: 0, average: 0, queries: 0, cachehits: 0, hitratio: 0, deltashare: 0 },
214
+ chart: [],
215
+ selcache: { generation: 0, entries: 0 },
216
+ queue: { depth: 0, deferred: 0, peak: 0 },
217
+ });
218
+ expect(minimal.chunk).toBeUndefined();
219
+ expect(() =>
220
+ perfreport({
221
+ runid: " ",
222
+ summary: { steps: 0, duration: 0, average: 0, queries: 0, cachehits: 0, hitratio: 0, deltashare: 0 },
223
+ chart: [],
224
+ selcache: { generation: 0, entries: 0 },
225
+ queue: { depth: 0, deferred: 0, peak: 0 },
226
+ }),
227
+ ).toThrow(/run id/i);
228
+ });
229
+
230
+ it("prunes the record list through the pure pruner beside the store", () => {
231
+ const records = [
232
+ perfrecordof({
233
+ runid: "run1",
234
+ stepid: "s1",
235
+ duration: 1,
236
+ queries: 1,
237
+ cachehits: 0,
238
+ delta: false,
239
+ now,
240
+ provenance: {},
241
+ }),
242
+ perfrecordof({
243
+ runid: "run1",
244
+ stepid: "s2",
245
+ duration: 1,
246
+ queries: 1,
247
+ cachehits: 0,
248
+ delta: false,
249
+ now: now - 100,
250
+ provenance: {},
251
+ }),
252
+ ];
253
+ const pruned = pruneperfrecords(records, 50, now);
254
+ expect(pruned.pruned).toBe(1);
255
+ expect(pruneperfrecords(records, undefined, now).kept).toHaveLength(2);
256
+ });
257
+ });
@@ -0,0 +1,85 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createHash } from "node:crypto";
3
+ import { readFile } from "node:fs/promises";
4
+ import { diffpermissionsets, permissionsetof, previousof } from "./permdiff.mjs";
5
+
6
+ /* ── The 1.1.95 permission diff logic against fixture manifests. ── */
7
+
8
+ const basemanifest = {
9
+ manifest_version: 3,
10
+ version: "1.0.0",
11
+ permissions: ["activeTab", "storage", "scripting", "sidePanel"],
12
+ optional_permissions: ["tabs", "downloads", "clipboardRead", "clipboardWrite", "offscreen", "nativeMessaging"],
13
+ optional_host_permissions: ["https://*/*"],
14
+ };
15
+
16
+ describe("the permdiff logic", () => {
17
+ it("reads the permission set of a manifest in its declared order with its set tags", () => {
18
+ const set = permissionsetof(basemanifest);
19
+ expect(set.required).toEqual([
20
+ "required:activeTab",
21
+ "required:storage",
22
+ "required:scripting",
23
+ "required:sidePanel",
24
+ ]);
25
+ expect(set.optional).toHaveLength(6);
26
+ expect(set.optionalhost).toEqual(["optionalhost:https://*/*"]);
27
+ expect(set.entries).toHaveLength(11);
28
+ });
29
+
30
+ it("reports a clean diff when the fixture manifests carry the same permission set", () => {
31
+ const diff = diffpermissionsets(permissionsetof(basemanifest), permissionsetof({ ...basemanifest }));
32
+ expect(diff.added).toEqual([]);
33
+ expect(diff.removed).toEqual([]);
34
+ expect(diff.reordered).toEqual([]);
35
+ });
36
+
37
+ it("reports every added permission a fixture manifest gains", () => {
38
+ const grown = { ...basemanifest, permissions: [...basemanifest.permissions, "debugger"] };
39
+ const diff = diffpermissionsets(permissionsetof(grown), permissionsetof(basemanifest));
40
+ expect(diff.added).toEqual(["required:debugger"]);
41
+ expect(diff.removed).toEqual([]);
42
+ expect(diff.reordered).toEqual([]);
43
+ });
44
+
45
+ it("reports every removed permission and every move between the required and the optional sets", () => {
46
+ const shrunk = { ...basemanifest, permissions: basemanifest.permissions.slice(0, 3) };
47
+ const diff = diffpermissionsets(permissionsetof(shrunk), permissionsetof(basemanifest));
48
+ expect(diff.added).toEqual([]);
49
+ expect(diff.removed).toEqual(["required:sidePanel"]);
50
+ const moved = {
51
+ ...basemanifest,
52
+ permissions: basemanifest.permissions.slice(0, 3),
53
+ optional_permissions: [...basemanifest.optional_permissions, "sidePanel"],
54
+ };
55
+ const moveddiff = diffpermissionsets(permissionsetof(moved), permissionsetof(basemanifest));
56
+ expect(moveddiff.added).toEqual(["optional:sidePanel"]);
57
+ expect(moveddiff.removed).toEqual(["required:sidePanel"]);
58
+ expect(moveddiff.reordered).toEqual([]);
59
+ });
60
+
61
+ it("reports a reordered set when the same permissions appear in a different order", () => {
62
+ const reordered = { ...basemanifest, permissions: [...basemanifest.permissions.slice(1), "activeTab"] };
63
+ const diff = diffpermissionsets(permissionsetof(reordered), permissionsetof(basemanifest));
64
+ expect(diff.added).toEqual([]);
65
+ expect(diff.removed).toEqual([]);
66
+ expect(diff.reordered).toEqual(["required"]);
67
+ });
68
+
69
+ it("resolves the previous release version of the package", async () => {
70
+ expect(previousof("1.1.95")).toBe("1.1.94");
71
+ expect(previousof("1.2.0")).toBe("1.1.0");
72
+ expect(previousof("2.0.0")).toBe("1.0.0");
73
+ const packagejson = JSON.parse(await readFile("package.json", "utf8")) as { version: string };
74
+ const manifest = JSON.parse(await readFile("web/manifest.json", "utf8")) as typeof basemanifest;
75
+ expect(
76
+ manifest.permissions.length + manifest.optional_permissions.length + manifest.optional_host_permissions.length,
77
+ ).toBe(permissionsetof(manifest).entries.length);
78
+ /* the permission set hash the gate records covers the sorted unique entries of the current manifest */
79
+ const entries = permissionsetof(manifest).entries;
80
+ const digest = createHash("sha256")
81
+ .update([...new Set(entries)].sort().join("\n"))
82
+ .digest("hex");
83
+ expect(digest).toMatch(/^[0-9a-f]{64}$/);
84
+ });
85
+ });
@@ -0,0 +1,128 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ credentialstep,
4
+ lookalikedistance,
5
+ originlabels,
6
+ phishnotetext,
7
+ phishthresholdvalid,
8
+ phishverdictof,
9
+ verdictfresh,
10
+ } from "../security.js";
11
+
12
+ const now = 1_800_000_000_000;
13
+
14
+ describe("phishguard", () => {
15
+ it("marks login and credential steps for the watch", () => {
16
+ expect(credentialstep({ kind: "consentpassword", target: "" })).toBe(true);
17
+ expect(credentialstep({ kind: "authflow", target: "" })).toBe(true);
18
+ expect(credentialstep({ kind: "fillcard", target: "" })).toBe(true);
19
+ expect(credentialstep({ kind: "click", target: "#submit" })).toBe(false);
20
+ expect(credentialstep({ kind: "type", target: "#password" })).toBe(true);
21
+ expect(
22
+ credentialstep({
23
+ kind: "fillform",
24
+ target: "#login",
25
+ value: "",
26
+ options: JSON.stringify({ fields: [{ name: "passphrase", value: "x" }] }),
27
+ }),
28
+ ).toBe(true);
29
+ });
30
+
31
+ it("splits origins into registrable ordered labels", () => {
32
+ expect(originlabels("https://pay.example.com")).toEqual(["com", "example", "pay"]);
33
+ expect(originlabels("https://example.com")).toEqual(["com", "example"]);
34
+ expect(originlabels("https://example.com/path/segment")).toEqual(["com", "example"]);
35
+ });
36
+
37
+ it("measures the lookalike distance between origins from zero to one", () => {
38
+ expect(lookalikedistance("https://example.com", "https://example.com")).toBe(0);
39
+ expect(lookalikedistance("https://pay.example.com", "https://pay.example.com")).toBe(0);
40
+ const lookalike = lookalikedistance("https://pay.example.com.attacker.example", "https://pay.example.com");
41
+ expect(lookalike).toBeGreaterThan(0);
42
+ expect(lookalike).toBeLessThan(1);
43
+ const far = lookalikedistance("https://example.com", "https://totally-different.example");
44
+ expect(far).toBeGreaterThan(lookalike);
45
+ expect(lookalikedistance("", "https://example.com")).toBe(1);
46
+ });
47
+
48
+ it("validates the threshold as a user choice between zero and one", () => {
49
+ expect(phishthresholdvalid(0.4).valid).toBe(true);
50
+ expect(phishthresholdvalid(0).valid).toBe(false);
51
+ expect(phishthresholdvalid(1).valid).toBe(false);
52
+ expect(phishthresholdvalid(1.5).valid).toBe(false);
53
+ });
54
+
55
+ it("blocks login origins whose distance crosses the user threshold and names the matched origin", () => {
56
+ const verdict = phishverdictof({
57
+ origin: "https://pay.example.com.attacker.example",
58
+ granted: ["https://pay.example.com", "https://other.example"],
59
+ threshold: 0.5,
60
+ now,
61
+ });
62
+ expect(verdict.blocked).toBe(true);
63
+ expect(verdict.matchedorigin).toBe("https://pay.example.com");
64
+ expect(verdict.reason).toMatch(/names https:\/\/pay\.example\.com/);
65
+ expect(verdict.distance).toBeLessThanOrEqual(0.5);
66
+ });
67
+
68
+ it("passes granted origins and distant origins under the threshold", () => {
69
+ const granted = phishverdictof({
70
+ origin: "https://pay.example.com",
71
+ granted: ["https://pay.example.com"],
72
+ threshold: 0.4,
73
+ now,
74
+ });
75
+ expect(granted.blocked).toBe(false);
76
+ expect(granted.distance).toBe(0);
77
+ const distant = phishverdictof({
78
+ origin: "https://unrelated.example",
79
+ granted: ["https://pay.example.com"],
80
+ threshold: 0.3,
81
+ now,
82
+ });
83
+ expect(distant.blocked).toBe(false);
84
+ expect(distant.matchedorigin).toBe("https://pay.example.com");
85
+ const lonely = phishverdictof({ origin: "https://first.example", granted: [], threshold: 0.4, now });
86
+ expect(lonely.blocked).toBe(false);
87
+ expect(lonely.matchedorigin).toBeUndefined();
88
+ });
89
+
90
+ it("blocks exactly at the threshold boundary", () => {
91
+ const origin = "https://pay.example.com.attacker.example";
92
+ const granted = ["https://pay.example.com"];
93
+ const distance = lookalikedistance(origin, granted[0] as string);
94
+ const at = phishverdictof({ origin, granted, threshold: distance, now });
95
+ expect(at.blocked).toBe(true);
96
+ const below = phishverdictof({ origin, granted, threshold: distance / 2, now });
97
+ expect(below.blocked).toBe(false);
98
+ });
99
+
100
+ it("expires verdicts past their freshness window", () => {
101
+ const verdict = phishverdictof({
102
+ origin: "https://pay.example.com",
103
+ granted: ["https://pay.example.com"],
104
+ threshold: 0.4,
105
+ now,
106
+ });
107
+ expect(verdictfresh(verdict, now, undefined)).toBe(true);
108
+ expect(verdictfresh(verdict, now + 1000, 5000)).toBe(true);
109
+ expect(verdictfresh(verdict, now + 6000, 5000)).toBe(false);
110
+ });
111
+
112
+ it("renders the phishguard notice the surfaces show before a login step", () => {
113
+ const blocked = phishverdictof({
114
+ origin: "https://pay.example.com.attacker.example",
115
+ granted: ["https://pay.example.com"],
116
+ threshold: 0.5,
117
+ now,
118
+ });
119
+ expect(phishnotetext(blocked)).toMatch(/blocks/);
120
+ const pass = phishverdictof({
121
+ origin: "https://pay.example.com",
122
+ granted: ["https://pay.example.com"],
123
+ threshold: 0.5,
124
+ now,
125
+ });
126
+ expect(phishnotetext(pass)).toMatch(/under the user threshold/);
127
+ });
128
+ });