s3db.js 13.6.1 → 14.0.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/README.md +56 -15
- package/dist/s3db.cjs +72446 -39022
- package/dist/s3db.cjs.map +1 -1
- package/dist/s3db.es.js +72172 -38790
- package/dist/s3db.es.js.map +1 -1
- package/mcp/lib/base-handler.js +157 -0
- package/mcp/lib/handlers/connection-handler.js +280 -0
- package/mcp/lib/handlers/query-handler.js +533 -0
- package/mcp/lib/handlers/resource-handler.js +428 -0
- package/mcp/lib/tool-registry.js +336 -0
- package/mcp/lib/tools/connection-tools.js +161 -0
- package/mcp/lib/tools/query-tools.js +267 -0
- package/mcp/lib/tools/resource-tools.js +404 -0
- package/package.json +85 -50
- package/src/clients/memory-client.class.js +346 -191
- package/src/clients/memory-storage.class.js +300 -84
- package/src/clients/s3-client.class.js +7 -6
- package/src/concerns/geo-encoding.js +19 -2
- package/src/concerns/ip.js +59 -9
- package/src/concerns/money.js +8 -1
- package/src/concerns/password-hashing.js +49 -8
- package/src/concerns/plugin-storage.js +186 -18
- package/src/concerns/storage-drivers/filesystem-driver.js +284 -0
- package/src/database.class.js +139 -29
- package/src/errors.js +332 -42
- package/src/plugins/api/auth/oidc-auth.js +66 -17
- package/src/plugins/api/auth/strategies/base-strategy.class.js +74 -0
- package/src/plugins/api/auth/strategies/factory.class.js +63 -0
- package/src/plugins/api/auth/strategies/global-strategy.class.js +44 -0
- package/src/plugins/api/auth/strategies/path-based-strategy.class.js +83 -0
- package/src/plugins/api/auth/strategies/path-rules-strategy.class.js +118 -0
- package/src/plugins/api/concerns/failban-manager.js +106 -57
- package/src/plugins/api/concerns/route-context.js +601 -0
- package/src/plugins/api/index.js +168 -40
- package/src/plugins/api/routes/auth-routes.js +198 -30
- package/src/plugins/api/routes/resource-routes.js +19 -4
- package/src/plugins/api/server/health-manager.class.js +163 -0
- package/src/plugins/api/server/middleware-chain.class.js +310 -0
- package/src/plugins/api/server/router.class.js +472 -0
- package/src/plugins/api/server.js +280 -1303
- package/src/plugins/api/utils/custom-routes.js +17 -5
- package/src/plugins/api/utils/guards.js +76 -17
- package/src/plugins/api/utils/openapi-generator-cached.class.js +133 -0
- package/src/plugins/api/utils/openapi-generator.js +7 -6
- package/src/plugins/audit.plugin.js +30 -8
- package/src/plugins/backup.plugin.js +110 -14
- package/src/plugins/cache/cache.class.js +22 -5
- package/src/plugins/cache/filesystem-cache.class.js +116 -19
- package/src/plugins/cache/memory-cache.class.js +211 -57
- package/src/plugins/cache/multi-tier-cache.class.js +371 -0
- package/src/plugins/cache/partition-aware-filesystem-cache.class.js +168 -47
- package/src/plugins/cache/redis-cache.class.js +552 -0
- package/src/plugins/cache/s3-cache.class.js +17 -8
- package/src/plugins/cache.plugin.js +176 -61
- package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/aws-driver.js +60 -29
- package/src/plugins/cloud-inventory/drivers/azure-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/base-driver.js +16 -2
- package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/linode-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +8 -1
- package/src/plugins/cloud-inventory/drivers/vultr-driver.js +8 -1
- package/src/plugins/cloud-inventory/index.js +29 -8
- package/src/plugins/cloud-inventory/registry.js +64 -42
- package/src/plugins/cloud-inventory.plugin.js +240 -138
- package/src/plugins/concerns/plugin-dependencies.js +54 -0
- package/src/plugins/concerns/resource-names.js +100 -0
- package/src/plugins/consumers/index.js +10 -2
- package/src/plugins/consumers/sqs-consumer.js +12 -2
- package/src/plugins/cookie-farm-suite.plugin.js +278 -0
- package/src/plugins/cookie-farm.errors.js +73 -0
- package/src/plugins/cookie-farm.plugin.js +869 -0
- package/src/plugins/costs.plugin.js +7 -1
- package/src/plugins/eventual-consistency/analytics.js +94 -19
- package/src/plugins/eventual-consistency/config.js +15 -7
- package/src/plugins/eventual-consistency/consolidation.js +29 -11
- package/src/plugins/eventual-consistency/garbage-collection.js +3 -1
- package/src/plugins/eventual-consistency/helpers.js +39 -14
- package/src/plugins/eventual-consistency/install.js +21 -2
- package/src/plugins/eventual-consistency/utils.js +32 -10
- package/src/plugins/fulltext.plugin.js +38 -11
- package/src/plugins/geo.plugin.js +61 -9
- package/src/plugins/identity/concerns/config.js +61 -0
- package/src/plugins/identity/concerns/mfa-manager.js +15 -2
- package/src/plugins/identity/concerns/rate-limit.js +124 -0
- package/src/plugins/identity/concerns/resource-schemas.js +9 -1
- package/src/plugins/identity/concerns/token-generator.js +29 -4
- package/src/plugins/identity/drivers/auth-driver.interface.js +76 -0
- package/src/plugins/identity/drivers/client-credentials-driver.js +127 -0
- package/src/plugins/identity/drivers/index.js +18 -0
- package/src/plugins/identity/drivers/password-driver.js +122 -0
- package/src/plugins/identity/email-service.js +17 -2
- package/src/plugins/identity/index.js +413 -69
- package/src/plugins/identity/oauth2-server.js +413 -30
- package/src/plugins/identity/oidc-discovery.js +16 -8
- package/src/plugins/identity/rsa-keys.js +115 -35
- package/src/plugins/identity/server.js +166 -45
- package/src/plugins/identity/session-manager.js +53 -7
- package/src/plugins/identity/ui/pages/mfa-verification.js +17 -15
- package/src/plugins/identity/ui/routes.js +363 -255
- package/src/plugins/importer/index.js +153 -20
- package/src/plugins/index.js +9 -2
- package/src/plugins/kubernetes-inventory/index.js +6 -0
- package/src/plugins/kubernetes-inventory/k8s-driver.js +867 -0
- package/src/plugins/kubernetes-inventory/resource-types.js +274 -0
- package/src/plugins/kubernetes-inventory.plugin.js +980 -0
- package/src/plugins/metrics.plugin.js +64 -16
- package/src/plugins/ml/base-model.class.js +25 -15
- package/src/plugins/ml/regression-model.class.js +1 -1
- package/src/plugins/ml.errors.js +57 -25
- package/src/plugins/ml.plugin.js +28 -4
- package/src/plugins/namespace.js +210 -0
- package/src/plugins/plugin.class.js +180 -8
- package/src/plugins/puppeteer/console-monitor.js +729 -0
- package/src/plugins/puppeteer/cookie-manager.js +492 -0
- package/src/plugins/puppeteer/network-monitor.js +816 -0
- package/src/plugins/puppeteer/performance-manager.js +746 -0
- package/src/plugins/puppeteer/proxy-manager.js +478 -0
- package/src/plugins/puppeteer/stealth-manager.js +556 -0
- package/src/plugins/puppeteer.errors.js +81 -0
- package/src/plugins/puppeteer.plugin.js +1327 -0
- package/src/plugins/queue-consumer.plugin.js +69 -14
- package/src/plugins/recon/behaviors/uptime-behavior.js +691 -0
- package/src/plugins/recon/concerns/command-runner.js +148 -0
- package/src/plugins/recon/concerns/diff-detector.js +372 -0
- package/src/plugins/recon/concerns/fingerprint-builder.js +307 -0
- package/src/plugins/recon/concerns/process-manager.js +338 -0
- package/src/plugins/recon/concerns/report-generator.js +478 -0
- package/src/plugins/recon/concerns/security-analyzer.js +571 -0
- package/src/plugins/recon/concerns/target-normalizer.js +68 -0
- package/src/plugins/recon/config/defaults.js +321 -0
- package/src/plugins/recon/config/resources.js +370 -0
- package/src/plugins/recon/index.js +778 -0
- package/src/plugins/recon/managers/dependency-manager.js +174 -0
- package/src/plugins/recon/managers/scheduler-manager.js +179 -0
- package/src/plugins/recon/managers/storage-manager.js +745 -0
- package/src/plugins/recon/managers/target-manager.js +274 -0
- package/src/plugins/recon/stages/asn-stage.js +314 -0
- package/src/plugins/recon/stages/certificate-stage.js +84 -0
- package/src/plugins/recon/stages/dns-stage.js +107 -0
- package/src/plugins/recon/stages/dnsdumpster-stage.js +362 -0
- package/src/plugins/recon/stages/fingerprint-stage.js +71 -0
- package/src/plugins/recon/stages/google-dorks-stage.js +440 -0
- package/src/plugins/recon/stages/http-stage.js +89 -0
- package/src/plugins/recon/stages/latency-stage.js +148 -0
- package/src/plugins/recon/stages/massdns-stage.js +302 -0
- package/src/plugins/recon/stages/osint-stage.js +1373 -0
- package/src/plugins/recon/stages/ports-stage.js +169 -0
- package/src/plugins/recon/stages/screenshot-stage.js +94 -0
- package/src/plugins/recon/stages/secrets-stage.js +514 -0
- package/src/plugins/recon/stages/subdomains-stage.js +295 -0
- package/src/plugins/recon/stages/tls-audit-stage.js +78 -0
- package/src/plugins/recon/stages/vulnerability-stage.js +78 -0
- package/src/plugins/recon/stages/web-discovery-stage.js +113 -0
- package/src/plugins/recon/stages/whois-stage.js +349 -0
- package/src/plugins/recon.plugin.js +75 -0
- package/src/plugins/recon.plugin.js.backup +2635 -0
- package/src/plugins/relation.errors.js +87 -14
- package/src/plugins/replicator.plugin.js +514 -137
- package/src/plugins/replicators/base-replicator.class.js +89 -1
- package/src/plugins/replicators/bigquery-replicator.class.js +66 -22
- package/src/plugins/replicators/dynamodb-replicator.class.js +22 -15
- package/src/plugins/replicators/mongodb-replicator.class.js +22 -15
- package/src/plugins/replicators/mysql-replicator.class.js +52 -17
- package/src/plugins/replicators/planetscale-replicator.class.js +30 -4
- package/src/plugins/replicators/postgres-replicator.class.js +62 -27
- package/src/plugins/replicators/s3db-replicator.class.js +25 -18
- package/src/plugins/replicators/schema-sync.helper.js +3 -3
- package/src/plugins/replicators/sqs-replicator.class.js +8 -2
- package/src/plugins/replicators/turso-replicator.class.js +23 -3
- package/src/plugins/replicators/webhook-replicator.class.js +42 -4
- package/src/plugins/s3-queue.plugin.js +464 -65
- package/src/plugins/scheduler.plugin.js +20 -6
- package/src/plugins/state-machine.plugin.js +40 -9
- package/src/plugins/tfstate/base-driver.js +28 -4
- package/src/plugins/tfstate/errors.js +65 -10
- package/src/plugins/tfstate/filesystem-driver.js +52 -8
- package/src/plugins/tfstate/index.js +163 -90
- package/src/plugins/tfstate/s3-driver.js +64 -6
- package/src/plugins/ttl.plugin.js +72 -17
- package/src/plugins/vector/distances.js +18 -12
- package/src/plugins/vector/kmeans.js +26 -4
- package/src/resource.class.js +115 -19
- package/src/testing/factory.class.js +20 -3
- package/src/testing/seeder.class.js +7 -1
- package/src/clients/memory-client.md +0 -917
- package/src/plugins/cloud-inventory/drivers/mock-drivers.js +0 -449
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SecurityAnalyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes scan results and generates security audit checklist
|
|
5
|
+
* Based on OWASP Top 10, CIS benchmarks, and industry best practices
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const report = await plugin.scan('example.com', { behavior: 'aggressive' });
|
|
9
|
+
* const audit = SecurityAnalyzer.analyze(report);
|
|
10
|
+
* console.log(audit);
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export class SecurityAnalyzer {
|
|
14
|
+
/**
|
|
15
|
+
* Analyze scan report and generate security checklist
|
|
16
|
+
* @param {Object} report - Scan report from ReconPlugin
|
|
17
|
+
* @returns {Object} Security audit with findings and recommendations
|
|
18
|
+
*/
|
|
19
|
+
static analyze(report) {
|
|
20
|
+
const audit = {
|
|
21
|
+
target: report.target.host,
|
|
22
|
+
timestamp: report.timestamp,
|
|
23
|
+
summary: {
|
|
24
|
+
score: 0, // 0-100
|
|
25
|
+
grade: 'F', // A, B, C, D, F
|
|
26
|
+
total: 0,
|
|
27
|
+
passed: 0,
|
|
28
|
+
failed: 0,
|
|
29
|
+
critical: 0,
|
|
30
|
+
high: 0,
|
|
31
|
+
medium: 0,
|
|
32
|
+
low: 0
|
|
33
|
+
},
|
|
34
|
+
checklist: [],
|
|
35
|
+
recommendations: []
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const results = report.results;
|
|
39
|
+
|
|
40
|
+
// A) Inventory Check
|
|
41
|
+
audit.checklist.push(this.checkInventory(results, audit.summary));
|
|
42
|
+
|
|
43
|
+
// B) Secrets Check
|
|
44
|
+
audit.checklist.push(this.checkSecrets(results, audit.summary));
|
|
45
|
+
|
|
46
|
+
// C) Dependencies Check (placeholder)
|
|
47
|
+
audit.checklist.push(this.checkDependencies(results, audit.summary));
|
|
48
|
+
|
|
49
|
+
// D) Headers & TLS Check
|
|
50
|
+
audit.checklist.push(this.checkSecurityHeaders(results, audit.summary));
|
|
51
|
+
audit.checklist.push(this.checkTLS(results, audit.summary));
|
|
52
|
+
|
|
53
|
+
// E) WAF & Rate Limiting
|
|
54
|
+
audit.checklist.push(this.checkWAF(results, audit.summary));
|
|
55
|
+
|
|
56
|
+
// F) Auth Check (placeholder)
|
|
57
|
+
audit.checklist.push(this.checkAuth(results, audit.summary));
|
|
58
|
+
|
|
59
|
+
// Calculate final score and grade
|
|
60
|
+
this.calculateScoreAndGrade(audit);
|
|
61
|
+
|
|
62
|
+
// Generate prioritized recommendations
|
|
63
|
+
this.generateRecommendations(audit);
|
|
64
|
+
|
|
65
|
+
return audit;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A) Inventory: subdomains, IPs, certs, CDN
|
|
70
|
+
*/
|
|
71
|
+
static checkInventory(results, summary) {
|
|
72
|
+
const check = {
|
|
73
|
+
id: 'A',
|
|
74
|
+
name: 'Inventory',
|
|
75
|
+
description: 'Subdomains, IPs, certificates, CDN detection',
|
|
76
|
+
status: 'unknown',
|
|
77
|
+
findings: [],
|
|
78
|
+
score: 100
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
summary.total++;
|
|
82
|
+
|
|
83
|
+
// Check subdomains
|
|
84
|
+
if (results.subdomains) {
|
|
85
|
+
const count = results.subdomains.subdomains?.length || 0;
|
|
86
|
+
|
|
87
|
+
check.findings.push({
|
|
88
|
+
type: count > 0 ? 'pass' : 'info',
|
|
89
|
+
severity: 'info',
|
|
90
|
+
item: 'Subdomains Enumeration',
|
|
91
|
+
detail: `${count} subdomains discovered`,
|
|
92
|
+
action: count > 50 ? 'Review all subdomains for unnecessary exposure' : 'OK'
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (count > 100) {
|
|
96
|
+
check.findings.push({
|
|
97
|
+
type: 'warning',
|
|
98
|
+
severity: 'medium',
|
|
99
|
+
item: 'Excessive Subdomains',
|
|
100
|
+
detail: `${count} subdomains may indicate wildcard DNS or shadow IT`,
|
|
101
|
+
action: 'Review subdomain creation policies and implement DNS zone monitoring'
|
|
102
|
+
});
|
|
103
|
+
summary.medium++;
|
|
104
|
+
check.score -= 10;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check SSL certificate
|
|
109
|
+
if (results.certificate) {
|
|
110
|
+
const validTo = results.certificate.validTo;
|
|
111
|
+
if (validTo) {
|
|
112
|
+
const daysUntilExpiry = this.calculateDaysUntilExpiry(validTo);
|
|
113
|
+
|
|
114
|
+
if (daysUntilExpiry < 7) {
|
|
115
|
+
check.findings.push({
|
|
116
|
+
type: 'fail',
|
|
117
|
+
severity: 'critical',
|
|
118
|
+
item: 'Certificate Expiring',
|
|
119
|
+
detail: `SSL certificate expires in ${daysUntilExpiry} days`,
|
|
120
|
+
action: 'Renew SSL certificate IMMEDIATELY. Implement auto-renewal (Let\'s Encrypt)'
|
|
121
|
+
});
|
|
122
|
+
summary.critical++;
|
|
123
|
+
summary.failed++;
|
|
124
|
+
check.score -= 25;
|
|
125
|
+
} else if (daysUntilExpiry < 30) {
|
|
126
|
+
check.findings.push({
|
|
127
|
+
type: 'warning',
|
|
128
|
+
severity: 'high',
|
|
129
|
+
item: 'Certificate Expiring Soon',
|
|
130
|
+
detail: `SSL certificate expires in ${daysUntilExpiry} days`,
|
|
131
|
+
action: 'Renew SSL certificate soon. Set up expiry monitoring/alerts'
|
|
132
|
+
});
|
|
133
|
+
summary.high++;
|
|
134
|
+
check.score -= 15;
|
|
135
|
+
} else {
|
|
136
|
+
check.findings.push({
|
|
137
|
+
type: 'pass',
|
|
138
|
+
severity: 'info',
|
|
139
|
+
item: 'Certificate Validity',
|
|
140
|
+
detail: `SSL certificate valid for ${daysUntilExpiry} days`
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check CDN
|
|
147
|
+
if (results.osint?.categories?.saas?.services?.cdn) {
|
|
148
|
+
const cdn = results.osint.categories.saas.services.cdn;
|
|
149
|
+
check.findings.push({
|
|
150
|
+
type: 'pass',
|
|
151
|
+
severity: 'info',
|
|
152
|
+
item: 'CDN Detected',
|
|
153
|
+
detail: `Using ${cdn.provider}`,
|
|
154
|
+
action: 'CDN provides DDoS protection and performance benefits'
|
|
155
|
+
});
|
|
156
|
+
} else {
|
|
157
|
+
check.findings.push({
|
|
158
|
+
type: 'warning',
|
|
159
|
+
severity: 'low',
|
|
160
|
+
item: 'No CDN Detected',
|
|
161
|
+
detail: 'No CDN detected',
|
|
162
|
+
action: 'Consider Cloudflare, Fastly, or AWS CloudFront for DDoS protection'
|
|
163
|
+
});
|
|
164
|
+
summary.low++;
|
|
165
|
+
check.score -= 5;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
check.status = check.score >= 80 ? 'pass' : (check.score >= 60 ? 'warning' : 'fail');
|
|
169
|
+
if (check.status === 'pass') summary.passed++;
|
|
170
|
+
|
|
171
|
+
return check;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* B) Secrets: run gitleaks + truffleHog on repos; rotate any leaked keys
|
|
176
|
+
*/
|
|
177
|
+
static checkSecrets(results, summary) {
|
|
178
|
+
const check = {
|
|
179
|
+
id: 'B',
|
|
180
|
+
name: 'Secrets',
|
|
181
|
+
description: 'Leaked API keys, credentials, tokens',
|
|
182
|
+
status: 'unknown',
|
|
183
|
+
findings: [],
|
|
184
|
+
score: 100
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
summary.total++;
|
|
188
|
+
|
|
189
|
+
if (results.secrets) {
|
|
190
|
+
const secretsFound = results.secrets.secrets?.length || 0;
|
|
191
|
+
|
|
192
|
+
if (secretsFound > 0) {
|
|
193
|
+
check.findings.push({
|
|
194
|
+
type: 'fail',
|
|
195
|
+
severity: 'critical',
|
|
196
|
+
item: 'Secrets Exposed',
|
|
197
|
+
detail: `${secretsFound} potential secrets found publicly`,
|
|
198
|
+
action: 'IMMEDIATE: Rotate all exposed keys, tokens, credentials. Revoke compromised secrets. Review commit history.'
|
|
199
|
+
});
|
|
200
|
+
summary.critical += secretsFound;
|
|
201
|
+
summary.failed++;
|
|
202
|
+
check.score = 0; // Automatic fail
|
|
203
|
+
check.status = 'fail';
|
|
204
|
+
} else {
|
|
205
|
+
check.findings.push({
|
|
206
|
+
type: 'pass',
|
|
207
|
+
severity: 'info',
|
|
208
|
+
item: 'No Secrets Detected',
|
|
209
|
+
detail: 'No exposed secrets found',
|
|
210
|
+
action: 'Continue monitoring. Implement pre-commit hooks (gitleaks, truffleHog)'
|
|
211
|
+
});
|
|
212
|
+
summary.passed++;
|
|
213
|
+
check.status = 'pass';
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
check.findings.push({
|
|
217
|
+
type: 'info',
|
|
218
|
+
severity: 'info',
|
|
219
|
+
item: 'Secrets Scan Not Run',
|
|
220
|
+
detail: 'Secrets detection stage was not executed',
|
|
221
|
+
action: 'Run scan with secrets detection enabled'
|
|
222
|
+
});
|
|
223
|
+
check.status = 'unknown';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return check;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* C) Dependencies: run Snyk/Dependabot and patch high/critical
|
|
231
|
+
*/
|
|
232
|
+
static checkDependencies(results, summary) {
|
|
233
|
+
const check = {
|
|
234
|
+
id: 'C',
|
|
235
|
+
name: 'Dependencies',
|
|
236
|
+
description: 'Vulnerable packages (requires manual setup)',
|
|
237
|
+
status: 'manual',
|
|
238
|
+
findings: [{
|
|
239
|
+
type: 'info',
|
|
240
|
+
severity: 'info',
|
|
241
|
+
item: 'Manual Check Required',
|
|
242
|
+
detail: 'Dependency scanning requires package.json/requirements.txt access',
|
|
243
|
+
action: 'Run: npm audit, yarn audit, snyk test, or GitHub Dependabot. Patch high/critical vulnerabilities.'
|
|
244
|
+
}],
|
|
245
|
+
score: 0
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
summary.total++;
|
|
249
|
+
|
|
250
|
+
return check;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* D) Headers & TLS: enforce HSTS, CSP, secure cookies, TLS1.2+/modern ciphers
|
|
255
|
+
*/
|
|
256
|
+
static checkSecurityHeaders(results, summary) {
|
|
257
|
+
const check = {
|
|
258
|
+
id: 'D1',
|
|
259
|
+
name: 'Security Headers',
|
|
260
|
+
description: 'HSTS, CSP, X-Frame-Options, etc.',
|
|
261
|
+
status: 'unknown',
|
|
262
|
+
findings: [],
|
|
263
|
+
score: 100
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
summary.total++;
|
|
267
|
+
|
|
268
|
+
if (results.http?.headers) {
|
|
269
|
+
const headers = results.http.headers;
|
|
270
|
+
|
|
271
|
+
// HSTS
|
|
272
|
+
if (!headers['strict-transport-security']) {
|
|
273
|
+
check.findings.push({
|
|
274
|
+
type: 'fail',
|
|
275
|
+
severity: 'high',
|
|
276
|
+
item: 'Missing HSTS',
|
|
277
|
+
detail: 'Strict-Transport-Security header not found',
|
|
278
|
+
action: 'Add header: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload'
|
|
279
|
+
});
|
|
280
|
+
summary.high++;
|
|
281
|
+
check.score -= 20;
|
|
282
|
+
} else {
|
|
283
|
+
check.findings.push({
|
|
284
|
+
type: 'pass',
|
|
285
|
+
severity: 'info',
|
|
286
|
+
item: 'HSTS Enabled',
|
|
287
|
+
detail: headers['strict-transport-security']
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// CSP
|
|
292
|
+
if (!headers['content-security-policy'] && !headers['content-security-policy-report-only']) {
|
|
293
|
+
check.findings.push({
|
|
294
|
+
type: 'fail',
|
|
295
|
+
severity: 'medium',
|
|
296
|
+
item: 'Missing CSP',
|
|
297
|
+
detail: 'Content-Security-Policy header not found',
|
|
298
|
+
action: 'Implement CSP to prevent XSS. Start with report-only mode.'
|
|
299
|
+
});
|
|
300
|
+
summary.medium++;
|
|
301
|
+
check.score -= 15;
|
|
302
|
+
} else {
|
|
303
|
+
check.findings.push({
|
|
304
|
+
type: 'pass',
|
|
305
|
+
severity: 'info',
|
|
306
|
+
item: 'CSP Present',
|
|
307
|
+
detail: 'Content-Security-Policy configured'
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// X-Frame-Options
|
|
312
|
+
if (!headers['x-frame-options']) {
|
|
313
|
+
check.findings.push({
|
|
314
|
+
type: 'fail',
|
|
315
|
+
severity: 'medium',
|
|
316
|
+
item: 'Missing X-Frame-Options',
|
|
317
|
+
detail: 'Clickjacking protection not found',
|
|
318
|
+
action: 'Add header: X-Frame-Options: DENY or SAMEORIGIN'
|
|
319
|
+
});
|
|
320
|
+
summary.medium++;
|
|
321
|
+
check.score -= 10;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// X-Content-Type-Options
|
|
325
|
+
if (!headers['x-content-type-options']) {
|
|
326
|
+
check.findings.push({
|
|
327
|
+
type: 'warning',
|
|
328
|
+
severity: 'low',
|
|
329
|
+
item: 'Missing X-Content-Type-Options',
|
|
330
|
+
detail: 'MIME-sniffing protection not found',
|
|
331
|
+
action: 'Add header: X-Content-Type-Options: nosniff'
|
|
332
|
+
});
|
|
333
|
+
summary.low++;
|
|
334
|
+
check.score -= 5;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
check.status = check.score >= 80 ? 'pass' : (check.score >= 60 ? 'warning' : 'fail');
|
|
339
|
+
if (check.status === 'pass') summary.passed++;
|
|
340
|
+
else if (check.status === 'fail') summary.failed++;
|
|
341
|
+
|
|
342
|
+
return check;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* D) TLS Configuration
|
|
347
|
+
*/
|
|
348
|
+
static checkTLS(results, summary) {
|
|
349
|
+
const check = {
|
|
350
|
+
id: 'D2',
|
|
351
|
+
name: 'TLS Configuration',
|
|
352
|
+
description: 'TLS 1.2+, modern ciphers',
|
|
353
|
+
status: 'unknown',
|
|
354
|
+
findings: [],
|
|
355
|
+
score: 100
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
summary.total++;
|
|
359
|
+
|
|
360
|
+
if (results.tlsAudit) {
|
|
361
|
+
const tlsOk = results.tlsAudit.tools?.openssl?.status === 'ok';
|
|
362
|
+
|
|
363
|
+
if (tlsOk) {
|
|
364
|
+
check.findings.push({
|
|
365
|
+
type: 'pass',
|
|
366
|
+
severity: 'info',
|
|
367
|
+
item: 'TLS Available',
|
|
368
|
+
detail: 'TLS connection successful',
|
|
369
|
+
action: 'Ensure TLS 1.2+ only. Disable TLS 1.0/1.1. Use modern cipher suites (sslyze/testssl for details).'
|
|
370
|
+
});
|
|
371
|
+
summary.passed++;
|
|
372
|
+
check.status = 'pass';
|
|
373
|
+
} else {
|
|
374
|
+
check.findings.push({
|
|
375
|
+
type: 'fail',
|
|
376
|
+
severity: 'critical',
|
|
377
|
+
item: 'TLS Connection Failed',
|
|
378
|
+
detail: 'Unable to establish TLS connection',
|
|
379
|
+
action: 'Verify SSL/TLS configuration and certificate validity'
|
|
380
|
+
});
|
|
381
|
+
summary.critical++;
|
|
382
|
+
summary.failed++;
|
|
383
|
+
check.score = 0;
|
|
384
|
+
check.status = 'fail';
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return check;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* E) WAF & rate-limiting: enable, tune false-positives
|
|
393
|
+
*/
|
|
394
|
+
static checkWAF(results, summary) {
|
|
395
|
+
const check = {
|
|
396
|
+
id: 'E',
|
|
397
|
+
name: 'WAF & Rate Limiting',
|
|
398
|
+
description: 'Web Application Firewall detection',
|
|
399
|
+
status: 'unknown',
|
|
400
|
+
findings: [],
|
|
401
|
+
score: 100
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
summary.total++;
|
|
405
|
+
|
|
406
|
+
// Check for WAF via headers or CDN
|
|
407
|
+
const hasCloudflare = results.osint?.categories?.saas?.services?.cdn?.provider?.includes('Cloudflare');
|
|
408
|
+
const hasWAFHeader = results.http?.headers?.['cf-ray'] || results.http?.headers?.['x-sucuri-id'];
|
|
409
|
+
|
|
410
|
+
if (hasCloudflare || hasWAFHeader) {
|
|
411
|
+
check.findings.push({
|
|
412
|
+
type: 'pass',
|
|
413
|
+
severity: 'info',
|
|
414
|
+
item: 'WAF Detected',
|
|
415
|
+
detail: hasCloudflare ? 'Cloudflare WAF detected' : 'WAF headers detected',
|
|
416
|
+
action: 'WAF is active. Monitor and tune rules regularly. Review false positives.'
|
|
417
|
+
});
|
|
418
|
+
summary.passed++;
|
|
419
|
+
check.status = 'pass';
|
|
420
|
+
} else {
|
|
421
|
+
check.findings.push({
|
|
422
|
+
type: 'warning',
|
|
423
|
+
severity: 'medium',
|
|
424
|
+
item: 'No WAF Detected',
|
|
425
|
+
detail: 'No Web Application Firewall detected',
|
|
426
|
+
action: 'Implement WAF (Cloudflare, AWS WAF, Akamai) for OWASP Top 10 protection. Configure rate limiting.'
|
|
427
|
+
});
|
|
428
|
+
summary.medium++;
|
|
429
|
+
check.score -= 15;
|
|
430
|
+
check.status = 'warning';
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return check;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* F) Auth: check password policies, MFA, session timeouts, brute-force protection
|
|
438
|
+
*/
|
|
439
|
+
static checkAuth(results, summary) {
|
|
440
|
+
const check = {
|
|
441
|
+
id: 'F',
|
|
442
|
+
name: 'Authentication',
|
|
443
|
+
description: 'Password policies, MFA, sessions (manual)',
|
|
444
|
+
status: 'manual',
|
|
445
|
+
findings: [{
|
|
446
|
+
type: 'info',
|
|
447
|
+
severity: 'info',
|
|
448
|
+
item: 'Manual Check Required',
|
|
449
|
+
detail: 'Authentication audit requires authenticated scan',
|
|
450
|
+
action: 'Verify: password policies (min 12 chars, complexity), MFA enforcement, session timeouts (15-30 min), brute-force protection (rate limiting, CAPTCHA)'
|
|
451
|
+
}],
|
|
452
|
+
score: 0
|
|
453
|
+
};
|
|
454
|
+
|
|
455
|
+
summary.total++;
|
|
456
|
+
|
|
457
|
+
return check;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Calculate overall score and grade
|
|
462
|
+
*/
|
|
463
|
+
static calculateScoreAndGrade(audit) {
|
|
464
|
+
let totalScore = 0;
|
|
465
|
+
let scoredChecks = 0;
|
|
466
|
+
|
|
467
|
+
for (const check of audit.checklist) {
|
|
468
|
+
if (check.status !== 'manual' && check.status !== 'unknown') {
|
|
469
|
+
totalScore += check.score;
|
|
470
|
+
scoredChecks++;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
audit.summary.score = scoredChecks > 0 ? Math.round(totalScore / scoredChecks) : 0;
|
|
475
|
+
|
|
476
|
+
// Assign grade
|
|
477
|
+
if (audit.summary.score >= 90) audit.summary.grade = 'A';
|
|
478
|
+
else if (audit.summary.score >= 80) audit.summary.grade = 'B';
|
|
479
|
+
else if (audit.summary.score >= 70) audit.summary.grade = 'C';
|
|
480
|
+
else if (audit.summary.score >= 60) audit.summary.grade = 'D';
|
|
481
|
+
else audit.summary.grade = 'F';
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Generate prioritized recommendations
|
|
486
|
+
*/
|
|
487
|
+
static generateRecommendations(audit) {
|
|
488
|
+
const recommendations = [];
|
|
489
|
+
|
|
490
|
+
for (const check of audit.checklist) {
|
|
491
|
+
for (const finding of check.findings) {
|
|
492
|
+
if (finding.type === 'fail' && finding.action) {
|
|
493
|
+
recommendations.push({
|
|
494
|
+
priority: finding.severity === 'critical' ? 1 : (finding.severity === 'high' ? 2 : 3),
|
|
495
|
+
severity: finding.severity,
|
|
496
|
+
category: check.name,
|
|
497
|
+
item: finding.item,
|
|
498
|
+
action: finding.action
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Sort by priority
|
|
505
|
+
recommendations.sort((a, b) => a.priority - b.priority);
|
|
506
|
+
|
|
507
|
+
audit.recommendations = recommendations;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Helper: Calculate days until SSL expiry
|
|
512
|
+
*/
|
|
513
|
+
static calculateDaysUntilExpiry(validToDate) {
|
|
514
|
+
if (!validToDate) return null;
|
|
515
|
+
|
|
516
|
+
const expiry = new Date(validToDate);
|
|
517
|
+
const now = new Date();
|
|
518
|
+
const diffTime = expiry - now;
|
|
519
|
+
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
|
|
520
|
+
|
|
521
|
+
return diffDays;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Generate markdown report
|
|
526
|
+
*/
|
|
527
|
+
static generateMarkdownReport(audit) {
|
|
528
|
+
let md = `# Security Audit Report\n\n`;
|
|
529
|
+
md += `**Target:** ${audit.target}\n`;
|
|
530
|
+
md += `**Date:** ${new Date(audit.timestamp).toLocaleString()}\n`;
|
|
531
|
+
md += `**Score:** ${audit.summary.score}/100 (Grade: ${audit.summary.grade})\n\n`;
|
|
532
|
+
|
|
533
|
+
md += `## Summary\n\n`;
|
|
534
|
+
md += `- Total Checks: ${audit.summary.total}\n`;
|
|
535
|
+
md += `- Passed: ${audit.summary.passed}\n`;
|
|
536
|
+
md += `- Failed: ${audit.summary.failed}\n`;
|
|
537
|
+
md += `- Critical: ${audit.summary.critical}\n`;
|
|
538
|
+
md += `- High: ${audit.summary.high}\n`;
|
|
539
|
+
md += `- Medium: ${audit.summary.medium}\n`;
|
|
540
|
+
md += `- Low: ${audit.summary.low}\n\n`;
|
|
541
|
+
|
|
542
|
+
md += `## Checklist\n\n`;
|
|
543
|
+
|
|
544
|
+
for (const check of audit.checklist) {
|
|
545
|
+
const statusIcon = check.status === 'pass' ? '✅' : (check.status === 'fail' ? '❌' : '⚠️');
|
|
546
|
+
md += `### ${statusIcon} ${check.id}) ${check.name}\n`;
|
|
547
|
+
md += `*${check.description}*\n\n`;
|
|
548
|
+
|
|
549
|
+
for (const finding of check.findings) {
|
|
550
|
+
const icon = finding.type === 'pass' ? '✓' : (finding.type === 'fail' ? '✗' : 'ℹ');
|
|
551
|
+
md += `- ${icon} **${finding.item}**: ${finding.detail}\n`;
|
|
552
|
+
if (finding.action) {
|
|
553
|
+
md += ` - Action: ${finding.action}\n`;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
md += `\n`;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (audit.recommendations.length > 0) {
|
|
561
|
+
md += `## Priority Recommendations\n\n`;
|
|
562
|
+
|
|
563
|
+
for (const rec of audit.recommendations) {
|
|
564
|
+
md += `${rec.priority}. **[${rec.severity.toUpperCase()}]** ${rec.item}\n`;
|
|
565
|
+
md += ` - ${rec.action}\n\n`;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return md;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TargetNormalizer
|
|
3
|
+
*
|
|
4
|
+
* Normalizes target URLs/domains into structured format:
|
|
5
|
+
* - Parses URLs
|
|
6
|
+
* - Extracts host, protocol, port
|
|
7
|
+
* - Handles edge cases
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export class TargetNormalizer {
|
|
11
|
+
/**
|
|
12
|
+
* Normalize target input to structured format
|
|
13
|
+
*/
|
|
14
|
+
static normalize(target) {
|
|
15
|
+
if (!target || typeof target !== 'string') {
|
|
16
|
+
throw new Error('Target must be a non-empty string');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let url;
|
|
20
|
+
try {
|
|
21
|
+
url = new URL(target.includes('://') ? target : `https://${target}`);
|
|
22
|
+
} catch (error) {
|
|
23
|
+
url = new URL(`https://${target}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const protocol = url.protocol ? url.protocol.replace(':', '') : null;
|
|
27
|
+
const host = url.hostname || target;
|
|
28
|
+
const port = url.port ? Number(url.port) : this.defaultPortForProtocol(protocol);
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
original: target,
|
|
32
|
+
host,
|
|
33
|
+
protocol,
|
|
34
|
+
port,
|
|
35
|
+
path: url.pathname === '/' ? null : url.pathname
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get default port for protocol
|
|
41
|
+
*/
|
|
42
|
+
static defaultPortForProtocol(protocol) {
|
|
43
|
+
switch (protocol) {
|
|
44
|
+
case 'http':
|
|
45
|
+
return 80;
|
|
46
|
+
case 'https':
|
|
47
|
+
return 443;
|
|
48
|
+
case 'ftp':
|
|
49
|
+
return 21;
|
|
50
|
+
case 'ssh':
|
|
51
|
+
return 22;
|
|
52
|
+
default:
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Build full URL from normalized target
|
|
59
|
+
*/
|
|
60
|
+
static buildUrl(target) {
|
|
61
|
+
const protocol = target.protocol || 'https';
|
|
62
|
+
const port = target.port && target.port !== this.defaultPortForProtocol(protocol)
|
|
63
|
+
? `:${target.port}`
|
|
64
|
+
: '';
|
|
65
|
+
const path = target.path || '';
|
|
66
|
+
return `${protocol}://${target.host}${port}${path}`;
|
|
67
|
+
}
|
|
68
|
+
}
|