guardrail-security 1.0.2 → 2.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.
Files changed (60) hide show
  1. package/dist/sbom/generator.d.ts +42 -0
  2. package/dist/sbom/generator.d.ts.map +1 -1
  3. package/dist/sbom/generator.js +168 -7
  4. package/dist/secrets/allowlist.d.ts +38 -0
  5. package/dist/secrets/allowlist.d.ts.map +1 -0
  6. package/dist/secrets/allowlist.js +131 -0
  7. package/dist/secrets/config-loader.d.ts +25 -0
  8. package/dist/secrets/config-loader.d.ts.map +1 -0
  9. package/dist/secrets/config-loader.js +103 -0
  10. package/dist/secrets/contextual-risk.d.ts +19 -0
  11. package/dist/secrets/contextual-risk.d.ts.map +1 -0
  12. package/dist/secrets/contextual-risk.js +88 -0
  13. package/dist/secrets/git-scanner.d.ts +29 -0
  14. package/dist/secrets/git-scanner.d.ts.map +1 -0
  15. package/dist/secrets/git-scanner.js +109 -0
  16. package/dist/secrets/guardian.d.ts +70 -57
  17. package/dist/secrets/guardian.d.ts.map +1 -1
  18. package/dist/secrets/guardian.js +531 -258
  19. package/dist/secrets/index.d.ts +4 -0
  20. package/dist/secrets/index.d.ts.map +1 -1
  21. package/dist/secrets/index.js +11 -1
  22. package/dist/secrets/patterns.d.ts +39 -10
  23. package/dist/secrets/patterns.d.ts.map +1 -1
  24. package/dist/secrets/patterns.js +129 -71
  25. package/dist/secrets/pre-commit.d.ts.map +1 -1
  26. package/dist/secrets/pre-commit.js +1 -1
  27. package/dist/secrets/vault-integration.d.ts.map +1 -1
  28. package/dist/secrets/vault-integration.js +1 -0
  29. package/dist/supply-chain/vulnerability-db.d.ts +89 -16
  30. package/dist/supply-chain/vulnerability-db.d.ts.map +1 -1
  31. package/dist/supply-chain/vulnerability-db.js +404 -115
  32. package/dist/utils/semver.d.ts +37 -0
  33. package/dist/utils/semver.d.ts.map +1 -0
  34. package/dist/utils/semver.js +109 -0
  35. package/package.json +17 -3
  36. package/src/__tests__/license/engine.test.ts +0 -250
  37. package/src/__tests__/supply-chain/typosquat.test.ts +0 -191
  38. package/src/attack-surface/analyzer.ts +0 -153
  39. package/src/attack-surface/index.ts +0 -5
  40. package/src/index.ts +0 -21
  41. package/src/languages/index.ts +0 -91
  42. package/src/languages/java-analyzer.ts +0 -490
  43. package/src/languages/python-analyzer.ts +0 -498
  44. package/src/license/compatibility-matrix.ts +0 -366
  45. package/src/license/engine.ts +0 -346
  46. package/src/license/index.ts +0 -6
  47. package/src/sbom/generator.ts +0 -355
  48. package/src/sbom/index.ts +0 -5
  49. package/src/secrets/guardian.ts +0 -468
  50. package/src/secrets/index.ts +0 -10
  51. package/src/secrets/patterns.ts +0 -186
  52. package/src/secrets/pre-commit.ts +0 -158
  53. package/src/secrets/vault-integration.ts +0 -360
  54. package/src/secrets/vault-providers.ts +0 -446
  55. package/src/supply-chain/detector.ts +0 -253
  56. package/src/supply-chain/index.ts +0 -11
  57. package/src/supply-chain/malicious-db.ts +0 -103
  58. package/src/supply-chain/script-analyzer.ts +0 -194
  59. package/src/supply-chain/typosquat.ts +0 -302
  60. package/src/supply-chain/vulnerability-db.ts +0 -386
@@ -1,498 +0,0 @@
1
- /**
2
- * Python Language Analyzer
3
- *
4
- * Security analysis for Python projects including:
5
- * - requirements.txt / Pipfile / pyproject.toml parsing
6
- * - Import analysis for detecting dangerous modules
7
- * - Secret detection patterns specific to Python
8
- * - Common vulnerability patterns (SQL injection, command injection, etc.)
9
- */
10
-
11
- import { readFileSync, existsSync } from "fs";
12
- import { join } from "path";
13
-
14
- export interface PythonDependency {
15
- name: string;
16
- version: string;
17
- source: "requirements" | "pipfile" | "pyproject" | "setup";
18
- extras?: string[];
19
- }
20
-
21
- export interface PythonSecurityIssue {
22
- type: "vulnerability" | "secret" | "dangerous_import" | "code_pattern";
23
- severity: "low" | "medium" | "high" | "critical";
24
- file: string;
25
- line?: number;
26
- message: string;
27
- recommendation: string;
28
- }
29
-
30
- export interface PythonAnalysisResult {
31
- projectPath: string;
32
- pythonVersion?: string;
33
- dependencies: PythonDependency[];
34
- securityIssues: PythonSecurityIssue[];
35
- summary: {
36
- totalDependencies: number;
37
- directDependencies: number;
38
- issuesBySeverity: Record<string, number>;
39
- };
40
- }
41
-
42
- // Dangerous Python imports that may indicate security issues
43
- const DANGEROUS_IMPORTS = [
44
- {
45
- module: "pickle",
46
- reason: "Arbitrary code execution via deserialization",
47
- severity: "high" as const,
48
- },
49
- {
50
- module: "marshal",
51
- reason: "Unsafe deserialization",
52
- severity: "high" as const,
53
- },
54
- {
55
- module: "shelve",
56
- reason: "Uses pickle internally",
57
- severity: "medium" as const,
58
- },
59
- {
60
- module: "subprocess",
61
- reason: "Command execution - ensure proper sanitization",
62
- severity: "medium" as const,
63
- },
64
- {
65
- module: "os.system",
66
- reason: "Command execution - prefer subprocess",
67
- severity: "high" as const,
68
- },
69
- {
70
- module: "eval",
71
- reason: "Arbitrary code execution",
72
- severity: "critical" as const,
73
- },
74
- {
75
- module: "exec",
76
- reason: "Arbitrary code execution",
77
- severity: "critical" as const,
78
- },
79
- {
80
- module: "compile",
81
- reason: "Dynamic code compilation",
82
- severity: "medium" as const,
83
- },
84
- {
85
- module: "__import__",
86
- reason: "Dynamic import - potential for injection",
87
- severity: "medium" as const,
88
- },
89
- ];
90
-
91
- // Python-specific secret patterns
92
- const PYTHON_SECRET_PATTERNS = [
93
- { pattern: /(?:api_key|apikey)\s*=\s*['"][^'"]{10,}['"]/gi, type: "API Key" },
94
- {
95
- pattern: /(?:secret|password|passwd|pwd)\s*=\s*['"][^'"]{6,}['"]/gi,
96
- type: "Password/Secret",
97
- },
98
- {
99
- pattern: /(?:token|auth_token|access_token)\s*=\s*['"][^'"]{10,}['"]/gi,
100
- type: "Token",
101
- },
102
- {
103
- pattern: /AWS_(?:ACCESS_KEY_ID|SECRET_ACCESS_KEY)\s*=\s*['"][^'"]+['"]/gi,
104
- type: "AWS Credential",
105
- },
106
- {
107
- pattern: /(?:private_key|ssh_key)\s*=\s*['"]-----BEGIN/gi,
108
- type: "Private Key",
109
- },
110
- {
111
- pattern: /mongodb(?:\+srv)?:\/\/[^:]+:[^@]+@/gi,
112
- type: "MongoDB Connection String",
113
- },
114
- {
115
- pattern: /postgres(?:ql)?:\/\/[^:]+:[^@]+@/gi,
116
- type: "PostgreSQL Connection String",
117
- },
118
- ];
119
-
120
- // Vulnerability code patterns
121
- const VULNERABILITY_PATTERNS = [
122
- {
123
- pattern: /cursor\.execute\s*\(\s*['"f].*\{.*\}/g,
124
- type: "SQL Injection",
125
- message: "String formatting in SQL query - use parameterized queries",
126
- severity: "critical" as const,
127
- },
128
- {
129
- pattern: /cursor\.execute\s*\(\s*.*%\s*\(/g,
130
- type: "SQL Injection",
131
- message: "String interpolation in SQL query - use parameterized queries",
132
- severity: "critical" as const,
133
- },
134
- {
135
- pattern: /subprocess\.(?:call|run|Popen)\s*\(\s*['"f].*\{/g,
136
- type: "Command Injection",
137
- message: "String formatting in shell command - use list arguments",
138
- severity: "critical" as const,
139
- },
140
- {
141
- pattern: /os\.system\s*\(/g,
142
- type: "Command Injection",
143
- message: "os.system is vulnerable to shell injection - use subprocess",
144
- severity: "high" as const,
145
- },
146
- {
147
- pattern: /yaml\.load\s*\([^)]*\)/g,
148
- type: "Unsafe Deserialization",
149
- message: "yaml.load is unsafe - use yaml.safe_load instead",
150
- severity: "high" as const,
151
- },
152
- {
153
- pattern: /pickle\.loads?\s*\(/g,
154
- type: "Unsafe Deserialization",
155
- message: "Pickle is unsafe with untrusted data",
156
- severity: "high" as const,
157
- },
158
- {
159
- pattern: /render_template_string\s*\(/g,
160
- type: "Server-Side Template Injection",
161
- message: "render_template_string with user input is dangerous",
162
- severity: "critical" as const,
163
- },
164
- {
165
- pattern: /DEBUG\s*=\s*True/g,
166
- type: "Debug Mode",
167
- message: "Debug mode enabled - disable in production",
168
- severity: "medium" as const,
169
- },
170
- {
171
- pattern: /verify\s*=\s*False/g,
172
- type: "SSL Verification Disabled",
173
- message: "SSL verification disabled - vulnerable to MITM attacks",
174
- severity: "high" as const,
175
- },
176
- ];
177
-
178
- export class PythonAnalyzer {
179
- /**
180
- * Analyze a Python project
181
- */
182
- async analyze(projectPath: string): Promise<PythonAnalysisResult> {
183
- const dependencies = await this.extractDependencies(projectPath);
184
- const securityIssues: PythonSecurityIssue[] = [];
185
-
186
- // Scan Python files for security issues
187
- const pythonFiles = await this.findPythonFiles(projectPath);
188
- for (const file of pythonFiles) {
189
- const issues = await this.scanFile(file);
190
- securityIssues.push(...issues);
191
- }
192
-
193
- // Get Python version if available
194
- const pythonVersion = this.detectPythonVersion(projectPath);
195
-
196
- // Calculate summary
197
- const issuesBySeverity: Record<string, number> = {
198
- critical: 0,
199
- high: 0,
200
- medium: 0,
201
- low: 0,
202
- };
203
- for (const issue of securityIssues) {
204
- const severity = issue.severity;
205
- if (issuesBySeverity[severity] !== undefined) {
206
- issuesBySeverity[severity]++;
207
- }
208
- }
209
-
210
- return {
211
- projectPath,
212
- pythonVersion,
213
- dependencies,
214
- securityIssues,
215
- summary: {
216
- totalDependencies: dependencies.length,
217
- directDependencies: dependencies.length,
218
- issuesBySeverity,
219
- },
220
- };
221
- }
222
-
223
- /**
224
- * Extract dependencies from various Python dependency files
225
- */
226
- private async extractDependencies(
227
- projectPath: string,
228
- ): Promise<PythonDependency[]> {
229
- const dependencies: PythonDependency[] = [];
230
-
231
- // Check requirements.txt
232
- const requirementsPath = join(projectPath, "requirements.txt");
233
- if (existsSync(requirementsPath)) {
234
- const content = readFileSync(requirementsPath, "utf-8");
235
- dependencies.push(...this.parseRequirementsTxt(content));
236
- }
237
-
238
- // Check pyproject.toml
239
- const pyprojectPath = join(projectPath, "pyproject.toml");
240
- if (existsSync(pyprojectPath)) {
241
- const content = readFileSync(pyprojectPath, "utf-8");
242
- dependencies.push(...this.parsePyprojectToml(content));
243
- }
244
-
245
- // Check Pipfile
246
- const pipfilePath = join(projectPath, "Pipfile");
247
- if (existsSync(pipfilePath)) {
248
- const content = readFileSync(pipfilePath, "utf-8");
249
- dependencies.push(...this.parsePipfile(content));
250
- }
251
-
252
- return dependencies;
253
- }
254
-
255
- /**
256
- * Parse requirements.txt format
257
- */
258
- private parseRequirementsTxt(content: string): PythonDependency[] {
259
- const dependencies: PythonDependency[] = [];
260
- const lines = content.split("\n");
261
-
262
- for (const line of lines) {
263
- const trimmed = line.trim();
264
- if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("-")) {
265
- continue;
266
- }
267
-
268
- // Match package==version or package>=version etc.
269
- const match = trimmed.match(
270
- /^([a-zA-Z0-9_-]+)\s*([=<>!~]+)?\s*([\d.]+)?/,
271
- );
272
- if (match && match[1]) {
273
- dependencies.push({
274
- name: match[1],
275
- version: match[3] ?? "*",
276
- source: "requirements",
277
- });
278
- }
279
- }
280
-
281
- return dependencies;
282
- }
283
-
284
- /**
285
- * Parse pyproject.toml dependencies
286
- */
287
- private parsePyprojectToml(content: string): PythonDependency[] {
288
- const dependencies: PythonDependency[] = [];
289
-
290
- // Simple regex parsing for dependencies section
291
- const depsMatch = content.match(
292
- /\[project\.dependencies\]([\s\S]*?)(?:\[|$)/,
293
- );
294
- if (depsMatch && depsMatch[1]) {
295
- const lines = depsMatch[1].split("\n");
296
- for (const line of lines) {
297
- const match = line.match(/^\s*"([^"]+)"/);
298
- if (match && match[1]) {
299
- const depSpec = match[1];
300
- const nameMatch = depSpec.match(/^([a-zA-Z0-9_-]+)/);
301
- const versionMatch = depSpec.match(/[=<>!~]+([\d.]+)/);
302
- if (nameMatch && nameMatch[1]) {
303
- dependencies.push({
304
- name: nameMatch[1],
305
- version: versionMatch?.[1] ?? "*",
306
- source: "pyproject",
307
- });
308
- }
309
- }
310
- }
311
- }
312
-
313
- return dependencies;
314
- }
315
-
316
- /**
317
- * Parse Pipfile dependencies
318
- */
319
- private parsePipfile(content: string): PythonDependency[] {
320
- const dependencies: PythonDependency[] = [];
321
-
322
- // Match [packages] section
323
- const packagesMatch = content.match(/\[packages\]([\s\S]*?)(?:\[|$)/);
324
- if (packagesMatch && packagesMatch[1]) {
325
- const lines = packagesMatch[1].split("\n");
326
- for (const line of lines) {
327
- const match = line.match(/^([a-zA-Z0-9_-]+)\s*=\s*"?([^"\n]+)"?/);
328
- if (match && match[1] && match[2]) {
329
- dependencies.push({
330
- name: match[1],
331
- version: match[2] === "*" ? "*" : match[2],
332
- source: "pipfile",
333
- });
334
- }
335
- }
336
- }
337
-
338
- return dependencies;
339
- }
340
-
341
- /**
342
- * Find all Python files in project
343
- */
344
- private async findPythonFiles(projectPath: string): Promise<string[]> {
345
- const files: string[] = [];
346
-
347
- // Simple recursive file finder
348
- const walkDir = (dir: string) => {
349
- try {
350
- const { readdirSync, statSync } = require("fs");
351
- const entries = readdirSync(dir);
352
-
353
- for (const entry of entries) {
354
- const fullPath = join(dir, entry);
355
-
356
- // Skip common non-source directories
357
- if (
358
- [
359
- "node_modules",
360
- ".git",
361
- "__pycache__",
362
- ".venv",
363
- "venv",
364
- "env",
365
- ".tox",
366
- ].includes(entry)
367
- ) {
368
- continue;
369
- }
370
-
371
- try {
372
- const stat = statSync(fullPath);
373
- if (stat.isDirectory()) {
374
- walkDir(fullPath);
375
- } else if (entry.endsWith(".py")) {
376
- files.push(fullPath);
377
- }
378
- } catch {
379
- // Skip files we can't access
380
- }
381
- }
382
- } catch {
383
- // Skip directories we can't access
384
- }
385
- };
386
-
387
- walkDir(projectPath);
388
- return files;
389
- }
390
-
391
- /**
392
- * Scan a Python file for security issues
393
- */
394
- private async scanFile(filePath: string): Promise<PythonSecurityIssue[]> {
395
- const issues: PythonSecurityIssue[] = [];
396
-
397
- try {
398
- const content = readFileSync(filePath, "utf-8");
399
- const lines = content.split("\n");
400
-
401
- // Check for dangerous imports
402
- for (let i = 0; i < lines.length; i++) {
403
- const line = lines[i];
404
-
405
- for (const dangerous of DANGEROUS_IMPORTS) {
406
- if (
407
- line &&
408
- (line.includes(`import ${dangerous.module}`) ||
409
- line.includes(`from ${dangerous.module}`) ||
410
- line.includes(dangerous.module))
411
- ) {
412
- issues.push({
413
- type: "dangerous_import",
414
- severity: dangerous.severity,
415
- file: filePath,
416
- line: i + 1,
417
- message: `Dangerous import: ${dangerous.module} - ${dangerous.reason}`,
418
- recommendation: `Review usage of ${dangerous.module} and ensure proper security controls`,
419
- });
420
- }
421
- }
422
- }
423
-
424
- // Check for secrets
425
- for (const secretPattern of PYTHON_SECRET_PATTERNS) {
426
- const matches = content.matchAll(secretPattern.pattern);
427
- for (const match of matches) {
428
- const lineNum = content.substring(0, match.index).split("\n").length;
429
- issues.push({
430
- type: "secret",
431
- severity: "critical",
432
- file: filePath,
433
- line: lineNum,
434
- message: `Potential ${secretPattern.type} detected`,
435
- recommendation:
436
- "Move secrets to environment variables or a secure vault",
437
- });
438
- }
439
- }
440
-
441
- // Check for vulnerability patterns
442
- for (const vulnPattern of VULNERABILITY_PATTERNS) {
443
- const matches = content.matchAll(vulnPattern.pattern);
444
- for (const match of matches) {
445
- const lineNum = content.substring(0, match.index).split("\n").length;
446
- issues.push({
447
- type: "code_pattern",
448
- severity: vulnPattern.severity,
449
- file: filePath,
450
- line: lineNum,
451
- message: `${vulnPattern.type}: ${vulnPattern.message}`,
452
- recommendation: `Fix the ${vulnPattern.type.toLowerCase()} vulnerability`,
453
- });
454
- }
455
- }
456
- } catch (error) {
457
- // Skip files we can't read
458
- }
459
-
460
- return issues;
461
- }
462
-
463
- /**
464
- * Detect Python version from project
465
- */
466
- private detectPythonVersion(projectPath: string): string | undefined {
467
- // Check pyproject.toml for requires-python
468
- const pyprojectPath = join(projectPath, "pyproject.toml");
469
- if (existsSync(pyprojectPath)) {
470
- const content = readFileSync(pyprojectPath, "utf-8");
471
- const match = content.match(/requires-python\s*=\s*"([^"]+)"/);
472
- if (match) {
473
- return match[1];
474
- }
475
- }
476
-
477
- // Check .python-version file
478
- const pythonVersionPath = join(projectPath, ".python-version");
479
- if (existsSync(pythonVersionPath)) {
480
- return readFileSync(pythonVersionPath, "utf-8").trim();
481
- }
482
-
483
- // Check runtime.txt (Heroku)
484
- const runtimePath = join(projectPath, "runtime.txt");
485
- if (existsSync(runtimePath)) {
486
- const content = readFileSync(runtimePath, "utf-8").trim();
487
- const match = content.match(/python-([\d.]+)/);
488
- if (match) {
489
- return match[1];
490
- }
491
- }
492
-
493
- return undefined;
494
- }
495
- }
496
-
497
- // Export singleton
498
- export const pythonAnalyzer = new PythonAnalyzer();