@sparkleideas/security 3.0.0-alpha.10
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 +234 -0
- package/__tests__/acceptance/security-compliance.test.ts +674 -0
- package/__tests__/credential-generator.test.ts +310 -0
- package/__tests__/fixtures/configurations.ts +419 -0
- package/__tests__/fixtures/index.ts +21 -0
- package/__tests__/helpers/create-mock.ts +469 -0
- package/__tests__/helpers/index.ts +32 -0
- package/__tests__/input-validator.test.ts +381 -0
- package/__tests__/integration/security-flow.test.ts +606 -0
- package/__tests__/password-hasher.test.ts +239 -0
- package/__tests__/path-validator.test.ts +302 -0
- package/__tests__/safe-executor.test.ts +292 -0
- package/__tests__/token-generator.test.ts +371 -0
- package/__tests__/unit/credential-generator.test.ts +182 -0
- package/__tests__/unit/password-hasher.test.ts +359 -0
- package/__tests__/unit/path-validator.test.ts +509 -0
- package/__tests__/unit/safe-executor.test.ts +667 -0
- package/__tests__/unit/token-generator.test.ts +310 -0
- package/package.json +28 -0
- package/src/CVE-REMEDIATION.ts +251 -0
- package/src/application/index.ts +10 -0
- package/src/application/services/security-application-service.ts +193 -0
- package/src/credential-generator.ts +368 -0
- package/src/domain/entities/security-context.ts +173 -0
- package/src/domain/index.ts +17 -0
- package/src/domain/services/security-domain-service.ts +296 -0
- package/src/index.ts +271 -0
- package/src/input-validator.ts +466 -0
- package/src/password-hasher.ts +270 -0
- package/src/path-validator.ts +525 -0
- package/src/safe-executor.ts +525 -0
- package/src/token-generator.ts +463 -0
- package/tmp.json +0 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3 Claude-Flow Security Compliance Acceptance Tests
|
|
3
|
+
*
|
|
4
|
+
* Acceptance tests for security requirements
|
|
5
|
+
* Tests CVE prevention and security compliance
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
8
|
+
import { createMock, type MockedInterface } from '../helpers/create-mock';
|
|
9
|
+
import { securityConfigs } from '../fixtures/configurations';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Security compliance checker interface
|
|
13
|
+
*/
|
|
14
|
+
interface ISecurityComplianceChecker {
|
|
15
|
+
checkPathTraversal(path: string): ComplianceResult;
|
|
16
|
+
checkCommandInjection(command: string, args: string[]): ComplianceResult;
|
|
17
|
+
checkNullByteInjection(input: string): ComplianceResult;
|
|
18
|
+
checkPasswordPolicy(password: string): ComplianceResult;
|
|
19
|
+
runFullAudit(): Promise<AuditResult>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* CVE scanner interface
|
|
24
|
+
*/
|
|
25
|
+
interface ICVEScanner {
|
|
26
|
+
scan(code: string): Promise<CVEScanResult>;
|
|
27
|
+
getKnownCVEs(): CVEInfo[];
|
|
28
|
+
validateFix(cveId: string): Promise<boolean>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Security policy enforcer interface
|
|
33
|
+
*/
|
|
34
|
+
interface ISecurityPolicyEnforcer {
|
|
35
|
+
enforcePathPolicy(path: string): EnforcementResult;
|
|
36
|
+
enforceCommandPolicy(command: string): EnforcementResult;
|
|
37
|
+
enforceInputPolicy(input: string): EnforcementResult;
|
|
38
|
+
getViolations(): PolicyViolation[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface ComplianceResult {
|
|
42
|
+
compliant: boolean;
|
|
43
|
+
violations: string[];
|
|
44
|
+
severity: 'critical' | 'high' | 'medium' | 'low' | 'none';
|
|
45
|
+
recommendations: string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface AuditResult {
|
|
49
|
+
passed: boolean;
|
|
50
|
+
checks: AuditCheck[];
|
|
51
|
+
overallScore: number;
|
|
52
|
+
timestamp: Date;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface AuditCheck {
|
|
56
|
+
name: string;
|
|
57
|
+
passed: boolean;
|
|
58
|
+
details: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface CVEScanResult {
|
|
62
|
+
vulnerabilities: CVEInfo[];
|
|
63
|
+
riskScore: number;
|
|
64
|
+
remediation: string[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface CVEInfo {
|
|
68
|
+
id: string;
|
|
69
|
+
severity: 'critical' | 'high' | 'medium' | 'low';
|
|
70
|
+
description: string;
|
|
71
|
+
affectedVersions: string[];
|
|
72
|
+
fixedIn?: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface EnforcementResult {
|
|
76
|
+
allowed: boolean;
|
|
77
|
+
reason?: string;
|
|
78
|
+
sanitized?: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface PolicyViolation {
|
|
82
|
+
type: string;
|
|
83
|
+
input: string;
|
|
84
|
+
timestamp: Date;
|
|
85
|
+
severity: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Security compliance checker implementation
|
|
90
|
+
*/
|
|
91
|
+
class SecurityComplianceChecker implements ISecurityComplianceChecker {
|
|
92
|
+
constructor(private readonly config: typeof securityConfigs.strict) {}
|
|
93
|
+
|
|
94
|
+
checkPathTraversal(path: string): ComplianceResult {
|
|
95
|
+
const violations: string[] = [];
|
|
96
|
+
const recommendations: string[] = [];
|
|
97
|
+
|
|
98
|
+
// Check for directory traversal patterns
|
|
99
|
+
const traversalPatterns = ['../', '..\\', '%2e%2e%2f', '%2e%2e/'];
|
|
100
|
+
for (const pattern of traversalPatterns) {
|
|
101
|
+
if (path.toLowerCase().includes(pattern.toLowerCase())) {
|
|
102
|
+
violations.push(`Path contains traversal pattern: ${pattern}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Check for blocked patterns from config
|
|
107
|
+
for (const pattern of this.config.paths.blockedPatterns) {
|
|
108
|
+
if (path.includes(pattern)) {
|
|
109
|
+
violations.push(`Path contains blocked pattern: ${pattern}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check for null bytes
|
|
114
|
+
if (path.includes('\0')) {
|
|
115
|
+
violations.push('Path contains null byte');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (violations.length > 0) {
|
|
119
|
+
recommendations.push('Sanitize path input');
|
|
120
|
+
recommendations.push('Use allowlist for valid paths');
|
|
121
|
+
recommendations.push('Validate against allowed directories');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
compliant: violations.length === 0,
|
|
126
|
+
violations,
|
|
127
|
+
severity: violations.length > 0 ? 'critical' : 'none',
|
|
128
|
+
recommendations,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
checkCommandInjection(command: string, args: string[]): ComplianceResult {
|
|
133
|
+
const violations: string[] = [];
|
|
134
|
+
const recommendations: string[] = [];
|
|
135
|
+
|
|
136
|
+
// Check base command
|
|
137
|
+
const baseCommand = command.split(' ')[0];
|
|
138
|
+
if (this.config.execution.blockedCommands.includes(baseCommand)) {
|
|
139
|
+
violations.push(`Command "${baseCommand}" is blocked`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!this.config.execution.allowedCommands.includes(baseCommand)) {
|
|
143
|
+
violations.push(`Command "${baseCommand}" is not in allowlist`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check for injection patterns in args
|
|
147
|
+
const injectionPatterns = [';', '|', '&', '`', '$', '(', ')', '<', '>', '\n'];
|
|
148
|
+
for (const arg of args) {
|
|
149
|
+
for (const pattern of injectionPatterns) {
|
|
150
|
+
if (arg.includes(pattern)) {
|
|
151
|
+
violations.push(`Argument contains injection pattern: ${pattern}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (violations.length > 0) {
|
|
157
|
+
recommendations.push('Sanitize command arguments');
|
|
158
|
+
recommendations.push('Use allowlist for commands');
|
|
159
|
+
recommendations.push('Disable shell execution');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
compliant: violations.length === 0,
|
|
164
|
+
violations,
|
|
165
|
+
severity: violations.length > 0 ? 'critical' : 'none',
|
|
166
|
+
recommendations,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
checkNullByteInjection(input: string): ComplianceResult {
|
|
171
|
+
const violations: string[] = [];
|
|
172
|
+
|
|
173
|
+
if (input.includes('\0')) {
|
|
174
|
+
violations.push('Input contains null byte');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Check for encoded null bytes
|
|
178
|
+
if (input.includes('%00')) {
|
|
179
|
+
violations.push('Input contains URL-encoded null byte');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
compliant: violations.length === 0,
|
|
184
|
+
violations,
|
|
185
|
+
severity: violations.length > 0 ? 'high' : 'none',
|
|
186
|
+
recommendations: violations.length > 0 ? ['Strip null bytes from input'] : [],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
checkPasswordPolicy(password: string): ComplianceResult {
|
|
191
|
+
const violations: string[] = [];
|
|
192
|
+
|
|
193
|
+
if (password.length < 8) {
|
|
194
|
+
violations.push('Password must be at least 8 characters');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!/[A-Z]/.test(password)) {
|
|
198
|
+
violations.push('Password must contain uppercase letter');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!/[a-z]/.test(password)) {
|
|
202
|
+
violations.push('Password must contain lowercase letter');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!/[0-9]/.test(password)) {
|
|
206
|
+
violations.push('Password must contain number');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!/[!@#$%^&*]/.test(password)) {
|
|
210
|
+
violations.push('Password should contain special character');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
compliant: violations.length === 0,
|
|
215
|
+
violations,
|
|
216
|
+
severity: violations.length > 2 ? 'high' : violations.length > 0 ? 'medium' : 'none',
|
|
217
|
+
recommendations: ['Use a password manager', 'Enable MFA'],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async runFullAudit(): Promise<AuditResult> {
|
|
222
|
+
const checks: AuditCheck[] = [
|
|
223
|
+
{
|
|
224
|
+
name: 'Path Traversal Protection',
|
|
225
|
+
passed: this.config.paths.blockedPatterns.includes('../'),
|
|
226
|
+
details: 'Verified blocked patterns include directory traversal',
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
name: 'Command Injection Protection',
|
|
230
|
+
passed: this.config.execution.shell === false,
|
|
231
|
+
details: 'Shell execution is disabled',
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: 'Dangerous Commands Blocked',
|
|
235
|
+
passed: this.config.execution.blockedCommands.includes('rm'),
|
|
236
|
+
details: 'Verified dangerous commands are blocked',
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: 'Secure Hashing Algorithm',
|
|
240
|
+
passed: this.config.hashing.algorithm === 'argon2',
|
|
241
|
+
details: 'Using recommended hashing algorithm',
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: 'Input Size Limit',
|
|
245
|
+
passed: this.config.validation.maxInputSize <= 10000,
|
|
246
|
+
details: 'Input size is properly limited',
|
|
247
|
+
},
|
|
248
|
+
];
|
|
249
|
+
|
|
250
|
+
const passed = checks.every((c) => c.passed);
|
|
251
|
+
const overallScore = (checks.filter((c) => c.passed).length / checks.length) * 100;
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
passed,
|
|
255
|
+
checks,
|
|
256
|
+
overallScore,
|
|
257
|
+
timestamp: new Date(),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
describe('Security Compliance Acceptance', () => {
|
|
263
|
+
let complianceChecker: SecurityComplianceChecker;
|
|
264
|
+
let mockCVEScanner: MockedInterface<ICVEScanner>;
|
|
265
|
+
let mockPolicyEnforcer: MockedInterface<ISecurityPolicyEnforcer>;
|
|
266
|
+
|
|
267
|
+
beforeEach(() => {
|
|
268
|
+
complianceChecker = new SecurityComplianceChecker(securityConfigs.strict);
|
|
269
|
+
mockCVEScanner = createMock<ICVEScanner>();
|
|
270
|
+
mockPolicyEnforcer = createMock<ISecurityPolicyEnforcer>();
|
|
271
|
+
|
|
272
|
+
// Configure CVE scanner mock
|
|
273
|
+
mockCVEScanner.getKnownCVEs.mockReturnValue([
|
|
274
|
+
{
|
|
275
|
+
id: 'CVE-1',
|
|
276
|
+
severity: 'critical',
|
|
277
|
+
description: 'Directory traversal vulnerability',
|
|
278
|
+
affectedVersions: ['<3.0.0'],
|
|
279
|
+
fixedIn: '3.0.0',
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
id: 'CVE-2',
|
|
283
|
+
severity: 'critical',
|
|
284
|
+
description: 'Absolute path injection vulnerability',
|
|
285
|
+
affectedVersions: ['<3.0.0'],
|
|
286
|
+
fixedIn: '3.0.0',
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
id: 'CVE-3',
|
|
290
|
+
severity: 'critical',
|
|
291
|
+
description: 'Command injection vulnerability',
|
|
292
|
+
affectedVersions: ['<3.0.0'],
|
|
293
|
+
fixedIn: '3.0.0',
|
|
294
|
+
},
|
|
295
|
+
]);
|
|
296
|
+
|
|
297
|
+
mockCVEScanner.validateFix.mockResolvedValue(true);
|
|
298
|
+
mockCVEScanner.scan.mockResolvedValue({
|
|
299
|
+
vulnerabilities: [],
|
|
300
|
+
riskScore: 0,
|
|
301
|
+
remediation: [],
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// Configure policy enforcer mock
|
|
305
|
+
mockPolicyEnforcer.enforcePathPolicy.mockImplementation((path) => ({
|
|
306
|
+
allowed: !path.includes('../'),
|
|
307
|
+
reason: path.includes('../') ? 'Path traversal detected' : undefined,
|
|
308
|
+
}));
|
|
309
|
+
mockPolicyEnforcer.enforceCommandPolicy.mockImplementation((cmd) => ({
|
|
310
|
+
allowed: securityConfigs.strict.execution.allowedCommands.includes(cmd.split(' ')[0]),
|
|
311
|
+
}));
|
|
312
|
+
mockPolicyEnforcer.getViolations.mockReturnValue([]);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
describe('CVE-1: Directory Traversal Prevention', () => {
|
|
316
|
+
it('should detect basic directory traversal', () => {
|
|
317
|
+
// Given
|
|
318
|
+
const maliciousPath = '../../../etc/passwd';
|
|
319
|
+
|
|
320
|
+
// When
|
|
321
|
+
const result = complianceChecker.checkPathTraversal(maliciousPath);
|
|
322
|
+
|
|
323
|
+
// Then
|
|
324
|
+
expect(result.compliant).toBe(false);
|
|
325
|
+
expect(result.severity).toBe('critical');
|
|
326
|
+
expect(result.violations).toContainEqual(expect.stringContaining('../'));
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('should detect Windows-style traversal', () => {
|
|
330
|
+
// Given
|
|
331
|
+
const maliciousPath = '..\\..\\..\\Windows\\System32';
|
|
332
|
+
|
|
333
|
+
// When
|
|
334
|
+
const result = complianceChecker.checkPathTraversal(maliciousPath);
|
|
335
|
+
|
|
336
|
+
// Then
|
|
337
|
+
expect(result.compliant).toBe(false);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it('should detect URL-encoded traversal', () => {
|
|
341
|
+
// Given
|
|
342
|
+
const maliciousPath = '%2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd';
|
|
343
|
+
|
|
344
|
+
// When
|
|
345
|
+
const result = complianceChecker.checkPathTraversal(maliciousPath);
|
|
346
|
+
|
|
347
|
+
// Then
|
|
348
|
+
expect(result.compliant).toBe(false);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('should allow safe paths', () => {
|
|
352
|
+
// Given
|
|
353
|
+
const safePath = './v3/src/security/index.ts';
|
|
354
|
+
|
|
355
|
+
// When
|
|
356
|
+
const result = complianceChecker.checkPathTraversal(safePath);
|
|
357
|
+
|
|
358
|
+
// Then
|
|
359
|
+
expect(result.compliant).toBe(true);
|
|
360
|
+
expect(result.severity).toBe('none');
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('should validate CVE-1 fix', async () => {
|
|
364
|
+
// When
|
|
365
|
+
const isFixed = await mockCVEScanner.validateFix('CVE-1');
|
|
366
|
+
|
|
367
|
+
// Then
|
|
368
|
+
expect(isFixed).toBe(true);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
describe('CVE-2: Absolute Path Injection Prevention', () => {
|
|
373
|
+
it('should detect /etc/ access attempts', () => {
|
|
374
|
+
// Given
|
|
375
|
+
const maliciousPath = '/etc/passwd';
|
|
376
|
+
|
|
377
|
+
// When
|
|
378
|
+
const result = complianceChecker.checkPathTraversal(maliciousPath);
|
|
379
|
+
|
|
380
|
+
// Then
|
|
381
|
+
expect(result.compliant).toBe(false);
|
|
382
|
+
expect(result.violations).toContainEqual(expect.stringContaining('/etc/'));
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should detect /tmp/ access attempts', () => {
|
|
386
|
+
// Given
|
|
387
|
+
const maliciousPath = '/tmp/malicious.sh';
|
|
388
|
+
|
|
389
|
+
// When
|
|
390
|
+
const result = complianceChecker.checkPathTraversal(maliciousPath);
|
|
391
|
+
|
|
392
|
+
// Then
|
|
393
|
+
expect(result.compliant).toBe(false);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('should detect home directory access attempts', () => {
|
|
397
|
+
// Given
|
|
398
|
+
const maliciousPath = '~/.ssh/id_rsa';
|
|
399
|
+
|
|
400
|
+
// When
|
|
401
|
+
const result = complianceChecker.checkPathTraversal(maliciousPath);
|
|
402
|
+
|
|
403
|
+
// Then
|
|
404
|
+
expect(result.compliant).toBe(false);
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('should validate CVE-2 fix', async () => {
|
|
408
|
+
// When
|
|
409
|
+
const isFixed = await mockCVEScanner.validateFix('CVE-2');
|
|
410
|
+
|
|
411
|
+
// Then
|
|
412
|
+
expect(isFixed).toBe(true);
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
describe('CVE-3: Command Injection Prevention', () => {
|
|
417
|
+
it('should detect semicolon injection', () => {
|
|
418
|
+
// Given
|
|
419
|
+
const command = 'npm';
|
|
420
|
+
const args = ['install; rm -rf /'];
|
|
421
|
+
|
|
422
|
+
// When
|
|
423
|
+
const result = complianceChecker.checkCommandInjection(command, args);
|
|
424
|
+
|
|
425
|
+
// Then
|
|
426
|
+
expect(result.compliant).toBe(false);
|
|
427
|
+
expect(result.violations).toContainEqual(expect.stringContaining(';'));
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it('should detect pipe injection', () => {
|
|
431
|
+
// Given
|
|
432
|
+
const command = 'npm';
|
|
433
|
+
const args = ['install | cat /etc/passwd'];
|
|
434
|
+
|
|
435
|
+
// When
|
|
436
|
+
const result = complianceChecker.checkCommandInjection(command, args);
|
|
437
|
+
|
|
438
|
+
// Then
|
|
439
|
+
expect(result.compliant).toBe(false);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it('should detect command substitution', () => {
|
|
443
|
+
// Given
|
|
444
|
+
const command = 'npm';
|
|
445
|
+
const args = ['install $(whoami)'];
|
|
446
|
+
|
|
447
|
+
// When
|
|
448
|
+
const result = complianceChecker.checkCommandInjection(command, args);
|
|
449
|
+
|
|
450
|
+
// Then
|
|
451
|
+
expect(result.compliant).toBe(false);
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
it('should detect backtick execution', () => {
|
|
455
|
+
// Given
|
|
456
|
+
const command = 'npm';
|
|
457
|
+
const args = ['install `rm -rf /`'];
|
|
458
|
+
|
|
459
|
+
// When
|
|
460
|
+
const result = complianceChecker.checkCommandInjection(command, args);
|
|
461
|
+
|
|
462
|
+
// Then
|
|
463
|
+
expect(result.compliant).toBe(false);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('should block dangerous commands', () => {
|
|
467
|
+
// Given
|
|
468
|
+
const command = 'rm';
|
|
469
|
+
const args = ['-rf', '/'];
|
|
470
|
+
|
|
471
|
+
// When
|
|
472
|
+
const result = complianceChecker.checkCommandInjection(command, args);
|
|
473
|
+
|
|
474
|
+
// Then
|
|
475
|
+
expect(result.compliant).toBe(false);
|
|
476
|
+
expect(result.violations).toContainEqual(expect.stringContaining('rm'));
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it('should allow safe commands', () => {
|
|
480
|
+
// Given
|
|
481
|
+
const command = 'npm';
|
|
482
|
+
const args = ['install', '--save', 'lodash'];
|
|
483
|
+
|
|
484
|
+
// When
|
|
485
|
+
const result = complianceChecker.checkCommandInjection(command, args);
|
|
486
|
+
|
|
487
|
+
// Then
|
|
488
|
+
expect(result.compliant).toBe(true);
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
it('should validate CVE-3 fix', async () => {
|
|
492
|
+
// When
|
|
493
|
+
const isFixed = await mockCVEScanner.validateFix('CVE-3');
|
|
494
|
+
|
|
495
|
+
// Then
|
|
496
|
+
expect(isFixed).toBe(true);
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
describe('Null Byte Injection Prevention', () => {
|
|
501
|
+
it('should detect null byte in path', () => {
|
|
502
|
+
// Given
|
|
503
|
+
const input = 'file.txt\0.exe';
|
|
504
|
+
|
|
505
|
+
// When
|
|
506
|
+
const result = complianceChecker.checkNullByteInjection(input);
|
|
507
|
+
|
|
508
|
+
// Then
|
|
509
|
+
expect(result.compliant).toBe(false);
|
|
510
|
+
expect(result.severity).toBe('high');
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it('should detect URL-encoded null byte', () => {
|
|
514
|
+
// Given
|
|
515
|
+
const input = 'file.txt%00.exe';
|
|
516
|
+
|
|
517
|
+
// When
|
|
518
|
+
const result = complianceChecker.checkNullByteInjection(input);
|
|
519
|
+
|
|
520
|
+
// Then
|
|
521
|
+
expect(result.compliant).toBe(false);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
it('should allow clean input', () => {
|
|
525
|
+
// Given
|
|
526
|
+
const input = 'file.txt';
|
|
527
|
+
|
|
528
|
+
// When
|
|
529
|
+
const result = complianceChecker.checkNullByteInjection(input);
|
|
530
|
+
|
|
531
|
+
// Then
|
|
532
|
+
expect(result.compliant).toBe(true);
|
|
533
|
+
});
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
describe('Password Policy Compliance', () => {
|
|
537
|
+
it('should require minimum length', () => {
|
|
538
|
+
// Given
|
|
539
|
+
const weakPassword = 'Short1!';
|
|
540
|
+
|
|
541
|
+
// When
|
|
542
|
+
const result = complianceChecker.checkPasswordPolicy(weakPassword);
|
|
543
|
+
|
|
544
|
+
// Then
|
|
545
|
+
expect(result.compliant).toBe(false);
|
|
546
|
+
expect(result.violations).toContainEqual(expect.stringContaining('8 characters'));
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it('should require uppercase letter', () => {
|
|
550
|
+
// Given
|
|
551
|
+
const noUpper = 'password123!';
|
|
552
|
+
|
|
553
|
+
// When
|
|
554
|
+
const result = complianceChecker.checkPasswordPolicy(noUpper);
|
|
555
|
+
|
|
556
|
+
// Then
|
|
557
|
+
expect(result.violations).toContainEqual(expect.stringContaining('uppercase'));
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('should require lowercase letter', () => {
|
|
561
|
+
// Given
|
|
562
|
+
const noLower = 'PASSWORD123!';
|
|
563
|
+
|
|
564
|
+
// When
|
|
565
|
+
const result = complianceChecker.checkPasswordPolicy(noLower);
|
|
566
|
+
|
|
567
|
+
// Then
|
|
568
|
+
expect(result.violations).toContainEqual(expect.stringContaining('lowercase'));
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it('should require number', () => {
|
|
572
|
+
// Given
|
|
573
|
+
const noNumber = 'Password!!';
|
|
574
|
+
|
|
575
|
+
// When
|
|
576
|
+
const result = complianceChecker.checkPasswordPolicy(noNumber);
|
|
577
|
+
|
|
578
|
+
// Then
|
|
579
|
+
expect(result.violations).toContainEqual(expect.stringContaining('number'));
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
it('should accept strong password', () => {
|
|
583
|
+
// Given
|
|
584
|
+
const strongPassword = 'SecureP@ss123!';
|
|
585
|
+
|
|
586
|
+
// When
|
|
587
|
+
const result = complianceChecker.checkPasswordPolicy(strongPassword);
|
|
588
|
+
|
|
589
|
+
// Then
|
|
590
|
+
expect(result.compliant).toBe(true);
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
describe('Full Security Audit', () => {
|
|
595
|
+
it('should pass full audit with strict config', async () => {
|
|
596
|
+
// When
|
|
597
|
+
const audit = await complianceChecker.runFullAudit();
|
|
598
|
+
|
|
599
|
+
// Then
|
|
600
|
+
expect(audit.passed).toBe(true);
|
|
601
|
+
expect(audit.overallScore).toBe(100);
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
it('should verify all security checks pass', async () => {
|
|
605
|
+
// When
|
|
606
|
+
const audit = await complianceChecker.runFullAudit();
|
|
607
|
+
|
|
608
|
+
// Then
|
|
609
|
+
for (const check of audit.checks) {
|
|
610
|
+
expect(check.passed).toBe(true);
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it('should verify path traversal protection is enabled', async () => {
|
|
615
|
+
// When
|
|
616
|
+
const audit = await complianceChecker.runFullAudit();
|
|
617
|
+
|
|
618
|
+
// Then
|
|
619
|
+
const pathCheck = audit.checks.find((c) => c.name === 'Path Traversal Protection');
|
|
620
|
+
expect(pathCheck?.passed).toBe(true);
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
it('should verify shell execution is disabled', async () => {
|
|
624
|
+
// When
|
|
625
|
+
const audit = await complianceChecker.runFullAudit();
|
|
626
|
+
|
|
627
|
+
// Then
|
|
628
|
+
const shellCheck = audit.checks.find((c) => c.name === 'Command Injection Protection');
|
|
629
|
+
expect(shellCheck?.passed).toBe(true);
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
it('should verify secure hashing is configured', async () => {
|
|
633
|
+
// When
|
|
634
|
+
const audit = await complianceChecker.runFullAudit();
|
|
635
|
+
|
|
636
|
+
// Then
|
|
637
|
+
const hashCheck = audit.checks.find((c) => c.name === 'Secure Hashing Algorithm');
|
|
638
|
+
expect(hashCheck?.passed).toBe(true);
|
|
639
|
+
});
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
describe('Security Configuration Compliance', () => {
|
|
643
|
+
it('should have 95% security test coverage target', () => {
|
|
644
|
+
// Given
|
|
645
|
+
const securityCoverageTarget = 0.95;
|
|
646
|
+
|
|
647
|
+
// Then
|
|
648
|
+
expect(securityCoverageTarget).toBe(0.95);
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
it('should use argon2 for password hashing', () => {
|
|
652
|
+
// Then
|
|
653
|
+
expect(securityConfigs.strict.hashing.algorithm).toBe('argon2');
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
it('should disable shell execution by default', () => {
|
|
657
|
+
// Then
|
|
658
|
+
expect(securityConfigs.strict.execution.shell).toBe(false);
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
it('should block all dangerous commands', () => {
|
|
662
|
+
// Then
|
|
663
|
+
expect(securityConfigs.strict.execution.blockedCommands).toContain('rm');
|
|
664
|
+
expect(securityConfigs.strict.execution.blockedCommands).toContain('del');
|
|
665
|
+
expect(securityConfigs.strict.execution.blockedCommands).toContain('format');
|
|
666
|
+
expect(securityConfigs.strict.execution.blockedCommands).toContain('dd');
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
it('should have limited allowed commands', () => {
|
|
670
|
+
// Then
|
|
671
|
+
expect(securityConfigs.strict.execution.allowedCommands).toEqual(['npm', 'npx', 'node', 'git']);
|
|
672
|
+
});
|
|
673
|
+
});
|
|
674
|
+
});
|