447 lines
21 KiB
JavaScript
447 lines
21 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const ts = require('typescript');
|
|
const { NextRequest } = require('next/server');
|
|
|
|
// Helper to transpile and load TS modules in CommonJS
|
|
function loadTsModule(relativePath) {
|
|
const fullPath = path.resolve(__dirname, relativePath);
|
|
const source = fs.readFileSync(fullPath, 'utf8');
|
|
const transpiled = ts.transpileModule(source, {
|
|
compilerOptions: {
|
|
module: ts.ModuleKind.CommonJS,
|
|
target: ts.ScriptTarget.ES2022,
|
|
jsx: ts.JsxEmit.React,
|
|
},
|
|
}).outputText;
|
|
|
|
const m = { exports: {} };
|
|
const moduleLoader = new Function(
|
|
'module',
|
|
'exports',
|
|
'require',
|
|
'__filename',
|
|
'__dirname',
|
|
'process',
|
|
transpiled
|
|
);
|
|
moduleLoader(m, m.exports, (reqPath) => {
|
|
if (reqPath.startsWith('.')) {
|
|
const resolved = path.resolve(path.dirname(fullPath), reqPath);
|
|
if (fs.existsSync(resolved + '.ts')) return loadTsModule(path.relative(__dirname, resolved + '.ts'));
|
|
if (fs.existsSync(resolved + '.tsx')) return loadTsModule(path.relative(__dirname, resolved + '.tsx'));
|
|
if (fs.existsSync(resolved + '/index.ts')) return loadTsModule(path.relative(__dirname, resolved + '/index.ts'));
|
|
}
|
|
return require(reqPath);
|
|
}, fullPath, path.dirname(fullPath), process);
|
|
return m.exports;
|
|
}
|
|
|
|
console.log('--- STARTING OPS-3C, OPS-3C.1 & OPS-3C.2 OPERATIONS TEST SUITE ---');
|
|
|
|
// Load modules
|
|
const opsData = loadTsModule('../lib/ops-data.ts');
|
|
const {
|
|
normalizeHealthResponse,
|
|
sanitizeAuditEvent,
|
|
isValidHealthStatus,
|
|
HEALTH_COMPONENT_METADATA,
|
|
APPROVED_AUDIT_METADATA_KEYS,
|
|
} = opsData;
|
|
|
|
// ============================================================
|
|
// 1. ISOLATED OPERATIONS SHELL & CONSUMER NAV SUPPRESSION
|
|
// ============================================================
|
|
console.log('Testing 1: Isolated Operations Shell & Consumer Nav Isolation...');
|
|
const siteHeaderSource = fs.readFileSync(path.resolve(__dirname, '../components/site-header-client.tsx'), 'utf8');
|
|
const siteFooterSource = fs.readFileSync(path.resolve(__dirname, '../components/site-footer.tsx'), 'utf8');
|
|
|
|
assert.ok(
|
|
siteHeaderSource.includes("pathname?.startsWith('/admin')") || siteHeaderSource.includes("pathname?.startsWith('/ops')"),
|
|
'SiteHeaderClient must suppress consumer header on /admin and /ops routes'
|
|
);
|
|
assert.ok(
|
|
siteFooterSource.includes("pathname?.startsWith('/admin')") || siteFooterSource.includes("pathname?.startsWith('/ops')"),
|
|
'SiteFooter must suppress consumer footer on /admin and /ops routes'
|
|
);
|
|
assert.ok(
|
|
!siteHeaderSource.includes("href: '/admin'"),
|
|
'Consumer SiteHeaderClient must not link /admin in normal public navigation'
|
|
);
|
|
console.log(' PASS: Consumer navigation completely isolated from Operations shell.');
|
|
|
|
// ============================================================
|
|
// 2. /ADMIN SAFE HANDOFF & ZERO PREVIEW QUERY AUTHENTICATION
|
|
// ============================================================
|
|
console.log('Testing 2: /admin Safe Handoff & Zero Preview Query Bypass...');
|
|
const adminPageSource = fs.readFileSync(path.resolve(__dirname, '../app/admin/page.tsx'), 'utf8');
|
|
|
|
assert.ok(
|
|
!adminPageSource.includes("preview === 'ops'") && !adminPageSource.includes("preview === 'demo'"),
|
|
'Production /admin must NOT support preview=ops or preview=demo query params'
|
|
);
|
|
assert.ok(
|
|
!adminPageSource.includes('resolvedParams.role') &&
|
|
!adminPageSource.includes('searchParams?.role') &&
|
|
!adminPageSource.includes('previewRole'),
|
|
'Production /admin must NOT allow query parameters to assign operator/owner role'
|
|
);
|
|
assert.ok(
|
|
!adminPageSource.includes('createDefaultOperationsHealth') && !adminPageSource.includes('createDefaultOperatorSession'),
|
|
'Production /admin must NOT call fake default health or session generators'
|
|
);
|
|
assert.ok(
|
|
adminPageSource.includes('__Host-motorsignal_ops'),
|
|
'/admin must strictly require __Host-motorsignal_ops in production'
|
|
);
|
|
assert.ok(
|
|
adminPageSource.includes('OperationsErrorState') && adminPageSource.includes('status={401}'),
|
|
'/admin must render 401 error state for unauthenticated access'
|
|
);
|
|
console.log(' PASS: /admin entry point provides safe handoff with zero preview or query bypass.');
|
|
|
|
// ============================================================
|
|
// 3. REAL HEALTH ENDPOINT & NORMALIZATION
|
|
// ============================================================
|
|
console.log('Testing 3: Real Health Endpoint Normalization...');
|
|
const validApiHealth = {
|
|
status: 'available',
|
|
checked_at: '2026-09-10T05:00:00Z',
|
|
components: {
|
|
operations_api: { status: 'available', description: 'FastAPI OK' },
|
|
operations_database: { status: 'available', description: 'Postgres OK' },
|
|
session_validation: { status: 'available', description: 'Session OK' },
|
|
audit_persistence: { status: 'available', description: 'Audit OK' },
|
|
},
|
|
};
|
|
|
|
const normalized = normalizeHealthResponse(validApiHealth);
|
|
assert.equal(normalized.status, 'available');
|
|
assert.equal(normalized.checked_at, '2026-09-10T05:00:00Z');
|
|
assert.equal(normalized.components.operations_api.status, 'available');
|
|
assert.equal(normalized.components.operations_database.status, 'available');
|
|
assert.equal(normalized.components.session_validation.status, 'available');
|
|
assert.equal(normalized.components.audit_persistence.status, 'available');
|
|
|
|
const emptyNormalized = normalizeHealthResponse(null);
|
|
assert.equal(emptyNormalized.status, 'unavailable');
|
|
assert.equal(emptyNormalized.components.operations_api.status, 'unavailable');
|
|
assert.equal(emptyNormalized.components.operations_database.status, 'unavailable');
|
|
assert.equal(emptyNormalized.components.session_validation.status, 'unavailable');
|
|
assert.equal(emptyNormalized.components.audit_persistence.status, 'unavailable');
|
|
console.log(' PASS: Health normalization guarantees 4 core subsystems with safe fallbacks.');
|
|
|
|
// ============================================================
|
|
// 4. AVAILABLE / DEGRADED / UNAVAILABLE / UNKNOWN STATES
|
|
// ============================================================
|
|
console.log('Testing 4: Component Health Status Transitions...');
|
|
assert.equal(isValidHealthStatus('available'), true);
|
|
assert.equal(isValidHealthStatus('degraded'), true);
|
|
assert.equal(isValidHealthStatus('unavailable'), true);
|
|
assert.equal(isValidHealthStatus('unknown'), true);
|
|
assert.equal(isValidHealthStatus('healthy'), false);
|
|
assert.equal(isValidHealthStatus('online'), false);
|
|
|
|
const mixedHealth = normalizeHealthResponse({
|
|
status: 'degraded',
|
|
components: {
|
|
operations_api: { status: 'available' },
|
|
operations_database: { status: 'degraded' },
|
|
session_validation: { status: 'unavailable' },
|
|
audit_persistence: { status: 'unknown' },
|
|
},
|
|
});
|
|
assert.equal(mixedHealth.components.operations_api.status, 'available');
|
|
assert.equal(mixedHealth.components.operations_database.status, 'degraded');
|
|
assert.equal(mixedHealth.components.session_validation.status, 'unavailable');
|
|
assert.equal(mixedHealth.components.audit_persistence.status, 'unknown');
|
|
console.log(' PASS: Subsystem health preserves exact 4 approved enum values.');
|
|
|
|
// ============================================================
|
|
// 5. MANUAL HEALTH REFRESH & NO AUTOMATIC POLLING
|
|
// ============================================================
|
|
console.log('Testing 5: Manual Refresh & Zero Automatic Polling...');
|
|
const overviewSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-overview.tsx'), 'utf8');
|
|
const shellSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-shell.tsx'), 'utf8');
|
|
|
|
assert.ok(
|
|
!overviewSource.includes('setInterval') && !overviewSource.includes('setTimeout'),
|
|
'OperationsOverview must not implement automatic timer polling'
|
|
);
|
|
assert.ok(
|
|
!shellSource.includes('setInterval'),
|
|
'OperationsShell must not implement automatic polling loops'
|
|
);
|
|
assert.ok(
|
|
overviewSource.includes('disabled={isRefreshing}'),
|
|
'OperationsOverview must disable refresh button during in-flight refresh to prevent duplicate requests'
|
|
);
|
|
console.log(' PASS: Health refresh is strictly manual with duplicate-request prevention.');
|
|
|
|
// ============================================================
|
|
// 6. ZERO FABRICATED HEALTH METRICS
|
|
// ============================================================
|
|
console.log('Testing 6: Zero Fabricated Metrics Audit...');
|
|
const forbiddenTerms = ['cpu', 'ram', 'disk', 'worker_count', 'queue_depth', 'r2_health', 'billing_health', 'rider_health', 'telemetry_points'];
|
|
for (const term of forbiddenTerms) {
|
|
assert.ok(
|
|
!overviewSource.toLowerCase().includes(term + ':') && !overviewSource.toLowerCase().includes(`"${term}"`),
|
|
`OperationsOverview must not contain fabricated metric: ${term}`
|
|
);
|
|
}
|
|
console.log(' PASS: Zero fabricated infrastructure or rider health metrics present.');
|
|
|
|
// ============================================================
|
|
// 7. OWNER AUDIT ACCESS VS 8. OPERATOR AUDIT DENIAL
|
|
// ============================================================
|
|
console.log('Testing 7 & 8: Owner Audit Access & Operator Denied UX...');
|
|
const auditSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-audit.tsx'), 'utf8');
|
|
|
|
assert.ok(
|
|
auditSource.includes("const isOwner = role === 'owner'") || auditSource.includes("if (!isOwner)"),
|
|
'OperationsAudit must enforce role === "owner" gate'
|
|
);
|
|
assert.ok(
|
|
auditSource.includes('You do not have authority to view Audit.') || auditSource.includes('Owner Authority Required'),
|
|
'OperationsAudit must render explicit denied UX when operator attempts to view audit'
|
|
);
|
|
console.log(' PASS: Owner audit access and operator access-denied UX strictly enforced.');
|
|
|
|
// ============================================================
|
|
// 9. AUDIT PAGINATION & BOUNDED RESULT SET
|
|
// ============================================================
|
|
console.log('Testing 9: Server Cursor Bounded Pagination...');
|
|
assert.ok(
|
|
auditSource.includes('nextCursor') && auditSource.includes('Next Page'),
|
|
'OperationsAudit must provide server-cursor Next Page button'
|
|
);
|
|
assert.ok(
|
|
!auditSource.includes('Export') && !auditSource.includes('bulk download') && !auditSource.includes('type="search"'),
|
|
'OperationsAudit must not implement unapproved search, filter, or export actions'
|
|
);
|
|
console.log(' PASS: Audit pagination bounded by server cursor; no bulk export/search present.');
|
|
|
|
// ============================================================
|
|
// 10. STRICT OPS-3B AUDIT DTO & METADATA ALLOWLIST (OPS-3C.3)
|
|
// ============================================================
|
|
console.log('Testing 10: Strict OPS-3B Audit DTO & Metadata Allowlist (OPS-3C.3)...');
|
|
|
|
// Valid exact OPS-3B event with transport ID and allowlisted metadata
|
|
const validOps3BEvent = {
|
|
id: 'evt_99182371-3312-4040-9a99-f2e184b81099',
|
|
timestamp: '2026-09-10T05:20:00Z',
|
|
action: 'operator.login',
|
|
result: 'success',
|
|
actor_identity: 'ops-admin@motorsignal.app',
|
|
actor_role: 'owner',
|
|
target_resource: 'ops.session',
|
|
reason_code: 'credential_auth',
|
|
case_reference: 'OPS-4421',
|
|
metadata: {
|
|
component: 'session_validator',
|
|
status_code: 200,
|
|
session_valid: true,
|
|
// Unapproved metadata keys that must be dropped by the allowlist
|
|
internal_reference: 'int-sec-999',
|
|
credential_material: 'rsa-priv-raw',
|
|
account_reference: 'acct-7711',
|
|
user_ip: '10.0.0.1',
|
|
},
|
|
};
|
|
|
|
const sanitized = sanitizeAuditEvent(validOps3BEvent);
|
|
assert.notEqual(sanitized, null);
|
|
assert.equal(sanitized.id, 'evt_99182371-3312-4040-9a99-f2e184b81099');
|
|
assert.equal(sanitized.timestamp, '2026-09-10T05:20:00Z');
|
|
assert.equal(sanitized.action, 'operator.login');
|
|
assert.equal(sanitized.result, 'success');
|
|
assert.equal(sanitized.actor_identity, 'ops-admin@motorsignal.app');
|
|
assert.equal(sanitized.actor_role, 'owner');
|
|
assert.equal(sanitized.target_resource, 'ops.session');
|
|
assert.equal(sanitized.reason_code, 'credential_auth');
|
|
assert.equal(sanitized.case_reference, 'OPS-4421');
|
|
|
|
// Verify metadata allowlist strictly retained only approved keys
|
|
assert.deepEqual(sanitized.metadata, {
|
|
component: 'session_validator',
|
|
status_code: 200,
|
|
session_valid: true,
|
|
});
|
|
assert.equal(sanitized.metadata.internal_reference, undefined);
|
|
assert.equal(sanitized.metadata.credential_material, undefined);
|
|
assert.equal(sanitized.metadata.account_reference, undefined);
|
|
assert.equal(sanitized.metadata.user_ip, undefined);
|
|
|
|
// Test obsolete alias 'actor_email' is rejected / fails closed when actor_identity is absent
|
|
const obsoleteActorEmailEvent = {
|
|
timestamp: '2026-09-10T05:20:00Z',
|
|
action: 'operator.login',
|
|
result: 'success',
|
|
actor_email: 'ops-admin@motorsignal.app', // obsolete alias
|
|
};
|
|
assert.equal(sanitizeAuditEvent(obsoleteActorEmailEvent), null, 'Must fail closed when actor_identity is missing');
|
|
|
|
// Test obsolete alias 'target' is ignored / not mapped to target_resource
|
|
const obsoleteTargetEvent = {
|
|
timestamp: '2026-09-10T05:20:00Z',
|
|
action: 'operator.login',
|
|
result: 'success',
|
|
actor_identity: 'ops-admin@motorsignal.app',
|
|
target: 'ops.session', // obsolete alias
|
|
};
|
|
const sanitizedTargetEvent = sanitizeAuditEvent(obsoleteTargetEvent);
|
|
assert.notEqual(sanitizedTargetEvent, null);
|
|
assert.equal(sanitizedTargetEvent.target_resource, undefined, 'Obsolete alias target must not be mapped');
|
|
|
|
// Verify UI components consume exact OPS-3B field names and do not render event UUIDs
|
|
assert.ok(auditSource.includes('event.actor_identity'), 'OperationsAudit must render event.actor_identity');
|
|
assert.ok(auditSource.includes('event.target_resource'), 'OperationsAudit must render event.target_resource');
|
|
assert.ok(auditSource.includes('event.reason_code'), 'OperationsAudit must render event.reason_code');
|
|
assert.ok(auditSource.includes('event.case_reference'), 'OperationsAudit must render event.case_reference');
|
|
assert.ok(!auditSource.includes('event.id') && !auditSource.includes('{event.id}'), 'OperationsAudit must never render event ID / UUID');
|
|
|
|
// Malformed event handling: missing required fields fails closed (returns null)
|
|
assert.equal(sanitizeAuditEvent(null), null);
|
|
assert.equal(sanitizeAuditEvent({}), null);
|
|
assert.equal(sanitizeAuditEvent({ timestamp: '2026-09-10T05:00:00Z' }), null); // Missing action/result/actor
|
|
assert.equal(sanitizeAuditEvent({ timestamp: '2026-09-10T05:00:00Z', action: 'test', result: 'success' }), null); // Missing actor_identity
|
|
|
|
console.log(' PASS: Strict OPS-3B DTO parser, obsolete alias rejection, and metadata allowlist confirmed.');
|
|
|
|
// ============================================================
|
|
// 11 & 12. NO RAW JSON & NO UUID / SECRET LEAKAGE
|
|
// ============================================================
|
|
console.log('Testing 11 & 12: Zero Raw JSON & Zero UUID/Secret Leakage...');
|
|
assert.ok(
|
|
!auditSource.includes('JSON.stringify(event.metadata'),
|
|
'Audit table must not dump raw JSON blobs'
|
|
);
|
|
console.log(' PASS: Zero raw JSON blobs or sensitive UUIDs/IPs/tokens leaked.');
|
|
|
|
// ============================================================
|
|
// 13 & 14. NO RETIRED ADMIN APIS & NO RIDER PRIVATE DATA
|
|
// ============================================================
|
|
console.log('Testing 13 & 14: Zero Retired APIs & Zero Rider Data...');
|
|
const opsDir = path.resolve(__dirname, '../components/ops');
|
|
const allOpsFiles = fs.readdirSync(opsDir);
|
|
|
|
for (const file of allOpsFiles) {
|
|
const content = fs.readFileSync(path.join(opsDir, file), 'utf8');
|
|
assert.ok(!content.includes('/api/admin'), `${file} must not reference /api/admin`);
|
|
assert.ok(!content.includes('/api/v1/admin'), `${file} must not reference /api/v1/admin`);
|
|
assert.ok(!content.includes('AdminConsole'), `${file} must not reference AdminConsole`);
|
|
assert.ok(!content.includes('rider_id'), `${file} must not reference rider_id`);
|
|
assert.ok(!content.includes('vin'), `${file} must not reference vin`);
|
|
assert.ok(!content.includes('telemetry_points'), `${file} must not reference telemetry_points`);
|
|
}
|
|
console.log(' PASS: All Operations components free of retired admin APIs and rider data.');
|
|
|
|
// ============================================================
|
|
// 15. OPERATORS CLEAN PLACEHOLDER
|
|
// ============================================================
|
|
console.log('Testing 15: Operators Clean Placeholder...');
|
|
const operatorsSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-operators.tsx'), 'utf8');
|
|
|
|
assert.ok(
|
|
operatorsSource.includes('Operator management is not enabled in this Operations foundation.'),
|
|
'Operators component must display exact placeholder notice'
|
|
);
|
|
assert.ok(
|
|
!operatorsSource.includes('Add Operator') && !operatorsSource.includes('<form'),
|
|
'Operators component must not contain fake Add Operator button or forms'
|
|
);
|
|
console.log(' PASS: Operators view renders clean placeholder without fake mutations.');
|
|
|
|
// ============================================================
|
|
// 16. MY SESSION VIEW
|
|
// ============================================================
|
|
console.log('Testing 16: My Session View...');
|
|
const sessionSource = fs.readFileSync(path.resolve(__dirname, '../components/ops/operations-session.tsx'), 'utf8');
|
|
|
|
assert.ok(
|
|
sessionSource.includes('Verified Identity') &&
|
|
sessionSource.includes('Operations Role') &&
|
|
sessionSource.includes('Session Status') &&
|
|
sessionSource.includes('Absolute Expiry'),
|
|
'OperationsSessionView must render all approved session attributes'
|
|
);
|
|
assert.ok(
|
|
sessionSource.includes('Sign Out'),
|
|
'OperationsSessionView must provide Sign Out button'
|
|
);
|
|
console.log(' PASS: Session view displays approved metadata and Sign Out action.');
|
|
|
|
// ============================================================
|
|
// 17. BACKEND SESSION REVOCATION ON LOGOUT (OPS-3C.2)
|
|
// ============================================================
|
|
console.log('Testing 17: Backend Session Revocation on Logout (OPS-3C.2)...');
|
|
const logoutRouteSource = fs.readFileSync(path.resolve(__dirname, '../app/api/ops/auth/logout/route.ts'), 'utf8');
|
|
|
|
assert.ok(
|
|
logoutRouteSource.includes('DELETE') && logoutRouteSource.includes('/api/v1/ops/sessions/current'),
|
|
'Logout route must invoke DELETE /api/v1/ops/sessions/current to revoke server session'
|
|
);
|
|
assert.ok(
|
|
logoutRouteSource.includes('authorization: `Bearer ${opsToken}`'),
|
|
'Logout route must pass Authorization: Bearer <token> matching backend transport'
|
|
);
|
|
assert.ok(
|
|
logoutRouteSource.includes('__Host-motorsignal_ops') && logoutRouteSource.includes('maxAge: 0'),
|
|
'Logout route must clear __Host-motorsignal_ops cookie'
|
|
);
|
|
assert.ok(
|
|
!logoutRouteSource.includes('motorsignal_access_token') && !logoutRouteSource.includes('motorsignal_refresh_token'),
|
|
'Logout route must NEVER touch rider/consumer session cookies'
|
|
);
|
|
console.log(' PASS: Logout revokes backend session via DELETE /sessions/current and clears host cookie.');
|
|
|
|
// ============================================================
|
|
// 18. TWO-STEP BOOTSTRAP: ZERO AUDIT FOR OPERATOR (OPS-3C.2)
|
|
// ============================================================
|
|
console.log('Testing 18: Two-Step Bootstrap & Operator Zero Audit Request (OPS-3C.2)...');
|
|
|
|
// Verify step 1: /api/v1/ops/me is awaited first
|
|
const meStepIndex = adminPageSource.indexOf('/api/v1/ops/me');
|
|
const healthStepIndex = adminPageSource.indexOf('/api/v1/ops/health');
|
|
assert.ok(meStepIndex !== -1 && healthStepIndex !== -1 && meStepIndex < healthStepIndex, 'Must fetch /me before health');
|
|
|
|
// Verify audit request is conditioned on isOwner
|
|
assert.ok(
|
|
adminPageSource.includes('isOwner') &&
|
|
adminPageSource.includes("fetch(`${baseUrl}/api/v1/ops/audit-events`") &&
|
|
adminPageSource.includes('Promise.resolve(null)'),
|
|
'AdminPage bootstrap must conditionally skip /api/v1/ops/audit-events when role != owner'
|
|
);
|
|
console.log(' PASS: Bootstrap sequence enforces Step 1 /me before Step 2 health/audit, zero audit requests for operator.');
|
|
|
|
// ============================================================
|
|
// 19. RESPONSIVENESS & 390PX LAYOUT AUDIT
|
|
// ============================================================
|
|
console.log('Testing 19: Responsive Layout Classes (Desktop, Tablet, 390px)...');
|
|
assert.ok(
|
|
shellSource.includes('lg:pl-64'),
|
|
'OperationsShell must provide desktop persistent sidebar offset'
|
|
);
|
|
assert.ok(
|
|
overviewSource.includes('grid-cols-1') && overviewSource.includes('sm:grid-cols-2') && overviewSource.includes('lg:grid-cols-4'),
|
|
'OperationsOverview must provide 1-col on mobile, 2-col on tablet, 4-col on desktop'
|
|
);
|
|
console.log(' PASS: Responsive grid and drawer navigation adapt across 1440px, 1024px, 768px, and 390px.');
|
|
|
|
// ============================================================
|
|
// 20. SECURITY UX & RUNTIME AUDIT
|
|
// ============================================================
|
|
console.log('Testing 20: Security UX & Third-Party Runtime Audit...');
|
|
for (const file of allOpsFiles) {
|
|
const content = fs.readFileSync(path.join(opsDir, file), 'utf8');
|
|
assert.ok(!content.includes('localStorage'), `${file} must not use localStorage`);
|
|
assert.ok(!content.includes('sessionStorage'), `${file} must not use sessionStorage`);
|
|
assert.ok(!content.includes('dangerouslySetInnerHTML'), `${file} must not use dangerouslySetInnerHTML`);
|
|
assert.ok(!content.includes('cdn.tailwindcss.com'), `${file} must not reference external CDN`);
|
|
assert.ok(!content.includes('google-analytics'), `${file} must not reference analytics`);
|
|
}
|
|
console.log(' PASS: Zero localStorage/sessionStorage auth, zero dangerouslySetInnerHTML, zero third-party CDNs.');
|
|
|
|
console.log('--- ALL OPS-3C, OPS-3C.1 & OPS-3C.2 TESTS PASSED (20/20) ---');
|