tlc-claude-code 1.4.7 → 1.4.9
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/docker-compose.dev.yml +6 -3
- package/package.json +1 -1
- package/server/index.js +229 -14
- package/server/lib/compliance/control-mapper.js +401 -0
- package/server/lib/compliance/control-mapper.test.js +117 -0
- package/server/lib/compliance/evidence-linker.js +296 -0
- package/server/lib/compliance/evidence-linker.test.js +121 -0
- package/server/lib/compliance/gdpr-checklist.js +416 -0
- package/server/lib/compliance/gdpr-checklist.test.js +131 -0
- package/server/lib/compliance/hipaa-checklist.js +277 -0
- package/server/lib/compliance/hipaa-checklist.test.js +101 -0
- package/server/lib/compliance/iso27001-checklist.js +287 -0
- package/server/lib/compliance/iso27001-checklist.test.js +99 -0
- package/server/lib/compliance/multi-framework-reporter.js +284 -0
- package/server/lib/compliance/multi-framework-reporter.test.js +127 -0
- package/server/lib/compliance/pci-dss-checklist.js +214 -0
- package/server/lib/compliance/pci-dss-checklist.test.js +95 -0
- package/server/lib/compliance/trust-centre.js +187 -0
- package/server/lib/compliance/trust-centre.test.js +93 -0
- package/server/lib/dashboard/api-server.js +155 -0
- package/server/lib/dashboard/api-server.test.js +155 -0
- package/server/lib/dashboard/health-api.js +199 -0
- package/server/lib/dashboard/health-api.test.js +122 -0
- package/server/lib/dashboard/notes-api.js +234 -0
- package/server/lib/dashboard/notes-api.test.js +134 -0
- package/server/lib/dashboard/router-api.js +176 -0
- package/server/lib/dashboard/router-api.test.js +132 -0
- package/server/lib/dashboard/tasks-api.js +289 -0
- package/server/lib/dashboard/tasks-api.test.js +161 -0
- package/server/lib/dashboard/tlc-introspection.js +197 -0
- package/server/lib/dashboard/tlc-introspection.test.js +138 -0
- package/server/lib/dashboard/version-api.js +222 -0
- package/server/lib/dashboard/version-api.test.js +112 -0
- package/server/lib/dashboard/websocket-server.js +104 -0
- package/server/lib/dashboard/websocket-server.test.js +118 -0
- package/server/lib/deploy/branch-classifier.js +163 -0
- package/server/lib/deploy/branch-classifier.test.js +164 -0
- package/server/lib/deploy/deployment-approval.js +299 -0
- package/server/lib/deploy/deployment-approval.test.js +296 -0
- package/server/lib/deploy/deployment-audit.js +374 -0
- package/server/lib/deploy/deployment-audit.test.js +307 -0
- package/server/lib/deploy/deployment-executor.js +335 -0
- package/server/lib/deploy/deployment-executor.test.js +329 -0
- package/server/lib/deploy/deployment-rules.js +163 -0
- package/server/lib/deploy/deployment-rules.test.js +188 -0
- package/server/lib/deploy/rollback-manager.js +379 -0
- package/server/lib/deploy/rollback-manager.test.js +321 -0
- package/server/lib/deploy/security-gates.js +236 -0
- package/server/lib/deploy/security-gates.test.js +222 -0
- package/server/lib/k8s/gitops-config.js +188 -0
- package/server/lib/k8s/gitops-config.test.js +59 -0
- package/server/lib/k8s/helm-generator.js +196 -0
- package/server/lib/k8s/helm-generator.test.js +59 -0
- package/server/lib/k8s/kustomize-generator.js +176 -0
- package/server/lib/k8s/kustomize-generator.test.js +58 -0
- package/server/lib/k8s/network-policy.js +114 -0
- package/server/lib/k8s/network-policy.test.js +53 -0
- package/server/lib/k8s/pod-security.js +114 -0
- package/server/lib/k8s/pod-security.test.js +55 -0
- package/server/lib/k8s/rbac-generator.js +132 -0
- package/server/lib/k8s/rbac-generator.test.js +57 -0
- package/server/lib/k8s/resource-manager.js +172 -0
- package/server/lib/k8s/resource-manager.test.js +60 -0
- package/server/lib/k8s/secrets-encryption.js +168 -0
- package/server/lib/k8s/secrets-encryption.test.js +49 -0
- package/server/lib/monitoring/alert-manager.js +238 -0
- package/server/lib/monitoring/alert-manager.test.js +106 -0
- package/server/lib/monitoring/health-check.js +226 -0
- package/server/lib/monitoring/health-check.test.js +176 -0
- package/server/lib/monitoring/incident-manager.js +230 -0
- package/server/lib/monitoring/incident-manager.test.js +98 -0
- package/server/lib/monitoring/log-aggregator.js +147 -0
- package/server/lib/monitoring/log-aggregator.test.js +89 -0
- package/server/lib/monitoring/metrics-collector.js +337 -0
- package/server/lib/monitoring/metrics-collector.test.js +172 -0
- package/server/lib/monitoring/status-page.js +214 -0
- package/server/lib/monitoring/status-page.test.js +105 -0
- package/server/lib/monitoring/uptime-monitor.js +194 -0
- package/server/lib/monitoring/uptime-monitor.test.js +109 -0
- package/server/lib/network/fail2ban-config.js +294 -0
- package/server/lib/network/fail2ban-config.test.js +275 -0
- package/server/lib/network/firewall-manager.js +252 -0
- package/server/lib/network/firewall-manager.test.js +254 -0
- package/server/lib/network/geoip-filter.js +282 -0
- package/server/lib/network/geoip-filter.test.js +264 -0
- package/server/lib/network/rate-limiter.js +229 -0
- package/server/lib/network/rate-limiter.test.js +293 -0
- package/server/lib/network/request-validator.js +351 -0
- package/server/lib/network/request-validator.test.js +345 -0
- package/server/lib/network/security-headers.js +251 -0
- package/server/lib/network/security-headers.test.js +283 -0
- package/server/lib/network/tls-config.js +210 -0
- package/server/lib/network/tls-config.test.js +248 -0
- package/server/lib/security/auth-security.js +369 -0
- package/server/lib/security/auth-security.test.js +448 -0
- package/server/lib/security/cis-benchmark.js +152 -0
- package/server/lib/security/cis-benchmark.test.js +137 -0
- package/server/lib/security/compose-templates.js +312 -0
- package/server/lib/security/compose-templates.test.js +229 -0
- package/server/lib/security/container-runtime.js +456 -0
- package/server/lib/security/container-runtime.test.js +503 -0
- package/server/lib/security/cors-validator.js +278 -0
- package/server/lib/security/cors-validator.test.js +310 -0
- package/server/lib/security/crypto-utils.js +253 -0
- package/server/lib/security/crypto-utils.test.js +409 -0
- package/server/lib/security/dockerfile-linter.js +459 -0
- package/server/lib/security/dockerfile-linter.test.js +483 -0
- package/server/lib/security/dockerfile-templates.js +278 -0
- package/server/lib/security/dockerfile-templates.test.js +164 -0
- package/server/lib/security/error-sanitizer.js +426 -0
- package/server/lib/security/error-sanitizer.test.js +331 -0
- package/server/lib/security/headers-generator.js +368 -0
- package/server/lib/security/headers-generator.test.js +398 -0
- package/server/lib/security/image-scanner.js +83 -0
- package/server/lib/security/image-scanner.test.js +106 -0
- package/server/lib/security/input-validator.js +352 -0
- package/server/lib/security/input-validator.test.js +330 -0
- package/server/lib/security/network-policy.js +174 -0
- package/server/lib/security/network-policy.test.js +164 -0
- package/server/lib/security/output-encoder.js +237 -0
- package/server/lib/security/output-encoder.test.js +276 -0
- package/server/lib/security/path-validator.js +359 -0
- package/server/lib/security/path-validator.test.js +293 -0
- package/server/lib/security/query-builder.js +421 -0
- package/server/lib/security/query-builder.test.js +318 -0
- package/server/lib/security/secret-detector.js +290 -0
- package/server/lib/security/secret-detector.test.js +354 -0
- package/server/lib/security/secrets-validator.js +137 -0
- package/server/lib/security/secrets-validator.test.js +120 -0
- package/server/lib/security-testing/dast-runner.js +154 -0
- package/server/lib/security-testing/dast-runner.test.js +62 -0
- package/server/lib/security-testing/dependency-scanner.js +172 -0
- package/server/lib/security-testing/dependency-scanner.test.js +64 -0
- package/server/lib/security-testing/pentest-runner.js +230 -0
- package/server/lib/security-testing/pentest-runner.test.js +60 -0
- package/server/lib/security-testing/sast-runner.js +136 -0
- package/server/lib/security-testing/sast-runner.test.js +62 -0
- package/server/lib/security-testing/secret-scanner.js +153 -0
- package/server/lib/security-testing/secret-scanner.test.js +66 -0
- package/server/lib/security-testing/security-gate.js +216 -0
- package/server/lib/security-testing/security-gate.test.js +115 -0
- package/server/lib/security-testing/security-reporter.js +303 -0
- package/server/lib/security-testing/security-reporter.test.js +114 -0
- package/server/lib/standards/audit-checker.js +546 -0
- package/server/lib/standards/audit-checker.test.js +415 -0
- package/server/lib/standards/cleanup-executor.js +452 -0
- package/server/lib/standards/cleanup-executor.test.js +293 -0
- package/server/lib/standards/refactor-stepper.js +425 -0
- package/server/lib/standards/refactor-stepper.test.js +298 -0
- package/server/lib/standards/standards-injector.js +167 -0
- package/server/lib/standards/standards-injector.test.js +232 -0
- package/server/lib/user-management.test.js +284 -0
- package/server/lib/vps/backup-manager.js +157 -0
- package/server/lib/vps/backup-manager.test.js +59 -0
- package/server/lib/vps/caddy-config.js +159 -0
- package/server/lib/vps/caddy-config.test.js +48 -0
- package/server/lib/vps/compose-orchestrator.js +219 -0
- package/server/lib/vps/compose-orchestrator.test.js +50 -0
- package/server/lib/vps/database-config.js +208 -0
- package/server/lib/vps/database-config.test.js +47 -0
- package/server/lib/vps/deploy-script.js +211 -0
- package/server/lib/vps/deploy-script.test.js +53 -0
- package/server/lib/vps/secrets-manager.js +148 -0
- package/server/lib/vps/secrets-manager.test.js +58 -0
- package/server/lib/vps/server-hardening.js +174 -0
- package/server/lib/vps/server-hardening.test.js +70 -0
- package/server/package-lock.json +19 -0
- package/server/package.json +1 -0
- package/server/templates/CLAUDE.md +37 -0
- package/server/templates/CODING-STANDARDS.md +408 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DAST Runner - OWASP ZAP Dynamic Application Security Testing
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Run ZAP baseline scan
|
|
7
|
+
* @param {Object} options - Scan options
|
|
8
|
+
* @param {string} options.target - Target URL to scan
|
|
9
|
+
* @param {Function} options.exec - Exec function for running commands
|
|
10
|
+
* @returns {Promise<Object>} Scan results
|
|
11
|
+
*/
|
|
12
|
+
export async function runZapBaseline({ target, exec }) {
|
|
13
|
+
const command = `zap-baseline.py -t ${target} -J report.json`;
|
|
14
|
+
const { stdout } = await exec(command);
|
|
15
|
+
return JSON.parse(stdout);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Run ZAP full scan
|
|
20
|
+
* @param {Object} options - Scan options
|
|
21
|
+
* @param {string} options.target - Target URL to scan
|
|
22
|
+
* @param {Function} options.exec - Exec function for running commands
|
|
23
|
+
* @returns {Promise<Object>} Scan results
|
|
24
|
+
*/
|
|
25
|
+
export async function runZapFullScan({ target, exec }) {
|
|
26
|
+
const command = `zap-full-scan.py -t ${target} -J report.json`;
|
|
27
|
+
const { stdout } = await exec(command);
|
|
28
|
+
return JSON.parse(stdout);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parse ZAP JSON report into normalized alerts
|
|
33
|
+
* @param {Object} report - Raw ZAP report
|
|
34
|
+
* @returns {Array} Normalized alerts
|
|
35
|
+
*/
|
|
36
|
+
export function parseZapReport(report) {
|
|
37
|
+
const alerts = [];
|
|
38
|
+
const riskMap = { '0': 'info', '1': 'low', '2': 'medium', '3': 'high' };
|
|
39
|
+
|
|
40
|
+
for (const site of report.site || []) {
|
|
41
|
+
for (const alert of site.alerts || []) {
|
|
42
|
+
alerts.push({
|
|
43
|
+
name: alert.alert,
|
|
44
|
+
risk: riskMap[alert.riskcode] || 'info',
|
|
45
|
+
description: alert.desc || alert.description || '',
|
|
46
|
+
instances: alert.instances || []
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return alerts;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Configure scan policy
|
|
56
|
+
* @param {Object} options - Policy options
|
|
57
|
+
* @param {string} options.strength - Scan strength (low, medium, high)
|
|
58
|
+
* @param {string} options.threshold - Alert threshold
|
|
59
|
+
* @returns {Object} Configured policy
|
|
60
|
+
*/
|
|
61
|
+
export function configureScanPolicy(options = {}) {
|
|
62
|
+
return {
|
|
63
|
+
strength: options.strength || 'medium',
|
|
64
|
+
threshold: options.threshold || 'medium',
|
|
65
|
+
maxDuration: options.maxDuration || 60,
|
|
66
|
+
maxDepth: options.maxDepth || 5
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Generate HTML report from alerts
|
|
72
|
+
* @param {Array} alerts - List of alerts
|
|
73
|
+
* @returns {string} HTML report
|
|
74
|
+
*/
|
|
75
|
+
export function generateHtmlReport(alerts) {
|
|
76
|
+
let html = `<!DOCTYPE html>
|
|
77
|
+
<html>
|
|
78
|
+
<head>
|
|
79
|
+
<title>DAST Scan Report</title>
|
|
80
|
+
<style>
|
|
81
|
+
body { font-family: Arial, sans-serif; margin: 20px; }
|
|
82
|
+
.alert { margin: 10px 0; padding: 10px; border: 1px solid #ccc; }
|
|
83
|
+
.high { border-color: red; background: #ffe0e0; }
|
|
84
|
+
.medium { border-color: orange; background: #fff3e0; }
|
|
85
|
+
.low { border-color: yellow; background: #fffde0; }
|
|
86
|
+
.info { border-color: blue; background: #e0f0ff; }
|
|
87
|
+
</style>
|
|
88
|
+
</head>
|
|
89
|
+
<body>
|
|
90
|
+
<h1>DAST Scan Report</h1>
|
|
91
|
+
<p>Found ${alerts.length} alert(s)</p>
|
|
92
|
+
`;
|
|
93
|
+
|
|
94
|
+
for (const alert of alerts) {
|
|
95
|
+
html += ` <div class="alert ${alert.risk}">
|
|
96
|
+
<h3>${alert.name}</h3>
|
|
97
|
+
<p><strong>Risk:</strong> ${alert.risk}</p>
|
|
98
|
+
<p>${alert.description}</p>
|
|
99
|
+
</div>
|
|
100
|
+
`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
html += `</body>
|
|
104
|
+
</html>`;
|
|
105
|
+
|
|
106
|
+
return html;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Create a DAST runner instance
|
|
111
|
+
* @param {Object} options - Runner options
|
|
112
|
+
* @returns {Object} DAST runner instance
|
|
113
|
+
*/
|
|
114
|
+
export function createDastRunner(options = {}) {
|
|
115
|
+
return {
|
|
116
|
+
/**
|
|
117
|
+
* Run baseline scan
|
|
118
|
+
* @param {Object} scanOptions - Scan options
|
|
119
|
+
* @returns {Promise<Object>} Scan results
|
|
120
|
+
*/
|
|
121
|
+
async baseline(scanOptions) {
|
|
122
|
+
const { target, auth, mockResults, exec } = scanOptions;
|
|
123
|
+
|
|
124
|
+
if (mockResults !== undefined) {
|
|
125
|
+
return mockResults;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (exec) {
|
|
129
|
+
return await runZapBaseline({ target, exec });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return { alerts: [] };
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Run full scan
|
|
137
|
+
* @param {Object} scanOptions - Scan options
|
|
138
|
+
* @returns {Promise<Object>} Scan results
|
|
139
|
+
*/
|
|
140
|
+
async fullScan(scanOptions) {
|
|
141
|
+
const { target, auth, mockResults, exec } = scanOptions;
|
|
142
|
+
|
|
143
|
+
if (mockResults !== undefined) {
|
|
144
|
+
return mockResults;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (exec) {
|
|
148
|
+
return await runZapFullScan({ target, exec });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return { alerts: [] };
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DAST Runner Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
5
|
+
import { runZapBaseline, runZapFullScan, parseZapReport, configureScanPolicy, generateHtmlReport, createDastRunner } from './dast-runner.js';
|
|
6
|
+
|
|
7
|
+
describe('dast-runner', () => {
|
|
8
|
+
describe('runZapBaseline', () => {
|
|
9
|
+
it('runs ZAP baseline scan', async () => {
|
|
10
|
+
const mockExec = vi.fn().mockResolvedValue({ stdout: '{"alerts": []}' });
|
|
11
|
+
const result = await runZapBaseline({ target: 'http://localhost:3000', exec: mockExec });
|
|
12
|
+
expect(result.alerts).toBeDefined();
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('runZapFullScan', () => {
|
|
17
|
+
it('runs ZAP full scan', async () => {
|
|
18
|
+
const mockExec = vi.fn().mockResolvedValue({ stdout: '{"alerts": []}' });
|
|
19
|
+
const result = await runZapFullScan({ target: 'http://localhost:3000', exec: mockExec });
|
|
20
|
+
expect(result).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('parseZapReport', () => {
|
|
25
|
+
it('parses ZAP JSON report', () => {
|
|
26
|
+
const report = { site: [{ alerts: [{ alert: 'XSS', riskcode: '3', instances: [{ uri: '/test' }] }] }] };
|
|
27
|
+
const parsed = parseZapReport(report);
|
|
28
|
+
expect(parsed[0].name).toBe('XSS');
|
|
29
|
+
expect(parsed[0].risk).toBe('high');
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('configureScanPolicy', () => {
|
|
34
|
+
it('configures scan policy', () => {
|
|
35
|
+
const policy = configureScanPolicy({ strength: 'high', threshold: 'medium' });
|
|
36
|
+
expect(policy.strength).toBe('high');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('generateHtmlReport', () => {
|
|
41
|
+
it('generates HTML report', () => {
|
|
42
|
+
const alerts = [{ name: 'XSS', risk: 'high', description: 'Cross-site scripting' }];
|
|
43
|
+
const html = generateHtmlReport(alerts);
|
|
44
|
+
expect(html).toContain('<html');
|
|
45
|
+
expect(html).toContain('XSS');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
describe('createDastRunner', () => {
|
|
50
|
+
it('creates runner', () => {
|
|
51
|
+
const runner = createDastRunner();
|
|
52
|
+
expect(runner.baseline).toBeDefined();
|
|
53
|
+
expect(runner.fullScan).toBeDefined();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('supports authenticated scanning', async () => {
|
|
57
|
+
const runner = createDastRunner();
|
|
58
|
+
const result = await runner.baseline({ target: 'http://localhost', auth: { username: 'test', password: 'test' }, mockResults: { alerts: [] } });
|
|
59
|
+
expect(result).toBeDefined();
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Scanner - npm audit and Trivy scanning
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Run npm audit
|
|
7
|
+
* @param {Object} options - Scan options
|
|
8
|
+
* @param {Function} options.exec - Exec function for running commands
|
|
9
|
+
* @returns {Promise<Object>} Audit results
|
|
10
|
+
*/
|
|
11
|
+
export async function runNpmAudit({ exec }) {
|
|
12
|
+
const command = 'npm audit --json';
|
|
13
|
+
const { stdout } = await exec(command);
|
|
14
|
+
return JSON.parse(stdout);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Run Trivy scan
|
|
19
|
+
* @param {Object} options - Scan options
|
|
20
|
+
* @param {string} options.path - Path to scan
|
|
21
|
+
* @param {Function} options.exec - Exec function for running commands
|
|
22
|
+
* @returns {Promise<Object>} Scan results
|
|
23
|
+
*/
|
|
24
|
+
export async function runTrivyScan({ path, exec }) {
|
|
25
|
+
const command = `trivy fs --format json ${path}`;
|
|
26
|
+
const { stdout } = await exec(command);
|
|
27
|
+
return JSON.parse(stdout);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Parse vulnerability results into normalized format
|
|
32
|
+
* @param {Object} results - Raw audit results
|
|
33
|
+
* @returns {Array} Normalized vulnerabilities
|
|
34
|
+
*/
|
|
35
|
+
export function parseVulnerabilities(results) {
|
|
36
|
+
const vulnerabilities = [];
|
|
37
|
+
|
|
38
|
+
if (results.vulnerabilities) {
|
|
39
|
+
for (const [pkg, vuln] of Object.entries(results.vulnerabilities)) {
|
|
40
|
+
const title = vuln.via?.[0]?.title || vuln.via?.[0] || 'Unknown vulnerability';
|
|
41
|
+
vulnerabilities.push({
|
|
42
|
+
package: pkg,
|
|
43
|
+
severity: vuln.severity,
|
|
44
|
+
title: typeof title === 'string' ? title : title.title || 'Unknown',
|
|
45
|
+
fixAvailable: vuln.fixAvailable || false
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return vulnerabilities;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check license compliance
|
|
55
|
+
* @param {Array} deps - List of dependencies with licenses
|
|
56
|
+
* @param {Object} options - License policy options
|
|
57
|
+
* @param {Array} options.allowed - List of allowed licenses
|
|
58
|
+
* @returns {Object} Compliance result
|
|
59
|
+
*/
|
|
60
|
+
export function checkLicenses(deps, options = {}) {
|
|
61
|
+
const { allowed = [] } = options;
|
|
62
|
+
const violations = [];
|
|
63
|
+
|
|
64
|
+
for (const dep of deps) {
|
|
65
|
+
if (!allowed.includes(dep.license)) {
|
|
66
|
+
violations.push({
|
|
67
|
+
package: dep.name,
|
|
68
|
+
license: dep.license
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
compliant: violations.length === 0,
|
|
75
|
+
violations
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Generate Software Bill of Materials (SBOM)
|
|
81
|
+
* @param {Array} deps - List of dependencies
|
|
82
|
+
* @param {Object} options - SBOM options
|
|
83
|
+
* @param {string} options.format - Output format (cyclonedx, spdx)
|
|
84
|
+
* @returns {string} SBOM in specified format
|
|
85
|
+
*/
|
|
86
|
+
export function generateSbom(deps, options = {}) {
|
|
87
|
+
const { format = 'cyclonedx' } = options;
|
|
88
|
+
|
|
89
|
+
if (format === 'cyclonedx') {
|
|
90
|
+
const sbom = {
|
|
91
|
+
bomFormat: 'CycloneDX',
|
|
92
|
+
specVersion: '1.4',
|
|
93
|
+
version: 1,
|
|
94
|
+
components: deps.map(dep => ({
|
|
95
|
+
type: 'library',
|
|
96
|
+
name: dep.name,
|
|
97
|
+
version: dep.version
|
|
98
|
+
}))
|
|
99
|
+
};
|
|
100
|
+
return JSON.stringify(sbom, null, 2);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// SPDX format
|
|
104
|
+
const sbom = {
|
|
105
|
+
spdxVersion: 'SPDX-2.3',
|
|
106
|
+
packages: deps.map(dep => ({
|
|
107
|
+
name: dep.name,
|
|
108
|
+
version: dep.version
|
|
109
|
+
}))
|
|
110
|
+
};
|
|
111
|
+
return JSON.stringify(sbom, null, 2);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Create a dependency scanner instance
|
|
116
|
+
* @param {Object} options - Scanner options
|
|
117
|
+
* @returns {Object} Dependency scanner instance
|
|
118
|
+
*/
|
|
119
|
+
export function createDependencyScanner(options = {}) {
|
|
120
|
+
return {
|
|
121
|
+
/**
|
|
122
|
+
* Run dependency scan
|
|
123
|
+
* @param {Object} scanOptions - Scan options
|
|
124
|
+
* @returns {Promise<Array>} Vulnerability results
|
|
125
|
+
*/
|
|
126
|
+
async scan(scanOptions) {
|
|
127
|
+
const { severity, mockResults, exec } = scanOptions;
|
|
128
|
+
|
|
129
|
+
let results;
|
|
130
|
+
if (mockResults !== undefined) {
|
|
131
|
+
results = mockResults;
|
|
132
|
+
} else if (exec) {
|
|
133
|
+
const auditResults = await runNpmAudit({ exec });
|
|
134
|
+
results = parseVulnerabilities(auditResults);
|
|
135
|
+
} else {
|
|
136
|
+
results = [];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Filter by severity if specified
|
|
140
|
+
if (severity) {
|
|
141
|
+
const severityOrder = ['low', 'moderate', 'high', 'critical'];
|
|
142
|
+
const minIndex = severityOrder.indexOf(severity.toLowerCase());
|
|
143
|
+
results = results.filter(r => {
|
|
144
|
+
const rIndex = severityOrder.indexOf(r.severity?.toLowerCase());
|
|
145
|
+
return rIndex >= minIndex;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return results;
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Check license compliance
|
|
154
|
+
* @param {Array} deps - Dependencies to check
|
|
155
|
+
* @param {Object} policy - License policy
|
|
156
|
+
* @returns {Object} Compliance result
|
|
157
|
+
*/
|
|
158
|
+
checkLicenses(deps, policy) {
|
|
159
|
+
return checkLicenses(deps, policy);
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Generate SBOM
|
|
164
|
+
* @param {Array} deps - Dependencies
|
|
165
|
+
* @param {Object} sbomOptions - SBOM options
|
|
166
|
+
* @returns {string} SBOM output
|
|
167
|
+
*/
|
|
168
|
+
generateSbom(deps, sbomOptions) {
|
|
169
|
+
return generateSbom(deps, sbomOptions);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Scanner Tests
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
5
|
+
import { runNpmAudit, runTrivyScan, parseVulnerabilities, checkLicenses, generateSbom, createDependencyScanner } from './dependency-scanner.js';
|
|
6
|
+
|
|
7
|
+
describe('dependency-scanner', () => {
|
|
8
|
+
describe('runNpmAudit', () => {
|
|
9
|
+
it('runs npm audit', async () => {
|
|
10
|
+
const mockExec = vi.fn().mockResolvedValue({ stdout: '{"vulnerabilities": {}}' });
|
|
11
|
+
const result = await runNpmAudit({ exec: mockExec });
|
|
12
|
+
expect(result.vulnerabilities).toBeDefined();
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('runTrivyScan', () => {
|
|
17
|
+
it('runs Trivy scan', async () => {
|
|
18
|
+
const mockExec = vi.fn().mockResolvedValue({ stdout: '{"Results": []}' });
|
|
19
|
+
const result = await runTrivyScan({ path: '.', exec: mockExec });
|
|
20
|
+
expect(result.Results).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('parseVulnerabilities', () => {
|
|
25
|
+
it('parses vulnerability results', () => {
|
|
26
|
+
const results = { vulnerabilities: { lodash: { severity: 'high', via: [{ title: 'Prototype Pollution' }] } } };
|
|
27
|
+
const parsed = parseVulnerabilities(results);
|
|
28
|
+
expect(parsed[0].package).toBe('lodash');
|
|
29
|
+
expect(parsed[0].severity).toBe('high');
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('checkLicenses', () => {
|
|
34
|
+
it('checks license compliance', () => {
|
|
35
|
+
const deps = [{ name: 'pkg1', license: 'MIT' }, { name: 'pkg2', license: 'GPL-3.0' }];
|
|
36
|
+
const result = checkLicenses(deps, { allowed: ['MIT', 'Apache-2.0'] });
|
|
37
|
+
expect(result.compliant).toBe(false);
|
|
38
|
+
expect(result.violations[0].package).toBe('pkg2');
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('generateSbom', () => {
|
|
43
|
+
it('generates SBOM', () => {
|
|
44
|
+
const deps = [{ name: 'lodash', version: '4.17.21' }];
|
|
45
|
+
const sbom = generateSbom(deps, { format: 'cyclonedx' });
|
|
46
|
+
expect(sbom).toContain('bomFormat');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('createDependencyScanner', () => {
|
|
51
|
+
it('creates scanner', () => {
|
|
52
|
+
const scanner = createDependencyScanner();
|
|
53
|
+
expect(scanner.scan).toBeDefined();
|
|
54
|
+
expect(scanner.checkLicenses).toBeDefined();
|
|
55
|
+
expect(scanner.generateSbom).toBeDefined();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('filters by severity threshold', async () => {
|
|
59
|
+
const scanner = createDependencyScanner();
|
|
60
|
+
const results = await scanner.scan({ severity: 'high', mockResults: [{ severity: 'high' }, { severity: 'low' }] });
|
|
61
|
+
expect(results.every(r => r.severity === 'high')).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pentest Runner - Nuclei penetration testing
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Common SQL injection payloads
|
|
6
|
+
const SQL_PAYLOADS = [
|
|
7
|
+
"' OR '1'='1",
|
|
8
|
+
"1; DROP TABLE users--",
|
|
9
|
+
"' UNION SELECT * FROM users--",
|
|
10
|
+
"1' AND '1'='1"
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
// Common XSS payloads
|
|
14
|
+
const XSS_PAYLOADS = [
|
|
15
|
+
'<script>alert(1)</script>',
|
|
16
|
+
'<img src=x onerror=alert(1)>',
|
|
17
|
+
'"><script>alert(1)</script>',
|
|
18
|
+
"javascript:alert(1)"
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Run Nuclei scan
|
|
23
|
+
* @param {Object} options - Scan options
|
|
24
|
+
* @param {string} options.target - Target URL
|
|
25
|
+
* @param {Array} options.templates - Template categories to use
|
|
26
|
+
* @param {Function} options.exec - Exec function for running commands
|
|
27
|
+
* @returns {Promise<Array>} Scan findings
|
|
28
|
+
*/
|
|
29
|
+
export async function runNuclei({ target, templates = [], exec }) {
|
|
30
|
+
let command = `nuclei -u ${target} -json`;
|
|
31
|
+
|
|
32
|
+
if (templates.length > 0) {
|
|
33
|
+
command += ` -t ${templates.join(',')}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const { stdout } = await exec(command);
|
|
37
|
+
return JSON.parse(stdout);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Test for SQL injection vulnerabilities
|
|
42
|
+
* @param {Object} options - Test options
|
|
43
|
+
* @param {string} options.url - URL to test
|
|
44
|
+
* @param {Function} options.mockFetch - Mock fetch function for testing
|
|
45
|
+
* @returns {Promise<Object>} Test result
|
|
46
|
+
*/
|
|
47
|
+
export async function testSqlInjection({ url, mockFetch }) {
|
|
48
|
+
const fetchFn = mockFetch || fetch;
|
|
49
|
+
|
|
50
|
+
for (const payload of SQL_PAYLOADS) {
|
|
51
|
+
try {
|
|
52
|
+
const testUrl = `${url}?id=${encodeURIComponent(payload)}`;
|
|
53
|
+
const response = await fetchFn(testUrl);
|
|
54
|
+
const text = await response.text();
|
|
55
|
+
|
|
56
|
+
// Check for SQL error messages indicating vulnerability
|
|
57
|
+
const errorPatterns = [
|
|
58
|
+
'error in your SQL syntax',
|
|
59
|
+
'mysql_fetch',
|
|
60
|
+
'ORA-',
|
|
61
|
+
'SQLServer',
|
|
62
|
+
'sqlite3',
|
|
63
|
+
'PostgreSQL',
|
|
64
|
+
'SQLSTATE'
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
if (errorPatterns.some(pattern => text.toLowerCase().includes(pattern.toLowerCase()))) {
|
|
68
|
+
return {
|
|
69
|
+
vulnerable: true,
|
|
70
|
+
payload,
|
|
71
|
+
evidence: text.substring(0, 200)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
// Continue testing
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { vulnerable: false };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Test for XSS vulnerabilities
|
|
84
|
+
* @param {Object} options - Test options
|
|
85
|
+
* @param {string} options.url - URL to test
|
|
86
|
+
* @param {Function} options.mockFetch - Mock fetch function for testing
|
|
87
|
+
* @returns {Promise<Object>} Test result
|
|
88
|
+
*/
|
|
89
|
+
export async function testXss({ url, mockFetch }) {
|
|
90
|
+
const fetchFn = mockFetch || fetch;
|
|
91
|
+
|
|
92
|
+
for (const payload of XSS_PAYLOADS) {
|
|
93
|
+
try {
|
|
94
|
+
const testUrl = `${url}?q=${encodeURIComponent(payload)}`;
|
|
95
|
+
const response = await fetchFn(testUrl);
|
|
96
|
+
const text = await response.text();
|
|
97
|
+
|
|
98
|
+
// Check if payload is reflected without encoding
|
|
99
|
+
if (text.includes(payload)) {
|
|
100
|
+
return {
|
|
101
|
+
vulnerable: true,
|
|
102
|
+
payload,
|
|
103
|
+
evidence: text.substring(0, 200)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
} catch (e) {
|
|
107
|
+
// Continue testing
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return { vulnerable: false };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Test for authentication bypass
|
|
116
|
+
* @param {Object} options - Test options
|
|
117
|
+
* @param {string} options.url - Protected URL to test
|
|
118
|
+
* @param {Function} options.mockFetch - Mock fetch function for testing
|
|
119
|
+
* @returns {Promise<Object>} Test result
|
|
120
|
+
*/
|
|
121
|
+
export async function testAuthBypass({ url, mockFetch }) {
|
|
122
|
+
const fetchFn = mockFetch || fetch;
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
// Test accessing protected resource without authentication
|
|
126
|
+
const response = await fetchFn(url, {
|
|
127
|
+
headers: {
|
|
128
|
+
// No auth headers
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// If we get a 200 on a protected endpoint, it's likely vulnerable
|
|
133
|
+
if (response.status === 200) {
|
|
134
|
+
return {
|
|
135
|
+
vulnerable: true,
|
|
136
|
+
evidence: 'Protected endpoint accessible without authentication'
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
} catch (e) {
|
|
140
|
+
// Network error, not a vulnerability indicator
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return { vulnerable: false };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Generate penetration test report
|
|
148
|
+
* @param {Array} findings - List of findings
|
|
149
|
+
* @returns {string} Formatted report
|
|
150
|
+
*/
|
|
151
|
+
export function generatePentestReport(findings) {
|
|
152
|
+
if (findings.length === 0) {
|
|
153
|
+
return '# Penetration Test Report\n\nNo vulnerabilities found.';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let report = '# Penetration Test Report\n\n';
|
|
157
|
+
report += `Found ${findings.length} vulnerability(ies):\n\n`;
|
|
158
|
+
|
|
159
|
+
const typeLabels = {
|
|
160
|
+
sqli: 'SQL Injection',
|
|
161
|
+
xss: 'Cross-Site Scripting (XSS)',
|
|
162
|
+
auth_bypass: 'Authentication Bypass',
|
|
163
|
+
default: 'Security Issue'
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
for (const finding of findings) {
|
|
167
|
+
const label = typeLabels[finding.type] || typeLabels.default;
|
|
168
|
+
report += `## ${label}\n`;
|
|
169
|
+
report += `- **Severity:** ${finding.severity}\n`;
|
|
170
|
+
report += `- **URL:** ${finding.url}\n`;
|
|
171
|
+
if (finding.payload) {
|
|
172
|
+
report += `- **Payload:** ${finding.payload}\n`;
|
|
173
|
+
}
|
|
174
|
+
if (finding.evidence) {
|
|
175
|
+
report += `- **Evidence:** ${finding.evidence}\n`;
|
|
176
|
+
}
|
|
177
|
+
report += '\n';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return report;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Create a pentest runner instance
|
|
185
|
+
* @param {Object} options - Runner options
|
|
186
|
+
* @returns {Object} Pentest runner instance
|
|
187
|
+
*/
|
|
188
|
+
export function createPentestRunner(options = {}) {
|
|
189
|
+
return {
|
|
190
|
+
/**
|
|
191
|
+
* Run full scan with Nuclei
|
|
192
|
+
* @param {Object} scanOptions - Scan options
|
|
193
|
+
* @returns {Promise<Array>} Scan findings
|
|
194
|
+
*/
|
|
195
|
+
async scan(scanOptions) {
|
|
196
|
+
const { target, templates, exec } = scanOptions;
|
|
197
|
+
if (exec) {
|
|
198
|
+
return await runNuclei({ target, templates, exec });
|
|
199
|
+
}
|
|
200
|
+
return [];
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Test for SQL injection
|
|
205
|
+
* @param {Object} testOptions - Test options
|
|
206
|
+
* @returns {Promise<Object>} Test result
|
|
207
|
+
*/
|
|
208
|
+
async testSqli(testOptions) {
|
|
209
|
+
return await testSqlInjection(testOptions);
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Test for XSS
|
|
214
|
+
* @param {Object} testOptions - Test options
|
|
215
|
+
* @returns {Promise<Object>} Test result
|
|
216
|
+
*/
|
|
217
|
+
async testXss(testOptions) {
|
|
218
|
+
return await testXss(testOptions);
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Test for auth bypass
|
|
223
|
+
* @param {Object} testOptions - Test options
|
|
224
|
+
* @returns {Promise<Object>} Test result
|
|
225
|
+
*/
|
|
226
|
+
async testAuthBypass(testOptions) {
|
|
227
|
+
return await testAuthBypass(testOptions);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
}
|