guardrail-security 1.0.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/attack-surface/analyzer.d.ts +50 -0
- package/dist/attack-surface/analyzer.d.ts.map +1 -0
- package/dist/attack-surface/analyzer.js +83 -0
- package/dist/attack-surface/index.d.ts +5 -0
- package/dist/attack-surface/index.d.ts.map +1 -0
- package/dist/attack-surface/index.js +20 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/languages/index.d.ts +21 -0
- package/dist/languages/index.d.ts.map +1 -0
- package/dist/languages/index.js +78 -0
- package/dist/languages/java-analyzer.d.ts +72 -0
- package/dist/languages/java-analyzer.d.ts.map +1 -0
- package/dist/languages/java-analyzer.js +417 -0
- package/dist/languages/python-analyzer.d.ts +70 -0
- package/dist/languages/python-analyzer.d.ts.map +1 -0
- package/dist/languages/python-analyzer.js +425 -0
- package/dist/license/compatibility-matrix.d.ts +28 -0
- package/dist/license/compatibility-matrix.d.ts.map +1 -0
- package/dist/license/compatibility-matrix.js +323 -0
- package/dist/license/engine.d.ts +77 -0
- package/dist/license/engine.d.ts.map +1 -0
- package/dist/license/engine.js +264 -0
- package/dist/license/index.d.ts +6 -0
- package/dist/license/index.d.ts.map +1 -0
- package/dist/license/index.js +21 -0
- package/dist/sbom/generator.d.ts +108 -0
- package/dist/sbom/generator.d.ts.map +1 -0
- package/dist/sbom/generator.js +271 -0
- package/dist/sbom/index.d.ts +5 -0
- package/dist/sbom/index.d.ts.map +1 -0
- package/dist/sbom/index.js +20 -0
- package/dist/secrets/guardian.d.ts +113 -0
- package/dist/secrets/guardian.d.ts.map +1 -0
- package/dist/secrets/guardian.js +334 -0
- package/dist/secrets/index.d.ts +10 -0
- package/dist/secrets/index.d.ts.map +1 -0
- package/dist/secrets/index.js +30 -0
- package/dist/secrets/patterns.d.ts +42 -0
- package/dist/secrets/patterns.d.ts.map +1 -0
- package/dist/secrets/patterns.js +165 -0
- package/dist/secrets/pre-commit.d.ts +39 -0
- package/dist/secrets/pre-commit.d.ts.map +1 -0
- package/dist/secrets/pre-commit.js +127 -0
- package/dist/secrets/vault-integration.d.ts +83 -0
- package/dist/secrets/vault-integration.d.ts.map +1 -0
- package/dist/secrets/vault-integration.js +295 -0
- package/dist/secrets/vault-providers.d.ts +110 -0
- package/dist/secrets/vault-providers.d.ts.map +1 -0
- package/dist/secrets/vault-providers.js +417 -0
- package/dist/supply-chain/detector.d.ts +80 -0
- package/dist/supply-chain/detector.d.ts.map +1 -0
- package/dist/supply-chain/detector.js +168 -0
- package/dist/supply-chain/index.d.ts +11 -0
- package/dist/supply-chain/index.d.ts.map +1 -0
- package/dist/supply-chain/index.js +26 -0
- package/dist/supply-chain/malicious-db.d.ts +41 -0
- package/dist/supply-chain/malicious-db.d.ts.map +1 -0
- package/dist/supply-chain/malicious-db.js +82 -0
- package/dist/supply-chain/script-analyzer.d.ts +54 -0
- package/dist/supply-chain/script-analyzer.d.ts.map +1 -0
- package/dist/supply-chain/script-analyzer.js +160 -0
- package/dist/supply-chain/typosquat.d.ts +58 -0
- package/dist/supply-chain/typosquat.d.ts.map +1 -0
- package/dist/supply-chain/typosquat.js +257 -0
- package/dist/supply-chain/vulnerability-db.d.ts +114 -0
- package/dist/supply-chain/vulnerability-db.d.ts.map +1 -0
- package/dist/supply-chain/vulnerability-db.js +310 -0
- package/package.json +34 -0
- package/src/__tests__/license/engine.test.ts +250 -0
- package/src/__tests__/supply-chain/typosquat.test.ts +191 -0
- package/src/attack-surface/analyzer.ts +152 -0
- package/src/attack-surface/index.ts +5 -0
- package/src/index.ts +21 -0
- package/src/languages/index.ts +91 -0
- package/src/languages/java-analyzer.ts +490 -0
- package/src/languages/python-analyzer.ts +498 -0
- package/src/license/compatibility-matrix.ts +366 -0
- package/src/license/engine.ts +345 -0
- package/src/license/index.ts +6 -0
- package/src/sbom/generator.ts +355 -0
- package/src/sbom/index.ts +5 -0
- package/src/secrets/guardian.ts +448 -0
- package/src/secrets/index.ts +10 -0
- package/src/secrets/patterns.ts +186 -0
- package/src/secrets/pre-commit.ts +158 -0
- package/src/secrets/vault-integration.ts +360 -0
- package/src/secrets/vault-providers.ts +446 -0
- package/src/supply-chain/detector.ts +252 -0
- package/src/supply-chain/index.ts +11 -0
- package/src/supply-chain/malicious-db.ts +103 -0
- package/src/supply-chain/script-analyzer.ts +194 -0
- package/src/supply-chain/typosquat.ts +302 -0
- package/src/supply-chain/vulnerability-db.ts +386 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-Language Security Analysis
|
|
3
|
+
*
|
|
4
|
+
* Provides security analysis for multiple programming languages
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export * from "./python-analyzer";
|
|
8
|
+
export * from "./java-analyzer";
|
|
9
|
+
|
|
10
|
+
export type SupportedLanguage =
|
|
11
|
+
| "javascript"
|
|
12
|
+
| "typescript"
|
|
13
|
+
| "python"
|
|
14
|
+
| "java"
|
|
15
|
+
| "go"
|
|
16
|
+
| "rust";
|
|
17
|
+
|
|
18
|
+
export interface LanguageDetectionResult {
|
|
19
|
+
primaryLanguage: SupportedLanguage;
|
|
20
|
+
languages: { language: SupportedLanguage; percentage: number }[];
|
|
21
|
+
buildTools: string[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Detect project languages
|
|
26
|
+
*/
|
|
27
|
+
export function detectProjectLanguages(
|
|
28
|
+
projectPath: string,
|
|
29
|
+
): LanguageDetectionResult {
|
|
30
|
+
const { existsSync } = require("fs");
|
|
31
|
+
const { join } = require("path");
|
|
32
|
+
|
|
33
|
+
const languages: { language: SupportedLanguage; percentage: number }[] = [];
|
|
34
|
+
const buildTools: string[] = [];
|
|
35
|
+
|
|
36
|
+
// Check for JavaScript/TypeScript
|
|
37
|
+
if (existsSync(join(projectPath, "package.json"))) {
|
|
38
|
+
if (existsSync(join(projectPath, "tsconfig.json"))) {
|
|
39
|
+
languages.push({ language: "typescript", percentage: 0 });
|
|
40
|
+
buildTools.push("npm/yarn/pnpm");
|
|
41
|
+
} else {
|
|
42
|
+
languages.push({ language: "javascript", percentage: 0 });
|
|
43
|
+
buildTools.push("npm/yarn/pnpm");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Check for Python
|
|
48
|
+
if (
|
|
49
|
+
existsSync(join(projectPath, "requirements.txt")) ||
|
|
50
|
+
existsSync(join(projectPath, "pyproject.toml")) ||
|
|
51
|
+
existsSync(join(projectPath, "Pipfile"))
|
|
52
|
+
) {
|
|
53
|
+
languages.push({ language: "python", percentage: 0 });
|
|
54
|
+
buildTools.push("pip/poetry/pipenv");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check for Java
|
|
58
|
+
if (existsSync(join(projectPath, "pom.xml"))) {
|
|
59
|
+
languages.push({ language: "java", percentage: 0 });
|
|
60
|
+
buildTools.push("maven");
|
|
61
|
+
}
|
|
62
|
+
if (
|
|
63
|
+
existsSync(join(projectPath, "build.gradle")) ||
|
|
64
|
+
existsSync(join(projectPath, "build.gradle.kts"))
|
|
65
|
+
) {
|
|
66
|
+
languages.push({ language: "java", percentage: 0 });
|
|
67
|
+
buildTools.push("gradle");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Check for Go
|
|
71
|
+
if (existsSync(join(projectPath, "go.mod"))) {
|
|
72
|
+
languages.push({ language: "go", percentage: 0 });
|
|
73
|
+
buildTools.push("go");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check for Rust
|
|
77
|
+
if (existsSync(join(projectPath, "Cargo.toml"))) {
|
|
78
|
+
languages.push({ language: "rust", percentage: 0 });
|
|
79
|
+
buildTools.push("cargo");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Determine primary language (first detected)
|
|
83
|
+
const primaryLanguage =
|
|
84
|
+
languages.length > 0 && languages[0] ? languages[0].language : "javascript";
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
primaryLanguage,
|
|
88
|
+
languages,
|
|
89
|
+
buildTools: [...new Set(buildTools)],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Java Language Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Security analysis for Java projects including:
|
|
5
|
+
* - Maven pom.xml / Gradle build.gradle parsing
|
|
6
|
+
* - Import analysis for detecting dangerous classes
|
|
7
|
+
* - Secret detection patterns specific to Java
|
|
8
|
+
* - Common vulnerability patterns (SQL injection, XXE, deserialization, etc.)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync, existsSync, readdirSync, statSync } from "fs";
|
|
12
|
+
import { join } from "path";
|
|
13
|
+
|
|
14
|
+
export interface JavaDependency {
|
|
15
|
+
groupId: string;
|
|
16
|
+
artifactId: string;
|
|
17
|
+
version: string;
|
|
18
|
+
scope?: string;
|
|
19
|
+
source: "maven" | "gradle";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface JavaSecurityIssue {
|
|
23
|
+
type: "vulnerability" | "secret" | "dangerous_import" | "code_pattern";
|
|
24
|
+
severity: "low" | "medium" | "high" | "critical";
|
|
25
|
+
file: string;
|
|
26
|
+
line?: number;
|
|
27
|
+
message: string;
|
|
28
|
+
recommendation: string;
|
|
29
|
+
cwe?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface JavaAnalysisResult {
|
|
33
|
+
projectPath: string;
|
|
34
|
+
javaVersion?: string;
|
|
35
|
+
buildTool: "maven" | "gradle" | "unknown";
|
|
36
|
+
dependencies: JavaDependency[];
|
|
37
|
+
securityIssues: JavaSecurityIssue[];
|
|
38
|
+
summary: {
|
|
39
|
+
totalDependencies: number;
|
|
40
|
+
issuesBySeverity: Record<string, number>;
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Dangerous Java imports
|
|
45
|
+
const DANGEROUS_IMPORTS = [
|
|
46
|
+
{
|
|
47
|
+
pkg: "java.io.ObjectInputStream",
|
|
48
|
+
reason: "Unsafe deserialization",
|
|
49
|
+
severity: "critical" as const,
|
|
50
|
+
cwe: "CWE-502",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
pkg: "java.lang.Runtime",
|
|
54
|
+
reason: "Command execution",
|
|
55
|
+
severity: "high" as const,
|
|
56
|
+
cwe: "CWE-78",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
pkg: "java.lang.ProcessBuilder",
|
|
60
|
+
reason: "Command execution",
|
|
61
|
+
severity: "high" as const,
|
|
62
|
+
cwe: "CWE-78",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
pkg: "javax.xml.parsers",
|
|
66
|
+
reason: "Potential XXE vulnerability",
|
|
67
|
+
severity: "medium" as const,
|
|
68
|
+
cwe: "CWE-611",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
pkg: "org.xml.sax",
|
|
72
|
+
reason: "Potential XXE vulnerability",
|
|
73
|
+
severity: "medium" as const,
|
|
74
|
+
cwe: "CWE-611",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
pkg: "java.security.MessageDigest",
|
|
78
|
+
reason: "Verify secure algorithm usage",
|
|
79
|
+
severity: "low" as const,
|
|
80
|
+
cwe: "CWE-327",
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
// Java-specific secret patterns
|
|
85
|
+
const JAVA_SECRET_PATTERNS = [
|
|
86
|
+
{ pattern: /(?:apiKey|API_KEY)\s*=\s*"[^"]{10,}"/g, type: "API Key" },
|
|
87
|
+
{
|
|
88
|
+
pattern: /(?:password|PASSWORD|passwd)\s*=\s*"[^"]{6,}"/g,
|
|
89
|
+
type: "Password",
|
|
90
|
+
},
|
|
91
|
+
{ pattern: /(?:secret|SECRET|secretKey)\s*=\s*"[^"]{10,}"/g, type: "Secret" },
|
|
92
|
+
{ pattern: /(?:token|TOKEN|accessToken)\s*=\s*"[^"]{10,}"/g, type: "Token" },
|
|
93
|
+
{
|
|
94
|
+
pattern: /(?:jdbc|JDBC):[^"]+password=[^"]+/g,
|
|
95
|
+
type: "JDBC Connection String",
|
|
96
|
+
},
|
|
97
|
+
{ pattern: /aws\.accessKeyId\s*=\s*"[^"]+"/g, type: "AWS Access Key" },
|
|
98
|
+
{ pattern: /aws\.secretKey\s*=\s*"[^"]+"/g, type: "AWS Secret Key" },
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
// Vulnerability code patterns
|
|
102
|
+
const VULNERABILITY_PATTERNS = [
|
|
103
|
+
{
|
|
104
|
+
pattern: /Statement\s+\w+\s*=.*createStatement\(\)/g,
|
|
105
|
+
type: "SQL Injection",
|
|
106
|
+
message: "Using Statement instead of PreparedStatement",
|
|
107
|
+
severity: "critical" as const,
|
|
108
|
+
cwe: "CWE-89",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
pattern: /executeQuery\s*\(\s*"[^"]*"\s*\+/g,
|
|
112
|
+
type: "SQL Injection",
|
|
113
|
+
message: "String concatenation in SQL query",
|
|
114
|
+
severity: "critical" as const,
|
|
115
|
+
cwe: "CWE-89",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
pattern: /Runtime\.getRuntime\(\)\.exec\s*\(/g,
|
|
119
|
+
type: "Command Injection",
|
|
120
|
+
message: "Runtime.exec() can be vulnerable to command injection",
|
|
121
|
+
severity: "high" as const,
|
|
122
|
+
cwe: "CWE-78",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
pattern: /new\s+ObjectInputStream\s*\(/g,
|
|
126
|
+
type: "Unsafe Deserialization",
|
|
127
|
+
message: "ObjectInputStream can deserialize malicious objects",
|
|
128
|
+
severity: "critical" as const,
|
|
129
|
+
cwe: "CWE-502",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
pattern: /DocumentBuilderFactory\.newInstance\(\)/g,
|
|
133
|
+
type: "XXE Vulnerability",
|
|
134
|
+
message: "XML parser may be vulnerable to XXE - disable external entities",
|
|
135
|
+
severity: "high" as const,
|
|
136
|
+
cwe: "CWE-611",
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
pattern: /SAXParserFactory\.newInstance\(\)/g,
|
|
140
|
+
type: "XXE Vulnerability",
|
|
141
|
+
message: "SAX parser may be vulnerable to XXE - disable external entities",
|
|
142
|
+
severity: "high" as const,
|
|
143
|
+
cwe: "CWE-611",
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
pattern: /MessageDigest\.getInstance\s*\(\s*"MD5"\s*\)/g,
|
|
147
|
+
type: "Weak Cryptography",
|
|
148
|
+
message: "MD5 is cryptographically broken",
|
|
149
|
+
severity: "medium" as const,
|
|
150
|
+
cwe: "CWE-327",
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
pattern: /MessageDigest\.getInstance\s*\(\s*"SHA-?1"\s*\)/g,
|
|
154
|
+
type: "Weak Cryptography",
|
|
155
|
+
message: "SHA-1 is deprecated for security use",
|
|
156
|
+
severity: "medium" as const,
|
|
157
|
+
cwe: "CWE-327",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
pattern: /new\s+Random\s*\(\)/g,
|
|
161
|
+
type: "Weak Random",
|
|
162
|
+
message: "java.util.Random is not cryptographically secure",
|
|
163
|
+
severity: "medium" as const,
|
|
164
|
+
cwe: "CWE-330",
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
pattern: /setAllowFileAccess\s*\(\s*true\s*\)/g,
|
|
168
|
+
type: "WebView File Access",
|
|
169
|
+
message: "WebView file access enabled",
|
|
170
|
+
severity: "high" as const,
|
|
171
|
+
cwe: "CWE-200",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
pattern: /TrustManager\s*\[\s*\]\s*\{[^}]*checkServerTrusted[^}]*\{\s*\}/g,
|
|
175
|
+
type: "SSL/TLS Bypass",
|
|
176
|
+
message: "Empty TrustManager bypasses SSL verification",
|
|
177
|
+
severity: "critical" as const,
|
|
178
|
+
cwe: "CWE-295",
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
export class JavaAnalyzer {
|
|
183
|
+
/**
|
|
184
|
+
* Analyze a Java project
|
|
185
|
+
*/
|
|
186
|
+
async analyze(projectPath: string): Promise<JavaAnalysisResult> {
|
|
187
|
+
const buildTool = this.detectBuildTool(projectPath);
|
|
188
|
+
const dependencies = await this.extractDependencies(projectPath, buildTool);
|
|
189
|
+
const securityIssues: JavaSecurityIssue[] = [];
|
|
190
|
+
|
|
191
|
+
// Scan Java files for security issues
|
|
192
|
+
const javaFiles = this.findJavaFiles(projectPath);
|
|
193
|
+
for (const file of javaFiles) {
|
|
194
|
+
const issues = this.scanFile(file);
|
|
195
|
+
securityIssues.push(...issues);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Get Java version if available
|
|
199
|
+
const javaVersion = this.detectJavaVersion(projectPath);
|
|
200
|
+
|
|
201
|
+
// Calculate summary
|
|
202
|
+
const issuesBySeverity: Record<string, number> = {
|
|
203
|
+
critical: 0,
|
|
204
|
+
high: 0,
|
|
205
|
+
medium: 0,
|
|
206
|
+
low: 0,
|
|
207
|
+
};
|
|
208
|
+
for (const issue of securityIssues) {
|
|
209
|
+
const severity = issue.severity;
|
|
210
|
+
if (issuesBySeverity[severity] !== undefined) {
|
|
211
|
+
issuesBySeverity[severity]++;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
projectPath,
|
|
217
|
+
javaVersion,
|
|
218
|
+
buildTool,
|
|
219
|
+
dependencies,
|
|
220
|
+
securityIssues,
|
|
221
|
+
summary: {
|
|
222
|
+
totalDependencies: dependencies.length,
|
|
223
|
+
issuesBySeverity,
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Detect build tool
|
|
230
|
+
*/
|
|
231
|
+
private detectBuildTool(projectPath: string): "maven" | "gradle" | "unknown" {
|
|
232
|
+
if (existsSync(join(projectPath, "pom.xml"))) {
|
|
233
|
+
return "maven";
|
|
234
|
+
}
|
|
235
|
+
if (
|
|
236
|
+
existsSync(join(projectPath, "build.gradle")) ||
|
|
237
|
+
existsSync(join(projectPath, "build.gradle.kts"))
|
|
238
|
+
) {
|
|
239
|
+
return "gradle";
|
|
240
|
+
}
|
|
241
|
+
return "unknown";
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Extract dependencies
|
|
246
|
+
*/
|
|
247
|
+
private async extractDependencies(
|
|
248
|
+
projectPath: string,
|
|
249
|
+
buildTool: string,
|
|
250
|
+
): Promise<JavaDependency[]> {
|
|
251
|
+
if (buildTool === "maven") {
|
|
252
|
+
return this.parseMavenPom(projectPath);
|
|
253
|
+
}
|
|
254
|
+
if (buildTool === "gradle") {
|
|
255
|
+
return this.parseGradleBuild(projectPath);
|
|
256
|
+
}
|
|
257
|
+
return [];
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Parse Maven pom.xml
|
|
262
|
+
*/
|
|
263
|
+
private parseMavenPom(projectPath: string): JavaDependency[] {
|
|
264
|
+
const dependencies: JavaDependency[] = [];
|
|
265
|
+
const pomPath = join(projectPath, "pom.xml");
|
|
266
|
+
|
|
267
|
+
if (!existsSync(pomPath)) {
|
|
268
|
+
return dependencies;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
const content = readFileSync(pomPath, "utf-8");
|
|
273
|
+
|
|
274
|
+
// Simple regex parsing for dependencies
|
|
275
|
+
const depRegex =
|
|
276
|
+
/<dependency>\s*<groupId>([^<]+)<\/groupId>\s*<artifactId>([^<]+)<\/artifactId>\s*(?:<version>([^<]+)<\/version>)?/g;
|
|
277
|
+
|
|
278
|
+
let match;
|
|
279
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
280
|
+
dependencies.push({
|
|
281
|
+
groupId: match[1] || "",
|
|
282
|
+
artifactId: match[2] || "",
|
|
283
|
+
version: match[3] || "managed",
|
|
284
|
+
source: "maven",
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
} catch {
|
|
288
|
+
// Skip if can't parse
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return dependencies;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Parse Gradle build file
|
|
296
|
+
*/
|
|
297
|
+
private parseGradleBuild(projectPath: string): JavaDependency[] {
|
|
298
|
+
const dependencies: JavaDependency[] = [];
|
|
299
|
+
const gradlePath = join(projectPath, "build.gradle");
|
|
300
|
+
const gradleKtsPath = join(projectPath, "build.gradle.kts");
|
|
301
|
+
|
|
302
|
+
const buildFile = existsSync(gradlePath)
|
|
303
|
+
? gradlePath
|
|
304
|
+
: existsSync(gradleKtsPath)
|
|
305
|
+
? gradleKtsPath
|
|
306
|
+
: null;
|
|
307
|
+
|
|
308
|
+
if (!buildFile) {
|
|
309
|
+
return dependencies;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
try {
|
|
313
|
+
const content = readFileSync(buildFile, "utf-8");
|
|
314
|
+
|
|
315
|
+
// Match implementation 'group:artifact:version' or implementation("group:artifact:version")
|
|
316
|
+
const depRegex =
|
|
317
|
+
/(?:implementation|compile|api|testImplementation)\s*[("']([^:]+):([^:]+):([^)"']+)/g;
|
|
318
|
+
|
|
319
|
+
let match;
|
|
320
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
321
|
+
dependencies.push({
|
|
322
|
+
groupId: match[1] || "",
|
|
323
|
+
artifactId: match[2] || "",
|
|
324
|
+
version: match[3] || "latest",
|
|
325
|
+
source: "gradle",
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
} catch {
|
|
329
|
+
// Skip if can't parse
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return dependencies;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Find all Java files
|
|
337
|
+
*/
|
|
338
|
+
private findJavaFiles(projectPath: string): string[] {
|
|
339
|
+
const files: string[] = [];
|
|
340
|
+
|
|
341
|
+
const walkDir = (dir: string) => {
|
|
342
|
+
try {
|
|
343
|
+
const entries = readdirSync(dir);
|
|
344
|
+
|
|
345
|
+
for (const entry of entries) {
|
|
346
|
+
const fullPath = join(dir, entry);
|
|
347
|
+
|
|
348
|
+
// Skip common non-source directories
|
|
349
|
+
if (
|
|
350
|
+
[
|
|
351
|
+
"node_modules",
|
|
352
|
+
".git",
|
|
353
|
+
"target",
|
|
354
|
+
"build",
|
|
355
|
+
".gradle",
|
|
356
|
+
".idea",
|
|
357
|
+
].includes(entry)
|
|
358
|
+
) {
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
const stat = statSync(fullPath);
|
|
364
|
+
if (stat.isDirectory()) {
|
|
365
|
+
walkDir(fullPath);
|
|
366
|
+
} else if (entry.endsWith(".java")) {
|
|
367
|
+
files.push(fullPath);
|
|
368
|
+
}
|
|
369
|
+
} catch {
|
|
370
|
+
// Skip files we can't access
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
} catch {
|
|
374
|
+
// Skip directories we can't access
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
walkDir(projectPath);
|
|
379
|
+
return files;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Scan a Java file for security issues
|
|
384
|
+
*/
|
|
385
|
+
private scanFile(filePath: string): JavaSecurityIssue[] {
|
|
386
|
+
const issues: JavaSecurityIssue[] = [];
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
const content = readFileSync(filePath, "utf-8");
|
|
390
|
+
const lines = content.split("\n");
|
|
391
|
+
|
|
392
|
+
// Check for dangerous imports
|
|
393
|
+
for (let i = 0; i < lines.length; i++) {
|
|
394
|
+
const line = lines[i];
|
|
395
|
+
if (!line) continue;
|
|
396
|
+
|
|
397
|
+
for (const dangerous of DANGEROUS_IMPORTS) {
|
|
398
|
+
if (
|
|
399
|
+
line.includes(`import ${dangerous.pkg}`) ||
|
|
400
|
+
line.includes(dangerous.pkg)
|
|
401
|
+
) {
|
|
402
|
+
issues.push({
|
|
403
|
+
type: "dangerous_import",
|
|
404
|
+
severity: dangerous.severity,
|
|
405
|
+
file: filePath,
|
|
406
|
+
line: i + 1,
|
|
407
|
+
message: `Dangerous import: ${dangerous.pkg} - ${dangerous.reason}`,
|
|
408
|
+
recommendation: `Review usage of ${dangerous.pkg}`,
|
|
409
|
+
cwe: dangerous.cwe,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Check for secrets
|
|
416
|
+
for (const secretPattern of JAVA_SECRET_PATTERNS) {
|
|
417
|
+
const matches = content.matchAll(secretPattern.pattern);
|
|
418
|
+
for (const match of matches) {
|
|
419
|
+
const lineNum = content.substring(0, match.index).split("\n").length;
|
|
420
|
+
issues.push({
|
|
421
|
+
type: "secret",
|
|
422
|
+
severity: "critical",
|
|
423
|
+
file: filePath,
|
|
424
|
+
line: lineNum,
|
|
425
|
+
message: `Potential ${secretPattern.type} detected`,
|
|
426
|
+
recommendation:
|
|
427
|
+
"Move secrets to environment variables or secure vault",
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Check for vulnerability patterns
|
|
433
|
+
for (const vulnPattern of VULNERABILITY_PATTERNS) {
|
|
434
|
+
const matches = content.matchAll(vulnPattern.pattern);
|
|
435
|
+
for (const match of matches) {
|
|
436
|
+
const lineNum = content.substring(0, match.index).split("\n").length;
|
|
437
|
+
issues.push({
|
|
438
|
+
type: "code_pattern",
|
|
439
|
+
severity: vulnPattern.severity,
|
|
440
|
+
file: filePath,
|
|
441
|
+
line: lineNum,
|
|
442
|
+
message: `${vulnPattern.type}: ${vulnPattern.message}`,
|
|
443
|
+
recommendation: `Fix the ${vulnPattern.type.toLowerCase()} vulnerability`,
|
|
444
|
+
cwe: vulnPattern.cwe,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
} catch {
|
|
449
|
+
// Skip files we can't read
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return issues;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Detect Java version
|
|
457
|
+
*/
|
|
458
|
+
private detectJavaVersion(projectPath: string): string | undefined {
|
|
459
|
+
// Check pom.xml for java version
|
|
460
|
+
const pomPath = join(projectPath, "pom.xml");
|
|
461
|
+
if (existsSync(pomPath)) {
|
|
462
|
+
try {
|
|
463
|
+
const content = readFileSync(pomPath, "utf-8");
|
|
464
|
+
const match = content.match(/<java\.version>([^<]+)<\/java\.version>/);
|
|
465
|
+
if (match && match[1]) {
|
|
466
|
+
return match[1];
|
|
467
|
+
}
|
|
468
|
+
const sourceMatch = content.match(
|
|
469
|
+
/<maven\.compiler\.source>([^<]+)<\/maven\.compiler\.source>/,
|
|
470
|
+
);
|
|
471
|
+
if (sourceMatch && sourceMatch[1]) {
|
|
472
|
+
return sourceMatch[1];
|
|
473
|
+
}
|
|
474
|
+
} catch {
|
|
475
|
+
// Skip
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Check .java-version file
|
|
480
|
+
const javaVersionPath = join(projectPath, ".java-version");
|
|
481
|
+
if (existsSync(javaVersionPath)) {
|
|
482
|
+
return readFileSync(javaVersionPath, "utf-8").trim();
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return undefined;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Export singleton
|
|
490
|
+
export const javaAnalyzer = new JavaAnalyzer();
|