claude-crap 0.1.2
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/CHANGELOG.md +308 -0
- package/LICENSE +21 -0
- package/README.md +550 -0
- package/bin/claude-crap.mjs +141 -0
- package/dist/adapters/bandit.d.ts +48 -0
- package/dist/adapters/bandit.d.ts.map +1 -0
- package/dist/adapters/bandit.js +145 -0
- package/dist/adapters/bandit.js.map +1 -0
- package/dist/adapters/common.d.ts +73 -0
- package/dist/adapters/common.d.ts.map +1 -0
- package/dist/adapters/common.js +78 -0
- package/dist/adapters/common.js.map +1 -0
- package/dist/adapters/eslint.d.ts +52 -0
- package/dist/adapters/eslint.d.ts.map +1 -0
- package/dist/adapters/eslint.js +142 -0
- package/dist/adapters/eslint.js.map +1 -0
- package/dist/adapters/index.d.ts +47 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +64 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/semgrep.d.ts +30 -0
- package/dist/adapters/semgrep.d.ts.map +1 -0
- package/dist/adapters/semgrep.js +130 -0
- package/dist/adapters/semgrep.js.map +1 -0
- package/dist/adapters/stryker.d.ts +55 -0
- package/dist/adapters/stryker.d.ts.map +1 -0
- package/dist/adapters/stryker.js +165 -0
- package/dist/adapters/stryker.js.map +1 -0
- package/dist/ast/cyclomatic.d.ts +48 -0
- package/dist/ast/cyclomatic.d.ts.map +1 -0
- package/dist/ast/cyclomatic.js +106 -0
- package/dist/ast/cyclomatic.js.map +1 -0
- package/dist/ast/index.d.ts +26 -0
- package/dist/ast/index.d.ts.map +1 -0
- package/dist/ast/index.js +23 -0
- package/dist/ast/index.js.map +1 -0
- package/dist/ast/language-config.d.ts +70 -0
- package/dist/ast/language-config.d.ts.map +1 -0
- package/dist/ast/language-config.js +192 -0
- package/dist/ast/language-config.js.map +1 -0
- package/dist/ast/tree-sitter-engine.d.ts +133 -0
- package/dist/ast/tree-sitter-engine.d.ts.map +1 -0
- package/dist/ast/tree-sitter-engine.js +270 -0
- package/dist/ast/tree-sitter-engine.js.map +1 -0
- package/dist/config.d.ts +57 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +78 -0
- package/dist/config.js.map +1 -0
- package/dist/crap-config.d.ts +97 -0
- package/dist/crap-config.d.ts.map +1 -0
- package/dist/crap-config.js +144 -0
- package/dist/crap-config.js.map +1 -0
- package/dist/dashboard/server.d.ts +65 -0
- package/dist/dashboard/server.d.ts.map +1 -0
- package/dist/dashboard/server.js +147 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +574 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics/crap.d.ts +71 -0
- package/dist/metrics/crap.d.ts.map +1 -0
- package/dist/metrics/crap.js +67 -0
- package/dist/metrics/crap.js.map +1 -0
- package/dist/metrics/index.d.ts +31 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +27 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/score.d.ts +143 -0
- package/dist/metrics/score.d.ts.map +1 -0
- package/dist/metrics/score.js +224 -0
- package/dist/metrics/score.js.map +1 -0
- package/dist/metrics/tdr.d.ts +106 -0
- package/dist/metrics/tdr.d.ts.map +1 -0
- package/dist/metrics/tdr.js +117 -0
- package/dist/metrics/tdr.js.map +1 -0
- package/dist/metrics/workspace-walker.d.ts +43 -0
- package/dist/metrics/workspace-walker.d.ts.map +1 -0
- package/dist/metrics/workspace-walker.js +137 -0
- package/dist/metrics/workspace-walker.js.map +1 -0
- package/dist/sarif/index.d.ts +21 -0
- package/dist/sarif/index.d.ts.map +1 -0
- package/dist/sarif/index.js +19 -0
- package/dist/sarif/index.js.map +1 -0
- package/dist/sarif/sarif-builder.d.ts +128 -0
- package/dist/sarif/sarif-builder.d.ts.map +1 -0
- package/dist/sarif/sarif-builder.js +79 -0
- package/dist/sarif/sarif-builder.js.map +1 -0
- package/dist/sarif/sarif-store.d.ts +205 -0
- package/dist/sarif/sarif-store.d.ts.map +1 -0
- package/dist/sarif/sarif-store.js +246 -0
- package/dist/sarif/sarif-store.js.map +1 -0
- package/dist/sarif/sarif-validator.d.ts +45 -0
- package/dist/sarif/sarif-validator.d.ts.map +1 -0
- package/dist/sarif/sarif-validator.js +138 -0
- package/dist/sarif/sarif-validator.js.map +1 -0
- package/dist/schemas/tool-schemas.d.ts +216 -0
- package/dist/schemas/tool-schemas.d.ts.map +1 -0
- package/dist/schemas/tool-schemas.js +208 -0
- package/dist/schemas/tool-schemas.js.map +1 -0
- package/dist/sdk.d.ts +45 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.js +44 -0
- package/dist/sdk.js.map +1 -0
- package/dist/tools/index.d.ts +24 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +23 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/test-harness.d.ts +75 -0
- package/dist/tools/test-harness.d.ts.map +1 -0
- package/dist/tools/test-harness.js +137 -0
- package/dist/tools/test-harness.js.map +1 -0
- package/dist/workspace-guard.d.ts +53 -0
- package/dist/workspace-guard.d.ts.map +1 -0
- package/dist/workspace-guard.js +61 -0
- package/dist/workspace-guard.js.map +1 -0
- package/package.json +133 -0
- package/plugin/.claude-plugin/plugin.json +29 -0
- package/plugin/.mcp.json +18 -0
- package/plugin/CLAUDE.md +143 -0
- package/plugin/bundle/dashboard/public/index.html +368 -0
- package/plugin/bundle/dashboard/public/vendor/vue.global.prod.js +9 -0
- package/plugin/bundle/mcp-server.mjs +8718 -0
- package/plugin/bundle/mcp-server.mjs.map +7 -0
- package/plugin/bundle/tdr-engine.mjs +50 -0
- package/plugin/bundle/tdr-engine.mjs.map +7 -0
- package/plugin/hooks/hooks.json +62 -0
- package/plugin/hooks/lib/crap-config.mjs +152 -0
- package/plugin/hooks/lib/gatekeeper-rules.mjs +257 -0
- package/plugin/hooks/lib/hook-io.mjs +151 -0
- package/plugin/hooks/lib/quality-gate.mjs +329 -0
- package/plugin/hooks/lib/test-harness.mjs +152 -0
- package/plugin/hooks/post-tool-use.mjs +245 -0
- package/plugin/hooks/pre-tool-use.mjs +290 -0
- package/plugin/hooks/session-start.mjs +109 -0
- package/plugin/hooks/stop-quality-gate.mjs +226 -0
- package/plugin/package.json +18 -0
- package/plugin/skills/adopt/SKILL.md +74 -0
- package/plugin/skills/analyze/SKILL.md +77 -0
- package/plugin/skills/check-test/SKILL.md +50 -0
- package/plugin/skills/score/SKILL.md +31 -0
- package/scripts/bug-report.mjs +328 -0
- package/scripts/build-fast.mjs +130 -0
- package/scripts/bundle-plugin.mjs +74 -0
- package/scripts/doctor.mjs +320 -0
- package/scripts/install.mjs +192 -0
- package/scripts/lib/cli-ui.mjs +122 -0
- package/scripts/postinstall.mjs +127 -0
- package/scripts/run-tests.mjs +95 -0
- package/scripts/status.mjs +110 -0
- package/scripts/uninstall.mjs +72 -0
- package/src/adapters/bandit.ts +191 -0
- package/src/adapters/common.ts +133 -0
- package/src/adapters/eslint.ts +187 -0
- package/src/adapters/index.ts +78 -0
- package/src/adapters/semgrep.ts +150 -0
- package/src/adapters/stryker.ts +218 -0
- package/src/ast/cyclomatic.ts +131 -0
- package/src/ast/index.ts +33 -0
- package/src/ast/language-config.ts +231 -0
- package/src/ast/tree-sitter-engine.ts +385 -0
- package/src/config.ts +109 -0
- package/src/crap-config.ts +196 -0
- package/src/dashboard/public/index.html +368 -0
- package/src/dashboard/public/vendor/vue.global.prod.js +9 -0
- package/src/dashboard/server.ts +205 -0
- package/src/index.ts +696 -0
- package/src/metrics/crap.ts +101 -0
- package/src/metrics/index.ts +51 -0
- package/src/metrics/score.ts +329 -0
- package/src/metrics/tdr.ts +155 -0
- package/src/metrics/workspace-walker.ts +146 -0
- package/src/sarif/index.ts +31 -0
- package/src/sarif/sarif-builder.ts +139 -0
- package/src/sarif/sarif-store.ts +347 -0
- package/src/sarif/sarif-validator.ts +145 -0
- package/src/schemas/tool-schemas.ts +225 -0
- package/src/sdk.ts +110 -0
- package/src/tests/adapters/bandit.test.ts +111 -0
- package/src/tests/adapters/dispatch.test.ts +100 -0
- package/src/tests/adapters/eslint.test.ts +138 -0
- package/src/tests/adapters/semgrep.test.ts +125 -0
- package/src/tests/adapters/stryker.test.ts +103 -0
- package/src/tests/crap-config.test.ts +228 -0
- package/src/tests/crap.test.ts +59 -0
- package/src/tests/cyclomatic.test.ts +87 -0
- package/src/tests/dashboard-http.test.ts +108 -0
- package/src/tests/dashboard-integrity.test.ts +128 -0
- package/src/tests/integration/mcp-server.integration.test.ts +352 -0
- package/src/tests/pre-tool-use-hook.test.ts +178 -0
- package/src/tests/sarif-store.test.ts +241 -0
- package/src/tests/sarif-validator.test.ts +164 -0
- package/src/tests/score.test.ts +260 -0
- package/src/tests/skills-frontmatter.test.ts +172 -0
- package/src/tests/stop-quality-gate-strictness.test.ts +243 -0
- package/src/tests/tdr.test.ts +86 -0
- package/src/tests/test-harness.test.ts +153 -0
- package/src/tests/workspace-guard.test.ts +111 -0
- package/src/tools/index.ts +24 -0
- package/src/tools/test-harness.ts +158 -0
- package/src/workspace-guard.ts +64 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bandit adapter.
|
|
3
|
+
*
|
|
4
|
+
* Bandit is a Python security linter. When run with `-f json` it
|
|
5
|
+
* emits a JSON report shaped like this (abbreviated):
|
|
6
|
+
*
|
|
7
|
+
* {
|
|
8
|
+
* "results": [
|
|
9
|
+
* {
|
|
10
|
+
* "filename": "app.py",
|
|
11
|
+
* "line_number": 42,
|
|
12
|
+
* "col_offset": 5,
|
|
13
|
+
* "test_id": "B608",
|
|
14
|
+
* "test_name": "hardcoded_sql_expressions",
|
|
15
|
+
* "issue_severity": "HIGH", // LOW | MEDIUM | HIGH
|
|
16
|
+
* "issue_confidence": "HIGH", // LOW | MEDIUM | HIGH
|
|
17
|
+
* "issue_text": "Possible SQL injection via string-based query construction.",
|
|
18
|
+
* "issue_cwe": { "id": 89 }
|
|
19
|
+
* }
|
|
20
|
+
* ],
|
|
21
|
+
* "metrics": { ... },
|
|
22
|
+
* "errors": [ ... ]
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* This adapter converts each `results[]` entry into a SARIF 2.1.0
|
|
26
|
+
* `result`, mapping Bandit severity levels to SARIF levels:
|
|
27
|
+
*
|
|
28
|
+
* LOW → "note"
|
|
29
|
+
* MEDIUM → "warning"
|
|
30
|
+
* HIGH → "error"
|
|
31
|
+
*
|
|
32
|
+
* Every finding gets a rule id of `bandit.<test_id>` (e.g.
|
|
33
|
+
* `bandit.B608`) so it is trivial to correlate with Bandit's own docs
|
|
34
|
+
* from inside the claude-crap dashboard.
|
|
35
|
+
*
|
|
36
|
+
* @module adapters/bandit
|
|
37
|
+
*/
|
|
38
|
+
import { type AdapterResult } from "./common.js";
|
|
39
|
+
/**
|
|
40
|
+
* Accept a Bandit JSON report and return a normalized
|
|
41
|
+
* `PersistedSarif` document.
|
|
42
|
+
*
|
|
43
|
+
* @param input Raw Bandit JSON (string or parsed object).
|
|
44
|
+
* @returns Adapter result.
|
|
45
|
+
* @throws When the input does not look like a Bandit report.
|
|
46
|
+
*/
|
|
47
|
+
export declare function adaptBandit(input: unknown): AdapterResult;
|
|
48
|
+
//# sourceMappingURL=bandit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bandit.d.ts","sourceRoot":"","sources":["../../src/adapters/bandit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,aAAa,CAAC;AAoBrB;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CA6DzD"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bandit adapter.
|
|
3
|
+
*
|
|
4
|
+
* Bandit is a Python security linter. When run with `-f json` it
|
|
5
|
+
* emits a JSON report shaped like this (abbreviated):
|
|
6
|
+
*
|
|
7
|
+
* {
|
|
8
|
+
* "results": [
|
|
9
|
+
* {
|
|
10
|
+
* "filename": "app.py",
|
|
11
|
+
* "line_number": 42,
|
|
12
|
+
* "col_offset": 5,
|
|
13
|
+
* "test_id": "B608",
|
|
14
|
+
* "test_name": "hardcoded_sql_expressions",
|
|
15
|
+
* "issue_severity": "HIGH", // LOW | MEDIUM | HIGH
|
|
16
|
+
* "issue_confidence": "HIGH", // LOW | MEDIUM | HIGH
|
|
17
|
+
* "issue_text": "Possible SQL injection via string-based query construction.",
|
|
18
|
+
* "issue_cwe": { "id": 89 }
|
|
19
|
+
* }
|
|
20
|
+
* ],
|
|
21
|
+
* "metrics": { ... },
|
|
22
|
+
* "errors": [ ... ]
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* This adapter converts each `results[]` entry into a SARIF 2.1.0
|
|
26
|
+
* `result`, mapping Bandit severity levels to SARIF levels:
|
|
27
|
+
*
|
|
28
|
+
* LOW → "note"
|
|
29
|
+
* MEDIUM → "warning"
|
|
30
|
+
* HIGH → "error"
|
|
31
|
+
*
|
|
32
|
+
* Every finding gets a rule id of `bandit.<test_id>` (e.g.
|
|
33
|
+
* `bandit.B608`) so it is trivial to correlate with Bandit's own docs
|
|
34
|
+
* from inside the claude-crap dashboard.
|
|
35
|
+
*
|
|
36
|
+
* @module adapters/bandit
|
|
37
|
+
*/
|
|
38
|
+
import { estimateEffortMinutes, wrapResultsInSarif, } from "./common.js";
|
|
39
|
+
const BANDIT = "bandit";
|
|
40
|
+
/**
|
|
41
|
+
* Accept a Bandit JSON report and return a normalized
|
|
42
|
+
* `PersistedSarif` document.
|
|
43
|
+
*
|
|
44
|
+
* @param input Raw Bandit JSON (string or parsed object).
|
|
45
|
+
* @returns Adapter result.
|
|
46
|
+
* @throws When the input does not look like a Bandit report.
|
|
47
|
+
*/
|
|
48
|
+
export function adaptBandit(input) {
|
|
49
|
+
const parsed = typeof input === "string" ? JSON.parse(input) : input;
|
|
50
|
+
if (!parsed || typeof parsed !== "object") {
|
|
51
|
+
throw new Error(`[adapter:bandit] expected a JSON object`);
|
|
52
|
+
}
|
|
53
|
+
const report = parsed;
|
|
54
|
+
if (!Array.isArray(report.results)) {
|
|
55
|
+
throw new Error(`[adapter:bandit] report is missing a results[] array`);
|
|
56
|
+
}
|
|
57
|
+
const results = [];
|
|
58
|
+
let totalEffortMinutes = 0;
|
|
59
|
+
for (const finding of report.results) {
|
|
60
|
+
const filename = finding.filename;
|
|
61
|
+
if (typeof filename !== "string" || !filename)
|
|
62
|
+
continue;
|
|
63
|
+
const level = mapSeverity(finding.issue_severity);
|
|
64
|
+
// High-severity security findings cost more to fix than the
|
|
65
|
+
// generic default, so we bias the budget toward reality. Bandit
|
|
66
|
+
// is always security-focused, so every finding is treated as a
|
|
67
|
+
// security issue for TDR accounting downstream.
|
|
68
|
+
const effortOverride = level === "error" ? 120 : level === "warning" ? 60 : 20;
|
|
69
|
+
const effort = estimateEffortMinutes(level, effortOverride);
|
|
70
|
+
totalEffortMinutes += effort;
|
|
71
|
+
const testId = finding.test_id ?? "unknown";
|
|
72
|
+
const ruleId = `bandit.${testId}`;
|
|
73
|
+
const messageText = finding.issue_text ??
|
|
74
|
+
`${finding.test_name ?? "Bandit finding"} (${finding.issue_severity ?? "UNKNOWN"})`;
|
|
75
|
+
const startLine = typeof finding.line_number === "number" && finding.line_number > 0
|
|
76
|
+
? finding.line_number
|
|
77
|
+
: 1;
|
|
78
|
+
const startColumn = typeof finding.col_offset === "number" && finding.col_offset >= 0
|
|
79
|
+
? finding.col_offset + 1
|
|
80
|
+
: 1;
|
|
81
|
+
results.push(buildSarifResult({
|
|
82
|
+
ruleId,
|
|
83
|
+
level,
|
|
84
|
+
message: messageText,
|
|
85
|
+
uri: filename,
|
|
86
|
+
startLine,
|
|
87
|
+
startColumn,
|
|
88
|
+
effortMinutes: effort,
|
|
89
|
+
cwe: finding.issue_cwe?.id,
|
|
90
|
+
confidence: finding.issue_confidence,
|
|
91
|
+
}));
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
document: wrapResultsInSarif(BANDIT, "unknown", results),
|
|
95
|
+
sourceTool: BANDIT,
|
|
96
|
+
findingCount: results.length,
|
|
97
|
+
totalEffortMinutes,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Map Bandit's `issue_severity` string to a SARIF level. Unknown
|
|
102
|
+
* values default to `"warning"` so findings are still surfaced.
|
|
103
|
+
*/
|
|
104
|
+
function mapSeverity(severity) {
|
|
105
|
+
switch ((severity ?? "").toUpperCase()) {
|
|
106
|
+
case "HIGH":
|
|
107
|
+
return "error";
|
|
108
|
+
case "MEDIUM":
|
|
109
|
+
return "warning";
|
|
110
|
+
case "LOW":
|
|
111
|
+
return "note";
|
|
112
|
+
default:
|
|
113
|
+
return "warning";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Build the SARIF `result` object for a single Bandit finding. We
|
|
118
|
+
* stash the CWE id and Bandit confidence in the `properties` bag so
|
|
119
|
+
* consumers can surface them in the dashboard hot-spot view.
|
|
120
|
+
*/
|
|
121
|
+
function buildSarifResult(opts) {
|
|
122
|
+
return {
|
|
123
|
+
ruleId: opts.ruleId,
|
|
124
|
+
level: opts.level,
|
|
125
|
+
message: { text: opts.message },
|
|
126
|
+
locations: [
|
|
127
|
+
{
|
|
128
|
+
physicalLocation: {
|
|
129
|
+
artifactLocation: { uri: opts.uri },
|
|
130
|
+
region: {
|
|
131
|
+
startLine: opts.startLine,
|
|
132
|
+
startColumn: opts.startColumn,
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
properties: {
|
|
138
|
+
sourceTool: BANDIT,
|
|
139
|
+
effortMinutes: opts.effortMinutes,
|
|
140
|
+
...(typeof opts.cwe === "number" ? { cwe: opts.cwe } : {}),
|
|
141
|
+
...(typeof opts.confidence === "string" ? { confidence: opts.confidence } : {}),
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=bandit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bandit.js","sourceRoot":"","sources":["../../src/adapters/bandit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types and helpers for per-scanner SARIF adapters.
|
|
3
|
+
*
|
|
4
|
+
* Every adapter in this directory converts a scanner's native output
|
|
5
|
+
* into a `PersistedSarif` 2.1.0 document that the `SarifStore` can
|
|
6
|
+
* ingest directly. The adapters also enrich the finding `properties`
|
|
7
|
+
* bag with a stable `effortMinutes` field so the Stop quality gate and
|
|
8
|
+
* the Technical Debt Ratio computation can treat every source tool
|
|
9
|
+
* uniformly.
|
|
10
|
+
*
|
|
11
|
+
* Rule-level effort estimates live in `DEFAULT_EFFORT_BY_SEVERITY`.
|
|
12
|
+
* Individual adapters may override the default per rule id when the
|
|
13
|
+
* scanner attaches a more specific hint.
|
|
14
|
+
*
|
|
15
|
+
* @module adapters/common
|
|
16
|
+
*/
|
|
17
|
+
import type { PersistedSarif } from "../sarif/sarif-store.js";
|
|
18
|
+
import type { SarifLevel } from "../sarif/sarif-builder.js";
|
|
19
|
+
/**
|
|
20
|
+
* The canonical list of scanners claude-crap understands. The
|
|
21
|
+
* `ingest_scanner_output` MCP tool uses this as its `enum` constraint,
|
|
22
|
+
* so keeping it narrow prevents drift.
|
|
23
|
+
*/
|
|
24
|
+
export declare const KNOWN_SCANNERS: readonly ["semgrep", "eslint", "bandit", "stryker"];
|
|
25
|
+
/**
|
|
26
|
+
* Union of supported scanner identifiers.
|
|
27
|
+
*/
|
|
28
|
+
export type KnownScanner = (typeof KNOWN_SCANNERS)[number];
|
|
29
|
+
/**
|
|
30
|
+
* Default remediation effort in minutes per SARIF severity level. These
|
|
31
|
+
* numbers are deliberately conservative — real projects should override
|
|
32
|
+
* them per rule via adapter-specific rule maps or via SARIF properties.
|
|
33
|
+
*
|
|
34
|
+
* The mapping follows the common-sense rule that every bug takes at
|
|
35
|
+
* least a test plus a patch, so even a note-level finding costs time.
|
|
36
|
+
*/
|
|
37
|
+
export declare const DEFAULT_EFFORT_BY_SEVERITY: Readonly<Record<SarifLevel, number>>;
|
|
38
|
+
/**
|
|
39
|
+
* Envelope common to every adapter output. Adapters return a
|
|
40
|
+
* `PersistedSarif` document and a small stats block describing what
|
|
41
|
+
* they saw, so the MCP tool handler can echo those stats back to the
|
|
42
|
+
* LLM even when the SarifStore rejects duplicates.
|
|
43
|
+
*/
|
|
44
|
+
export interface AdapterResult {
|
|
45
|
+
/** Normalized SARIF 2.1.0 document ready for `SarifStore.ingestRun`. */
|
|
46
|
+
readonly document: PersistedSarif;
|
|
47
|
+
/** Scanner identifier, propagated into every finding's `properties.sourceTool`. */
|
|
48
|
+
readonly sourceTool: KnownScanner;
|
|
49
|
+
/** Raw number of findings the adapter read from the scanner's native output. */
|
|
50
|
+
readonly findingCount: number;
|
|
51
|
+
/** Total estimated remediation effort across all findings, in minutes. */
|
|
52
|
+
readonly totalEffortMinutes: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build a `PersistedSarif` document from a flat list of already-mapped
|
|
56
|
+
* result entries. Every adapter produces its results with the same
|
|
57
|
+
* shape and then calls this helper to wrap them in a valid 2.1.0 envelope.
|
|
58
|
+
*
|
|
59
|
+
* @param sourceTool Stable scanner identifier (e.g. `"semgrep"`).
|
|
60
|
+
* @param version Adapter version string stored in `tool.driver.version`.
|
|
61
|
+
* @param results Pre-built SARIF `result` entries.
|
|
62
|
+
*/
|
|
63
|
+
export declare function wrapResultsInSarif(sourceTool: KnownScanner, version: string, results: ReadonlyArray<object>): PersistedSarif;
|
|
64
|
+
/**
|
|
65
|
+
* Estimate remediation effort for a single finding given its severity
|
|
66
|
+
* and an optional rule-specific override. Returns `minutes` clamped to
|
|
67
|
+
* a non-negative integer.
|
|
68
|
+
*
|
|
69
|
+
* @param level SARIF severity level (`"error"`, `"warning"`, ...).
|
|
70
|
+
* @param override Optional rule-specific effort in minutes.
|
|
71
|
+
*/
|
|
72
|
+
export declare function estimateEffortMinutes(level: SarifLevel | undefined, override?: number): number;
|
|
73
|
+
//# sourceMappingURL=common.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/adapters/common.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,cAAc,qDAAsD,CAAC;AAElF;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAK1E,CAAC;AAEH;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,mFAAmF;IACnF,QAAQ,CAAC,UAAU,EAAE,YAAY,CAAC;IAClC,gFAAgF;IAChF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,0EAA0E;IAC1E,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,YAAY,EACxB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,GAC7B,cAAc,CAgBhB;AAyBD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAM9F"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types and helpers for per-scanner SARIF adapters.
|
|
3
|
+
*
|
|
4
|
+
* Every adapter in this directory converts a scanner's native output
|
|
5
|
+
* into a `PersistedSarif` 2.1.0 document that the `SarifStore` can
|
|
6
|
+
* ingest directly. The adapters also enrich the finding `properties`
|
|
7
|
+
* bag with a stable `effortMinutes` field so the Stop quality gate and
|
|
8
|
+
* the Technical Debt Ratio computation can treat every source tool
|
|
9
|
+
* uniformly.
|
|
10
|
+
*
|
|
11
|
+
* Rule-level effort estimates live in `DEFAULT_EFFORT_BY_SEVERITY`.
|
|
12
|
+
* Individual adapters may override the default per rule id when the
|
|
13
|
+
* scanner attaches a more specific hint.
|
|
14
|
+
*
|
|
15
|
+
* @module adapters/common
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The canonical list of scanners claude-crap understands. The
|
|
19
|
+
* `ingest_scanner_output` MCP tool uses this as its `enum` constraint,
|
|
20
|
+
* so keeping it narrow prevents drift.
|
|
21
|
+
*/
|
|
22
|
+
export const KNOWN_SCANNERS = ["semgrep", "eslint", "bandit", "stryker"];
|
|
23
|
+
/**
|
|
24
|
+
* Default remediation effort in minutes per SARIF severity level. These
|
|
25
|
+
* numbers are deliberately conservative — real projects should override
|
|
26
|
+
* them per rule via adapter-specific rule maps or via SARIF properties.
|
|
27
|
+
*
|
|
28
|
+
* The mapping follows the common-sense rule that every bug takes at
|
|
29
|
+
* least a test plus a patch, so even a note-level finding costs time.
|
|
30
|
+
*/
|
|
31
|
+
export const DEFAULT_EFFORT_BY_SEVERITY = Object.freeze({
|
|
32
|
+
error: 60,
|
|
33
|
+
warning: 30,
|
|
34
|
+
note: 10,
|
|
35
|
+
none: 5,
|
|
36
|
+
});
|
|
37
|
+
/**
|
|
38
|
+
* Build a `PersistedSarif` document from a flat list of already-mapped
|
|
39
|
+
* result entries. Every adapter produces its results with the same
|
|
40
|
+
* shape and then calls this helper to wrap them in a valid 2.1.0 envelope.
|
|
41
|
+
*
|
|
42
|
+
* @param sourceTool Stable scanner identifier (e.g. `"semgrep"`).
|
|
43
|
+
* @param version Adapter version string stored in `tool.driver.version`.
|
|
44
|
+
* @param results Pre-built SARIF `result` entries.
|
|
45
|
+
*/
|
|
46
|
+
export function wrapResultsInSarif(sourceTool, version, results) {
|
|
47
|
+
return {
|
|
48
|
+
$schema: "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0.json",
|
|
49
|
+
version: "2.1.0",
|
|
50
|
+
runs: [
|
|
51
|
+
{
|
|
52
|
+
tool: {
|
|
53
|
+
driver: {
|
|
54
|
+
name: sourceTool,
|
|
55
|
+
version,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
results: results,
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Estimate remediation effort for a single finding given its severity
|
|
65
|
+
* and an optional rule-specific override. Returns `minutes` clamped to
|
|
66
|
+
* a non-negative integer.
|
|
67
|
+
*
|
|
68
|
+
* @param level SARIF severity level (`"error"`, `"warning"`, ...).
|
|
69
|
+
* @param override Optional rule-specific effort in minutes.
|
|
70
|
+
*/
|
|
71
|
+
export function estimateEffortMinutes(level, override) {
|
|
72
|
+
if (typeof override === "number" && Number.isFinite(override) && override >= 0) {
|
|
73
|
+
return Math.round(override);
|
|
74
|
+
}
|
|
75
|
+
const base = DEFAULT_EFFORT_BY_SEVERITY[level ?? "warning"];
|
|
76
|
+
return Math.max(0, Math.round(base ?? 30));
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=common.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/adapters/common.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAU,CAAC;AAOlF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAyC,MAAM,CAAC,MAAM,CAAC;IAC5F,KAAK,EAAE,EAAE;IACT,OAAO,EAAE,EAAE;IACX,IAAI,EAAE,EAAE;IACR,IAAI,EAAE,CAAC;CACR,CAAC,CAAC;AAmBH;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAAwB,EACxB,OAAe,EACf,OAA8B;IAE9B,OAAO;QACL,OAAO,EAAE,qEAAqE;QAC9E,OAAO,EAAE,OAAO;QAChB,IAAI,EAAE;YACJ;gBACE,IAAI,EAAE;oBACJ,MAAM,EAAE;wBACN,IAAI,EAAE,UAAU;wBAChB,OAAO;qBACR;iBACF;gBACD,OAAO,EAAE,OAA0C;aACpD;SACF;KACgB,CAAC;AACtB,CAAC;AAyBD;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAA6B,EAAE,QAAiB;IACpF,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAC/E,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IACD,MAAM,IAAI,GAAG,0BAA0B,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC;IAC5D,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint adapter.
|
|
3
|
+
*
|
|
4
|
+
* ESLint's default JSON output (`eslint -f json .`) is NOT SARIF.
|
|
5
|
+
* This adapter converts it into a SARIF 2.1.0 document with one
|
|
6
|
+
* `result` per ESLint `messages[]` entry, mapping ESLint's numeric
|
|
7
|
+
* severity to SARIF levels:
|
|
8
|
+
*
|
|
9
|
+
* severity 0 → "note" (parser info / disabled)
|
|
10
|
+
* severity 1 → "warning"
|
|
11
|
+
* severity 2 → "error"
|
|
12
|
+
*
|
|
13
|
+
* ESLint's JSON shape:
|
|
14
|
+
*
|
|
15
|
+
* [
|
|
16
|
+
* {
|
|
17
|
+
* "filePath": "/abs/path/to/foo.js",
|
|
18
|
+
* "messages": [
|
|
19
|
+
* {
|
|
20
|
+
* "ruleId": "no-unused-vars",
|
|
21
|
+
* "severity": 1,
|
|
22
|
+
* "message": "'foo' is defined but never used.",
|
|
23
|
+
* "line": 10,
|
|
24
|
+
* "column": 5,
|
|
25
|
+
* "endLine": 10,
|
|
26
|
+
* "endColumn": 8
|
|
27
|
+
* }
|
|
28
|
+
* ],
|
|
29
|
+
* "errorCount": 0,
|
|
30
|
+
* "warningCount": 1,
|
|
31
|
+
* "fatalErrorCount": 0,
|
|
32
|
+
* "source": "...",
|
|
33
|
+
* "usedDeprecatedRules": []
|
|
34
|
+
* }
|
|
35
|
+
* ]
|
|
36
|
+
*
|
|
37
|
+
* We preserve the full `line`/`column` range when ESLint provides one
|
|
38
|
+
* so the dashboard's hot-spot table can show a precise location.
|
|
39
|
+
*
|
|
40
|
+
* @module adapters/eslint
|
|
41
|
+
*/
|
|
42
|
+
import { type AdapterResult } from "./common.js";
|
|
43
|
+
/**
|
|
44
|
+
* Accept ESLint native JSON output and return a normalized
|
|
45
|
+
* `PersistedSarif` document plus counts.
|
|
46
|
+
*
|
|
47
|
+
* @param input Raw ESLint JSON (string or parsed array).
|
|
48
|
+
* @returns Adapter result.
|
|
49
|
+
* @throws When the input is not a valid ESLint report.
|
|
50
|
+
*/
|
|
51
|
+
export declare function adaptEslint(input: unknown): AdapterResult;
|
|
52
|
+
//# sourceMappingURL=eslint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eslint.d.ts","sourceRoot":"","sources":["../../src/adapters/eslint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAGH,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,aAAa,CAAC;AAyBrB;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CA0CzD"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESLint adapter.
|
|
3
|
+
*
|
|
4
|
+
* ESLint's default JSON output (`eslint -f json .`) is NOT SARIF.
|
|
5
|
+
* This adapter converts it into a SARIF 2.1.0 document with one
|
|
6
|
+
* `result` per ESLint `messages[]` entry, mapping ESLint's numeric
|
|
7
|
+
* severity to SARIF levels:
|
|
8
|
+
*
|
|
9
|
+
* severity 0 → "note" (parser info / disabled)
|
|
10
|
+
* severity 1 → "warning"
|
|
11
|
+
* severity 2 → "error"
|
|
12
|
+
*
|
|
13
|
+
* ESLint's JSON shape:
|
|
14
|
+
*
|
|
15
|
+
* [
|
|
16
|
+
* {
|
|
17
|
+
* "filePath": "/abs/path/to/foo.js",
|
|
18
|
+
* "messages": [
|
|
19
|
+
* {
|
|
20
|
+
* "ruleId": "no-unused-vars",
|
|
21
|
+
* "severity": 1,
|
|
22
|
+
* "message": "'foo' is defined but never used.",
|
|
23
|
+
* "line": 10,
|
|
24
|
+
* "column": 5,
|
|
25
|
+
* "endLine": 10,
|
|
26
|
+
* "endColumn": 8
|
|
27
|
+
* }
|
|
28
|
+
* ],
|
|
29
|
+
* "errorCount": 0,
|
|
30
|
+
* "warningCount": 1,
|
|
31
|
+
* "fatalErrorCount": 0,
|
|
32
|
+
* "source": "...",
|
|
33
|
+
* "usedDeprecatedRules": []
|
|
34
|
+
* }
|
|
35
|
+
* ]
|
|
36
|
+
*
|
|
37
|
+
* We preserve the full `line`/`column` range when ESLint provides one
|
|
38
|
+
* so the dashboard's hot-spot table can show a precise location.
|
|
39
|
+
*
|
|
40
|
+
* @module adapters/eslint
|
|
41
|
+
*/
|
|
42
|
+
import { estimateEffortMinutes, wrapResultsInSarif, } from "./common.js";
|
|
43
|
+
const ESLINT = "eslint";
|
|
44
|
+
/**
|
|
45
|
+
* Accept ESLint native JSON output and return a normalized
|
|
46
|
+
* `PersistedSarif` document plus counts.
|
|
47
|
+
*
|
|
48
|
+
* @param input Raw ESLint JSON (string or parsed array).
|
|
49
|
+
* @returns Adapter result.
|
|
50
|
+
* @throws When the input is not a valid ESLint report.
|
|
51
|
+
*/
|
|
52
|
+
export function adaptEslint(input) {
|
|
53
|
+
const parsed = typeof input === "string" ? JSON.parse(input) : input;
|
|
54
|
+
if (!Array.isArray(parsed)) {
|
|
55
|
+
throw new Error(`[adapter:eslint] expected an array of file reports`);
|
|
56
|
+
}
|
|
57
|
+
const results = [];
|
|
58
|
+
let totalEffortMinutes = 0;
|
|
59
|
+
for (const fileReport of parsed) {
|
|
60
|
+
const filePath = fileReport?.filePath;
|
|
61
|
+
if (typeof filePath !== "string" || !filePath)
|
|
62
|
+
continue;
|
|
63
|
+
const messages = Array.isArray(fileReport.messages) ? fileReport.messages : [];
|
|
64
|
+
for (const msg of messages) {
|
|
65
|
+
const level = mapSeverity(msg.severity);
|
|
66
|
+
const ruleId = typeof msg.ruleId === "string" ? msg.ruleId : "eslint.unknown";
|
|
67
|
+
const line = typeof msg.line === "number" && msg.line > 0 ? msg.line : 1;
|
|
68
|
+
const column = typeof msg.column === "number" && msg.column > 0 ? msg.column : 1;
|
|
69
|
+
const effort = estimateEffortMinutes(level);
|
|
70
|
+
totalEffortMinutes += effort;
|
|
71
|
+
results.push(buildSarifResult({
|
|
72
|
+
ruleId,
|
|
73
|
+
level,
|
|
74
|
+
message: msg.message ?? ruleId,
|
|
75
|
+
uri: filePath,
|
|
76
|
+
startLine: line,
|
|
77
|
+
startColumn: column,
|
|
78
|
+
endLine: typeof msg.endLine === "number" ? msg.endLine : undefined,
|
|
79
|
+
endColumn: typeof msg.endColumn === "number" ? msg.endColumn : undefined,
|
|
80
|
+
effortMinutes: effort,
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
document: wrapResultsInSarif(ESLINT, "unknown", results),
|
|
86
|
+
sourceTool: ESLINT,
|
|
87
|
+
findingCount: results.length,
|
|
88
|
+
totalEffortMinutes,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Translate ESLint's numeric severity to a SARIF level. ESLint uses:
|
|
93
|
+
*
|
|
94
|
+
* 0 = off / disabled → `"note"` (informational)
|
|
95
|
+
* 1 = warn → `"warning"`
|
|
96
|
+
* 2 = error → `"error"`
|
|
97
|
+
*
|
|
98
|
+
* Unknown values default to `"warning"` so the finding is still
|
|
99
|
+
* visible without being treated as a blocker.
|
|
100
|
+
*/
|
|
101
|
+
function mapSeverity(severity) {
|
|
102
|
+
switch (severity) {
|
|
103
|
+
case 2:
|
|
104
|
+
return "error";
|
|
105
|
+
case 1:
|
|
106
|
+
return "warning";
|
|
107
|
+
case 0:
|
|
108
|
+
return "note";
|
|
109
|
+
default:
|
|
110
|
+
return "warning";
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Assemble a SARIF `result` object from the narrow set of fields an
|
|
115
|
+
* ESLint message provides. The shape matches what the SarifStore
|
|
116
|
+
* expects when hydrating a finding from a persisted document.
|
|
117
|
+
*/
|
|
118
|
+
function buildSarifResult(opts) {
|
|
119
|
+
return {
|
|
120
|
+
ruleId: opts.ruleId,
|
|
121
|
+
level: opts.level,
|
|
122
|
+
message: { text: opts.message },
|
|
123
|
+
locations: [
|
|
124
|
+
{
|
|
125
|
+
physicalLocation: {
|
|
126
|
+
artifactLocation: { uri: opts.uri },
|
|
127
|
+
region: {
|
|
128
|
+
startLine: opts.startLine,
|
|
129
|
+
startColumn: opts.startColumn,
|
|
130
|
+
...(opts.endLine !== undefined ? { endLine: opts.endLine } : {}),
|
|
131
|
+
...(opts.endColumn !== undefined ? { endColumn: opts.endColumn } : {}),
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
properties: {
|
|
137
|
+
sourceTool: ESLINT,
|
|
138
|
+
effortMinutes: opts.effortMinutes,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=eslint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eslint.js","sourceRoot":"","sources":["../../src/adapters/eslint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public SDK entry point for the per-scanner SARIF adapters.
|
|
3
|
+
*
|
|
4
|
+
* Adapters convert a scanner's native output (SARIF, JSON, or some
|
|
5
|
+
* other structured format) into a normalized `PersistedSarif`
|
|
6
|
+
* document that the `SarifStore` can ingest directly. Every adapter
|
|
7
|
+
* enriches its findings with a stable `effortMinutes` value on the
|
|
8
|
+
* `properties` bag so the Stop quality gate and the project score
|
|
9
|
+
* engine can compute a Technical Debt Ratio.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* import {
|
|
15
|
+
* adaptScannerOutput,
|
|
16
|
+
* adaptSemgrep,
|
|
17
|
+
* adaptEslint,
|
|
18
|
+
* adaptBandit,
|
|
19
|
+
* adaptStryker,
|
|
20
|
+
* } from "claude-crap/adapters";
|
|
21
|
+
*
|
|
22
|
+
* const result = adaptScannerOutput("eslint", rawJsonFromEslint);
|
|
23
|
+
* sarifStore.ingestRun(result.document, result.sourceTool);
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @module adapters
|
|
27
|
+
*/
|
|
28
|
+
export { adaptSemgrep } from "./semgrep.js";
|
|
29
|
+
export { adaptEslint } from "./eslint.js";
|
|
30
|
+
export { adaptBandit } from "./bandit.js";
|
|
31
|
+
export { adaptStryker } from "./stryker.js";
|
|
32
|
+
export { DEFAULT_EFFORT_BY_SEVERITY, KNOWN_SCANNERS, estimateEffortMinutes, wrapResultsInSarif, } from "./common.js";
|
|
33
|
+
export type { AdapterResult, KnownScanner } from "./common.js";
|
|
34
|
+
import type { AdapterResult, KnownScanner } from "./common.js";
|
|
35
|
+
/**
|
|
36
|
+
* Route a raw scanner output to the correct adapter based on its
|
|
37
|
+
* name. Preferred entry point for the `ingest_scanner_output` MCP
|
|
38
|
+
* tool — the dispatch is a single switch so the compiler can verify
|
|
39
|
+
* every case with `never` exhaustiveness.
|
|
40
|
+
*
|
|
41
|
+
* @param scanner One of the known scanner identifiers.
|
|
42
|
+
* @param rawOutput The scanner's native output (string or parsed).
|
|
43
|
+
* @returns A normalized `AdapterResult`.
|
|
44
|
+
* @throws When `scanner` is unknown or the raw output is malformed.
|
|
45
|
+
*/
|
|
46
|
+
export declare function adaptScannerOutput(scanner: KnownScanner, rawOutput: unknown): AdapterResult;
|
|
47
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EACL,0BAA0B,EAC1B,cAAc,EACd,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAM/D,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE/D;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,OAAO,GACjB,aAAa,CAef"}
|