veritas-kanban/scripts/check-security-artifacts.mjs
Brad Groux 4d7c29b73a
Some checks are pending
CI / Lint & Type Check (push) Waiting to run
CI / Workspace Unit Tests (push) Waiting to run
CI / Build (push) Waiting to run
CI / Security Audit (push) Waiting to run
fix: normalize security artifact paths to lowercase before matching (#807)
Fixes case-sensitivity regression where prohibited paths like
'.VERITAS-KANBAN/security.json' would not be caught on Linux CI,
even though they alias protected paths on case-insensitive systems.

Changes:
- Normalize candidate paths to lowercase in findSecurityArtifactViolations()
- Add comprehensive test file (security-artifacts-guard.test.ts) with:
  * Unit tests for path normalization and matching
  * Mixed-case variant detection
  * NUL-delimited Git output handling
  * Integration tests with isolated temporary Git repositories
  * Edge cases: spaces, nested paths, untracked files
  * Diagnostic message validation

Security verification:
- All security-related tests pass
- Auth middleware tests pass
- Typecheck passes
- Lint budget at 600 (limit)
- Guard invocation verified against live repository

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-07-10 13:01:36 -05:00

55 lines
1.6 KiB
JavaScript

#!/usr/bin/env node
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import path from 'node:path';
const RUNTIME_SECURITY_CONFIG = /(?:^|\/)\.veritas-kanban\/security\.json$/;
export function normalizeGitPath(filePath) {
return filePath.replaceAll('\\', '/');
}
export function findSecurityArtifactViolations(paths) {
return paths
.map(normalizeGitPath)
.filter((filePath) => RUNTIME_SECURITY_CONFIG.test(filePath.toLowerCase()));
}
function listTrackedFiles() {
const result = spawnSync('git', ['ls-files', '-z', '--full-name'], {
encoding: 'utf8',
maxBuffer: 20 * 1024 * 1024,
});
if (result.error || result.status !== 0) {
const detail = result.stderr?.trim() || result.error?.message || 'unknown error';
throw new Error(`failed to list tracked files: ${detail}`);
}
return result.stdout.split('\0').filter(Boolean);
}
export function runSecurityArtifactCheck(paths = listTrackedFiles()) {
const violations = findSecurityArtifactViolations(paths);
if (violations.length > 0) {
console.error('Security artifact check failed.');
console.error('Runtime security configuration files must not be tracked:');
for (const filePath of violations) {
console.error(`- ${filePath}`);
}
process.exitCode = 1;
return false;
}
console.log(`Security artifact check passed (${paths.length} tracked files scanned).`);
return true;
}
function isDirectExecution() {
return process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
}
if (isDirectExecution()) {
runSecurityArtifactCheck();
}