kirograph 0.17.0 → 0.20.0
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/LICENSE +1 -1
- package/README.md +105 -1822
- package/dist/architecture/layers/dart.d.ts +10 -0
- package/dist/architecture/layers/dart.d.ts.map +1 -0
- package/dist/architecture/layers/dart.js +112 -0
- package/dist/architecture/layers/dart.js.map +7 -0
- package/dist/architecture/layers/elixir.d.ts +6 -0
- package/dist/architecture/layers/elixir.d.ts.map +1 -0
- package/dist/architecture/layers/index.d.ts.map +1 -1
- package/dist/architecture/layers/index.js +3 -1
- package/dist/architecture/layers/index.js.map +2 -2
- package/dist/architecture/layers/ocaml.d.ts +6 -0
- package/dist/architecture/layers/ocaml.d.ts.map +1 -0
- package/dist/architecture/layers/scala.d.ts +6 -0
- package/dist/architecture/layers/scala.d.ts.map +1 -0
- package/dist/architecture/layers/solidity.d.ts +6 -0
- package/dist/architecture/layers/solidity.d.ts.map +1 -0
- package/dist/architecture/layers/vue.d.ts +6 -0
- package/dist/architecture/layers/vue.d.ts.map +1 -0
- package/dist/architecture/manifest/elm.d.ts +3 -0
- package/dist/architecture/manifest/elm.d.ts.map +1 -0
- package/dist/architecture/manifest/index.d.ts.map +1 -1
- package/dist/architecture/manifest/ocaml.d.ts +3 -0
- package/dist/architecture/manifest/ocaml.d.ts.map +1 -0
- package/dist/architecture/manifest/scala.d.ts +3 -0
- package/dist/architecture/manifest/scala.d.ts.map +1 -0
- package/dist/bin/banner.d.ts.map +1 -1
- package/dist/bin/banner.js +74 -6
- package/dist/bin/banner.js.map +2 -2
- package/dist/bin/commands/affected.d.ts.map +1 -1
- package/dist/bin/commands/attack-surface.d.ts +3 -0
- package/dist/bin/commands/attack-surface.d.ts.map +1 -0
- package/dist/bin/commands/attack-surface.js +164 -0
- package/dist/bin/commands/attack-surface.js.map +7 -0
- package/dist/bin/commands/benchmark.d.ts +9 -0
- package/dist/bin/commands/benchmark.d.ts.map +1 -0
- package/dist/bin/commands/benchmark.js +206 -0
- package/dist/bin/commands/benchmark.js.map +7 -0
- package/dist/bin/commands/budget.d.ts +6 -0
- package/dist/bin/commands/budget.d.ts.map +1 -0
- package/dist/bin/commands/budget.js +70 -0
- package/dist/bin/commands/budget.js.map +7 -0
- package/dist/bin/commands/caveman.d.ts +7 -0
- package/dist/bin/commands/caveman.d.ts.map +1 -0
- package/dist/bin/commands/communities.d.ts +3 -0
- package/dist/bin/commands/communities.d.ts.map +1 -0
- package/dist/bin/commands/communities.js +77 -0
- package/dist/bin/commands/communities.js.map +7 -0
- package/dist/bin/commands/compression.d.ts +7 -0
- package/dist/bin/commands/compression.d.ts.map +1 -0
- package/dist/bin/commands/context.d.ts.map +1 -1
- package/dist/bin/commands/data.d.ts +3 -0
- package/dist/bin/commands/data.d.ts.map +1 -0
- package/dist/bin/commands/dead-code.d.ts +3 -0
- package/dist/bin/commands/dead-code.d.ts.map +1 -0
- package/dist/bin/commands/dep-confusion.d.ts +3 -0
- package/dist/bin/commands/dep-confusion.d.ts.map +1 -0
- package/dist/bin/commands/dep-confusion.js +119 -0
- package/dist/bin/commands/dep-confusion.js.map +7 -0
- package/dist/bin/commands/docs.d.ts +3 -0
- package/dist/bin/commands/docs.d.ts.map +1 -0
- package/dist/bin/commands/exec.d.ts +11 -0
- package/dist/bin/commands/exec.d.ts.map +1 -0
- package/dist/bin/commands/export.d.ts +3 -0
- package/dist/bin/commands/export.d.ts.map +1 -0
- package/dist/bin/commands/export.js +304 -10
- package/dist/bin/commands/export.js.map +2 -2
- package/dist/bin/commands/flows.d.ts +3 -0
- package/dist/bin/commands/flows.d.ts.map +1 -0
- package/dist/bin/commands/flows.js +97 -0
- package/dist/bin/commands/flows.js.map +7 -0
- package/dist/bin/commands/gain.d.ts +6 -0
- package/dist/bin/commands/gain.d.ts.map +1 -0
- package/dist/bin/commands/help.d.ts +7 -0
- package/dist/bin/commands/help.d.ts.map +1 -1
- package/dist/bin/commands/help.js +399 -101
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/commands/hotspots.d.ts +3 -0
- package/dist/bin/commands/hotspots.d.ts.map +1 -0
- package/dist/bin/commands/hotspots.js +94 -1
- package/dist/bin/commands/hotspots.js.map +3 -3
- package/dist/bin/commands/install.d.ts.map +1 -1
- package/dist/bin/commands/install.js +90 -8
- package/dist/bin/commands/install.js.map +2 -2
- package/dist/bin/commands/licenses.d.ts +3 -0
- package/dist/bin/commands/licenses.d.ts.map +1 -0
- package/dist/bin/commands/licenses.js +201 -0
- package/dist/bin/commands/licenses.js.map +7 -0
- package/dist/bin/commands/memory.d.ts +16 -0
- package/dist/bin/commands/memory.d.ts.map +1 -0
- package/dist/bin/commands/memory.js +151 -4
- package/dist/bin/commands/memory.js.map +2 -2
- package/dist/bin/commands/path.d.ts +3 -0
- package/dist/bin/commands/path.d.ts.map +1 -0
- package/dist/bin/commands/pattern.d.ts +3 -0
- package/dist/bin/commands/pattern.d.ts.map +1 -0
- package/dist/bin/commands/pattern.js +592 -0
- package/dist/bin/commands/pattern.js.map +7 -0
- package/dist/bin/commands/query.d.ts.map +1 -1
- package/dist/bin/commands/reachability.d.ts +3 -0
- package/dist/bin/commands/reachability.d.ts.map +1 -0
- package/dist/bin/commands/reachability.js +160 -0
- package/dist/bin/commands/reachability.js.map +7 -0
- package/dist/bin/commands/read.d.ts +6 -0
- package/dist/bin/commands/read.d.ts.map +1 -0
- package/dist/bin/commands/read.js +100 -0
- package/dist/bin/commands/read.js.map +7 -0
- package/dist/bin/commands/refactor.d.ts +3 -0
- package/dist/bin/commands/refactor.d.ts.map +1 -0
- package/dist/bin/commands/refactor.js +116 -0
- package/dist/bin/commands/refactor.js.map +7 -0
- package/dist/bin/commands/remediation.d.ts +3 -0
- package/dist/bin/commands/remediation.d.ts.map +1 -0
- package/dist/bin/commands/remediation.js +133 -0
- package/dist/bin/commands/remediation.js.map +7 -0
- package/dist/bin/commands/sbom.d.ts +3 -0
- package/dist/bin/commands/sbom.d.ts.map +1 -0
- package/dist/bin/commands/sbom.js +76 -0
- package/dist/bin/commands/sbom.js.map +7 -0
- package/dist/bin/commands/security-ci-report.d.ts +9 -0
- package/dist/bin/commands/security-ci-report.d.ts.map +1 -0
- package/dist/bin/commands/security-ci-report.js +267 -0
- package/dist/bin/commands/security-ci-report.js.map +7 -0
- package/dist/bin/commands/security-export.d.ts +2 -0
- package/dist/bin/commands/security-export.d.ts.map +1 -0
- package/dist/bin/commands/security-export.js +1349 -0
- package/dist/bin/commands/security-export.js.map +7 -0
- package/dist/bin/commands/security-flows.d.ts +3 -0
- package/dist/bin/commands/security-flows.d.ts.map +1 -0
- package/dist/bin/commands/security-flows.js +126 -0
- package/dist/bin/commands/security-flows.js.map +7 -0
- package/dist/bin/commands/security-secrets.d.ts +3 -0
- package/dist/bin/commands/security-secrets.d.ts.map +1 -0
- package/dist/bin/commands/security-secrets.js +170 -0
- package/dist/bin/commands/security-secrets.js.map +7 -0
- package/dist/bin/commands/security.d.ts +3 -0
- package/dist/bin/commands/security.d.ts.map +1 -0
- package/dist/bin/commands/security.js +208 -0
- package/dist/bin/commands/security.js.map +7 -0
- package/dist/bin/commands/snapshot.d.ts +3 -0
- package/dist/bin/commands/snapshot.d.ts.map +1 -0
- package/dist/bin/commands/staleness.d.ts +3 -0
- package/dist/bin/commands/staleness.d.ts.map +1 -0
- package/dist/bin/commands/staleness.js +144 -0
- package/dist/bin/commands/staleness.js.map +7 -0
- package/dist/bin/commands/status.d.ts.map +1 -1
- package/dist/bin/commands/status.js +129 -2
- package/dist/bin/commands/status.js.map +2 -2
- package/dist/bin/commands/supply-chain.d.ts +3 -0
- package/dist/bin/commands/supply-chain.d.ts.map +1 -0
- package/dist/bin/commands/supply-chain.js +138 -0
- package/dist/bin/commands/supply-chain.js.map +7 -0
- package/dist/bin/commands/surprising.d.ts +3 -0
- package/dist/bin/commands/surprising.d.ts.map +1 -0
- package/dist/bin/commands/sync.d.ts.map +1 -1
- package/dist/bin/commands/uninit.d.ts.map +1 -1
- package/dist/bin/commands/uninit.js +109 -60
- package/dist/bin/commands/uninit.js.map +2 -2
- package/dist/bin/commands/utils.d.ts +5 -0
- package/dist/bin/commands/utils.d.ts.map +1 -1
- package/dist/bin/commands/vex.d.ts +3 -0
- package/dist/bin/commands/vex.d.ts.map +1 -0
- package/dist/bin/commands/vex.js +76 -0
- package/dist/bin/commands/vex.js.map +7 -0
- package/dist/bin/commands/vuln-suppress.d.ts +3 -0
- package/dist/bin/commands/vuln-suppress.d.ts.map +1 -0
- package/dist/bin/commands/vuln-suppress.js +95 -0
- package/dist/bin/commands/vuln-suppress.js.map +7 -0
- package/dist/bin/commands/vulns.d.ts +3 -0
- package/dist/bin/commands/vulns.d.ts.map +1 -0
- package/dist/bin/commands/vulns.js +382 -0
- package/dist/bin/commands/vulns.js.map +7 -0
- package/dist/bin/installer/archive.d.ts +47 -0
- package/dist/bin/installer/archive.d.ts.map +1 -0
- package/dist/bin/installer/auto-detect.d.ts +16 -0
- package/dist/bin/installer/auto-detect.d.ts.map +1 -0
- package/dist/bin/installer/auto-detect.js +125 -0
- package/dist/bin/installer/auto-detect.js.map +7 -0
- package/dist/bin/installer/caveman.d.ts +12 -0
- package/dist/bin/installer/caveman.d.ts.map +1 -0
- package/dist/bin/installer/cli-agent.d.ts +7 -2
- package/dist/bin/installer/cli-agent.d.ts.map +1 -1
- package/dist/bin/installer/cli-agent.js +32 -5
- package/dist/bin/installer/cli-agent.js.map +2 -2
- package/dist/bin/installer/common.d.ts +36 -0
- package/dist/bin/installer/common.d.ts.map +1 -0
- package/dist/bin/installer/common.js +48 -0
- package/dist/bin/installer/common.js.map +2 -2
- package/dist/bin/installer/config-prompt.d.ts +1 -1
- package/dist/bin/installer/config-prompt.d.ts.map +1 -1
- package/dist/bin/installer/config-prompt.js +85 -2
- package/dist/bin/installer/config-prompt.js.map +2 -2
- package/dist/bin/installer/dashboard.d.ts.map +1 -1
- package/dist/bin/installer/detect.d.ts +30 -0
- package/dist/bin/installer/detect.d.ts.map +1 -0
- package/dist/bin/installer/detect.js +309 -0
- package/dist/bin/installer/detect.js.map +7 -0
- package/dist/bin/installer/hooks.d.ts +4 -1
- package/dist/bin/installer/hooks.d.ts.map +1 -1
- package/dist/bin/installer/hooks.js +34 -0
- package/dist/bin/installer/hooks.js.map +2 -2
- package/dist/bin/installer/index.d.ts +5 -4
- package/dist/bin/installer/index.d.ts.map +1 -1
- package/dist/bin/installer/index.js +46 -8
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/installer/instructions.d.ts +14 -0
- package/dist/bin/installer/instructions.d.ts.map +1 -0
- package/dist/bin/installer/instructions.js +209 -3
- package/dist/bin/installer/instructions.js.map +2 -2
- package/dist/bin/installer/mcp.d.ts.map +1 -1
- package/dist/bin/installer/prompts.d.ts +17 -0
- package/dist/bin/installer/prompts.d.ts.map +1 -1
- package/dist/bin/installer/qdrant-dashboard.d.ts.map +1 -1
- package/dist/bin/installer/steering.d.ts +14 -2
- package/dist/bin/installer/steering.d.ts.map +1 -1
- package/dist/bin/installer/steering.js +488 -0
- package/dist/bin/installer/steering.js.map +2 -2
- package/dist/bin/installer/targets/aider.d.ts +6 -0
- package/dist/bin/installer/targets/aider.d.ts.map +1 -0
- package/dist/bin/installer/targets/aider.js +3 -3
- package/dist/bin/installer/targets/aider.js.map +2 -2
- package/dist/bin/installer/targets/amp.d.ts +6 -0
- package/dist/bin/installer/targets/amp.d.ts.map +1 -0
- package/dist/bin/installer/targets/amp.js +3 -3
- package/dist/bin/installer/targets/amp.js.map +2 -2
- package/dist/bin/installer/targets/antigravity.d.ts +13 -0
- package/dist/bin/installer/targets/antigravity.d.ts.map +1 -0
- package/dist/bin/installer/targets/antigravity.js +35 -6
- package/dist/bin/installer/targets/antigravity.js.map +2 -2
- package/dist/bin/installer/targets/augment.d.ts +6 -0
- package/dist/bin/installer/targets/augment.d.ts.map +1 -0
- package/dist/bin/installer/targets/augment.js +3 -3
- package/dist/bin/installer/targets/augment.js.map +2 -2
- package/dist/bin/installer/targets/claude.d.ts +6 -0
- package/dist/bin/installer/targets/claude.d.ts.map +1 -0
- package/dist/bin/installer/targets/claude.js +49 -5
- package/dist/bin/installer/targets/claude.js.map +2 -2
- package/dist/bin/installer/targets/cline.d.ts +13 -0
- package/dist/bin/installer/targets/cline.d.ts.map +1 -0
- package/dist/bin/installer/targets/cline.js +32 -6
- package/dist/bin/installer/targets/cline.js.map +2 -2
- package/dist/bin/installer/targets/codex.d.ts +6 -0
- package/dist/bin/installer/targets/codex.d.ts.map +1 -0
- package/dist/bin/installer/targets/codex.js +64 -12
- package/dist/bin/installer/targets/codex.js.map +2 -2
- package/dist/bin/installer/targets/continue.d.ts +12 -0
- package/dist/bin/installer/targets/continue.d.ts.map +1 -0
- package/dist/bin/installer/targets/continue.js +2 -2
- package/dist/bin/installer/targets/continue.js.map +2 -2
- package/dist/bin/installer/targets/copilot-cli.d.ts +12 -0
- package/dist/bin/installer/targets/copilot-cli.d.ts.map +1 -0
- package/dist/bin/installer/targets/copilot-cli.js +101 -0
- package/dist/bin/installer/targets/copilot-cli.js.map +7 -0
- package/dist/bin/installer/targets/copilot.d.ts +6 -0
- package/dist/bin/installer/targets/copilot.d.ts.map +1 -0
- package/dist/bin/installer/targets/copilot.js +31 -5
- package/dist/bin/installer/targets/copilot.js.map +2 -2
- package/dist/bin/installer/targets/cursor.d.ts +6 -0
- package/dist/bin/installer/targets/cursor.d.ts.map +1 -0
- package/dist/bin/installer/targets/cursor.js +4 -4
- package/dist/bin/installer/targets/cursor.js.map +2 -2
- package/dist/bin/installer/targets/devin.d.ts +13 -0
- package/dist/bin/installer/targets/devin.d.ts.map +1 -0
- package/dist/bin/installer/targets/devin.js +2 -2
- package/dist/bin/installer/targets/devin.js.map +2 -2
- package/dist/bin/installer/targets/gemini-cli.d.ts +16 -0
- package/dist/bin/installer/targets/gemini-cli.d.ts.map +1 -0
- package/dist/bin/installer/targets/gemini-cli.js +2 -2
- package/dist/bin/installer/targets/gemini-cli.js.map +2 -2
- package/dist/bin/installer/targets/generic.d.ts +59 -0
- package/dist/bin/installer/targets/generic.d.ts.map +1 -0
- package/dist/bin/installer/targets/generic.js +2 -14
- package/dist/bin/installer/targets/generic.js.map +2 -2
- package/dist/bin/installer/targets/goose.d.ts +6 -0
- package/dist/bin/installer/targets/goose.d.ts.map +1 -0
- package/dist/bin/installer/targets/goose.js +3 -3
- package/dist/bin/installer/targets/goose.js.map +2 -2
- package/dist/bin/installer/targets/index.d.ts +11 -0
- package/dist/bin/installer/targets/index.d.ts.map +1 -0
- package/dist/bin/installer/targets/index.js +250 -0
- package/dist/bin/installer/targets/index.js.map +2 -2
- package/dist/bin/installer/targets/junie.d.ts +13 -0
- package/dist/bin/installer/targets/junie.d.ts.map +1 -0
- package/dist/bin/installer/targets/junie.js +2 -2
- package/dist/bin/installer/targets/junie.js.map +2 -2
- package/dist/bin/installer/targets/kilo.d.ts +12 -0
- package/dist/bin/installer/targets/kilo.d.ts.map +1 -0
- package/dist/bin/installer/targets/kilo.js +2 -2
- package/dist/bin/installer/targets/kilo.js.map +2 -2
- package/dist/bin/installer/targets/kiro.d.ts +5 -0
- package/dist/bin/installer/targets/kiro.d.ts.map +1 -0
- package/dist/bin/installer/targets/kiro.js +4 -4
- package/dist/bin/installer/targets/kiro.js.map +2 -2
- package/dist/bin/installer/targets/opencode.d.ts +13 -0
- package/dist/bin/installer/targets/opencode.d.ts.map +1 -0
- package/dist/bin/installer/targets/opencode.js +2 -2
- package/dist/bin/installer/targets/opencode.js.map +2 -2
- package/dist/bin/installer/targets/openhands.d.ts +6 -0
- package/dist/bin/installer/targets/openhands.d.ts.map +1 -0
- package/dist/bin/installer/targets/openhands.js +3 -3
- package/dist/bin/installer/targets/openhands.js.map +2 -2
- package/dist/bin/installer/targets/qoder.d.ts +12 -0
- package/dist/bin/installer/targets/qoder.d.ts.map +1 -0
- package/dist/bin/installer/targets/qoder.js +91 -0
- package/dist/bin/installer/targets/qoder.js.map +7 -0
- package/dist/bin/installer/targets/qwen.d.ts +12 -0
- package/dist/bin/installer/targets/qwen.d.ts.map +1 -0
- package/dist/bin/installer/targets/qwen.js +96 -0
- package/dist/bin/installer/targets/qwen.js.map +7 -0
- package/dist/bin/installer/targets/replit.d.ts +6 -0
- package/dist/bin/installer/targets/replit.d.ts.map +1 -0
- package/dist/bin/installer/targets/replit.js +3 -3
- package/dist/bin/installer/targets/replit.js.map +2 -2
- package/dist/bin/installer/targets/roo.d.ts +13 -0
- package/dist/bin/installer/targets/roo.d.ts.map +1 -0
- package/dist/bin/installer/targets/roo.js +2 -2
- package/dist/bin/installer/targets/roo.js.map +2 -2
- package/dist/bin/installer/targets/tabnine.d.ts +6 -0
- package/dist/bin/installer/targets/tabnine.d.ts.map +1 -0
- package/dist/bin/installer/targets/tabnine.js +3 -3
- package/dist/bin/installer/targets/tabnine.js.map +2 -2
- package/dist/bin/installer/targets/trae.d.ts +6 -0
- package/dist/bin/installer/targets/trae.d.ts.map +1 -0
- package/dist/bin/installer/targets/trae.js +5 -5
- package/dist/bin/installer/targets/trae.js.map +2 -2
- package/dist/bin/installer/targets/warp.d.ts +12 -0
- package/dist/bin/installer/targets/warp.d.ts.map +1 -0
- package/dist/bin/installer/targets/warp.js +2 -2
- package/dist/bin/installer/targets/warp.js.map +2 -2
- package/dist/bin/installer/targets/windsurf.d.ts +13 -0
- package/dist/bin/installer/targets/windsurf.d.ts.map +1 -0
- package/dist/bin/installer/targets/windsurf.js +35 -6
- package/dist/bin/installer/targets/windsurf.js.map +2 -2
- package/dist/bin/kirograph.js +74 -6
- package/dist/bin/kirograph.js.map +3 -3
- package/dist/bin/progress.d.ts +16 -1
- package/dist/bin/progress.d.ts.map +1 -1
- package/dist/compression/filters/aws.d.ts +9 -0
- package/dist/compression/filters/aws.d.ts.map +1 -0
- package/dist/compression/filters/docker.d.ts +6 -0
- package/dist/compression/filters/docker.d.ts.map +1 -0
- package/dist/compression/filters/files.d.ts +6 -0
- package/dist/compression/filters/files.d.ts.map +1 -0
- package/dist/compression/filters/generic.d.ts +7 -0
- package/dist/compression/filters/generic.d.ts.map +1 -0
- package/dist/compression/filters/git.d.ts +6 -0
- package/dist/compression/filters/git.d.ts.map +1 -0
- package/dist/compression/filters/github.d.ts +6 -0
- package/dist/compression/filters/github.d.ts.map +1 -0
- package/dist/compression/filters/lint.d.ts +6 -0
- package/dist/compression/filters/lint.d.ts.map +1 -0
- package/dist/compression/filters/misc.d.ts +6 -0
- package/dist/compression/filters/misc.d.ts.map +1 -0
- package/dist/compression/filters/package.d.ts +6 -0
- package/dist/compression/filters/package.d.ts.map +1 -0
- package/dist/compression/filters/test.d.ts +6 -0
- package/dist/compression/filters/test.d.ts.map +1 -0
- package/dist/compression/index.d.ts +29 -0
- package/dist/compression/index.d.ts.map +1 -0
- package/dist/compression/naive-cost.d.ts +26 -0
- package/dist/compression/naive-cost.d.ts.map +1 -0
- package/dist/compression/tracker.d.ts +155 -0
- package/dist/compression/tracker.d.ts.map +1 -0
- package/dist/compression/tracker.js +80 -0
- package/dist/compression/tracker.js.map +2 -2
- package/dist/compression/types.d.ts +49 -0
- package/dist/compression/types.d.ts.map +1 -0
- package/dist/config.d.ts +90 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +144 -3
- package/dist/config.js.map +2 -2
- package/dist/core/lock-manager.d.ts +2 -0
- package/dist/core/lock-manager.d.ts.map +1 -1
- package/dist/core/pipeline.d.ts +4 -1
- package/dist/core/pipeline.d.ts.map +1 -1
- package/dist/core/pipeline.js +78 -2
- package/dist/core/pipeline.js.map +2 -2
- package/dist/core/snapshot.d.ts +58 -0
- package/dist/core/snapshot.d.ts.map +1 -0
- package/dist/data/filters.d.ts +23 -0
- package/dist/data/filters.d.ts.map +1 -0
- package/dist/data/indexer.d.ts +25 -0
- package/dist/data/indexer.d.ts.map +1 -0
- package/dist/data/linker.d.ts +37 -0
- package/dist/data/linker.d.ts.map +1 -0
- package/dist/data/lint.d.ts +27 -0
- package/dist/data/lint.d.ts.map +1 -0
- package/dist/data/parsers/csv.d.ts +11 -0
- package/dist/data/parsers/csv.d.ts.map +1 -0
- package/dist/data/parsers/excel.d.ts +10 -0
- package/dist/data/parsers/excel.d.ts.map +1 -0
- package/dist/data/parsers/index.d.ts +28 -0
- package/dist/data/parsers/index.d.ts.map +1 -0
- package/dist/data/parsers/json-array.d.ts +9 -0
- package/dist/data/parsers/json-array.d.ts.map +1 -0
- package/dist/data/parsers/jsonl.d.ts +9 -0
- package/dist/data/parsers/jsonl.d.ts.map +1 -0
- package/dist/data/parsers/parquet.d.ts +10 -0
- package/dist/data/parsers/parquet.d.ts.map +1 -0
- package/dist/data/profiler.d.ts +29 -0
- package/dist/data/profiler.d.ts.map +1 -0
- package/dist/data/queries.d.ts +95 -0
- package/dist/data/queries.d.ts.map +1 -0
- package/dist/data/types.d.ts +103 -0
- package/dist/data/types.d.ts.map +1 -0
- package/dist/db/database.d.ts +57 -0
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/database.js +75 -5
- package/dist/db/database.js.map +2 -2
- package/dist/db/memory-schema.sql +5 -1
- package/dist/db/patterns-schema.sql +23 -0
- package/dist/db/schema.sql +3 -1
- package/dist/db/security-schema.sql +72 -0
- package/dist/docs/formats/asciidoc.d.ts +9 -0
- package/dist/docs/formats/asciidoc.d.ts.map +1 -0
- package/dist/docs/formats/html.d.ts +10 -0
- package/dist/docs/formats/html.d.ts.map +1 -0
- package/dist/docs/formats/index.d.ts +31 -0
- package/dist/docs/formats/index.d.ts.map +1 -0
- package/dist/docs/formats/markdown.d.ts +11 -0
- package/dist/docs/formats/markdown.d.ts.map +1 -0
- package/dist/docs/formats/openapi.d.ts +17 -0
- package/dist/docs/formats/openapi.d.ts.map +1 -0
- package/dist/docs/formats/org.d.ts +9 -0
- package/dist/docs/formats/org.d.ts.map +1 -0
- package/dist/docs/formats/plaintext.d.ts +11 -0
- package/dist/docs/formats/plaintext.d.ts.map +1 -0
- package/dist/docs/formats/rdoc.d.ts +10 -0
- package/dist/docs/formats/rdoc.d.ts.map +1 -0
- package/dist/docs/formats/rst.d.ts +11 -0
- package/dist/docs/formats/rst.d.ts.map +1 -0
- package/dist/docs/indexer.d.ts +49 -0
- package/dist/docs/indexer.d.ts.map +1 -0
- package/dist/docs/linker.d.ts +30 -0
- package/dist/docs/linker.d.ts.map +1 -0
- package/dist/docs/lint.d.ts +27 -0
- package/dist/docs/lint.d.ts.map +1 -0
- package/dist/docs/queries.d.ts +78 -0
- package/dist/docs/queries.d.ts.map +1 -0
- package/dist/docs/section-id.d.ts +48 -0
- package/dist/docs/section-id.d.ts.map +1 -0
- package/dist/docs/summarizer.d.ts +13 -0
- package/dist/docs/summarizer.d.ts.map +1 -0
- package/dist/docs/types.d.ts +71 -0
- package/dist/docs/types.d.ts.map +1 -0
- package/dist/docs/vectors.d.ts +54 -0
- package/dist/docs/vectors.d.ts.map +1 -0
- package/dist/extraction/extractor.d.ts +1 -1
- package/dist/extraction/extractor.d.ts.map +1 -1
- package/dist/extraction/extractor.js +81 -0
- package/dist/extraction/extractor.js.map +2 -2
- package/dist/extraction/grammars.d.ts +6 -0
- package/dist/extraction/grammars.d.ts.map +1 -1
- package/dist/extraction/grammars.js +32 -8
- package/dist/extraction/grammars.js.map +2 -2
- package/dist/extraction/languages.d.ts.map +1 -1
- package/dist/extraction/languages.js +43 -1
- package/dist/extraction/languages.js.map +2 -2
- package/dist/extraction/notebook.d.ts +14 -0
- package/dist/extraction/notebook.d.ts.map +1 -0
- package/dist/extraction/notebook.js +300 -0
- package/dist/extraction/notebook.js.map +7 -0
- package/dist/extraction/wasm/tree-sitter-astro.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-gdscript.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-julia.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-perl.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-powershell.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-sql.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-verilog.wasm +0 -0
- package/dist/frameworks/amplify.d.ts +9 -0
- package/dist/frameworks/amplify.d.ts.map +1 -0
- package/dist/frameworks/angular.d.ts +9 -0
- package/dist/frameworks/angular.d.ts.map +1 -0
- package/dist/frameworks/ansible.d.ts +8 -0
- package/dist/frameworks/ansible.d.ts.map +1 -0
- package/dist/frameworks/cloudformation.d.ts +9 -0
- package/dist/frameworks/cloudformation.d.ts.map +1 -0
- package/dist/frameworks/docker.d.ts +9 -0
- package/dist/frameworks/docker.d.ts.map +1 -0
- package/dist/frameworks/elixir.d.ts +6 -0
- package/dist/frameworks/elixir.d.ts.map +1 -0
- package/dist/frameworks/flutter.d.ts +12 -0
- package/dist/frameworks/flutter.d.ts.map +1 -0
- package/dist/frameworks/flutter.js +130 -0
- package/dist/frameworks/flutter.js.map +7 -0
- package/dist/frameworks/iac.d.ts +10 -0
- package/dist/frameworks/iac.d.ts.map +1 -0
- package/dist/frameworks/index.d.ts +14 -0
- package/dist/frameworks/index.d.ts.map +1 -1
- package/dist/frameworks/index.js +7 -1
- package/dist/frameworks/index.js.map +3 -3
- package/dist/frameworks/kubernetes.d.ts +9 -0
- package/dist/frameworks/kubernetes.d.ts.map +1 -0
- package/dist/frameworks/pulumi.d.ts +9 -0
- package/dist/frameworks/pulumi.d.ts.map +1 -0
- package/dist/frameworks/scala.d.ts +6 -0
- package/dist/frameworks/scala.d.ts.map +1 -0
- package/dist/frameworks/solidity.d.ts +6 -0
- package/dist/frameworks/solidity.d.ts.map +1 -0
- package/dist/frameworks/terraform.d.ts +14 -0
- package/dist/frameworks/terraform.d.ts.map +1 -0
- package/dist/frameworks/vue.d.ts +7 -0
- package/dist/frameworks/vue.d.ts.map +1 -0
- package/dist/graph/communities.d.ts +43 -0
- package/dist/graph/communities.d.ts.map +1 -0
- package/dist/graph/communities.js +317 -0
- package/dist/graph/communities.js.map +7 -0
- package/dist/graph/flows.d.ts +55 -0
- package/dist/graph/flows.d.ts.map +1 -0
- package/dist/graph/flows.js +138 -0
- package/dist/graph/flows.js.map +7 -0
- package/dist/graph/refactor.d.ts +43 -0
- package/dist/graph/refactor.d.ts.map +1 -0
- package/dist/graph/refactor.js +155 -0
- package/dist/graph/refactor.js.map +7 -0
- package/dist/index.d.ts +36 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +2 -2
- package/dist/mcp/cache.d.ts +69 -0
- package/dist/mcp/cache.d.ts.map +1 -0
- package/dist/mcp/cache.js +172 -0
- package/dist/mcp/cache.js.map +7 -0
- package/dist/mcp/read-modes.d.ts +31 -0
- package/dist/mcp/read-modes.d.ts.map +1 -0
- package/dist/mcp/read-modes.js +295 -0
- package/dist/mcp/read-modes.js.map +7 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +17 -3
- package/dist/mcp/server.js.map +2 -2
- package/dist/mcp/tool-names.d.ts +2 -0
- package/dist/mcp/tool-names.d.ts.map +1 -0
- package/dist/mcp/tool-names.js +24 -1
- package/dist/mcp/tool-names.js.map +2 -2
- package/dist/mcp/tools.d.ts +12 -0
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +1560 -4
- package/dist/mcp/tools.js.map +3 -3
- package/dist/memory/compress.d.ts +19 -0
- package/dist/memory/compress.d.ts.map +1 -0
- package/dist/memory/database.d.ts +92 -0
- package/dist/memory/database.d.ts.map +1 -0
- package/dist/memory/database.js +70 -2
- package/dist/memory/database.js.map +2 -2
- package/dist/memory/index.d.ts +71 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +12 -2
- package/dist/memory/index.js.map +2 -2
- package/dist/memory/symbols.d.ts +21 -0
- package/dist/memory/symbols.d.ts.map +1 -0
- package/dist/memory/types.d.ts +78 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js.map +1 -1
- package/dist/memory/vectors.d.ts +43 -0
- package/dist/memory/vectors.d.ts.map +1 -0
- package/dist/patterns/graph.d.ts +16 -0
- package/dist/patterns/graph.d.ts.map +1 -0
- package/dist/patterns/graph.js +45 -0
- package/dist/patterns/graph.js.map +7 -0
- package/dist/patterns/index.d.ts +4 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +39 -0
- package/dist/patterns/index.js.map +7 -0
- package/dist/patterns/indexer.d.ts +15 -0
- package/dist/patterns/indexer.d.ts.map +1 -0
- package/dist/patterns/indexer.js +97 -0
- package/dist/patterns/indexer.js.map +7 -0
- package/dist/patterns/library/command-injection-go.yaml +11 -0
- package/dist/patterns/library/dangerous-eval-js.yaml +14 -0
- package/dist/patterns/library/dangerous-exec-py.yaml +13 -0
- package/dist/patterns/library/dangerous-reflection-java.yaml +8 -0
- package/dist/patterns/library/path-traversal-go.yaml +11 -0
- package/dist/patterns/library/path-traversal-py.yaml +12 -0
- package/dist/patterns/library/path-traversal-readfile-js.yaml +14 -0
- package/dist/patterns/library/prototype-pollution-js.yaml +12 -0
- package/dist/patterns/library/sql-injection-concat-js.yaml +12 -0
- package/dist/patterns/library/sql-injection-java.yaml +13 -0
- package/dist/patterns/library/sql-injection-py.yaml +12 -0
- package/dist/patterns/library/sql-injection-template-js.yaml +13 -0
- package/dist/patterns/library/weak-crypto-md5-js.yaml +13 -0
- package/dist/patterns/library/weak-crypto-py.yaml +13 -0
- package/dist/patterns/loader.d.ts +18 -0
- package/dist/patterns/loader.d.ts.map +1 -0
- package/dist/patterns/loader.js +277 -0
- package/dist/patterns/loader.js.map +7 -0
- package/dist/patterns/runner.d.ts +42 -0
- package/dist/patterns/runner.d.ts.map +1 -0
- package/dist/patterns/runner.js +277 -0
- package/dist/patterns/runner.js.map +7 -0
- package/dist/patterns/types.d.ts +27 -0
- package/dist/patterns/types.d.ts.map +1 -0
- package/dist/patterns/types.js +34 -0
- package/dist/patterns/types.js.map +7 -0
- package/dist/resolution/bridges/android-rn.d.ts +13 -0
- package/dist/resolution/bridges/android-rn.d.ts.map +1 -0
- package/dist/resolution/bridges/android-rn.js +298 -0
- package/dist/resolution/bridges/android-rn.js.map +7 -0
- package/dist/resolution/bridges/expo-modules.d.ts +10 -0
- package/dist/resolution/bridges/expo-modules.d.ts.map +1 -0
- package/dist/resolution/bridges/expo-modules.js +155 -0
- package/dist/resolution/bridges/expo-modules.js.map +7 -0
- package/dist/resolution/bridges/flutter-channel.d.ts +14 -0
- package/dist/resolution/bridges/flutter-channel.d.ts.map +1 -0
- package/dist/resolution/bridges/flutter-channel.js +385 -0
- package/dist/resolution/bridges/flutter-channel.js.map +7 -0
- package/dist/resolution/bridges/index.d.ts +40 -0
- package/dist/resolution/bridges/index.d.ts.map +1 -0
- package/dist/resolution/bridges/index.js +80 -0
- package/dist/resolution/bridges/index.js.map +7 -0
- package/dist/resolution/bridges/native-events.d.ts +10 -0
- package/dist/resolution/bridges/native-events.d.ts.map +1 -0
- package/dist/resolution/bridges/native-events.js +188 -0
- package/dist/resolution/bridges/native-events.js.map +7 -0
- package/dist/resolution/bridges/native-views.d.ts +13 -0
- package/dist/resolution/bridges/native-views.d.ts.map +1 -0
- package/dist/resolution/bridges/native-views.js +244 -0
- package/dist/resolution/bridges/native-views.js.map +7 -0
- package/dist/resolution/bridges/react-native.d.ts +9 -0
- package/dist/resolution/bridges/react-native.d.ts.map +1 -0
- package/dist/resolution/bridges/react-native.js +161 -0
- package/dist/resolution/bridges/react-native.js.map +7 -0
- package/dist/resolution/bridges/swift-objc.d.ts +14 -0
- package/dist/resolution/bridges/swift-objc.d.ts.map +1 -0
- package/dist/resolution/bridges/swift-objc.js +227 -0
- package/dist/resolution/bridges/swift-objc.js.map +7 -0
- package/dist/resolution/bridges/turbomodules.d.ts +10 -0
- package/dist/resolution/bridges/turbomodules.d.ts.map +1 -0
- package/dist/resolution/bridges/turbomodules.js +216 -0
- package/dist/resolution/bridges/turbomodules.js.map +7 -0
- package/dist/resolution/index.d.ts +28 -3
- package/dist/resolution/index.d.ts.map +1 -1
- package/dist/resolution/index.js +61 -2
- package/dist/resolution/index.js.map +2 -2
- package/dist/security/attack-surface.d.ts +46 -0
- package/dist/security/attack-surface.d.ts.map +1 -0
- package/dist/security/attack-surface.js +206 -0
- package/dist/security/attack-surface.js.map +7 -0
- package/dist/security/context-warnings.d.ts +40 -0
- package/dist/security/context-warnings.d.ts.map +1 -0
- package/dist/security/context-warnings.js +123 -0
- package/dist/security/context-warnings.js.map +7 -0
- package/dist/security/data-flows.d.ts +33 -0
- package/dist/security/data-flows.d.ts.map +1 -0
- package/dist/security/data-flows.js +286 -0
- package/dist/security/data-flows.js.map +7 -0
- package/dist/security/dep-confusion.d.ts +23 -0
- package/dist/security/dep-confusion.d.ts.map +1 -0
- package/dist/security/dep-confusion.js +255 -0
- package/dist/security/dep-confusion.js.map +7 -0
- package/dist/security/errors.d.ts +16 -0
- package/dist/security/errors.d.ts.map +1 -0
- package/dist/security/errors.js +51 -0
- package/dist/security/errors.js.map +7 -0
- package/dist/security/export/fix-suggestions.d.ts +21 -0
- package/dist/security/export/fix-suggestions.d.ts.map +1 -0
- package/dist/security/export/fix-suggestions.js +58 -0
- package/dist/security/export/fix-suggestions.js.map +7 -0
- package/dist/security/export/sbom.d.ts +33 -0
- package/dist/security/export/sbom.d.ts.map +1 -0
- package/dist/security/export/sbom.js +230 -0
- package/dist/security/export/sbom.js.map +7 -0
- package/dist/security/export/serialization.d.ts +62 -0
- package/dist/security/export/serialization.d.ts.map +1 -0
- package/dist/security/export/serialization.js +142 -0
- package/dist/security/export/serialization.js.map +7 -0
- package/dist/security/export/vex.d.ts +58 -0
- package/dist/security/export/vex.d.ts.map +1 -0
- package/dist/security/export/vex.js +220 -0
- package/dist/security/export/vex.js.map +7 -0
- package/dist/security/index.d.ts +21 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +67 -0
- package/dist/security/index.js.map +7 -0
- package/dist/security/integrator.d.ts +63 -0
- package/dist/security/integrator.d.ts.map +1 -0
- package/dist/security/integrator.js +491 -0
- package/dist/security/integrator.js.map +7 -0
- package/dist/security/license.d.ts +33 -0
- package/dist/security/license.d.ts.map +1 -0
- package/dist/security/license.js +70 -0
- package/dist/security/license.js.map +7 -0
- package/dist/security/manifest/adapter.d.ts +92 -0
- package/dist/security/manifest/adapter.d.ts.map +1 -0
- package/dist/security/manifest/adapter.js +243 -0
- package/dist/security/manifest/adapter.js.map +7 -0
- package/dist/security/manifest/parser.d.ts +73 -0
- package/dist/security/manifest/parser.d.ts.map +1 -0
- package/dist/security/manifest/parser.js +376 -0
- package/dist/security/manifest/parser.js.map +7 -0
- package/dist/security/manifest/plugins/cargo.d.ts +11 -0
- package/dist/security/manifest/plugins/cargo.d.ts.map +1 -0
- package/dist/security/manifest/plugins/cargo.js +233 -0
- package/dist/security/manifest/plugins/cargo.js.map +7 -0
- package/dist/security/manifest/plugins/composer.d.ts +11 -0
- package/dist/security/manifest/plugins/composer.d.ts.map +1 -0
- package/dist/security/manifest/plugins/composer.js +152 -0
- package/dist/security/manifest/plugins/composer.js.map +7 -0
- package/dist/security/manifest/plugins/go.d.ts +12 -0
- package/dist/security/manifest/plugins/go.d.ts.map +1 -0
- package/dist/security/manifest/plugins/go.js +146 -0
- package/dist/security/manifest/plugins/go.js.map +7 -0
- package/dist/security/manifest/plugins/gradle.d.ts +12 -0
- package/dist/security/manifest/plugins/gradle.d.ts.map +1 -0
- package/dist/security/manifest/plugins/gradle.js +159 -0
- package/dist/security/manifest/plugins/gradle.js.map +7 -0
- package/dist/security/manifest/plugins/hex.d.ts +11 -0
- package/dist/security/manifest/plugins/hex.d.ts.map +1 -0
- package/dist/security/manifest/plugins/hex.js +159 -0
- package/dist/security/manifest/plugins/hex.js.map +7 -0
- package/dist/security/manifest/plugins/maven.d.ts +11 -0
- package/dist/security/manifest/plugins/maven.d.ts.map +1 -0
- package/dist/security/manifest/plugins/maven.js +128 -0
- package/dist/security/manifest/plugins/maven.js.map +7 -0
- package/dist/security/manifest/plugins/npm.d.ts +11 -0
- package/dist/security/manifest/plugins/npm.d.ts.map +1 -0
- package/dist/security/manifest/plugins/npm.js +284 -0
- package/dist/security/manifest/plugins/npm.js.map +7 -0
- package/dist/security/manifest/plugins/nuget.d.ts +12 -0
- package/dist/security/manifest/plugins/nuget.d.ts.map +1 -0
- package/dist/security/manifest/plugins/nuget.js +186 -0
- package/dist/security/manifest/plugins/nuget.js.map +7 -0
- package/dist/security/manifest/plugins/pip.d.ts +11 -0
- package/dist/security/manifest/plugins/pip.d.ts.map +1 -0
- package/dist/security/manifest/plugins/pip.js +123 -0
- package/dist/security/manifest/plugins/pip.js.map +7 -0
- package/dist/security/manifest/plugins/pubspec.d.ts +11 -0
- package/dist/security/manifest/plugins/pubspec.d.ts.map +1 -0
- package/dist/security/manifest/plugins/pubspec.js +212 -0
- package/dist/security/manifest/plugins/pubspec.js.map +7 -0
- package/dist/security/manifest/plugins/pyproject.d.ts +3 -0
- package/dist/security/manifest/plugins/pyproject.d.ts.map +1 -0
- package/dist/security/manifest/plugins/pyproject.js +261 -0
- package/dist/security/manifest/plugins/pyproject.js.map +7 -0
- package/dist/security/manifest/plugins/rubygems.d.ts +11 -0
- package/dist/security/manifest/plugins/rubygems.d.ts.map +1 -0
- package/dist/security/manifest/plugins/rubygems.js +185 -0
- package/dist/security/manifest/plugins/rubygems.js.map +7 -0
- package/dist/security/manifest/plugins/swift.d.ts +11 -0
- package/dist/security/manifest/plugins/swift.d.ts.map +1 -0
- package/dist/security/manifest/plugins/swift.js +200 -0
- package/dist/security/manifest/plugins/swift.js.map +7 -0
- package/dist/security/owasp.d.ts +16 -0
- package/dist/security/owasp.d.ts.map +1 -0
- package/dist/security/owasp.js +104 -0
- package/dist/security/owasp.js.map +7 -0
- package/dist/security/pipeline.d.ts +37 -0
- package/dist/security/pipeline.d.ts.map +1 -0
- package/dist/security/pipeline.js +125 -0
- package/dist/security/pipeline.js.map +7 -0
- package/dist/security/reachability.d.ts +67 -0
- package/dist/security/reachability.d.ts.map +1 -0
- package/dist/security/reachability.js +276 -0
- package/dist/security/reachability.js.map +7 -0
- package/dist/security/remediation.d.ts +51 -0
- package/dist/security/remediation.d.ts.map +1 -0
- package/dist/security/remediation.js +126 -0
- package/dist/security/remediation.js.map +7 -0
- package/dist/security/secrets.d.ts +38 -0
- package/dist/security/secrets.d.ts.map +1 -0
- package/dist/security/secrets.js +176 -0
- package/dist/security/secrets.js.map +7 -0
- package/dist/security/staleness.d.ts +43 -0
- package/dist/security/staleness.d.ts.map +1 -0
- package/dist/security/staleness.js +231 -0
- package/dist/security/staleness.js.map +7 -0
- package/dist/security/supply-chain.d.ts +34 -0
- package/dist/security/supply-chain.d.ts.map +1 -0
- package/dist/security/supply-chain.js +301 -0
- package/dist/security/supply-chain.js.map +7 -0
- package/dist/security/suppressions.d.ts +18 -0
- package/dist/security/suppressions.d.ts.map +1 -0
- package/dist/security/suppressions.js +106 -0
- package/dist/security/suppressions.js.map +7 -0
- package/dist/security/types.d.ts +144 -0
- package/dist/security/types.d.ts.map +1 -0
- package/dist/security/types.js +17 -0
- package/dist/security/types.js.map +7 -0
- package/dist/security/vuln/client.d.ts +90 -0
- package/dist/security/vuln/client.d.ts.map +1 -0
- package/dist/security/vuln/client.js +355 -0
- package/dist/security/vuln/client.js.map +7 -0
- package/dist/security/vuln/epss-client.d.ts +26 -0
- package/dist/security/vuln/epss-client.d.ts.map +1 -0
- package/dist/security/vuln/epss-client.js +95 -0
- package/dist/security/vuln/epss-client.js.map +7 -0
- package/dist/security/vuln/index.d.ts +6 -0
- package/dist/security/vuln/index.d.ts.map +1 -0
- package/dist/security/vuln/index.js +29 -0
- package/dist/security/vuln/index.js.map +7 -0
- package/dist/security/vuln/osv-adapter.d.ts +58 -0
- package/dist/security/vuln/osv-adapter.d.ts.map +1 -0
- package/dist/security/vuln/osv-adapter.js +321 -0
- package/dist/security/vuln/osv-adapter.js.map +7 -0
- package/dist/security/vuln/types.d.ts +29 -0
- package/dist/security/vuln/types.d.ts.map +1 -0
- package/dist/security/vuln/types.js +17 -0
- package/dist/security/vuln/types.js.map +7 -0
- package/dist/sync/index.d.ts +4 -2
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/types.d.ts +10 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +2 -2
- package/dist/vectors/index.d.ts +11 -3
- package/dist/vectors/index.d.ts.map +1 -1
- package/dist/vectors/qdrant-index.d.ts +10 -3
- package/dist/vectors/qdrant-index.d.ts.map +1 -1
- package/dist/vectors/typesense-index.d.ts.map +1 -1
- package/dist/watchmen/index.js +83 -0
- package/dist/watchmen/index.js.map +7 -0
- package/dist/watchmen/synthesize.js +475 -0
- package/dist/watchmen/synthesize.js.map +7 -0
- package/package.json +6 -2
package/dist/mcp/tools.js
CHANGED
|
@@ -29,6 +29,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
29
29
|
var tools_exports = {};
|
|
30
30
|
__export(tools_exports, {
|
|
31
31
|
KIROGRAPH_TOOL_NAMES: () => import_tool_names.KIROGRAPH_TOOL_NAMES,
|
|
32
|
+
LIVE_SEARCH_TOOL_DEFINITION: () => LIVE_SEARCH_TOOL_DEFINITION,
|
|
32
33
|
ToolHandler: () => ToolHandler,
|
|
33
34
|
tools: () => tools
|
|
34
35
|
});
|
|
@@ -46,6 +47,18 @@ const MAX_OUTPUT = 15e3;
|
|
|
46
47
|
function truncate(s) {
|
|
47
48
|
return s.length > MAX_OUTPUT ? s.slice(0, MAX_OUTPUT) + "\n\u2026[truncated]" : s;
|
|
48
49
|
}
|
|
50
|
+
function estimateFileTokens(projectRoot, filePaths) {
|
|
51
|
+
let total = 0;
|
|
52
|
+
for (const fp of filePaths) {
|
|
53
|
+
try {
|
|
54
|
+
const fullPath = path.isAbsolute(fp) ? fp : path.join(projectRoot, fp);
|
|
55
|
+
const stat = fs.statSync(fullPath);
|
|
56
|
+
total += Math.round(stat.size / 4);
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return total;
|
|
61
|
+
}
|
|
49
62
|
function clampLimit(value, defaultValue) {
|
|
50
63
|
const n = typeof value === "number" ? value : defaultValue;
|
|
51
64
|
return Math.max(1, Math.min(100, Math.round(n)));
|
|
@@ -357,6 +370,77 @@ const tools = [
|
|
|
357
370
|
}
|
|
358
371
|
}
|
|
359
372
|
},
|
|
373
|
+
{
|
|
374
|
+
name: "kirograph_read",
|
|
375
|
+
description: 'Read a file with caching and multiple modes. First read returns full content; subsequent reads of unchanged files return a compact "cached" marker (~13 tokens). Supports modes: full, map, signatures, diff, lines, imports, exports.',
|
|
376
|
+
inputSchema: {
|
|
377
|
+
type: "object",
|
|
378
|
+
properties: {
|
|
379
|
+
path: { type: "string", description: "File path to read (absolute or relative to project root)" },
|
|
380
|
+
mode: {
|
|
381
|
+
type: "string",
|
|
382
|
+
description: 'Read mode: "full" (default), "map" (structure overview), "signatures" (function signatures), "diff" (changes since last read), "lines" (line range), "imports", "exports"',
|
|
383
|
+
enum: ["full", "map", "signatures", "diff", "lines", "imports", "exports"],
|
|
384
|
+
default: "full"
|
|
385
|
+
},
|
|
386
|
+
start: { type: "number", description: "Start line (for lines mode)" },
|
|
387
|
+
end: { type: "number", description: "End line (for lines mode)" },
|
|
388
|
+
noCache: { type: "boolean", description: "Force fresh read, bypass cache (default: false)", default: false },
|
|
389
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
390
|
+
},
|
|
391
|
+
required: ["path"]
|
|
392
|
+
}
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
name: "kirograph_budget",
|
|
396
|
+
description: "Show current session context budget usage. Returns tokens consumed, remaining budget, and utilization percentage.",
|
|
397
|
+
inputSchema: {
|
|
398
|
+
type: "object",
|
|
399
|
+
properties: {
|
|
400
|
+
reset: { type: "boolean", description: "Reset session budget counters (default: false)", default: false },
|
|
401
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
name: "kirograph_flows",
|
|
407
|
+
description: "Trace execution flows from entry points (routes, handlers, main functions) through the call graph. Returns ordered call chains sorted by criticality.",
|
|
408
|
+
inputSchema: {
|
|
409
|
+
type: "object",
|
|
410
|
+
properties: {
|
|
411
|
+
entryPoint: { type: "string", description: "Symbol name to trace from, or omit to auto-detect entry points" },
|
|
412
|
+
maxFlows: { type: "number", description: "Max number of flows to return (default 10)" },
|
|
413
|
+
maxDepth: { type: "number", description: "Max call chain depth (default 10)" },
|
|
414
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
name: "kirograph_communities",
|
|
420
|
+
description: "Detect code communities (clusters of related symbols) using graph-based community detection. Shows which code belongs together and how communities are coupled.",
|
|
421
|
+
inputSchema: {
|
|
422
|
+
type: "object",
|
|
423
|
+
properties: {
|
|
424
|
+
resolution: { type: "number", description: "Resolution parameter (default 1.0, higher = more communities)" },
|
|
425
|
+
limit: { type: "number", description: "Max communities to return (default 15)" },
|
|
426
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
name: "kirograph_refactor",
|
|
432
|
+
description: 'Refactoring assistant. Use mode "rename" to preview all locations that reference a symbol (rename preview). Use mode "suggest" for community-driven refactoring suggestions.',
|
|
433
|
+
inputSchema: {
|
|
434
|
+
type: "object",
|
|
435
|
+
properties: {
|
|
436
|
+
mode: { type: "string", description: 'Mode: "rename" (preview references) or "suggest" (refactoring suggestions)', enum: ["rename", "suggest"] },
|
|
437
|
+
symbol: { type: "string", description: "Symbol name (required for rename mode)" },
|
|
438
|
+
limit: { type: "number", description: "Max results (default 10)" },
|
|
439
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
440
|
+
},
|
|
441
|
+
required: ["mode"]
|
|
442
|
+
}
|
|
443
|
+
},
|
|
360
444
|
// ── Memory tools (require enableMemory=true) ────────────────────────────────
|
|
361
445
|
{
|
|
362
446
|
name: "kirograph_mem_search",
|
|
@@ -372,6 +456,7 @@ const tools = [
|
|
|
372
456
|
},
|
|
373
457
|
limit: { type: "number", description: "Max results (default: 10)", default: 10 },
|
|
374
458
|
sessionId: { type: "string", description: "Filter to specific session" },
|
|
459
|
+
asOf: { type: "number", description: "Query facts valid at this timestamp (epoch ms). Filters out expired/superseded observations." },
|
|
375
460
|
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
376
461
|
},
|
|
377
462
|
required: ["query"]
|
|
@@ -586,8 +671,233 @@ const tools = [
|
|
|
586
671
|
},
|
|
587
672
|
required: ["dataset"]
|
|
588
673
|
}
|
|
674
|
+
},
|
|
675
|
+
// ── Security tools (require enableSecurity=true) ────────────────────────────
|
|
676
|
+
{
|
|
677
|
+
name: "kirograph_security",
|
|
678
|
+
description: "Security overview: vulnerability counts, affected/not_affected verdicts, stale data warnings. Requires enableSecurity=true and enableArchitecture=true.",
|
|
679
|
+
inputSchema: {
|
|
680
|
+
type: "object",
|
|
681
|
+
properties: {
|
|
682
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
name: "kirograph_vulns",
|
|
688
|
+
description: "List vulnerabilities with reachability verdicts, severity, and affected components. Supports on-demand refresh.",
|
|
689
|
+
inputSchema: {
|
|
690
|
+
type: "object",
|
|
691
|
+
properties: {
|
|
692
|
+
severity: { type: "string", description: "Filter by severity level", enum: ["critical", "high", "medium", "low"] },
|
|
693
|
+
verdict: { type: "string", description: "Filter by reachability verdict", enum: ["affected", "not_affected", "under_investigation"] },
|
|
694
|
+
limit: { type: "number", description: "Max results (default: 20)", default: 20 },
|
|
695
|
+
refresh: { type: "boolean", description: "Trigger fresh vulnerability enrichment before listing (default: false)", default: false },
|
|
696
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
name: "kirograph_vuln_add",
|
|
702
|
+
description: "Manually register a CVE against a dependency without querying external databases.",
|
|
703
|
+
inputSchema: {
|
|
704
|
+
type: "object",
|
|
705
|
+
properties: {
|
|
706
|
+
cveId: { type: "string", description: 'CVE identifier (e.g., "CVE-2023-12345")' },
|
|
707
|
+
package: { type: "string", description: "Package name to associate the CVE with" },
|
|
708
|
+
severity: { type: "number", description: "CVSS v3.1 base score (optional)" },
|
|
709
|
+
summary: { type: "string", description: "Vulnerability summary (optional)" },
|
|
710
|
+
fixedVersion: { type: "string", description: "Version that fixes the vulnerability (optional)" },
|
|
711
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
712
|
+
},
|
|
713
|
+
required: ["cveId", "package"]
|
|
714
|
+
}
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
name: "kirograph_vuln_suppress",
|
|
718
|
+
description: "Suppress a CVE so it no longer appears in vulnerability reports (mark as false positive or accepted risk). Suppressions are stored in .kirograph/security-suppressions.json.",
|
|
719
|
+
inputSchema: {
|
|
720
|
+
type: "object",
|
|
721
|
+
properties: {
|
|
722
|
+
cveId: { type: "string", description: 'CVE identifier to suppress (e.g., "CVE-2024-1234")' },
|
|
723
|
+
reason: { type: "string", description: "Reason for suppression (optional)" },
|
|
724
|
+
expires: { type: "string", description: 'Expiry date in ISO format after which the suppression is removed (e.g. "2026-12-31", optional)' },
|
|
725
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
726
|
+
},
|
|
727
|
+
required: ["cveId"]
|
|
728
|
+
}
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
name: "kirograph_sbom",
|
|
732
|
+
description: "Generate and return CycloneDX 1.5 SBOM JSON for the project.",
|
|
733
|
+
inputSchema: {
|
|
734
|
+
type: "object",
|
|
735
|
+
properties: {
|
|
736
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
name: "kirograph_vex",
|
|
742
|
+
description: "Generate and return CycloneDX 1.5 VEX JSON with reachability verdicts.",
|
|
743
|
+
inputSchema: {
|
|
744
|
+
type: "object",
|
|
745
|
+
properties: {
|
|
746
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
name: "kirograph_reachability",
|
|
752
|
+
description: "Check reachability for a specific dependency or vulnerability. Returns verdict, paths, and impact summary.",
|
|
753
|
+
inputSchema: {
|
|
754
|
+
type: "object",
|
|
755
|
+
properties: {
|
|
756
|
+
target: { type: "string", description: "Dependency name or CVE ID to check reachability for" },
|
|
757
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
758
|
+
},
|
|
759
|
+
required: ["target"]
|
|
760
|
+
}
|
|
761
|
+
},
|
|
762
|
+
{
|
|
763
|
+
name: "kirograph_staleness",
|
|
764
|
+
description: "Check dependency freshness \u2014 identifies packages significantly behind their latest published version.",
|
|
765
|
+
inputSchema: {
|
|
766
|
+
type: "object",
|
|
767
|
+
properties: {
|
|
768
|
+
threshold: { type: "number", description: "Only return packages with staleness_score >= threshold (default: 0.3)", default: 0.3 },
|
|
769
|
+
refresh: { type: "boolean", description: "Fetch latest version info from registries before listing (default: false)", default: false },
|
|
770
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
name: "kirograph_licenses",
|
|
776
|
+
description: "Show dependency licenses and check against the configured license policy (deny/warn lists). Supports wildcard patterns (e.g. GPL-* matches GPL-2.0, GPL-3.0-only).",
|
|
777
|
+
inputSchema: {
|
|
778
|
+
type: "object",
|
|
779
|
+
properties: {
|
|
780
|
+
policy: { type: "boolean", description: "Return only policy violations (default: false)", default: false },
|
|
781
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
name: "kirograph_attack_surface",
|
|
787
|
+
description: "Map the attack surface: all HTTP routes and their paths to vulnerable dependencies, with hop count, authentication status, and risk score.",
|
|
788
|
+
inputSchema: {
|
|
789
|
+
type: "object",
|
|
790
|
+
properties: {
|
|
791
|
+
limit: { type: "number", description: "Max routes to return (default: 20)", default: 20 },
|
|
792
|
+
publicOnly: { type: "boolean", description: "Only return public/unauthenticated routes", default: false },
|
|
793
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
},
|
|
797
|
+
{
|
|
798
|
+
name: "kirograph_secrets",
|
|
799
|
+
description: "Scan for hardcoded secrets and credentials, enriched with call-graph blast radius showing which entry points reach the secret.",
|
|
800
|
+
inputSchema: {
|
|
801
|
+
type: "object",
|
|
802
|
+
properties: {
|
|
803
|
+
includeTests: { type: "boolean", description: "Include test files in scan", default: false },
|
|
804
|
+
severity: { type: "string", description: "Filter by severity: critical, high, medium, low", enum: ["critical", "high", "medium", "low"] },
|
|
805
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
name: "kirograph_security_flows",
|
|
811
|
+
description: "SAST-lite: detect dangerous data flows (SQL injection, dangerous eval, unsafe deserialization, path traversal, weak crypto). Each finding tagged with OWASP Top 10 category.",
|
|
812
|
+
inputSchema: {
|
|
813
|
+
type: "object",
|
|
814
|
+
properties: {
|
|
815
|
+
type: { type: "string", description: "Filter by type: sql, eval, deserialize, path, crypto, all", default: "all" },
|
|
816
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
name: "kirograph_supply_chain",
|
|
822
|
+
description: "Supply chain health: OpenSSF Scorecard scores, maintainer count, abandoned package detection for project dependencies.",
|
|
823
|
+
inputSchema: {
|
|
824
|
+
type: "object",
|
|
825
|
+
properties: {
|
|
826
|
+
threshold: { type: "string", description: "Minimum risk level: critical, high, medium", enum: ["critical", "high", "medium"] },
|
|
827
|
+
refresh: { type: "boolean", description: "Re-fetch from APIs", default: false },
|
|
828
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
name: "kirograph_dep_confusion",
|
|
834
|
+
description: "Detect dependency confusion vulnerabilities: internal package names that exist in public registries (typosquatting/supply chain attack vectors).",
|
|
835
|
+
inputSchema: {
|
|
836
|
+
type: "object",
|
|
837
|
+
properties: {
|
|
838
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
name: "kirograph_remediation",
|
|
844
|
+
description: "Remediation SLA tracking: which vulnerabilities are overdue for fixing based on severity thresholds (critical=7d, high=30d, medium=90d).",
|
|
845
|
+
inputSchema: {
|
|
846
|
+
type: "object",
|
|
847
|
+
properties: {
|
|
848
|
+
overdueOnly: { type: "boolean", description: "Show only overdue items", default: false },
|
|
849
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
},
|
|
853
|
+
// ── Pattern tools (require enablePatterns=true) ─────────────────────────────
|
|
854
|
+
{
|
|
855
|
+
name: "kirograph_pattern_coverage",
|
|
856
|
+
description: "OWASP Top 10 coverage report: shows which vulnerability categories are covered by bundled pattern rules and how many matches exist in each.",
|
|
857
|
+
inputSchema: {
|
|
858
|
+
type: "object",
|
|
859
|
+
properties: {
|
|
860
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
name: "kirograph_pattern_save_baseline",
|
|
866
|
+
description: "Save current pattern match counts as a baseline for future diffing.",
|
|
867
|
+
inputSchema: {
|
|
868
|
+
type: "object",
|
|
869
|
+
properties: {
|
|
870
|
+
label: { type: "string", description: 'Baseline label (default: "default")', default: "default" },
|
|
871
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
},
|
|
875
|
+
{
|
|
876
|
+
name: "kirograph_pattern_diff",
|
|
877
|
+
description: "Diff current pattern matches against a saved baseline. Shows new findings, resolved findings, and unchanged counts.",
|
|
878
|
+
inputSchema: {
|
|
879
|
+
type: "object",
|
|
880
|
+
properties: {
|
|
881
|
+
label: { type: "string", description: 'Baseline label (default: "default")', default: "default" },
|
|
882
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
883
|
+
}
|
|
884
|
+
}
|
|
589
885
|
}
|
|
590
886
|
];
|
|
887
|
+
const LIVE_SEARCH_TOOL_DEFINITION = {
|
|
888
|
+
name: "kirograph_live_search",
|
|
889
|
+
description: "Run a live AST pattern search against the indexed file list using @ast-grep/napi. Finds structural code patterns (SQL injection, eval, path traversal, etc.) without needing the codebase to be re-indexed. Only available when enablePatterns: true.",
|
|
890
|
+
inputSchema: {
|
|
891
|
+
type: "object",
|
|
892
|
+
required: ["pattern", "language"],
|
|
893
|
+
properties: {
|
|
894
|
+
pattern: { type: "string", description: 'ast-grep inline pattern (e.g. "eval($X)")' },
|
|
895
|
+
language: { type: "string", description: "Language to search: javascript, typescript, python, go, rust, java, etc." },
|
|
896
|
+
limit: { type: "number", description: "Max results (default 20, max 100)", default: 20 },
|
|
897
|
+
projectPath: { type: "string", description: "Project root path (optional)" }
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
};
|
|
591
901
|
class ToolHandler {
|
|
592
902
|
constructor(cg) {
|
|
593
903
|
this.connections = /* @__PURE__ */ new Map();
|
|
@@ -639,6 +949,7 @@ class ToolHandler {
|
|
|
639
949
|
async getConnection(projectPath) {
|
|
640
950
|
if (!projectPath) return this.defaultCg;
|
|
641
951
|
const resolved = path.resolve(projectPath);
|
|
952
|
+
if (!path.isAbsolute(resolved)) return null;
|
|
642
953
|
if (this.connections.has(resolved)) return this.connections.get(resolved);
|
|
643
954
|
try {
|
|
644
955
|
const cg = await import__.default.open(resolved);
|
|
@@ -769,6 +1080,69 @@ class ToolHandler {
|
|
|
769
1080
|
}
|
|
770
1081
|
return lines.join("\n");
|
|
771
1082
|
}
|
|
1083
|
+
if (toolName === "kirograph_read") {
|
|
1084
|
+
const filePath = args.path;
|
|
1085
|
+
if (!filePath) return "Error: path is required.";
|
|
1086
|
+
const projectRoot = args.projectPath || process.cwd();
|
|
1087
|
+
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.join(projectRoot, filePath);
|
|
1088
|
+
const mode = args.mode ?? "full";
|
|
1089
|
+
const noCache = args.noCache ?? false;
|
|
1090
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
1091
|
+
return `Error: File not found: ${resolvedPath}`;
|
|
1092
|
+
}
|
|
1093
|
+
const { getFileReadCache } = await Promise.resolve().then(() => require("./cache.js"));
|
|
1094
|
+
const { executeReadMode } = await Promise.resolve().then(() => require("./read-modes.js"));
|
|
1095
|
+
const cache = getFileReadCache();
|
|
1096
|
+
if (mode !== "full") {
|
|
1097
|
+
let cg2 = null;
|
|
1098
|
+
try {
|
|
1099
|
+
cg2 = await this.getConnection(args.projectPath);
|
|
1100
|
+
} catch {
|
|
1101
|
+
}
|
|
1102
|
+
const result2 = executeReadMode({
|
|
1103
|
+
mode,
|
|
1104
|
+
filePath: resolvedPath,
|
|
1105
|
+
start: args.start,
|
|
1106
|
+
end: args.end,
|
|
1107
|
+
cg: cg2
|
|
1108
|
+
});
|
|
1109
|
+
cache.read(resolvedPath, true);
|
|
1110
|
+
return result2.content;
|
|
1111
|
+
}
|
|
1112
|
+
const result = cache.read(resolvedPath, noCache);
|
|
1113
|
+
if (result.cached) {
|
|
1114
|
+
return result.content;
|
|
1115
|
+
}
|
|
1116
|
+
if (result.changed) {
|
|
1117
|
+
return `[file changed since last read]
|
|
1118
|
+
|
|
1119
|
+
${result.content}`;
|
|
1120
|
+
}
|
|
1121
|
+
return result.content;
|
|
1122
|
+
}
|
|
1123
|
+
if (toolName === "kirograph_budget") {
|
|
1124
|
+
const projectRoot = args.projectPath || process.cwd();
|
|
1125
|
+
const reset = args.reset ?? false;
|
|
1126
|
+
const { BudgetTracker } = await Promise.resolve().then(() => require("../compression/tracker.js"));
|
|
1127
|
+
const budget = BudgetTracker.getInstance(projectRoot);
|
|
1128
|
+
if (reset) {
|
|
1129
|
+
budget.reset();
|
|
1130
|
+
return "Context budget counters reset.";
|
|
1131
|
+
}
|
|
1132
|
+
const status = budget.getStatus();
|
|
1133
|
+
const lines = [
|
|
1134
|
+
"Context Budget:",
|
|
1135
|
+
` Tokens consumed: ${status.consumed.toLocaleString()}`,
|
|
1136
|
+
` Budget limit: ${status.limit > 0 ? status.limit.toLocaleString() : "unlimited"}`,
|
|
1137
|
+
` Remaining: ${status.limit > 0 ? status.remaining.toLocaleString() : "\u221E"}`,
|
|
1138
|
+
` Utilization: ${status.utilization}%`
|
|
1139
|
+
];
|
|
1140
|
+
if (status.warning) {
|
|
1141
|
+
lines.push(`
|
|
1142
|
+
\u26A0 ${status.warning}`);
|
|
1143
|
+
}
|
|
1144
|
+
return lines.join("\n");
|
|
1145
|
+
}
|
|
772
1146
|
const cg = await this.getConnection(args.projectPath);
|
|
773
1147
|
if (!cg) return "KiroGraph not initialized. Run `kirograph init` in your project first.";
|
|
774
1148
|
writeSessionMarker(cg.getProjectRoot());
|
|
@@ -906,6 +1280,83 @@ class ToolHandler {
|
|
|
906
1280
|
}
|
|
907
1281
|
} catch {
|
|
908
1282
|
}
|
|
1283
|
+
try {
|
|
1284
|
+
const projectRootSec = cg.getProjectRoot();
|
|
1285
|
+
const configSec = await (await Promise.resolve().then(() => require("../config.js"))).loadConfig(projectRootSec);
|
|
1286
|
+
if (configSec.enableSecurity) {
|
|
1287
|
+
const dbSec = cg.getDatabase();
|
|
1288
|
+
dbSec.applySecuritySchema();
|
|
1289
|
+
const rawDbSec = dbSec.getRawDb();
|
|
1290
|
+
const contextNodeIds = [
|
|
1291
|
+
...ctx.entryPoints.map((n) => n.id),
|
|
1292
|
+
...ctx.relatedNodes.map((n) => n.id)
|
|
1293
|
+
].filter(Boolean);
|
|
1294
|
+
if (contextNodeIds.length > 0) {
|
|
1295
|
+
const { getSecurityWarningsForNodes, formatSecurityWarnings } = await Promise.resolve().then(() => require("../security/context-warnings.js"));
|
|
1296
|
+
const warnings = getSecurityWarningsForNodes(rawDbSec, contextNodeIds);
|
|
1297
|
+
if (warnings.length > 0) {
|
|
1298
|
+
const nodeNames = /* @__PURE__ */ new Map();
|
|
1299
|
+
for (const n of ctx.entryPoints) {
|
|
1300
|
+
nodeNames.set(n.id, n.name);
|
|
1301
|
+
}
|
|
1302
|
+
for (const n of ctx.relatedNodes) {
|
|
1303
|
+
nodeNames.set(n.id, n.name);
|
|
1304
|
+
}
|
|
1305
|
+
const secSection = formatSecurityWarnings(warnings, nodeNames);
|
|
1306
|
+
if (secSection) {
|
|
1307
|
+
lines.push(secSection);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
} catch {
|
|
1313
|
+
}
|
|
1314
|
+
try {
|
|
1315
|
+
const projectRootPat = cg.getProjectRoot();
|
|
1316
|
+
const configPat = await (await Promise.resolve().then(() => require("../config.js"))).loadConfig(projectRootPat);
|
|
1317
|
+
if (configPat.enablePatterns) {
|
|
1318
|
+
const dbPat = cg.getDatabase();
|
|
1319
|
+
dbPat.applyPatternsSchema();
|
|
1320
|
+
const rawDbPat = dbPat.getRawDb();
|
|
1321
|
+
const tableExists = rawDbPat.get("SELECT name FROM sqlite_master WHERE type='table' AND name='pattern_matches'");
|
|
1322
|
+
if (tableExists) {
|
|
1323
|
+
const contextFiles = [
|
|
1324
|
+
...ctx.entryPoints.map((n) => n.filePath),
|
|
1325
|
+
...ctx.relatedNodes.map((n) => n.filePath)
|
|
1326
|
+
].filter(Boolean);
|
|
1327
|
+
const uniqueContextFiles = [...new Set(contextFiles)];
|
|
1328
|
+
if (uniqueContextFiles.length > 0) {
|
|
1329
|
+
const placeholders = uniqueContextFiles.map(() => "?").join(",");
|
|
1330
|
+
const patternRows = rawDbPat.all(
|
|
1331
|
+
`SELECT file_path, pattern_id, severity, owasp_category, line FROM pattern_matches WHERE file_path IN (${placeholders}) ORDER BY severity DESC LIMIT 5`,
|
|
1332
|
+
uniqueContextFiles
|
|
1333
|
+
);
|
|
1334
|
+
if (patternRows.length > 0) {
|
|
1335
|
+
const patternWarnings = patternRows.map(
|
|
1336
|
+
(p) => `- **${p.pattern_id}** [${p.severity.toUpperCase()}/${p.owasp_category}]: ${p.file_path}:${p.line}`
|
|
1337
|
+
).join("\n");
|
|
1338
|
+
lines.push("", "## \u26A0 Pattern Findings", "", patternWarnings);
|
|
1339
|
+
if (patternRows.length === 5) lines.push("", "More findings \u2014 run `kirograph pattern --list` to see all rules.");
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
} catch {
|
|
1345
|
+
}
|
|
1346
|
+
const graphTokens = lines.join("\n").length / 4;
|
|
1347
|
+
const uniqueFiles = /* @__PURE__ */ new Set([
|
|
1348
|
+
...ctx.entryPoints.map((n) => n.filePath),
|
|
1349
|
+
...ctx.relatedNodes.map((n) => n.filePath)
|
|
1350
|
+
]);
|
|
1351
|
+
if (uniqueFiles.size > 0) {
|
|
1352
|
+
const naiveTokens = estimateFileTokens(cg.getProjectRoot(), [...uniqueFiles]);
|
|
1353
|
+
if (naiveTokens > 0) {
|
|
1354
|
+
const savingsPct = Math.round((1 - graphTokens / naiveTokens) * 100);
|
|
1355
|
+
if (savingsPct > 0) {
|
|
1356
|
+
lines.push("", `---`, `Context savings: ~${Math.round(graphTokens)} tokens (graph) vs ~${naiveTokens} tokens (full files) \u2014 ${savingsPct}% reduction`);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
909
1360
|
return lines.join("\n");
|
|
910
1361
|
}
|
|
911
1362
|
case "kirograph_callers": {
|
|
@@ -968,6 +1419,27 @@ class ToolHandler {
|
|
|
968
1419
|
}
|
|
969
1420
|
} catch {
|
|
970
1421
|
}
|
|
1422
|
+
try {
|
|
1423
|
+
const rawDbImp = cg.getDatabase().getRawDb();
|
|
1424
|
+
const tableExistsImp = rawDbImp.get("SELECT name FROM sqlite_master WHERE type='table' AND name='pattern_matches'");
|
|
1425
|
+
if (tableExistsImp) {
|
|
1426
|
+
const symNode = rawDbImp.get("SELECT id FROM nodes WHERE name = ? LIMIT 1", [args.symbol]);
|
|
1427
|
+
if (symNode) {
|
|
1428
|
+
const patternMatches = rawDbImp.all(
|
|
1429
|
+
"SELECT pattern_id, severity, line FROM pattern_matches WHERE symbol_node_id = ?",
|
|
1430
|
+
[symNode.id]
|
|
1431
|
+
);
|
|
1432
|
+
if (patternMatches.length > 0) {
|
|
1433
|
+
output += "\n\n\u26A0 This symbol has pattern matches:";
|
|
1434
|
+
for (const pm of patternMatches) {
|
|
1435
|
+
output += `
|
|
1436
|
+
[${pm.severity.toUpperCase()}] ${pm.pattern_id} at line ${pm.line}`;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
} catch {
|
|
1442
|
+
}
|
|
971
1443
|
return output;
|
|
972
1444
|
}
|
|
973
1445
|
case "kirograph_node": {
|
|
@@ -1035,6 +1507,41 @@ class ToolHandler {
|
|
|
1035
1507
|
}
|
|
1036
1508
|
} catch {
|
|
1037
1509
|
}
|
|
1510
|
+
let securityLine = " Security: disabled";
|
|
1511
|
+
try {
|
|
1512
|
+
const { loadConfig: loadCfgSec } = await Promise.resolve().then(() => require("../config.js"));
|
|
1513
|
+
const cfgSec = await loadCfgSec(cg.getProjectRoot());
|
|
1514
|
+
if (cfgSec.enableSecurity) {
|
|
1515
|
+
const db = cg.getDatabase();
|
|
1516
|
+
db.applySecuritySchema();
|
|
1517
|
+
const rawDb = db.getRawDb();
|
|
1518
|
+
const depCount = rawDb.get("SELECT COUNT(*) as cnt FROM sec_dependencies")?.cnt ?? 0;
|
|
1519
|
+
const vulnCount = rawDb.get("SELECT COUNT(*) as cnt FROM sec_vulnerabilities")?.cnt ?? 0;
|
|
1520
|
+
const affectedCount = rawDb.get("SELECT COUNT(*) as cnt FROM sec_reachability WHERE verdict = 'affected'")?.cnt ?? 0;
|
|
1521
|
+
const staleCount = rawDb.get("SELECT COUNT(*) as cnt FROM sec_dependencies WHERE vuln_data_stale = 1")?.cnt ?? 0;
|
|
1522
|
+
const staleNote = staleCount > 0 ? ` \u26A0 ${staleCount} stale` : "";
|
|
1523
|
+
securityLine = ` Security: enabled \u2014 ${depCount} deps, ${vulnCount} vulns (${affectedCount} affected)${staleNote}`;
|
|
1524
|
+
}
|
|
1525
|
+
} catch {
|
|
1526
|
+
}
|
|
1527
|
+
let patternsLine = " Patterns: disabled";
|
|
1528
|
+
try {
|
|
1529
|
+
const { loadConfig: loadCfgPat } = await Promise.resolve().then(() => require("../config.js"));
|
|
1530
|
+
const cfgP = await loadCfgPat(cg.getProjectRoot());
|
|
1531
|
+
if (cfgP.enablePatterns) {
|
|
1532
|
+
const rawDbP = cg.getDatabase().getRawDb();
|
|
1533
|
+
const tableExistsP = rawDbP.get("SELECT name FROM sqlite_master WHERE type='table' AND name='pattern_matches'");
|
|
1534
|
+
if (tableExistsP) {
|
|
1535
|
+
const matchCount = rawDbP.get("SELECT COUNT(*) as cnt FROM pattern_matches")?.cnt ?? 0;
|
|
1536
|
+
const fileCount = rawDbP.get("SELECT COUNT(DISTINCT file_path) as cnt FROM pattern_matches")?.cnt ?? 0;
|
|
1537
|
+
const ruleCount = rawDbP.get("SELECT COUNT(DISTINCT pattern_id) as cnt FROM pattern_matches")?.cnt ?? 0;
|
|
1538
|
+
patternsLine = ` Patterns: enabled \u2014 ${matchCount} matches, ${ruleCount} rules triggered, ${fileCount} files`;
|
|
1539
|
+
} else {
|
|
1540
|
+
patternsLine = ` Patterns: enabled (no data yet \u2014 run kirograph index)`;
|
|
1541
|
+
}
|
|
1542
|
+
}
|
|
1543
|
+
} catch {
|
|
1544
|
+
}
|
|
1038
1545
|
const threshold = stats.syncWarningThreshold ?? 10;
|
|
1039
1546
|
const pendingFiles = stats.pendingFiles ?? 0;
|
|
1040
1547
|
const syncRunning = stats.syncRunning ?? false;
|
|
@@ -1065,6 +1572,8 @@ class ToolHandler {
|
|
|
1065
1572
|
archLine,
|
|
1066
1573
|
docsLine,
|
|
1067
1574
|
dataLine,
|
|
1575
|
+
securityLine,
|
|
1576
|
+
patternsLine,
|
|
1068
1577
|
` DB size: ${dbMb} MB`,
|
|
1069
1578
|
...semanticLines,
|
|
1070
1579
|
...syncLines,
|
|
@@ -1386,9 +1895,125 @@ class ToolHandler {
|
|
|
1386
1895
|
}
|
|
1387
1896
|
return lines.join("\n");
|
|
1388
1897
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
const
|
|
1898
|
+
case "kirograph_flows": {
|
|
1899
|
+
const { getExecutionFlows, traceFlow, detectEntryPoints } = await Promise.resolve().then(() => require("../graph/flows.js"));
|
|
1900
|
+
const db = cg.getDatabase();
|
|
1901
|
+
if (args.entryPoint) {
|
|
1902
|
+
const results = cg.searchNodes(args.entryPoint, void 0, 5);
|
|
1903
|
+
if (results.length === 0) return `Symbol "${args.entryPoint}" not found in index.`;
|
|
1904
|
+
const hops = traceFlow(db, results[0].node.id, args.maxDepth ?? 10);
|
|
1905
|
+
if (hops.length < 2) return `No outgoing call chain found from "${args.entryPoint}".`;
|
|
1906
|
+
const lines2 = [`## Execution flow from \`${args.entryPoint}\``, ""];
|
|
1907
|
+
for (let i = 0; i < hops.length; i++) {
|
|
1908
|
+
const hop = hops[i];
|
|
1909
|
+
const indent = " ".repeat(i);
|
|
1910
|
+
const arrow = i === 0 ? "\u2192" : "\u21B3";
|
|
1911
|
+
const conf = hop.confidence && hop.confidence !== "extracted" ? ` [${hop.confidence}]` : "";
|
|
1912
|
+
lines2.push(`${indent}${arrow} ${hop.kind} \`${hop.symbol}\` \u2014 ${hop.filePath}:${hop.line}${conf}`);
|
|
1913
|
+
}
|
|
1914
|
+
return lines2.join("\n");
|
|
1915
|
+
}
|
|
1916
|
+
const flows = getExecutionFlows(db, {
|
|
1917
|
+
maxFlows: args.maxFlows ?? 10,
|
|
1918
|
+
maxDepth: args.maxDepth ?? 10
|
|
1919
|
+
});
|
|
1920
|
+
if (flows.length === 0) return "No execution flows detected. The graph may be too small or have no call edges.";
|
|
1921
|
+
const lines = [`## Execution Flows (${flows.length} detected)`, ""];
|
|
1922
|
+
for (const flow of flows) {
|
|
1923
|
+
lines.push(`### \`${flow.entryPoint}\` (${flow.entryPointKind}) \u2014 criticality: ${flow.criticality.toFixed(2)}`);
|
|
1924
|
+
lines.push(`File: ${flow.entryPointFile}`, "");
|
|
1925
|
+
for (let i = 0; i < flow.hops.length; i++) {
|
|
1926
|
+
const hop = flow.hops[i];
|
|
1927
|
+
const indent = " ".repeat(Math.min(i, 5));
|
|
1928
|
+
const arrow = i === 0 ? "\u2192" : "\u21B3";
|
|
1929
|
+
const conf = hop.confidence && hop.confidence !== "extracted" ? ` [${hop.confidence}]` : "";
|
|
1930
|
+
lines.push(`${indent}${arrow} \`${hop.symbol}\` (${hop.kind}) \u2014 ${hop.filePath}:${hop.line}${conf}`);
|
|
1931
|
+
}
|
|
1932
|
+
lines.push("");
|
|
1933
|
+
}
|
|
1934
|
+
return lines.join("\n");
|
|
1935
|
+
}
|
|
1936
|
+
case "kirograph_communities": {
|
|
1937
|
+
const { detectCommunities } = await Promise.resolve().then(() => require("../graph/communities.js"));
|
|
1938
|
+
const db = cg.getDatabase();
|
|
1939
|
+
const result = detectCommunities(db, {
|
|
1940
|
+
resolution: args.resolution ?? 1
|
|
1941
|
+
});
|
|
1942
|
+
if (result.communities.length === 0) return "No communities detected. The graph may be too small or have no edges.";
|
|
1943
|
+
const limit = Math.min(args.limit ?? 15, result.communities.length);
|
|
1944
|
+
const lines = [
|
|
1945
|
+
`## Communities (${result.communities.length} detected, modularity: ${result.modularity.toFixed(3)})`,
|
|
1946
|
+
`Graph: ${result.totalNodes} nodes, ${result.totalEdges} edges`,
|
|
1947
|
+
""
|
|
1948
|
+
];
|
|
1949
|
+
for (const c of result.communities.slice(0, limit)) {
|
|
1950
|
+
lines.push(`### ${c.label} (${c.memberCount} symbols)`);
|
|
1951
|
+
lines.push(`- Directory: \`${c.dominantDirectory}\``);
|
|
1952
|
+
lines.push(`- Language: ${c.dominantLanguage}`);
|
|
1953
|
+
lines.push(`- Inter-community edges: ${c.interCommunityEdges}`);
|
|
1954
|
+
lines.push(`- Top members:`);
|
|
1955
|
+
for (const m of c.members.slice(0, 8)) {
|
|
1956
|
+
lines.push(` - ${m.kind} \`${m.name}\` \u2014 ${m.filePath}`);
|
|
1957
|
+
}
|
|
1958
|
+
if (c.memberCount > 8) lines.push(` - \u2026and ${c.memberCount - 8} more`);
|
|
1959
|
+
lines.push("");
|
|
1960
|
+
}
|
|
1961
|
+
return lines.join("\n");
|
|
1962
|
+
}
|
|
1963
|
+
case "kirograph_refactor": {
|
|
1964
|
+
const { renamePreview, suggestRefactorings } = await Promise.resolve().then(() => require("../graph/refactor.js"));
|
|
1965
|
+
const db = cg.getDatabase();
|
|
1966
|
+
const mode = args.mode;
|
|
1967
|
+
if (mode === "rename") {
|
|
1968
|
+
if (!args.symbol) return 'Error: "symbol" parameter is required for rename mode.';
|
|
1969
|
+
const preview = renamePreview(db, args.symbol);
|
|
1970
|
+
if (!preview) return `Symbol "${args.symbol}" not found in index.`;
|
|
1971
|
+
const lines = [
|
|
1972
|
+
`## Rename Preview: \`${preview.symbol}\``,
|
|
1973
|
+
`Kind: ${preview.kind}`,
|
|
1974
|
+
`Defined at: ${preview.filePath}:${preview.line}`,
|
|
1975
|
+
`Total references: ${preview.totalReferences}`,
|
|
1976
|
+
""
|
|
1977
|
+
];
|
|
1978
|
+
if (preview.references.length === 0) {
|
|
1979
|
+
lines.push("No references found \u2014 this symbol can be safely renamed without affecting other code.");
|
|
1980
|
+
} else {
|
|
1981
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
1982
|
+
for (const ref of preview.references) {
|
|
1983
|
+
if (!byFile.has(ref.filePath)) byFile.set(ref.filePath, []);
|
|
1984
|
+
byFile.get(ref.filePath).push(ref);
|
|
1985
|
+
}
|
|
1986
|
+
for (const [file, refs] of byFile) {
|
|
1987
|
+
lines.push(`### ${file} (${refs.length} references)`);
|
|
1988
|
+
for (const ref of refs.slice(0, 10)) {
|
|
1989
|
+
lines.push(`- Line ${ref.line}: \`${ref.context}\` (${ref.edgeKind})`);
|
|
1990
|
+
}
|
|
1991
|
+
if (refs.length > 10) lines.push(` \u2026and ${refs.length - 10} more`);
|
|
1992
|
+
lines.push("");
|
|
1993
|
+
}
|
|
1994
|
+
}
|
|
1995
|
+
return lines.join("\n");
|
|
1996
|
+
}
|
|
1997
|
+
if (mode === "suggest") {
|
|
1998
|
+
const suggestions = suggestRefactorings(db, args.limit ?? 10);
|
|
1999
|
+
if (suggestions.length === 0) return "No refactoring suggestions \u2014 the codebase structure looks clean.";
|
|
2000
|
+
const lines = [`## Refactoring Suggestions (${suggestions.length})`, ""];
|
|
2001
|
+
for (const s of suggestions) {
|
|
2002
|
+
const icon = s.priority === "high" ? "\u{1F534}" : s.priority === "medium" ? "\u{1F7E1}" : "\u{1F7E2}";
|
|
2003
|
+
lines.push(`${icon} **${s.type}** [${s.priority}]: ${s.description}`);
|
|
2004
|
+
lines.push(` Rationale: ${s.rationale}`);
|
|
2005
|
+
if (s.symbols.length > 0) {
|
|
2006
|
+
lines.push(` Symbols: ${s.symbols.slice(0, 3).join(", ")}`);
|
|
2007
|
+
}
|
|
2008
|
+
lines.push("");
|
|
2009
|
+
}
|
|
2010
|
+
return lines.join("\n");
|
|
2011
|
+
}
|
|
2012
|
+
return 'Unknown mode. Use "rename" or "suggest".';
|
|
2013
|
+
}
|
|
2014
|
+
// ── Memory tools ────────────────────────────────────────────────────────
|
|
2015
|
+
case "kirograph_mem_search": {
|
|
2016
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
1392
2017
|
const projectRoot = cg.getProjectRoot();
|
|
1393
2018
|
const config = await loadConfig(projectRoot);
|
|
1394
2019
|
if (!config.enableMemory) return "Memory is not enabled. Set enableMemory: true in .kirograph/config.json";
|
|
@@ -1400,7 +2025,8 @@ class ToolHandler {
|
|
|
1400
2025
|
const results = await mem.search(args.query, {
|
|
1401
2026
|
limit: args.limit ?? 10,
|
|
1402
2027
|
kind: args.kind,
|
|
1403
|
-
sessionId: args.sessionId
|
|
2028
|
+
sessionId: args.sessionId,
|
|
2029
|
+
asOf: args.asOf
|
|
1404
2030
|
});
|
|
1405
2031
|
if (results.length === 0) return `No memory observations found for "${args.query}".`;
|
|
1406
2032
|
return results.map((r, i) => {
|
|
@@ -1830,6 +2456,935 @@ ${lines.join("\n")}`;
|
|
|
1830
2456
|
|
|
1831
2457
|
${lines.join("\n")}`;
|
|
1832
2458
|
}
|
|
2459
|
+
// ── Security tools (require enableSecurity=true) ──────────────────────────
|
|
2460
|
+
case "kirograph_security": {
|
|
2461
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
2462
|
+
const projectRoot = cg.getProjectRoot();
|
|
2463
|
+
const config = await loadConfig(projectRoot);
|
|
2464
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
2465
|
+
if (!config.enableArchitecture) return "Security requires architecture analysis. Set enableArchitecture: true in .kirograph/config.json and run kirograph index.";
|
|
2466
|
+
const db = cg.getDatabase();
|
|
2467
|
+
db.applySecuritySchema();
|
|
2468
|
+
const rawDb = db.getRawDb();
|
|
2469
|
+
const depCount = rawDb.get(
|
|
2470
|
+
`SELECT COUNT(*) as count FROM sec_dependencies`
|
|
2471
|
+
) ?? { count: 0 };
|
|
2472
|
+
const vulnCount = rawDb.get(
|
|
2473
|
+
`SELECT COUNT(*) as count FROM sec_vulnerabilities`
|
|
2474
|
+
) ?? { count: 0 };
|
|
2475
|
+
const verdictRows = rawDb.all(
|
|
2476
|
+
`SELECT verdict, COUNT(*) as count FROM sec_reachability GROUP BY verdict`
|
|
2477
|
+
);
|
|
2478
|
+
const verdicts = {};
|
|
2479
|
+
for (const row of verdictRows) {
|
|
2480
|
+
verdicts[row.verdict] = row.count;
|
|
2481
|
+
}
|
|
2482
|
+
const staleCount = rawDb.get(
|
|
2483
|
+
`SELECT COUNT(*) as count FROM sec_dependencies WHERE vuln_data_stale = 1`
|
|
2484
|
+
) ?? { count: 0 };
|
|
2485
|
+
const { SuppressionManager: SecSuppressionManager } = await Promise.resolve().then(() => require("../security/suppressions.js"));
|
|
2486
|
+
const secSuppressions = new SecSuppressionManager(projectRoot);
|
|
2487
|
+
const allCveIds = rawDb.all(`SELECT cve_id FROM sec_vulnerabilities`);
|
|
2488
|
+
const suppressedCveCount = allCveIds.filter((r) => secSuppressions.isSuppressed(r.cve_id)).length;
|
|
2489
|
+
const visibleVulnCount = vulnCount.count - suppressedCveCount;
|
|
2490
|
+
const lines = [
|
|
2491
|
+
"# Security Overview",
|
|
2492
|
+
"",
|
|
2493
|
+
`Dependencies: ${depCount.count}`,
|
|
2494
|
+
`Vulnerabilities: ${visibleVulnCount}${suppressedCveCount > 0 ? ` (${suppressedCveCount} suppressed)` : ""}`
|
|
2495
|
+
];
|
|
2496
|
+
if (visibleVulnCount > 0) {
|
|
2497
|
+
const affected = verdicts["affected"] ?? 0;
|
|
2498
|
+
const notAffected = verdicts["not_affected"] ?? 0;
|
|
2499
|
+
const underInvestigation = verdicts["under_investigation"] ?? 0;
|
|
2500
|
+
const pending = visibleVulnCount - affected - notAffected - underInvestigation;
|
|
2501
|
+
lines.push("", "## Reachability Verdicts", "");
|
|
2502
|
+
if (affected > 0) lines.push(`\u25CF Affected: ${affected}`);
|
|
2503
|
+
if (notAffected > 0) lines.push(`\u25CF Not affected: ${notAffected}`);
|
|
2504
|
+
if (underInvestigation > 0) lines.push(`\u25CF Under investigation: ${underInvestigation}`);
|
|
2505
|
+
if (pending > 0) lines.push(`\u25CF Pending analysis: ${pending}`);
|
|
2506
|
+
}
|
|
2507
|
+
if (staleCount.count > 0) {
|
|
2508
|
+
lines.push("", `\u26A0 ${staleCount.count} dependenc${staleCount.count === 1 ? "y has" : "ies have"} stale vulnerability data. Use kirograph_vulns with refresh=true to update.`);
|
|
2509
|
+
}
|
|
2510
|
+
const lastVulnCheck = rawDb.get(
|
|
2511
|
+
`SELECT MIN(last_vuln_check) as oldest FROM sec_dependencies WHERE last_vuln_check IS NOT NULL`
|
|
2512
|
+
)?.oldest;
|
|
2513
|
+
if (lastVulnCheck != null) {
|
|
2514
|
+
const ageMs = Date.now() - lastVulnCheck;
|
|
2515
|
+
const ageDays = ageMs / (1e3 * 60 * 60 * 24);
|
|
2516
|
+
const maxAge = config.securityEnrichMaxAgeDays ?? 7;
|
|
2517
|
+
if (ageDays > maxAge) {
|
|
2518
|
+
lines.push("", `\u26A0 Vulnerability data is ${Math.floor(ageDays)} days old (max: ${maxAge}). Run kirograph vulns --refresh to update.`);
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
try {
|
|
2522
|
+
const rawDbSec = db.getRawDb();
|
|
2523
|
+
const tableExistsSec = rawDbSec.get("SELECT name FROM sqlite_master WHERE type='table' AND name='pattern_matches'");
|
|
2524
|
+
if (tableExistsSec) {
|
|
2525
|
+
const patternCount = rawDbSec.get("SELECT COUNT(*) as cnt FROM pattern_matches")?.cnt ?? 0;
|
|
2526
|
+
if (patternCount > 0) {
|
|
2527
|
+
const critCount = rawDbSec.get("SELECT COUNT(*) as cnt FROM pattern_matches WHERE severity = 'critical'")?.cnt ?? 0;
|
|
2528
|
+
lines.push(`SAST findings: ${patternCount} pattern matches (${critCount} critical)`);
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
} catch {
|
|
2532
|
+
}
|
|
2533
|
+
return lines.join("\n");
|
|
2534
|
+
}
|
|
2535
|
+
case "kirograph_vulns": {
|
|
2536
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
2537
|
+
const projectRoot = cg.getProjectRoot();
|
|
2538
|
+
const config = await loadConfig(projectRoot);
|
|
2539
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
2540
|
+
const db = cg.getDatabase();
|
|
2541
|
+
db.applySecuritySchema();
|
|
2542
|
+
const rawDb = db.getRawDb();
|
|
2543
|
+
if (args.refresh === true) {
|
|
2544
|
+
const { OsvAdapter } = await Promise.resolve().then(() => require("../security/vuln/osv-adapter.js"));
|
|
2545
|
+
const { VulnerabilityDatabaseClient } = await Promise.resolve().then(() => require("../security/vuln/client.js"));
|
|
2546
|
+
const adapters = config.securityDatabases.map((dbName) => {
|
|
2547
|
+
if (dbName.toUpperCase() === "OSV") return new OsvAdapter();
|
|
2548
|
+
return null;
|
|
2549
|
+
}).filter(Boolean);
|
|
2550
|
+
const client = new VulnerabilityDatabaseClient(adapters, db);
|
|
2551
|
+
await client.enrichAll();
|
|
2552
|
+
}
|
|
2553
|
+
let query = `
|
|
2554
|
+
SELECT
|
|
2555
|
+
v.node_id, v.cve_id, v.severity_score, v.fixed_version, v.summary,
|
|
2556
|
+
v.epss_score, v.epss_percentile, v.risk_score,
|
|
2557
|
+
d.package_name, d.ecosystem, d.resolved_version, d.declared_constraint,
|
|
2558
|
+
r.verdict
|
|
2559
|
+
FROM sec_vulnerabilities v
|
|
2560
|
+
LEFT JOIN edges e ON e.target = v.node_id AND e.kind = 'has_vulnerability'
|
|
2561
|
+
LEFT JOIN sec_dependencies d ON d.node_id = e.source
|
|
2562
|
+
LEFT JOIN sec_reachability r ON r.vulnerability_node_id = v.node_id
|
|
2563
|
+
WHERE 1=1
|
|
2564
|
+
`;
|
|
2565
|
+
const params = [];
|
|
2566
|
+
if (args.severity) {
|
|
2567
|
+
const severityRanges = {
|
|
2568
|
+
critical: [9, 10],
|
|
2569
|
+
high: [7, 8.9],
|
|
2570
|
+
medium: [4, 6.9],
|
|
2571
|
+
low: [0.1, 3.9]
|
|
2572
|
+
};
|
|
2573
|
+
const range = severityRanges[args.severity.toLowerCase()];
|
|
2574
|
+
if (!range) return `Invalid severity: ${args.severity}. Use: critical, high, medium, low`;
|
|
2575
|
+
query += ` AND v.severity_score >= ? AND v.severity_score <= ?`;
|
|
2576
|
+
params.push(range[0], range[1]);
|
|
2577
|
+
}
|
|
2578
|
+
if (args.verdict) {
|
|
2579
|
+
const validVerdicts = ["affected", "not_affected", "under_investigation"];
|
|
2580
|
+
if (!validVerdicts.includes(args.verdict)) {
|
|
2581
|
+
return `Invalid verdict: ${args.verdict}. Use: affected, not_affected, under_investigation`;
|
|
2582
|
+
}
|
|
2583
|
+
query += ` AND r.verdict = ?`;
|
|
2584
|
+
params.push(args.verdict);
|
|
2585
|
+
}
|
|
2586
|
+
const limit = clampLimit(args.limit, 20);
|
|
2587
|
+
query += ` ORDER BY v.risk_score DESC NULLS LAST LIMIT ?`;
|
|
2588
|
+
params.push(limit);
|
|
2589
|
+
const rows = rawDb.all(query, params);
|
|
2590
|
+
const verdictRankMcp = (v) => v === "affected" ? 3 : v === "under_investigation" ? 2 : v === "not_affected" ? 1 : 0;
|
|
2591
|
+
const dedupMapMcp = /* @__PURE__ */ new Map();
|
|
2592
|
+
for (const row of rows) {
|
|
2593
|
+
const key = `${row.cve_id}::${row.package_name ?? ""}::${row.ecosystem ?? ""}`;
|
|
2594
|
+
const ex = dedupMapMcp.get(key);
|
|
2595
|
+
if (!ex || verdictRankMcp(row.verdict) > verdictRankMcp(ex.verdict)) dedupMapMcp.set(key, row);
|
|
2596
|
+
}
|
|
2597
|
+
const dedupedRowsMcp = [...dedupMapMcp.values()];
|
|
2598
|
+
const { SuppressionManager } = await Promise.resolve().then(() => require("../security/suppressions.js"));
|
|
2599
|
+
const suppressionMgr = new SuppressionManager(projectRoot);
|
|
2600
|
+
const suppressedCount = dedupedRowsMcp.filter((row) => suppressionMgr.isSuppressed(row.cve_id)).length;
|
|
2601
|
+
const filteredRows = dedupedRowsMcp.filter((row) => !suppressionMgr.isSuppressed(row.cve_id));
|
|
2602
|
+
if (filteredRows.length === 0) {
|
|
2603
|
+
const noMatch = "No vulnerabilities found" + (args.severity || args.verdict ? " matching filters." : ".");
|
|
2604
|
+
return suppressedCount > 0 ? `${noMatch} (${suppressedCount} suppressed)` : noMatch;
|
|
2605
|
+
}
|
|
2606
|
+
const { formatFixSuggestion } = await Promise.resolve().then(() => require("../security/export/fix-suggestions.js"));
|
|
2607
|
+
const lines = [`Vulnerabilities (${filteredRows.length}${suppressedCount > 0 ? `, ${suppressedCount} suppressed` : ""}):
|
|
2608
|
+
`];
|
|
2609
|
+
for (const row of filteredRows) {
|
|
2610
|
+
const score = row.severity_score;
|
|
2611
|
+
let severityLabel;
|
|
2612
|
+
if (score == null) severityLabel = "UNKNOWN";
|
|
2613
|
+
else if (score >= 9) severityLabel = "CRITICAL";
|
|
2614
|
+
else if (score >= 7) severityLabel = "HIGH";
|
|
2615
|
+
else if (score >= 4) severityLabel = "MEDIUM";
|
|
2616
|
+
else severityLabel = "LOW";
|
|
2617
|
+
let verdictLabel;
|
|
2618
|
+
if (!row.verdict) verdictLabel = "pending";
|
|
2619
|
+
else if (row.verdict === "affected") verdictLabel = "affected";
|
|
2620
|
+
else if (row.verdict === "not_affected") verdictLabel = "not affected";
|
|
2621
|
+
else verdictLabel = "investigating";
|
|
2622
|
+
const pkg = row.package_name ? `${row.package_name}@${row.resolved_version || row.declared_constraint || "?"}` : "unknown package";
|
|
2623
|
+
const epssNote = row.epss_score != null ? ` [EPSS: ${row.epss_score.toFixed(2)}]` : "";
|
|
2624
|
+
const riskNote = row.risk_score != null ? ` [Risk: ${row.risk_score.toFixed(1)}]` : "";
|
|
2625
|
+
lines.push(`${severityLabel} ${row.cve_id} ${pkg} [${verdictLabel}]${epssNote}${riskNote}`);
|
|
2626
|
+
if (row.summary && row.summary !== "Manually registered") {
|
|
2627
|
+
const truncSummary = row.summary.length > 120 ? row.summary.slice(0, 120) + "\u2026" : row.summary;
|
|
2628
|
+
lines.push(` ${truncSummary}`);
|
|
2629
|
+
}
|
|
2630
|
+
if (row.fixed_version && row.ecosystem && row.package_name) {
|
|
2631
|
+
const fix = formatFixSuggestion(row.ecosystem, row.package_name, row.fixed_version);
|
|
2632
|
+
if (fix) lines.push(` ${fix}`);
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
return truncate(lines.join("\n"));
|
|
2636
|
+
}
|
|
2637
|
+
case "kirograph_vuln_add": {
|
|
2638
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
2639
|
+
const projectRoot = cg.getProjectRoot();
|
|
2640
|
+
const config = await loadConfig(projectRoot);
|
|
2641
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
2642
|
+
const cveId = args.cveId;
|
|
2643
|
+
const pkgName = args.package;
|
|
2644
|
+
if (!cveId) return "Error: cveId is required.";
|
|
2645
|
+
if (!pkgName) return "Error: package is required.";
|
|
2646
|
+
const db = cg.getDatabase();
|
|
2647
|
+
db.applySecuritySchema();
|
|
2648
|
+
const rawDb = db.getRawDb();
|
|
2649
|
+
const depRow = rawDb.get(
|
|
2650
|
+
`SELECT node_id, ecosystem FROM sec_dependencies WHERE package_name = ?`,
|
|
2651
|
+
[pkgName]
|
|
2652
|
+
);
|
|
2653
|
+
if (!depRow) {
|
|
2654
|
+
return `No dependency found matching "${pkgName}". Run kirograph index first to discover dependencies.`;
|
|
2655
|
+
}
|
|
2656
|
+
const vulnNodeId = `vuln:${cveId}`;
|
|
2657
|
+
const now = Date.now();
|
|
2658
|
+
const severity = args.severity;
|
|
2659
|
+
const summary = args.summary ?? "Manually registered";
|
|
2660
|
+
const fixedVersion = args.fixedVersion ?? null;
|
|
2661
|
+
rawDb.run(
|
|
2662
|
+
`INSERT OR REPLACE INTO nodes
|
|
2663
|
+
(id, kind, name, qualified_name, file_path, language,
|
|
2664
|
+
start_line, end_line, start_column, end_column,
|
|
2665
|
+
is_exported, is_async, is_static, is_abstract, updated_at)
|
|
2666
|
+
VALUES (?, 'vulnerability', ?, ?, '', 'unknown', 0, 0, 0, 0, 0, 0, 0, 0, ?)`,
|
|
2667
|
+
[vulnNodeId, cveId, cveId, now]
|
|
2668
|
+
);
|
|
2669
|
+
rawDb.run(
|
|
2670
|
+
`INSERT OR REPLACE INTO sec_vulnerabilities
|
|
2671
|
+
(node_id, cve_id, severity_score, affected_ranges, fixed_version, summary, source_database)
|
|
2672
|
+
VALUES (?, ?, ?, '[]', ?, ?, 'manual')`,
|
|
2673
|
+
[vulnNodeId, cveId, severity ?? null, fixedVersion, summary]
|
|
2674
|
+
);
|
|
2675
|
+
rawDb.run(
|
|
2676
|
+
`INSERT OR IGNORE INTO edges (source, target, kind, confidence, confidence_score)
|
|
2677
|
+
VALUES (?, ?, 'has_vulnerability', 'extracted', 1.0)`,
|
|
2678
|
+
[depRow.node_id, vulnNodeId]
|
|
2679
|
+
);
|
|
2680
|
+
return `Registered ${cveId} against ${pkgName}.`;
|
|
2681
|
+
}
|
|
2682
|
+
case "kirograph_vuln_suppress": {
|
|
2683
|
+
const projectRoot = cg.getProjectRoot();
|
|
2684
|
+
const cveId = args.cveId;
|
|
2685
|
+
if (!cveId) return "Error: cveId is required.";
|
|
2686
|
+
const { SuppressionManager } = await Promise.resolve().then(() => require("../security/suppressions.js"));
|
|
2687
|
+
const manager = new SuppressionManager(projectRoot);
|
|
2688
|
+
manager.add(cveId, args.reason, args.expires);
|
|
2689
|
+
const reasonNote = args.reason ? ` Reason: ${args.reason}.` : "";
|
|
2690
|
+
const expiresNote = args.expires ? ` Expires: ${args.expires}.` : "";
|
|
2691
|
+
return `${cveId} suppressed.${reasonNote}${expiresNote}`;
|
|
2692
|
+
}
|
|
2693
|
+
case "kirograph_sbom": {
|
|
2694
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
2695
|
+
const projectRoot = cg.getProjectRoot();
|
|
2696
|
+
const config = await loadConfig(projectRoot);
|
|
2697
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
2698
|
+
const db = cg.getDatabase();
|
|
2699
|
+
db.applySecuritySchema();
|
|
2700
|
+
const { SBOMExporter } = await Promise.resolve().then(() => require("../security/export/sbom.js"));
|
|
2701
|
+
const exporter = new SBOMExporter(db, projectRoot);
|
|
2702
|
+
const json = exporter.exportJSON();
|
|
2703
|
+
return truncate(json);
|
|
2704
|
+
}
|
|
2705
|
+
case "kirograph_vex": {
|
|
2706
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
2707
|
+
const projectRoot = cg.getProjectRoot();
|
|
2708
|
+
const config = await loadConfig(projectRoot);
|
|
2709
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
2710
|
+
const db = cg.getDatabase();
|
|
2711
|
+
db.applySecuritySchema();
|
|
2712
|
+
const { VEXExporter } = await Promise.resolve().then(() => require("../security/export/vex.js"));
|
|
2713
|
+
const exporter = new VEXExporter(db, projectRoot);
|
|
2714
|
+
const json = exporter.exportJSON();
|
|
2715
|
+
return truncate(json);
|
|
2716
|
+
}
|
|
2717
|
+
case "kirograph_reachability": {
|
|
2718
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
2719
|
+
const projectRoot = cg.getProjectRoot();
|
|
2720
|
+
const config = await loadConfig(projectRoot);
|
|
2721
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
2722
|
+
const target = args.target;
|
|
2723
|
+
if (!target) return "Error: target is required (dependency name or CVE ID).";
|
|
2724
|
+
const db = cg.getDatabase();
|
|
2725
|
+
db.applySecuritySchema();
|
|
2726
|
+
const rawDb = db.getRawDb();
|
|
2727
|
+
let vulnerabilityNodeId = null;
|
|
2728
|
+
let targetLabel = target;
|
|
2729
|
+
const vulnRow = rawDb.get(
|
|
2730
|
+
`SELECT node_id FROM sec_vulnerabilities WHERE cve_id = ?`,
|
|
2731
|
+
[target]
|
|
2732
|
+
);
|
|
2733
|
+
if (vulnRow) {
|
|
2734
|
+
vulnerabilityNodeId = vulnRow.node_id;
|
|
2735
|
+
} else {
|
|
2736
|
+
const depRow = rawDb.get(
|
|
2737
|
+
`SELECT node_id, package_name FROM sec_dependencies WHERE package_name = ?`,
|
|
2738
|
+
[target]
|
|
2739
|
+
);
|
|
2740
|
+
if (depRow) {
|
|
2741
|
+
targetLabel = depRow.package_name;
|
|
2742
|
+
const vulnEdge = rawDb.get(
|
|
2743
|
+
`SELECT target FROM edges WHERE source = ? AND kind = 'has_vulnerability' LIMIT 1`,
|
|
2744
|
+
[depRow.node_id]
|
|
2745
|
+
);
|
|
2746
|
+
if (vulnEdge) {
|
|
2747
|
+
vulnerabilityNodeId = vulnEdge.target;
|
|
2748
|
+
} else {
|
|
2749
|
+
return `No vulnerabilities found for dependency "${target}". The dependency exists but has no known vulnerabilities.`;
|
|
2750
|
+
}
|
|
2751
|
+
} else {
|
|
2752
|
+
return `Target "${target}" not found. Provide a valid CVE ID or dependency package name.`;
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
const { ReachabilityAnalyzer } = await Promise.resolve().then(() => require("../security/reachability.js"));
|
|
2756
|
+
const analyzer = new ReachabilityAnalyzer(db, config);
|
|
2757
|
+
const result = await analyzer.analyze(vulnerabilityNodeId);
|
|
2758
|
+
const lines = [
|
|
2759
|
+
`# Reachability: ${targetLabel}`,
|
|
2760
|
+
"",
|
|
2761
|
+
`Verdict: ${result.verdict}`,
|
|
2762
|
+
`Reaching entry points: ${result.reachingEntryPointCount}`
|
|
2763
|
+
];
|
|
2764
|
+
if (result.paths.length > 0) {
|
|
2765
|
+
lines.push("", "## Paths");
|
|
2766
|
+
for (const p of result.paths.slice(0, 5)) {
|
|
2767
|
+
lines.push(`- From ${p.entryPoint}: ${p.path.join(" \u2192 ")}`);
|
|
2768
|
+
}
|
|
2769
|
+
if (result.paths.length > 5) {
|
|
2770
|
+
lines.push(` \u2026and ${result.paths.length - 5} more paths`);
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
if (result.unresolvedSymbols.length > 0) {
|
|
2774
|
+
lines.push("", "## Unresolved Symbols");
|
|
2775
|
+
for (const sym of result.unresolvedSymbols.slice(0, 10)) {
|
|
2776
|
+
lines.push(`- ${sym}`);
|
|
2777
|
+
}
|
|
2778
|
+
if (result.unresolvedSymbols.length > 10) {
|
|
2779
|
+
lines.push(` \u2026and ${result.unresolvedSymbols.length - 10} more`);
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
if (result.verdict === "affected") {
|
|
2783
|
+
const impact = await analyzer.getImpactSummary(vulnerabilityNodeId);
|
|
2784
|
+
if (impact) {
|
|
2785
|
+
lines.push("", "## Impact Summary");
|
|
2786
|
+
if (impact.affectedLayers.length > 0) {
|
|
2787
|
+
lines.push(`Affected layers: ${impact.affectedLayers.join(", ")}`);
|
|
2788
|
+
}
|
|
2789
|
+
lines.push(`Affected entry points: ${impact.affectedEntryPoints.length}`);
|
|
2790
|
+
lines.push(`Distinct paths: ${impact.distinctPathCount}`);
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
return truncate(lines.join("\n"));
|
|
2794
|
+
}
|
|
2795
|
+
case "kirograph_staleness": {
|
|
2796
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
2797
|
+
const projectRoot = cg.getProjectRoot();
|
|
2798
|
+
const config = await loadConfig(projectRoot);
|
|
2799
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
2800
|
+
const db = cg.getDatabase();
|
|
2801
|
+
db.applySecuritySchema();
|
|
2802
|
+
const rawDb = db.getRawDb();
|
|
2803
|
+
const threshold = typeof args.threshold === "number" ? args.threshold : 0.3;
|
|
2804
|
+
if (args.refresh === true) {
|
|
2805
|
+
const { StalenessChecker } = await Promise.resolve().then(() => require("../security/staleness.js"));
|
|
2806
|
+
const checker = new StalenessChecker(db);
|
|
2807
|
+
await checker.checkAll();
|
|
2808
|
+
}
|
|
2809
|
+
const rows = rawDb.all(
|
|
2810
|
+
`SELECT package_name, ecosystem, resolved_version, declared_constraint,
|
|
2811
|
+
latest_version, latest_published, staleness_score
|
|
2812
|
+
FROM sec_dependencies
|
|
2813
|
+
WHERE staleness_score >= ?
|
|
2814
|
+
ORDER BY staleness_score DESC`,
|
|
2815
|
+
[threshold]
|
|
2816
|
+
);
|
|
2817
|
+
if (rows.length === 0) {
|
|
2818
|
+
return `No dependencies found with staleness_score >= ${threshold}.` + (args.refresh ? "" : " Use refresh=true to fetch latest version data from registries.");
|
|
2819
|
+
}
|
|
2820
|
+
const lines = [`Stale Dependencies (threshold: ${threshold}):
|
|
2821
|
+
`];
|
|
2822
|
+
for (const row of rows) {
|
|
2823
|
+
const resolved = row.resolved_version ?? row.declared_constraint ?? "?";
|
|
2824
|
+
const latest = row.latest_version ?? "?";
|
|
2825
|
+
const score = row.staleness_score ?? 0;
|
|
2826
|
+
const months = row.latest_published ? Math.round((Date.now() - row.latest_published) / (1e3 * 60 * 60 * 24 * 30)) : null;
|
|
2827
|
+
const bar = "\u2588".repeat(Math.round(score * 10)) + "\u2591".repeat(10 - Math.round(score * 10));
|
|
2828
|
+
const monthsStr = months !== null ? `, ${months}mo since latest` : "";
|
|
2829
|
+
lines.push(`${row.package_name} (${row.ecosystem}): ${resolved} \u2192 ${latest}${monthsStr}`);
|
|
2830
|
+
lines.push(` ${bar} ${score.toFixed(2)}`);
|
|
2831
|
+
}
|
|
2832
|
+
const totalCount = rawDb.get(`SELECT COUNT(*) as count FROM sec_dependencies`) ?? { count: 0 };
|
|
2833
|
+
lines.push("", `${rows.length} of ${totalCount.count} dependencies are stale (score >= ${threshold})`);
|
|
2834
|
+
return truncate(lines.join("\n"));
|
|
2835
|
+
}
|
|
2836
|
+
case "kirograph_licenses": {
|
|
2837
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
2838
|
+
const projectRoot = cg.getProjectRoot();
|
|
2839
|
+
const config = await loadConfig(projectRoot);
|
|
2840
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
2841
|
+
const db = cg.getDatabase();
|
|
2842
|
+
db.applySecuritySchema();
|
|
2843
|
+
const rawDb = db.getRawDb();
|
|
2844
|
+
const deps = rawDb.all(
|
|
2845
|
+
`SELECT package_name, ecosystem, license FROM sec_dependencies ORDER BY ecosystem, package_name`
|
|
2846
|
+
);
|
|
2847
|
+
const { checkLicensePolicy } = await Promise.resolve().then(() => require("../security/license.js"));
|
|
2848
|
+
const policy = config.securityLicensePolicy;
|
|
2849
|
+
const violations = checkLicensePolicy(deps, policy);
|
|
2850
|
+
if (args.policy === true) {
|
|
2851
|
+
if (violations.length === 0) {
|
|
2852
|
+
return "No license policy violations found.";
|
|
2853
|
+
}
|
|
2854
|
+
const lines2 = ["# License Policy Violations\n"];
|
|
2855
|
+
for (const v of violations) {
|
|
2856
|
+
lines2.push(`${v.severity.toUpperCase()} ${v.packageName} [${v.ecosystem}] ${v.license}`);
|
|
2857
|
+
}
|
|
2858
|
+
const denyCount2 = violations.filter((v) => v.severity === "deny").length;
|
|
2859
|
+
const warnCount2 = violations.filter((v) => v.severity === "warn").length;
|
|
2860
|
+
lines2.push("");
|
|
2861
|
+
if (denyCount2 > 0) lines2.push(`${denyCount2} denied license${denyCount2 !== 1 ? "s" : ""}`);
|
|
2862
|
+
if (warnCount2 > 0) lines2.push(`${warnCount2} license warning${warnCount2 !== 1 ? "s" : ""}`);
|
|
2863
|
+
return truncate(lines2.join("\n"));
|
|
2864
|
+
}
|
|
2865
|
+
if (deps.length === 0) {
|
|
2866
|
+
return "No dependencies found. Run kirograph index first.";
|
|
2867
|
+
}
|
|
2868
|
+
const violationMap = /* @__PURE__ */ new Map();
|
|
2869
|
+
for (const v of violations) {
|
|
2870
|
+
violationMap.set(`${v.ecosystem}:${v.packageName}`, v.severity);
|
|
2871
|
+
}
|
|
2872
|
+
const lines = [`# License Report (${deps.length} dependencies)
|
|
2873
|
+
`];
|
|
2874
|
+
if (violations.length > 0) {
|
|
2875
|
+
lines.push("## Policy Violations\n");
|
|
2876
|
+
for (const v of violations) {
|
|
2877
|
+
lines.push(`${v.severity.toUpperCase()} ${v.packageName} [${v.ecosystem}] ${v.license}`);
|
|
2878
|
+
}
|
|
2879
|
+
lines.push("");
|
|
2880
|
+
}
|
|
2881
|
+
lines.push("## All Dependencies\n");
|
|
2882
|
+
lines.push("package | ecosystem | license | status");
|
|
2883
|
+
lines.push("------- | --------- | ------- | ------");
|
|
2884
|
+
for (const dep of deps) {
|
|
2885
|
+
const key = `${dep.ecosystem}:${dep.package_name}`;
|
|
2886
|
+
const violation = violationMap.get(key);
|
|
2887
|
+
const license = dep.license ?? "(unknown)";
|
|
2888
|
+
const status = violation ?? (dep.license ? "ok" : "unknown");
|
|
2889
|
+
lines.push(`${dep.package_name} | ${dep.ecosystem} | ${license} | ${status}`);
|
|
2890
|
+
}
|
|
2891
|
+
const denyCount = violations.filter((v) => v.severity === "deny").length;
|
|
2892
|
+
const warnCount = violations.filter((v) => v.severity === "warn").length;
|
|
2893
|
+
const unknownCount = deps.filter((d) => !d.license).length;
|
|
2894
|
+
lines.push("");
|
|
2895
|
+
if (denyCount > 0) lines.push(`${denyCount} denied license${denyCount !== 1 ? "s" : ""}`);
|
|
2896
|
+
if (warnCount > 0) lines.push(`${warnCount} license warning${warnCount !== 1 ? "s" : ""}`);
|
|
2897
|
+
if (unknownCount > 0) lines.push(`${unknownCount} unknown license${unknownCount !== 1 ? "s" : ""}`);
|
|
2898
|
+
if (denyCount === 0 && warnCount === 0) lines.push("No policy violations");
|
|
2899
|
+
return truncate(lines.join("\n"));
|
|
2900
|
+
}
|
|
2901
|
+
case "kirograph_attack_surface": {
|
|
2902
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
2903
|
+
const projectRoot = cg.getProjectRoot();
|
|
2904
|
+
const config = await loadConfig(projectRoot);
|
|
2905
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
2906
|
+
const db = cg.getDatabase();
|
|
2907
|
+
db.applySecuritySchema();
|
|
2908
|
+
const { AttackSurfaceAnalyzer } = await Promise.resolve().then(() => require("../security/attack-surface.js"));
|
|
2909
|
+
const analyzer = new AttackSurfaceAnalyzer(db);
|
|
2910
|
+
const result = await analyzer.analyze();
|
|
2911
|
+
if (result.totalRoutes === 0) {
|
|
2912
|
+
return "No route nodes found in the graph. Ensure the project has been indexed with architecture analysis enabled.";
|
|
2913
|
+
}
|
|
2914
|
+
const limit = clampLimit(args.limit, 20);
|
|
2915
|
+
const routes = args.publicOnly === true ? result.criticalPaths.filter((r) => r.exposureLevel === "public") : result.criticalPaths;
|
|
2916
|
+
const lines = [
|
|
2917
|
+
"# Attack Surface",
|
|
2918
|
+
"",
|
|
2919
|
+
`Total routes: ${result.totalRoutes} Public: ${result.publicRoutes} Authenticated: ${result.authenticatedRoutes} Routes with vulns: ${result.routesWithVulns}`,
|
|
2920
|
+
""
|
|
2921
|
+
];
|
|
2922
|
+
const displayed = routes.slice(0, limit);
|
|
2923
|
+
if (displayed.length === 0) {
|
|
2924
|
+
lines.push(args.publicOnly === true ? "No public routes with vulnerable dependencies found." : "No routes with vulnerable dependencies found.");
|
|
2925
|
+
} else {
|
|
2926
|
+
for (const entry of displayed) {
|
|
2927
|
+
const authTag = entry.isAuthenticated ? "[auth]" : "[public]";
|
|
2928
|
+
const riskTag = entry.riskScore > 0 ? ` risk=${entry.riskScore.toFixed(1)}` : "";
|
|
2929
|
+
lines.push(`${authTag} ${entry.route} (${entry.exposureLevel})${riskTag} ${entry.filePath}`);
|
|
2930
|
+
for (const dep of entry.vulnerableDeps.slice(0, 3)) {
|
|
2931
|
+
lines.push(` \u2514 ${dep.cveId} via ${dep.packageName} (${dep.hopCount} hop${dep.hopCount !== 1 ? "s" : ""}${dep.verdict ? `, ${dep.verdict}` : ""})`);
|
|
2932
|
+
}
|
|
2933
|
+
if (entry.vulnerableDeps.length > 3) {
|
|
2934
|
+
lines.push(` \u2514 \u2026and ${entry.vulnerableDeps.length - 3} more vulns`);
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
if (routes.length > limit) {
|
|
2938
|
+
lines.push("", `\u2026and ${routes.length - limit} more routes (increase limit to see all)`);
|
|
2939
|
+
}
|
|
2940
|
+
}
|
|
2941
|
+
return truncate(lines.join("\n"));
|
|
2942
|
+
}
|
|
2943
|
+
case "kirograph_secrets": {
|
|
2944
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
2945
|
+
const projectRoot = cg.getProjectRoot();
|
|
2946
|
+
const config = await loadConfig(projectRoot);
|
|
2947
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
2948
|
+
const db = cg.getDatabase();
|
|
2949
|
+
db.applySecuritySchema();
|
|
2950
|
+
const { SecretsScanner } = await Promise.resolve().then(() => require("../security/secrets.js"));
|
|
2951
|
+
const scanner = new SecretsScanner(db, projectRoot);
|
|
2952
|
+
const result = await scanner.scan({ includeTests: args.includeTests === true });
|
|
2953
|
+
if (result.totalFindings === 0) {
|
|
2954
|
+
return `No secrets found. Scanned ${result.filesScanned} file${result.filesScanned !== 1 ? "s" : ""}.`;
|
|
2955
|
+
}
|
|
2956
|
+
let findings = result.findings;
|
|
2957
|
+
if (args.severity) {
|
|
2958
|
+
findings = findings.filter((f) => f.severity === args.severity);
|
|
2959
|
+
}
|
|
2960
|
+
if (findings.length === 0) {
|
|
2961
|
+
return `No secrets found matching severity "${args.severity}". Total findings (all severities): ${result.totalFindings}.`;
|
|
2962
|
+
}
|
|
2963
|
+
const lines = [
|
|
2964
|
+
`# Secrets Scan (${result.filesScanned} files scanned)`,
|
|
2965
|
+
"",
|
|
2966
|
+
`Found: ${result.totalFindings} Critical: ${result.criticalCount} High: ${result.highCount}`,
|
|
2967
|
+
""
|
|
2968
|
+
];
|
|
2969
|
+
for (const f of findings) {
|
|
2970
|
+
lines.push(`${f.severity.toUpperCase()} ${f.type}`);
|
|
2971
|
+
lines.push(` ${f.filePath}:${f.line}:${f.column} snippet: ${f.snippet}`);
|
|
2972
|
+
if (f.nodeName) {
|
|
2973
|
+
lines.push(` in function: ${f.nodeName}`);
|
|
2974
|
+
}
|
|
2975
|
+
if (f.entryPointCount > 0) {
|
|
2976
|
+
lines.push(` reachable from ${f.entryPointCount} entry point${f.entryPointCount !== 1 ? "s" : ""}`);
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
return truncate(lines.join("\n"));
|
|
2980
|
+
}
|
|
2981
|
+
case "kirograph_security_flows": {
|
|
2982
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
2983
|
+
const projectRoot = cg.getProjectRoot();
|
|
2984
|
+
const config = await loadConfig(projectRoot);
|
|
2985
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
2986
|
+
const db = cg.getDatabase();
|
|
2987
|
+
db.applySecuritySchema();
|
|
2988
|
+
const { DataFlowAnalyzer } = await Promise.resolve().then(() => require("../security/data-flows.js"));
|
|
2989
|
+
const analyzer = new DataFlowAnalyzer(db);
|
|
2990
|
+
let findings = await analyzer.analyze();
|
|
2991
|
+
const typeFilter = args.type ?? "all";
|
|
2992
|
+
const TYPE_MAP = {
|
|
2993
|
+
sql: "sql-injection",
|
|
2994
|
+
eval: "dangerous-eval",
|
|
2995
|
+
deserialize: "unsafe-deserialize",
|
|
2996
|
+
path: "path-traversal",
|
|
2997
|
+
crypto: "hardcoded-crypto"
|
|
2998
|
+
};
|
|
2999
|
+
if (typeFilter !== "all") {
|
|
3000
|
+
const mapped = TYPE_MAP[typeFilter];
|
|
3001
|
+
if (!mapped) return `Invalid type filter "${typeFilter}". Use: sql, eval, deserialize, path, crypto, all`;
|
|
3002
|
+
findings = findings.filter((f) => f.type === mapped);
|
|
3003
|
+
}
|
|
3004
|
+
if (findings.length === 0) {
|
|
3005
|
+
return typeFilter === "all" ? "No dangerous data flows detected." : `No "${typeFilter}" findings detected.`;
|
|
3006
|
+
}
|
|
3007
|
+
const lines = [
|
|
3008
|
+
`# Security Flows (${findings.length} finding${findings.length !== 1 ? "s" : ""})`,
|
|
3009
|
+
""
|
|
3010
|
+
];
|
|
3011
|
+
for (const f of findings) {
|
|
3012
|
+
lines.push(`${f.severity.toUpperCase()} [${f.owaspCategory}] ${f.type}`);
|
|
3013
|
+
lines.push(` ${f.filePath}:${f.line} symbol: ${f.symbol}`);
|
|
3014
|
+
lines.push(` ${f.description}`);
|
|
3015
|
+
lines.push(` Fix: ${f.recommendation}`);
|
|
3016
|
+
lines.push("");
|
|
3017
|
+
}
|
|
3018
|
+
return truncate(lines.join("\n"));
|
|
3019
|
+
}
|
|
3020
|
+
case "kirograph_supply_chain": {
|
|
3021
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
3022
|
+
const projectRoot = cg.getProjectRoot();
|
|
3023
|
+
const config = await loadConfig(projectRoot);
|
|
3024
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
3025
|
+
const db = cg.getDatabase();
|
|
3026
|
+
db.applySecuritySchema();
|
|
3027
|
+
const { SupplyChainChecker } = await Promise.resolve().then(() => require("../security/supply-chain.js"));
|
|
3028
|
+
const checker = new SupplyChainChecker(db);
|
|
3029
|
+
const { results, errors } = await checker.checkAll();
|
|
3030
|
+
const RISK_ORDER = { critical: 0, high: 1, medium: 2, low: 3, unknown: 4 };
|
|
3031
|
+
const thresholdArg = args.threshold;
|
|
3032
|
+
const thresholdOrder = thresholdArg ? RISK_ORDER[thresholdArg] ?? 4 : 2;
|
|
3033
|
+
const filtered = results.filter((r) => (RISK_ORDER[r.riskLevel] ?? 4) <= thresholdOrder).sort((a, b) => (RISK_ORDER[a.riskLevel] ?? 4) - (RISK_ORDER[b.riskLevel] ?? 4));
|
|
3034
|
+
if (filtered.length === 0) {
|
|
3035
|
+
return `No supply chain risks found at threshold "${thresholdArg ?? "medium"}" or above. Checked ${results.length} dependencies.`;
|
|
3036
|
+
}
|
|
3037
|
+
const lines = [
|
|
3038
|
+
`# Supply Chain Health (${filtered.length} risks, threshold: ${thresholdArg ?? "medium"})`,
|
|
3039
|
+
""
|
|
3040
|
+
];
|
|
3041
|
+
for (const r of filtered) {
|
|
3042
|
+
const scoreStr = r.scorecardScore !== null ? ` scorecard=${r.scorecardScore.toFixed(1)}/10` : "";
|
|
3043
|
+
const maintainerStr = r.maintainerCount !== null ? ` maintainers=${r.maintainerCount}` : "";
|
|
3044
|
+
lines.push(`${r.riskLevel.toUpperCase()} ${r.packageName} (${r.ecosystem})${scoreStr}${maintainerStr}`);
|
|
3045
|
+
for (const reason of r.riskReasons) {
|
|
3046
|
+
lines.push(` \u2022 ${reason}`);
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
if (errors.length > 0) {
|
|
3050
|
+
lines.push("", `\u26A0 ${errors.length} package${errors.length !== 1 ? "s" : ""} could not be checked (network errors)`);
|
|
3051
|
+
}
|
|
3052
|
+
return truncate(lines.join("\n"));
|
|
3053
|
+
}
|
|
3054
|
+
case "kirograph_dep_confusion": {
|
|
3055
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
3056
|
+
const projectRoot = cg.getProjectRoot();
|
|
3057
|
+
const config = await loadConfig(projectRoot);
|
|
3058
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
3059
|
+
const db = cg.getDatabase();
|
|
3060
|
+
db.applySecuritySchema();
|
|
3061
|
+
const { DepConfusionChecker } = await Promise.resolve().then(() => require("../security/dep-confusion.js"));
|
|
3062
|
+
const checker = new DepConfusionChecker(db);
|
|
3063
|
+
const findings = await checker.check();
|
|
3064
|
+
if (findings.length === 0) {
|
|
3065
|
+
return "No dependency confusion vulnerabilities detected.";
|
|
3066
|
+
}
|
|
3067
|
+
const RISK_ORDER = { critical: 0, high: 1, medium: 2 };
|
|
3068
|
+
const sorted = [...findings].sort((a, b) => (RISK_ORDER[a.riskLevel] ?? 3) - (RISK_ORDER[b.riskLevel] ?? 3));
|
|
3069
|
+
const lines = [
|
|
3070
|
+
`# Dependency Confusion (${findings.length} finding${findings.length !== 1 ? "s" : ""})`,
|
|
3071
|
+
""
|
|
3072
|
+
];
|
|
3073
|
+
for (const f of sorted) {
|
|
3074
|
+
lines.push(`${f.riskLevel.toUpperCase()} ${f.packageName} (${f.ecosystem})`);
|
|
3075
|
+
lines.push(` ${f.explanation}`);
|
|
3076
|
+
if (f.publicExists && f.publicVersion) {
|
|
3077
|
+
lines.push(` Public version: ${f.publicVersion}${f.publicPublishedAt ? ` published: ${f.publicPublishedAt}` : ""}`);
|
|
3078
|
+
}
|
|
3079
|
+
}
|
|
3080
|
+
return truncate(lines.join("\n"));
|
|
3081
|
+
}
|
|
3082
|
+
case "kirograph_remediation": {
|
|
3083
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
3084
|
+
const projectRoot = cg.getProjectRoot();
|
|
3085
|
+
const config = await loadConfig(projectRoot);
|
|
3086
|
+
if (!config.enableSecurity) return "Security analysis is not enabled. Set enableSecurity: true in .kirograph/config.json and run kirograph index.";
|
|
3087
|
+
const db = cg.getDatabase();
|
|
3088
|
+
db.applySecuritySchema();
|
|
3089
|
+
const { RemediationTracker } = await Promise.resolve().then(() => require("../security/remediation.js"));
|
|
3090
|
+
const tracker = new RemediationTracker(db);
|
|
3091
|
+
let statuses = tracker.getStatus();
|
|
3092
|
+
if (args.overdueOnly === true) {
|
|
3093
|
+
statuses = statuses.filter((s) => s.isOverdue);
|
|
3094
|
+
}
|
|
3095
|
+
if (statuses.length === 0) {
|
|
3096
|
+
return args.overdueOnly === true ? "No overdue vulnerabilities found." : "No open vulnerabilities with SLA tracking data found.";
|
|
3097
|
+
}
|
|
3098
|
+
const SLA_ORDER = { overdue: 0, warning: 1, no_fix: 2, ok: 3 };
|
|
3099
|
+
statuses.sort((a, b) => {
|
|
3100
|
+
const slaA = SLA_ORDER[a.slaStatus] ?? 3;
|
|
3101
|
+
const slaB = SLA_ORDER[b.slaStatus] ?? 3;
|
|
3102
|
+
if (slaA !== slaB) return slaA - slaB;
|
|
3103
|
+
return (b.severity ?? 0) - (a.severity ?? 0);
|
|
3104
|
+
});
|
|
3105
|
+
const overdueCount = statuses.filter((s) => s.isOverdue).length;
|
|
3106
|
+
const warningCount = statuses.filter((s) => s.slaStatus === "warning").length;
|
|
3107
|
+
const lines = [
|
|
3108
|
+
`# Remediation SLA (${statuses.length} open${overdueCount > 0 ? `, ${overdueCount} overdue` : ""}${warningCount > 0 ? `, ${warningCount} warning` : ""})`,
|
|
3109
|
+
""
|
|
3110
|
+
];
|
|
3111
|
+
for (const s of statuses) {
|
|
3112
|
+
const severityLabel = s.severity == null ? "UNKNOWN" : s.severity >= 9 ? "CRITICAL" : s.severity >= 7 ? "HIGH" : s.severity >= 4 ? "MEDIUM" : "LOW";
|
|
3113
|
+
const slaTag = s.slaStatus === "overdue" ? "[OVERDUE]" : s.slaStatus === "warning" ? "[WARNING]" : s.slaStatus === "no_fix" ? "[NO_FIX]" : "[OK]";
|
|
3114
|
+
lines.push(`${slaTag} ${severityLabel} ${s.cveId} ${s.packageName}`);
|
|
3115
|
+
if (s.daysOpen !== null) lines.push(` Open for ${s.daysOpen} day${s.daysOpen !== 1 ? "s" : ""}`);
|
|
3116
|
+
if (s.daysWithFixAvailable !== null) lines.push(` Fix available for ${s.daysWithFixAvailable} day${s.daysWithFixAvailable !== 1 ? "s" : ""}`);
|
|
3117
|
+
if (s.slaDeadline !== null) {
|
|
3118
|
+
const deadlineDate = new Date(s.slaDeadline).toISOString().slice(0, 10);
|
|
3119
|
+
lines.push(` SLA deadline: ${deadlineDate}`);
|
|
3120
|
+
}
|
|
3121
|
+
}
|
|
3122
|
+
return truncate(lines.join("\n"));
|
|
3123
|
+
}
|
|
3124
|
+
case "kirograph_live_search": {
|
|
3125
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
3126
|
+
const projectRoot = cg.getProjectRoot();
|
|
3127
|
+
const config = await loadConfig(projectRoot);
|
|
3128
|
+
if (!config.enablePatterns) {
|
|
3129
|
+
return "kirograph_live_search requires enablePatterns: true in .kirograph/config.json";
|
|
3130
|
+
}
|
|
3131
|
+
const { PatternRunner } = await Promise.resolve().then(() => require("../patterns/runner.js"));
|
|
3132
|
+
const runner = new PatternRunner();
|
|
3133
|
+
if (!runner.isAvailable()) {
|
|
3134
|
+
return "kirograph_live_search requires @ast-grep/napi. Run: npm install @ast-grep/napi";
|
|
3135
|
+
}
|
|
3136
|
+
const pattern = args.pattern;
|
|
3137
|
+
const language = args.language;
|
|
3138
|
+
const rawLimit = typeof args.limit === "number" ? args.limit : 20;
|
|
3139
|
+
const limit = Math.max(1, Math.min(100, Math.round(rawLimit)));
|
|
3140
|
+
const db = cg.getDatabase();
|
|
3141
|
+
const rawDb = db.getRawDb();
|
|
3142
|
+
const files = rawDb.all(
|
|
3143
|
+
"SELECT path FROM files WHERE language = ? LIMIT 5000",
|
|
3144
|
+
[language]
|
|
3145
|
+
);
|
|
3146
|
+
const results = [];
|
|
3147
|
+
let truncated = false;
|
|
3148
|
+
for (const file of files) {
|
|
3149
|
+
if (results.length >= limit) {
|
|
3150
|
+
truncated = true;
|
|
3151
|
+
break;
|
|
3152
|
+
}
|
|
3153
|
+
let content;
|
|
3154
|
+
try {
|
|
3155
|
+
const fullPath = path.isAbsolute(file.path) ? file.path : path.join(projectRoot, file.path);
|
|
3156
|
+
content = fs.readFileSync(fullPath, "utf8");
|
|
3157
|
+
} catch {
|
|
3158
|
+
continue;
|
|
3159
|
+
}
|
|
3160
|
+
const matches = await runner.runInline(pattern, language, content);
|
|
3161
|
+
for (const m of matches) {
|
|
3162
|
+
if (results.length >= limit) {
|
|
3163
|
+
truncated = true;
|
|
3164
|
+
break;
|
|
3165
|
+
}
|
|
3166
|
+
results.push({ filePath: file.path, line: m.line, matchText: m.matchText });
|
|
3167
|
+
}
|
|
3168
|
+
}
|
|
3169
|
+
if (results.length === 0) {
|
|
3170
|
+
return `0 matches for '${pattern}' in ${language} files`;
|
|
3171
|
+
}
|
|
3172
|
+
const lines = [
|
|
3173
|
+
`${results.length} match${results.length !== 1 ? "es" : ""} for '${pattern}' in ${language} files`,
|
|
3174
|
+
""
|
|
3175
|
+
];
|
|
3176
|
+
for (const r of results) {
|
|
3177
|
+
lines.push(` ${r.filePath}:${r.line}`);
|
|
3178
|
+
lines.push(` ${r.matchText}`);
|
|
3179
|
+
lines.push("");
|
|
3180
|
+
}
|
|
3181
|
+
if (truncated) {
|
|
3182
|
+
lines.push(`[truncated \u2014 showing first ${limit} results; refine your pattern or use --lang to narrow scope]`);
|
|
3183
|
+
}
|
|
3184
|
+
return truncate(lines.join("\n"));
|
|
3185
|
+
}
|
|
3186
|
+
// ── Pattern tools ──────────────────────────────────────────────────────────
|
|
3187
|
+
case "kirograph_pattern_coverage": {
|
|
3188
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
3189
|
+
const projectRoot = cg.getProjectRoot();
|
|
3190
|
+
const config = await loadConfig(projectRoot);
|
|
3191
|
+
if (!config.enablePatterns) {
|
|
3192
|
+
return "Pattern analysis is not enabled. Set enablePatterns: true in .kirograph/config.json and run kirograph index.";
|
|
3193
|
+
}
|
|
3194
|
+
const { PatternLibraryLoader } = await Promise.resolve().then(() => require("../patterns/loader.js"));
|
|
3195
|
+
const loader = new PatternLibraryLoader();
|
|
3196
|
+
const builtinPath = path.join(__dirname, "../patterns/library");
|
|
3197
|
+
const customPath = config.patternLibraryPath;
|
|
3198
|
+
let rules = [];
|
|
3199
|
+
try {
|
|
3200
|
+
rules = loader.load(builtinPath, customPath);
|
|
3201
|
+
} catch {
|
|
3202
|
+
}
|
|
3203
|
+
const rulesByCategory = /* @__PURE__ */ new Map();
|
|
3204
|
+
for (const rule of rules) {
|
|
3205
|
+
const prefix = rule.owaspCategory.match(/^(A\d{2})/)?.[1] ?? rule.owaspCategory;
|
|
3206
|
+
if (!rulesByCategory.has(prefix)) rulesByCategory.set(prefix, []);
|
|
3207
|
+
rulesByCategory.get(prefix).push(rule);
|
|
3208
|
+
}
|
|
3209
|
+
const db = cg.getDatabase();
|
|
3210
|
+
db.applyPatternsSchema();
|
|
3211
|
+
const rawDb = db.getRawDb();
|
|
3212
|
+
const matchCountsByCategory = /* @__PURE__ */ new Map();
|
|
3213
|
+
try {
|
|
3214
|
+
const tableExists = rawDb.get("SELECT name FROM sqlite_master WHERE type='table' AND name='pattern_matches'");
|
|
3215
|
+
if (tableExists) {
|
|
3216
|
+
const rows = rawDb.all(
|
|
3217
|
+
"SELECT owasp_category, COUNT(*) as cnt FROM pattern_matches GROUP BY owasp_category"
|
|
3218
|
+
);
|
|
3219
|
+
for (const row of rows) {
|
|
3220
|
+
const prefix = row.owasp_category.match(/^(A\d{2})/)?.[1] ?? row.owasp_category;
|
|
3221
|
+
matchCountsByCategory.set(prefix, (matchCountsByCategory.get(prefix) ?? 0) + row.cnt);
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
} catch {
|
|
3225
|
+
}
|
|
3226
|
+
const owaspTop10 = [
|
|
3227
|
+
"A01",
|
|
3228
|
+
"A02",
|
|
3229
|
+
"A03",
|
|
3230
|
+
"A04",
|
|
3231
|
+
"A05",
|
|
3232
|
+
"A06",
|
|
3233
|
+
"A07",
|
|
3234
|
+
"A08",
|
|
3235
|
+
"A09",
|
|
3236
|
+
"A10"
|
|
3237
|
+
];
|
|
3238
|
+
const owaspNames = {
|
|
3239
|
+
A01: "Broken Access Control",
|
|
3240
|
+
A02: "Cryptographic Failures",
|
|
3241
|
+
A03: "Injection",
|
|
3242
|
+
A04: "Insecure Design",
|
|
3243
|
+
A05: "Security Misconfiguration",
|
|
3244
|
+
A06: "Vulnerable and Outdated Components",
|
|
3245
|
+
A07: "Identification and Authentication Failures",
|
|
3246
|
+
A08: "Software and Data Integrity Failures",
|
|
3247
|
+
A09: "Security Logging and Monitoring Failures",
|
|
3248
|
+
A10: "Server-Side Request Forgery"
|
|
3249
|
+
};
|
|
3250
|
+
const covered = [];
|
|
3251
|
+
const uncovered = [];
|
|
3252
|
+
const lines = [
|
|
3253
|
+
"# OWASP Top 10 Pattern Coverage",
|
|
3254
|
+
"",
|
|
3255
|
+
`Total rules loaded: ${rules.length}`,
|
|
3256
|
+
"",
|
|
3257
|
+
"Category | Rules | Matches",
|
|
3258
|
+
"-------- | ----- | -------"
|
|
3259
|
+
];
|
|
3260
|
+
for (const cat of owaspTop10) {
|
|
3261
|
+
const catRules = rulesByCategory.get(cat) ?? [];
|
|
3262
|
+
const matchCount = matchCountsByCategory.get(cat) ?? 0;
|
|
3263
|
+
const label = `${cat}: ${owaspNames[cat] ?? cat}`;
|
|
3264
|
+
if (catRules.length > 0) {
|
|
3265
|
+
covered.push(label);
|
|
3266
|
+
lines.push(`${label} | ${catRules.length} | ${matchCount}`);
|
|
3267
|
+
} else {
|
|
3268
|
+
uncovered.push(label);
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
for (const [cat, catRules] of rulesByCategory.entries()) {
|
|
3272
|
+
if (!owaspTop10.includes(cat)) {
|
|
3273
|
+
const matchCount = matchCountsByCategory.get(cat) ?? 0;
|
|
3274
|
+
lines.push(`${cat} | ${catRules.length} | ${matchCount}`);
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
if (uncovered.length > 0) {
|
|
3278
|
+
lines.push("", `Uncovered categories (${uncovered.length}):`, ...uncovered.map((c) => ` - ${c}`));
|
|
3279
|
+
}
|
|
3280
|
+
lines.push("", `Coverage: ${covered.length} / ${owaspTop10.length} OWASP Top 10 categories`);
|
|
3281
|
+
return lines.join("\n");
|
|
3282
|
+
}
|
|
3283
|
+
case "kirograph_pattern_save_baseline": {
|
|
3284
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
3285
|
+
const projectRoot = cg.getProjectRoot();
|
|
3286
|
+
const config = await loadConfig(projectRoot);
|
|
3287
|
+
if (!config.enablePatterns) {
|
|
3288
|
+
return "Pattern analysis is not enabled. Set enablePatterns: true in .kirograph/config.json and run kirograph index.";
|
|
3289
|
+
}
|
|
3290
|
+
const label = args.label ?? "default";
|
|
3291
|
+
const db = cg.getDatabase();
|
|
3292
|
+
db.applyPatternsSchema();
|
|
3293
|
+
const rawDb = db.getRawDb();
|
|
3294
|
+
const tableExists = rawDb.get("SELECT name FROM sqlite_master WHERE type='table' AND name='pattern_matches'");
|
|
3295
|
+
if (!tableExists) {
|
|
3296
|
+
return "No pattern_matches table found. Run kirograph index first to populate pattern data.";
|
|
3297
|
+
}
|
|
3298
|
+
const rows = rawDb.all(
|
|
3299
|
+
"SELECT pattern_id, COUNT(*) as cnt FROM pattern_matches GROUP BY pattern_id"
|
|
3300
|
+
);
|
|
3301
|
+
const baseline = {};
|
|
3302
|
+
for (const row of rows) {
|
|
3303
|
+
baseline[row.pattern_id] = row.cnt;
|
|
3304
|
+
}
|
|
3305
|
+
const kirographDir = path.join(projectRoot, ".kirograph");
|
|
3306
|
+
if (!fs.existsSync(kirographDir)) fs.mkdirSync(kirographDir, { recursive: true });
|
|
3307
|
+
const baselinePath = path.join(kirographDir, `pattern-baseline-${label}.json`);
|
|
3308
|
+
fs.writeFileSync(baselinePath, JSON.stringify({ label, savedAt: Date.now(), counts: baseline }, null, 2));
|
|
3309
|
+
const totalMatches = Object.values(baseline).reduce((s, c) => s + c, 0);
|
|
3310
|
+
return `Baseline "${label}" saved: ${Object.keys(baseline).length} patterns, ${totalMatches} total matches \u2192 ${baselinePath}`;
|
|
3311
|
+
}
|
|
3312
|
+
case "kirograph_pattern_diff": {
|
|
3313
|
+
const { loadConfig } = await Promise.resolve().then(() => require("../config.js"));
|
|
3314
|
+
const projectRoot = cg.getProjectRoot();
|
|
3315
|
+
const config = await loadConfig(projectRoot);
|
|
3316
|
+
if (!config.enablePatterns) {
|
|
3317
|
+
return "Pattern analysis is not enabled. Set enablePatterns: true in .kirograph/config.json and run kirograph index.";
|
|
3318
|
+
}
|
|
3319
|
+
const label = args.label ?? "default";
|
|
3320
|
+
const baselinePath = path.join(projectRoot, ".kirograph", `pattern-baseline-${label}.json`);
|
|
3321
|
+
if (!fs.existsSync(baselinePath)) {
|
|
3322
|
+
return `Baseline "${label}" not found. Run kirograph_pattern_save_baseline first.`;
|
|
3323
|
+
}
|
|
3324
|
+
let baseline;
|
|
3325
|
+
try {
|
|
3326
|
+
baseline = JSON.parse(fs.readFileSync(baselinePath, "utf8"));
|
|
3327
|
+
} catch {
|
|
3328
|
+
return `Failed to read baseline file: ${baselinePath}`;
|
|
3329
|
+
}
|
|
3330
|
+
const db = cg.getDatabase();
|
|
3331
|
+
db.applyPatternsSchema();
|
|
3332
|
+
const rawDb = db.getRawDb();
|
|
3333
|
+
const tableExists = rawDb.get("SELECT name FROM sqlite_master WHERE type='table' AND name='pattern_matches'");
|
|
3334
|
+
const currentCounts = {};
|
|
3335
|
+
if (tableExists) {
|
|
3336
|
+
const rows = rawDb.all(
|
|
3337
|
+
"SELECT pattern_id, COUNT(*) as cnt FROM pattern_matches GROUP BY pattern_id"
|
|
3338
|
+
);
|
|
3339
|
+
for (const row of rows) {
|
|
3340
|
+
currentCounts[row.pattern_id] = row.cnt;
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
const allPatternIds = /* @__PURE__ */ new Set([...Object.keys(baseline.counts), ...Object.keys(currentCounts)]);
|
|
3344
|
+
const newFindings = [];
|
|
3345
|
+
const resolvedFindings = [];
|
|
3346
|
+
const unchanged = [];
|
|
3347
|
+
const changed = [];
|
|
3348
|
+
for (const id of allPatternIds) {
|
|
3349
|
+
const before = baseline.counts[id] ?? 0;
|
|
3350
|
+
const after = currentCounts[id] ?? 0;
|
|
3351
|
+
if (before === 0 && after > 0) {
|
|
3352
|
+
newFindings.push({ id, count: after });
|
|
3353
|
+
} else if (before > 0 && after === 0) {
|
|
3354
|
+
resolvedFindings.push({ id, count: before });
|
|
3355
|
+
} else if (before === after) {
|
|
3356
|
+
unchanged.push({ id, count: after });
|
|
3357
|
+
} else {
|
|
3358
|
+
changed.push({ id, before, after, delta: after - before });
|
|
3359
|
+
}
|
|
3360
|
+
}
|
|
3361
|
+
const savedDate = new Date(baseline.savedAt).toISOString().slice(0, 16).replace("T", " ");
|
|
3362
|
+
const lines = [
|
|
3363
|
+
`# Pattern Diff: "${label}" (saved ${savedDate}) \u2192 current`,
|
|
3364
|
+
"",
|
|
3365
|
+
`NEW: ${newFindings.length} RESOLVED: ${resolvedFindings.length} CHANGED: ${changed.length} UNCHANGED: ${unchanged.length}`
|
|
3366
|
+
];
|
|
3367
|
+
if (newFindings.length > 0) {
|
|
3368
|
+
lines.push("", "## New Findings (not in baseline)");
|
|
3369
|
+
for (const f of newFindings.sort((a, b) => b.count - a.count)) {
|
|
3370
|
+
lines.push(` + ${f.id}: ${f.count} matches`);
|
|
3371
|
+
}
|
|
3372
|
+
}
|
|
3373
|
+
if (resolvedFindings.length > 0) {
|
|
3374
|
+
lines.push("", "## Resolved (in baseline, not in current)");
|
|
3375
|
+
for (const f of resolvedFindings.sort((a, b) => b.count - a.count)) {
|
|
3376
|
+
lines.push(` - ${f.id}: was ${f.count} matches`);
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
if (changed.length > 0) {
|
|
3380
|
+
lines.push("", "## Changed");
|
|
3381
|
+
for (const f of changed.sort((a, b) => Math.abs(b.delta) - Math.abs(a.delta))) {
|
|
3382
|
+
const sign = f.delta > 0 ? "+" : "";
|
|
3383
|
+
lines.push(` ~ ${f.id}: ${f.before} \u2192 ${f.after} (${sign}${f.delta})`);
|
|
3384
|
+
}
|
|
3385
|
+
}
|
|
3386
|
+
return lines.join("\n");
|
|
3387
|
+
}
|
|
1833
3388
|
default:
|
|
1834
3389
|
return `Unknown tool: ${toolName}`;
|
|
1835
3390
|
}
|
|
@@ -1838,6 +3393,7 @@ ${lines.join("\n")}`;
|
|
|
1838
3393
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1839
3394
|
0 && (module.exports = {
|
|
1840
3395
|
KIROGRAPH_TOOL_NAMES,
|
|
3396
|
+
LIVE_SEARCH_TOOL_DEFINITION,
|
|
1841
3397
|
ToolHandler,
|
|
1842
3398
|
tools
|
|
1843
3399
|
});
|