@havoc-security/scanner 0.1.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/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-test.log +22 -0
- package/dist/analyzers/AuthorizationCoverageAnalyzer.d.ts +7 -0
- package/dist/analyzers/AuthorizationCoverageAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/AuthorizationCoverageAnalyzer.js +100 -0
- package/dist/analyzers/AuthorizationCoverageAnalyzer.js.map +1 -0
- package/dist/analyzers/CredentialExposureAnalyzer.d.ts +11 -0
- package/dist/analyzers/CredentialExposureAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/CredentialExposureAnalyzer.js +262 -0
- package/dist/analyzers/CredentialExposureAnalyzer.js.map +1 -0
- package/dist/analyzers/DependencyAuditAnalyzer.d.ts +28 -0
- package/dist/analyzers/DependencyAuditAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/DependencyAuditAnalyzer.js +107 -0
- package/dist/analyzers/DependencyAuditAnalyzer.js.map +1 -0
- package/dist/analyzers/EncryptionAnalyzer.d.ts +7 -0
- package/dist/analyzers/EncryptionAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/EncryptionAnalyzer.js +170 -0
- package/dist/analyzers/EncryptionAnalyzer.js.map +1 -0
- package/dist/analyzers/FileUploadAnalyzer.d.ts +8 -0
- package/dist/analyzers/FileUploadAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/FileUploadAnalyzer.js +193 -0
- package/dist/analyzers/FileUploadAnalyzer.js.map +1 -0
- package/dist/analyzers/IdorAnalyzer.d.ts +7 -0
- package/dist/analyzers/IdorAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/IdorAnalyzer.js +91 -0
- package/dist/analyzers/IdorAnalyzer.js.map +1 -0
- package/dist/analyzers/MassAssignmentAnalyzer.d.ts +7 -0
- package/dist/analyzers/MassAssignmentAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/MassAssignmentAnalyzer.js +90 -0
- package/dist/analyzers/MassAssignmentAnalyzer.js.map +1 -0
- package/dist/analyzers/PrivilegeEscalationAnalyzer.d.ts +7 -0
- package/dist/analyzers/PrivilegeEscalationAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/PrivilegeEscalationAnalyzer.js +217 -0
- package/dist/analyzers/PrivilegeEscalationAnalyzer.js.map +1 -0
- package/dist/analyzers/RateLimitAnalyzer.d.ts +7 -0
- package/dist/analyzers/RateLimitAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/RateLimitAnalyzer.js +151 -0
- package/dist/analyzers/RateLimitAnalyzer.js.map +1 -0
- package/dist/analyzers/SessionSecurityAnalyzer.d.ts +10 -0
- package/dist/analyzers/SessionSecurityAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/SessionSecurityAnalyzer.js +295 -0
- package/dist/analyzers/SessionSecurityAnalyzer.js.map +1 -0
- package/dist/analyzers/SqlInjectionAnalyzer.d.ts +7 -0
- package/dist/analyzers/SqlInjectionAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/SqlInjectionAnalyzer.js +77 -0
- package/dist/analyzers/SqlInjectionAnalyzer.js.map +1 -0
- package/dist/analyzers/XssSurfaceAnalyzer.d.ts +7 -0
- package/dist/analyzers/XssSurfaceAnalyzer.d.ts.map +1 -0
- package/dist/analyzers/XssSurfaceAnalyzer.js +100 -0
- package/dist/analyzers/XssSurfaceAnalyzer.js.map +1 -0
- package/dist/analyzers/index.d.ts +13 -0
- package/dist/analyzers/index.d.ts.map +1 -0
- package/dist/analyzers/index.js +13 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +139 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/PhpParser.d.ts +56 -0
- package/dist/parsers/PhpParser.d.ts.map +1 -0
- package/dist/parsers/PhpParser.js +193 -0
- package/dist/parsers/PhpParser.js.map +1 -0
- package/dist/parsers/RouteParser.d.ts +87 -0
- package/dist/parsers/RouteParser.d.ts.map +1 -0
- package/dist/parsers/RouteParser.js +327 -0
- package/dist/parsers/RouteParser.js.map +1 -0
- package/dist/rules/index.d.ts +14 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +9 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/types/index.d.ts +137 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +13 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +30 -0
- package/package.json.bak +27 -0
- package/src/analyzers/AuthorizationCoverageAnalyzer.ts +213 -0
- package/src/analyzers/CredentialExposureAnalyzer.ts +312 -0
- package/src/analyzers/DependencyAuditAnalyzer.ts +135 -0
- package/src/analyzers/EncryptionAnalyzer.ts +195 -0
- package/src/analyzers/FileUploadAnalyzer.ts +239 -0
- package/src/analyzers/IdorAnalyzer.ts +118 -0
- package/src/analyzers/InsecureDeserializationAnalyzer.ts +212 -0
- package/src/analyzers/MassAssignmentAnalyzer.ts +105 -0
- package/src/analyzers/OpenRedirectAnalyzer.ts +149 -0
- package/src/analyzers/PrivilegeEscalationAnalyzer.ts +258 -0
- package/src/analyzers/RateLimitAnalyzer.ts +195 -0
- package/src/analyzers/SecurityHeaderAnalyzer.ts +263 -0
- package/src/analyzers/SessionSecurityAnalyzer.ts +342 -0
- package/src/analyzers/SqlInjectionAnalyzer.ts +99 -0
- package/src/analyzers/XssSurfaceAnalyzer.ts +112 -0
- package/src/analyzers/exclusions.ts +87 -0
- package/src/analyzers/index.ts +15 -0
- package/src/index.ts +226 -0
- package/src/parsers/PhpParser.ts +259 -0
- package/src/parsers/RouteParser.ts +384 -0
- package/src/rules/index.ts +16 -0
- package/src/types/index.ts +164 -0
- package/tests/EncryptionAnalyzer.test.ts +137 -0
- package/tests/PrivilegeEscalationAnalyzer.test.ts +141 -0
- package/tests/RateLimitAnalyzer.test.ts +112 -0
- package/tests/analyzers.test.ts +678 -0
- package/tests/auth-coverage-route-aware.test.ts +294 -0
- package/tests/credential-exposure.test.ts +142 -0
- package/tests/file-upload.test.ts +141 -0
- package/tests/fixtures/app/Http/Controllers/AdminController.php +19 -0
- package/tests/fixtures/app/Http/Controllers/PostController.php +49 -0
- package/tests/fixtures/app/Http/Controllers/PublicController.php +17 -0
- package/tests/fixtures/app/Models/Comment.php +11 -0
- package/tests/fixtures/app/Models/OpenModel.php +11 -0
- package/tests/fixtures/app/Models/Post.php +14 -0
- package/tests/fixtures/app/Models/SafeModel.php +10 -0
- package/tests/fixtures/app/Models/User.php +15 -0
- package/tests/fixtures/blade/mail.blade.php +8 -0
- package/tests/fixtures/blade/safe.blade.php +12 -0
- package/tests/fixtures/blade/vulnerable.blade.php +12 -0
- package/tests/fixtures/controllers/AdminController.php +19 -0
- package/tests/fixtures/controllers/PostController.php +49 -0
- package/tests/fixtures/controllers/PublicController.php +17 -0
- package/tests/fixtures/deserialization/safe.php +32 -0
- package/tests/fixtures/deserialization/unsafe.php +60 -0
- package/tests/fixtures/models/Comment.php +11 -0
- package/tests/fixtures/models/OpenModel.php +11 -0
- package/tests/fixtures/models/Post.php +14 -0
- package/tests/fixtures/models/SafeModel.php +10 -0
- package/tests/fixtures/models/User.php +15 -0
- package/tests/fixtures/redirect/safe.php +38 -0
- package/tests/fixtures/redirect/unsafe.php +39 -0
- package/tests/fixtures/routes/api.php +9 -0
- package/tests/fixtures/routes/web.php +18 -0
- package/tests/fixtures/security-headers/app/Http/Middleware/SecurityHeaders.php +24 -0
- package/tests/fixtures/security-headers/app/Providers/AppServiceProvider.php +16 -0
- package/tests/fixtures/sql/safe_queries.php +7 -0
- package/tests/fixtures/sql/vulnerable_queries.php +7 -0
- package/tests/new-analyzers.test.ts +373 -0
- package/tests/route-parser.test.ts +257 -0
- package/tests/scanner.test.ts +82 -0
- package/tests/session-security.test.ts +161 -0
- package/tests/types.test.ts +29 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
> @havoc/scanner@0.1.0 test /root/projects/havoc/packages/scanner
|
|
3
|
+
> vitest run
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
RUN v2.1.9 /root/projects/havoc/packages/scanner
|
|
7
|
+
|
|
8
|
+
✓ tests/analyzers.test.ts (75 tests) 365ms
|
|
9
|
+
✓ tests/credential-exposure.test.ts (16 tests) 20ms
|
|
10
|
+
✓ tests/file-upload.test.ts (11 tests) 8ms
|
|
11
|
+
✓ tests/session-security.test.ts (14 tests) 8ms
|
|
12
|
+
✓ tests/PrivilegeEscalationAnalyzer.test.ts (12 tests) 36ms
|
|
13
|
+
✓ tests/EncryptionAnalyzer.test.ts (12 tests) 33ms
|
|
14
|
+
✓ tests/RateLimitAnalyzer.test.ts (13 tests) 10ms
|
|
15
|
+
✓ tests/scanner.test.ts (9 tests) 11ms
|
|
16
|
+
✓ tests/types.test.ts (6 tests) 5ms
|
|
17
|
+
|
|
18
|
+
Test Files 9 passed (9)
|
|
19
|
+
Tests 168 passed (168)
|
|
20
|
+
Start at 14:40:00
|
|
21
|
+
Duration 5.50s (transform 1.25s, setup 0ms, collect 2.11s, tests 497ms, environment 3ms, prepare 886ms)
|
|
22
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Analyzer, Finding, HavocConfig, ParsedFile } from '../types/index.js';
|
|
2
|
+
export declare class AuthorizationCoverageAnalyzer implements Analyzer {
|
|
3
|
+
readonly name = "AuthorizationCoverageAnalyzer";
|
|
4
|
+
readonly description = "Checks that all controller action methods have authorization in place";
|
|
5
|
+
analyze(files: ParsedFile[], _config: HavocConfig): Promise<Finding[]>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=AuthorizationCoverageAnalyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthorizationCoverageAnalyzer.d.ts","sourceRoot":"","sources":["../../src/analyzers/AuthorizationCoverageAnalyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAY,MAAM,mBAAmB,CAAC;AA8CzF,qBAAa,6BAA8B,YAAW,QAAQ;IAC5D,QAAQ,CAAC,IAAI,mCAAiB;IAC9B,QAAQ,CAAC,WAAW,2EAA2E;IAEzF,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;CAqE7E"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Severity } from '../types/index.js';
|
|
2
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
3
|
+
const EXCLUDED_METHOD_NAMES = new Set([
|
|
4
|
+
'__construct', '__destruct', 'middleware', 'callAction',
|
|
5
|
+
'authorize', 'authorizeResource', 'authorizeForUser',
|
|
6
|
+
]);
|
|
7
|
+
const AUTHORIZATION_PATTERNS = [
|
|
8
|
+
/\$this->authorize\s*\(/,
|
|
9
|
+
/Gate::authorize\s*\(/,
|
|
10
|
+
/Gate::allows\s*\(/,
|
|
11
|
+
/Gate::denies\s*\(/,
|
|
12
|
+
/Gate::check\s*\(/,
|
|
13
|
+
/Gate::any\s*\(/,
|
|
14
|
+
/Gate::none\s*\(/,
|
|
15
|
+
/->can\s*\(/,
|
|
16
|
+
/->cannot\s*\(/,
|
|
17
|
+
/->cant\s*\(/,
|
|
18
|
+
/\$this->middleware\s*\(\s*['"]can:/,
|
|
19
|
+
];
|
|
20
|
+
const FORM_REQUEST_PATTERNS = [/extends\s+FormRequest/];
|
|
21
|
+
const FINDING_ID = 'HAVOC-AUTH-001';
|
|
22
|
+
const COVERAGE_FINDING_ID = 'HAVOC-AUTH-002';
|
|
23
|
+
const ANALYZER_NAME = 'AuthorizationCoverageAnalyzer';
|
|
24
|
+
function isControllerFile(path) {
|
|
25
|
+
return path.includes('Http/Controllers') || path.includes('Http\\Controllers');
|
|
26
|
+
}
|
|
27
|
+
function isPublicAction(method) {
|
|
28
|
+
return (method.visibility === 'public' &&
|
|
29
|
+
!EXCLUDED_METHOD_NAMES.has(method.name) &&
|
|
30
|
+
!method.name.startsWith('_'));
|
|
31
|
+
}
|
|
32
|
+
function hasAuthorization(method) {
|
|
33
|
+
return AUTHORIZATION_PATTERNS.some((re) => re.test(method.bodyText));
|
|
34
|
+
}
|
|
35
|
+
export class AuthorizationCoverageAnalyzer {
|
|
36
|
+
name = ANALYZER_NAME;
|
|
37
|
+
description = 'Checks that all controller action methods have authorization in place';
|
|
38
|
+
async analyze(files, _config) {
|
|
39
|
+
const findings = [];
|
|
40
|
+
let totalActions = 0;
|
|
41
|
+
let coveredActions = 0;
|
|
42
|
+
for (const file of files) {
|
|
43
|
+
if (!isControllerFile(file.path))
|
|
44
|
+
continue;
|
|
45
|
+
const phpFile = file;
|
|
46
|
+
if (!phpFile.classes)
|
|
47
|
+
continue;
|
|
48
|
+
const hasFormRequest = FORM_REQUEST_PATTERNS.some((re) => re.test(file.content));
|
|
49
|
+
for (const cls of phpFile.classes) {
|
|
50
|
+
for (const method of cls.methods) {
|
|
51
|
+
if (!isPublicAction(method))
|
|
52
|
+
continue;
|
|
53
|
+
totalActions++;
|
|
54
|
+
const authorized = hasAuthorization(method) || hasFormRequest;
|
|
55
|
+
if (authorized) {
|
|
56
|
+
coveredActions++;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
findings.push({
|
|
60
|
+
id: FINDING_ID,
|
|
61
|
+
severity: Severity.High,
|
|
62
|
+
analyzer: ANALYZER_NAME,
|
|
63
|
+
title: `Unprotected controller action: ${cls.name}::${method.name}()`,
|
|
64
|
+
description: `The method \`${method.name}()\` in \`${cls.name}\` does not appear to perform ` +
|
|
65
|
+
`any authorization checks. Missing \`$this->authorize()\`, Gate checks, or ` +
|
|
66
|
+
`policy-backed FormRequest.`,
|
|
67
|
+
file: file.path,
|
|
68
|
+
line: method.line,
|
|
69
|
+
recommendation: 'Add `$this->authorize(\'action\', Model::class)` at the top of the method, ' +
|
|
70
|
+
'or use a policy-backed FormRequest, or apply `can:permission` middleware to the route.',
|
|
71
|
+
cwe: 'CWE-862',
|
|
72
|
+
snippet: method.bodyText.slice(0, 200),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (totalActions > 0) {
|
|
79
|
+
const coveragePercent = Math.round((coveredActions / totalActions) * 100);
|
|
80
|
+
const minCoverage = (_config.analyzers?.authorizationCoverage?.minCoverage ?? 80);
|
|
81
|
+
if (coveragePercent < minCoverage) {
|
|
82
|
+
findings.push({
|
|
83
|
+
id: COVERAGE_FINDING_ID,
|
|
84
|
+
severity: Severity.Medium,
|
|
85
|
+
analyzer: ANALYZER_NAME,
|
|
86
|
+
title: `Authorization coverage is ${coveragePercent}% (minimum: ${minCoverage}%)`,
|
|
87
|
+
description: `Only ${coveredActions} of ${totalActions} controller actions have detectable ` +
|
|
88
|
+
`authorization checks. Target coverage: ${minCoverage}%.`,
|
|
89
|
+
file: 'app/Http/Controllers',
|
|
90
|
+
line: 1,
|
|
91
|
+
recommendation: 'Review unprotected controller actions and add appropriate authorization. ' +
|
|
92
|
+
'Consider using Laravel policies for consistent authorization.',
|
|
93
|
+
cwe: 'CWE-862',
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return findings;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=AuthorizationCoverageAnalyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthorizationCoverageAnalyzer.js","sourceRoot":"","sources":["../../src/analyzers/AuthorizationCoverageAnalyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Analyzer, Finding, HavocConfig, ParsedFile } from '../types/index.js';
|
|
2
|
+
export declare class CredentialExposureAnalyzer implements Analyzer {
|
|
3
|
+
readonly name = "CredentialExposureAnalyzer";
|
|
4
|
+
readonly description = "Detects hardcoded credentials, secrets, and insecure configuration exposure";
|
|
5
|
+
analyze(files: ParsedFile[], _config: HavocConfig): Promise<Finding[]>;
|
|
6
|
+
private checkConfigHardcodedCredentials;
|
|
7
|
+
private checkHardcodedSecrets;
|
|
8
|
+
private checkDbCredentials;
|
|
9
|
+
private checkPrivateKeys;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=CredentialExposureAnalyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CredentialExposureAnalyzer.d.ts","sourceRoot":"","sources":["../../src/analyzers/CredentialExposureAnalyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAY,MAAM,mBAAmB,CAAC;AA6EzF,qBAAa,0BAA2B,YAAW,QAAQ;IACzD,QAAQ,CAAC,IAAI,gCAAiB;IAC9B,QAAQ,CAAC,WAAW,iFAAiF;IAE/F,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAuH5E,OAAO,CAAC,+BAA+B;IA6BvC,OAAO,CAAC,qBAAqB;IA4B7B,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,gBAAgB;CAuBzB"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { Severity } from '../types/index.js';
|
|
2
|
+
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
3
|
+
const ANALYZER_NAME = 'CredentialExposureAnalyzer';
|
|
4
|
+
const FINDING_ENV_FILE_COMMITTED = 'HAVOC-CRED-001';
|
|
5
|
+
const FINDING_HARDCODED_SECRET = 'HAVOC-CRED-002';
|
|
6
|
+
const FINDING_APP_DEBUG = 'HAVOC-CRED-003';
|
|
7
|
+
const FINDING_APP_ENV = 'HAVOC-CRED-004';
|
|
8
|
+
const FINDING_DEBUG_CONFIG = 'HAVOC-CRED-005';
|
|
9
|
+
const FINDING_DB_CREDENTIALS = 'HAVOC-CRED-006';
|
|
10
|
+
const FINDING_PRIVATE_KEY = 'HAVOC-CRED-007';
|
|
11
|
+
const FINDING_CONFIG_HARDCODED = 'HAVOC-CRED-008';
|
|
12
|
+
const SENSITIVE_CONFIG_KEYS = [
|
|
13
|
+
'password', 'secret', 'key', 'token', 'api_key', 'apikey',
|
|
14
|
+
'access_key', 'private_key', 'client_secret', 'client_id',
|
|
15
|
+
'app_id', 'app_secret', 'encryption_key', 'signing_secret',
|
|
16
|
+
'webhook_secret',
|
|
17
|
+
];
|
|
18
|
+
// Secret patterns
|
|
19
|
+
const SECRET_PATTERNS = [
|
|
20
|
+
{ pattern: /['"]sk_live_[0-9a-zA-Z]{24,}['"]/, label: 'Stripe live secret key' },
|
|
21
|
+
{ pattern: /['"]pk_live_[0-9a-zA-Z]{24,}['"]/, label: 'Stripe live publishable key' },
|
|
22
|
+
{ pattern: /['"]AKIA[0-9A-Z]{16}['"]/, label: 'AWS Access Key ID' },
|
|
23
|
+
{ pattern: /(?:password|passwd|pwd)\s*=\s*['"][^'"]{3,}['"]/, label: 'Hardcoded password' },
|
|
24
|
+
{ pattern: /(?:api_key|apikey|api_secret)\s*=\s*['"][^'"]{8,}['"]/, label: 'Hardcoded API key' },
|
|
25
|
+
{ pattern: /(?:access_token|auth_token)\s*=\s*['"][^'"]{8,}['"]/, label: 'Hardcoded access token' },
|
|
26
|
+
{ pattern: /ghp_[0-9a-zA-Z]{36}/, label: 'GitHub personal access token' },
|
|
27
|
+
{ pattern: /xox[baprs]-[0-9a-zA-Z-]{10,}/, label: 'Slack token' },
|
|
28
|
+
];
|
|
29
|
+
const DB_CREDENTIAL_PATTERNS = [
|
|
30
|
+
/new\s+PDO\s*\(\s*['"][^'"]+['"],\s*['"][^'"]+['"],\s*['"][^'"]{3,}['"]/,
|
|
31
|
+
/mysqli_connect\s*\([^)]*,\s*['"][^'"]+['"],\s*['"][^'"]{3,}['"]/,
|
|
32
|
+
];
|
|
33
|
+
const PRIVATE_KEY_PATTERNS = [
|
|
34
|
+
{ pattern: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/, label: 'Private key (PEM)' },
|
|
35
|
+
{ pattern: /'jwt_secret'\s*=>\s*'[^']{16,}'/, label: 'JWT secret in config' },
|
|
36
|
+
];
|
|
37
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
38
|
+
function isPhpFile(path) {
|
|
39
|
+
return path.endsWith('.php') && !path.endsWith('.blade.php');
|
|
40
|
+
}
|
|
41
|
+
function isEnvFile(path) {
|
|
42
|
+
const filename = path.split('/').pop() ?? '';
|
|
43
|
+
return filename === '.env' || /^\.env\.[a-z]+$/.test(filename);
|
|
44
|
+
}
|
|
45
|
+
function isProductionEnvConfig(path) {
|
|
46
|
+
return /\.env\.(production|prod|staging|live)/.test(path);
|
|
47
|
+
}
|
|
48
|
+
function isLocalEnvFile(path) {
|
|
49
|
+
return /\.env\.(local|testing|test|development|dev)/.test(path) || path === '.env.example';
|
|
50
|
+
}
|
|
51
|
+
function isAppConfig(path) {
|
|
52
|
+
return path === 'config/app.php' || path.endsWith('/config/app.php');
|
|
53
|
+
}
|
|
54
|
+
function isConfigFile(path) {
|
|
55
|
+
return /(?:^|\/)config\/[^/]+\.php$/.test(path);
|
|
56
|
+
}
|
|
57
|
+
function findLineNumber(content, matchIndex) {
|
|
58
|
+
return content.slice(0, matchIndex).split('\n').length;
|
|
59
|
+
}
|
|
60
|
+
// ─── Analyzer ─────────────────────────────────────────────────────────────────
|
|
61
|
+
export class CredentialExposureAnalyzer {
|
|
62
|
+
name = ANALYZER_NAME;
|
|
63
|
+
description = 'Detects hardcoded credentials, secrets, and insecure configuration exposure';
|
|
64
|
+
async analyze(files, _config) {
|
|
65
|
+
const findings = [];
|
|
66
|
+
for (const file of files) {
|
|
67
|
+
// 1. .env files committed to the repo
|
|
68
|
+
if (isEnvFile(file.path)) {
|
|
69
|
+
findings.push({
|
|
70
|
+
id: FINDING_ENV_FILE_COMMITTED,
|
|
71
|
+
severity: Severity.Critical,
|
|
72
|
+
analyzer: ANALYZER_NAME,
|
|
73
|
+
title: '`.env` file committed to repository',
|
|
74
|
+
description: `The file \`${file.path}\` was found in the scanned path. ` +
|
|
75
|
+
`.env files typically contain secrets and should never be committed to version control.`,
|
|
76
|
+
file: file.path,
|
|
77
|
+
line: 1,
|
|
78
|
+
recommendation: 'Add `.env` to `.gitignore`. Use `.env.example` with placeholder values instead.',
|
|
79
|
+
cwe: 'CWE-312',
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// 2. Hardcoded secrets in PHP source
|
|
83
|
+
if (isPhpFile(file.path)) {
|
|
84
|
+
this.checkHardcodedSecrets(file, findings);
|
|
85
|
+
this.checkDbCredentials(file, findings);
|
|
86
|
+
this.checkPrivateKeys(file, findings);
|
|
87
|
+
}
|
|
88
|
+
// 3. APP_DEBUG=true in non-local env configs
|
|
89
|
+
if (isEnvFile(file.path) && !isLocalEnvFile(file.path)) {
|
|
90
|
+
const debugIdx = file.content.search(/^APP_DEBUG\s*=\s*true/im);
|
|
91
|
+
if (debugIdx !== -1) {
|
|
92
|
+
findings.push({
|
|
93
|
+
id: FINDING_APP_DEBUG,
|
|
94
|
+
severity: Severity.High,
|
|
95
|
+
analyzer: ANALYZER_NAME,
|
|
96
|
+
title: '`APP_DEBUG=true` in non-local environment config',
|
|
97
|
+
description: `\`APP_DEBUG=true\` was found in \`${file.path}\`. ` +
|
|
98
|
+
`Debug mode exposes stack traces and sensitive application internals to end users.`,
|
|
99
|
+
file: file.path,
|
|
100
|
+
line: findLineNumber(file.content, debugIdx),
|
|
101
|
+
recommendation: 'Set `APP_DEBUG=false` in all non-local environment configs.',
|
|
102
|
+
cwe: 'CWE-209',
|
|
103
|
+
snippet: 'APP_DEBUG=true',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
// 4. APP_ENV=local or testing in production-looking configs
|
|
107
|
+
if (isProductionEnvConfig(file.path)) {
|
|
108
|
+
const envMatch = /^APP_ENV\s*=\s*(local|testing)/im.exec(file.content);
|
|
109
|
+
if (envMatch) {
|
|
110
|
+
findings.push({
|
|
111
|
+
id: FINDING_APP_ENV,
|
|
112
|
+
severity: Severity.Medium,
|
|
113
|
+
analyzer: ANALYZER_NAME,
|
|
114
|
+
title: `\`APP_ENV=${envMatch[1]}\` in production environment config`,
|
|
115
|
+
description: `\`APP_ENV=${envMatch[1]}\` was found in \`${file.path}\`. ` +
|
|
116
|
+
`Production configs should use \`APP_ENV=production\`.`,
|
|
117
|
+
file: file.path,
|
|
118
|
+
line: findLineNumber(file.content, envMatch.index),
|
|
119
|
+
recommendation: 'Set `APP_ENV=production` in production environment configs.',
|
|
120
|
+
cwe: 'CWE-16',
|
|
121
|
+
snippet: `APP_ENV=${envMatch[1]}`,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// 5. Config files with hardcoded credentials
|
|
127
|
+
if (isPhpFile(file.path) && isConfigFile(file.path)) {
|
|
128
|
+
this.checkConfigHardcodedCredentials(file, findings);
|
|
129
|
+
}
|
|
130
|
+
// 6. Verbose error display in config/app.php
|
|
131
|
+
if (isPhpFile(file.path) && isAppConfig(file.path)) {
|
|
132
|
+
const debugConfigIdx = file.content.search(/'debug'\s*=>\s*true/);
|
|
133
|
+
if (debugConfigIdx !== -1) {
|
|
134
|
+
findings.push({
|
|
135
|
+
id: FINDING_DEBUG_CONFIG,
|
|
136
|
+
severity: Severity.High,
|
|
137
|
+
analyzer: ANALYZER_NAME,
|
|
138
|
+
title: '`debug => true` hardcoded in `config/app.php`',
|
|
139
|
+
description: `\`'debug' => true\` is hardcoded in \`${file.path}\`. ` +
|
|
140
|
+
`This enables debug mode regardless of the APP_DEBUG environment variable.`,
|
|
141
|
+
file: file.path,
|
|
142
|
+
line: findLineNumber(file.content, debugConfigIdx),
|
|
143
|
+
recommendation: "Use `'debug' => (bool) env('APP_DEBUG', false)` to read the value from the environment.",
|
|
144
|
+
cwe: 'CWE-209',
|
|
145
|
+
snippet: "'debug' => true",
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const envHardcoded = /'env'\s*=>\s*'(local|testing)'/.exec(file.content);
|
|
149
|
+
if (envHardcoded) {
|
|
150
|
+
findings.push({
|
|
151
|
+
id: FINDING_APP_ENV,
|
|
152
|
+
severity: Severity.Medium,
|
|
153
|
+
analyzer: ANALYZER_NAME,
|
|
154
|
+
title: `\`'env' => '${envHardcoded[1]}'\` hardcoded in \`config/app.php\``,
|
|
155
|
+
description: `The application environment is hardcoded as \`${envHardcoded[1]}\` in \`${file.path}\`. `,
|
|
156
|
+
file: file.path,
|
|
157
|
+
line: findLineNumber(file.content, envHardcoded.index),
|
|
158
|
+
recommendation: "Use `'env' => env('APP_ENV', 'production')` to read from the environment.",
|
|
159
|
+
cwe: 'CWE-16',
|
|
160
|
+
snippet: `'env' => '${envHardcoded[1]}'`,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return findings;
|
|
166
|
+
}
|
|
167
|
+
checkConfigHardcodedCredentials(file, findings) {
|
|
168
|
+
for (const key of SENSITIVE_CONFIG_KEYS) {
|
|
169
|
+
const regex = new RegExp(`['"]\\b${key}\\b['"]\\s*=>\\s*'([^']{3,})'`, 'gi');
|
|
170
|
+
let match;
|
|
171
|
+
while ((match = regex.exec(file.content)) !== null) {
|
|
172
|
+
const value = match[1];
|
|
173
|
+
if (/^(null|true|false|your-|xxx|placeholder|changeme)/i.test(value))
|
|
174
|
+
continue;
|
|
175
|
+
const before = file.content.slice(Math.max(0, match.index - 80), match.index);
|
|
176
|
+
if (/env\s*\([^)]*,\s*$/.test(before))
|
|
177
|
+
continue;
|
|
178
|
+
findings.push({
|
|
179
|
+
id: FINDING_CONFIG_HARDCODED,
|
|
180
|
+
severity: Severity.High,
|
|
181
|
+
analyzer: ANALYZER_NAME,
|
|
182
|
+
title: `Hardcoded \`${key}\` in config file \`${file.path}\``,
|
|
183
|
+
description: `The config key \`${key}\` in \`${file.path}\` has a hardcoded value instead of ` +
|
|
184
|
+
`using \`env()\`. Credentials in source control are exposed to anyone with repo access.`,
|
|
185
|
+
file: file.path,
|
|
186
|
+
line: findLineNumber(file.content, match.index),
|
|
187
|
+
recommendation: `Use \`'${key}' => env('${key.toUpperCase()}')\` to read from environment variables.`,
|
|
188
|
+
cwe: 'CWE-798',
|
|
189
|
+
snippet: match[0].slice(0, 60),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
checkHardcodedSecrets(file, findings) {
|
|
195
|
+
for (const { pattern, label } of SECRET_PATTERNS) {
|
|
196
|
+
const regex = new RegExp(pattern.source, 'g');
|
|
197
|
+
let match;
|
|
198
|
+
while ((match = regex.exec(file.content)) !== null) {
|
|
199
|
+
// Skip if value comes from env() call
|
|
200
|
+
const before = file.content.slice(Math.max(0, match.index - 40), match.index);
|
|
201
|
+
if (/env\s*\(/.test(before))
|
|
202
|
+
continue;
|
|
203
|
+
findings.push({
|
|
204
|
+
id: FINDING_HARDCODED_SECRET,
|
|
205
|
+
severity: Severity.Critical,
|
|
206
|
+
analyzer: ANALYZER_NAME,
|
|
207
|
+
title: `Hardcoded ${label} detected`,
|
|
208
|
+
description: `A ${label} appears to be hardcoded in \`${file.path}\`. ` +
|
|
209
|
+
`Hardcoded credentials can be extracted from source code and version control history.`,
|
|
210
|
+
file: file.path,
|
|
211
|
+
line: findLineNumber(file.content, match.index),
|
|
212
|
+
recommendation: 'Store secrets in environment variables and access them via `env()`. Never commit secrets to source control.',
|
|
213
|
+
cwe: 'CWE-798',
|
|
214
|
+
snippet: match[0].slice(0, 60),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
checkDbCredentials(file, findings) {
|
|
220
|
+
for (const pattern of DB_CREDENTIAL_PATTERNS) {
|
|
221
|
+
const regex = new RegExp(pattern.source, 'gs');
|
|
222
|
+
let match;
|
|
223
|
+
while ((match = regex.exec(file.content)) !== null) {
|
|
224
|
+
findings.push({
|
|
225
|
+
id: FINDING_DB_CREDENTIALS,
|
|
226
|
+
severity: Severity.Critical,
|
|
227
|
+
analyzer: ANALYZER_NAME,
|
|
228
|
+
title: 'Database credentials hardcoded in source',
|
|
229
|
+
description: `Database credentials appear to be hardcoded in \`${file.path}\`. ` +
|
|
230
|
+
`This exposes credentials to anyone with read access to the source code.`,
|
|
231
|
+
file: file.path,
|
|
232
|
+
line: findLineNumber(file.content, match.index),
|
|
233
|
+
recommendation: 'Use environment variables for database credentials: `DB_HOST`, `DB_USERNAME`, `DB_PASSWORD`.',
|
|
234
|
+
cwe: 'CWE-312',
|
|
235
|
+
snippet: match[0].slice(0, 60),
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
checkPrivateKeys(file, findings) {
|
|
241
|
+
for (const { pattern, label } of PRIVATE_KEY_PATTERNS) {
|
|
242
|
+
const regex = new RegExp(pattern.source, 'g');
|
|
243
|
+
let match;
|
|
244
|
+
while ((match = regex.exec(file.content)) !== null) {
|
|
245
|
+
findings.push({
|
|
246
|
+
id: FINDING_PRIVATE_KEY,
|
|
247
|
+
severity: Severity.Critical,
|
|
248
|
+
analyzer: ANALYZER_NAME,
|
|
249
|
+
title: `${label} found inline in source code`,
|
|
250
|
+
description: `A ${label} was found inline in \`${file.path}\`. ` +
|
|
251
|
+
`Private keys and JWT secrets must never be stored in source code.`,
|
|
252
|
+
file: file.path,
|
|
253
|
+
line: findLineNumber(file.content, match.index),
|
|
254
|
+
recommendation: 'Store private keys in environment variables or a secrets manager. Never commit them to source control.',
|
|
255
|
+
cwe: 'CWE-321',
|
|
256
|
+
snippet: match[0].slice(0, 60),
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=CredentialExposureAnalyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CredentialExposureAnalyzer.js","sourceRoot":"","sources":["../../src/analyzers/CredentialExposureAnalyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Analyzer, Finding, HavocConfig, ParsedFile } from '../types/index.js';
|
|
2
|
+
interface ComposerAdvisory {
|
|
3
|
+
advisoryId: string;
|
|
4
|
+
packageName: string;
|
|
5
|
+
title: string;
|
|
6
|
+
link: string;
|
|
7
|
+
cve: string | null;
|
|
8
|
+
severity: string;
|
|
9
|
+
affectedVersions: string;
|
|
10
|
+
sources: {
|
|
11
|
+
name: string;
|
|
12
|
+
remoteId: string;
|
|
13
|
+
}[];
|
|
14
|
+
reportedAt: string;
|
|
15
|
+
}
|
|
16
|
+
interface ComposerAuditResult {
|
|
17
|
+
advisories: Record<string, ComposerAdvisory[]>;
|
|
18
|
+
abandoned: Record<string, string>;
|
|
19
|
+
}
|
|
20
|
+
export declare class DependencyAuditAnalyzer implements Analyzer {
|
|
21
|
+
readonly name = "DependencyAuditAnalyzer";
|
|
22
|
+
readonly description = "Audits composer.json for known vulnerable PHP dependencies";
|
|
23
|
+
private readonly composerRunner;
|
|
24
|
+
constructor(composerRunner?: (projectPath: string) => Promise<ComposerAuditResult | null>);
|
|
25
|
+
analyze(files: ParsedFile[], config: HavocConfig): Promise<Finding[]>;
|
|
26
|
+
}
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=DependencyAuditAnalyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DependencyAuditAnalyzer.d.ts","sourceRoot":"","sources":["../../src/analyzers/DependencyAuditAnalyzer.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAY,MAAM,mBAAmB,CAAC;AAiBzF,UAAU,gBAAgB;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9C,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,mBAAmB;IAC3B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC/C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAMD,qBAAa,uBAAwB,YAAW,QAAQ;IACtD,QAAQ,CAAC,IAAI,6BAAiB;IAC9B,QAAQ,CAAC,WAAW,gEAAgE;IAEpF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA+D;gBAElF,cAAc,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAInF,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;CA6D5E"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { Severity } from '../types/index.js';
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
const FINDING_VULNERABLE_DEP = 'HAVOC-DEP-001';
|
|
8
|
+
const FINDING_COMPOSER_UNAVAILABLE = 'HAVOC-DEP-002';
|
|
9
|
+
const ANALYZER_NAME = 'DependencyAuditAnalyzer';
|
|
10
|
+
const SEVERITY_MAP = {
|
|
11
|
+
critical: Severity.Critical,
|
|
12
|
+
high: Severity.High,
|
|
13
|
+
medium: Severity.Medium,
|
|
14
|
+
moderate: Severity.Medium,
|
|
15
|
+
low: Severity.Low,
|
|
16
|
+
info: Severity.Info,
|
|
17
|
+
};
|
|
18
|
+
function mapSeverity(severity) {
|
|
19
|
+
return SEVERITY_MAP[severity.toLowerCase()] ?? Severity.Medium;
|
|
20
|
+
}
|
|
21
|
+
export class DependencyAuditAnalyzer {
|
|
22
|
+
name = ANALYZER_NAME;
|
|
23
|
+
description = 'Audits composer.json for known vulnerable PHP dependencies';
|
|
24
|
+
composerRunner;
|
|
25
|
+
constructor(composerRunner) {
|
|
26
|
+
this.composerRunner = composerRunner ?? defaultComposerRunner;
|
|
27
|
+
}
|
|
28
|
+
async analyze(files, config) {
|
|
29
|
+
const findings = [];
|
|
30
|
+
const composerFile = files.find((f) => f.path === 'composer.json' || f.path.endsWith('/composer.json'));
|
|
31
|
+
if (!composerFile)
|
|
32
|
+
return findings;
|
|
33
|
+
const projectRoot = config.projectRoot ?? '.';
|
|
34
|
+
try {
|
|
35
|
+
const auditResult = await this.composerRunner(projectRoot);
|
|
36
|
+
if (!auditResult) {
|
|
37
|
+
findings.push({
|
|
38
|
+
id: FINDING_COMPOSER_UNAVAILABLE,
|
|
39
|
+
severity: Severity.Info,
|
|
40
|
+
analyzer: ANALYZER_NAME,
|
|
41
|
+
title: 'composer audit unavailable — dependency check skipped',
|
|
42
|
+
description: '`composer` is not available in PATH or does not support the `audit` command (requires Composer 2.4+).',
|
|
43
|
+
file: composerFile.path,
|
|
44
|
+
line: 1,
|
|
45
|
+
recommendation: 'Install Composer 2.4+ and run `composer audit` manually.',
|
|
46
|
+
cwe: 'CWE-1104',
|
|
47
|
+
});
|
|
48
|
+
return findings;
|
|
49
|
+
}
|
|
50
|
+
for (const [packageName, advisories] of Object.entries(auditResult.advisories)) {
|
|
51
|
+
for (const advisory of advisories) {
|
|
52
|
+
findings.push({
|
|
53
|
+
id: FINDING_VULNERABLE_DEP,
|
|
54
|
+
severity: mapSeverity(advisory.severity),
|
|
55
|
+
analyzer: ANALYZER_NAME,
|
|
56
|
+
title: `Vulnerable dependency: ${packageName} — ${advisory.title}`,
|
|
57
|
+
description: `Package \`${packageName}\` has a known vulnerability: ${advisory.title}. ` +
|
|
58
|
+
(advisory.cve ? `CVE: ${advisory.cve}. ` : '') +
|
|
59
|
+
`Affected versions: ${advisory.affectedVersions}.`,
|
|
60
|
+
file: 'composer.json',
|
|
61
|
+
line: 1,
|
|
62
|
+
recommendation: `Update \`${packageName}\` to a patched version. See: ${advisory.link}`,
|
|
63
|
+
cwe: advisory.cve ? `CVE: ${advisory.cve}` : 'CWE-1104',
|
|
64
|
+
snippet: `"${packageName}": "${advisory.affectedVersions}"`,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
findings.push({
|
|
71
|
+
id: FINDING_COMPOSER_UNAVAILABLE,
|
|
72
|
+
severity: Severity.Info,
|
|
73
|
+
analyzer: ANALYZER_NAME,
|
|
74
|
+
title: 'composer audit failed — dependency check skipped',
|
|
75
|
+
description: `composer audit returned an error: ${err.message}`,
|
|
76
|
+
file: composerFile.path,
|
|
77
|
+
line: 1,
|
|
78
|
+
recommendation: 'Ensure composer is installed and `composer.lock` is present.',
|
|
79
|
+
cwe: 'CWE-1104',
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return findings;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function defaultComposerRunner(projectPath) {
|
|
86
|
+
const composerLock = resolve(projectPath, 'composer.lock');
|
|
87
|
+
if (!existsSync(composerLock))
|
|
88
|
+
return null;
|
|
89
|
+
try {
|
|
90
|
+
const { stdout } = await execFileAsync('composer', ['audit', '--format=json', '--no-interaction'], {
|
|
91
|
+
cwd: projectPath,
|
|
92
|
+
timeout: 30_000,
|
|
93
|
+
});
|
|
94
|
+
return JSON.parse(stdout);
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
const error = err;
|
|
98
|
+
if (error.stdout) {
|
|
99
|
+
try {
|
|
100
|
+
return JSON.parse(error.stdout);
|
|
101
|
+
}
|
|
102
|
+
catch { /* not JSON */ }
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=DependencyAuditAnalyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DependencyAuditAnalyzer.js","sourceRoot":"","sources":["../../src/analyzers/DependencyAuditAnalyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAA8C,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAEzF,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,MAAM,sBAAsB,GAAG,eAAe,CAAC;AAC/C,MAAM,4BAA4B,GAAG,eAAe,CAAC;AACrD,MAAM,aAAa,GAAG,yBAAyB,CAAC;AAEhD,MAAM,YAAY,GAA6B;IAC7C,QAAQ,EAAE,QAAQ,CAAC,QAAQ;IAC3B,IAAI,EAAE,QAAQ,CAAC,IAAI;IACnB,MAAM,EAAE,QAAQ,CAAC,MAAM;IACvB,QAAQ,EAAE,QAAQ,CAAC,MAAM;IACzB,GAAG,EAAE,QAAQ,CAAC,GAAG;IACjB,IAAI,EAAE,QAAQ,CAAC,IAAI;CACpB,CAAC;AAmBF,SAAS,WAAW,CAAC,QAAgB;IACnC,OAAO,YAAY,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC;AACjE,CAAC;AAED,MAAM,OAAO,uBAAuB;IACzB,IAAI,GAAG,aAAa,CAAC;IACrB,WAAW,GAAG,4DAA4D,CAAC;IAEnE,cAAc,CAA+D;IAE9F,YAAY,cAA6E;QACvF,IAAI,CAAC,cAAc,GAAG,cAAc,IAAI,qBAAqB,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAmB,EAAE,MAAmB;QACpD,MAAM,QAAQ,GAAc,EAAE,CAAC;QAE/B,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,eAAe,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC;QACxG,IAAI,CAAC,YAAY;YAAE,OAAO,QAAQ,CAAC;QAEnC,MAAM,WAAW,GAAI,MAAiD,CAAC,WAAW,IAAI,GAAG,CAAC;QAE1F,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;YAC3D,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,QAAQ,CAAC,IAAI,CAAC;oBACZ,EAAE,EAAE,4BAA4B;oBAChC,QAAQ,EAAE,QAAQ,CAAC,IAAI;oBACvB,QAAQ,EAAE,aAAa;oBACvB,KAAK,EAAE,uDAAuD;oBAC9D,WAAW,EACT,uGAAuG;oBACzG,IAAI,EAAE,YAAY,CAAC,IAAI;oBACvB,IAAI,EAAE,CAAC;oBACP,cAAc,EAAE,0DAA0D;oBAC1E,GAAG,EAAE,UAAU;iBAChB,CAAC,CAAC;gBACH,OAAO,QAAQ,CAAC;YAClB,CAAC;YAED,KAAK,MAAM,CAAC,WAAW,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC/E,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;oBAClC,QAAQ,CAAC,IAAI,CAAC;wBACZ,EAAE,EAAE,sBAAsB;wBAC1B,QAAQ,EAAE,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC;wBACxC,QAAQ,EAAE,aAAa;wBACvB,KAAK,EAAE,0BAA0B,WAAW,MAAM,QAAQ,CAAC,KAAK,EAAE;wBAClE,WAAW,EACT,aAAa,WAAW,iCAAiC,QAAQ,CAAC,KAAK,IAAI;4BAC3E,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;4BAC9C,sBAAsB,QAAQ,CAAC,gBAAgB,GAAG;wBACpD,IAAI,EAAE,eAAe;wBACrB,IAAI,EAAE,CAAC;wBACP,cAAc,EAAE,YAAY,WAAW,iCAAiC,QAAQ,CAAC,IAAI,EAAE;wBACvF,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,UAAU;wBACvD,OAAO,EAAE,IAAI,WAAW,OAAO,QAAQ,CAAC,gBAAgB,GAAG;qBAC5D,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,4BAA4B;gBAChC,QAAQ,EAAE,QAAQ,CAAC,IAAI;gBACvB,QAAQ,EAAE,aAAa;gBACvB,KAAK,EAAE,kDAAkD;gBACzD,WAAW,EAAE,qCAAsC,GAAa,CAAC,OAAO,EAAE;gBAC1E,IAAI,EAAE,YAAY,CAAC,IAAI;gBACvB,IAAI,EAAE,CAAC;gBACP,cAAc,EAAE,8DAA8D;gBAC9E,GAAG,EAAE,UAAU;aAChB,CAAC,CAAC;QACL,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF;AAED,KAAK,UAAU,qBAAqB,CAAC,WAAmB;IACtD,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAC3D,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAC;IAE3C,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,kBAAkB,CAAC,EAAE;YACjG,GAAG,EAAE,WAAW;YAChB,OAAO,EAAE,MAAM;SAChB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAwB,CAAC;IACnD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,GAAyC,CAAC;QACxD,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjB,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAwB,CAAC;YACzD,CAAC;YAAC,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;QAC5B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Analyzer, Finding, HavocConfig, ParsedFile } from '../types/index.js';
|
|
2
|
+
export declare class EncryptionAnalyzer implements Analyzer {
|
|
3
|
+
readonly name = "EncryptionAnalyzer";
|
|
4
|
+
readonly description = "Detects sensitive model attributes missing encryption casts and other encryption issues";
|
|
5
|
+
analyze(files: ParsedFile[], _config: HavocConfig): Promise<Finding[]>;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=EncryptionAnalyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EncryptionAnalyzer.d.ts","sourceRoot":"","sources":["../../src/analyzers/EncryptionAnalyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAY,MAAM,mBAAmB,CAAC;AAiEzF,qBAAa,kBAAmB,YAAW,QAAQ;IACjD,QAAQ,CAAC,IAAI,wBAAiB;IAC9B,QAAQ,CAAC,WAAW,6FACwE;IAEtF,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;CA4H7E"}
|