ghagga-core 2.2.0 → 2.4.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/dist/agents/consensus.d.ts +1 -1
- package/dist/agents/consensus.d.ts.map +1 -1
- package/dist/agents/consensus.js +7 -5
- package/dist/agents/consensus.js.map +1 -1
- package/dist/agents/prompts.d.ts.map +1 -1
- package/dist/agents/prompts.js +1 -3
- package/dist/agents/prompts.js.map +1 -1
- package/dist/agents/simple.d.ts +1 -1
- package/dist/agents/simple.d.ts.map +1 -1
- package/dist/agents/simple.js +12 -17
- package/dist/agents/simple.js.map +1 -1
- package/dist/agents/workflow.d.ts +1 -1
- package/dist/agents/workflow.d.ts.map +1 -1
- package/dist/agents/workflow.js +5 -4
- package/dist/agents/workflow.js.map +1 -1
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +25 -5
- package/dist/format.js.map +1 -1
- package/dist/index.d.ts +12 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -8
- package/dist/index.js.map +1 -1
- package/dist/memory/context.d.ts.map +1 -1
- package/dist/memory/context.js.map +1 -1
- package/dist/memory/engram-client.d.ts.map +1 -1
- package/dist/memory/engram-client.js +4 -1
- package/dist/memory/engram-client.js.map +1 -1
- package/dist/memory/engram-mapping.d.ts +1 -1
- package/dist/memory/engram-mapping.d.ts.map +1 -1
- package/dist/memory/engram-mapping.js +2 -2
- package/dist/memory/engram-mapping.js.map +1 -1
- package/dist/memory/engram.d.ts +1 -2
- package/dist/memory/engram.d.ts.map +1 -1
- package/dist/memory/engram.js +1 -2
- package/dist/memory/engram.js.map +1 -1
- package/dist/memory/persist.d.ts.map +1 -1
- package/dist/memory/persist.js +1 -1
- package/dist/memory/persist.js.map +1 -1
- package/dist/memory/privacy.d.ts.map +1 -1
- package/dist/memory/privacy.js +4 -1
- package/dist/memory/privacy.js.map +1 -1
- package/dist/memory/search.d.ts +26 -0
- package/dist/memory/search.d.ts.map +1 -1
- package/dist/memory/search.js +55 -8
- package/dist/memory/search.js.map +1 -1
- package/dist/memory/sqlite.d.ts +7 -2
- package/dist/memory/sqlite.d.ts.map +1 -1
- package/dist/memory/sqlite.js +42 -34
- package/dist/memory/sqlite.js.map +1 -1
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +74 -32
- package/dist/pipeline.js.map +1 -1
- package/dist/providers/fallback.d.ts.map +1 -1
- package/dist/providers/fallback.js +5 -3
- package/dist/providers/fallback.js.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/tools/budget.d.ts +26 -0
- package/dist/tools/budget.d.ts.map +1 -0
- package/dist/tools/budget.js +78 -0
- package/dist/tools/budget.js.map +1 -0
- package/dist/tools/cpd.d.ts +1 -1
- package/dist/tools/cpd.d.ts.map +1 -1
- package/dist/tools/cpd.js +20 -15
- package/dist/tools/cpd.js.map +1 -1
- package/dist/tools/execution.d.ts +18 -0
- package/dist/tools/execution.d.ts.map +1 -0
- package/dist/tools/execution.js +82 -0
- package/dist/tools/execution.js.map +1 -0
- package/dist/tools/index.d.ts +16 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +20 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/orchestrator.d.ts +24 -0
- package/dist/tools/orchestrator.d.ts.map +1 -0
- package/dist/tools/orchestrator.js +127 -0
- package/dist/tools/orchestrator.js.map +1 -0
- package/dist/tools/plugins/bandit.d.ts +22 -0
- package/dist/tools/plugins/bandit.d.ts.map +1 -0
- package/dist/tools/plugins/bandit.js +79 -0
- package/dist/tools/plugins/bandit.js.map +1 -0
- package/dist/tools/plugins/biome.d.ts +22 -0
- package/dist/tools/plugins/biome.d.ts.map +1 -0
- package/dist/tools/plugins/biome.js +80 -0
- package/dist/tools/plugins/biome.js.map +1 -0
- package/dist/tools/plugins/clippy.d.ts +25 -0
- package/dist/tools/plugins/clippy.d.ts.map +1 -0
- package/dist/tools/plugins/clippy.js +95 -0
- package/dist/tools/plugins/clippy.js.map +1 -0
- package/dist/tools/plugins/cpd.d.ts +20 -0
- package/dist/tools/plugins/cpd.d.ts.map +1 -0
- package/dist/tools/plugins/cpd.js +111 -0
- package/dist/tools/plugins/cpd.js.map +1 -0
- package/dist/tools/plugins/gitleaks.d.ts +18 -0
- package/dist/tools/plugins/gitleaks.d.ts.map +1 -0
- package/dist/tools/plugins/gitleaks.js +81 -0
- package/dist/tools/plugins/gitleaks.js.map +1 -0
- package/dist/tools/plugins/golangci-lint.d.ts +27 -0
- package/dist/tools/plugins/golangci-lint.d.ts.map +1 -0
- package/dist/tools/plugins/golangci-lint.js +87 -0
- package/dist/tools/plugins/golangci-lint.js.map +1 -0
- package/dist/tools/plugins/hadolint.d.ts +22 -0
- package/dist/tools/plugins/hadolint.d.ts.map +1 -0
- package/dist/tools/plugins/hadolint.js +99 -0
- package/dist/tools/plugins/hadolint.js.map +1 -0
- package/dist/tools/plugins/index.d.ts +37 -0
- package/dist/tools/plugins/index.d.ts.map +1 -0
- package/dist/tools/plugins/index.js +93 -0
- package/dist/tools/plugins/index.js.map +1 -0
- package/dist/tools/plugins/lizard.d.ts +24 -0
- package/dist/tools/plugins/lizard.d.ts.map +1 -0
- package/dist/tools/plugins/lizard.js +83 -0
- package/dist/tools/plugins/lizard.js.map +1 -0
- package/dist/tools/plugins/markdownlint.d.ts +17 -0
- package/dist/tools/plugins/markdownlint.d.ts.map +1 -0
- package/dist/tools/plugins/markdownlint.js +70 -0
- package/dist/tools/plugins/markdownlint.js.map +1 -0
- package/dist/tools/plugins/pmd.d.ts +23 -0
- package/dist/tools/plugins/pmd.d.ts.map +1 -0
- package/dist/tools/plugins/pmd.js +108 -0
- package/dist/tools/plugins/pmd.js.map +1 -0
- package/dist/tools/plugins/psalm.d.ts +24 -0
- package/dist/tools/plugins/psalm.d.ts.map +1 -0
- package/dist/tools/plugins/psalm.js +85 -0
- package/dist/tools/plugins/psalm.js.map +1 -0
- package/dist/tools/plugins/ruff.d.ts +22 -0
- package/dist/tools/plugins/ruff.d.ts.map +1 -0
- package/dist/tools/plugins/ruff.js +80 -0
- package/dist/tools/plugins/ruff.js.map +1 -0
- package/dist/tools/plugins/semgrep.d.ts +23 -0
- package/dist/tools/plugins/semgrep.d.ts.map +1 -0
- package/dist/tools/plugins/semgrep.js +82 -0
- package/dist/tools/plugins/semgrep.js.map +1 -0
- package/dist/tools/plugins/shellcheck.d.ts +23 -0
- package/dist/tools/plugins/shellcheck.d.ts.map +1 -0
- package/dist/tools/plugins/shellcheck.js +87 -0
- package/dist/tools/plugins/shellcheck.js.map +1 -0
- package/dist/tools/plugins/trivy.d.ts +25 -0
- package/dist/tools/plugins/trivy.d.ts.map +1 -0
- package/dist/tools/plugins/trivy.js +108 -0
- package/dist/tools/plugins/trivy.js.map +1 -0
- package/dist/tools/registry.d.ts +37 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +61 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/resolve.d.ts +37 -0
- package/dist/tools/resolve.d.ts.map +1 -0
- package/dist/tools/resolve.js +67 -0
- package/dist/tools/resolve.js.map +1 -0
- package/dist/tools/runner.d.ts +16 -2
- package/dist/tools/runner.d.ts.map +1 -1
- package/dist/tools/runner.js +78 -10
- package/dist/tools/runner.js.map +1 -1
- package/dist/tools/semgrep.d.ts +1 -1
- package/dist/tools/semgrep.d.ts.map +1 -1
- package/dist/tools/semgrep.js +20 -14
- package/dist/tools/semgrep.js.map +1 -1
- package/dist/tools/trivy.d.ts +1 -1
- package/dist/tools/trivy.d.ts.map +1 -1
- package/dist/tools/trivy.js +3 -1
- package/dist/tools/trivy.js.map +1 -1
- package/dist/tools/types.d.ts +83 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +11 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/types.d.ts +15 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/diff.js +1 -1
- package/dist/utils/diff.js.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semgrep plugin — security analysis (always-on).
|
|
3
|
+
*
|
|
4
|
+
* Adapted from:
|
|
5
|
+
* - packages/core/src/tools/semgrep.ts (parsing logic)
|
|
6
|
+
* - apps/action/src/tools/semgrep.ts (install/run flow)
|
|
7
|
+
*
|
|
8
|
+
* Uses ExecutionContext for DI instead of direct child_process.
|
|
9
|
+
*/
|
|
10
|
+
const SEMGREP_VERSION = '1.90.0';
|
|
11
|
+
/**
|
|
12
|
+
* Map Semgrep severity to GHAGGA FindingSeverity.
|
|
13
|
+
* ERROR -> high, WARNING -> medium, INFO -> info, default -> low
|
|
14
|
+
*/
|
|
15
|
+
export function mapSemgrepSeverity(semgrepSeverity) {
|
|
16
|
+
switch (semgrepSeverity.toUpperCase()) {
|
|
17
|
+
case 'ERROR':
|
|
18
|
+
return 'high';
|
|
19
|
+
case 'WARNING':
|
|
20
|
+
return 'medium';
|
|
21
|
+
case 'INFO':
|
|
22
|
+
return 'info';
|
|
23
|
+
default:
|
|
24
|
+
return 'low';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse Semgrep JSON output into ReviewFinding[].
|
|
29
|
+
* Exported for direct testing with fixture data.
|
|
30
|
+
*/
|
|
31
|
+
export function parseSemgrepOutput(raw, repoDir) {
|
|
32
|
+
if (raw.timedOut)
|
|
33
|
+
return [];
|
|
34
|
+
try {
|
|
35
|
+
const result = JSON.parse(raw.stdout);
|
|
36
|
+
return (result.results ?? []).map((r) => ({
|
|
37
|
+
severity: mapSemgrepSeverity(r.extra.severity),
|
|
38
|
+
category: 'security',
|
|
39
|
+
file: r.path.replace(`${repoDir}/`, ''),
|
|
40
|
+
line: r.start.line,
|
|
41
|
+
message: r.extra.message,
|
|
42
|
+
source: 'semgrep',
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export const semgrepPlugin = {
|
|
50
|
+
name: 'semgrep',
|
|
51
|
+
displayName: 'Semgrep',
|
|
52
|
+
category: 'security',
|
|
53
|
+
tier: 'always-on',
|
|
54
|
+
version: SEMGREP_VERSION,
|
|
55
|
+
outputFormat: 'json',
|
|
56
|
+
cachePaths: ['/usr/local/bin/semgrep'],
|
|
57
|
+
async install(ctx) {
|
|
58
|
+
const cached = await ctx.cacheRestore('semgrep', ['/usr/local/bin/semgrep']);
|
|
59
|
+
if (cached) {
|
|
60
|
+
try {
|
|
61
|
+
await ctx.exec('semgrep', ['--version'], { timeoutMs: 10_000 });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
ctx.log('warn', 'Semgrep cache restored but binary not functional, reinstalling');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
await ctx.exec('pip', ['install', '--quiet', `semgrep==${SEMGREP_VERSION}`], {
|
|
69
|
+
timeoutMs: 120_000,
|
|
70
|
+
});
|
|
71
|
+
await ctx.exec('semgrep', ['--version'], { timeoutMs: 10_000 });
|
|
72
|
+
await ctx.cacheSave('semgrep', ['/usr/local/bin/semgrep']);
|
|
73
|
+
},
|
|
74
|
+
async run(ctx, repoDir, _files, timeout) {
|
|
75
|
+
return ctx.exec('semgrep', ['--json', '--config', 'auto', '--quiet', repoDir], {
|
|
76
|
+
timeoutMs: timeout,
|
|
77
|
+
allowExitCodes: [1], // semgrep returns 1 when findings are present
|
|
78
|
+
});
|
|
79
|
+
},
|
|
80
|
+
parse: parseSemgrepOutput,
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=semgrep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semgrep.js","sourceRoot":"","sources":["../../../src/tools/plugins/semgrep.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShellCheck plugin — shell script linting (always-on).
|
|
3
|
+
*
|
|
4
|
+
* Lints shell scripts for common issues and portability problems.
|
|
5
|
+
* Only runs on *.sh and *.bash files; returns empty findings if none found.
|
|
6
|
+
*
|
|
7
|
+
* Pre-installed on GitHub Actions runners — install is a verification step.
|
|
8
|
+
* Uses ExecutionContext for DI instead of direct child_process.
|
|
9
|
+
*/
|
|
10
|
+
import type { FindingSeverity, ReviewFinding } from '../../types.js';
|
|
11
|
+
import type { RawToolOutput, ToolDefinition } from '../types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Map ShellCheck level to GHAGGA FindingSeverity.
|
|
14
|
+
* error→high, warning→medium, info→info, style→low
|
|
15
|
+
*/
|
|
16
|
+
export declare function mapShellCheckSeverity(level: string): FindingSeverity;
|
|
17
|
+
/**
|
|
18
|
+
* Parse ShellCheck JSON output into ReviewFinding[].
|
|
19
|
+
* Exported for direct testing with fixture data.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseShellCheckOutput(raw: RawToolOutput, repoDir: string): ReviewFinding[];
|
|
22
|
+
export declare const shellcheckPlugin: ToolDefinition;
|
|
23
|
+
//# sourceMappingURL=shellcheck.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shellcheck.d.ts","sourceRoot":"","sources":["../../../src/tools/plugins/shellcheck.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,KAAK,EAAoB,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAInF;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAapE;AAYD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CAiB1F;AAED,eAAO,MAAM,gBAAgB,EAAE,cAiD9B,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShellCheck plugin — shell script linting (always-on).
|
|
3
|
+
*
|
|
4
|
+
* Lints shell scripts for common issues and portability problems.
|
|
5
|
+
* Only runs on *.sh and *.bash files; returns empty findings if none found.
|
|
6
|
+
*
|
|
7
|
+
* Pre-installed on GitHub Actions runners — install is a verification step.
|
|
8
|
+
* Uses ExecutionContext for DI instead of direct child_process.
|
|
9
|
+
*/
|
|
10
|
+
const SHELLCHECK_VERSION = '0.10.0';
|
|
11
|
+
/**
|
|
12
|
+
* Map ShellCheck level to GHAGGA FindingSeverity.
|
|
13
|
+
* error→high, warning→medium, info→info, style→low
|
|
14
|
+
*/
|
|
15
|
+
export function mapShellCheckSeverity(level) {
|
|
16
|
+
switch (level.toLowerCase()) {
|
|
17
|
+
case 'error':
|
|
18
|
+
return 'high';
|
|
19
|
+
case 'warning':
|
|
20
|
+
return 'medium';
|
|
21
|
+
case 'info':
|
|
22
|
+
return 'info';
|
|
23
|
+
case 'style':
|
|
24
|
+
return 'low';
|
|
25
|
+
default:
|
|
26
|
+
return 'low';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Parse ShellCheck JSON output into ReviewFinding[].
|
|
31
|
+
* Exported for direct testing with fixture data.
|
|
32
|
+
*/
|
|
33
|
+
export function parseShellCheckOutput(raw, repoDir) {
|
|
34
|
+
if (raw.timedOut)
|
|
35
|
+
return [];
|
|
36
|
+
try {
|
|
37
|
+
const findings = JSON.parse(raw.stdout);
|
|
38
|
+
return findings.map((f) => ({
|
|
39
|
+
severity: mapShellCheckSeverity(f.level),
|
|
40
|
+
category: 'quality',
|
|
41
|
+
file: f.file.replace(`${repoDir}/`, ''),
|
|
42
|
+
line: f.line,
|
|
43
|
+
message: `SC${f.code}: ${f.message}`,
|
|
44
|
+
source: 'shellcheck',
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export const shellcheckPlugin = {
|
|
52
|
+
name: 'shellcheck',
|
|
53
|
+
displayName: 'ShellCheck',
|
|
54
|
+
category: 'quality',
|
|
55
|
+
tier: 'always-on',
|
|
56
|
+
version: SHELLCHECK_VERSION,
|
|
57
|
+
outputFormat: 'json',
|
|
58
|
+
async install(ctx) {
|
|
59
|
+
// ShellCheck is typically pre-installed on GitHub Actions runners
|
|
60
|
+
try {
|
|
61
|
+
await ctx.exec('shellcheck', ['--version'], { timeoutMs: 10_000 });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
ctx.log('info', 'ShellCheck not found, installing...');
|
|
66
|
+
}
|
|
67
|
+
await ctx.exec('bash', [
|
|
68
|
+
'-c',
|
|
69
|
+
`curl -sL "https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/shellcheck-v${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | tar xJ --strip-components=1 -C /usr/local/bin shellcheck-v${SHELLCHECK_VERSION}/shellcheck`,
|
|
70
|
+
], { timeoutMs: 120_000 });
|
|
71
|
+
await ctx.exec('shellcheck', ['--version'], { timeoutMs: 10_000 });
|
|
72
|
+
},
|
|
73
|
+
async run(ctx, _repoDir, files, timeout) {
|
|
74
|
+
// Filter to only shell script files
|
|
75
|
+
const shellFiles = files.filter((f) => /\.(sh|bash)$/.test(f));
|
|
76
|
+
if (shellFiles.length === 0) {
|
|
77
|
+
// No shell files — return empty output (will parse to empty findings)
|
|
78
|
+
return { stdout: '[]', stderr: '', exitCode: 0, timedOut: false };
|
|
79
|
+
}
|
|
80
|
+
return ctx.exec('shellcheck', ['--format=json', ...shellFiles], {
|
|
81
|
+
timeoutMs: timeout,
|
|
82
|
+
allowExitCodes: [1], // shellcheck returns 1 when findings are present
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
parse: parseShellCheckOutput,
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=shellcheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shellcheck.js","sourceRoot":"","sources":["../../../src/tools/plugins/shellcheck.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trivy plugin — SCA vulnerability + license scanning (always-on).
|
|
3
|
+
*
|
|
4
|
+
* Adapted from:
|
|
5
|
+
* - packages/core/src/tools/trivy.ts (parsing logic)
|
|
6
|
+
* - apps/action/src/tools/trivy.ts (install/run flow)
|
|
7
|
+
*
|
|
8
|
+
* Enhanced: adds --scanners license to existing vuln scanner.
|
|
9
|
+
* Uses ExecutionContext for DI instead of direct child_process.
|
|
10
|
+
*/
|
|
11
|
+
import type { FindingSeverity, ReviewFinding } from '../../types.js';
|
|
12
|
+
import type { RawToolOutput, ToolDefinition } from '../types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Map Trivy severity to GHAGGA FindingSeverity.
|
|
15
|
+
* CRITICAL -> critical, HIGH -> high, MEDIUM -> medium, LOW -> low, default -> info
|
|
16
|
+
*/
|
|
17
|
+
export declare function mapTrivySeverity(trivySeverity: string): FindingSeverity;
|
|
18
|
+
/**
|
|
19
|
+
* Parse Trivy JSON output into ReviewFinding[].
|
|
20
|
+
* Handles both vulnerability and license findings.
|
|
21
|
+
* Exported for direct testing with fixture data.
|
|
22
|
+
*/
|
|
23
|
+
export declare function parseTrivyOutput(raw: RawToolOutput, _repoDir: string): ReviewFinding[];
|
|
24
|
+
export declare const trivyPlugin: ToolDefinition;
|
|
25
|
+
//# sourceMappingURL=trivy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trivy.d.ts","sourceRoot":"","sources":["../../../src/tools/plugins/trivy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,KAAK,EAAoB,aAAa,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAInF;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,eAAe,CAavE;AA0BD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,EAAE,CAuCtF;AAED,eAAO,MAAM,WAAW,EAAE,cAmDzB,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trivy plugin — SCA vulnerability + license scanning (always-on).
|
|
3
|
+
*
|
|
4
|
+
* Adapted from:
|
|
5
|
+
* - packages/core/src/tools/trivy.ts (parsing logic)
|
|
6
|
+
* - apps/action/src/tools/trivy.ts (install/run flow)
|
|
7
|
+
*
|
|
8
|
+
* Enhanced: adds --scanners license to existing vuln scanner.
|
|
9
|
+
* Uses ExecutionContext for DI instead of direct child_process.
|
|
10
|
+
*/
|
|
11
|
+
const TRIVY_VERSION = '0.69.3';
|
|
12
|
+
/**
|
|
13
|
+
* Map Trivy severity to GHAGGA FindingSeverity.
|
|
14
|
+
* CRITICAL -> critical, HIGH -> high, MEDIUM -> medium, LOW -> low, default -> info
|
|
15
|
+
*/
|
|
16
|
+
export function mapTrivySeverity(trivySeverity) {
|
|
17
|
+
switch (trivySeverity.toUpperCase()) {
|
|
18
|
+
case 'CRITICAL':
|
|
19
|
+
return 'critical';
|
|
20
|
+
case 'HIGH':
|
|
21
|
+
return 'high';
|
|
22
|
+
case 'MEDIUM':
|
|
23
|
+
return 'medium';
|
|
24
|
+
case 'LOW':
|
|
25
|
+
return 'low';
|
|
26
|
+
default:
|
|
27
|
+
return 'info';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Parse Trivy JSON output into ReviewFinding[].
|
|
32
|
+
* Handles both vulnerability and license findings.
|
|
33
|
+
* Exported for direct testing with fixture data.
|
|
34
|
+
*/
|
|
35
|
+
export function parseTrivyOutput(raw, _repoDir) {
|
|
36
|
+
if (raw.timedOut)
|
|
37
|
+
return [];
|
|
38
|
+
try {
|
|
39
|
+
const result = JSON.parse(raw.stdout);
|
|
40
|
+
const findings = [];
|
|
41
|
+
for (const target of result.Results ?? []) {
|
|
42
|
+
// Parse vulnerability findings (existing behavior)
|
|
43
|
+
for (const vuln of target.Vulnerabilities ?? []) {
|
|
44
|
+
const fixInfo = vuln.FixedVersion
|
|
45
|
+
? ` (fix: upgrade to ${vuln.FixedVersion})`
|
|
46
|
+
: ' (no fix available)';
|
|
47
|
+
findings.push({
|
|
48
|
+
severity: mapTrivySeverity(vuln.Severity),
|
|
49
|
+
category: 'dependency-vulnerability',
|
|
50
|
+
file: target.Target,
|
|
51
|
+
message: `${vuln.VulnerabilityID}: ${vuln.PkgName}@${vuln.InstalledVersion} - ${vuln.Title ?? vuln.Description ?? 'Known vulnerability'}${fixInfo}`,
|
|
52
|
+
source: 'trivy',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// Parse license findings (new enhancement)
|
|
56
|
+
for (const license of target.Licenses ?? []) {
|
|
57
|
+
findings.push({
|
|
58
|
+
severity: 'info',
|
|
59
|
+
category: 'license',
|
|
60
|
+
file: target.Target,
|
|
61
|
+
message: `${license.PkgName}: ${license.Name} (${license.Category})`,
|
|
62
|
+
source: 'trivy',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return findings;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export const trivyPlugin = {
|
|
73
|
+
name: 'trivy',
|
|
74
|
+
displayName: 'Trivy',
|
|
75
|
+
category: 'sca',
|
|
76
|
+
tier: 'always-on',
|
|
77
|
+
version: TRIVY_VERSION,
|
|
78
|
+
outputFormat: 'json',
|
|
79
|
+
cachePaths: ['/usr/local/bin/trivy'],
|
|
80
|
+
async install(ctx) {
|
|
81
|
+
const cached = await ctx.cacheRestore('trivy', ['/usr/local/bin/trivy']);
|
|
82
|
+
if (cached) {
|
|
83
|
+
try {
|
|
84
|
+
await ctx.exec('trivy', ['--version'], { timeoutMs: 10_000 });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
ctx.log('warn', 'Trivy cache restored but binary not functional, reinstalling');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Install Trivy via official install script
|
|
92
|
+
await ctx.exec('bash', [
|
|
93
|
+
'-c',
|
|
94
|
+
`curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v${TRIVY_VERSION}`,
|
|
95
|
+
], { timeoutMs: 120_000 });
|
|
96
|
+
await ctx.exec('trivy', ['--version'], { timeoutMs: 10_000 });
|
|
97
|
+
await ctx.cacheSave('trivy', ['/usr/local/bin/trivy']);
|
|
98
|
+
},
|
|
99
|
+
async run(ctx, repoDir, _files, timeout) {
|
|
100
|
+
// Enhanced: adds 'license' scanner alongside 'vuln'
|
|
101
|
+
return ctx.exec('trivy', ['fs', '--format', 'json', '--scanners', 'vuln,license', '--quiet', repoDir], {
|
|
102
|
+
timeoutMs: timeout,
|
|
103
|
+
allowExitCodes: [1],
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
parse: parseTrivyOutput,
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=trivy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trivy.js","sourceRoot":"","sources":["../../../src/tools/plugins/trivy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Registry — central registration point for all static analysis tools.
|
|
3
|
+
*
|
|
4
|
+
* Plugins register themselves via `toolRegistry.register()`.
|
|
5
|
+
* The orchestrator discovers tools through `toolRegistry.getAll()`.
|
|
6
|
+
*
|
|
7
|
+
* Singleton pattern: one registry per process, populated at import time
|
|
8
|
+
* by `plugins/index.ts`.
|
|
9
|
+
*/
|
|
10
|
+
import type { ToolDefinition, ToolTier } from './types.js';
|
|
11
|
+
export declare class ToolRegistry {
|
|
12
|
+
private tools;
|
|
13
|
+
/**
|
|
14
|
+
* Register a tool definition.
|
|
15
|
+
* Validates that auto-detect tools have a detect function.
|
|
16
|
+
* Throws on duplicate name registration.
|
|
17
|
+
*/
|
|
18
|
+
register(tool: ToolDefinition): void;
|
|
19
|
+
/** Get all registered tools */
|
|
20
|
+
getAll(): ToolDefinition[];
|
|
21
|
+
/** Get tool by name, or undefined */
|
|
22
|
+
getByName(name: string): ToolDefinition | undefined;
|
|
23
|
+
/** Get tools by tier */
|
|
24
|
+
getByTier(tier: ToolTier): ToolDefinition[];
|
|
25
|
+
/** Get registered tool count */
|
|
26
|
+
get size(): number;
|
|
27
|
+
/** Validate all registrations (called once at startup) */
|
|
28
|
+
validateAll(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Clear all registrations (useful for tests).
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
clear(): void;
|
|
34
|
+
}
|
|
35
|
+
/** Singleton registry instance — populated by plugins/index.ts */
|
|
36
|
+
export declare const toolRegistry: ToolRegistry;
|
|
37
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3D,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAqC;IAElD;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAepC,+BAA+B;IAC/B,MAAM,IAAI,cAAc,EAAE;IAI1B,qCAAqC;IACrC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAInD,wBAAwB;IACxB,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,cAAc,EAAE;IAI3C,gCAAgC;IAChC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,0DAA0D;IAC1D,WAAW,IAAI,IAAI;IAUnB;;;OAGG;IACH,KAAK,IAAI,IAAI;CAGd;AAED,kEAAkE;AAClE,eAAO,MAAM,YAAY,cAAqB,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Registry — central registration point for all static analysis tools.
|
|
3
|
+
*
|
|
4
|
+
* Plugins register themselves via `toolRegistry.register()`.
|
|
5
|
+
* The orchestrator discovers tools through `toolRegistry.getAll()`.
|
|
6
|
+
*
|
|
7
|
+
* Singleton pattern: one registry per process, populated at import time
|
|
8
|
+
* by `plugins/index.ts`.
|
|
9
|
+
*/
|
|
10
|
+
export class ToolRegistry {
|
|
11
|
+
tools = new Map();
|
|
12
|
+
/**
|
|
13
|
+
* Register a tool definition.
|
|
14
|
+
* Validates that auto-detect tools have a detect function.
|
|
15
|
+
* Throws on duplicate name registration.
|
|
16
|
+
*/
|
|
17
|
+
register(tool) {
|
|
18
|
+
if (this.tools.has(tool.name)) {
|
|
19
|
+
throw new Error(`Tool "${tool.name}" is already registered`);
|
|
20
|
+
}
|
|
21
|
+
if (tool.tier === 'auto-detect' && typeof tool.detect !== 'function') {
|
|
22
|
+
throw new Error(`Tool "${tool.name}" has tier "auto-detect" but no detect function. ` +
|
|
23
|
+
'Auto-detect tools must provide a detect(files) function.');
|
|
24
|
+
}
|
|
25
|
+
this.tools.set(tool.name, tool);
|
|
26
|
+
}
|
|
27
|
+
/** Get all registered tools */
|
|
28
|
+
getAll() {
|
|
29
|
+
return Array.from(this.tools.values());
|
|
30
|
+
}
|
|
31
|
+
/** Get tool by name, or undefined */
|
|
32
|
+
getByName(name) {
|
|
33
|
+
return this.tools.get(name);
|
|
34
|
+
}
|
|
35
|
+
/** Get tools by tier */
|
|
36
|
+
getByTier(tier) {
|
|
37
|
+
return this.getAll().filter((t) => t.tier === tier);
|
|
38
|
+
}
|
|
39
|
+
/** Get registered tool count */
|
|
40
|
+
get size() {
|
|
41
|
+
return this.tools.size;
|
|
42
|
+
}
|
|
43
|
+
/** Validate all registrations (called once at startup) */
|
|
44
|
+
validateAll() {
|
|
45
|
+
for (const tool of this.tools.values()) {
|
|
46
|
+
if (tool.tier === 'auto-detect' && typeof tool.detect !== 'function') {
|
|
47
|
+
throw new Error(`Validation failed: tool "${tool.name}" is auto-detect but has no detect function`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Clear all registrations (useful for tests).
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
clear() {
|
|
56
|
+
this.tools.clear();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** Singleton registry instance — populated by plugins/index.ts */
|
|
60
|
+
export const toolRegistry = new ToolRegistry();
|
|
61
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,OAAO,YAAY;IACf,KAAK,GAAG,IAAI,GAAG,EAA0B,CAAC;IAElD;;;;OAIG;IACH,QAAQ,CAAC,IAAoB;QAC3B,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,SAAS,IAAI,CAAC,IAAI,yBAAyB,CAAC,CAAC;QAC/D,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YACrE,MAAM,IAAI,KAAK,CACb,SAAS,IAAI,CAAC,IAAI,mDAAmD;gBACnE,0DAA0D,CAC7D,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,+BAA+B;IAC/B,MAAM;QACJ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,qCAAqC;IACrC,SAAS,CAAC,IAAY;QACpB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,wBAAwB;IACxB,SAAS,CAAC,IAAc;QACtB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACtD,CAAC;IAED,gCAAgC;IAChC,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED,0DAA0D;IAC1D,WAAW;QACT,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACvC,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;gBACrE,MAAM,IAAI,KAAK,CACb,4BAA4B,IAAI,CAAC,IAAI,6CAA6C,CACnF,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AAED,kEAAkE;AAClE,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Activation Resolver — determines which tools should run.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order (per spec):
|
|
5
|
+
* 1. Start with all always-on tools
|
|
6
|
+
* 2. Run detect(files) for each auto-detect tool; add matches
|
|
7
|
+
* 3. Add tools from enabledTools (force-enable override)
|
|
8
|
+
* 4. Remove tools from disabledTools (force-disable override)
|
|
9
|
+
* 5. Remove tools disabled by deprecated boolean flags (fallback only)
|
|
10
|
+
*
|
|
11
|
+
* disabledTools takes precedence over everything.
|
|
12
|
+
* New fields take precedence over deprecated boolean flags.
|
|
13
|
+
*/
|
|
14
|
+
import type { ToolRegistry } from './registry.js';
|
|
15
|
+
import type { ToolDefinition } from './types.js';
|
|
16
|
+
export interface ToolActivationInput {
|
|
17
|
+
registry: ToolRegistry;
|
|
18
|
+
files: string[];
|
|
19
|
+
enabledTools?: string[];
|
|
20
|
+
disabledTools?: string[];
|
|
21
|
+
/** Deprecated boolean flags (backward compat) */
|
|
22
|
+
enableSemgrep?: boolean;
|
|
23
|
+
enableTrivy?: boolean;
|
|
24
|
+
enableCpd?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface ActivatedTool {
|
|
27
|
+
definition: ToolDefinition;
|
|
28
|
+
reason: 'always-on' | 'auto-detect' | 'force-enabled';
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolve which tools should run for this review.
|
|
32
|
+
*
|
|
33
|
+
* Returns activated tools in execution order: always-on first, then auto-detect,
|
|
34
|
+
* then force-enabled.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveActivatedTools(input: ToolActivationInput): ActivatedTool[];
|
|
37
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/tools/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,iDAAiD;IACjD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,cAAc,CAAC;IAC3B,MAAM,EAAE,WAAW,GAAG,aAAa,GAAG,eAAe,CAAC;CACvD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,mBAAmB,GAAG,aAAa,EAAE,CAsDjF"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Activation Resolver — determines which tools should run.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order (per spec):
|
|
5
|
+
* 1. Start with all always-on tools
|
|
6
|
+
* 2. Run detect(files) for each auto-detect tool; add matches
|
|
7
|
+
* 3. Add tools from enabledTools (force-enable override)
|
|
8
|
+
* 4. Remove tools from disabledTools (force-disable override)
|
|
9
|
+
* 5. Remove tools disabled by deprecated boolean flags (fallback only)
|
|
10
|
+
*
|
|
11
|
+
* disabledTools takes precedence over everything.
|
|
12
|
+
* New fields take precedence over deprecated boolean flags.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Resolve which tools should run for this review.
|
|
16
|
+
*
|
|
17
|
+
* Returns activated tools in execution order: always-on first, then auto-detect,
|
|
18
|
+
* then force-enabled.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveActivatedTools(input) {
|
|
21
|
+
const { registry, files, enabledTools, disabledTools } = input;
|
|
22
|
+
const activated = new Map();
|
|
23
|
+
// Step 1: Start with all always-on tools
|
|
24
|
+
for (const tool of registry.getByTier('always-on')) {
|
|
25
|
+
activated.set(tool.name, { definition: tool, reason: 'always-on' });
|
|
26
|
+
}
|
|
27
|
+
// Step 2: Run detect(files) for each auto-detect tool
|
|
28
|
+
for (const tool of registry.getByTier('auto-detect')) {
|
|
29
|
+
if (tool.detect && tool.detect(files)) {
|
|
30
|
+
activated.set(tool.name, { definition: tool, reason: 'auto-detect' });
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Step 3: Add tools from enabledTools (force-enable)
|
|
34
|
+
if (enabledTools && enabledTools.length > 0) {
|
|
35
|
+
for (const name of enabledTools) {
|
|
36
|
+
if (!activated.has(name)) {
|
|
37
|
+
const tool = registry.getByName(name);
|
|
38
|
+
if (tool) {
|
|
39
|
+
activated.set(name, { definition: tool, reason: 'force-enabled' });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Step 4: Remove tools from disabledTools (force-disable) — takes precedence over everything
|
|
45
|
+
if (disabledTools && disabledTools.length > 0) {
|
|
46
|
+
for (const name of disabledTools) {
|
|
47
|
+
activated.delete(name);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Step 5: Apply deprecated boolean flags as fallback
|
|
51
|
+
// Only apply when the new disabledTools field does NOT already handle the tool
|
|
52
|
+
const hasNewDisabledField = disabledTools !== undefined && disabledTools.length > 0;
|
|
53
|
+
if (!hasNewDisabledField) {
|
|
54
|
+
const deprecatedFlags = [
|
|
55
|
+
{ flag: input.enableSemgrep, tool: 'semgrep' },
|
|
56
|
+
{ flag: input.enableTrivy, tool: 'trivy' },
|
|
57
|
+
{ flag: input.enableCpd, tool: 'cpd' },
|
|
58
|
+
];
|
|
59
|
+
for (const { flag, tool } of deprecatedFlags) {
|
|
60
|
+
if (flag === false) {
|
|
61
|
+
activated.delete(tool);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return Array.from(activated.values());
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/tools/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAqBH;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAA0B;IAC9D,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,GAAG,KAAK,CAAC;IAE/D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;IAEnD,yCAAyC;IACzC,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC;QACnD,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,sDAAsD;IACtD,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,SAAS,CAAC,aAAa,CAAC,EAAE,CAAC;QACrD,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YACtC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IAED,qDAAqD;IACrD,IAAI,YAAY,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5C,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;YAChC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzB,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;gBACtC,IAAI,IAAI,EAAE,CAAC;oBACT,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;gBACrE,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,6FAA6F;IAC7F,IAAI,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;YACjC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,qDAAqD;IACrD,+EAA+E;IAC/E,MAAM,mBAAmB,GAAG,aAAa,KAAK,SAAS,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;IACpF,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,MAAM,eAAe,GAAuD;YAC1E,EAAE,IAAI,EAAE,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE;YAC9C,EAAE,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE;YAC1C,EAAE,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE;SACvC,CAAC;QAEF,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,eAAe,EAAE,CAAC;YAC7C,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;gBACnB,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;AACxC,CAAC"}
|
package/dist/tools/runner.d.ts
CHANGED
|
@@ -4,19 +4,33 @@
|
|
|
4
4
|
*
|
|
5
5
|
* These tools are run one at a time because running them in parallel
|
|
6
6
|
* (Python + JVM + Go) can exceed container memory limits (2GB on Cloud Run).
|
|
7
|
+
*
|
|
8
|
+
* Feature flag `GHAGGA_TOOL_REGISTRY`:
|
|
9
|
+
* - When true: uses the new registry-driven orchestrator (Phase 2+)
|
|
10
|
+
* - When false/unset: uses the existing hardcoded 3-tool path
|
|
11
|
+
*/
|
|
12
|
+
import type { ReviewSettings, StaticAnalysisResult } from '../types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Check if the tool registry feature flag is enabled.
|
|
7
15
|
*/
|
|
8
|
-
|
|
16
|
+
export declare function isToolRegistryEnabled(): boolean;
|
|
9
17
|
/**
|
|
10
18
|
* Run all enabled static analysis tools sequentially.
|
|
11
19
|
*
|
|
20
|
+
* When GHAGGA_TOOL_REGISTRY=true, uses the registry-driven orchestrator.
|
|
21
|
+
* Otherwise, falls back to the hardcoded 3-tool path.
|
|
22
|
+
*
|
|
12
23
|
* @param files - Map of file paths to file contents (for Semgrep)
|
|
13
24
|
* @param scanPath - Directory path on disk (for Trivy and CPD)
|
|
14
25
|
* @param settings - Which tools are enabled
|
|
15
26
|
*/
|
|
16
|
-
export declare function runStaticAnalysis(files: Map<string, string>, scanPath: string, settings: Pick<ReviewSettings, 'enableSemgrep' | 'enableTrivy' | 'enableCpd' | 'customRules'>): Promise<StaticAnalysisResult>;
|
|
27
|
+
export declare function runStaticAnalysis(files: Map<string, string>, scanPath: string, settings: Pick<ReviewSettings, 'enableSemgrep' | 'enableTrivy' | 'enableCpd' | 'customRules' | 'enabledTools' | 'disabledTools'>): Promise<StaticAnalysisResult>;
|
|
17
28
|
/**
|
|
18
29
|
* Format static analysis findings into a prompt context block.
|
|
19
30
|
* This is injected into LLM prompts so agents don't repeat findings.
|
|
31
|
+
*
|
|
32
|
+
* Iterates all tool results dynamically (not hardcoded to semgrep/trivy/cpd).
|
|
33
|
+
* Applies a finding cap of 200 with severity-priority sorting.
|
|
20
34
|
*/
|
|
21
35
|
export declare function formatStaticAnalysisContext(result: StaticAnalysisResult): string;
|
|
22
36
|
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/tools/runner.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/tools/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAiB,cAAc,EAAE,oBAAoB,EAAc,MAAM,aAAa,CAAC;AAoBnG;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAE/C;AAkBD;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,IAAI,CACZ,cAAc,EACd,eAAe,GAAG,aAAa,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,GAAG,eAAe,CACjG,GACA,OAAO,CAAC,oBAAoB,CAAC,CA6B/B;AA8CD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAqChF"}
|