@methiu/raigal 0.16.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/LICENSE +1 -0
- package/README.md +511 -0
- package/dist/AgentApp-DxfJZJXy.js +390 -0
- package/dist/adapters/astro.d.ts +22 -0
- package/dist/adapters/astro.js +20 -0
- package/dist/adapters/expo.d.ts +24 -0
- package/dist/adapters/expo.js +33 -0
- package/dist/adapters/nuxt.d.ts +33 -0
- package/dist/adapters/nuxt.js +41 -0
- package/dist/adapters/sveltekit.d.ts +15 -0
- package/dist/adapters/sveltekit.js +19 -0
- package/dist/adapters/vite.d.ts +26 -0
- package/dist/adapters/vite.js +33 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +29535 -0
- package/dist/core-BpOFk2i9.d.ts +34 -0
- package/dist/core-o3KNKhg2.js +100 -0
- package/dist/finding-assessment-M5fUTE-C.js +321 -0
- package/dist/index.d.ts +351 -0
- package/dist/index.js +20228 -0
- package/dist/json-DBN8fnd3.js +35 -0
- package/dist/json-DKuTrkDa.js +35 -0
- package/dist/mask-secrets-BXn-ZSEh.js +71 -0
- package/dist/mcp.js +15018 -0
- package/dist/mount-zBL9B_lE.js +18 -0
- package/dist/sarif-B-l1Fhzd.js +66 -0
- package/dist/sarif-B5s74n0Q.js +66 -0
- package/dist/version-CVfOxP_O.js +6 -0
- package/package.json +129 -0
- package/scripts/check-licenses.mjs +45 -0
- package/scripts/check-tarball-keys.mjs +106 -0
- package/scripts/gen-config-schema.mjs +35 -0
- package/scripts/install-tools.mjs +383 -0
- package/scripts/polyfill.cjs +10 -0
- package/scripts/release-guard.mjs +29 -0
- package/tools/jb/README.md +45 -0
- package/tools/jb/aislop.DotSettings +3 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/framework-adapters/core.d.ts
|
|
2
|
+
type RaigalFramework = "astro" | "expo" | "nuxt" | "sveltekit" | "vite" | "tanstack-start" | "redwoodsdk" | "t3";
|
|
3
|
+
type RaigalAdapterCommand = "ci" | "scan";
|
|
4
|
+
interface RaigalRunRequest {
|
|
5
|
+
framework: RaigalFramework;
|
|
6
|
+
bin: string;
|
|
7
|
+
args: string[];
|
|
8
|
+
cwd: string;
|
|
9
|
+
env: NodeJS.ProcessEnv;
|
|
10
|
+
}
|
|
11
|
+
interface RaigalRunResult {
|
|
12
|
+
command: string;
|
|
13
|
+
args: string[];
|
|
14
|
+
exitCode: number | null;
|
|
15
|
+
signal: NodeJS.Signals | null;
|
|
16
|
+
skipped: boolean;
|
|
17
|
+
}
|
|
18
|
+
type RaigalRunner = (request: RaigalRunRequest) => RaigalRunResult | Promise<RaigalRunResult>;
|
|
19
|
+
interface RaigalAdapterOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Running during a framework build is opt-in so integrations never surprise
|
|
22
|
+
* local dev servers or production builds.
|
|
23
|
+
*/
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
command?: RaigalAdapterCommand;
|
|
26
|
+
args?: string[];
|
|
27
|
+
bin?: string;
|
|
28
|
+
cwd?: string;
|
|
29
|
+
env?: NodeJS.ProcessEnv;
|
|
30
|
+
failOnError?: boolean;
|
|
31
|
+
runner?: RaigalRunner;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { RaigalFramework as n, RaigalRunResult as r, RaigalAdapterOptions as t };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
//#region src/framework-adapters/core.ts
|
|
4
|
+
const DEFAULT_ARGS = {
|
|
5
|
+
ci: ["ci"],
|
|
6
|
+
scan: ["scan"]
|
|
7
|
+
};
|
|
8
|
+
const resolveRaigalRunRequest = (framework, options = {}) => {
|
|
9
|
+
const command = options.command ?? "ci";
|
|
10
|
+
const env = options.env ? {
|
|
11
|
+
...process.env,
|
|
12
|
+
...options.env
|
|
13
|
+
} : { ...process.env };
|
|
14
|
+
return {
|
|
15
|
+
framework,
|
|
16
|
+
bin: options.bin ?? "raigal",
|
|
17
|
+
args: [...DEFAULT_ARGS[command], ...options.args ?? []],
|
|
18
|
+
cwd: options.cwd ?? process.cwd(),
|
|
19
|
+
env
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
const runRaigal = async (request) => new Promise((resolve) => {
|
|
23
|
+
spawn(request.bin, request.args, {
|
|
24
|
+
cwd: request.cwd,
|
|
25
|
+
env: request.env,
|
|
26
|
+
stdio: "inherit"
|
|
27
|
+
}).on("close", (exitCode, signal) => {
|
|
28
|
+
resolve({
|
|
29
|
+
command: request.bin,
|
|
30
|
+
args: request.args,
|
|
31
|
+
exitCode,
|
|
32
|
+
signal,
|
|
33
|
+
skipped: false
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
const maybeRunRaigal = async (framework, options = {}) => {
|
|
38
|
+
const request = resolveRaigalRunRequest(framework, options);
|
|
39
|
+
if (options.enabled !== true) return {
|
|
40
|
+
command: request.bin,
|
|
41
|
+
args: request.args,
|
|
42
|
+
exitCode: 0,
|
|
43
|
+
signal: null,
|
|
44
|
+
skipped: true
|
|
45
|
+
};
|
|
46
|
+
const result = await (options.runner ?? runRaigal)(request);
|
|
47
|
+
if (result.exitCode === 30 || result.exitCode === 31) {
|
|
48
|
+
process.stderr.write(`[raigal] Analysis skipped for ${framework}: organization entitlement required.\n`);
|
|
49
|
+
return {
|
|
50
|
+
...result,
|
|
51
|
+
exitCode: 0,
|
|
52
|
+
skipped: true
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (options.failOnError !== false && !result.skipped && result.exitCode !== 0) throw new Error(`raigal ${request.args.join(" ")} failed for ${framework} with exit code ${String(result.exitCode)}`);
|
|
56
|
+
return result;
|
|
57
|
+
};
|
|
58
|
+
const createRaigalPackageScripts = (_framework, options = {}) => {
|
|
59
|
+
const command = options.command ?? "ci";
|
|
60
|
+
const scripts = {
|
|
61
|
+
"raigal:scan": "raigal scan",
|
|
62
|
+
"raigal:ci": `raigal ${command}`,
|
|
63
|
+
"aislop:scan": "raigal scan",
|
|
64
|
+
"aislop:ci": `raigal ${command}`
|
|
65
|
+
};
|
|
66
|
+
if (options.includeAgent ?? true) {
|
|
67
|
+
scripts["raigal:agent"] = "raigal agent";
|
|
68
|
+
scripts["aislop:agent"] = "raigal agent";
|
|
69
|
+
}
|
|
70
|
+
if (options.includeHook ?? true) {
|
|
71
|
+
scripts["raigal:hook"] = "raigal hook install";
|
|
72
|
+
scripts["aislop:hook"] = "raigal hook install";
|
|
73
|
+
}
|
|
74
|
+
return scripts;
|
|
75
|
+
};
|
|
76
|
+
const createRaigalCiWorkflow = (packageManagerCommand = "npx --yes @methiu/raigal@latest ci") => [
|
|
77
|
+
"name: raigal",
|
|
78
|
+
"",
|
|
79
|
+
"on:",
|
|
80
|
+
" pull_request:",
|
|
81
|
+
" push:",
|
|
82
|
+
" branches: [main]",
|
|
83
|
+
"",
|
|
84
|
+
"permissions:",
|
|
85
|
+
" contents: read",
|
|
86
|
+
" id-token: write",
|
|
87
|
+
"",
|
|
88
|
+
"jobs:",
|
|
89
|
+
" quality-gate:",
|
|
90
|
+
" runs-on: ubuntu-latest",
|
|
91
|
+
" steps:",
|
|
92
|
+
" - uses: actions/checkout@v4",
|
|
93
|
+
" - uses: actions/setup-node@v4",
|
|
94
|
+
" with:",
|
|
95
|
+
" node-version: 22",
|
|
96
|
+
` - run: ${packageManagerCommand}`,
|
|
97
|
+
""
|
|
98
|
+
].join("\n");
|
|
99
|
+
//#endregion
|
|
100
|
+
export { createRaigalPackageScripts as n, maybeRunRaigal as r, createRaigalCiWorkflow as t };
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
//#region src/output/engine-info.ts
|
|
2
|
+
const ENGINE_INFO = {
|
|
3
|
+
format: {
|
|
4
|
+
label: "Formatting",
|
|
5
|
+
description: "Whitespace, indentation, line wrapping, and import ordering"
|
|
6
|
+
},
|
|
7
|
+
lint: {
|
|
8
|
+
label: "Linting",
|
|
9
|
+
description: "Static analysis for likely bugs and bad patterns"
|
|
10
|
+
},
|
|
11
|
+
"code-quality": {
|
|
12
|
+
label: "Code Quality",
|
|
13
|
+
description: "Complexity limits, dead code detection, and duplication checks"
|
|
14
|
+
},
|
|
15
|
+
"ai-slop": {
|
|
16
|
+
label: "AI Slop",
|
|
17
|
+
description: "Narrative comments, dead patterns, unsafe type casts, TODO stubs, generic names"
|
|
18
|
+
},
|
|
19
|
+
architecture: {
|
|
20
|
+
label: "Architecture",
|
|
21
|
+
description: "Project-specific import and layering rules"
|
|
22
|
+
},
|
|
23
|
+
security: {
|
|
24
|
+
label: "Security",
|
|
25
|
+
description: "Secret leaks, risky APIs, and dependency vulnerabilities"
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const getEngineLabel = (engine) => ENGINE_INFO[engine].label;
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/scoring/rule-impact-builders.ts
|
|
31
|
+
const strict = (rationale) => ({
|
|
32
|
+
tier: "strict",
|
|
33
|
+
multiplier: 1,
|
|
34
|
+
rationale
|
|
35
|
+
});
|
|
36
|
+
const standard = (rationale) => ({
|
|
37
|
+
tier: "standard",
|
|
38
|
+
multiplier: 1,
|
|
39
|
+
rationale
|
|
40
|
+
});
|
|
41
|
+
const maintainability = (rationale, cap = 24) => ({
|
|
42
|
+
tier: "maintainability",
|
|
43
|
+
multiplier: .75,
|
|
44
|
+
cap,
|
|
45
|
+
rationale
|
|
46
|
+
});
|
|
47
|
+
const mechanical = (rationale, cap = 16) => ({
|
|
48
|
+
tier: "mechanical",
|
|
49
|
+
multiplier: .5,
|
|
50
|
+
cap,
|
|
51
|
+
rationale
|
|
52
|
+
});
|
|
53
|
+
const style = (rationale, cap = 8) => ({
|
|
54
|
+
tier: "style",
|
|
55
|
+
multiplier: .5,
|
|
56
|
+
cap,
|
|
57
|
+
rationale
|
|
58
|
+
});
|
|
59
|
+
const advisory = (rationale, cap = 8) => ({
|
|
60
|
+
tier: "advisory",
|
|
61
|
+
multiplier: .25,
|
|
62
|
+
cap,
|
|
63
|
+
rationale
|
|
64
|
+
});
|
|
65
|
+
//#endregion
|
|
66
|
+
//#region src/scoring/rule-impact-ai-slop.ts
|
|
67
|
+
const AI_SLOP_RULE_SCORE_IMPACTS = {
|
|
68
|
+
"ai-slop/trivial-comment": style("Restating comments are style noise; keep the finding visible but score gently."),
|
|
69
|
+
"ai-slop/swallowed-exception": strict("Swallowed failures hide real broken states and deserve full scoring impact."),
|
|
70
|
+
"ai-slop/silent-recovery": strict("Logging/defaulting and continuing can corrupt behavior unless handled intentionally."),
|
|
71
|
+
"ai-slop/meta-comment": style("Process narration is cleanup noise and should not make a healthy repo look broken."),
|
|
72
|
+
"ai-slop/hidden-fallback": maintainability("Safe-looking defaults that hide missing or failed state deserve explicit review."),
|
|
73
|
+
"ai-slop/redundant-try-catch": maintainability("Redundant error plumbing adds noise but is usually not a runtime defect."),
|
|
74
|
+
"ai-slop/redundant-type-coercion": maintainability("Redundant coercion is cleanup and readability debt."),
|
|
75
|
+
"ai-slop/duplicate-type-declaration": maintainability("Duplicate types create drift risk but usually need intentional consolidation."),
|
|
76
|
+
"ai-slop/thin-wrapper": maintainability("Thin wrappers add abstraction debt without usually changing behavior."),
|
|
77
|
+
"ai-slop/generic-naming": advisory("Vague names are weak signals and often subjective."),
|
|
78
|
+
"ai-slop/unused-import": mechanical("Unused imports are mechanical cleanup."),
|
|
79
|
+
"ai-slop/unused-css": style("Dead CSS classes are cleanup, not correctness bugs, and dynamic usage can make detection lower-confidence."),
|
|
80
|
+
"ai-slop/console-leftover": style("Leftover debug output is visible cleanup unless it leaks sensitive data."),
|
|
81
|
+
"ai-slop/todo-stub": standard("Unresolved TODO/stub markers often indicate unfinished behavior."),
|
|
82
|
+
"ai-slop/unreachable-code": strict("Unreachable code is a high-confidence logic defect."),
|
|
83
|
+
"ai-slop/constant-condition": strict("Constant conditions usually indicate broken branches."),
|
|
84
|
+
"ai-slop/empty-function": standard("Empty functions may be intentional shims, but often indicate placeholder behavior."),
|
|
85
|
+
"ai-slop/unsafe-type-assertion": maintainability("Unsafe casts bypass type checks and can hide real data-shape bugs."),
|
|
86
|
+
"ai-slop/double-type-assertion": strict("Double assertions deliberately force types through unknown/any and deserve strong impact."),
|
|
87
|
+
"ai-slop/ts-directive": style("TypeScript suppressions need review, but individual directives can be intentional debt."),
|
|
88
|
+
"ai-slop/narrative-comment": style("Narrative comments are cleanup/style findings rather than defects."),
|
|
89
|
+
"ai-slop/duplicate-import": mechanical("Duplicate imports are mechanical cleanup."),
|
|
90
|
+
"ai-slop/hardcoded-url": advisory("Hardcoded URLs are medium-confidence config signals and can be intentional canonical URLs.", 4),
|
|
91
|
+
"ai-slop/hardcoded-id": advisory("Hardcoded provider IDs are config signals, but not all IDs are equally risky.", 4),
|
|
92
|
+
"ai-slop/python-bare-except": strict("Bare except catches system exits and hides real failures."),
|
|
93
|
+
"ai-slop/python-broad-except": standard("Broad exception handling is risky but sometimes intentional at boundaries."),
|
|
94
|
+
"ai-slop/python-mutable-default": strict("Mutable defaults are a high-confidence Python behavior bug."),
|
|
95
|
+
"ai-slop/python-print-debug": style("Debug print output is cleanup unless it leaks sensitive data."),
|
|
96
|
+
"ai-slop/python-range-len-loop": advisory("Index loops are style/readability signals and often not harmful."),
|
|
97
|
+
"ai-slop/python-chained-dict-get": maintainability("Chained dict fallback hides shape assumptions and deserves review."),
|
|
98
|
+
"ai-slop/python-repetitive-dispatch": maintainability("Repetitive dispatch ladders create maintainability debt."),
|
|
99
|
+
"ai-slop/python-isinstance-ladder": maintainability("Long isinstance ladders are brittle polymorphism but not immediate defects."),
|
|
100
|
+
"ai-slop/go-library-panic": maintainability("Go panics in libraries deserve review, but compiler/runtime invariants and test helpers make this lower-confidence than a confirmed defect."),
|
|
101
|
+
"ai-slop/rust-non-test-unwrap": strict("Production unwrap can panic instead of handling expected failure."),
|
|
102
|
+
"ai-slop/rust-todo-stub": standard("Rust todo/unimplemented stubs represent unfinished behavior."),
|
|
103
|
+
"ai-slop/hallucinated-import": strict("Imports missing from the manifest are high-confidence install/runtime failures."),
|
|
104
|
+
"ai-slop/tautological-test": standard("Literal-success assertions are high-confidence test gaps but do not break production directly."),
|
|
105
|
+
"ai-slop/csharp-not-implemented": standard("NotImplementedException stubs usually indicate unfinished behavior."),
|
|
106
|
+
"ai-slop/csharp-async-void": standard("async void can't be awaited and its exceptions crash the process."),
|
|
107
|
+
"ai-slop/csharp-sync-over-async": standard("Blocking on a Task with .Result/.Wait() risks deadlock and burns a thread."),
|
|
108
|
+
"ai-slop/csharp-suppressed-warning": maintainability("Unjustified warning suppression hides analyzer findings rather than fixing them."),
|
|
109
|
+
"ai-slop/csharp-empty-catch-rethrow": mechanical("catch (...) { throw; } is pure noise with no behavior change - mechanical cleanup."),
|
|
110
|
+
"ai-slop/csharp-null-forgiving": maintainability("The null-forgiving ! silences nullable safety without proving the value is non-null."),
|
|
111
|
+
"ai-slop/csharp-console-leftover": style("Leftover Console/Debug/Trace output is visible cleanup unless it leaks sensitive data."),
|
|
112
|
+
"ai-slop/csharp-redundant-doc-comment": style("XML-doc that restates the member name is a cleanup/style finding, not a defect."),
|
|
113
|
+
"ai-slop/csharp-broad-catch": standard("catch (Exception) buries specific failures, though it is sometimes intentional at boundaries."),
|
|
114
|
+
"ai-slop/csharp-linq-count": advisory("Comparing .Count() to 0/1 is a readability/perf idiom signal, often harmless."),
|
|
115
|
+
"ai-slop/csharp-index-loop": advisory("Index for-loops over .Length/.Count are style/readability signals, often not harmful."),
|
|
116
|
+
"ai-slop/csharp-if-ladder": maintainability("Repeated if/else-if ladders dispatching on one value create maintainability debt."),
|
|
117
|
+
"ai-slop/csharp-string-concat-in-loop": maintainability("Repeated string += in a loop is O(n^2) allocation churn; a StringBuilder is the fix."),
|
|
118
|
+
"ai-slop/cpp-not-implemented": standard("Stubs that throw or assert 'not implemented' usually indicate unfinished behavior."),
|
|
119
|
+
"ai-slop/cpp-using-namespace-std-in-header": maintainability("A using-directive at header scope leaks std names into every includer, a hygiene debt."),
|
|
120
|
+
"ai-slop/cpp-c-style-cast": style("A C-style cast in C++ is a cleanup/style finding; a named cast makes intent explicit."),
|
|
121
|
+
"ai-slop/cpp-manual-delete": maintainability("Manual delete is a memory-safety hazard; RAII/smart pointers own the resource instead."),
|
|
122
|
+
"ai-slop/cpp-iostream-leftover": style("Leftover std::cout/std::cerr in library code is visible cleanup unless it leaks data."),
|
|
123
|
+
"ai-slop/cpp-null-literal": style("NULL versus nullptr is a modernization/style finding; nullptr avoids integer-overload pitfalls."),
|
|
124
|
+
"ai-slop/cpp-define-constant": style("A macro constant instead of constexpr is a style/hygiene finding, not a behavior bug."),
|
|
125
|
+
"ai-slop/cpp-endl-in-stream": style("std::endl versus '\\n' is a minor performance/style finding from redundant flushes.")
|
|
126
|
+
};
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/scoring/rule-impact.ts
|
|
129
|
+
const RULE_SCORE_IMPACTS = {
|
|
130
|
+
formatting: mechanical("Formatter output is mechanical cleanup.", 12),
|
|
131
|
+
"import-order": mechanical("Import ordering is mechanical cleanup.", 12),
|
|
132
|
+
"python-formatting": mechanical("Python formatter output is mechanical cleanup.", 12),
|
|
133
|
+
"go-formatting": mechanical("Go formatter output is mechanical cleanup.", 12),
|
|
134
|
+
"rust-formatting": mechanical("Rust formatter output is mechanical cleanup.", 12),
|
|
135
|
+
"ruby-formatting": mechanical("Ruby formatter output is mechanical cleanup.", 12),
|
|
136
|
+
"php-formatting": mechanical("PHP formatter output is mechanical cleanup.", 12),
|
|
137
|
+
"csharp-formatting": mechanical("C# formatter output is mechanical cleanup.", 12),
|
|
138
|
+
"code-quality/duplicate-block": maintainability("Large copy-paste blocks are real maintenance debt, but repeated blocks are less severe than defects."),
|
|
139
|
+
"code-quality/repeated-chained-call": maintainability("Repeated call chains hurt readability and change safety without usually being runtime defects."),
|
|
140
|
+
"code-quality/unused-declaration": mechanical("Unused declarations are cleanup work and often removable, so they should not dominate the score."),
|
|
141
|
+
"complexity/file-too-large": style("Large files are reviewability pressure, but size alone is not a confirmed defect."),
|
|
142
|
+
"complexity/function-too-long": style("Long functions are maintainability pressure, but length alone is not a confirmed defect."),
|
|
143
|
+
"complexity/deep-nesting": maintainability("Deep nesting makes behavior harder to reason about and is more actionable than pure size."),
|
|
144
|
+
"complexity/too-many-params": maintainability("Large parameter lists hurt call-site safety but usually need intentional refactoring."),
|
|
145
|
+
"knip/files": mechanical("Unused files are cleanup work and can repeat heavily in stale repos."),
|
|
146
|
+
"knip/dependencies": mechanical("Unused production dependencies are cleanup and supply-chain surface, but usually mechanical."),
|
|
147
|
+
"knip/devDependencies": mechanical("Unused dev dependencies are low-risk cleanup."),
|
|
148
|
+
"knip/unlisted": strict("A used package missing from the manifest can break installs and CI."),
|
|
149
|
+
"knip/unresolved": strict("An unresolved import is a high-confidence build/runtime failure."),
|
|
150
|
+
"knip/binaries": mechanical("Unused package binaries are manifest cleanup."),
|
|
151
|
+
"knip/exports": mechanical("Unused exports are public-surface cleanup and can be noisy in libraries."),
|
|
152
|
+
"knip/types": mechanical("Unused exported types are low-risk cleanup."),
|
|
153
|
+
"knip/duplicates": maintainability("Duplicate exports are real API hygiene issues."),
|
|
154
|
+
"security/hardcoded-secret": strict("Secret-looking source literals are high-risk."),
|
|
155
|
+
"security/vulnerable-dependency": strict("Known vulnerabilities deserve full impact even when remediation varies."),
|
|
156
|
+
"security/eval": strict("Dynamic code execution can run attacker-controlled input."),
|
|
157
|
+
"security/innerhtml": strict("Raw HTML assignment can become XSS."),
|
|
158
|
+
"security/dangerously-set-innerhtml": strict("React raw HTML escape hatches can become XSS."),
|
|
159
|
+
"security/sql-injection": strict("Interpolated SQL can become data exfiltration or mutation."),
|
|
160
|
+
"security/shell-injection": strict("Interpolated shell commands can become command execution."),
|
|
161
|
+
"security/unsafe-deserialization": strict("Deserializing untrusted data with a legacy .NET formatter can become remote code execution."),
|
|
162
|
+
"security/unsafe-c-call": strict("Unbounded C string and process calls can become buffer overflows or command execution."),
|
|
163
|
+
"security/dependency-audit-skipped": advisory("An unavailable audit is visibility loss, not evidence of a vulnerability."),
|
|
164
|
+
"dotnet/projects-skipped": advisory("Skipping unrestored C# projects is visibility loss, not evidence of a defect."),
|
|
165
|
+
"cppcheck/chunks-skipped": advisory("A skipped cppcheck chunk is visibility loss, not evidence that the skipped files have a defect."),
|
|
166
|
+
"eslint/no-undef": strict("Undefined identifiers are high-confidence runtime failures."),
|
|
167
|
+
"eslint/no-unused-vars": mechanical("Unused variables are mechanical cleanup."),
|
|
168
|
+
"eslint/no-unassigned-vars": strict("Variables that are never assigned point to broken logic."),
|
|
169
|
+
"eslint/no-empty": style("Empty blocks can be intentional placeholders but should be reviewed."),
|
|
170
|
+
"eslint/no-useless-escape": mechanical("Useless escapes are mechanical cleanup."),
|
|
171
|
+
"eslint/no-unused-expressions": maintainability("Unused expressions often indicate dropped logic."),
|
|
172
|
+
"eslint/no-shadow-restricted-names": strict("Shadowing restricted names can break runtime behavior."),
|
|
173
|
+
"eslint/no-constant-binary-expression": strict("Constant binary expressions usually indicate broken conditions."),
|
|
174
|
+
"eslint/no-unsafe-optional-chaining": strict("Unsafe optional chaining can throw at runtime."),
|
|
175
|
+
"eslint/require-yield": maintainability("Generators without yield are API-shape mistakes."),
|
|
176
|
+
"import/no-duplicates": mechanical("Duplicate import paths are mechanical cleanup."),
|
|
177
|
+
"import/default": strict("Missing default exports can break module loading."),
|
|
178
|
+
"import/named": strict("Missing named exports can break module loading."),
|
|
179
|
+
"import/namespace": strict("Invalid namespace imports can break module loading."),
|
|
180
|
+
"typescript-eslint/triple-slash-reference": mechanical("Triple-slash references are usually cleanup in modern TypeScript projects."),
|
|
181
|
+
"unicorn/no-useless-fallback-in-spread": maintainability("Useless fallbacks add noise."),
|
|
182
|
+
"unicorn/prefer-string-starts-ends-with": mechanical("String predicate preferences are mechanical readability cleanup."),
|
|
183
|
+
"unicorn/no-invalid-remove-event-listener": strict("Invalid event listener removal can leave behavior broken."),
|
|
184
|
+
"unicorn/no-empty-file": mechanical("Empty files are cleanup work."),
|
|
185
|
+
"unicorn/no-useless-length-check": maintainability("Useless length checks add dead branches."),
|
|
186
|
+
"unicorn/no-new-array": maintainability("Avoiding new Array prevents sparse-array mistakes."),
|
|
187
|
+
"unicorn/no-useless-spread": maintainability("Useless spreads add noise and allocation."),
|
|
188
|
+
"unicorn/no-single-promise-in-promise-methods": maintainability("Single-element Promise combinators add unnecessary structure."),
|
|
189
|
+
...AI_SLOP_RULE_SCORE_IMPACTS
|
|
190
|
+
};
|
|
191
|
+
const DEFAULT_IMPACT = standard("Unclassified external rule uses standard impact.");
|
|
192
|
+
const WILDCARD_RULE_SCORE_IMPACTS = [
|
|
193
|
+
["oxlint/", standard("External oxlint rule uses standard lint impact.")],
|
|
194
|
+
["ruff/", standard("External ruff rule uses standard lint impact.")],
|
|
195
|
+
["go/", standard("External Go lint rule uses standard lint impact.")],
|
|
196
|
+
["clippy/", standard("External clippy rule uses standard lint impact.")],
|
|
197
|
+
["rubocop/", standard("External rubocop rule uses standard lint impact.")],
|
|
198
|
+
["typescript/", strict("TypeScript compiler diagnostics can break builds.")],
|
|
199
|
+
["expo-doctor/", maintainability("Expo Doctor findings are project-configuration hygiene.")]
|
|
200
|
+
];
|
|
201
|
+
const scoreImpactForRule = (ruleId) => RULE_SCORE_IMPACTS[ruleId] ?? WILDCARD_RULE_SCORE_IMPACTS.find(([prefix]) => ruleId.startsWith(prefix))?.[1] ?? DEFAULT_IMPACT;
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/output/finding-assessment.ts
|
|
204
|
+
const KNIP_FORCE_RULES = /* @__PURE__ */ new Set([
|
|
205
|
+
"knip/files",
|
|
206
|
+
"knip/dependencies",
|
|
207
|
+
"knip/devDependencies"
|
|
208
|
+
]);
|
|
209
|
+
const isForceFixable = (diagnostic) => {
|
|
210
|
+
if (diagnostic.fixable) return false;
|
|
211
|
+
if (KNIP_FORCE_RULES.has(diagnostic.rule)) return true;
|
|
212
|
+
if (diagnostic.rule === "security/vulnerable-dependency") return diagnostic.detail === "npm" || diagnostic.detail === "pnpm";
|
|
213
|
+
if (diagnostic.rule.startsWith("expo-doctor/")) return diagnostic.rule !== "expo-doctor/config-error";
|
|
214
|
+
return false;
|
|
215
|
+
};
|
|
216
|
+
const FINDING_KIND_LABELS = {
|
|
217
|
+
"confirmed-defect": "confirmed defects",
|
|
218
|
+
"conservative-security": "conservative security",
|
|
219
|
+
"style-policy": "style/policy",
|
|
220
|
+
"ai-slop-indicator": "AI-slop indicators"
|
|
221
|
+
};
|
|
222
|
+
const STYLE_POLICY_RULES = /* @__PURE__ */ new Set([
|
|
223
|
+
"ai-slop/trivial-comment",
|
|
224
|
+
"ai-slop/narrative-comment",
|
|
225
|
+
"ai-slop/meta-comment",
|
|
226
|
+
"ai-slop/console-leftover",
|
|
227
|
+
"ai-slop/ts-directive",
|
|
228
|
+
"complexity/file-too-large",
|
|
229
|
+
"complexity/function-too-long",
|
|
230
|
+
"complexity/deep-nesting",
|
|
231
|
+
"complexity/too-many-params",
|
|
232
|
+
"code-quality/duplicate-block",
|
|
233
|
+
"eslint/no-empty",
|
|
234
|
+
"eslint/no-unused-vars",
|
|
235
|
+
"eslint/no-useless-escape",
|
|
236
|
+
"eslint/no-unused-expressions",
|
|
237
|
+
"unicorn/no-useless-fallback-in-spread",
|
|
238
|
+
"unicorn/prefer-string-starts-ends-with",
|
|
239
|
+
"unicorn/no-new-array",
|
|
240
|
+
"unicorn/no-useless-spread"
|
|
241
|
+
]);
|
|
242
|
+
const CONFIRMED_DEFECT_RULES = /* @__PURE__ */ new Set([
|
|
243
|
+
"ai-slop/hallucinated-import",
|
|
244
|
+
"eslint/no-undef",
|
|
245
|
+
"eslint/no-unreachable",
|
|
246
|
+
"security/vulnerable-dependency"
|
|
247
|
+
]);
|
|
248
|
+
const LOW_CONFIDENCE_SECURITY_RULES = /* @__PURE__ */ new Set(["security/innerhtml", "security/dangerously-set-innerhtml"]);
|
|
249
|
+
const confidenceFor = (diagnostic, kind) => {
|
|
250
|
+
if (kind === "confirmed-defect") return "high";
|
|
251
|
+
if (kind === "style-policy") return "medium";
|
|
252
|
+
if (kind === "conservative-security") {
|
|
253
|
+
if (LOW_CONFIDENCE_SECURITY_RULES.has(diagnostic.rule)) return "medium";
|
|
254
|
+
return diagnostic.severity === "error" ? "high" : "medium";
|
|
255
|
+
}
|
|
256
|
+
return diagnostic.severity === "error" ? "high" : "medium";
|
|
257
|
+
};
|
|
258
|
+
const classifyKind = (diagnostic) => {
|
|
259
|
+
if (CONFIRMED_DEFECT_RULES.has(diagnostic.rule)) return "confirmed-defect";
|
|
260
|
+
if (diagnostic.engine === "security") return "conservative-security";
|
|
261
|
+
if (STYLE_POLICY_RULES.has(diagnostic.rule)) return "style-policy";
|
|
262
|
+
if (diagnostic.engine === "format" || diagnostic.engine === "code-quality") return "style-policy";
|
|
263
|
+
if (diagnostic.engine === "ai-slop") return "ai-slop-indicator";
|
|
264
|
+
if (diagnostic.severity === "error") return "confirmed-defect";
|
|
265
|
+
return "style-policy";
|
|
266
|
+
};
|
|
267
|
+
const assessDiagnostic = (diagnostic) => {
|
|
268
|
+
const kind = classifyKind(diagnostic);
|
|
269
|
+
return {
|
|
270
|
+
kind,
|
|
271
|
+
confidence: confidenceFor(diagnostic, kind),
|
|
272
|
+
label: FINDING_KIND_LABELS[kind]
|
|
273
|
+
};
|
|
274
|
+
};
|
|
275
|
+
const withFindingAssessments = (diagnostics) => diagnostics.map((diagnostic) => ({
|
|
276
|
+
...diagnostic,
|
|
277
|
+
assessment: assessDiagnostic(diagnostic),
|
|
278
|
+
scoreImpact: scoreImpactForRule(diagnostic.rule),
|
|
279
|
+
forceFixable: isForceFixable(diagnostic)
|
|
280
|
+
}));
|
|
281
|
+
const summarizeFindingAssessments = (diagnostics) => {
|
|
282
|
+
const byKind = {
|
|
283
|
+
"confirmed-defect": 0,
|
|
284
|
+
"conservative-security": 0,
|
|
285
|
+
"style-policy": 0,
|
|
286
|
+
"ai-slop-indicator": 0
|
|
287
|
+
};
|
|
288
|
+
const byConfidence = {
|
|
289
|
+
high: 0,
|
|
290
|
+
medium: 0,
|
|
291
|
+
low: 0
|
|
292
|
+
};
|
|
293
|
+
const rows = /* @__PURE__ */ new Map();
|
|
294
|
+
for (const diagnostic of diagnostics) {
|
|
295
|
+
const assessment = assessDiagnostic(diagnostic);
|
|
296
|
+
byKind[assessment.kind]++;
|
|
297
|
+
byConfidence[assessment.confidence]++;
|
|
298
|
+
const row = rows.get(assessment.kind) ?? {
|
|
299
|
+
kind: assessment.kind,
|
|
300
|
+
label: assessment.label,
|
|
301
|
+
count: 0,
|
|
302
|
+
errors: 0,
|
|
303
|
+
warnings: 0,
|
|
304
|
+
info: 0,
|
|
305
|
+
fixable: 0
|
|
306
|
+
};
|
|
307
|
+
row.count++;
|
|
308
|
+
if (diagnostic.severity === "error") row.errors++;
|
|
309
|
+
else if (diagnostic.severity === "warning") row.warnings++;
|
|
310
|
+
else row.info++;
|
|
311
|
+
if (diagnostic.fixable) row.fixable++;
|
|
312
|
+
rows.set(assessment.kind, row);
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
rows: [...rows.values()].sort((a, b) => b.count - a.count),
|
|
316
|
+
byKind,
|
|
317
|
+
byConfidence
|
|
318
|
+
};
|
|
319
|
+
};
|
|
320
|
+
//#endregion
|
|
321
|
+
export { getEngineLabel as a, ENGINE_INFO as i, withFindingAssessments as n, scoreImpactForRule as r, summarizeFindingAssessments as t };
|