Add comprehensive security tests for OWASP A02, A05, A07, and A08 categories
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Export Center CI / export-ci (push) Has been cancelled
Findings Ledger CI / build-test (push) Has been cancelled
Findings Ledger CI / migration-validation (push) Has been cancelled
Findings Ledger CI / generate-manifest (push) Has been cancelled
Manifest Integrity / Validate Schema Integrity (push) Has been cancelled
Lighthouse CI / Lighthouse Audit (push) Has been cancelled
Lighthouse CI / Axe Accessibility Audit (push) Has been cancelled
Manifest Integrity / Validate Contract Documents (push) Has been cancelled
Manifest Integrity / Validate Pack Fixtures (push) Has been cancelled
Manifest Integrity / Audit SHA256SUMS Files (push) Has been cancelled
Manifest Integrity / Verify Merkle Roots (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled

- Implemented tests for Cryptographic Failures (A02) to ensure proper handling of sensitive data, secure algorithms, and key management.
- Added tests for Security Misconfiguration (A05) to validate production configurations, security headers, CORS settings, and feature management.
- Developed tests for Authentication Failures (A07) to enforce strong password policies, rate limiting, session management, and MFA support.
- Created tests for Software and Data Integrity Failures (A08) to verify artifact signatures, SBOM integrity, attestation chains, and feed updates.
This commit is contained in:
master
2025-12-16 16:40:19 +02:00
parent 415eff1207
commit 2170a58734
206 changed files with 30547 additions and 534 deletions

View File

@@ -0,0 +1,359 @@
// =============================================================================
// deterministic-fixtures.ts
// Deterministic test fixtures for TTFS testing
// Part of Task T15: Create deterministic test fixtures
// =============================================================================
/**
* Frozen timestamp used across all fixtures.
* ISO 8601 format: 2025-12-04T12:00:00.000Z
*/
export const FROZEN_TIMESTAMP = '2025-12-04T12:00:00.000Z';
export const FROZEN_TIMESTAMP_MS = new Date(FROZEN_TIMESTAMP).getTime();
/**
* Deterministic seed for reproducible random generation.
*/
export const DETERMINISTIC_SEED = 42;
/**
* Pre-generated deterministic UUIDs.
*/
export const FIXTURE_IDS = {
TENANT_ID: '11111111-1111-1111-1111-111111111111',
RUN_ID: '22222222-2222-2222-2222-222222222222',
JOB_ID: '33333333-3333-3333-3333-333333333333',
SOURCE_ID: '44444444-4444-4444-4444-444444444444',
SIGNATURE_ID: '55555555-5555-5555-5555-555555555555',
TENANT_ID_STRING: 'test-tenant-deterministic',
CORRELATION_ID: 'corr-deterministic-001',
SIGNAL_ID: 'sig-deterministic-001',
} as const;
/**
* Deterministic digest values.
*/
export const DIGESTS = {
/** 64-character lowercase hex digest (SHA-256). */
PAYLOAD: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
/** Image digest reference. */
IMAGE: 'sha256:abc123def456789012345678901234567890123456789012345678901234abcd',
} as const;
/**
* FirstSignal kind values.
*/
export type FirstSignalKind =
| 'queued'
| 'started'
| 'phase'
| 'blocked'
| 'failed'
| 'succeeded'
| 'canceled'
| 'unavailable';
/**
* FirstSignal phase values.
*/
export type FirstSignalPhase =
| 'resolve'
| 'fetch'
| 'restore'
| 'analyze'
| 'policy'
| 'report'
| 'unknown';
/**
* FirstSignal scope interface.
*/
export interface FirstSignalScope {
type: 'repo' | 'image' | 'artifact';
id: string;
}
/**
* LastKnownOutcome interface.
*/
export interface LastKnownOutcome {
signatureId: string;
errorCode?: string;
token: string;
excerpt?: string;
confidence: 'low' | 'medium' | 'high';
firstSeenAt: string;
hitCount: number;
}
/**
* NextAction interface.
*/
export interface NextAction {
type: 'open_logs' | 'open_job' | 'docs' | 'retry' | 'cli_command';
label: string;
target: string;
}
/**
* FirstSignalDiagnostics interface.
*/
export interface FirstSignalDiagnostics {
cacheHit: boolean;
source: 'snapshot' | 'failure_index' | 'cold_start';
correlationId: string;
}
/**
* FirstSignal interface.
*/
export interface FirstSignal {
version: '1.0';
signalId: string;
jobId: string;
timestamp: string;
kind: FirstSignalKind;
phase: FirstSignalPhase;
scope: FirstSignalScope;
summary: string;
etaSeconds?: number;
lastKnownOutcome?: LastKnownOutcome;
nextActions?: NextAction[];
diagnostics: FirstSignalDiagnostics;
}
/**
* Pre-built FirstSignal fixtures for different scenarios.
*/
export const FIRST_SIGNAL_FIXTURES: Record<string, FirstSignal> = {
queued: {
version: '1.0',
signalId: FIXTURE_IDS.SIGNAL_ID,
jobId: FIXTURE_IDS.JOB_ID,
timestamp: FROZEN_TIMESTAMP,
kind: 'queued',
phase: 'resolve',
scope: { type: 'image', id: DIGESTS.IMAGE },
summary: 'Job queued, waiting for available worker',
etaSeconds: 120,
diagnostics: {
cacheHit: true,
source: 'snapshot',
correlationId: FIXTURE_IDS.CORRELATION_ID,
},
},
failed: {
version: '1.0',
signalId: FIXTURE_IDS.SIGNAL_ID,
jobId: FIXTURE_IDS.JOB_ID,
timestamp: FROZEN_TIMESTAMP,
kind: 'failed',
phase: 'analyze',
scope: { type: 'image', id: DIGESTS.IMAGE },
summary: 'Analysis failed: dependency resolution error',
lastKnownOutcome: {
signatureId: FIXTURE_IDS.SIGNATURE_ID,
errorCode: 'EDEPNOTFOUND',
token: 'EDEPNOTFOUND',
excerpt: 'Could not resolve dependency @types/node@^18.0.0',
confidence: 'high',
firstSeenAt: '2025-12-01T10:00:00.000Z',
hitCount: 15,
},
nextActions: [
{ type: 'open_logs', label: 'View Logs', target: `/logs/${FIXTURE_IDS.JOB_ID}` },
{ type: 'retry', label: 'Retry Job', target: `/retry/${FIXTURE_IDS.JOB_ID}` },
],
diagnostics: {
cacheHit: false,
source: 'failure_index',
correlationId: FIXTURE_IDS.CORRELATION_ID,
},
},
succeeded: {
version: '1.0',
signalId: FIXTURE_IDS.SIGNAL_ID,
jobId: FIXTURE_IDS.JOB_ID,
timestamp: FROZEN_TIMESTAMP,
kind: 'succeeded',
phase: 'report',
scope: { type: 'image', id: DIGESTS.IMAGE },
summary: 'Scan completed: 3 critical, 12 high, 45 medium findings',
nextActions: [
{ type: 'open_job', label: 'View Results', target: `/jobs/${FIXTURE_IDS.JOB_ID}` },
],
diagnostics: {
cacheHit: true,
source: 'snapshot',
correlationId: FIXTURE_IDS.CORRELATION_ID,
},
},
blocked: {
version: '1.0',
signalId: FIXTURE_IDS.SIGNAL_ID,
jobId: FIXTURE_IDS.JOB_ID,
timestamp: FROZEN_TIMESTAMP,
kind: 'blocked',
phase: 'policy',
scope: { type: 'image', id: DIGESTS.IMAGE },
summary: 'Blocked by policy: critical-vuln-gate',
nextActions: [
{ type: 'docs', label: 'Policy Details', target: '/docs/policies/critical-vuln-gate' },
],
diagnostics: {
cacheHit: true,
source: 'snapshot',
correlationId: FIXTURE_IDS.CORRELATION_ID,
},
},
};
/**
* API response fixtures for TTFS measurement.
*/
export const API_RESPONSE_FIXTURES = {
firstSignalSuccess: (signal: FirstSignal) => ({
runId: FIXTURE_IDS.RUN_ID,
firstSignal: signal,
summaryEtag: 'W/"deterministic-etag-001"',
}),
firstSignalNotFound: {
error: 'Run not found',
code: 'RUN_NOT_FOUND',
},
firstSignalUnavailable: {
runId: FIXTURE_IDS.RUN_ID,
firstSignal: null,
summaryEtag: null,
},
};
/**
* Timing fixtures for TTFS measurement.
*/
export const TIMING_FIXTURES = {
cacheHit: {
ttfsMs: 120,
cacheStatus: 'hit' as const,
},
coldStart: {
ttfsMs: 850,
cacheStatus: 'miss' as const,
},
sloBreachP50: {
ttfsMs: 2500, // > 2000ms P50 target
cacheStatus: 'miss' as const,
},
sloBreachP95: {
ttfsMs: 6000, // > 5000ms P95 target
cacheStatus: 'miss' as const,
},
};
/**
* Seeded random number generator for deterministic test data.
*/
export class SeededRandom {
private seed: number;
constructor(seed: number = DETERMINISTIC_SEED) {
this.seed = seed;
}
/**
* Returns a pseudo-random number between 0 and 1.
*/
next(): number {
// Simple LCG implementation
this.seed = (this.seed * 1103515245 + 12345) % 2147483648;
return this.seed / 2147483648;
}
/**
* Returns a pseudo-random integer between 0 and max (exclusive).
*/
nextInt(max: number): number {
return Math.floor(this.next() * max);
}
/**
* Generates a deterministic UUID.
*/
nextUuid(): string {
const hex = () => this.nextInt(16).toString(16);
return [
Array(8).fill(0).map(hex).join(''),
Array(4).fill(0).map(hex).join(''),
'4' + Array(3).fill(0).map(hex).join(''),
((this.nextInt(4) + 8).toString(16)) + Array(3).fill(0).map(hex).join(''),
Array(12).fill(0).map(hex).join(''),
].join('-');
}
}
/**
* Jest/Vitest setup helper to freeze time and random.
*/
export function setupDeterministicEnvironment(): () => void {
const originalDate = global.Date;
const originalRandom = Math.random;
const rng = new SeededRandom(DETERMINISTIC_SEED);
// Freeze Date
const FrozenDate = class extends originalDate {
constructor(...args: ConstructorParameters<typeof Date>) {
if (args.length === 0) {
super(FROZEN_TIMESTAMP_MS);
} else {
super(...args);
}
}
static now(): number {
return FROZEN_TIMESTAMP_MS;
}
} as DateConstructor;
global.Date = FrozenDate;
Math.random = () => rng.next();
// Return cleanup function
return () => {
global.Date = originalDate;
Math.random = originalRandom;
};
}
/**
* Playwright setup helper to freeze browser time.
*/
export async function setupPlaywrightDeterministic(page: import('@playwright/test').Page): Promise<void> {
await page.addInitScript(`{
const FROZEN_TIME = ${FROZEN_TIMESTAMP_MS};
const OriginalDate = Date;
Date = class extends OriginalDate {
constructor(...args) {
if (args.length === 0) {
super(FROZEN_TIME);
} else {
super(...args);
}
}
static now() { return FROZEN_TIME; }
};
// Also freeze performance.now relative to start
const perfStart = performance.now();
const originalPerfNow = performance.now.bind(performance);
performance.now = () => originalPerfNow() - perfStart;
}`);
// Disable animations for deterministic screenshots
await page.emulateMedia({ reducedMotion: 'reduce' });
}