@pensar/apex 2.5.0 → 2.6.1-canary.0cc77dcc
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.
- package/README.md +38 -0
- package/bin/pensar.js +12 -4
- package/build/agent-6mzhkzxj.js +13 -0
- package/build/agent-9ymr36jt.js +13 -0
- package/build/{agent-m30a62kh.js → agent-fb1d2x2a.js} +48 -50
- package/build/agent-h8z7zb26.js +15 -0
- package/build/agent-runs-awgj4sat.js +313 -0
- package/build/{apps-x4ef2gpc.js → apps-d50kkp3g.js} +50 -58
- package/build/{auth-7ay8qgxm.js → auth-396rc66q.js} +61 -68
- package/build/blackboxAgent-j3ncq55f.js +13 -0
- package/build/blackboxPentest-aw69ect4.js +59 -0
- package/build/cli-0ym9qz7r.js +175 -0
- package/build/{cli-zav1yx5k.js → cli-2dqvgkh8.js} +990 -632
- package/build/cli-38j2jc0m.js +62 -0
- package/build/{cli-ee3axgts.js → cli-3gt761d2.js} +7 -1
- package/build/{cli-sx3jkv7p.js → cli-3p4gyc9m.js} +28872 -25951
- package/build/cli-3qznjsnr.js +14 -0
- package/build/cli-5ppxe6qz.js +355 -0
- package/build/cli-691cfgdy.js +186 -0
- package/build/{cli-x92fkt2f.js → cli-6y2mzdz2.js} +55 -97
- package/build/{cli-73k43t1r.js → cli-8s4ew1hv.js} +53 -78
- package/build/{cli-53rm0rrz.js → cli-8w8k4dph.js} +1 -1
- package/build/{cli-68260ckj.js → cli-91gs9whc.js} +1658 -1606
- package/build/{cli-7vg06q5r.js → cli-9s7snz6x.js} +135 -125
- package/build/{cli-bw2ddttk.js → cli-a1m3pmke.js} +4730 -267
- package/build/{cli-knd276ed.js → cli-bb02pjt0.js} +31 -31
- package/build/{cli-p4r7q012.js → cli-c0ha5wh1.js} +16072 -12743
- package/build/{cli-j54qa6tm.js → cli-c2k4xxaw.js} +42 -42
- package/build/{cli-k71t434r.js → cli-ec2hwkv3.js} +34 -76
- package/build/{cli-vkbzcnw1.js → cli-fapvkkbh.js} +43 -40
- package/build/cli-g06s7edq.js +11 -0
- package/build/cli-gsaf4jk3.js +280 -0
- package/build/{cli-bc05qbss.js → cli-h6y3bt60.js} +13 -21
- package/build/cli-jdrx44ge.js +14 -0
- package/build/{cli-z5c0ypg1.js → cli-jp6wytx1.js} +4 -4
- package/build/cli-mz5pk30x.js +5508 -0
- package/build/{cli-kff2bbq7.js → cli-nmg32cfd.js} +43 -43
- package/build/cli-pckpvwbd.js +22 -0
- package/build/cli-pwm29ks7.js +6337 -0
- package/build/cli-retw3rw5.js +12 -0
- package/build/cli-se8ae8z3.js +2250 -0
- package/build/{cli-dm0fc0bm.js → cli-v1sarr4y.js} +5 -9
- package/build/cli-v9rrvh12.js +429 -0
- package/build/{cli-3py0a0wg.js → cli-vfta27tv.js} +206 -136
- package/build/cli-xcy2vxj2.js +1732 -0
- package/build/{cli-y3dpm18e.js → cli-xn12g67s.js} +75 -92
- package/build/cli.js +769 -195
- package/build/{config-4kzh88xz.js → config-hddxxr27.js} +16 -19
- package/build/{doctor-rz7sx6ev.js → doctor-z94afr10.js} +26 -15
- package/build/export-trajectory-rt4tf07k.js +112 -0
- package/build/{fastStrike-a54atjc8.js → fastStrike-mgws2nav.js} +56 -31
- package/build/{fixes-fv66yn1z.js → fixes-9qtsvwxx.js} +22 -30
- package/build/{getMachineId-bsd-xj3bn2fv.js → getMachineId-bsd-zr67nd9p.js} +5 -5
- package/build/{getMachineId-darwin-07jzjcx6.js → getMachineId-darwin-fd1d562d.js} +5 -5
- package/build/{getMachineId-linux-ahngynxd.js → getMachineId-linux-kjmg243j.js} +4 -4
- package/build/{getMachineId-unsupported-vy43b44z.js → getMachineId-unsupported-n4r58vke.js} +4 -4
- package/build/{getMachineId-win-8x66cynz.js → getMachineId-win-yzgpfsrb.js} +5 -5
- package/build/index-0d05xcz4.js +25 -0
- package/build/index-0ef0j7yp.js +12 -0
- package/build/index-557xary4.js +121 -0
- package/build/index-a0bxw5zq.js +17477 -0
- package/build/{index-cbwn03kz.js → index-a2xycbjs.js} +12 -31
- package/build/{index-psqjpw5m.js → index-c1h0g35p.js} +11 -11
- package/build/index-cfbd4zej.js +17 -0
- package/build/index-mqaw1ac6.js +9 -0
- package/build/{index-kyd1jm89.js → index-nfhetcq8.js} +159 -169
- package/build/index-qnyevb6q.js +1563 -0
- package/build/{index-fpwr10qm.js → index-wyn4g2c1.js} +5048 -3786
- package/build/index-zbq3rvwx.js +36 -0
- package/build/index-zhr65sg3.js +102 -0
- package/build/{issues-019678t0.js → issues-9zdszfq8.js} +51 -47
- package/build/knowledge-r6rmer3d.js +139 -0
- package/build/lib/chunk-02awpt3c.js +431 -0
- package/build/lib/chunk-0me4mrrd.js +7150 -0
- package/build/lib/chunk-2j49k090.js +922 -0
- package/build/lib/chunk-2njv7wz3.js +178 -0
- package/build/lib/chunk-2t65f749.js +1412 -0
- package/build/lib/chunk-3502z798.js +9232 -0
- package/build/lib/chunk-3q89v4xv.js +21 -0
- package/build/lib/chunk-3w05jmk6.js +685 -0
- package/build/lib/chunk-48zms2q1.js +572 -0
- package/build/lib/chunk-4ydrmwd9.js +980 -0
- package/build/lib/chunk-517rf135.js +143 -0
- package/build/lib/chunk-5cp1fp0x.js +186 -0
- package/build/lib/chunk-7gjdygcx.js +171 -0
- package/build/lib/chunk-8vhr65w3.js +272 -0
- package/build/lib/chunk-9nsrd4xg.js +12 -0
- package/build/lib/chunk-asbemhm5.js +56 -0
- package/build/lib/chunk-b8eq85d2.js +143 -0
- package/build/lib/chunk-bx94rwt5.js +24 -0
- package/build/lib/chunk-eb4h4k1f.js +213 -0
- package/build/lib/chunk-ebkq3bcq.js +102 -0
- package/build/lib/chunk-esymacwg.js +14 -0
- package/build/lib/chunk-f04pr76h.js +317 -0
- package/build/lib/chunk-fy3bbbqy.js +7896 -0
- package/build/lib/chunk-g8d2x00m.js +2429 -0
- package/build/lib/chunk-ja7j565c.js +743 -0
- package/build/lib/chunk-jah67hkt.js +120 -0
- package/build/lib/chunk-jdjagvgr.js +85 -0
- package/build/lib/chunk-k5m5rasy.js +439 -0
- package/build/lib/chunk-mp1czx2y.js +510 -0
- package/build/lib/chunk-mrjbsaf4.js +31 -0
- package/build/lib/chunk-mv74mq5n.js +59 -0
- package/build/lib/chunk-n0ba7khr.js +64 -0
- package/build/lib/chunk-ngp6p527.js +317 -0
- package/build/lib/chunk-q5e23acz.js +52 -0
- package/build/lib/chunk-sk8xnywf.js +212 -0
- package/build/lib/chunk-t5bt5vn1.js +146 -0
- package/build/lib/chunk-tsnk2k9x.js +345 -0
- package/build/lib/chunk-vt9spgd1.js +62 -0
- package/build/lib/chunk-vx228eeq.js +82 -0
- package/build/lib/chunk-wr6ke041.js +11157 -0
- package/build/lib/chunk-xcdtrzk5.js +27 -0
- package/build/lib/chunk-xpfg2fbk.js +90 -0
- package/build/lib/chunk-zrbqn62x.js +697 -0
- package/build/lib/core/agents/agentRuntime.d.ts +47 -0
- package/build/lib/core/agents/defineAgent.d.ts +82 -0
- package/build/lib/core/agents/offSecAgent/index.d.ts +9 -0
- package/build/lib/core/agents/offSecAgent/index.js +225 -0
- package/build/lib/core/agents/offSecAgent/interruptedStepFinalization.d.ts +23 -0
- package/build/lib/core/agents/offSecAgent/interruptedStepMessages.d.ts +42 -0
- package/build/lib/core/agents/offSecAgent/messagePersistence.d.ts +39 -0
- package/build/lib/core/agents/offSecAgent/offensiveSecurityAgent.d.ts +233 -0
- package/build/lib/core/agents/offSecAgent/prompt.d.ts +35 -0
- package/build/lib/core/agents/offSecAgent/recordedTools.d.ts +11 -0
- package/build/lib/core/agents/offSecAgent/runtimeContext.d.ts +43 -0
- package/build/lib/core/agents/offSecAgent/streamDiagnostics.d.ts +67 -0
- package/build/lib/core/agents/offSecAgent/subagentSpawner.d.ts +223 -0
- package/build/lib/core/agents/offSecAgent/subagentSpawner.js +19 -0
- package/build/lib/core/agents/offSecAgent/toolLifecycle.d.ts +54 -0
- package/build/lib/core/agents/offSecAgent/tools/__tests__/sandboxScript.d.ts +1 -0
- package/build/lib/core/agents/offSecAgent/tools/addMemory.d.ts +16 -0
- package/build/lib/core/agents/offSecAgent/tools/agentScratch.d.ts +17 -0
- package/build/lib/core/agents/offSecAgent/tools/applyPatch.d.ts +8 -0
- package/build/lib/core/agents/offSecAgent/tools/applyPatchImpl.d.ts +24 -0
- package/build/lib/core/agents/offSecAgent/tools/askUserQuestions.d.ts +42 -0
- package/build/lib/core/agents/offSecAgent/tools/browserHeaderRouting.d.ts +9 -0
- package/build/lib/core/agents/offSecAgent/tools/browserToolFactories.d.ts +40 -0
- package/build/lib/core/agents/offSecAgent/tools/browserTools.d.ts +96 -0
- package/build/lib/core/agents/offSecAgent/tools/callbackListener.d.ts +212 -0
- package/build/lib/core/agents/offSecAgent/tools/camoufox.d.ts +106 -0
- package/build/lib/core/agents/offSecAgent/tools/checkpointState.d.ts +17 -0
- package/build/lib/core/agents/offSecAgent/tools/completeAuthentication.d.ts +39 -0
- package/build/lib/core/agents/offSecAgent/tools/crawlAuthenticated.d.ts +36 -0
- package/build/lib/core/agents/offSecAgent/tools/createAttackSurfaceReport.d.ts +31 -0
- package/build/lib/core/agents/offSecAgent/tools/createFile.d.ts +12 -0
- package/build/lib/core/agents/offSecAgent/tools/createTask.d.ts +20 -0
- package/build/lib/core/agents/offSecAgent/tools/delegateAuth.d.ts +72 -0
- package/build/lib/core/agents/offSecAgent/tools/deleteFile.d.ts +10 -0
- package/build/lib/core/agents/offSecAgent/tools/destructiveGuard.d.ts +94 -0
- package/build/lib/core/agents/offSecAgent/tools/detectAuthScheme.d.ts +72 -0
- package/build/lib/core/agents/offSecAgent/tools/documentApp.d.ts +38 -0
- package/build/lib/core/agents/offSecAgent/tools/documentEndpoint.d.ts +111 -0
- package/build/lib/core/agents/offSecAgent/tools/documentFinding.d.ts +285 -0
- package/build/lib/core/agents/offSecAgent/tools/email/adapters.d.ts +45 -0
- package/build/lib/core/agents/offSecAgent/tools/email/adapters.js +8 -0
- package/build/lib/core/agents/offSecAgent/tools/email/getAttachments.d.ts +33 -0
- package/build/lib/core/agents/offSecAgent/tools/email/getMessage.d.ts +20 -0
- package/build/lib/core/agents/offSecAgent/tools/email/index.d.ts +145 -0
- package/build/lib/core/agents/offSecAgent/tools/email/listInboxes.d.ts +23 -0
- package/build/lib/core/agents/offSecAgent/tools/email/listMessages.d.ts +22 -0
- package/build/lib/core/agents/offSecAgent/tools/email/markRead.d.ts +20 -0
- package/build/lib/core/agents/offSecAgent/tools/email/searchMessages.d.ts +24 -0
- package/build/lib/core/agents/offSecAgent/tools/email/sendEmail.d.ts +28 -0
- package/build/lib/core/agents/offSecAgent/tools/email/types.d.ts +40 -0
- package/build/lib/core/agents/offSecAgent/tools/email/types.js +0 -0
- package/build/lib/core/agents/offSecAgent/tools/executeCommand.d.ts +84 -0
- package/build/lib/core/agents/offSecAgent/tools/extractJsEndpoints.d.ts +13 -0
- package/build/lib/core/agents/offSecAgent/tools/fileWorkspace.d.ts +21 -0
- package/build/lib/core/agents/offSecAgent/tools/fileWorkspaceRemote.d.ts +13 -0
- package/build/lib/core/agents/offSecAgent/tools/getMemory.d.ts +12 -0
- package/build/lib/core/agents/offSecAgent/tools/getPage.d.ts +17 -0
- package/build/lib/core/agents/offSecAgent/tools/gitDiff.d.ts +12 -0
- package/build/lib/core/agents/offSecAgent/tools/gitStatus.d.ts +10 -0
- package/build/lib/core/agents/offSecAgent/tools/glob.d.ts +8 -0
- package/build/lib/core/agents/offSecAgent/tools/globImpl.d.ts +13 -0
- package/build/lib/core/agents/offSecAgent/tools/globPattern.d.ts +7 -0
- package/build/lib/core/agents/offSecAgent/tools/grep.d.ts +10 -0
- package/build/lib/core/agents/offSecAgent/tools/grepImpl.d.ts +34 -0
- package/build/lib/core/agents/offSecAgent/tools/httpRequest.d.ts +70 -0
- package/build/lib/core/agents/offSecAgent/tools/index.d.ts +466 -0
- package/build/lib/core/agents/offSecAgent/tools/index.js +262 -0
- package/build/lib/core/agents/offSecAgent/tools/listFiles.d.ts +9 -0
- package/build/lib/core/agents/offSecAgent/tools/listFilesImpl.d.ts +27 -0
- package/build/lib/core/agents/offSecAgent/tools/listMemories.d.ts +13 -0
- package/build/lib/core/agents/offSecAgent/tools/listPromptInjections.d.ts +18 -0
- package/build/lib/core/agents/offSecAgent/tools/listTasks.d.ts +24 -0
- package/build/lib/core/agents/offSecAgent/tools/patchApply.d.ts +24 -0
- package/build/lib/core/agents/offSecAgent/tools/patchParse.d.ts +24 -0
- package/build/lib/core/agents/offSecAgent/tools/perCommandShell.d.ts +82 -0
- package/build/lib/core/agents/offSecAgent/tools/playwrightMcp.d.ts +369 -0
- package/build/lib/core/agents/offSecAgent/tools/probeAuthEndpoints.d.ts +28 -0
- package/build/lib/core/agents/offSecAgent/tools/profileCodebase.d.ts +33 -0
- package/build/lib/core/agents/offSecAgent/tools/provideComparisonResults.d.ts +31 -0
- package/build/lib/core/agents/offSecAgent/tools/queryWhiteboxCatalog.d.ts +17 -0
- package/build/lib/core/agents/offSecAgent/tools/readFile.d.ts +11 -0
- package/build/lib/core/agents/offSecAgent/tools/readFileImpl.d.ts +31 -0
- package/build/lib/core/agents/offSecAgent/tools/readSkill.d.ts +15 -0
- package/build/lib/core/agents/offSecAgent/tools/reportError.d.ts +31 -0
- package/build/lib/core/agents/offSecAgent/tools/response.d.ts +15 -0
- package/build/lib/core/agents/offSecAgent/tools/runAttackSurface.d.ts +81 -0
- package/build/lib/core/agents/offSecAgent/tools/runCodeQuery.d.ts +44 -0
- package/build/lib/core/agents/offSecAgent/tools/runPentestWorkflow.d.ts +32 -0
- package/build/lib/core/agents/offSecAgent/tools/runWhiteboxScan.d.ts +52 -0
- package/build/lib/core/agents/offSecAgent/tools/sandbox.d.ts +30 -0
- package/build/lib/core/agents/offSecAgent/tools/sandboxPlaywright.d.ts +137 -0
- package/build/lib/core/agents/offSecAgent/tools/sandboxScript.d.ts +6 -0
- package/build/lib/core/agents/offSecAgent/tools/scopeGuard.d.ts +91 -0
- package/build/lib/core/agents/offSecAgent/tools/scriptSyntaxCheck.d.ts +45 -0
- package/build/lib/core/agents/offSecAgent/tools/smsInbox.d.ts +67 -0
- package/build/lib/core/agents/offSecAgent/tools/smsInbox.js +6 -0
- package/build/lib/core/agents/offSecAgent/tools/smsListMessages.d.ts +29 -0
- package/build/lib/core/agents/offSecAgent/tools/spawnCodingAgent.d.ts +43 -0
- package/build/lib/core/agents/offSecAgent/tools/spawnPentestAgent.d.ts +51 -0
- package/build/lib/core/agents/offSecAgent/tools/spawnPentestSwarm.d.ts +52 -0
- package/build/lib/core/agents/offSecAgent/tools/submitPlan.d.ts +12 -0
- package/build/lib/core/agents/offSecAgent/tools/testEndpointVariations.d.ts +34 -0
- package/build/lib/core/agents/offSecAgent/tools/threatModelGenerator.d.ts +32 -0
- package/build/lib/core/agents/offSecAgent/tools/toolOutput.d.ts +23 -0
- package/build/lib/core/agents/offSecAgent/tools/types.d.ts +215 -0
- package/build/lib/core/agents/offSecAgent/tools/updateFile.d.ts +14 -0
- package/build/lib/core/agents/offSecAgent/tools/updateTask.d.ts +20 -0
- package/build/lib/core/agents/offSecAgent/tools/validateDiscovery.d.ts +24 -0
- package/build/lib/core/agents/offSecAgent/tools/webSearch.d.ts +16 -0
- package/build/lib/core/agents/offSecAgent/tools/whiteboxCandidates.d.ts +108 -0
- package/build/lib/core/agents/offSecAgent/tools/whiteboxJobs.d.ts +157 -0
- package/build/lib/core/agents/offSecAgent/tools/windowsCurl.d.ts +20 -0
- package/build/lib/core/agents/offSecAgent/tools/workspaceApps.d.ts +120 -0
- package/build/lib/core/agents/offSecAgent/tools/workspaceDomains.d.ts +22 -0
- package/build/lib/core/agents/offSecAgent/tools/workspaceFailure.d.ts +6 -0
- package/build/lib/core/agents/offSecAgent/tools/writePlan.d.ts +13 -0
- package/build/lib/core/agents/offSecAgent/trace.d.ts +268 -0
- package/build/lib/core/agents/offSecAgent/types.d.ts +543 -0
- package/build/lib/core/agents/offSecAgent/types.js +7 -0
- package/build/lib/core/agents/specialized/attackSurface/blackboxAgent.d.ts +66 -0
- package/build/lib/core/agents/specialized/attackSurface/blackboxAgent.js +33 -0
- package/build/lib/core/agents/specialized/attackSurface/blackboxRiskScoring.d.ts +30 -0
- package/build/lib/core/agents/specialized/attackSurface/grpcSchema.d.ts +93 -0
- package/build/lib/core/agents/specialized/attackSurface/jsExtraction.d.ts +42 -0
- package/build/lib/core/agents/specialized/attackSurface/prompts.d.ts +1 -0
- package/build/lib/core/agents/specialized/attackSurface/schemas.d.ts +284 -0
- package/build/lib/core/agents/specialized/attackSurface/schemas.js +144 -0
- package/build/lib/core/agents/specialized/attackSurface/types.d.ts +32 -0
- package/build/lib/core/agents/specialized/authenticationAgent/agent.d.ts +73 -0
- package/build/lib/core/agents/specialized/authenticationAgent/index.d.ts +4 -0
- package/build/lib/core/agents/specialized/authenticationAgent/index.js +71 -0
- package/build/lib/core/agents/specialized/authenticationAgent/prompts.d.ts +10 -0
- package/build/lib/core/agents/specialized/authenticationAgent/types.d.ts +620 -0
- package/build/lib/core/agents/specialized/benchmark/comparisonAgent.d.ts +10 -0
- package/build/lib/core/agents/specialized/benchmark/docker-utils.d.ts +17 -0
- package/build/lib/core/agents/specialized/benchmark/flag-detector.d.ts +60 -0
- package/build/lib/core/agents/specialized/benchmark/index.d.ts +8 -0
- package/build/lib/core/agents/specialized/benchmark/prompts.d.ts +1 -0
- package/build/lib/core/agents/specialized/benchmark/remote/circuit-breaker.d.ts +22 -0
- package/build/lib/core/agents/specialized/benchmark/remote/daytona-wrapper.d.ts +33 -0
- package/build/lib/core/agents/specialized/benchmark/remote/index.d.ts +2 -0
- package/build/lib/core/agents/specialized/benchmark/types.d.ts +92 -0
- package/build/lib/core/agents/specialized/benchmarkComparisonAgent.d.ts +27 -0
- package/build/lib/core/agents/specialized/codeAgent/agent.d.ts +50 -0
- package/build/lib/core/agents/specialized/codeAgent/agent.js +31 -0
- package/build/lib/core/agents/specialized/codeAgent/prompts.d.ts +1 -0
- package/build/lib/core/agents/specialized/codeAgent/schemas.d.ts +11 -0
- package/build/lib/core/agents/specialized/codeAgent/schemas.js +10 -0
- package/build/lib/core/agents/specialized/cvssScorer/index.d.ts +56 -0
- package/build/lib/core/agents/specialized/environment/agent.d.ts +43 -0
- package/build/lib/core/agents/specialized/environment/index.d.ts +4 -0
- package/build/lib/core/agents/specialized/environment/prompts.d.ts +10 -0
- package/build/lib/core/agents/specialized/environment/types.d.ts +37 -0
- package/build/lib/core/agents/specialized/findingJudge/agent.d.ts +22 -0
- package/build/lib/core/agents/specialized/findingJudge/agent.js +224 -0
- package/build/lib/core/agents/specialized/findingJudge/index.d.ts +31 -0
- package/build/lib/core/agents/specialized/findingJudge/index.js +13 -0
- package/build/lib/core/agents/specialized/findingJudge/prompts.d.ts +3 -0
- package/build/lib/core/agents/specialized/findingJudge/types.d.ts +77 -0
- package/build/lib/core/agents/specialized/googleSignInPrompt.d.ts +4 -0
- package/build/lib/core/agents/specialized/mobileOtpPrompt.d.ts +1 -0
- package/build/lib/core/agents/specialized/patching/agent.d.ts +64 -0
- package/build/lib/core/agents/specialized/patching/index.d.ts +4 -0
- package/build/lib/core/agents/specialized/patching/index.js +44 -0
- package/build/lib/core/agents/specialized/patching/prompts.d.ts +17 -0
- package/build/lib/core/agents/specialized/patching/types.d.ts +40 -0
- package/build/lib/core/agents/specialized/pentest/agent.d.ts +172 -0
- package/build/lib/core/agents/specialized/pentest/agent.js +43 -0
- package/build/lib/core/agents/specialized/pentest/helperWorkspace.d.ts +16 -0
- package/build/lib/core/agents/specialized/pentest/planPrompt.d.ts +13 -0
- package/build/lib/core/agents/specialized/utils.d.ts +7 -0
- package/build/lib/core/agents/specialized/vulnerabilityReproduction/agent.d.ts +94 -0
- package/build/lib/core/agents/specialized/whiteboxAttackSurface/agent.d.ts +148 -0
- package/build/lib/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.d.ts +61 -0
- package/build/lib/core/agents/specialized/whiteboxAttackSurface/index.d.ts +5 -0
- package/build/lib/core/agents/specialized/whiteboxAttackSurface/index.js +47 -0
- package/build/lib/core/agents/specialized/whiteboxAttackSurface/prompts.d.ts +34 -0
- package/build/lib/core/agents/specialized/whiteboxAttackSurface/types.d.ts +382 -0
- package/build/lib/core/agents/specialized/whiteboxAttackSurface/types.js +21 -0
- package/build/lib/core/ai/ai.d.ts +332 -0
- package/build/lib/core/ai/ai.js +68 -0
- package/build/lib/core/ai/caching.d.ts +26 -0
- package/build/lib/core/ai/compactionEvidence.d.ts +15 -0
- package/build/lib/core/ai/compactionTelemetry.d.ts +55 -0
- package/build/lib/core/ai/contextManagement.d.ts +85 -0
- package/build/lib/core/ai/index.d.ts +10 -0
- package/build/lib/core/ai/index.js +72 -0
- package/build/lib/core/ai/inference-attempt/adapters.d.ts +15 -0
- package/build/lib/core/ai/inference-attempt/builder.d.ts +31 -0
- package/build/lib/core/ai/inference-attempt/envelope.d.ts +120 -0
- package/build/lib/core/ai/inference-attempt/errors.d.ts +6 -0
- package/build/lib/core/ai/inference-attempt/identity.d.ts +13 -0
- package/build/lib/core/ai/inference-attempt/index.d.ts +36 -0
- package/build/lib/core/ai/inference-attempt/project.d.ts +21 -0
- package/build/lib/core/ai/inference-attempt/recording.d.ts +23 -0
- package/build/lib/core/ai/inference-attempt/tokens.d.ts +13 -0
- package/build/lib/core/ai/inference-attempt/validate.d.ts +2 -0
- package/build/lib/core/ai/mantle.d.ts +28 -0
- package/build/lib/core/ai/model-history.d.ts +4 -0
- package/build/lib/core/ai/models/anthropic.d.ts +2 -0
- package/build/lib/core/ai/models/bedrock.d.ts +2 -0
- package/build/lib/core/ai/models/claude-capabilities.d.ts +6 -0
- package/build/lib/core/ai/models/concentrate.d.ts +2 -0
- package/build/lib/core/ai/models/google.d.ts +2 -0
- package/build/lib/core/ai/models/inception.d.ts +2 -0
- package/build/lib/core/ai/models/index.d.ts +29 -0
- package/build/lib/core/ai/models/mantle.d.ts +2 -0
- package/build/lib/core/ai/models/openai.d.ts +2 -0
- package/build/lib/core/ai/models/openrouter.d.ts +2 -0
- package/build/lib/core/ai/models/pensar.d.ts +2 -0
- package/build/lib/core/ai/native-rollout-evidence/capture.d.ts +31 -0
- package/build/lib/core/ai/native-rollout-evidence/content.d.ts +14 -0
- package/build/lib/core/ai/native-rollout-evidence/index.d.ts +9 -0
- package/build/lib/core/ai/native-rollout-evidence/provider-metadata.d.ts +15 -0
- package/build/lib/core/ai/native-rollout-evidence/schema.d.ts +554 -0
- package/build/lib/core/ai/native-rollout-evidence/validate.d.ts +8 -0
- package/build/lib/core/ai/providers/anthropic.d.ts +2 -0
- package/build/lib/core/ai/providers/concentrate.d.ts +9 -0
- package/build/lib/core/ai/providers/custom.d.ts +3 -0
- package/build/lib/core/ai/providers/hoonify.d.ts +6 -0
- package/build/lib/core/ai/providers/openai-pro-fetch.d.ts +2 -0
- package/build/lib/core/ai/providers/pensar.d.ts +34 -0
- package/build/lib/core/ai/providers/pensarFormatters.d.ts +2 -0
- package/build/lib/core/ai/providers/pensarResponsesStream.d.ts +15 -0
- package/build/lib/core/ai/providers/pensarSSE.d.ts +23 -0
- package/build/lib/core/ai/providers/pensarSigning.d.ts +1 -0
- package/build/lib/core/ai/providers/pensarStreamUsage.d.ts +19 -0
- package/build/lib/core/ai/streamTelemetry.d.ts +22 -0
- package/build/lib/core/ai/utils.d.ts +90 -0
- package/build/lib/core/ai/utils.js +22 -0
- package/build/lib/core/api/apiClient.d.ts +7 -0
- package/build/lib/core/api/apps.d.ts +213 -0
- package/build/lib/core/api/attackSurface.d.ts +14 -0
- package/build/lib/core/api/authentication.d.ts +3 -0
- package/build/lib/core/api/benchmark.d.ts +5 -0
- package/build/lib/core/api/blackboxPentest.d.ts +11 -0
- package/build/lib/core/api/clientIdentity.d.ts +10 -0
- package/build/lib/core/api/constants.d.ts +5 -0
- package/build/lib/core/api/domains.d.ts +19 -0
- package/build/lib/core/api/environment.d.ts +16 -0
- package/build/lib/core/api/index.d.ts +41 -0
- package/build/lib/core/api/index.js +187 -0
- package/build/lib/core/api/issues.d.ts +273 -0
- package/build/lib/core/api/knowledge.d.ts +33 -0
- package/build/lib/core/api/localWorker.d.ts +4 -0
- package/build/lib/core/api/offesecAgent.d.ts +34 -0
- package/build/lib/core/api/patching.d.ts +15 -0
- package/build/lib/core/api/recordedRun.d.ts +36 -0
- package/build/lib/core/api/targetedPentest.d.ts +15 -0
- package/build/lib/core/api/targetedPentest.js +32 -0
- package/build/lib/core/api/threatModel.d.ts +1 -0
- package/build/lib/core/api/trajectoryExport.d.ts +38 -0
- package/build/lib/core/api/vulnerabilityReproduction.d.ts +10 -0
- package/build/lib/core/assets/wordlists.d.ts +31 -0
- package/build/lib/core/atif/convert.d.ts +27 -0
- package/build/lib/core/atif/index.d.ts +7 -0
- package/build/lib/core/atif/schema.d.ts +459 -0
- package/build/lib/core/atif/serialize.d.ts +2 -0
- package/build/lib/core/atif/test-fixtures.d.ts +28 -0
- package/build/lib/core/atif/validate.d.ts +12 -0
- package/build/lib/core/auth/connection.d.ts +12 -0
- package/build/lib/core/auth/device-flow.d.ts +38 -0
- package/build/lib/core/auth/gateway.d.ts +25 -0
- package/build/lib/core/auth/index.d.ts +8 -0
- package/build/lib/core/auth/signing.d.ts +14 -0
- package/build/lib/core/auth/token.d.ts +17 -0
- package/build/lib/core/auth/types.d.ts +85 -0
- package/build/lib/core/auth/workspaces.d.ts +34 -0
- package/build/lib/core/benchmark/index.d.ts +3 -0
- package/build/lib/core/benchmark/report.d.ts +3 -0
- package/build/lib/core/benchmark/runner.d.ts +3 -0
- package/build/lib/core/benchmark/types.d.ts +80 -0
- package/build/lib/core/cli/logLevelArgs.d.ts +18 -0
- package/build/lib/core/cli/model.d.ts +9 -0
- package/build/lib/core/cli/pentestMode.d.ts +5 -0
- package/build/lib/core/cli/tuiArgs.d.ts +5 -0
- package/build/lib/core/config/config.d.ts +86 -0
- package/build/lib/core/config/customProviders.d.ts +62 -0
- package/build/lib/core/config/index.d.ts +6 -0
- package/build/lib/core/config/index.js +6 -0
- package/build/lib/core/credentials/index.d.ts +2 -0
- package/build/lib/core/credentials/manager.d.ts +85 -0
- package/build/lib/core/credentials/types.d.ts +79 -0
- package/build/lib/core/doctor.d.ts +1 -0
- package/build/lib/core/eventBus.d.ts +195 -0
- package/build/lib/core/eventBus.js +6 -0
- package/build/lib/core/findings/attackSurfaceRegistry.d.ts +88 -0
- package/build/lib/core/findings/attackSurfaceRegistry.js +6 -0
- package/build/lib/core/findings/registry.d.ts +177 -0
- package/build/lib/core/findings/registry.js +21 -0
- package/build/lib/core/history.d.ts +23 -0
- package/build/lib/core/hoonify.d.ts +8 -0
- package/build/lib/core/http/parse.d.ts +50 -0
- package/build/lib/core/http/redirects.d.ts +23 -0
- package/build/lib/core/http/targetHeaders.d.ts +30 -0
- package/build/lib/core/http/targetScope.d.ts +12 -0
- package/build/lib/core/http/types.d.ts +11 -0
- package/build/lib/core/id/id.d.ts +60 -0
- package/build/lib/core/id/id.js +28 -0
- package/build/lib/core/installation/index.d.ts +25 -0
- package/build/lib/core/integrations/herdr.d.ts +13 -0
- package/build/lib/core/integrations/surface/index.d.ts +37 -0
- package/build/lib/core/integrations/surface/types.d.ts +22 -0
- package/build/lib/core/integrations/wandb/client.d.ts +23 -0
- package/build/lib/core/integrations/wandb/upload.d.ts +36 -0
- package/build/lib/core/logger/index.d.ts +62 -0
- package/build/lib/core/logger/structured.d.ts +30 -0
- package/build/lib/core/memory/index.d.ts +92 -0
- package/build/lib/core/memory/index.js +21 -0
- package/build/lib/core/messages/types.d.ts +96 -0
- package/build/lib/core/obfuscation/engine.d.ts +51 -0
- package/build/lib/core/obfuscation/index.d.ts +20 -0
- package/build/lib/core/observability/active-root-spans.d.ts +5 -0
- package/build/lib/core/observability/active-spans.d.ts +22 -0
- package/build/lib/core/observability/index.d.ts +36 -0
- package/build/lib/core/observability/index.js +27 -0
- package/build/lib/core/observability/runtime.d.ts +63 -0
- package/build/lib/core/observability/telemetry.d.ts +52 -0
- package/build/lib/core/observability/testkit.d.ts +24 -0
- package/build/lib/core/operator/approvalGate.d.ts +61 -0
- package/build/lib/core/operator/index.d.ts +9 -0
- package/build/lib/core/operator/types.d.ts +167 -0
- package/build/lib/core/plan/index.d.ts +16 -0
- package/build/lib/core/prompt-injections/index.d.ts +111 -0
- package/build/lib/core/prompt-injections/index.js +24 -0
- package/build/lib/core/providers/index.d.ts +5 -0
- package/build/lib/core/providers/types.d.ts +13 -0
- package/build/lib/core/providers/utils.d.ts +15 -0
- package/build/lib/core/providers/verify.d.ts +11 -0
- package/build/lib/core/report/builder.d.ts +9 -0
- package/build/lib/core/report/index.d.ts +7 -0
- package/build/lib/core/report/renderers/json.d.ts +2 -0
- package/build/lib/core/report/renderers/markdown.d.ts +2 -0
- package/build/lib/core/report/schemas.d.ts +115 -0
- package/build/lib/core/runtime/agentExecution.d.ts +15 -0
- package/build/lib/core/runtime/launchLocalWorker.d.ts +33 -0
- package/build/lib/core/runtime/localRunLock.d.ts +9 -0
- package/build/lib/core/runtime/localRunWorker.d.ts +18 -0
- package/build/lib/core/runtime/localWorkerEndpoint.d.ts +14 -0
- package/build/lib/core/runtime/localWorkerProtocol.d.ts +294 -0
- package/build/lib/core/runtime/localWorkerTransport.d.ts +30 -0
- package/build/lib/core/runtime/persistenceError.d.ts +9 -0
- package/build/lib/core/runtime/recordedExecution.d.ts +6 -0
- package/build/lib/core/runtime/recordedRunClient.d.ts +34 -0
- package/build/lib/core/runtime/runCheckpointStore.d.ts +11 -0
- package/build/lib/core/runtime/runContext.d.ts +48 -0
- package/build/lib/core/runtime/runControl.d.ts +44 -0
- package/build/lib/core/runtime/runControlStore.d.ts +83 -0
- package/build/lib/core/runtime/runDeadline.d.ts +4 -0
- package/build/lib/core/runtime/runEvidence.d.ts +37 -0
- package/build/lib/core/runtime/runInference.d.ts +9 -0
- package/build/lib/core/runtime/runModelStore.d.ts +27 -0
- package/build/lib/core/runtime/runObservation.d.ts +19 -0
- package/build/lib/core/runtime/runRecoveryContext.d.ts +20 -0
- package/build/lib/core/runtime/runRecoveryPlan.d.ts +38 -0
- package/build/lib/core/runtime/runRecoveryStore.d.ts +77 -0
- package/build/lib/core/runtime/runStore.d.ts +133 -0
- package/build/lib/core/runtime/runStorePath.d.ts +1 -0
- package/build/lib/core/runtime/runToolStore.d.ts +50 -0
- package/build/lib/core/runtime/runTools.d.ts +16 -0
- package/build/lib/core/runtime/sqliteControlStore.d.ts +41 -0
- package/build/lib/core/runtime/sqliteModelStore.d.ts +20 -0
- package/build/lib/core/runtime/sqliteRecoveryStore.d.ts +35 -0
- package/build/lib/core/runtime/sqliteRunStore.d.ts +8 -0
- package/build/lib/core/runtime/sqliteToolStore.d.ts +30 -0
- package/build/lib/core/runtime/testfixtures/localWorkerChild.d.ts +1 -0
- package/build/lib/core/runtime/testfixtures/localWorkerLauncher.d.ts +1 -0
- package/build/lib/core/runtime/testfixtures/recoveryChild.d.ts +1 -0
- package/build/lib/core/services/rateLimiter/index.d.ts +36 -0
- package/build/lib/core/services/rateLimiter/types.d.ts +10 -0
- package/build/lib/core/session/execution-metrics.d.ts +37 -0
- package/build/lib/core/session/index.d.ts +586 -0
- package/build/lib/core/session/index.js +36 -0
- package/build/lib/core/session/loader.d.ts +30 -0
- package/build/lib/core/session/persistence.d.ts +215 -0
- package/build/lib/core/session/persistence.js +35 -0
- package/build/lib/core/session/types.d.ts +59 -0
- package/build/lib/core/session/usage.d.ts +37 -0
- package/build/lib/core/skills/builtins/index.d.ts +13 -0
- package/build/lib/core/skills/builtins/pentest.d.ts +19 -0
- package/build/lib/core/skills/builtins/promptInjection.d.ts +2 -0
- package/build/lib/core/skills/builtins/threatModel.d.ts +11 -0
- package/build/lib/core/skills/index.d.ts +5 -0
- package/build/lib/core/skills/parser.d.ts +11 -0
- package/build/lib/core/skills/registry.d.ts +30 -0
- package/build/lib/core/skills/scanner.d.ts +12 -0
- package/build/lib/core/skills/types.d.ts +44 -0
- package/build/lib/core/skills/utils.d.ts +6 -0
- package/build/lib/core/storage/index.d.ts +248 -0
- package/build/lib/core/tasks/index.d.ts +78 -0
- package/build/lib/core/tools/backends/capture.d.ts +10 -0
- package/build/lib/core/tools/backends/collectCommand.d.ts +9 -0
- package/build/lib/core/tools/backends/helpers.d.ts +43 -0
- package/build/lib/core/tools/backends/httpTransport.d.ts +18 -0
- package/build/lib/core/tools/backends/index.d.ts +8 -0
- package/build/lib/core/tools/backends/index.js +31 -0
- package/build/lib/core/tools/backends/local.d.ts +11 -0
- package/build/lib/core/tools/backends/policy.d.ts +39 -0
- package/build/lib/core/tools/backends/resolve.d.ts +25 -0
- package/build/lib/core/tools/backends/types.d.ts +240 -0
- package/build/lib/core/tools/backends/whitebox.d.ts +6 -0
- package/build/lib/core/tools/backends/whiteboxJobs.d.ts +18 -0
- package/build/lib/core/tools/backends/windowsProgram.d.ts +4 -0
- package/build/lib/core/tools/daytonaSandbox.d.ts +14 -0
- package/build/lib/core/toolset/index.d.ts +28 -0
- package/build/lib/core/util/lazyLogger.d.ts +16 -0
- package/build/lib/core/utils/concurrency.d.ts +16 -0
- package/build/lib/core/utils/prompt.d.ts +12 -0
- package/build/lib/core/whitebox/adapters.d.ts +29 -0
- package/build/lib/core/whitebox/artifacts.d.ts +32 -0
- package/build/lib/core/whitebox/boundedProcess.d.ts +35 -0
- package/build/lib/core/whitebox/candidates.d.ts +29 -0
- package/build/lib/core/whitebox/catalog.d.ts +10 -0
- package/build/lib/core/whitebox/index.d.ts +9 -0
- package/build/lib/core/whitebox/jobKernel.d.ts +33 -0
- package/build/lib/core/whitebox/jobSupervisor.d.ts +14 -0
- package/build/lib/core/whitebox/jobs.d.ts +17 -0
- package/build/lib/core/whitebox/paths.d.ts +22 -0
- package/build/lib/core/whitebox/profileTransport.d.ts +10 -0
- package/build/lib/core/whitebox/profiles.d.ts +3 -0
- package/build/lib/core/whitebox/repoProfile.d.ts +4 -0
- package/build/lib/core/whitebox/types.d.ts +108 -0
- package/build/lib/core/workflows/fastStrike.d.ts +4 -0
- package/build/lib/core/workflows/pentest.d.ts +167 -0
- package/build/lib/core/workflows/pentest.js +42 -0
- package/build/lib/core/workflows/seams.d.ts +102 -0
- package/build/lib/core/workflows/seams.js +37 -0
- package/build/lib/core/workflows/threatModel.d.ts +26 -0
- package/build/lib/core/workflows/whiteboxAttackSurface.d.ts +142 -0
- package/build/lib/core/workflows/whiteboxAttackSurface.js +36 -0
- package/build/lib/index.d.ts +26 -0
- package/build/lib/index.js +96 -0
- package/build/lib/lib/attack-path/types.d.ts +9 -0
- package/build/lib/lib/cvss/calculator.d.ts +31 -0
- package/build/lib/lib/cvss/index.d.ts +24 -0
- package/build/lib/lib/cvss/index.js +24 -0
- package/build/lib/lib/cvss/macrovector-scores.d.ts +59 -0
- package/build/lib/lib/cvss/types.d.ts +114 -0
- package/build/lib/lib/cvss/types.js +8 -0
- package/build/lib/lib/cwe/cwe-catalog.d.ts +13 -0
- package/build/lib/lib/cwe/types.d.ts +28 -0
- package/build/lib/lib/cwe/validate.d.ts +15 -0
- package/build/lib/lib/evidence/types.d.ts +12 -0
- package/build/lib/util/errors.d.ts +186 -0
- package/build/lib/util/lock.d.ts +2 -0
- package/build/lib/util/name.d.ts +14 -0
- package/build/lib/util/url.d.ts +40 -0
- package/build/{logs-wzr5176k.js → logs-1r7nkzhg.js} +22 -30
- package/build/{multipart-parser-g2xxkezz.js → multipart-parser-vjjdbpd1.js} +10 -10
- package/build/native-rollout-evidence-y0vdtjje.js +135 -0
- package/build/offesecAgent-dfj42jkj.js +22 -0
- package/build/{parse-rtccymzn.js → parse-cqecqrc5.js} +0 -4
- package/build/pentest-n8k0dd5q.js +22 -0
- package/build/pentests-jh9az51h.js +143 -0
- package/build/recordedRunClient-mvendk9n.js +11 -0
- package/build/{targetedPentest-tdwtgmxm.js → targetedPentest-rgffxsgd.js} +22 -22
- package/build/{targets-763n91t7.js → targets-wn0gmdhe.js} +24 -32
- package/build/threatModel-6efzjpbv.js +21 -0
- package/build/{token-3bbp59y6.js → token-mk7t8y0y.js} +2 -2
- package/build/{token-util-6xjme11b.js → token-util-vd776hrv.js} +1 -2
- package/build/{uninstall-d33159cz.js → uninstall-cmv057jn.js} +3 -3
- package/build/{upload-9qj1kpt9.js → upload-vkkj6h54.js} +8 -14
- package/build/{utils-t7gc2903.js → utils-b1kae4f3.js} +29 -29
- package/package.json +230 -5
- package/src/cli/agent-runs.attach.test.ts +359 -0
- package/src/cli/agent-runs.test.ts +1172 -0
- package/src/cli/agent-runs.ts +430 -0
- package/src/cli/apps.test.ts +63 -0
- package/src/cli/apps.ts +526 -0
- package/src/cli/auth.ts +375 -0
- package/src/cli/command-exit.test.ts +15 -0
- package/src/cli/command-exit.ts +3 -0
- package/src/cli/config.ts +210 -0
- package/src/cli/export-trajectory.test.ts +224 -0
- package/src/cli/export-trajectory.ts +119 -0
- package/src/cli/fixes.ts +56 -0
- package/src/cli/issues.test.ts +214 -0
- package/src/cli/issues.ts +242 -0
- package/src/cli/json-output.test.ts +62 -0
- package/src/cli/knowledge.test.ts +140 -0
- package/src/cli/knowledge.ts +146 -0
- package/src/cli/launcher.test.ts +81 -0
- package/src/cli/logs.ts +111 -0
- package/src/cli/native-rollout-evidence.test.ts +322 -0
- package/src/cli/native-rollout-evidence.ts +206 -0
- package/src/cli/pentests.test.ts +194 -0
- package/src/cli/pentests.ts +159 -0
- package/src/cli/resume.test.ts +107 -0
- package/src/cli/targets.ts +125 -0
- package/src/cli/uninstall.ts +302 -0
- package/src/cli.ts +882 -0
- package/src/core/agents/agentRuntime.test.ts +242 -0
- package/src/core/agents/agentRuntime.ts +120 -0
- package/src/core/agents/defineAgent.ts +134 -0
- package/src/core/agents/offSecAgent/index.ts +162 -0
- package/src/core/agents/offSecAgent/interruptedStepFinalization.test.ts +295 -0
- package/src/core/agents/offSecAgent/interruptedStepFinalization.ts +162 -0
- package/src/core/agents/offSecAgent/interruptedStepMessages.test.ts +296 -0
- package/src/core/agents/offSecAgent/interruptedStepMessages.ts +137 -0
- package/src/core/agents/offSecAgent/messagePersistence.test.ts +281 -0
- package/src/core/agents/offSecAgent/messagePersistence.ts +111 -0
- package/src/core/agents/offSecAgent/offensiveSecurityAgent.runtimeContext.test.ts +689 -0
- package/src/core/agents/offSecAgent/offensiveSecurityAgent.test.ts +2899 -0
- package/src/core/agents/offSecAgent/offensiveSecurityAgent.ts +1610 -0
- package/src/core/agents/offSecAgent/prompt.test.ts +72 -0
- package/src/core/agents/offSecAgent/prompt.ts +368 -0
- package/src/core/agents/offSecAgent/recordedTools.test.ts +580 -0
- package/src/core/agents/offSecAgent/recordedTools.ts +162 -0
- package/src/core/agents/offSecAgent/runtimeContext.test.ts +531 -0
- package/src/core/agents/offSecAgent/runtimeContext.ts +387 -0
- package/src/core/agents/offSecAgent/selectiveConstruction.publicPath.test.ts +295 -0
- package/src/core/agents/offSecAgent/selectiveConstruction.test.ts +498 -0
- package/src/core/agents/offSecAgent/streamDiagnostics.test.ts +310 -0
- package/src/core/agents/offSecAgent/streamDiagnostics.ts +237 -0
- package/src/core/agents/offSecAgent/subagentSpawner.hooks.test.ts +102 -0
- package/src/core/agents/offSecAgent/subagentSpawner.test.ts +384 -0
- package/src/core/agents/offSecAgent/subagentSpawner.ts +664 -0
- package/src/core/agents/offSecAgent/toolLifecycle.test.ts +199 -0
- package/src/core/agents/offSecAgent/toolLifecycle.ts +171 -0
- package/src/core/agents/offSecAgent/tools/__tests__/sandboxScript.ts +14 -0
- package/src/core/agents/offSecAgent/tools/addMemory.ts +87 -0
- package/src/core/agents/offSecAgent/tools/agentScratch.test.ts +36 -0
- package/src/core/agents/offSecAgent/tools/agentScratch.ts +23 -0
- package/src/core/agents/offSecAgent/tools/applyPatch.test.ts +546 -0
- package/src/core/agents/offSecAgent/tools/applyPatch.ts +58 -0
- package/src/core/agents/offSecAgent/tools/applyPatchImpl.ts +300 -0
- package/src/core/agents/offSecAgent/tools/askUserQuestions.ts +81 -0
- package/src/core/agents/offSecAgent/tools/authBackendStatus.test.ts +112 -0
- package/src/core/agents/offSecAgent/tools/browserHeaderRouting.test.ts +136 -0
- package/src/core/agents/offSecAgent/tools/browserHeaderRouting.ts +81 -0
- package/src/core/agents/offSecAgent/tools/browserToolFactories.ts +372 -0
- package/src/core/agents/offSecAgent/tools/browserTools.test.ts +243 -0
- package/src/core/agents/offSecAgent/tools/browserTools.ts +285 -0
- package/src/core/agents/offSecAgent/tools/callbackListener.test.ts +1011 -0
- package/src/core/agents/offSecAgent/tools/callbackListener.ts +1038 -0
- package/src/core/agents/offSecAgent/tools/camoufox.test.ts +119 -0
- package/src/core/agents/offSecAgent/tools/camoufox.ts +199 -0
- package/src/core/agents/offSecAgent/tools/checkpointState.ts +79 -0
- package/src/core/agents/offSecAgent/tools/codingTools.windows.test.ts +625 -0
- package/src/core/agents/offSecAgent/tools/completeAuthentication.ts +136 -0
- package/src/core/agents/offSecAgent/tools/crawlAuthenticated.test.ts +267 -0
- package/src/core/agents/offSecAgent/tools/crawlAuthenticated.ts +170 -0
- package/src/core/agents/offSecAgent/tools/createAttackSurfaceReport.ts +75 -0
- package/src/core/agents/offSecAgent/tools/createFile.test.ts +111 -0
- package/src/core/agents/offSecAgent/tools/createFile.ts +44 -0
- package/src/core/agents/offSecAgent/tools/createTask.ts +118 -0
- package/src/core/agents/offSecAgent/tools/delegateAuth.ts +391 -0
- package/src/core/agents/offSecAgent/tools/deleteFile.test.ts +102 -0
- package/src/core/agents/offSecAgent/tools/deleteFile.ts +39 -0
- package/src/core/agents/offSecAgent/tools/destructiveGuard.test.ts +827 -0
- package/src/core/agents/offSecAgent/tools/destructiveGuard.ts +747 -0
- package/src/core/agents/offSecAgent/tools/detectAuthScheme.ts +191 -0
- package/src/core/agents/offSecAgent/tools/documentApp.ts +242 -0
- package/src/core/agents/offSecAgent/tools/documentEndpoint.ts +291 -0
- package/src/core/agents/offSecAgent/tools/documentFinding.test.ts +2091 -0
- package/src/core/agents/offSecAgent/tools/documentFinding.ts +1240 -0
- package/src/core/agents/offSecAgent/tools/email/adapters.test.ts +279 -0
- package/src/core/agents/offSecAgent/tools/email/adapters.ts +1121 -0
- package/src/core/agents/offSecAgent/tools/email/getAttachments.ts +73 -0
- package/src/core/agents/offSecAgent/tools/email/getMessage.ts +56 -0
- package/src/core/agents/offSecAgent/tools/email/inboxBackend.test.ts +137 -0
- package/src/core/agents/offSecAgent/tools/email/index.ts +55 -0
- package/src/core/agents/offSecAgent/tools/email/listInboxes.ts +47 -0
- package/src/core/agents/offSecAgent/tools/email/listMessages.ts +67 -0
- package/src/core/agents/offSecAgent/tools/email/markRead.ts +57 -0
- package/src/core/agents/offSecAgent/tools/email/searchMessages.ts +76 -0
- package/src/core/agents/offSecAgent/tools/email/sendEmail.ts +165 -0
- package/src/core/agents/offSecAgent/tools/email/types.ts +45 -0
- package/src/core/agents/offSecAgent/tools/executeCommand.backends.test.ts +171 -0
- package/src/core/agents/offSecAgent/tools/executeCommand.test.ts +1153 -0
- package/src/core/agents/offSecAgent/tools/executeCommand.ts +604 -0
- package/src/core/agents/offSecAgent/tools/extractJsEndpoints.ts +44 -0
- package/src/core/agents/offSecAgent/tools/fileBackends.test.ts +66 -0
- package/src/core/agents/offSecAgent/tools/fileMutations.test.ts +159 -0
- package/src/core/agents/offSecAgent/tools/fileToolBackendPurity.test.ts +72 -0
- package/src/core/agents/offSecAgent/tools/fileWorkspace.test.ts +357 -0
- package/src/core/agents/offSecAgent/tools/fileWorkspace.ts +434 -0
- package/src/core/agents/offSecAgent/tools/fileWorkspaceRemote.ts +263 -0
- package/src/core/agents/offSecAgent/tools/getMemory.ts +69 -0
- package/src/core/agents/offSecAgent/tools/getPage.test.ts +1127 -0
- package/src/core/agents/offSecAgent/tools/getPage.ts +70 -0
- package/src/core/agents/offSecAgent/tools/gitDiff.ts +67 -0
- package/src/core/agents/offSecAgent/tools/gitStatus.ts +61 -0
- package/src/core/agents/offSecAgent/tools/gitTools.test.ts +237 -0
- package/src/core/agents/offSecAgent/tools/glob.test.ts +695 -0
- package/src/core/agents/offSecAgent/tools/glob.ts +51 -0
- package/src/core/agents/offSecAgent/tools/globImpl.ts +313 -0
- package/src/core/agents/offSecAgent/tools/globPattern.ts +203 -0
- package/src/core/agents/offSecAgent/tools/grep.test.ts +922 -0
- package/src/core/agents/offSecAgent/tools/grep.ts +73 -0
- package/src/core/agents/offSecAgent/tools/grepImpl.ts +718 -0
- package/src/core/agents/offSecAgent/tools/httpBackends.test.ts +534 -0
- package/src/core/agents/offSecAgent/tools/httpRequest.test.ts +2409 -0
- package/src/core/agents/offSecAgent/tools/httpRequest.ts +425 -0
- package/src/core/agents/offSecAgent/tools/index.ts +787 -0
- package/src/core/agents/offSecAgent/tools/listFiles.bounded.test.ts +514 -0
- package/src/core/agents/offSecAgent/tools/listFiles.public-counters.test.ts +163 -0
- package/src/core/agents/offSecAgent/tools/listFiles.test.ts +521 -0
- package/src/core/agents/offSecAgent/tools/listFiles.ts +49 -0
- package/src/core/agents/offSecAgent/tools/listFilesImpl.ts +351 -0
- package/src/core/agents/offSecAgent/tools/listMemories.ts +71 -0
- package/src/core/agents/offSecAgent/tools/listPromptInjections.test.ts +201 -0
- package/src/core/agents/offSecAgent/tools/listPromptInjections.ts +78 -0
- package/src/core/agents/offSecAgent/tools/listTasks.ts +100 -0
- package/src/core/agents/offSecAgent/tools/patchApply.test.ts +573 -0
- package/src/core/agents/offSecAgent/tools/patchApply.ts +430 -0
- package/src/core/agents/offSecAgent/tools/patchParse.test.ts +383 -0
- package/src/core/agents/offSecAgent/tools/patchParse.ts +354 -0
- package/src/core/agents/offSecAgent/tools/perCommandShell.drain.test.ts +109 -0
- package/src/core/agents/offSecAgent/tools/perCommandShell.test.ts +565 -0
- package/src/core/agents/offSecAgent/tools/perCommandShell.ts +516 -0
- package/src/core/agents/offSecAgent/tools/perCommandShell.windows.test.ts +142 -0
- package/src/core/agents/offSecAgent/tools/playwrightMcp.test.ts +778 -0
- package/src/core/agents/offSecAgent/tools/playwrightMcp.ts +1426 -0
- package/src/core/agents/offSecAgent/tools/probeAuthEndpoints.ts +262 -0
- package/src/core/agents/offSecAgent/tools/profileCodebase.ts +110 -0
- package/src/core/agents/offSecAgent/tools/provideComparisonResults.ts +106 -0
- package/src/core/agents/offSecAgent/tools/queryWhiteboxCatalog.coalescing-cost.test.ts +108 -0
- package/src/core/agents/offSecAgent/tools/queryWhiteboxCatalog.test.ts +183 -0
- package/src/core/agents/offSecAgent/tools/queryWhiteboxCatalog.ts +143 -0
- package/src/core/agents/offSecAgent/tools/readFile.test.ts +1242 -0
- package/src/core/agents/offSecAgent/tools/readFile.ts +89 -0
- package/src/core/agents/offSecAgent/tools/readFileImpl.ts +956 -0
- package/src/core/agents/offSecAgent/tools/readSkill.ts +38 -0
- package/src/core/agents/offSecAgent/tools/reportError.test.ts +48 -0
- package/src/core/agents/offSecAgent/tools/reportError.ts +65 -0
- package/src/core/agents/offSecAgent/tools/response.ts +27 -0
- package/src/core/agents/offSecAgent/tools/runAttackSurface.ts +198 -0
- package/src/core/agents/offSecAgent/tools/runCodeQuery.test.ts +82 -0
- package/src/core/agents/offSecAgent/tools/runCodeQuery.ts +237 -0
- package/src/core/agents/offSecAgent/tools/runPentestWorkflow.ts +122 -0
- package/src/core/agents/offSecAgent/tools/runWhiteboxScan.ts +189 -0
- package/src/core/agents/offSecAgent/tools/runtimeBackendConsumers.test.ts +143 -0
- package/src/core/agents/offSecAgent/tools/sandbox.ts +37 -0
- package/src/core/agents/offSecAgent/tools/sandboxPlaywright.test.ts +235 -0
- package/src/core/agents/offSecAgent/tools/sandboxPlaywright.ts +1029 -0
- package/src/core/agents/offSecAgent/tools/sandboxScript.test.ts +31 -0
- package/src/core/agents/offSecAgent/tools/sandboxScript.ts +60 -0
- package/src/core/agents/offSecAgent/tools/scopeGuard.test.ts +574 -0
- package/src/core/agents/offSecAgent/tools/scopeGuard.ts +317 -0
- package/src/core/agents/offSecAgent/tools/scriptSyntaxCheck.test.ts +566 -0
- package/src/core/agents/offSecAgent/tools/scriptSyntaxCheck.ts +240 -0
- package/src/core/agents/offSecAgent/tools/selectiveFactories.test.ts +476 -0
- package/src/core/agents/offSecAgent/tools/smsInbox.ts +130 -0
- package/src/core/agents/offSecAgent/tools/smsListMessages.test.ts +443 -0
- package/src/core/agents/offSecAgent/tools/smsListMessages.ts +123 -0
- package/src/core/agents/offSecAgent/tools/spawnCodingAgent.ts +229 -0
- package/src/core/agents/offSecAgent/tools/spawnPentestAgent.test.ts +413 -0
- package/src/core/agents/offSecAgent/tools/spawnPentestAgent.ts +266 -0
- package/src/core/agents/offSecAgent/tools/spawnPentestSwarm.ts +146 -0
- package/src/core/agents/offSecAgent/tools/submitPlan.ts +51 -0
- package/src/core/agents/offSecAgent/tools/testEndpointVariations.ts +127 -0
- package/src/core/agents/offSecAgent/tools/threatModelGenerator.ts +664 -0
- package/src/core/agents/offSecAgent/tools/toolOutput.test.ts +480 -0
- package/src/core/agents/offSecAgent/tools/toolOutput.ts +160 -0
- package/src/core/agents/offSecAgent/tools/types.ts +267 -0
- package/src/core/agents/offSecAgent/tools/updateFile.ts +121 -0
- package/src/core/agents/offSecAgent/tools/updateTask.ts +107 -0
- package/src/core/agents/offSecAgent/tools/validateDiscovery.ts +123 -0
- package/src/core/agents/offSecAgent/tools/webSearch.ts +265 -0
- package/src/core/agents/offSecAgent/tools/whiteboxCandidates.ts +197 -0
- package/src/core/agents/offSecAgent/tools/whiteboxJobs.ts +312 -0
- package/src/core/agents/offSecAgent/tools/windowsCurl.test.ts +801 -0
- package/src/core/agents/offSecAgent/tools/windowsCurl.ts +202 -0
- package/src/core/agents/offSecAgent/tools/workspaceApps.test.ts +257 -0
- package/src/core/agents/offSecAgent/tools/workspaceApps.ts +334 -0
- package/src/core/agents/offSecAgent/tools/workspaceDomains.test.ts +113 -0
- package/src/core/agents/offSecAgent/tools/workspaceDomains.ts +57 -0
- package/src/core/agents/offSecAgent/tools/workspaceFailure.ts +11 -0
- package/src/core/agents/offSecAgent/tools/writePlan.ts +65 -0
- package/src/core/agents/offSecAgent/trace.test.ts +796 -0
- package/src/core/agents/offSecAgent/trace.ts +633 -0
- package/src/core/agents/offSecAgent/types.test.ts +224 -0
- package/src/core/agents/offSecAgent/types.ts +676 -0
- package/src/core/agents/specialized/attackSurface/blackboxAgent.test.ts +31 -0
- package/src/core/agents/specialized/attackSurface/blackboxAgent.ts +272 -0
- package/src/core/agents/specialized/attackSurface/blackboxRiskScoring.ts +276 -0
- package/src/core/agents/specialized/attackSurface/grpcSchema.ts +142 -0
- package/src/core/agents/specialized/attackSurface/jsExtraction.test.ts +237 -0
- package/src/core/agents/specialized/attackSurface/jsExtraction.ts +174 -0
- package/src/core/agents/specialized/attackSurface/prompts.ts +349 -0
- package/src/core/agents/specialized/attackSurface/schemas.ts +300 -0
- package/src/core/agents/specialized/attackSurface/types.ts +100 -0
- package/src/core/agents/specialized/authenticationAgent/agent.test.ts +111 -0
- package/src/core/agents/specialized/authenticationAgent/agent.ts +300 -0
- package/src/core/agents/specialized/authenticationAgent/index.ts +74 -0
- package/src/core/agents/specialized/authenticationAgent/prompts.test.ts +36 -0
- package/src/core/agents/specialized/authenticationAgent/prompts.ts +645 -0
- package/src/core/agents/specialized/authenticationAgent/types.ts +833 -0
- package/src/core/agents/specialized/benchmark/comparisonAgent.ts +344 -0
- package/src/core/agents/specialized/benchmark/docker-utils.ts +257 -0
- package/src/core/agents/specialized/benchmark/flag-detector.ts +686 -0
- package/src/core/agents/specialized/benchmark/index.ts +28 -0
- package/src/core/agents/specialized/benchmark/prompts.ts +257 -0
- package/src/core/agents/specialized/benchmark/remote/circuit-breaker.ts +108 -0
- package/src/core/agents/specialized/benchmark/remote/daytona-wrapper.ts +1005 -0
- package/src/core/agents/specialized/benchmark/remote/index.ts +2 -0
- package/src/core/agents/specialized/benchmark/types.ts +98 -0
- package/src/core/agents/specialized/benchmarkComparisonAgent.test.ts +111 -0
- package/src/core/agents/specialized/benchmarkComparisonAgent.ts +203 -0
- package/src/core/agents/specialized/codeAgent/agent.test.ts +89 -0
- package/src/core/agents/specialized/codeAgent/agent.ts +134 -0
- package/src/core/agents/specialized/codeAgent/prompts.ts +48 -0
- package/src/core/agents/specialized/codeAgent/schemas.ts +33 -0
- package/src/core/agents/specialized/cvssScorer/index.test.ts +87 -0
- package/src/core/agents/specialized/cvssScorer/index.ts +546 -0
- package/src/core/agents/specialized/environment/agent.ts +75 -0
- package/src/core/agents/specialized/environment/index.ts +16 -0
- package/src/core/agents/specialized/environment/prompts.ts +194 -0
- package/src/core/agents/specialized/environment/types.ts +48 -0
- package/src/core/agents/specialized/findingJudge/agent.test.ts +114 -0
- package/src/core/agents/specialized/findingJudge/agent.ts +59 -0
- package/src/core/agents/specialized/findingJudge/index.test.ts +155 -0
- package/src/core/agents/specialized/findingJudge/index.ts +166 -0
- package/src/core/agents/specialized/findingJudge/prompts.ts +140 -0
- package/src/core/agents/specialized/findingJudge/types.ts +115 -0
- package/src/core/agents/specialized/googleSignInPrompt.test.ts +29 -0
- package/src/core/agents/specialized/googleSignInPrompt.ts +18 -0
- package/src/core/agents/specialized/mobileOtpPrompt.ts +9 -0
- package/src/core/agents/specialized/patching/agent.test.ts +220 -0
- package/src/core/agents/specialized/patching/agent.ts +146 -0
- package/src/core/agents/specialized/patching/index.ts +20 -0
- package/src/core/agents/specialized/patching/prompts.test.ts +172 -0
- package/src/core/agents/specialized/patching/prompts.ts +396 -0
- package/src/core/agents/specialized/patching/types.ts +49 -0
- package/src/core/agents/specialized/pentest/agent.test.ts +1046 -0
- package/src/core/agents/specialized/pentest/agent.ts +1482 -0
- package/src/core/agents/specialized/pentest/helperLifecycle.test.ts +425 -0
- package/src/core/agents/specialized/pentest/helperWorkspace.test.ts +84 -0
- package/src/core/agents/specialized/pentest/helperWorkspace.ts +33 -0
- package/src/core/agents/specialized/pentest/planPrompt.ts +68 -0
- package/src/core/agents/specialized/pentest/results.test.ts +198 -0
- package/src/core/agents/specialized/utils.test.ts +16 -0
- package/src/core/agents/specialized/utils.ts +28 -0
- package/src/core/agents/specialized/vulnerabilityReproduction/agent.test.ts +195 -0
- package/src/core/agents/specialized/vulnerabilityReproduction/agent.ts +159 -0
- package/src/core/agents/specialized/whiteboxAttackSurface/agent.test.ts +39 -0
- package/src/core/agents/specialized/whiteboxAttackSurface/agent.ts +227 -0
- package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.concurrency.test.ts +138 -0
- package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.test.ts +199 -0
- package/src/core/agents/specialized/whiteboxAttackSurface/endpointDocumentationAgent.ts +330 -0
- package/src/core/agents/specialized/whiteboxAttackSurface/index.ts +24 -0
- package/src/core/agents/specialized/whiteboxAttackSurface/prompts.ts +345 -0
- package/src/core/agents/specialized/whiteboxAttackSurface/types.test.ts +67 -0
- package/src/core/agents/specialized/whiteboxAttackSurface/types.ts +234 -0
- package/src/core/ai/ai.contract.test.ts +43 -0
- package/src/core/ai/ai.durability-hooks.test.ts +527 -0
- package/src/core/ai/ai.effective-tools.test.ts +739 -0
- package/src/core/ai/ai.native-rollout-evidence.test.ts +373 -0
- package/src/core/ai/ai.outer-catch-recovery.test.ts +82 -0
- package/src/core/ai/ai.proactive-recovery.test.ts +69 -0
- package/src/core/ai/ai.recorded-attempts.test.ts +621 -0
- package/src/core/ai/ai.recorded-context.test.ts +448 -0
- package/src/core/ai/ai.recorded-tools.test.ts +850 -0
- package/src/core/ai/ai.request-retention.test.ts +296 -0
- package/src/core/ai/ai.test.ts +636 -0
- package/src/core/ai/ai.ts +2349 -0
- package/src/core/ai/caching.test.ts +179 -0
- package/src/core/ai/caching.ts +119 -0
- package/src/core/ai/compactionEvidence.test.ts +52 -0
- package/src/core/ai/compactionEvidence.ts +101 -0
- package/src/core/ai/compactionTelemetry.test.ts +621 -0
- package/src/core/ai/compactionTelemetry.ts +287 -0
- package/src/core/ai/concentrate.test.ts +28 -0
- package/src/core/ai/contextManagement.test.ts +350 -0
- package/src/core/ai/contextManagement.ts +594 -0
- package/src/core/ai/index.ts +73 -0
- package/src/core/ai/inference-attempt/adapters.test.ts +84 -0
- package/src/core/ai/inference-attempt/adapters.ts +391 -0
- package/src/core/ai/inference-attempt/attempt.test.ts +198 -0
- package/src/core/ai/inference-attempt/builder.ts +171 -0
- package/src/core/ai/inference-attempt/envelope.ts +120 -0
- package/src/core/ai/inference-attempt/errors.ts +15 -0
- package/src/core/ai/inference-attempt/fixtures/anthropic-messages-cache-create.json +18 -0
- package/src/core/ai/inference-attempt/fixtures/anthropic-messages-cache-read.json +18 -0
- package/src/core/ai/inference-attempt/fixtures/anthropic-messages-incomplete.json +16 -0
- package/src/core/ai/inference-attempt/fixtures/anthropic-messages-mixed.json +22 -0
- package/src/core/ai/inference-attempt/fixtures/anthropic-messages-non-cache.json +26 -0
- package/src/core/ai/inference-attempt/fixtures/bedrock-converse-cache-create.json +18 -0
- package/src/core/ai/inference-attempt/fixtures/bedrock-converse-cache-read.json +26 -0
- package/src/core/ai/inference-attempt/fixtures/bedrock-converse-incomplete.json +19 -0
- package/src/core/ai/inference-attempt/fixtures/bedrock-converse-mixed.json +20 -0
- package/src/core/ai/inference-attempt/fixtures/bedrock-converse-non-cache.json +24 -0
- package/src/core/ai/inference-attempt/fixtures/openai-responses-cache-read.json +17 -0
- package/src/core/ai/inference-attempt/fixtures/openai-responses-incomplete.json +16 -0
- package/src/core/ai/inference-attempt/fixtures/openai-responses-non-cache.json +23 -0
- package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-rest-incomplete.json +21 -0
- package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-rest-native.json +16 -0
- package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-stream-cache-create.json +20 -0
- package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-stream-cache-read.json +20 -0
- package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-stream-incomplete.json +16 -0
- package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-stream-mixed.json +21 -0
- package/src/core/ai/inference-attempt/fixtures/pensar-anthropic-stream-non-cache.json +19 -0
- package/src/core/ai/inference-attempt/fixtures/reject-cache-read-exceeds-inclusive.json +11 -0
- package/src/core/ai/inference-attempt/fixtures/reject-components-exceed-inclusive.json +12 -0
- package/src/core/ai/inference-attempt/fixtures/reject-identity-mismatch.json +13 -0
- package/src/core/ai/inference-attempt/fixtures/reject-negative-token.json +9 -0
- package/src/core/ai/inference-attempt/fixtures/reject-non-integer-token.json +8 -0
- package/src/core/ai/inference-attempt/fixtures/sdk-normalized-cache-read.json +19 -0
- package/src/core/ai/inference-attempt/fixtures/sdk-normalized-incomplete.json +15 -0
- package/src/core/ai/inference-attempt/fixtures/sdk-normalized-mixed.json +20 -0
- package/src/core/ai/inference-attempt/fixtures/sdk-normalized-non-cache.json +16 -0
- package/src/core/ai/inference-attempt/identity.ts +25 -0
- package/src/core/ai/inference-attempt/index.ts +70 -0
- package/src/core/ai/inference-attempt/project.test.ts +118 -0
- package/src/core/ai/inference-attempt/project.ts +60 -0
- package/src/core/ai/inference-attempt/recording.ts +46 -0
- package/src/core/ai/inference-attempt/tokens.ts +165 -0
- package/src/core/ai/inference-attempt/validate.ts +20 -0
- package/src/core/ai/mantle.test.ts +50 -0
- package/src/core/ai/mantle.ts +45 -0
- package/src/core/ai/model-history.test.ts +53 -0
- package/src/core/ai/model-history.ts +38 -0
- package/src/core/ai/models/anthropic.ts +145 -0
- package/src/core/ai/models/bedrock.ts +600 -0
- package/src/core/ai/models/claude-capabilities.ts +22 -0
- package/src/core/ai/models/concentrate.ts +192 -0
- package/src/core/ai/models/google.ts +154 -0
- package/src/core/ai/models/inception.ts +10 -0
- package/src/core/ai/models/index.ts +250 -0
- package/src/core/ai/models/mantle.ts +14 -0
- package/src/core/ai/models/models.test.ts +290 -0
- package/src/core/ai/models/openai.ts +409 -0
- package/src/core/ai/models/openrouter.ts +336 -0
- package/src/core/ai/models/pensar.ts +66 -0
- package/src/core/ai/native-rollout-evidence/capture.test.ts +1836 -0
- package/src/core/ai/native-rollout-evidence/capture.ts +1572 -0
- package/src/core/ai/native-rollout-evidence/content.test.ts +55 -0
- package/src/core/ai/native-rollout-evidence/content.ts +129 -0
- package/src/core/ai/native-rollout-evidence/index.ts +68 -0
- package/src/core/ai/native-rollout-evidence/provider-metadata.test.ts +85 -0
- package/src/core/ai/native-rollout-evidence/provider-metadata.ts +129 -0
- package/src/core/ai/native-rollout-evidence/recording.test.ts +398 -0
- package/src/core/ai/native-rollout-evidence/schema.test.ts +210 -0
- package/src/core/ai/native-rollout-evidence/schema.ts +353 -0
- package/src/core/ai/native-rollout-evidence/validate.ts +141 -0
- package/src/core/ai/providers/anthropic-models.test.ts +289 -0
- package/src/core/ai/providers/anthropic.ts +73 -0
- package/src/core/ai/providers/concentrate.live.test.ts +71 -0
- package/src/core/ai/providers/concentrate.test.ts +160 -0
- package/src/core/ai/providers/concentrate.ts +145 -0
- package/src/core/ai/providers/custom.live.test.ts +42 -0
- package/src/core/ai/providers/custom.test.ts +474 -0
- package/src/core/ai/providers/custom.ts +70 -0
- package/src/core/ai/providers/gateway-models.test.ts +638 -0
- package/src/core/ai/providers/hoonify.live.test.ts +72 -0
- package/src/core/ai/providers/hoonify.test.ts +284 -0
- package/src/core/ai/providers/hoonify.ts +32 -0
- package/src/core/ai/providers/openai-models.test.ts +356 -0
- package/src/core/ai/providers/openai-pro-fetch.test.ts +64 -0
- package/src/core/ai/providers/openai-pro-fetch.ts +32 -0
- package/src/core/ai/providers/pensar.ts +645 -0
- package/src/core/ai/providers/pensarFormatters.test.ts +272 -0
- package/src/core/ai/providers/pensarFormatters.ts +533 -0
- package/src/core/ai/providers/pensarResponsesStream.test.ts +191 -0
- package/src/core/ai/providers/pensarResponsesStream.ts +294 -0
- package/src/core/ai/providers/pensarSSE.test.ts +83 -0
- package/src/core/ai/providers/pensarSSE.ts +128 -0
- package/src/core/ai/providers/pensarSigning.ts +2 -0
- package/src/core/ai/providers/pensarStreamUsage.test.ts +79 -0
- package/src/core/ai/providers/pensarStreamUsage.ts +62 -0
- package/src/core/ai/stepContext.test.ts +61 -0
- package/src/core/ai/streamIdleTimeout.test.ts +390 -0
- package/src/core/ai/streamTelemetry.ts +90 -0
- package/src/core/ai/usage-callback.test.ts +511 -0
- package/src/core/ai/utils.native-rollout-evidence.test.ts +135 -0
- package/src/core/ai/utils.pensar.test.ts +59 -0
- package/src/core/ai/utils.ts +796 -0
- package/src/core/api/apiClient.test.ts +58 -0
- package/src/core/api/apiClient.ts +73 -0
- package/src/core/api/apps.test.ts +78 -0
- package/src/core/api/apps.ts +388 -0
- package/src/core/api/attackSurface.test.ts +24 -0
- package/src/core/api/attackSurface.ts +89 -0
- package/src/core/api/authentication.ts +8 -0
- package/src/core/api/benchmark.ts +35 -0
- package/src/core/api/blackboxPentest.test.ts +89 -0
- package/src/core/api/blackboxPentest.ts +64 -0
- package/src/core/api/clientIdentity.test.ts +61 -0
- package/src/core/api/clientIdentity.ts +57 -0
- package/src/core/api/comments.test.ts +69 -0
- package/src/core/api/constants.ts +15 -0
- package/src/core/api/domains.test.ts +35 -0
- package/src/core/api/domains.ts +32 -0
- package/src/core/api/environment.ts +47 -0
- package/src/core/api/index.ts +219 -0
- package/src/core/api/issues.test.ts +82 -0
- package/src/core/api/issues.ts +439 -0
- package/src/core/api/knowledge.ts +70 -0
- package/src/core/api/localWorker.ts +29 -0
- package/src/core/api/offesecAgent.test.ts +289 -0
- package/src/core/api/offesecAgent.ts +77 -0
- package/src/core/api/patching.ts +44 -0
- package/src/core/api/recordedRun.control.test.ts +830 -0
- package/src/core/api/recordedRun.inference.test.ts +529 -0
- package/src/core/api/recordedRun.loop.test.ts +290 -0
- package/src/core/api/recordedRun.recovery.test.ts +651 -0
- package/src/core/api/recordedRun.test.ts +646 -0
- package/src/core/api/recordedRun.ts +542 -0
- package/src/core/api/targetedPentest.test.ts +177 -0
- package/src/core/api/targetedPentest.ts +83 -0
- package/src/core/api/threatModel.ts +5 -0
- package/src/core/api/trajectoryExport.test.ts +299 -0
- package/src/core/api/trajectoryExport.ts +362 -0
- package/src/core/api/vulnerabilityReproduction.test.ts +56 -0
- package/src/core/api/vulnerabilityReproduction.ts +54 -0
- package/src/core/assets/wordlists.test.ts +85 -0
- package/src/core/assets/wordlists.ts +111 -0
- package/src/core/atif/compatibility.test.ts +107 -0
- package/src/core/atif/convert.test.ts +815 -0
- package/src/core/atif/convert.ts +1658 -0
- package/src/core/atif/fixtures/portable-bundle-v1.json +813 -0
- package/src/core/atif/index.ts +34 -0
- package/src/core/atif/schema.ts +345 -0
- package/src/core/atif/serialize.test.ts +244 -0
- package/src/core/atif/serialize.ts +419 -0
- package/src/core/atif/test-fixtures.ts +184 -0
- package/src/core/atif/validate.test.ts +188 -0
- package/src/core/atif/validate.ts +289 -0
- package/src/core/auth/connection.ts +26 -0
- package/src/core/auth/device-flow.ts +203 -0
- package/src/core/auth/gateway.ts +66 -0
- package/src/core/auth/index.ts +29 -0
- package/src/core/auth/signing.ts +25 -0
- package/src/core/auth/token.ts +140 -0
- package/src/core/auth/types.ts +106 -0
- package/src/core/auth/workspaces.ts +192 -0
- package/src/core/benchmark/index.ts +11 -0
- package/src/core/benchmark/report.ts +257 -0
- package/src/core/benchmark/runner.ts +698 -0
- package/src/core/benchmark/types.ts +80 -0
- package/src/core/cli/logLevelArgs.test.ts +83 -0
- package/src/core/cli/logLevelArgs.ts +62 -0
- package/src/core/cli/model.ts +35 -0
- package/src/core/cli/pentestMode.test.ts +24 -0
- package/src/core/cli/pentestMode.ts +23 -0
- package/src/core/cli/tuiArgs.test.ts +58 -0
- package/src/core/cli/tuiArgs.ts +28 -0
- package/src/core/config/config.test.ts +183 -0
- package/src/core/config/config.ts +201 -0
- package/src/core/config/customProviders.test.ts +183 -0
- package/src/core/config/customProviders.ts +190 -0
- package/src/core/config/index.ts +7 -0
- package/src/core/credentials/index.ts +6 -0
- package/src/core/credentials/manager.ts +313 -0
- package/src/core/credentials/types.ts +112 -0
- package/src/core/doctor.ts +138 -0
- package/src/core/eventBus.test.ts +374 -0
- package/src/core/eventBus.ts +410 -0
- package/src/core/findings/attackSurfaceRegistry.ts +294 -0
- package/src/core/findings/registry.test.ts +1680 -0
- package/src/core/findings/registry.ts +800 -0
- package/src/core/history.ts +82 -0
- package/src/core/hoonify.test.ts +250 -0
- package/src/core/hoonify.ts +163 -0
- package/src/core/http/headers.contract.test.ts +1161 -0
- package/src/core/http/parse.ts +257 -0
- package/src/core/http/redirects.test.ts +207 -0
- package/src/core/http/redirects.ts +145 -0
- package/src/core/http/targetHeaders.ts +604 -0
- package/src/core/http/targetScope.ts +52 -0
- package/src/core/http/types.ts +39 -0
- package/src/core/id/id.test.ts +88 -0
- package/src/core/id/id.ts +138 -0
- package/src/core/installation/index.ts +222 -0
- package/src/core/installation/installation.test.ts +415 -0
- package/src/core/integrations/herdr.test.ts +335 -0
- package/src/core/integrations/herdr.ts +163 -0
- package/src/core/integrations/surface/index.test.ts +534 -0
- package/src/core/integrations/surface/index.ts +224 -0
- package/src/core/integrations/surface/types.ts +29 -0
- package/src/core/integrations/wandb/client.ts +167 -0
- package/src/core/integrations/wandb/upload.test.ts +415 -0
- package/src/core/integrations/wandb/upload.ts +76 -0
- package/src/core/logger/errorLog.test.ts +98 -0
- package/src/core/logger/index.ts +214 -0
- package/src/core/logger/structured.test.ts +212 -0
- package/src/core/logger/structured.ts +207 -0
- package/src/core/memory/backend.test.ts +186 -0
- package/src/core/memory/index.ts +279 -0
- package/src/core/memory/memory.test.ts +362 -0
- package/src/core/messages/types.ts +141 -0
- package/src/core/obfuscation/engine.test.ts +314 -0
- package/src/core/obfuscation/engine.ts +647 -0
- package/src/core/obfuscation/index.ts +27 -0
- package/src/core/observability/active-root-spans.ts +30 -0
- package/src/core/observability/active-spans.ts +80 -0
- package/src/core/observability/index.ts +91 -0
- package/src/core/observability/log-correlation.test.ts +276 -0
- package/src/core/observability/payload-capture.test.ts +59 -0
- package/src/core/observability/runtime.test.ts +743 -0
- package/src/core/observability/runtime.ts +371 -0
- package/src/core/observability/shutdown.test.ts +551 -0
- package/src/core/observability/telemetry.test.ts +895 -0
- package/src/core/observability/telemetry.ts +220 -0
- package/src/core/observability/testkit.test.ts +66 -0
- package/src/core/observability/testkit.ts +97 -0
- package/src/core/observability/trace-contract.test.ts +1031 -0
- package/src/core/observability.test.ts +135 -0
- package/src/core/operator/approvalGate.test.ts +300 -0
- package/src/core/operator/approvalGate.ts +287 -0
- package/src/core/operator/index.ts +30 -0
- package/src/core/operator/types.ts +355 -0
- package/src/core/plan/index.ts +44 -0
- package/src/core/prompt-injections/index.test.ts +248 -0
- package/src/core/prompt-injections/index.ts +350 -0
- package/src/core/providers/index.ts +5 -0
- package/src/core/providers/types.ts +87 -0
- package/src/core/providers/utils.test.ts +189 -0
- package/src/core/providers/utils.ts +183 -0
- package/src/core/providers/verify.test.ts +49 -0
- package/src/core/providers/verify.ts +198 -0
- package/src/core/report/builder.test.ts +229 -0
- package/src/core/report/builder.ts +58 -0
- package/src/core/report/index.ts +13 -0
- package/src/core/report/renderers/json.test.ts +76 -0
- package/src/core/report/renderers/json.ts +5 -0
- package/src/core/report/renderers/markdown.test.ts +366 -0
- package/src/core/report/renderers/markdown.ts +133 -0
- package/src/core/report/schemas.ts +48 -0
- package/src/core/runtime/agentExecution.test.ts +94 -0
- package/src/core/runtime/agentExecution.ts +27 -0
- package/src/core/runtime/launchLocalWorker.test.ts +707 -0
- package/src/core/runtime/launchLocalWorker.ts +273 -0
- package/src/core/runtime/localRunLock.ts +118 -0
- package/src/core/runtime/localRunWorker.test.ts +381 -0
- package/src/core/runtime/localRunWorker.ts +287 -0
- package/src/core/runtime/localWorker.acceptance.test.ts +548 -0
- package/src/core/runtime/localWorkerEndpoint.test.ts +145 -0
- package/src/core/runtime/localWorkerEndpoint.ts +82 -0
- package/src/core/runtime/localWorkerProtocol.ts +176 -0
- package/src/core/runtime/localWorkerTransport.test.ts +841 -0
- package/src/core/runtime/localWorkerTransport.ts +434 -0
- package/src/core/runtime/persistenceError.ts +14 -0
- package/src/core/runtime/recordedExecution.ts +43 -0
- package/src/core/runtime/recordedRunClient.integration.test.ts +106 -0
- package/src/core/runtime/recordedRunClient.test.ts +677 -0
- package/src/core/runtime/recordedRunClient.ts +303 -0
- package/src/core/runtime/runCheckpointStore.ts +23 -0
- package/src/core/runtime/runContext.test.ts +445 -0
- package/src/core/runtime/runContext.ts +184 -0
- package/src/core/runtime/runControl.test.ts +923 -0
- package/src/core/runtime/runControl.ts +419 -0
- package/src/core/runtime/runControlStore.ts +106 -0
- package/src/core/runtime/runDeadline.test.ts +44 -0
- package/src/core/runtime/runDeadline.ts +28 -0
- package/src/core/runtime/runEvidence.test.ts +384 -0
- package/src/core/runtime/runEvidence.ts +267 -0
- package/src/core/runtime/runInference.test.ts +408 -0
- package/src/core/runtime/runInference.ts +125 -0
- package/src/core/runtime/runModelStore.ts +55 -0
- package/src/core/runtime/runObservation.ts +24 -0
- package/src/core/runtime/runRecoveryContext.test.ts +602 -0
- package/src/core/runtime/runRecoveryContext.ts +195 -0
- package/src/core/runtime/runRecoveryPlan.test.ts +851 -0
- package/src/core/runtime/runRecoveryPlan.ts +540 -0
- package/src/core/runtime/runRecoveryStore.ts +78 -0
- package/src/core/runtime/runStore.ts +148 -0
- package/src/core/runtime/runStorePath.ts +13 -0
- package/src/core/runtime/runToolStore.ts +66 -0
- package/src/core/runtime/runTools.crash.test.ts +414 -0
- package/src/core/runtime/runTools.test.ts +689 -0
- package/src/core/runtime/runTools.ts +208 -0
- package/src/core/runtime/sqliteControlStore.test.ts +917 -0
- package/src/core/runtime/sqliteControlStore.ts +555 -0
- package/src/core/runtime/sqliteModelStore.test.ts +619 -0
- package/src/core/runtime/sqliteModelStore.ts +286 -0
- package/src/core/runtime/sqliteRecoveryStore.public.test.ts +374 -0
- package/src/core/runtime/sqliteRecoveryStore.test.ts +836 -0
- package/src/core/runtime/sqliteRecoveryStore.ts +435 -0
- package/src/core/runtime/sqliteRunContext.test.ts +1000 -0
- package/src/core/runtime/sqliteRunObservation.test.ts +432 -0
- package/src/core/runtime/sqliteRunStore.test.ts +698 -0
- package/src/core/runtime/sqliteRunStore.ts +593 -0
- package/src/core/runtime/sqliteToolStore.test.ts +1231 -0
- package/src/core/runtime/sqliteToolStore.ts +419 -0
- package/src/core/runtime/testfixtures/localWorkerChild.ts +254 -0
- package/src/core/runtime/testfixtures/localWorkerLauncher.ts +15 -0
- package/src/core/runtime/testfixtures/recoveryChild.ts +159 -0
- package/src/core/services/rateLimiter/index.test.ts +106 -0
- package/src/core/services/rateLimiter/index.ts +157 -0
- package/src/core/services/rateLimiter/types.ts +10 -0
- package/src/core/session/admitted-config.test.ts +50 -0
- package/src/core/session/deprecated-headers-shim.test.ts +111 -0
- package/src/core/session/execution-metrics.test.ts +297 -0
- package/src/core/session/execution-metrics.ts +176 -0
- package/src/core/session/index.ts +1085 -0
- package/src/core/session/loader.ts +212 -0
- package/src/core/session/persistence.test.ts +1210 -0
- package/src/core/session/persistence.ts +1076 -0
- package/src/core/session/rehydrate.test.ts +393 -0
- package/src/core/session/types.ts +139 -0
- package/src/core/session/usage.test.ts +99 -0
- package/src/core/session/usage.ts +84 -0
- package/src/core/skills/builtins/index.ts +22 -0
- package/src/core/skills/builtins/pentest.ts +135 -0
- package/src/core/skills/builtins/promptInjection.ts +44 -0
- package/src/core/skills/builtins/threatModel.ts +336 -0
- package/src/core/skills/index.ts +15 -0
- package/src/core/skills/parser.test.ts +120 -0
- package/src/core/skills/parser.ts +106 -0
- package/src/core/skills/registry.test.ts +202 -0
- package/src/core/skills/registry.ts +109 -0
- package/src/core/skills/scanner.test.ts +157 -0
- package/src/core/skills/scanner.ts +115 -0
- package/src/core/skills/types.ts +45 -0
- package/src/core/skills/utils.ts +16 -0
- package/src/core/storage/index.ts +150 -0
- package/src/core/tasks/index.ts +242 -0
- package/src/core/tools/backends/capture.ts +38 -0
- package/src/core/tools/backends/collectCommand.test.ts +33 -0
- package/src/core/tools/backends/collectCommand.ts +28 -0
- package/src/core/tools/backends/helpers.ts +140 -0
- package/src/core/tools/backends/httpTransport.ts +534 -0
- package/src/core/tools/backends/index.ts +61 -0
- package/src/core/tools/backends/local.test.ts +307 -0
- package/src/core/tools/backends/local.ts +820 -0
- package/src/core/tools/backends/policy.test.ts +137 -0
- package/src/core/tools/backends/policy.ts +129 -0
- package/src/core/tools/backends/resolve.test.ts +309 -0
- package/src/core/tools/backends/resolve.ts +152 -0
- package/src/core/tools/backends/types.ts +279 -0
- package/src/core/tools/backends/whitebox.test.ts +275 -0
- package/src/core/tools/backends/whitebox.ts +14 -0
- package/src/core/tools/backends/whiteboxJobs.test.ts +375 -0
- package/src/core/tools/backends/whiteboxJobs.ts +157 -0
- package/src/core/tools/backends/windowsProgram.ts +36 -0
- package/src/core/tools/daytonaSandbox.test.ts +491 -0
- package/src/core/tools/daytonaSandbox.ts +232 -0
- package/src/core/toolset/index.ts +583 -0
- package/src/core/util/lazyLogger.ts +32 -0
- package/src/core/utils/concurrency.test.ts +204 -0
- package/src/core/utils/concurrency.ts +75 -0
- package/src/core/utils/prompt.ts +28 -0
- package/src/core/whitebox/adapters.ts +277 -0
- package/src/core/whitebox/artifacts.ts +128 -0
- package/src/core/whitebox/boundedProcess.ts +265 -0
- package/src/core/whitebox/candidates.ts +268 -0
- package/src/core/whitebox/catalog.ts +82 -0
- package/src/core/whitebox/index.ts +53 -0
- package/src/core/whitebox/jobKernel.test.ts +91 -0
- package/src/core/whitebox/jobKernel.ts +347 -0
- package/src/core/whitebox/jobSupervisor.ts +264 -0
- package/src/core/whitebox/jobs.ts +21 -0
- package/src/core/whitebox/paths.ts +94 -0
- package/src/core/whitebox/profileTransport.ts +225 -0
- package/src/core/whitebox/profiles.ts +294 -0
- package/src/core/whitebox/repoProfile.test.ts +217 -0
- package/src/core/whitebox/repoProfile.ts +256 -0
- package/src/core/whitebox/types.ts +189 -0
- package/src/core/whitebox/whitebox.test.ts +789 -0
- package/src/core/workflows/fastStrike.seams.test.ts +217 -0
- package/src/core/workflows/fastStrike.test.ts +29 -0
- package/src/core/workflows/fastStrike.ts +239 -0
- package/src/core/workflows/pentest.test.ts +575 -0
- package/src/core/workflows/pentest.ts +1022 -0
- package/src/core/workflows/seams.test.ts +386 -0
- package/src/core/workflows/seams.ts +276 -0
- package/src/core/workflows/threatModel.ts +108 -0
- package/src/core/workflows/usage-forwarding.test.ts +203 -0
- package/src/core/workflows/whiteboxAttackSurface.test.ts +695 -0
- package/src/core/workflows/whiteboxAttackSurface.ts +1740 -0
- package/src/index.ts +49 -0
- package/src/lib/attack-path/types.ts +13 -0
- package/src/lib/cvss/calculator.test.ts +2090 -0
- package/src/lib/cvss/calculator.ts +643 -0
- package/src/lib/cvss/index.ts +65 -0
- package/src/lib/cvss/macrovector-scores.ts +402 -0
- package/src/lib/cvss/types.ts +241 -0
- package/src/lib/cwe/cwe-catalog.ts +444 -0
- package/src/lib/cwe/types.test.ts +142 -0
- package/src/lib/cwe/types.ts +37 -0
- package/src/lib/cwe/validate.test.ts +110 -0
- package/src/lib/cwe/validate.ts +53 -0
- package/src/lib/evidence/types.ts +9 -0
- package/src/tests/agentWorkingDirectory.test.ts +175 -0
- package/src/tests/attackSurface.test.ts +35 -0
- package/src/tests/auth.test.ts +196 -0
- package/src/tests/authCookieExport.test.ts +143 -0
- package/src/tests/authPentestHandoff.test.ts +231 -0
- package/src/tests/browserCookies.test.ts +128 -0
- package/src/tests/credentialManager.test.ts +657 -0
- package/src/tests/data/console.openapi.json +1037 -0
- package/src/tests/data/console.sitemap.xml +348 -0
- package/src/tests/delegateAuthMerge.test.ts +48 -0
- package/src/tui/auto-copy.ts +36 -0
- package/src/tui/clipboard.ts +65 -0
- package/src/tui/command-registry.test.ts +124 -0
- package/src/tui/command-registry.ts +645 -0
- package/src/tui/command-router.ts +81 -0
- package/src/tui/components/agent-display.tsx +480 -0
- package/src/tui/components/ascii-art.tsx +225 -0
- package/src/tui/components/chat/approval-inline.tsx +51 -0
- package/src/tui/components/chat/config-view.tsx +279 -0
- package/src/tui/components/chat/home-view.tsx +288 -0
- package/src/tui/components/chat/index.tsx +161 -0
- package/src/tui/components/chat/input-area.tsx +529 -0
- package/src/tui/components/chat/lib/play-core/num.ts +102 -0
- package/src/tui/components/chat/lib/wave-simulation.ts +124 -0
- package/src/tui/components/chat/loading-indicator.test.ts +33 -0
- package/src/tui/components/chat/loading-indicator.tsx +115 -0
- package/src/tui/components/chat/message-list.tsx +264 -0
- package/src/tui/components/chat/petri-animation.tsx +175 -0
- package/src/tui/components/chat/plan-review-message.tsx +49 -0
- package/src/tui/components/chat/questions-form.tsx +756 -0
- package/src/tui/components/commands/__tests__/recorded-runs-lifecycle.tsx +907 -0
- package/src/tui/components/commands/advanced-settings.tsx +94 -0
- package/src/tui/components/commands/api-key-input.tsx +122 -0
- package/src/tui/components/commands/auth-flow-logic.test.ts +58 -0
- package/src/tui/components/commands/auth-flow-logic.ts +30 -0
- package/src/tui/components/commands/auth-flow.tsx +702 -0
- package/src/tui/components/commands/credits-flow.tsx +210 -0
- package/src/tui/components/commands/help-dialog.tsx +238 -0
- package/src/tui/components/commands/models-display.tsx +111 -0
- package/src/tui/components/commands/operator-wizard.tsx +328 -0
- package/src/tui/components/commands/provider-manager.tsx +241 -0
- package/src/tui/components/commands/provider-selection.tsx +129 -0
- package/src/tui/components/commands/recorded-runs.test.ts +40 -0
- package/src/tui/components/commands/recorded-runs.tsx +793 -0
- package/src/tui/components/commands/sessions-display.tsx +365 -0
- package/src/tui/components/commands/shortcuts-dialog.tsx +52 -0
- package/src/tui/components/commands/skills-dialog.tsx +308 -0
- package/src/tui/components/commands/theme-picker.tsx +177 -0
- package/src/tui/components/commands/web-wizard.tsx +965 -0
- package/src/tui/components/dialog-layout.tsx +73 -0
- package/src/tui/components/error-boundary.tsx +82 -0
- package/src/tui/components/footer.tsx +101 -0
- package/src/tui/components/input.tsx +43 -0
- package/src/tui/components/loaders.tsx +784 -0
- package/src/tui/components/model-picker/ModelPicker.tsx +907 -0
- package/src/tui/components/model-picker/ModelPickerDialog.tsx +75 -0
- package/src/tui/components/model-picker/index.ts +2 -0
- package/src/tui/components/model-picker/model-navigation.test.ts +102 -0
- package/src/tui/components/model-picker/model-navigation.ts +54 -0
- package/src/tui/components/model-picker/model-search.test.ts +62 -0
- package/src/tui/components/model-picker/model-search.ts +42 -0
- package/src/tui/components/model-picker/model-visibility.test.ts +65 -0
- package/src/tui/components/model-picker/model-visibility.ts +59 -0
- package/src/tui/components/operator-dashboard/conversation.test.ts +431 -0
- package/src/tui/components/operator-dashboard/conversation.ts +179 -0
- package/src/tui/components/operator-dashboard/display-state.test.ts +369 -0
- package/src/tui/components/operator-dashboard/display-state.ts +163 -0
- package/src/tui/components/operator-dashboard/herdr-state.test.ts +162 -0
- package/src/tui/components/operator-dashboard/herdr-state.ts +81 -0
- package/src/tui/components/operator-dashboard/index.tsx +2044 -0
- package/src/tui/components/operator-dashboard/logic.test.ts +753 -0
- package/src/tui/components/operator-dashboard/logic.ts +465 -0
- package/src/tui/components/operator-dashboard/queue.test.ts +147 -0
- package/src/tui/components/operator-dashboard/queue.ts +55 -0
- package/src/tui/components/operator-dashboard/queued-messages.tsx +50 -0
- package/src/tui/components/operator-dashboard/run-events.test.ts +913 -0
- package/src/tui/components/operator-dashboard/run-events.ts +507 -0
- package/src/tui/components/operator-dashboard/run-tracing.test.ts +206 -0
- package/src/tui/components/operator-dashboard/run-tracing.ts +105 -0
- package/src/tui/components/operator-dashboard/subagent-detail-view.tsx +51 -0
- package/src/tui/components/operator-dashboard/subagent-dialog.tsx +142 -0
- package/src/tui/components/operator-dashboard/subagent-hub.test.ts +162 -0
- package/src/tui/components/operator-dashboard/subagent-hub.tsx +266 -0
- package/src/tui/components/operator-dashboard/subagent-state.test.ts +431 -0
- package/src/tui/components/operator-dashboard/subagent-state.ts +404 -0
- package/src/tui/components/operator-dashboard/subagent-status-bar.tsx +80 -0
- package/src/tui/components/operator-dashboard/tool-argument-buffer.test.ts +317 -0
- package/src/tui/components/operator-dashboard/tool-arguments.perf.test.ts +121 -0
- package/src/tui/components/operator-dashboard/welcome-logo.tsx +79 -0
- package/src/tui/components/operator-dashboard/workflow-data.test.ts +291 -0
- package/src/tui/components/operator-dashboard/workflow-data.ts +130 -0
- package/src/tui/components/report-viewer-dialog.tsx +98 -0
- package/src/tui/components/responsible-use-disclosure.tsx +55 -0
- package/src/tui/components/screenshot-modal.tsx +204 -0
- package/src/tui/components/shared/__tests__/prompt-input-lifecycle.tsx +64 -0
- package/src/tui/components/shared/__tests__/tool-renderer-lifecycle.tsx +69 -0
- package/src/tui/components/shared/action-label.test.ts +133 -0
- package/src/tui/components/shared/action-label.ts +18 -0
- package/src/tui/components/shared/ascii-spinner.tsx +40 -0
- package/src/tui/components/shared/dialog-controls.tsx +76 -0
- package/src/tui/components/shared/index.ts +37 -0
- package/src/tui/components/shared/markdown-viewer.tsx +188 -0
- package/src/tui/components/shared/markdown.ts +239 -0
- package/src/tui/components/shared/message-renderer.tsx +178 -0
- package/src/tui/components/shared/message-utils.ts +175 -0
- package/src/tui/components/shared/pentest-workflow-display.tsx +330 -0
- package/src/tui/components/shared/prompt-input-lifecycle.test.ts +22 -0
- package/src/tui/components/shared/prompt-input-logic.test.ts +782 -0
- package/src/tui/components/shared/prompt-input-logic.ts +423 -0
- package/src/tui/components/shared/prompt-input.tsx +782 -0
- package/src/tui/components/shared/result-registry.test.ts +90 -0
- package/src/tui/components/shared/result-registry.ts +900 -0
- package/src/tui/components/shared/syntax-highlight.ts +242 -0
- package/src/tui/components/shared/tool-registry.test.ts +59 -0
- package/src/tui/components/shared/tool-registry.ts +279 -0
- package/src/tui/components/shared/tool-renderer.test.ts +13 -0
- package/src/tui/components/shared/tool-renderer.tsx +223 -0
- package/src/tui/components/shared/type-guards.ts +64 -0
- package/src/tui/components/shared/use-paste-extmarks.ts +109 -0
- package/src/tui/components/switch.tsx +81 -0
- package/src/tui/components/terminal-focus-handler.tsx +34 -0
- package/src/tui/components/toast.tsx +82 -0
- package/src/tui/components/usage-labels.test.ts +191 -0
- package/src/tui/components/usage-labels.ts +78 -0
- package/src/tui/console-theme.ts +59 -0
- package/src/tui/context/agent.tsx +251 -0
- package/src/tui/context/command.tsx +291 -0
- package/src/tui/context/config.tsx +59 -0
- package/src/tui/context/dialog.tsx +206 -0
- package/src/tui/context/dimensions.tsx +31 -0
- package/src/tui/context/focus-ownership.test.ts +26 -0
- package/src/tui/context/focus-ownership.ts +12 -0
- package/src/tui/context/focus.tsx +86 -0
- package/src/tui/context/herdr.tsx +28 -0
- package/src/tui/context/input.tsx +45 -0
- package/src/tui/context/keybinding.tsx +92 -0
- package/src/tui/context/obfuscation.tsx +114 -0
- package/src/tui/context/route.tsx +127 -0
- package/src/tui/context/session-usage.test.ts +295 -0
- package/src/tui/context/session-usage.ts +204 -0
- package/src/tui/context/session.tsx +62 -0
- package/src/tui/context/toast.tsx +66 -0
- package/src/tui/generated-ascii-art.json +1 -0
- package/src/tui/generated-ascii-art.json.d.ts +2 -0
- package/src/tui/herdr-startup.test.ts +157 -0
- package/src/tui/hooks/use-sessions-list.ts +165 -0
- package/src/tui/index.tsx +838 -0
- package/src/tui/keybindings/index.ts +126 -0
- package/src/tui/keybindings/registry.ts +146 -0
- package/src/tui/keybindings-registry.ts +43 -0
- package/src/tui/obfuscation/patch.ts +139 -0
- package/src/tui/terminal-focus.ts +218 -0
- package/src/tui/theme/colors.ts +43 -0
- package/src/tui/theme/context.tsx +137 -0
- package/src/tui/theme/detect-mode.ts +56 -0
- package/src/tui/theme/index.ts +75 -0
- package/src/tui/theme/registry.ts +29 -0
- package/src/tui/theme/themes/apex.ts +190 -0
- package/src/tui/theme/themes/ayu.ts +189 -0
- package/src/tui/theme/themes/catppuccin.ts +189 -0
- package/src/tui/theme/themes/dracula.ts +190 -0
- package/src/tui/theme/themes/everforest.ts +189 -0
- package/src/tui/theme/themes/flexoki.ts +190 -0
- package/src/tui/theme/themes/github.ts +189 -0
- package/src/tui/theme/themes/gruvbox.ts +189 -0
- package/src/tui/theme/themes/index.ts +54 -0
- package/src/tui/theme/themes/kanagawa.ts +189 -0
- package/src/tui/theme/themes/material.ts +190 -0
- package/src/tui/theme/themes/monokai.ts +190 -0
- package/src/tui/theme/themes/nightfox.ts +189 -0
- package/src/tui/theme/themes/nord.ts +190 -0
- package/src/tui/theme/themes/onedark.ts +190 -0
- package/src/tui/theme/themes/rose-pine.ts +189 -0
- package/src/tui/theme/themes/solarized.ts +189 -0
- package/src/tui/theme/themes/tokyonight.ts +189 -0
- package/src/tui/theme/themes/vesper.ts +191 -0
- package/src/tui/theme/types.ts +143 -0
- package/src/tui/utils/command-flags.test.ts +269 -0
- package/src/tui/utils/command-flags.ts +377 -0
- package/src/tui/utils/kitty-graphics.ts +76 -0
- package/src/tui/utils/open-file.ts +37 -0
- package/src/tui/utils/open-report.ts +34 -0
- package/src/tui/utils/open-url.ts +39 -0
- package/src/tui/utils/paste.ts +8 -0
- package/src/tui/utils/scroll.ts +81 -0
- package/src/util/errors.ts +58 -0
- package/src/util/lock.ts +100 -0
- package/src/util/name.ts +148 -0
- package/src/util/url.test.ts +162 -0
- package/src/util/url.ts +107 -0
- package/build/agent-13rrhp8d.js +0 -20
- package/build/agent-zbj2331s.js +0 -30
- package/build/authentication-f4j3w5vs.js +0 -20
- package/build/blackboxAgent-v2vx9ws4.js +0 -20
- package/build/blackboxPentest-w5nwzpvk.js +0 -70
- package/build/cli-5wemv37a.js +0 -194
- package/build/cli-7s062hr6.js +0 -22
- package/build/cli-9qbxkhye.js +0 -55
- package/build/cli-avmhkjtv.js +0 -22961
- package/build/cli-g3czc3jz.js +0 -204
- package/build/cli-ww7hbazg.js +0 -88
- package/build/index-18vyy6mj.js +0 -19
- package/build/index-5wh7gq37.js +0 -12858
- package/build/index-e23zc8g4.js +0 -34
- package/build/index-hnwhswqj.js +0 -213
- package/build/index-wkmnr4nm.js +0 -39
- package/build/index-ywmfnd30.js +0 -12
- package/build/offesecAgent-c97pqzcn.js +0 -27
- package/build/pentest-4p5jt7gj.js +0 -30
- package/build/pentests-v98eg8n3.js +0 -92
- package/build/threatModel-81d4rcbk.js +0 -29
- /package/build/{cli-c8131c4q.js → cli-0wsh8j2t.js} +0 -0
- /package/build/{cli-9pbk8m32.js → cli-ag6jtndp.js} +0 -0
- /package/build/{cli-e08r86zk.js → cli-b3dvxjgk.js} +0 -0
- /package/build/{cli-fw5r7pfj.js → cli-q126f6ef.js} +0 -0
- /package/build/{cli-9fsre5pt.js → cli-qba2x69h.js} +0 -0
- /package/build/{cli-7q3ae3ft.js → cli-z82n3qk1.js} +0 -0
|
@@ -0,0 +1,1680 @@
|
|
|
1
|
+
import type { LanguageModelMiddleware } from "ai";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import type { Finding } from "../agents/offSecAgent/types";
|
|
4
|
+
import { generateObjectResponse } from "../ai";
|
|
5
|
+
import {
|
|
6
|
+
classifyFromContent,
|
|
7
|
+
extractTitleStem,
|
|
8
|
+
extractVulnClass,
|
|
9
|
+
FindingsRegistry,
|
|
10
|
+
generateFingerprint,
|
|
11
|
+
normalizeEndpoint,
|
|
12
|
+
} from "./registry";
|
|
13
|
+
|
|
14
|
+
vi.mock("fs", () => ({
|
|
15
|
+
existsSync: vi.fn(),
|
|
16
|
+
readdirSync: vi.fn(),
|
|
17
|
+
readFileSync: vi.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
vi.mock("../ai", () => ({
|
|
21
|
+
generateObjectResponse: vi.fn(),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Test fixture factory
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
function makeFinding(overrides: Partial<Finding> = {}): Finding {
|
|
29
|
+
return {
|
|
30
|
+
title: "Test Finding",
|
|
31
|
+
severity: "MEDIUM",
|
|
32
|
+
description: "A test vulnerability.",
|
|
33
|
+
impact: "Test impact.",
|
|
34
|
+
evidence: "Test evidence.",
|
|
35
|
+
endpoint: "https://target.com/api/test",
|
|
36
|
+
pocPath: "pocs/poc_test.sh",
|
|
37
|
+
remediation: "Fix the thing.",
|
|
38
|
+
credentialIds: [],
|
|
39
|
+
...overrides,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Pre-built fixtures modeled after real findings from ~/.pensar/sessions/
|
|
44
|
+
const sqlInjectionProducts = makeFinding({
|
|
45
|
+
title: "SQL Injection in /api/products Search Parameter",
|
|
46
|
+
severity: "CRITICAL",
|
|
47
|
+
endpoint: "https://target.com/api/products",
|
|
48
|
+
description:
|
|
49
|
+
"The /api/products endpoint's search query parameter is vulnerable to SQL injection.",
|
|
50
|
+
remediation: "Use parameterized queries.",
|
|
51
|
+
references: "CWE-89, OWASP A03:2021",
|
|
52
|
+
cwes: [
|
|
53
|
+
{
|
|
54
|
+
id: "CWE-89",
|
|
55
|
+
reasoning:
|
|
56
|
+
"Classic SQL injection via unsanitized user input in the search query parameter.",
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const sqlInjectionProductsId = makeFinding({
|
|
62
|
+
title: "SQL Injection in /api/products Endpoint (GET id parameter)",
|
|
63
|
+
severity: "CRITICAL",
|
|
64
|
+
endpoint: "https://target.com/api/products",
|
|
65
|
+
description:
|
|
66
|
+
"The /api/products endpoint is vulnerable to SQL injection through the id GET parameter.",
|
|
67
|
+
remediation: "Implement parameterized queries.",
|
|
68
|
+
references: "CWE-89, OWASP A03:2021",
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const sqlInjectionOrders = makeFinding({
|
|
72
|
+
title: "SQL Injection in /api/orders Search Parameter",
|
|
73
|
+
severity: "CRITICAL",
|
|
74
|
+
endpoint: "https://target.com/api/orders",
|
|
75
|
+
description:
|
|
76
|
+
"The /api/orders endpoint search param is vulnerable to SQL injection.",
|
|
77
|
+
remediation: "Use parameterized queries.",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const missingCspRoot = makeFinding({
|
|
81
|
+
title: "Missing Content Security Policy (CSP) Header",
|
|
82
|
+
severity: "HIGH",
|
|
83
|
+
endpoint: "https://target.com/",
|
|
84
|
+
description: "The application does not implement a CSP header.",
|
|
85
|
+
remediation: "Implement a Content Security Policy header.",
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const missingCspApi = makeFinding({
|
|
89
|
+
title: "Missing Content Security Policy (CSP) Header",
|
|
90
|
+
severity: "HIGH",
|
|
91
|
+
endpoint: "https://target.com/api/users",
|
|
92
|
+
description: "The API endpoint does not implement a CSP header.",
|
|
93
|
+
remediation: "Implement a Content Security Policy header.",
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const corsWildcard = makeFinding({
|
|
97
|
+
title: "Misconfigured CORS Policy - Allows All Origins and Methods",
|
|
98
|
+
severity: "HIGH",
|
|
99
|
+
endpoint: "https://target.com/",
|
|
100
|
+
description: "The API is configured with overly permissive CORS headers.",
|
|
101
|
+
remediation: "Fix CORS configuration to whitelist specific origins.",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const corsOnApi = makeFinding({
|
|
105
|
+
title: "Misconfigured CORS Policy - Allows All Origins and Methods",
|
|
106
|
+
severity: "HIGH",
|
|
107
|
+
endpoint: "https://target.com/api/orders",
|
|
108
|
+
description: "The orders API allows all origins and methods.",
|
|
109
|
+
remediation: "Fix CORS configuration.",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const reflectedXss = makeFinding({
|
|
113
|
+
title: "Reflected XSS via Search Parameter",
|
|
114
|
+
severity: "HIGH",
|
|
115
|
+
endpoint: "https://target.com/search",
|
|
116
|
+
description: "Reflected cross-site scripting in the search parameter.",
|
|
117
|
+
remediation: "Sanitize user input and encode output.",
|
|
118
|
+
cwes: [
|
|
119
|
+
{
|
|
120
|
+
id: "CWE-79",
|
|
121
|
+
reasoning:
|
|
122
|
+
"Reflected XSS via unsanitized search parameter injected into the response.",
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const storedXss = makeFinding({
|
|
128
|
+
title: "Stored XSS in Profile Name Field",
|
|
129
|
+
severity: "HIGH",
|
|
130
|
+
endpoint: "https://target.com/api/profile",
|
|
131
|
+
description: "Stored cross-site scripting in the profile name field.",
|
|
132
|
+
remediation: "Sanitize stored user input and encode on render.",
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const sentryExposure = makeFinding({
|
|
136
|
+
title:
|
|
137
|
+
"Information Disclosure: Sentry Public Key Exposed in Documentation Meta Tags",
|
|
138
|
+
severity: "LOW",
|
|
139
|
+
endpoint: "https://docs.target.com/overview",
|
|
140
|
+
description: "Sentry error tracking public key is exposed in meta tags.",
|
|
141
|
+
remediation: "Consider using CSP to restrict Sentry event submission.",
|
|
142
|
+
references: "CWE-200",
|
|
143
|
+
cwes: [
|
|
144
|
+
{
|
|
145
|
+
id: "CWE-200",
|
|
146
|
+
reasoning:
|
|
147
|
+
"Sentry public key exposed in HTML meta tags constitutes information disclosure.",
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// extractVulnClass
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
describe("extractVulnClass", () => {
|
|
157
|
+
it("classifies SQL injection titles", () => {
|
|
158
|
+
expect(extractVulnClass("SQL Injection in /api/products")).toBe(
|
|
159
|
+
"sql-injection",
|
|
160
|
+
);
|
|
161
|
+
expect(extractVulnClass("Blind SQL Injection via search")).toBe(
|
|
162
|
+
"sql-injection",
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("classifies XSS titles (reflected and stored share the class)", () => {
|
|
167
|
+
expect(extractVulnClass("Reflected XSS via Search")).toBe("xss");
|
|
168
|
+
expect(extractVulnClass("Stored XSS in Profile")).toBe("xss");
|
|
169
|
+
expect(extractVulnClass("Cross-Site Scripting in input field")).toBe("xss");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("classifies missing CSP", () => {
|
|
173
|
+
expect(
|
|
174
|
+
extractVulnClass("Missing Content Security Policy (CSP) Header"),
|
|
175
|
+
).toBe("missing-csp");
|
|
176
|
+
expect(extractVulnClass("Missing CSP on all endpoints")).toBe(
|
|
177
|
+
"missing-csp",
|
|
178
|
+
);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("classifies CORS issues", () => {
|
|
182
|
+
expect(extractVulnClass("Misconfigured CORS Policy")).toBe("cors");
|
|
183
|
+
expect(
|
|
184
|
+
extractVulnClass("Cross-Origin Resource Sharing Misconfiguration"),
|
|
185
|
+
).toBe("cors");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("classifies IDOR", () => {
|
|
189
|
+
expect(extractVulnClass("IDOR on User Profile Endpoint")).toBe("idor");
|
|
190
|
+
expect(extractVulnClass("Insecure Direct Object Reference in orders")).toBe(
|
|
191
|
+
"idor",
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("classifies missing-authentication patterns", () => {
|
|
196
|
+
expect(extractVulnClass("Missing Authentication on /api/admin")).toBe(
|
|
197
|
+
"missing-authentication",
|
|
198
|
+
);
|
|
199
|
+
expect(extractVulnClass("No Authentication Required for API")).toBe(
|
|
200
|
+
"missing-authentication",
|
|
201
|
+
);
|
|
202
|
+
expect(extractVulnClass("Unauthenticated Access to Admin Dashboard")).toBe(
|
|
203
|
+
"missing-authentication",
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("does not misclassify titles where 'no' is a substring before 'auth'", () => {
|
|
208
|
+
expect(extractVulnClass("Known Auth Vulnerability")).toBe(
|
|
209
|
+
"known-auth-vulnerability",
|
|
210
|
+
);
|
|
211
|
+
expect(extractVulnClass("Minor Auth Misconfiguration")).toBe(
|
|
212
|
+
"minor-auth-misconfiguration",
|
|
213
|
+
);
|
|
214
|
+
expect(extractVulnClass("Canonical Auth Token Leak")).toBe(
|
|
215
|
+
"canonical-auth-token-leak",
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("classifies SSRF", () => {
|
|
220
|
+
expect(extractVulnClass("Server-Side Request Forgery")).toBe("ssrf");
|
|
221
|
+
expect(extractVulnClass("SSRF via image upload URL")).toBe("ssrf");
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("classifies RCE / command injection", () => {
|
|
225
|
+
expect(extractVulnClass("Remote Code Execution via file upload")).toBe(
|
|
226
|
+
"rce",
|
|
227
|
+
);
|
|
228
|
+
expect(extractVulnClass("Command Injection in ping utility")).toBe(
|
|
229
|
+
"command-injection",
|
|
230
|
+
);
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("classifies path traversal / LFI", () => {
|
|
234
|
+
expect(extractVulnClass("Path Traversal in /api/files")).toBe(
|
|
235
|
+
"path-traversal",
|
|
236
|
+
);
|
|
237
|
+
expect(extractVulnClass("Local File Inclusion via template")).toBe(
|
|
238
|
+
"path-traversal",
|
|
239
|
+
);
|
|
240
|
+
expect(extractVulnClass("Directory Traversal on static assets")).toBe(
|
|
241
|
+
"path-traversal",
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("classifies information disclosure", () => {
|
|
246
|
+
expect(extractVulnClass("Information Disclosure: Sentry Public Key")).toBe(
|
|
247
|
+
"information-disclosure",
|
|
248
|
+
);
|
|
249
|
+
expect(extractVulnClass("Sensitive Data Exposure in error page")).toBe(
|
|
250
|
+
"information-disclosure",
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("classifies CSRF", () => {
|
|
255
|
+
expect(
|
|
256
|
+
extractVulnClass("Cross-Site Request Forgery on /api/settings"),
|
|
257
|
+
).toBe("csrf");
|
|
258
|
+
expect(extractVulnClass("CSRF on form submission")).toBe("csrf");
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("classifies auth bypass and privilege escalation", () => {
|
|
262
|
+
expect(extractVulnClass("Authentication Bypass via token reuse")).toBe(
|
|
263
|
+
"auth-bypass",
|
|
264
|
+
);
|
|
265
|
+
expect(extractVulnClass("Privilege Escalation to admin role")).toBe(
|
|
266
|
+
"privilege-escalation",
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it("classifies open redirect", () => {
|
|
271
|
+
expect(extractVulnClass("Open Redirect on login callback")).toBe(
|
|
272
|
+
"open-redirect",
|
|
273
|
+
);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("classifies missing security headers", () => {
|
|
277
|
+
expect(extractVulnClass("Missing X-Frame-Options Header")).toBe(
|
|
278
|
+
"missing-security-header",
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it("falls back to normalized title for unknown vuln types", () => {
|
|
283
|
+
const cls = extractVulnClass("Unusual Custom Vulnerability Pattern");
|
|
284
|
+
expect(cls).toBe("unusual-custom-vulnerability-pattern");
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it("is case insensitive", () => {
|
|
288
|
+
expect(extractVulnClass("SQL INJECTION IN /API")).toBe("sql-injection");
|
|
289
|
+
expect(extractVulnClass("sql injection in /api")).toBe("sql-injection");
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// ---------------------------------------------------------------------------
|
|
294
|
+
// classifyFromContent
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
|
|
297
|
+
describe("classifyFromContent", () => {
|
|
298
|
+
it("reclassifies idor to missing-authentication when description indicates no auth", () => {
|
|
299
|
+
const finding = makeFinding({
|
|
300
|
+
title: "IDOR on User Profile Endpoint",
|
|
301
|
+
description:
|
|
302
|
+
"The endpoint allows unauthenticated access to user profiles by iterating IDs.",
|
|
303
|
+
evidence: "curl https://target.com/api/users/1 returns user data.",
|
|
304
|
+
});
|
|
305
|
+
expect(classifyFromContent(finding)).toBe("missing-authentication");
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it("reclassifies idor to missing-authentication when evidence contains PR:N", () => {
|
|
309
|
+
const finding = makeFinding({
|
|
310
|
+
title: "IDOR on User Profile Endpoint",
|
|
311
|
+
description: "User profiles can be accessed by changing the ID.",
|
|
312
|
+
evidence: "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N — no auth required.",
|
|
313
|
+
});
|
|
314
|
+
expect(classifyFromContent(finding)).toBe("missing-authentication");
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it("reclassifies idor to missing-authentication when evidence contains PR=N", () => {
|
|
318
|
+
const finding = makeFinding({
|
|
319
|
+
title: "Insecure Direct Object Reference in orders",
|
|
320
|
+
description: "Orders are accessible without authentication.",
|
|
321
|
+
evidence: "Scored with PR=N since no credentials are needed.",
|
|
322
|
+
});
|
|
323
|
+
expect(classifyFromContent(finding)).toBe("missing-authentication");
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it("keeps idor when description does not indicate missing auth", () => {
|
|
327
|
+
const finding = makeFinding({
|
|
328
|
+
title: "IDOR on User Profile Endpoint",
|
|
329
|
+
description:
|
|
330
|
+
"Authenticated users can access other users' profiles by changing the ID parameter.",
|
|
331
|
+
evidence:
|
|
332
|
+
"Logged in as user A, accessed user B profile by changing id=2.",
|
|
333
|
+
});
|
|
334
|
+
expect(classifyFromContent(finding)).toBe("idor");
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
it("does not alter non-idor classifications", () => {
|
|
338
|
+
const finding = makeFinding({
|
|
339
|
+
title: "SQL Injection in /api/products",
|
|
340
|
+
description: "The endpoint has no authentication and is SQL injectable.",
|
|
341
|
+
});
|
|
342
|
+
expect(classifyFromContent(finding)).toBe("sql-injection");
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// ---------------------------------------------------------------------------
|
|
347
|
+
// extractTitleStem
|
|
348
|
+
// ---------------------------------------------------------------------------
|
|
349
|
+
|
|
350
|
+
describe("extractTitleStem", () => {
|
|
351
|
+
it("strips endpoint paths from titles", () => {
|
|
352
|
+
expect(
|
|
353
|
+
extractTitleStem("SQL Injection in /api/products Search Parameter"),
|
|
354
|
+
).toBe("sql injection in search parameter");
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it("produces the same stem for the same vuln on different endpoints", () => {
|
|
358
|
+
const stem1 = extractTitleStem(
|
|
359
|
+
"SQL Injection in /api/products Search Parameter",
|
|
360
|
+
);
|
|
361
|
+
const stem2 = extractTitleStem(
|
|
362
|
+
"SQL Injection in /api/orders Search Parameter",
|
|
363
|
+
);
|
|
364
|
+
expect(stem1).toBe(stem2);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it("preserves titles without endpoint references", () => {
|
|
368
|
+
expect(
|
|
369
|
+
extractTitleStem("Missing Content Security Policy (CSP) Header"),
|
|
370
|
+
).toBe("missing content security policy csp header");
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("produces different stems for different XSS variants", () => {
|
|
374
|
+
const reflected = extractTitleStem("Reflected XSS via Search Parameter");
|
|
375
|
+
const stored = extractTitleStem("Stored XSS in Profile Name Field");
|
|
376
|
+
expect(reflected).not.toBe(stored);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it("strips full URLs from titles", () => {
|
|
380
|
+
expect(extractTitleStem("SSRF at https://target.com/api/fetch Found")).toBe(
|
|
381
|
+
"ssrf at found",
|
|
382
|
+
);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("normalises whitespace and casing", () => {
|
|
386
|
+
expect(extractTitleStem(" XSS via INPUT ")).toBe("xss via input");
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
// ---------------------------------------------------------------------------
|
|
391
|
+
// normalizeEndpoint
|
|
392
|
+
// ---------------------------------------------------------------------------
|
|
393
|
+
|
|
394
|
+
describe("normalizeEndpoint", () => {
|
|
395
|
+
it("returns lowercase endpoints unchanged", () => {
|
|
396
|
+
expect(normalizeEndpoint("https://target.com/api/products")).toBe(
|
|
397
|
+
"https://target.com/api/products",
|
|
398
|
+
);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
it("strips trailing slashes", () => {
|
|
402
|
+
expect(normalizeEndpoint("https://target.com/api/products/")).toBe(
|
|
403
|
+
"https://target.com/api/products",
|
|
404
|
+
);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it("strips query parameters", () => {
|
|
408
|
+
expect(
|
|
409
|
+
normalizeEndpoint("https://target.com/api/products?search=test"),
|
|
410
|
+
).toBe("https://target.com/api/products");
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it("lowercases the URL", () => {
|
|
414
|
+
expect(normalizeEndpoint("HTTPS://TARGET.COM/API/Products")).toBe(
|
|
415
|
+
"https://target.com/api/products",
|
|
416
|
+
);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it("leaves bare origin intact", () => {
|
|
420
|
+
expect(normalizeEndpoint("https://target.com")).toBe("https://target.com");
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it("strips fragment identifiers", () => {
|
|
424
|
+
expect(normalizeEndpoint("https://target.com/page#section")).toBe(
|
|
425
|
+
"https://target.com/page",
|
|
426
|
+
);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it("preserves port numbers", () => {
|
|
430
|
+
expect(normalizeEndpoint("https://target.com:8080/api")).toBe(
|
|
431
|
+
"https://target.com:8080/api",
|
|
432
|
+
);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it("handles combined query + fragment + trailing slash", () => {
|
|
436
|
+
expect(normalizeEndpoint("https://target.com/api/?q=1#top")).toBe(
|
|
437
|
+
"https://target.com/api",
|
|
438
|
+
);
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// ---------------------------------------------------------------------------
|
|
443
|
+
// generateFingerprint
|
|
444
|
+
// ---------------------------------------------------------------------------
|
|
445
|
+
|
|
446
|
+
describe("generateFingerprint", () => {
|
|
447
|
+
it("exactKey uses endpoint||vulnClass, appWideKey uses stem only", () => {
|
|
448
|
+
const fp = generateFingerprint(sqlInjectionProducts);
|
|
449
|
+
expect(fp.exactKey).toContain("||");
|
|
450
|
+
expect(fp.appWideKey).not.toContain("||");
|
|
451
|
+
expect(fp.appWideKey).toBe(extractTitleStem(sqlInjectionProducts.title));
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it("same endpoint + same vuln class => same exactKey", () => {
|
|
455
|
+
const fp1 = generateFingerprint(sqlInjectionProducts);
|
|
456
|
+
const fp2 = generateFingerprint(sqlInjectionProductsId);
|
|
457
|
+
expect(fp1.exactKey).toBe(fp2.exactKey);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it("different endpoints => different exactKeys", () => {
|
|
461
|
+
const fp1 = generateFingerprint(sqlInjectionProducts);
|
|
462
|
+
const fp2 = generateFingerprint(sqlInjectionOrders);
|
|
463
|
+
expect(fp1.exactKey).not.toBe(fp2.exactKey);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it("same title stem => same appWideKey (application-wide dedup)", () => {
|
|
467
|
+
const fp1 = generateFingerprint(missingCspRoot);
|
|
468
|
+
const fp2 = generateFingerprint(missingCspApi);
|
|
469
|
+
expect(fp1.appWideKey).toBe(fp2.appWideKey);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
it("different title stems => different appWideKeys", () => {
|
|
473
|
+
const fp1 = generateFingerprint(reflectedXss);
|
|
474
|
+
const fp2 = generateFingerprint(storedXss);
|
|
475
|
+
expect(fp1.appWideKey).not.toBe(fp2.appWideKey);
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
it("unknown vuln types still produce matching appWideKeys across endpoints", () => {
|
|
479
|
+
const finding1 = makeFinding({
|
|
480
|
+
title: "Unusual Custom Vulnerability on /api/users",
|
|
481
|
+
endpoint: "https://target.com/api/users",
|
|
482
|
+
});
|
|
483
|
+
const finding2 = makeFinding({
|
|
484
|
+
title: "Unusual Custom Vulnerability on /api/orders",
|
|
485
|
+
endpoint: "https://target.com/api/orders",
|
|
486
|
+
});
|
|
487
|
+
const fp1 = generateFingerprint(finding1);
|
|
488
|
+
const fp2 = generateFingerprint(finding2);
|
|
489
|
+
expect(fp1.appWideKey).toBe(fp2.appWideKey);
|
|
490
|
+
// exactKeys should differ (different endpoints)
|
|
491
|
+
expect(fp1.exactKey).not.toBe(fp2.exactKey);
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
// ---------------------------------------------------------------------------
|
|
496
|
+
// FindingsRegistry — exact endpoint + vuln class dedup
|
|
497
|
+
// ---------------------------------------------------------------------------
|
|
498
|
+
|
|
499
|
+
describe("FindingsRegistry", () => {
|
|
500
|
+
describe("exact endpoint + vuln class dedup", () => {
|
|
501
|
+
it("detects duplicate when same endpoint + same vuln class", async () => {
|
|
502
|
+
const registry = new FindingsRegistry();
|
|
503
|
+
await registry.register(sqlInjectionProducts);
|
|
504
|
+
|
|
505
|
+
const check = registry.isDuplicate(sqlInjectionProductsId);
|
|
506
|
+
expect(check.duplicate).toBe(true);
|
|
507
|
+
expect(check.matchType).toBe("exact");
|
|
508
|
+
expect(check.matchedFinding?.title).toBe(sqlInjectionProducts.title);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it("does not flag different vuln classes on the same endpoint", async () => {
|
|
512
|
+
const registry = new FindingsRegistry();
|
|
513
|
+
await registry.register(sqlInjectionProducts);
|
|
514
|
+
|
|
515
|
+
const xssOnProducts = makeFinding({
|
|
516
|
+
title: "Reflected XSS on /api/products",
|
|
517
|
+
endpoint: "https://target.com/api/products",
|
|
518
|
+
});
|
|
519
|
+
const check = registry.isDuplicate(xssOnProducts);
|
|
520
|
+
expect(check.duplicate).toBe(false);
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it("does not flag different endpoints with different vuln classes", async () => {
|
|
524
|
+
const registry = new FindingsRegistry();
|
|
525
|
+
await registry.register(reflectedXss);
|
|
526
|
+
|
|
527
|
+
const check = registry.isDuplicate(sentryExposure);
|
|
528
|
+
expect(check.duplicate).toBe(false);
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// -------------------------------------------------------------------------
|
|
533
|
+
// Application-wide dedup
|
|
534
|
+
// -------------------------------------------------------------------------
|
|
535
|
+
|
|
536
|
+
describe("application-wide dedup", () => {
|
|
537
|
+
it("detects missing CSP on / then on /api/users as duplicate", async () => {
|
|
538
|
+
const registry = new FindingsRegistry();
|
|
539
|
+
await registry.register(missingCspRoot);
|
|
540
|
+
|
|
541
|
+
const check = registry.isDuplicate(missingCspApi);
|
|
542
|
+
expect(check.duplicate).toBe(true);
|
|
543
|
+
expect(check.matchType).toBe("application-wide");
|
|
544
|
+
expect(check.matchedFinding?.title).toBe(missingCspRoot.title);
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
it("detects CORS on / then similar CORS on /api/orders as duplicate", async () => {
|
|
548
|
+
const registry = new FindingsRegistry();
|
|
549
|
+
await registry.register(corsWildcard);
|
|
550
|
+
|
|
551
|
+
const check = registry.isDuplicate(corsOnApi);
|
|
552
|
+
expect(check.duplicate).toBe(true);
|
|
553
|
+
expect(check.matchType).toBe("application-wide");
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
it("detects SQL injection on /api/products then /api/orders as duplicate (same stem)", async () => {
|
|
557
|
+
const registry = new FindingsRegistry();
|
|
558
|
+
await registry.register(sqlInjectionProducts);
|
|
559
|
+
|
|
560
|
+
const check = registry.isDuplicate(sqlInjectionOrders);
|
|
561
|
+
expect(check.duplicate).toBe(true);
|
|
562
|
+
expect(check.matchType).toBe("application-wide");
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
it("does NOT flag reflected XSS and stored XSS as duplicates (different stems)", async () => {
|
|
566
|
+
const registry = new FindingsRegistry();
|
|
567
|
+
await registry.register(reflectedXss);
|
|
568
|
+
|
|
569
|
+
const check = registry.isDuplicate(storedXss);
|
|
570
|
+
expect(check.duplicate).toBe(false);
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
it("does NOT flag different vuln classes as duplicates across endpoints", async () => {
|
|
574
|
+
const registry = new FindingsRegistry();
|
|
575
|
+
await registry.register(missingCspRoot);
|
|
576
|
+
|
|
577
|
+
const check = registry.isDuplicate(corsWildcard);
|
|
578
|
+
expect(check.duplicate).toBe(false);
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
it("deduplicates unknown vuln types across endpoints via stem", async () => {
|
|
582
|
+
const registry = new FindingsRegistry();
|
|
583
|
+
await registry.register(
|
|
584
|
+
makeFinding({
|
|
585
|
+
title: "Unusual Custom Vulnerability on /api/users",
|
|
586
|
+
endpoint: "https://target.com/api/users",
|
|
587
|
+
}),
|
|
588
|
+
);
|
|
589
|
+
|
|
590
|
+
const check = registry.isDuplicate(
|
|
591
|
+
makeFinding({
|
|
592
|
+
title: "Unusual Custom Vulnerability on /api/orders",
|
|
593
|
+
endpoint: "https://target.com/api/orders",
|
|
594
|
+
}),
|
|
595
|
+
);
|
|
596
|
+
expect(check.duplicate).toBe(true);
|
|
597
|
+
expect(check.matchType).toBe("application-wide");
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
// -------------------------------------------------------------------------
|
|
602
|
+
// Pre-loaded findings
|
|
603
|
+
// -------------------------------------------------------------------------
|
|
604
|
+
|
|
605
|
+
describe("pre-loaded findings", () => {
|
|
606
|
+
it("indexes all pre-loaded findings", () => {
|
|
607
|
+
const registry = FindingsRegistry.fromFindings([
|
|
608
|
+
sqlInjectionProducts,
|
|
609
|
+
missingCspRoot,
|
|
610
|
+
corsWildcard,
|
|
611
|
+
]);
|
|
612
|
+
|
|
613
|
+
expect(registry.size).toBe(3);
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
it("rejects duplicates of pre-loaded findings", () => {
|
|
617
|
+
const registry = FindingsRegistry.fromFindings([
|
|
618
|
+
sqlInjectionProducts,
|
|
619
|
+
missingCspRoot,
|
|
620
|
+
]);
|
|
621
|
+
|
|
622
|
+
const check = registry.isDuplicate(sqlInjectionProductsId);
|
|
623
|
+
expect(check.duplicate).toBe(true);
|
|
624
|
+
expect(check.matchType).toBe("exact");
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
it("accepts genuinely new findings after pre-load", () => {
|
|
628
|
+
const registry = FindingsRegistry.fromFindings([sqlInjectionProducts]);
|
|
629
|
+
|
|
630
|
+
const check = registry.isDuplicate(sentryExposure);
|
|
631
|
+
expect(check.duplicate).toBe(false);
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
it("returns both pre-loaded and newly registered findings from getFindings()", async () => {
|
|
635
|
+
const registry = FindingsRegistry.fromFindings([
|
|
636
|
+
sqlInjectionProducts,
|
|
637
|
+
missingCspRoot,
|
|
638
|
+
]);
|
|
639
|
+
|
|
640
|
+
await registry.register(sentryExposure);
|
|
641
|
+
const all = registry.getFindings();
|
|
642
|
+
|
|
643
|
+
expect(all.length).toBe(3);
|
|
644
|
+
expect(all.map((f) => f.title)).toContain(sentryExposure.title);
|
|
645
|
+
expect(all.map((f) => f.title)).toContain(sqlInjectionProducts.title);
|
|
646
|
+
});
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
// -------------------------------------------------------------------------
|
|
650
|
+
// Concurrent registration
|
|
651
|
+
// -------------------------------------------------------------------------
|
|
652
|
+
|
|
653
|
+
describe("concurrent registration", () => {
|
|
654
|
+
it("serialises concurrent register calls without losing data", async () => {
|
|
655
|
+
const registry = new FindingsRegistry();
|
|
656
|
+
|
|
657
|
+
// 5 agents submit the same finding, 5 submit unique findings
|
|
658
|
+
const duplicateFinding = missingCspRoot;
|
|
659
|
+
const uniqueFindings = [
|
|
660
|
+
sqlInjectionProducts,
|
|
661
|
+
corsWildcard,
|
|
662
|
+
reflectedXss,
|
|
663
|
+
storedXss,
|
|
664
|
+
sentryExposure,
|
|
665
|
+
];
|
|
666
|
+
|
|
667
|
+
const allSubmissions = [
|
|
668
|
+
...Array(5).fill(duplicateFinding),
|
|
669
|
+
...uniqueFindings,
|
|
670
|
+
] as Finding[];
|
|
671
|
+
|
|
672
|
+
const results = await Promise.all(
|
|
673
|
+
allSubmissions.map((f) => registry.register(f)),
|
|
674
|
+
);
|
|
675
|
+
|
|
676
|
+
// Only 1 of the 5 identical submissions should succeed
|
|
677
|
+
const duplicateResults = results.slice(0, 5);
|
|
678
|
+
const accepted = duplicateResults.filter((r) => !r.duplicate);
|
|
679
|
+
const rejected = duplicateResults.filter((r) => r.duplicate);
|
|
680
|
+
expect(accepted.length).toBe(1);
|
|
681
|
+
expect(rejected.length).toBe(4);
|
|
682
|
+
|
|
683
|
+
// All 5 unique findings should succeed
|
|
684
|
+
const uniqueResults = results.slice(5);
|
|
685
|
+
for (const r of uniqueResults) {
|
|
686
|
+
expect(r.duplicate).toBe(false);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Total: 1 (from dupes) + 5 (unique) = 6
|
|
690
|
+
expect(registry.size).toBe(6);
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
it("handles large concurrent swarm without race conditions", async () => {
|
|
694
|
+
const registry = new FindingsRegistry();
|
|
695
|
+
|
|
696
|
+
const findings = Array.from({ length: 50 }, (_, i) =>
|
|
697
|
+
makeFinding({
|
|
698
|
+
title: `Unique Finding ${i}`,
|
|
699
|
+
endpoint: `https://target.com/api/endpoint-${i}`,
|
|
700
|
+
}),
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
await Promise.all(findings.map((f) => registry.register(f)));
|
|
704
|
+
|
|
705
|
+
expect(registry.size).toBe(50);
|
|
706
|
+
});
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
// -------------------------------------------------------------------------
|
|
710
|
+
// Edge cases
|
|
711
|
+
// -------------------------------------------------------------------------
|
|
712
|
+
|
|
713
|
+
describe("edge cases", () => {
|
|
714
|
+
it("empty registry returns not-duplicate for any finding", () => {
|
|
715
|
+
const registry = new FindingsRegistry();
|
|
716
|
+
const check = registry.isDuplicate(sqlInjectionProducts);
|
|
717
|
+
expect(check.duplicate).toBe(false);
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
it("handles finding with empty title without throwing", async () => {
|
|
721
|
+
const registry = new FindingsRegistry();
|
|
722
|
+
const empty = makeFinding({ title: "" });
|
|
723
|
+
const result = await registry.register(empty);
|
|
724
|
+
expect(result.duplicate).toBe(false);
|
|
725
|
+
expect(registry.size).toBe(1);
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
it("handles finding with very long title", async () => {
|
|
729
|
+
const registry = new FindingsRegistry();
|
|
730
|
+
const long = makeFinding({ title: "A".repeat(1000) });
|
|
731
|
+
const result = await registry.register(long);
|
|
732
|
+
expect(result.duplicate).toBe(false);
|
|
733
|
+
expect(registry.size).toBe(1);
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
it("vuln class extraction is case insensitive", () => {
|
|
737
|
+
expect(extractVulnClass("SQL INJECTION")).toBe(
|
|
738
|
+
extractVulnClass("sql injection"),
|
|
739
|
+
);
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
it("endpoint normalisation handles fragments", () => {
|
|
743
|
+
const finding1 = makeFinding({
|
|
744
|
+
title: "XSS on page",
|
|
745
|
+
endpoint: "https://target.com/page#section",
|
|
746
|
+
});
|
|
747
|
+
const finding2 = makeFinding({
|
|
748
|
+
title: "XSS on page",
|
|
749
|
+
endpoint: "https://target.com/page",
|
|
750
|
+
});
|
|
751
|
+
const fp1 = generateFingerprint(finding1);
|
|
752
|
+
const fp2 = generateFingerprint(finding2);
|
|
753
|
+
expect(fp1.exactKey).toBe(fp2.exactKey);
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
it("endpoint normalisation preserves port numbers", () => {
|
|
757
|
+
const finding = makeFinding({
|
|
758
|
+
title: "XSS on page",
|
|
759
|
+
endpoint: "https://target.com:8080/api",
|
|
760
|
+
});
|
|
761
|
+
const fp = generateFingerprint(finding);
|
|
762
|
+
expect(fp.exactKey).toContain("8080");
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
it("handles unicode and special characters in title", async () => {
|
|
766
|
+
const registry = new FindingsRegistry();
|
|
767
|
+
const unicode = makeFinding({
|
|
768
|
+
title: "XSS via «script» tag — héllo wörld",
|
|
769
|
+
endpoint: "https://target.com/page",
|
|
770
|
+
});
|
|
771
|
+
const result = await registry.register(unicode);
|
|
772
|
+
expect(result.duplicate).toBe(false);
|
|
773
|
+
expect(registry.size).toBe(1);
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
it("getFindings returns a snapshot (not a live reference)", async () => {
|
|
777
|
+
const registry = new FindingsRegistry();
|
|
778
|
+
await registry.register(sqlInjectionProducts);
|
|
779
|
+
|
|
780
|
+
const snapshot = registry.getFindings();
|
|
781
|
+
await registry.register(corsWildcard);
|
|
782
|
+
|
|
783
|
+
expect(snapshot.length).toBe(1);
|
|
784
|
+
expect(registry.getFindings().length).toBe(2);
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
// -------------------------------------------------------------------------
|
|
789
|
+
// fromDirectory
|
|
790
|
+
// -------------------------------------------------------------------------
|
|
791
|
+
|
|
792
|
+
describe("fromDirectory", () => {
|
|
793
|
+
let mockedFs: typeof import("fs");
|
|
794
|
+
|
|
795
|
+
beforeEach(async () => {
|
|
796
|
+
mockedFs = await import("node:fs");
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
afterEach(() => {
|
|
800
|
+
vi.mocked(mockedFs.existsSync).mockReset();
|
|
801
|
+
vi.mocked(mockedFs.readdirSync).mockReset();
|
|
802
|
+
vi.mocked(mockedFs.readFileSync).mockReset();
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
it("loads and indexes JSON findings from a directory", () => {
|
|
806
|
+
vi.mocked(mockedFs.existsSync).mockReturnValue(true);
|
|
807
|
+
vi.mocked(mockedFs.readdirSync).mockReturnValue([
|
|
808
|
+
"finding-1.json",
|
|
809
|
+
"finding-2.json",
|
|
810
|
+
"finding-3.json",
|
|
811
|
+
"finding-1.md",
|
|
812
|
+
] as unknown as ReturnType<typeof mockedFs.readdirSync>);
|
|
813
|
+
vi.mocked(mockedFs.readFileSync).mockImplementation((filePath) => {
|
|
814
|
+
const p = String(filePath);
|
|
815
|
+
if (p.includes("finding-1.json"))
|
|
816
|
+
return JSON.stringify(sqlInjectionProducts);
|
|
817
|
+
if (p.includes("finding-2.json")) return JSON.stringify(missingCspRoot);
|
|
818
|
+
if (p.includes("finding-3.json")) return JSON.stringify(corsWildcard);
|
|
819
|
+
return "";
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
const registry = FindingsRegistry.fromDirectory("/fake/findings");
|
|
823
|
+
expect(registry.size).toBe(3);
|
|
824
|
+
|
|
825
|
+
expect(registry.isDuplicate(sqlInjectionProductsId).duplicate).toBe(true);
|
|
826
|
+
expect(registry.isDuplicate(missingCspApi).duplicate).toBe(true);
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
it("returns empty registry for missing directory", () => {
|
|
830
|
+
vi.mocked(mockedFs.existsSync).mockReturnValue(false);
|
|
831
|
+
|
|
832
|
+
const registry = FindingsRegistry.fromDirectory("/no/such/dir");
|
|
833
|
+
expect(registry.size).toBe(0);
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
it("skips malformed JSON files gracefully", () => {
|
|
837
|
+
vi.mocked(mockedFs.existsSync).mockReturnValue(true);
|
|
838
|
+
vi.mocked(mockedFs.readdirSync).mockReturnValue([
|
|
839
|
+
"good.json",
|
|
840
|
+
"bad.json",
|
|
841
|
+
] as unknown as ReturnType<typeof mockedFs.readdirSync>);
|
|
842
|
+
vi.mocked(mockedFs.readFileSync).mockImplementation((filePath) => {
|
|
843
|
+
const p = String(filePath);
|
|
844
|
+
if (p.includes("good.json")) return JSON.stringify(sentryExposure);
|
|
845
|
+
if (p.includes("bad.json")) return "NOT VALID JSON {{{";
|
|
846
|
+
return "";
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
const registry = FindingsRegistry.fromDirectory("/fake/findings");
|
|
850
|
+
expect(registry.size).toBe(1);
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
it("skips JSON files that lack required fields", () => {
|
|
854
|
+
vi.mocked(mockedFs.existsSync).mockReturnValue(true);
|
|
855
|
+
vi.mocked(mockedFs.readdirSync).mockReturnValue([
|
|
856
|
+
"good.json",
|
|
857
|
+
"incomplete.json",
|
|
858
|
+
] as unknown as ReturnType<typeof mockedFs.readdirSync>);
|
|
859
|
+
vi.mocked(mockedFs.readFileSync).mockImplementation((filePath) => {
|
|
860
|
+
const p = String(filePath);
|
|
861
|
+
if (p.includes("good.json")) return JSON.stringify(sentryExposure);
|
|
862
|
+
if (p.includes("incomplete.json"))
|
|
863
|
+
return JSON.stringify({ severity: "HIGH" });
|
|
864
|
+
return "";
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
const registry = FindingsRegistry.fromDirectory("/fake/findings");
|
|
868
|
+
expect(registry.size).toBe(1);
|
|
869
|
+
});
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
// -------------------------------------------------------------------------
|
|
873
|
+
// register() returns dedup result
|
|
874
|
+
// -------------------------------------------------------------------------
|
|
875
|
+
|
|
876
|
+
describe("register() return value", () => {
|
|
877
|
+
it("returns { duplicate: false } for new findings", async () => {
|
|
878
|
+
const registry = new FindingsRegistry();
|
|
879
|
+
const result = await registry.register(sqlInjectionProducts);
|
|
880
|
+
expect(result.duplicate).toBe(false);
|
|
881
|
+
expect(result.matchedFinding).toBeUndefined();
|
|
882
|
+
expect(result.matchType).toBeUndefined();
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
it("returns duplicate info with matchedFinding for exact matches", async () => {
|
|
886
|
+
const registry = new FindingsRegistry();
|
|
887
|
+
await registry.register(sqlInjectionProducts);
|
|
888
|
+
|
|
889
|
+
const result = await registry.register(sqlInjectionProductsId);
|
|
890
|
+
expect(result.duplicate).toBe(true);
|
|
891
|
+
expect(result.matchType).toBe("exact");
|
|
892
|
+
expect(result.matchedFinding?.title).toBe(sqlInjectionProducts.title);
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
it("returns duplicate info with matchedFinding for app-wide matches", async () => {
|
|
896
|
+
const registry = new FindingsRegistry();
|
|
897
|
+
await registry.register(missingCspRoot);
|
|
898
|
+
|
|
899
|
+
const result = await registry.register(missingCspApi);
|
|
900
|
+
expect(result.duplicate).toBe(true);
|
|
901
|
+
expect(result.matchType).toBe("application-wide");
|
|
902
|
+
expect(result.matchedFinding?.title).toBe(missingCspRoot.title);
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
it("does not add duplicate findings to the registry", async () => {
|
|
906
|
+
const registry = new FindingsRegistry();
|
|
907
|
+
await registry.register(sqlInjectionProducts);
|
|
908
|
+
await registry.register(sqlInjectionProductsId); // duplicate
|
|
909
|
+
await registry.register(sqlInjectionOrders); // app-wide dup
|
|
910
|
+
|
|
911
|
+
expect(registry.size).toBe(1);
|
|
912
|
+
});
|
|
913
|
+
});
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
// ---------------------------------------------------------------------------
|
|
917
|
+
// Tier 3: Semantic dedup via LLM
|
|
918
|
+
// ---------------------------------------------------------------------------
|
|
919
|
+
|
|
920
|
+
describe("FindingsRegistry Tier 3 (semantic dedup)", () => {
|
|
921
|
+
const mockedGenerate = vi.mocked(generateObjectResponse);
|
|
922
|
+
|
|
923
|
+
afterEach(() => {
|
|
924
|
+
mockedGenerate.mockReset();
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
it("skips Tier 3 when no model is configured", async () => {
|
|
928
|
+
const registry = new FindingsRegistry(); // no model
|
|
929
|
+
await registry.register(sqlInjectionProducts);
|
|
930
|
+
|
|
931
|
+
// This finding has a completely different title/stem, so Tier 1+2 won't catch it.
|
|
932
|
+
// Without a model, Tier 3 is skipped and it should be accepted.
|
|
933
|
+
const noCspRephrased = makeFinding({
|
|
934
|
+
title: "No CSP Header Configured",
|
|
935
|
+
endpoint: "https://target.com/",
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
const result = await registry.register(noCspRephrased);
|
|
939
|
+
expect(result.duplicate).toBe(false);
|
|
940
|
+
expect(mockedGenerate).not.toHaveBeenCalled();
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
it("skips Tier 3 when registry is empty", async () => {
|
|
944
|
+
const registry = new FindingsRegistry({ model: "test-model" });
|
|
945
|
+
|
|
946
|
+
const result = await registry.register(sqlInjectionProducts);
|
|
947
|
+
expect(result.duplicate).toBe(false);
|
|
948
|
+
expect(mockedGenerate).not.toHaveBeenCalled();
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
it("calls LLM when Tier 1+2 miss and model is configured", async () => {
|
|
952
|
+
mockedGenerate.mockResolvedValue({
|
|
953
|
+
isDuplicate: true,
|
|
954
|
+
matchedIndex: 1,
|
|
955
|
+
reasoning: "Both describe missing CSP header",
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
const registry = new FindingsRegistry({ model: "test-model" });
|
|
959
|
+
await registry.register(missingCspRoot);
|
|
960
|
+
|
|
961
|
+
// Different phrasing — Tier 1+2 won't match but LLM will
|
|
962
|
+
const rephrased = makeFinding({
|
|
963
|
+
title: "No CSP Header Configured",
|
|
964
|
+
endpoint: "https://target.com/admin",
|
|
965
|
+
});
|
|
966
|
+
|
|
967
|
+
const result = await registry.register(rephrased);
|
|
968
|
+
expect(result.duplicate).toBe(true);
|
|
969
|
+
expect(result.matchType).toBe("semantic");
|
|
970
|
+
expect(result.matchedFinding?.title).toBe(missingCspRoot.title);
|
|
971
|
+
expect(mockedGenerate).toHaveBeenCalledOnce();
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
it("accepts finding when LLM says not a duplicate", async () => {
|
|
975
|
+
mockedGenerate.mockResolvedValue({
|
|
976
|
+
isDuplicate: false,
|
|
977
|
+
reasoning: "These are genuinely different vulnerabilities",
|
|
978
|
+
});
|
|
979
|
+
|
|
980
|
+
const registry = new FindingsRegistry({ model: "test-model" });
|
|
981
|
+
await registry.register(sqlInjectionProducts);
|
|
982
|
+
|
|
983
|
+
const genuinelyNew = makeFinding({
|
|
984
|
+
title: "Broken Access Control on Admin Panel",
|
|
985
|
+
endpoint: "https://target.com/admin",
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
const result = await registry.register(genuinelyNew);
|
|
989
|
+
expect(result.duplicate).toBe(false);
|
|
990
|
+
expect(registry.size).toBe(2);
|
|
991
|
+
expect(mockedGenerate).toHaveBeenCalledOnce();
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
it("degrades gracefully when LLM errors out", async () => {
|
|
995
|
+
mockedGenerate.mockRejectedValue(new Error("API unavailable"));
|
|
996
|
+
|
|
997
|
+
const registry = new FindingsRegistry({ model: "test-model" });
|
|
998
|
+
await registry.register(missingCspRoot);
|
|
999
|
+
|
|
1000
|
+
const rephrased = makeFinding({
|
|
1001
|
+
title: "No CSP Header Configured",
|
|
1002
|
+
endpoint: "https://target.com/admin",
|
|
1003
|
+
});
|
|
1004
|
+
|
|
1005
|
+
// LLM fails — should fall through and accept the finding
|
|
1006
|
+
const result = await registry.register(rephrased);
|
|
1007
|
+
expect(result.duplicate).toBe(false);
|
|
1008
|
+
expect(registry.size).toBe(2);
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
it("does not call LLM when Tier 1 already matches", async () => {
|
|
1012
|
+
const registry = new FindingsRegistry({ model: "test-model" });
|
|
1013
|
+
await registry.register(sqlInjectionProducts);
|
|
1014
|
+
|
|
1015
|
+
// Exact same endpoint + same vuln class — Tier 1 catches it
|
|
1016
|
+
const result = await registry.register(sqlInjectionProductsId);
|
|
1017
|
+
expect(result.duplicate).toBe(true);
|
|
1018
|
+
expect(result.matchType).toBe("exact");
|
|
1019
|
+
expect(mockedGenerate).not.toHaveBeenCalled();
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
it("does not call LLM when Tier 2 already matches", async () => {
|
|
1023
|
+
const registry = new FindingsRegistry({ model: "test-model" });
|
|
1024
|
+
await registry.register(missingCspRoot);
|
|
1025
|
+
|
|
1026
|
+
// Same stem — Tier 2 catches it
|
|
1027
|
+
const result = await registry.register(missingCspApi);
|
|
1028
|
+
expect(result.duplicate).toBe(true);
|
|
1029
|
+
expect(result.matchType).toBe("application-wide");
|
|
1030
|
+
expect(mockedGenerate).not.toHaveBeenCalled();
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
it("re-checks Tier 1+2 after LLM to guard against races", async () => {
|
|
1034
|
+
// Simulate a slow LLM call during which another finding gets registered
|
|
1035
|
+
let resolveSlowCall: (value: unknown) => void;
|
|
1036
|
+
const slowCall = new Promise((resolve) => {
|
|
1037
|
+
resolveSlowCall = resolve;
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
mockedGenerate.mockImplementation(async () => {
|
|
1041
|
+
await slowCall;
|
|
1042
|
+
return { isDuplicate: false, reasoning: "Different" };
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1045
|
+
const registry = new FindingsRegistry({ model: "test-model" });
|
|
1046
|
+
await registry.register(sqlInjectionProducts);
|
|
1047
|
+
|
|
1048
|
+
// Two agents submit findings that pass Tier 1+2 — both reach the LLM
|
|
1049
|
+
const finding1 = makeFinding({
|
|
1050
|
+
title: "Broken Access Control on Admin Panel",
|
|
1051
|
+
endpoint: "https://target.com/admin",
|
|
1052
|
+
});
|
|
1053
|
+
const finding2 = makeFinding({
|
|
1054
|
+
title: "Broken Access Control on Admin Panel",
|
|
1055
|
+
endpoint: "https://target.com/admin",
|
|
1056
|
+
});
|
|
1057
|
+
|
|
1058
|
+
const promise1 = registry.register(finding1);
|
|
1059
|
+
const promise2 = registry.register(finding2);
|
|
1060
|
+
|
|
1061
|
+
// Release the LLM calls
|
|
1062
|
+
resolveSlowCall!(undefined);
|
|
1063
|
+
|
|
1064
|
+
const [result1, result2] = await Promise.all([promise1, promise2]);
|
|
1065
|
+
|
|
1066
|
+
// One should succeed, the other should be caught by the Tier 1+2 re-check
|
|
1067
|
+
const accepted = [result1, result2].filter((r) => !r.duplicate);
|
|
1068
|
+
const rejected = [result1, result2].filter((r) => r.duplicate);
|
|
1069
|
+
expect(accepted.length).toBe(1);
|
|
1070
|
+
expect(rejected.length).toBe(1);
|
|
1071
|
+
expect(registry.size).toBe(2); // sqlInjection + 1 new
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
it("passes correct prompt structure to the LLM", async () => {
|
|
1075
|
+
mockedGenerate.mockResolvedValue({
|
|
1076
|
+
isDuplicate: false,
|
|
1077
|
+
reasoning: "Different",
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
const registry = new FindingsRegistry({ model: "test-model" });
|
|
1081
|
+
await registry.register(sqlInjectionProducts);
|
|
1082
|
+
|
|
1083
|
+
const newFinding = makeFinding({
|
|
1084
|
+
title: "Completely Novel Vulnerability",
|
|
1085
|
+
endpoint: "https://target.com/novel",
|
|
1086
|
+
});
|
|
1087
|
+
await registry.register(newFinding);
|
|
1088
|
+
|
|
1089
|
+
expect(mockedGenerate).toHaveBeenCalledWith(
|
|
1090
|
+
expect.objectContaining({
|
|
1091
|
+
model: "test-model",
|
|
1092
|
+
prompt: expect.stringContaining("Completely Novel Vulnerability"),
|
|
1093
|
+
system: expect.stringContaining("deduplication specialist"),
|
|
1094
|
+
}),
|
|
1095
|
+
);
|
|
1096
|
+
|
|
1097
|
+
// The prompt should contain the existing finding
|
|
1098
|
+
const callArgs = mockedGenerate.mock.calls[0]?.[0] as { prompt: string };
|
|
1099
|
+
expect(callArgs.prompt).toContain(sqlInjectionProducts.title);
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
it("does not index finding rejected as semantic duplicate", async () => {
|
|
1103
|
+
mockedGenerate.mockResolvedValue({
|
|
1104
|
+
isDuplicate: true,
|
|
1105
|
+
matchedIndex: 1,
|
|
1106
|
+
reasoning: "Same vuln, different words",
|
|
1107
|
+
});
|
|
1108
|
+
|
|
1109
|
+
const registry = new FindingsRegistry({ model: "test-model" });
|
|
1110
|
+
await registry.register(missingCspRoot);
|
|
1111
|
+
|
|
1112
|
+
const rephrased = makeFinding({
|
|
1113
|
+
title: "No CSP Header Configured",
|
|
1114
|
+
endpoint: "https://target.com/admin",
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
const result = await registry.register(rephrased);
|
|
1118
|
+
expect(result.duplicate).toBe(true);
|
|
1119
|
+
expect(registry.size).toBe(1);
|
|
1120
|
+
});
|
|
1121
|
+
|
|
1122
|
+
it("handles LLM returning an out-of-bounds matchedIndex", async () => {
|
|
1123
|
+
mockedGenerate.mockResolvedValue({
|
|
1124
|
+
isDuplicate: true,
|
|
1125
|
+
matchedIndex: 999, // doesn't exist
|
|
1126
|
+
reasoning: "Some match",
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1129
|
+
const registry = new FindingsRegistry({ model: "test-model" });
|
|
1130
|
+
await registry.register(sqlInjectionProducts);
|
|
1131
|
+
|
|
1132
|
+
const newFinding = makeFinding({
|
|
1133
|
+
title: "Completely Novel Vulnerability",
|
|
1134
|
+
endpoint: "https://target.com/novel",
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
// Invalid index should be treated as not-duplicate (safe fallback)
|
|
1138
|
+
const result = await registry.register(newFinding);
|
|
1139
|
+
expect(result.duplicate).toBe(false);
|
|
1140
|
+
expect(registry.size).toBe(2);
|
|
1141
|
+
});
|
|
1142
|
+
});
|
|
1143
|
+
|
|
1144
|
+
// ---------------------------------------------------------------------------
|
|
1145
|
+
// unregister (rollback)
|
|
1146
|
+
// ---------------------------------------------------------------------------
|
|
1147
|
+
|
|
1148
|
+
describe("FindingsRegistry.unregister", () => {
|
|
1149
|
+
it("removes a finding from all indexes so it can be re-registered", async () => {
|
|
1150
|
+
const registry = new FindingsRegistry();
|
|
1151
|
+
const finding = makeFinding({
|
|
1152
|
+
title: "SQL Injection in /api/products Search Parameter",
|
|
1153
|
+
endpoint: "https://target.com/api/products",
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
await registry.register(finding);
|
|
1157
|
+
expect(registry.size).toBe(1);
|
|
1158
|
+
|
|
1159
|
+
await registry.unregister(finding);
|
|
1160
|
+
expect(registry.size).toBe(0);
|
|
1161
|
+
expect(registry.getFindings()).toEqual([]);
|
|
1162
|
+
|
|
1163
|
+
// The same finding (or one with matching fingerprints) can now be registered again
|
|
1164
|
+
const result = await registry.register(finding);
|
|
1165
|
+
expect(result.duplicate).toBe(false);
|
|
1166
|
+
expect(registry.size).toBe(1);
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
it("clears both exact and app-wide keys", async () => {
|
|
1170
|
+
const registry = new FindingsRegistry();
|
|
1171
|
+
await registry.register(missingCspRoot);
|
|
1172
|
+
await registry.unregister(missingCspRoot);
|
|
1173
|
+
|
|
1174
|
+
// Exact duplicate should no longer match
|
|
1175
|
+
expect(registry.isDuplicate(missingCspRoot).duplicate).toBe(false);
|
|
1176
|
+
|
|
1177
|
+
// App-wide duplicate (same stem, different endpoint) should no longer match
|
|
1178
|
+
expect(registry.isDuplicate(missingCspApi).duplicate).toBe(false);
|
|
1179
|
+
});
|
|
1180
|
+
|
|
1181
|
+
it("is safe to call on a finding that was never registered", async () => {
|
|
1182
|
+
const registry = new FindingsRegistry();
|
|
1183
|
+
await registry.unregister(sqlInjectionProducts);
|
|
1184
|
+
expect(registry.size).toBe(0);
|
|
1185
|
+
});
|
|
1186
|
+
|
|
1187
|
+
it("does not remove other findings that share no keys", async () => {
|
|
1188
|
+
const registry = new FindingsRegistry();
|
|
1189
|
+
await registry.register(sqlInjectionProducts);
|
|
1190
|
+
await registry.register(reflectedXss);
|
|
1191
|
+
expect(registry.size).toBe(2);
|
|
1192
|
+
|
|
1193
|
+
await registry.unregister(sqlInjectionProducts);
|
|
1194
|
+
expect(registry.size).toBe(1);
|
|
1195
|
+
expect(registry.getFindings()[0]?.title).toBe(reflectedXss.title);
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
it("only removes the exact reference — not another finding with the same fingerprint", async () => {
|
|
1199
|
+
const registry = new FindingsRegistry();
|
|
1200
|
+
const finding1 = makeFinding({
|
|
1201
|
+
title: "Unique Finding A",
|
|
1202
|
+
endpoint: "https://target.com/a",
|
|
1203
|
+
});
|
|
1204
|
+
const finding2 = makeFinding({
|
|
1205
|
+
title: "Unique Finding B",
|
|
1206
|
+
endpoint: "https://target.com/b",
|
|
1207
|
+
});
|
|
1208
|
+
|
|
1209
|
+
await registry.register(finding1);
|
|
1210
|
+
await registry.register(finding2);
|
|
1211
|
+
|
|
1212
|
+
// Unregister finding1 — finding2 should still be there
|
|
1213
|
+
await registry.unregister(finding1);
|
|
1214
|
+
expect(registry.size).toBe(1);
|
|
1215
|
+
expect(registry.getFindings()[0]).toBe(finding2);
|
|
1216
|
+
});
|
|
1217
|
+
|
|
1218
|
+
it("serialises correctly with concurrent register/unregister calls", async () => {
|
|
1219
|
+
const registry = new FindingsRegistry();
|
|
1220
|
+
const finding = makeFinding({
|
|
1221
|
+
title: "Concurrent Test Finding",
|
|
1222
|
+
endpoint: "https://target.com/concurrent",
|
|
1223
|
+
});
|
|
1224
|
+
|
|
1225
|
+
// Register then immediately unregister — should end at size 0
|
|
1226
|
+
await registry.register(finding);
|
|
1227
|
+
const [, secondRegResult] = await Promise.all([
|
|
1228
|
+
registry.unregister(finding),
|
|
1229
|
+
registry.register(finding),
|
|
1230
|
+
]);
|
|
1231
|
+
|
|
1232
|
+
// After unregister + re-register, exactly one copy should exist
|
|
1233
|
+
expect(secondRegResult.duplicate).toBe(false);
|
|
1234
|
+
expect(registry.size).toBe(1);
|
|
1235
|
+
});
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1238
|
+
// ---------------------------------------------------------------------------
|
|
1239
|
+
// groupByRootCause
|
|
1240
|
+
// ---------------------------------------------------------------------------
|
|
1241
|
+
|
|
1242
|
+
describe("groupByRootCause", () => {
|
|
1243
|
+
const mockedGenerate = vi.mocked(generateObjectResponse);
|
|
1244
|
+
|
|
1245
|
+
afterEach(() => {
|
|
1246
|
+
mockedGenerate.mockReset();
|
|
1247
|
+
});
|
|
1248
|
+
|
|
1249
|
+
it("groups Cognito enumeration findings on different endpoints", async () => {
|
|
1250
|
+
mockedGenerate.mockResolvedValueOnce({
|
|
1251
|
+
groups: [
|
|
1252
|
+
{
|
|
1253
|
+
groupId: "cognito-user-existence-leak",
|
|
1254
|
+
findingIndices: [0, 1],
|
|
1255
|
+
rootCause:
|
|
1256
|
+
"Cognito leaks user-existence information through distinct error responses",
|
|
1257
|
+
},
|
|
1258
|
+
],
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
const findings = [
|
|
1262
|
+
makeFinding({
|
|
1263
|
+
title: "Email Enumeration via Forgot Password Flow",
|
|
1264
|
+
endpoint: "https://target.com/forgot-password",
|
|
1265
|
+
description:
|
|
1266
|
+
"The forgot password endpoint reveals whether an email exists via different error messages from Cognito.",
|
|
1267
|
+
}),
|
|
1268
|
+
makeFinding({
|
|
1269
|
+
title: "Username Enumeration via Cognito Account Lockout Response",
|
|
1270
|
+
endpoint: "https://target.com/login",
|
|
1271
|
+
description:
|
|
1272
|
+
"The login endpoint reveals user existence through Cognito lockout error responses.",
|
|
1273
|
+
}),
|
|
1274
|
+
];
|
|
1275
|
+
const registry = FindingsRegistry.fromFindings(findings, {
|
|
1276
|
+
model: "test-model",
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1279
|
+
const groups = await registry.groupByRootCause();
|
|
1280
|
+
|
|
1281
|
+
expect(groups).toHaveLength(1);
|
|
1282
|
+
expect(groups[0]?.groupId).toBe("cognito-user-existence-leak");
|
|
1283
|
+
expect(groups[0]?.findingIndices).toEqual([0, 1]);
|
|
1284
|
+
|
|
1285
|
+
const allFindings = registry.getFindings();
|
|
1286
|
+
expect(allFindings[0]?.rootCauseGroup).toBe("cognito-user-existence-leak");
|
|
1287
|
+
expect(allFindings[1]?.rootCauseGroup).toBe("cognito-user-existence-leak");
|
|
1288
|
+
expect(allFindings[0]?.relatedFindings).toEqual([allFindings[1]?.title]);
|
|
1289
|
+
expect(allFindings[1]?.relatedFindings).toEqual([allFindings[0]?.title]);
|
|
1290
|
+
});
|
|
1291
|
+
|
|
1292
|
+
it("normalizes grouped findings to the group's max severity and designates the lead", async () => {
|
|
1293
|
+
mockedGenerate.mockResolvedValueOnce({
|
|
1294
|
+
groups: [
|
|
1295
|
+
{
|
|
1296
|
+
groupId: "dev-layout-authz-bypass",
|
|
1297
|
+
findingIndices: [0, 1, 2],
|
|
1298
|
+
rootCause:
|
|
1299
|
+
"Authorization enforced only at the /dev layout, leaking page content via RSC/307 bodies across all /dev routes",
|
|
1300
|
+
},
|
|
1301
|
+
],
|
|
1302
|
+
});
|
|
1303
|
+
|
|
1304
|
+
const findings = [
|
|
1305
|
+
makeFinding({
|
|
1306
|
+
title: "RSC Header Bypass on /dev/files",
|
|
1307
|
+
severity: "HIGH",
|
|
1308
|
+
endpoint: "https://target.com/dev/files",
|
|
1309
|
+
description:
|
|
1310
|
+
"The /dev/files page leaks the full file inventory because authorization is only enforced at the layout.",
|
|
1311
|
+
}),
|
|
1312
|
+
makeFinding({
|
|
1313
|
+
title: "RSC Header Bypass on /dev/tags",
|
|
1314
|
+
severity: "MEDIUM",
|
|
1315
|
+
endpoint: "https://target.com/dev/tags",
|
|
1316
|
+
description:
|
|
1317
|
+
"The /dev/tags page leaks tag data via the same layout-only auth gap.",
|
|
1318
|
+
}),
|
|
1319
|
+
makeFinding({
|
|
1320
|
+
title: "RSC Header Bypass on /dev/usage",
|
|
1321
|
+
severity: "MEDIUM",
|
|
1322
|
+
endpoint: "https://target.com/dev/usage",
|
|
1323
|
+
description:
|
|
1324
|
+
"The /dev/usage page leaks billing analytics via the same layout-only auth gap.",
|
|
1325
|
+
}),
|
|
1326
|
+
];
|
|
1327
|
+
const registry = FindingsRegistry.fromFindings(findings, {
|
|
1328
|
+
model: "test-model",
|
|
1329
|
+
});
|
|
1330
|
+
|
|
1331
|
+
const groups = await registry.groupByRootCause();
|
|
1332
|
+
|
|
1333
|
+
expect(groups).toHaveLength(1);
|
|
1334
|
+
expect(groups[0]!.normalizedSeverity).toBe("HIGH");
|
|
1335
|
+
expect(groups[0]!.leadFindingIndex).toBe(0);
|
|
1336
|
+
|
|
1337
|
+
const allFindings = registry.getFindings();
|
|
1338
|
+
// All grouped findings normalized up to the group max (HIGH).
|
|
1339
|
+
expect(allFindings.map((f) => f.severity)).toEqual([
|
|
1340
|
+
"HIGH",
|
|
1341
|
+
"HIGH",
|
|
1342
|
+
"HIGH",
|
|
1343
|
+
]);
|
|
1344
|
+
// Exactly one lead, and it is the originally-highest-severity finding.
|
|
1345
|
+
expect(allFindings.filter((f) => f.rootCauseLead)).toHaveLength(1);
|
|
1346
|
+
expect(allFindings[0]!.rootCauseLead).toBe(true);
|
|
1347
|
+
expect(allFindings[1]!.rootCauseLead).toBe(false);
|
|
1348
|
+
expect(allFindings[2]!.rootCauseLead).toBe(false);
|
|
1349
|
+
});
|
|
1350
|
+
|
|
1351
|
+
it("derives each group's severity from pre-normalization ranks when a finding spans multiple groups", async () => {
|
|
1352
|
+
// finding[1] is in BOTH groups. group g1 normalizes it up to HIGH; g2 must
|
|
1353
|
+
// still see its ORIGINAL severity (LOW) so it isn't over-normalized.
|
|
1354
|
+
mockedGenerate.mockResolvedValueOnce({
|
|
1355
|
+
groups: [
|
|
1356
|
+
{ groupId: "g1", findingIndices: [0, 1], rootCause: "shared cause A" },
|
|
1357
|
+
{ groupId: "g2", findingIndices: [1, 2], rootCause: "shared cause B" },
|
|
1358
|
+
],
|
|
1359
|
+
});
|
|
1360
|
+
|
|
1361
|
+
const findings = [
|
|
1362
|
+
makeFinding({
|
|
1363
|
+
title: "High severity root",
|
|
1364
|
+
severity: "HIGH",
|
|
1365
|
+
endpoint: "https://target.com/a",
|
|
1366
|
+
description: "High-severity finding in group 1.",
|
|
1367
|
+
}),
|
|
1368
|
+
makeFinding({
|
|
1369
|
+
title: "Shared low finding",
|
|
1370
|
+
severity: "LOW",
|
|
1371
|
+
endpoint: "https://target.com/b",
|
|
1372
|
+
description: "Low-severity finding that the LLM places in both groups.",
|
|
1373
|
+
}),
|
|
1374
|
+
makeFinding({
|
|
1375
|
+
title: "Other low finding",
|
|
1376
|
+
severity: "LOW",
|
|
1377
|
+
endpoint: "https://target.com/c",
|
|
1378
|
+
description: "Low-severity finding only in group 2.",
|
|
1379
|
+
}),
|
|
1380
|
+
];
|
|
1381
|
+
const registry = FindingsRegistry.fromFindings(findings, {
|
|
1382
|
+
model: "test-model",
|
|
1383
|
+
});
|
|
1384
|
+
|
|
1385
|
+
const groups = await registry.groupByRootCause();
|
|
1386
|
+
|
|
1387
|
+
const g2 = groups.find((g) => g.groupId === "g2")!;
|
|
1388
|
+
// g2 contains two originally-LOW findings; it must stay LOW, not inherit
|
|
1389
|
+
// HIGH from g1's mutation of the shared finding.
|
|
1390
|
+
expect(g2.normalizedSeverity).toBe("LOW");
|
|
1391
|
+
|
|
1392
|
+
const all = registry.getFindings();
|
|
1393
|
+
// finding[2] is only in g2 and must not be inflated.
|
|
1394
|
+
expect(all[2]!.severity).toBe("LOW");
|
|
1395
|
+
});
|
|
1396
|
+
|
|
1397
|
+
it("groups same-endpoint auth + rate-limiting findings", async () => {
|
|
1398
|
+
mockedGenerate.mockResolvedValueOnce({
|
|
1399
|
+
groups: [
|
|
1400
|
+
{
|
|
1401
|
+
groupId: "unauth-ses-access",
|
|
1402
|
+
findingIndices: [0, 1],
|
|
1403
|
+
rootCause:
|
|
1404
|
+
"Unauthenticated access to SES API enables both abuse scenarios",
|
|
1405
|
+
},
|
|
1406
|
+
],
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
const findings = [
|
|
1410
|
+
makeFinding({
|
|
1411
|
+
title: "Missing Rate Limiting on Email API",
|
|
1412
|
+
severity: "CRITICAL",
|
|
1413
|
+
endpoint: "https://target.com/api/email/send",
|
|
1414
|
+
description: "The email sending endpoint has no rate limiting.",
|
|
1415
|
+
}),
|
|
1416
|
+
makeFinding({
|
|
1417
|
+
title: "Unauthenticated Email Sending via SES API",
|
|
1418
|
+
severity: "HIGH",
|
|
1419
|
+
endpoint: "https://target.com/api/email/send",
|
|
1420
|
+
description:
|
|
1421
|
+
"The email sending endpoint does not require authentication.",
|
|
1422
|
+
}),
|
|
1423
|
+
];
|
|
1424
|
+
const registry = FindingsRegistry.fromFindings(findings, {
|
|
1425
|
+
model: "test-model",
|
|
1426
|
+
});
|
|
1427
|
+
|
|
1428
|
+
const groups = await registry.groupByRootCause();
|
|
1429
|
+
|
|
1430
|
+
expect(groups).toHaveLength(1);
|
|
1431
|
+
expect(groups[0]?.groupId).toBe("unauth-ses-access");
|
|
1432
|
+
|
|
1433
|
+
const allFindings = registry.getFindings();
|
|
1434
|
+
expect(allFindings[0]?.rootCauseGroup).toBe("unauth-ses-access");
|
|
1435
|
+
expect(allFindings[1]?.rootCauseGroup).toBe("unauth-ses-access");
|
|
1436
|
+
});
|
|
1437
|
+
|
|
1438
|
+
it("does not group unrelated findings", async () => {
|
|
1439
|
+
mockedGenerate.mockResolvedValueOnce({
|
|
1440
|
+
groups: [],
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1443
|
+
const findings = [
|
|
1444
|
+
makeFinding({
|
|
1445
|
+
title: "SQL Injection in /api/products",
|
|
1446
|
+
endpoint: "https://target.com/api/products",
|
|
1447
|
+
description: "SQL injection via search parameter.",
|
|
1448
|
+
}),
|
|
1449
|
+
makeFinding({
|
|
1450
|
+
title: "Open Redirect on Login Callback",
|
|
1451
|
+
endpoint: "https://target.com/auth/callback",
|
|
1452
|
+
description: "Unvalidated redirect parameter in OAuth callback.",
|
|
1453
|
+
}),
|
|
1454
|
+
];
|
|
1455
|
+
const registry = FindingsRegistry.fromFindings(findings, {
|
|
1456
|
+
model: "test-model",
|
|
1457
|
+
});
|
|
1458
|
+
|
|
1459
|
+
const groups = await registry.groupByRootCause();
|
|
1460
|
+
|
|
1461
|
+
expect(groups).toHaveLength(0);
|
|
1462
|
+
|
|
1463
|
+
const allFindings = registry.getFindings();
|
|
1464
|
+
expect(allFindings[0]?.rootCauseGroup).toBeUndefined();
|
|
1465
|
+
expect(allFindings[1]?.rootCauseGroup).toBeUndefined();
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
it("returns empty array when no model configured", async () => {
|
|
1469
|
+
const registry = new FindingsRegistry(); // no model
|
|
1470
|
+
await registry.register(
|
|
1471
|
+
makeFinding({
|
|
1472
|
+
title: "Finding A",
|
|
1473
|
+
endpoint: "https://target.com/a",
|
|
1474
|
+
}),
|
|
1475
|
+
);
|
|
1476
|
+
await registry.register(
|
|
1477
|
+
makeFinding({
|
|
1478
|
+
title: "Finding B",
|
|
1479
|
+
endpoint: "https://target.com/b",
|
|
1480
|
+
}),
|
|
1481
|
+
);
|
|
1482
|
+
|
|
1483
|
+
const groups = await registry.groupByRootCause();
|
|
1484
|
+
|
|
1485
|
+
expect(groups).toEqual([]);
|
|
1486
|
+
expect(mockedGenerate).not.toHaveBeenCalled();
|
|
1487
|
+
});
|
|
1488
|
+
|
|
1489
|
+
it("returns empty array when fewer than 2 findings", async () => {
|
|
1490
|
+
const registry = new FindingsRegistry({ model: "test-model" });
|
|
1491
|
+
await registry.register(
|
|
1492
|
+
makeFinding({
|
|
1493
|
+
title: "Single Finding",
|
|
1494
|
+
endpoint: "https://target.com/a",
|
|
1495
|
+
}),
|
|
1496
|
+
);
|
|
1497
|
+
|
|
1498
|
+
const groups = await registry.groupByRootCause();
|
|
1499
|
+
|
|
1500
|
+
expect(groups).toEqual([]);
|
|
1501
|
+
expect(mockedGenerate).not.toHaveBeenCalled();
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
it("sanitises out-of-bounds indices in returned groups", async () => {
|
|
1505
|
+
mockedGenerate.mockResolvedValueOnce({
|
|
1506
|
+
groups: [
|
|
1507
|
+
{
|
|
1508
|
+
groupId: "test-group",
|
|
1509
|
+
findingIndices: [0, 1, 999, -1],
|
|
1510
|
+
rootCause: "Shared root cause",
|
|
1511
|
+
},
|
|
1512
|
+
],
|
|
1513
|
+
});
|
|
1514
|
+
|
|
1515
|
+
const findings = [
|
|
1516
|
+
makeFinding({
|
|
1517
|
+
title: "Finding A",
|
|
1518
|
+
endpoint: "https://target.com/a",
|
|
1519
|
+
}),
|
|
1520
|
+
makeFinding({
|
|
1521
|
+
title: "Finding B",
|
|
1522
|
+
endpoint: "https://target.com/b",
|
|
1523
|
+
}),
|
|
1524
|
+
];
|
|
1525
|
+
const registry = FindingsRegistry.fromFindings(findings, {
|
|
1526
|
+
model: "test-model",
|
|
1527
|
+
});
|
|
1528
|
+
|
|
1529
|
+
const groups = await registry.groupByRootCause();
|
|
1530
|
+
|
|
1531
|
+
expect(groups).toHaveLength(1);
|
|
1532
|
+
expect(groups[0]?.findingIndices).toEqual([0, 1]);
|
|
1533
|
+
});
|
|
1534
|
+
|
|
1535
|
+
it("uses index-based exclusion for relatedFindings (handles same-titled findings)", async () => {
|
|
1536
|
+
mockedGenerate.mockResolvedValueOnce({
|
|
1537
|
+
groups: [
|
|
1538
|
+
{
|
|
1539
|
+
groupId: "same-title-group",
|
|
1540
|
+
findingIndices: [0, 1, 2],
|
|
1541
|
+
rootCause: "Shared root cause across endpoints",
|
|
1542
|
+
},
|
|
1543
|
+
],
|
|
1544
|
+
});
|
|
1545
|
+
|
|
1546
|
+
const findings = [
|
|
1547
|
+
makeFinding({
|
|
1548
|
+
title: "User Enumeration",
|
|
1549
|
+
endpoint: "https://target.com/login",
|
|
1550
|
+
}),
|
|
1551
|
+
makeFinding({
|
|
1552
|
+
title: "User Enumeration",
|
|
1553
|
+
endpoint: "https://target.com/register",
|
|
1554
|
+
}),
|
|
1555
|
+
makeFinding({
|
|
1556
|
+
title: "Different Finding",
|
|
1557
|
+
endpoint: "https://target.com/api",
|
|
1558
|
+
}),
|
|
1559
|
+
];
|
|
1560
|
+
const registry = FindingsRegistry.fromFindings(findings, {
|
|
1561
|
+
model: "test-model",
|
|
1562
|
+
});
|
|
1563
|
+
|
|
1564
|
+
const groups = await registry.groupByRootCause();
|
|
1565
|
+
|
|
1566
|
+
const allFindings = registry.getFindings();
|
|
1567
|
+
// Index 0 should see indices 1 and 2 as related (even though idx 1 has same title)
|
|
1568
|
+
expect(allFindings[0]?.relatedFindings).toEqual([
|
|
1569
|
+
"User Enumeration",
|
|
1570
|
+
"Different Finding",
|
|
1571
|
+
]);
|
|
1572
|
+
expect(allFindings[1]?.relatedFindings).toEqual([
|
|
1573
|
+
"User Enumeration",
|
|
1574
|
+
"Different Finding",
|
|
1575
|
+
]);
|
|
1576
|
+
expect(allFindings[2]?.relatedFindings).toEqual([
|
|
1577
|
+
"User Enumeration",
|
|
1578
|
+
"User Enumeration",
|
|
1579
|
+
]);
|
|
1580
|
+
expect(groups).toHaveLength(1);
|
|
1581
|
+
});
|
|
1582
|
+
|
|
1583
|
+
it("returns empty array and logs when LLM call fails", async () => {
|
|
1584
|
+
mockedGenerate.mockRejectedValueOnce(new Error("API rate limit exceeded"));
|
|
1585
|
+
|
|
1586
|
+
const findings = [
|
|
1587
|
+
makeFinding({
|
|
1588
|
+
title: "Finding A",
|
|
1589
|
+
endpoint: "https://target.com/a",
|
|
1590
|
+
}),
|
|
1591
|
+
makeFinding({
|
|
1592
|
+
title: "Finding B",
|
|
1593
|
+
endpoint: "https://target.com/b",
|
|
1594
|
+
}),
|
|
1595
|
+
];
|
|
1596
|
+
const registry = FindingsRegistry.fromFindings(findings, {
|
|
1597
|
+
model: "test-model",
|
|
1598
|
+
});
|
|
1599
|
+
|
|
1600
|
+
const groups = await registry.groupByRootCause();
|
|
1601
|
+
|
|
1602
|
+
expect(groups).toEqual([]);
|
|
1603
|
+
const allFindings = registry.getFindings();
|
|
1604
|
+
expect(allFindings[0]?.rootCauseGroup).toBeUndefined();
|
|
1605
|
+
expect(allFindings[1]?.rootCauseGroup).toBeUndefined();
|
|
1606
|
+
});
|
|
1607
|
+
});
|
|
1608
|
+
|
|
1609
|
+
describe("FindingsRegistry model-call hooks", () => {
|
|
1610
|
+
const mockedGenerate = vi.mocked(generateObjectResponse);
|
|
1611
|
+
const languageModelMiddleware: LanguageModelMiddleware = {
|
|
1612
|
+
specificationVersion: "v3",
|
|
1613
|
+
};
|
|
1614
|
+
const usageRecorder = vi.fn();
|
|
1615
|
+
|
|
1616
|
+
afterEach(() => {
|
|
1617
|
+
mockedGenerate.mockReset();
|
|
1618
|
+
});
|
|
1619
|
+
|
|
1620
|
+
it("forwards languageModelMiddleware and usageRecorder to semantic dedup", async () => {
|
|
1621
|
+
mockedGenerate.mockResolvedValue({
|
|
1622
|
+
isDuplicate: false,
|
|
1623
|
+
reasoning: "Different vulnerabilities",
|
|
1624
|
+
});
|
|
1625
|
+
|
|
1626
|
+
const registry = new FindingsRegistry({
|
|
1627
|
+
model: "test-model",
|
|
1628
|
+
languageModelMiddleware,
|
|
1629
|
+
usageRecorder,
|
|
1630
|
+
});
|
|
1631
|
+
await registry.register(missingCspRoot);
|
|
1632
|
+
await registry.register(
|
|
1633
|
+
makeFinding({
|
|
1634
|
+
title: "No CSP Header Configured",
|
|
1635
|
+
endpoint: "https://target.com/admin",
|
|
1636
|
+
}),
|
|
1637
|
+
);
|
|
1638
|
+
|
|
1639
|
+
expect(mockedGenerate).toHaveBeenCalledOnce();
|
|
1640
|
+
expect(mockedGenerate).toHaveBeenCalledWith(
|
|
1641
|
+
expect.objectContaining({
|
|
1642
|
+
operation: "apex.finding.deduplicate",
|
|
1643
|
+
languageModelMiddleware,
|
|
1644
|
+
usageRecorder,
|
|
1645
|
+
}),
|
|
1646
|
+
);
|
|
1647
|
+
});
|
|
1648
|
+
|
|
1649
|
+
it("forwards the hooks to root-cause grouping", async () => {
|
|
1650
|
+
mockedGenerate.mockResolvedValue({ groups: [] });
|
|
1651
|
+
|
|
1652
|
+
const registry = FindingsRegistry.fromFindings(
|
|
1653
|
+
[sqlInjectionProducts, missingCspRoot],
|
|
1654
|
+
{ model: "test-model", languageModelMiddleware, usageRecorder },
|
|
1655
|
+
);
|
|
1656
|
+
await registry.groupByRootCause();
|
|
1657
|
+
|
|
1658
|
+
expect(mockedGenerate).toHaveBeenCalledWith(
|
|
1659
|
+
expect.objectContaining({
|
|
1660
|
+
operation: "apex.finding.root-cause",
|
|
1661
|
+
languageModelMiddleware,
|
|
1662
|
+
usageRecorder,
|
|
1663
|
+
}),
|
|
1664
|
+
);
|
|
1665
|
+
});
|
|
1666
|
+
|
|
1667
|
+
it("passes no hooks when the registry was built without them", async () => {
|
|
1668
|
+
mockedGenerate.mockResolvedValue({ groups: [] });
|
|
1669
|
+
|
|
1670
|
+
const registry = FindingsRegistry.fromFindings(
|
|
1671
|
+
[sqlInjectionProducts, missingCspRoot],
|
|
1672
|
+
{ model: "test-model" },
|
|
1673
|
+
);
|
|
1674
|
+
await registry.groupByRootCause();
|
|
1675
|
+
|
|
1676
|
+
const call = mockedGenerate.mock.calls[0]?.[0];
|
|
1677
|
+
expect(call?.languageModelMiddleware).toBeUndefined();
|
|
1678
|
+
expect(call?.usageRecorder).toBeUndefined();
|
|
1679
|
+
});
|
|
1680
|
+
});
|