@greenarmor/ges-scanner-integration 0.6.0 → 0.6.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/dist/index.d.ts CHANGED
@@ -4,10 +4,32 @@ export interface ScanResult {
4
4
  findings: number;
5
5
  output: string;
6
6
  }
7
- export declare function runNpmAudit(): ScanResult;
8
- export declare function runPnpmAudit(): ScanResult;
7
+ export interface SbomResult {
8
+ scanner: string;
9
+ status: "generated" | "error" | "not-available";
10
+ format: string;
11
+ components: number;
12
+ output: string;
13
+ }
14
+ export type Ecosystem = "node" | "python" | "rust" | "go" | "ruby" | "java" | "php" | "dotnet" | "unknown";
15
+ export type NodePackageManager = "pnpm" | "npm" | "yarn" | "bun";
16
+ export type PythonToolchain = "pip" | "poetry" | "pipenv" | "pdm" | "uv";
17
+ export interface ProjectDetection {
18
+ ecosystem: Ecosystem;
19
+ nodePackageManager?: NodePackageManager;
20
+ pythonToolchain?: PythonToolchain;
21
+ }
22
+ export declare function detectEcosystem(dir?: string): Ecosystem;
23
+ export declare function detectProject(dir?: string): ProjectDetection;
9
24
  export declare function runTrivy(): ScanResult;
10
25
  export declare function runGitleaks(): ScanResult;
11
26
  export declare function runSemgrep(): ScanResult;
12
- export declare function runAllScans(): ScanResult[];
27
+ export declare function runSyft(): ScanResult;
28
+ export declare function runTrivySbom(): ScanResult;
29
+ export declare function runGrype(): ScanResult;
30
+ export declare function runDependencyAudit(detection: ProjectDetection): ScanResult;
31
+ export declare function runAllScans(detection?: ProjectDetection): ScanResult[];
32
+ export declare function runAllSbomScans(): ScanResult[];
33
+ export declare function runAllScansWithSbom(detection?: ProjectDetection): ScanResult[];
13
34
  export declare function formatScanResults(results: ScanResult[]): string;
35
+ export declare function formatSbomResults(results: ScanResult[]): string;
package/dist/index.js CHANGED
@@ -1,9 +1,74 @@
1
1
  import { execSync } from "node:child_process";
2
- export function runNpmAudit() {
3
- return runScan("npm audit", "npm audit", "--audit-level=high --json");
2
+ import { existsSync } from "node:fs";
3
+ const NODE_PM_MARKERS = [
4
+ { file: "pnpm-lock.yaml", pm: "pnpm" },
5
+ { file: "yarn.lock", pm: "yarn" },
6
+ { file: "bun.lockb", pm: "bun" },
7
+ { file: "package-lock.json", pm: "npm" },
8
+ ];
9
+ const PYTHON_TOOLCHAIN_MARKERS = [
10
+ { file: "uv.lock", toolchain: "uv" },
11
+ { file: "pdm.lock", toolchain: "pdm" },
12
+ { file: "poetry.lock", toolchain: "poetry" },
13
+ { file: "Pipfile.lock", toolchain: "pipenv" },
14
+ { file: "requirements.txt", toolchain: "pip" },
15
+ ];
16
+ export function detectEcosystem(dir = ".") {
17
+ return detectProject(dir).ecosystem;
4
18
  }
5
- export function runPnpmAudit() {
6
- return runScan("pnpm audit", "pnpm audit", "--audit-level=high --json");
19
+ export function detectProject(dir = ".") {
20
+ const detection = { ecosystem: "unknown" };
21
+ if (NODE_PM_MARKERS.some((m) => existsSync(`${dir}/${m.file}`)) || existsSync(`${dir}/package.json`)) {
22
+ detection.ecosystem = "node";
23
+ for (const marker of NODE_PM_MARKERS) {
24
+ if (existsSync(`${dir}/${marker.file}`)) {
25
+ detection.nodePackageManager = marker.pm;
26
+ break;
27
+ }
28
+ }
29
+ if (!detection.nodePackageManager && existsSync(`${dir}/package.json`)) {
30
+ detection.nodePackageManager = "npm";
31
+ }
32
+ return detection;
33
+ }
34
+ if (PYTHON_TOOLCHAIN_MARKERS.some((m) => existsSync(`${dir}/${m.file}`)) || existsSync(`${dir}/pyproject.toml`)) {
35
+ detection.ecosystem = "python";
36
+ for (const marker of PYTHON_TOOLCHAIN_MARKERS) {
37
+ if (existsSync(`${dir}/${marker.file}`)) {
38
+ detection.pythonToolchain = marker.toolchain;
39
+ break;
40
+ }
41
+ }
42
+ if (!detection.pythonToolchain) {
43
+ detection.pythonToolchain = "pip";
44
+ }
45
+ return detection;
46
+ }
47
+ if (existsSync(`${dir}/Cargo.toml`) || existsSync(`${dir}/Cargo.lock`)) {
48
+ detection.ecosystem = "rust";
49
+ return detection;
50
+ }
51
+ if (existsSync(`${dir}/go.mod`) || existsSync(`${dir}/go.sum`)) {
52
+ detection.ecosystem = "go";
53
+ return detection;
54
+ }
55
+ if (existsSync(`${dir}/Gemfile`) || existsSync(`${dir}/Gemfile.lock`)) {
56
+ detection.ecosystem = "ruby";
57
+ return detection;
58
+ }
59
+ if (existsSync(`${dir}/pom.xml`) || existsSync(`${dir}/build.gradle`) || existsSync(`${dir}/build.gradle.kts`) || existsSync(`${dir}/gradle.lockfile`)) {
60
+ detection.ecosystem = "java";
61
+ return detection;
62
+ }
63
+ if (existsSync(`${dir}/composer.json`) || existsSync(`${dir}/composer.lock`)) {
64
+ detection.ecosystem = "php";
65
+ return detection;
66
+ }
67
+ if (existsSync(`${dir}/packages.lock.json`) || existsSync(`${dir}/nuget.config`)) {
68
+ detection.ecosystem = "dotnet";
69
+ return detection;
70
+ }
71
+ return detection;
7
72
  }
8
73
  export function runTrivy() {
9
74
  return runScan("Trivy", "trivy", "fs --severity HIGH,CRITICAL .");
@@ -14,6 +79,102 @@ export function runGitleaks() {
14
79
  export function runSemgrep() {
15
80
  return runScan("Semgrep", "semgrep", "--config auto --json .");
16
81
  }
82
+ export function runSyft() {
83
+ return runScan("Syft (SBOM)", "syft", ". -o cyclonedx-json");
84
+ }
85
+ export function runTrivySbom() {
86
+ return runScan("Trivy SBOM", "trivy", "sbom . --format cyclonedx-json");
87
+ }
88
+ export function runGrype() {
89
+ return runScan("Grype (SBOM scan)", "grype", "sbom:./sbom.json --fail-on high");
90
+ }
91
+ function getNodeAuditor(pm) {
92
+ const auditors = {
93
+ pnpm: { name: "pnpm audit", command: "pnpm", args: "audit --audit-level=high --json" },
94
+ npm: { name: "npm audit", command: "npm", args: "audit --audit-level=high --json" },
95
+ yarn: { name: "yarn audit", command: "yarn", args: "audit --level high --json" },
96
+ bun: { name: "bun audit", command: "bun", args: "audit --audit-level=high --json" },
97
+ };
98
+ if (pm && auditors[pm])
99
+ return auditors[pm];
100
+ return null;
101
+ }
102
+ function getPythonAuditor(toolchain) {
103
+ const auditors = {
104
+ pip: { name: "pip-audit", command: "pip-audit", args: "--format json" },
105
+ poetry: { name: "pip-audit", command: "pip-audit", args: "--format json" },
106
+ pipenv: { name: "pip-audit", command: "pip-audit", args: "--format json" },
107
+ pdm: { name: "pip-audit", command: "pip-audit", args: "--format json" },
108
+ uv: { name: "pip-audit", command: "pip-audit", args: "--format json" },
109
+ };
110
+ if (toolchain && auditors[toolchain])
111
+ return auditors[toolchain];
112
+ return null;
113
+ }
114
+ const UNIVERSAL_AUDITORS = {
115
+ node: [],
116
+ python: [],
117
+ rust: [{ name: "cargo audit", command: "cargo", args: "audit --json" }],
118
+ go: [{ name: "govulncheck", command: "govulncheck", args: "./..." }],
119
+ ruby: [{ name: "bundle-audit", command: "bundle-audit", args: "check --format json" }],
120
+ java: [{ name: "OWASP Dependency-Check", command: "dependency-check", args: "--scan . --format JSON --out /dev/null" }],
121
+ php: [{ name: "composer audit", command: "composer", args: "audit --format json" }],
122
+ dotnet: [{ name: "dotnet audit", command: "dotnet", args: "list package --vulnerable --include-transitive" }],
123
+ unknown: [],
124
+ };
125
+ export function runDependencyAudit(detection) {
126
+ let primary = null;
127
+ if (detection.ecosystem === "node") {
128
+ primary = getNodeAuditor(detection.nodePackageManager);
129
+ }
130
+ else if (detection.ecosystem === "python") {
131
+ primary = getPythonAuditor(detection.pythonToolchain);
132
+ }
133
+ if (primary) {
134
+ const result = runScan(primary.name, primary.command, primary.args);
135
+ if (result.status !== "not-available") {
136
+ return result;
137
+ }
138
+ }
139
+ const fallbacks = UNIVERSAL_AUDITORS[detection.ecosystem] || [];
140
+ for (const auditor of fallbacks) {
141
+ const result = runScan(auditor.name, auditor.command, auditor.args);
142
+ if (result.status !== "not-available") {
143
+ return result;
144
+ }
145
+ }
146
+ const tried = [primary, ...fallbacks].filter(Boolean).map((a) => a.name);
147
+ return {
148
+ scanner: "Dependency Audit",
149
+ status: "not-available",
150
+ findings: 0,
151
+ output: tried.length > 0
152
+ ? `Auditor not found: ${tried.join(", ")}`
153
+ : `No dependency auditor configured for ecosystem: ${detection.ecosystem}`,
154
+ };
155
+ }
156
+ export function runAllScans(detection) {
157
+ const detected = detection ?? detectProject();
158
+ return [
159
+ runDependencyAudit(detected),
160
+ runTrivy(),
161
+ runGitleaks(),
162
+ runSemgrep(),
163
+ ];
164
+ }
165
+ export function runAllSbomScans() {
166
+ return [
167
+ runSyft(),
168
+ runTrivySbom(),
169
+ runGrype(),
170
+ ];
171
+ }
172
+ export function runAllScansWithSbom(detection) {
173
+ return [
174
+ ...runAllScans(detection),
175
+ ...runAllSbomScans(),
176
+ ];
177
+ }
17
178
  function runScan(name, command, args) {
18
179
  try {
19
180
  const output = execSync(`${command} ${args}`, {
@@ -46,19 +207,23 @@ function runScan(name, command, args) {
46
207
  };
47
208
  }
48
209
  }
49
- export function runAllScans() {
50
- return [
51
- runNpmAudit(),
52
- runTrivy(),
53
- runGitleaks(),
54
- runSemgrep(),
55
- ];
56
- }
57
210
  export function formatScanResults(results) {
58
211
  const lines = ["", " Security Scan Results", " -------------------"];
59
212
  for (const result of results) {
60
213
  const statusIcon = result.status === "pass" ? "PASS" : result.status === "fail" ? "FAIL" : result.status === "error" ? "ERROR" : "N/A";
61
- lines.push(` ${result.scanner.padEnd(20)} ${statusIcon}`);
214
+ lines.push(` ${result.scanner.padEnd(28)} ${statusIcon}`);
215
+ }
216
+ lines.push("");
217
+ return lines.join("\n");
218
+ }
219
+ export function formatSbomResults(results) {
220
+ const sbomScanners = results.filter((r) => r.scanner.includes("SBOM") || r.scanner.includes("Syft") || r.scanner.includes("Grype"));
221
+ if (sbomScanners.length === 0)
222
+ return "";
223
+ const lines = ["", " SBOM Scan Results", " -----------------"];
224
+ for (const result of sbomScanners) {
225
+ const statusIcon = result.status === "pass" ? "GENERATED" : result.status === "fail" ? "FAIL" : result.status === "error" ? "ERROR" : "N/A";
226
+ lines.push(` ${result.scanner.padEnd(28)} ${statusIcon}`);
62
227
  }
63
228
  lines.push("");
64
229
  return lines.join("\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greenarmor/ges-scanner-integration",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "type": "module",
5
5
  "description": "GESF Scanner Integration - Trivy, Gitleaks, Semgrep, npm audit",
6
6
  "main": "./dist/index.js",
@@ -12,7 +12,7 @@
12
12
  }
13
13
  },
14
14
  "dependencies": {
15
- "@greenarmor/ges-core": "0.6.0"
15
+ "@greenarmor/ges-core": "0.6.2"
16
16
  },
17
17
  "devDependencies": {
18
18
  "typescript": "^6.0.0",