chore: COD-28 harden dependencies and public assets

This commit is contained in:
Aamer Akhter
2026-06-08 09:56:06 -04:00
parent 44439c951b
commit eb874339dd
8 changed files with 5240 additions and 3599 deletions
+3865 -2694
View File
File diff suppressed because it is too large Load Diff
+25 -10
View File
@@ -21,8 +21,9 @@
"typecheck": "tsc --noEmit",
"lint": "eslint --config config/eslint.config.js 'src/**/*.ts'",
"lint:fix": "eslint --config config/eslint.config.js 'src/**/*.ts' --fix",
"format": "prettier --write 'src/**/*.ts'",
"format:check": "prettier --check 'src/**/*.ts'",
"format": "prettier --write 'src/**/*.ts' 'src/web/public/**/*.{js,css,html,json}'",
"format:check": "prettier --check 'src/**/*.ts' 'src/web/public/**/*.{js,css,html,json}'",
"check:public-assets": "node scripts/check-public-assets.mjs",
"capture:subagents": "node scripts/capture-subagent-screenshots.mjs",
"changeset": "changeset",
"version-packages": "changeset version && npm install --package-lock-only && node scripts/check-lockfile-sync.mjs",
@@ -53,7 +54,7 @@
"@fastify/compress": "^8.3.1",
"@fastify/cookie": "^11.0.2",
"@fastify/multipart": "^10.0.0",
"@fastify/static": "^8.0.0",
"@fastify/static": "^9.1.3",
"@fastify/websocket": "^11.2.0",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-unicode11": "^0.9.0",
@@ -62,18 +63,18 @@
"chalk": "^5.3.0",
"chokidar": "^3.6.0",
"commander": "^12.1.0",
"fastify": "^5.1.0",
"fastify": "^5.8.5",
"node-pty": "^1.1.0",
"qrcode": "^1.5.4",
"uuid": "^10.0.0",
"uuid": "^14.0.0",
"web-push": "^3.6.7",
"zod": "^4.3.6"
},
"devDependencies": {
"@changesets/cli": "^2.29.8",
"@eslint/js": "^9.0.0",
"@remotion/cli": "4.0.429",
"@remotion/transitions": "4.0.429",
"@remotion/cli": "4.0.473",
"@remotion/transitions": "4.0.473",
"@types/node": "^20.19.33",
"@types/pngjs": "^6.0.5",
"@types/qrcode": "^1.5.6",
@@ -81,7 +82,7 @@
"@types/uuid": "^10.0.0",
"@types/web-push": "^3.6.4",
"@types/ws": "^8.18.1",
"@vitest/coverage-v8": "^4.0.18",
"@vitest/coverage-v8": "^4.1.8",
"agent-browser": "^0.6.0",
"esbuild": "^0.27.3",
"eslint": "^9.0.0",
@@ -90,16 +91,30 @@
"pngjs": "^7.0.0",
"prettier": "^3.4.0",
"puppeteer": "^24.36.0",
"remotion": "4.0.429",
"remotion": "4.0.473",
"tsx": "^4.15.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.0.0",
"vitest": "^4.0.18"
"vitest": "^4.1.8"
},
"optionalDependencies": {
"@remotion/compositor-linux-x64-gnu": "^4.0.432",
"@rspack/binding-linux-x64-gnu": "^1.7.7"
},
"overrides": {
"basic-ftp": "^5.3.1",
"fast-uri": "^3.1.2",
"flatted": "^3.4.2",
"anymatch": {
"picomatch": "^2.3.2"
},
"micromatch": {
"picomatch": "^2.3.2"
},
"readdirp": {
"picomatch": "^2.3.2"
}
},
"engines": {
"node": ">=18.0.0"
},
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -45,6 +45,6 @@
"jsdom": "^24.1.3",
"tsup": "^8.5.1",
"typescript": "^5.5.0",
"vitest": "^2.1.9"
"vitest": "^4.1.8"
}
}
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env node
import { execFileSync } from 'node:child_process';
import { readdirSync, readFileSync } from 'node:fs';
import { dirname, extname, join, relative, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const publicRoot = resolve(repoRoot, 'src/web/public');
const prettierBin = resolve(repoRoot, 'node_modules/.bin/prettier');
const checkedExtensions = new Set(['.js', '.css', '.html', '.json']);
function collectTextAssets(dir) {
const files = [];
for (const entry of readdirSync(dir, { withFileTypes: true })) {
const fullPath = join(dir, entry.name);
if (entry.isDirectory()) {
files.push(...collectTextAssets(fullPath));
continue;
}
if (checkedExtensions.has(extname(entry.name))) {
files.push(fullPath);
}
}
return files;
}
function findNullByte(buffer) {
for (let i = 0; i < buffer.length; i += 1) {
if (buffer[i] === 0) return i;
}
return -1;
}
const files = collectTextAssets(publicRoot);
const failures = [];
for (const file of files) {
const rel = relative(repoRoot, file);
const data = readFileSync(file);
const nullByteIndex = findNullByte(data);
if (nullByteIndex !== -1) {
failures.push(`${rel}: contains literal NUL byte at offset ${nullByteIndex}`);
}
if (extname(file) === '.js') {
try {
execFileSync(process.execPath, ['--check', file], { cwd: repoRoot, stdio: 'pipe' });
} catch (err) {
failures.push(`${rel}: JavaScript syntax check failed\n${String(err.stderr || err.message).trim()}`);
}
}
}
try {
execFileSync(prettierBin, ['--check', ...files], { cwd: repoRoot, stdio: 'pipe' });
} catch (err) {
failures.push(`Prettier public asset check failed\n${String(err.stdout || err.stderr || err.message).trim()}`);
}
if (failures.length > 0) {
console.error(failures.join('\n\n'));
process.exit(1);
}
console.log(`Public asset checks passed (${files.length} files).`);
+3 -3
View File
@@ -1089,7 +1089,7 @@ class CodemanApp {
const placeholders = [];
const masked = text.replace(fenceRe, (m) => {
placeholders.push(m);
return `FENCE${placeholders.length - 1}`;
return `__CODEMAN_FENCE_${placeholders.length - 1}__`;
});
// Split on blank-line paragraph boundaries; wrap any paragraph containing
@@ -1099,13 +1099,13 @@ class CodemanApp {
.map((chunk) => {
if (/^\n{2,}$/.test(chunk)) return chunk; // keep separators
if (!chunk.trim()) return chunk;
if (chunk.includes('FENCE')) return chunk;
if (chunk.includes('__CODEMAN_FENCE_')) return chunk;
if (BOX_PATTERN.test(chunk)) return '\n```\n' + chunk + '\n```\n';
return chunk;
})
.join('');
return processed.replace(/FENCE(\d+)/g, (_m, i) => placeholders[Number(i)]);
return processed.replace(/__CODEMAN_FENCE_(\d+)__/g, (_m, i) => placeholders[Number(i)]);
}
/** Render markdown to sanitized HTML, falling back to plain text if marked.js unavailable */
+154
View File
@@ -0,0 +1,154 @@
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { describe, expect, it } from 'vitest';
const root = resolve(import.meta.dirname, '..');
type PackageLockPackage = {
version?: string;
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
};
type PackageLock = {
packages: Record<string, PackageLockPackage>;
};
function readJson<T>(relativePath: string): T {
return JSON.parse(readFileSync(resolve(root, relativePath), 'utf8')) as T;
}
function compareVersions(actual: string, expected: string): number {
const actualParts = actual.split('.').map((part) => Number(part.replace(/\D.*/, '')) || 0);
const expectedParts = expected.split('.').map((part) => Number(part.replace(/\D.*/, '')) || 0);
for (let i = 0; i < Math.max(actualParts.length, expectedParts.length); i++) {
const left = actualParts[i] ?? 0;
const right = expectedParts[i] ?? 0;
if (left > right) return 1;
if (left < right) return -1;
}
return 0;
}
function packageNameFromLockPath(lockPath: string): string | null {
const parts = lockPath.split('node_modules/');
if (parts.length < 2) return null;
return parts[parts.length - 1] ?? null;
}
function lockedVersions(lock: PackageLock, packageName: string): string[] {
const versions = new Set<string>();
for (const [lockPath, pkg] of Object.entries(lock.packages)) {
if (packageNameFromLockPath(lockPath) === packageName && pkg.version) {
versions.add(pkg.version);
}
}
return [...versions].sort();
}
function expectEveryLockedVersionAtLeast(lock: PackageLock, packageName: string, minimum: string): void {
const versions = lockedVersions(lock, packageName);
expect(versions, `${packageName} should be present in package-lock.json`).not.toHaveLength(0);
for (const version of versions) {
expect(
compareVersions(version, minimum),
`${packageName}@${version} should be >= ${minimum}`
).toBeGreaterThanOrEqual(0);
}
}
function expectNoVulnerableVite(lock: PackageLock): void {
const versions = lockedVersions(lock, 'vite');
expect(versions, 'vite should be present in package-lock.json').not.toHaveLength(0);
for (const version of versions) {
const major = Number(version.split('.')[0]);
if (major === 6) {
expect(compareVersions(version, '6.4.2'), `vite@${version} should be >= 6.4.2`).toBeGreaterThanOrEqual(0);
} else if (major === 7) {
expect(compareVersions(version, '7.3.2'), `vite@${version} should be >= 7.3.2`).toBeGreaterThanOrEqual(0);
} else {
expect(major, `vite@${version} should be on a supported patched major`).toBeGreaterThanOrEqual(8);
}
}
}
function expectNoVulnerablePicomatch(lock: PackageLock): void {
const versions = lockedVersions(lock, 'picomatch');
expect(versions, 'picomatch should be present in package-lock.json').not.toHaveLength(0);
for (const version of versions) {
const major = Number(version.split('.')[0]);
if (major === 2) {
expect(compareVersions(version, '2.3.2'), `picomatch@${version} should be >= 2.3.2`).toBeGreaterThanOrEqual(0);
} else if (major === 4) {
expect(compareVersions(version, '4.0.4'), `picomatch@${version} should be >= 4.0.4`).toBeGreaterThanOrEqual(0);
}
}
}
function expectNoVulnerableBraceExpansion(lock: PackageLock): void {
const versions = lockedVersions(lock, 'brace-expansion');
expect(versions, 'brace-expansion should be present in package-lock.json').not.toHaveLength(0);
for (const version of versions) {
const major = Number(version.split('.')[0]);
if (major === 1) {
expect(
compareVersions(version, '1.1.13'),
`brace-expansion@${version} should be >= 1.1.13`
).toBeGreaterThanOrEqual(0);
} else if (major === 4) {
expect(
compareVersions(version, '5.0.5'),
`brace-expansion@${version} should not remain on vulnerable 4.x`
).toBeGreaterThanOrEqual(0);
} else if (major === 5) {
expect(compareVersions(version, '5.0.6'), `brace-expansion@${version} should be >= 5.0.6`).toBeGreaterThanOrEqual(
0
);
}
}
}
describe('dependency security policy', () => {
it('keeps direct security-sensitive dependency ranges on patched versions', () => {
const rootPackage = readJson<PackageLockPackage>('package.json');
const xtermPackage = readJson<PackageLockPackage>('packages/xterm-zerolag-input/package.json');
expect(rootPackage.dependencies?.['@fastify/static']).toBe('^9.1.3');
expect(rootPackage.dependencies?.fastify).toBe('^5.8.5');
expect(rootPackage.dependencies?.uuid).toBe('^14.0.0');
expect(rootPackage.devDependencies?.['@remotion/cli']).toBe('4.0.473');
expect(rootPackage.devDependencies?.remotion).toBe('4.0.473');
expect(rootPackage.devDependencies?.['@remotion/transitions']).toBe('4.0.473');
expect(rootPackage.devDependencies?.vitest).toBe('^4.1.8');
expect(rootPackage.devDependencies?.['@vitest/coverage-v8']).toBe('^4.1.8');
expect(xtermPackage.devDependencies?.vitest).toBe('^4.1.8');
});
it('keeps critical and high audit findings resolved in the lockfile', () => {
const lock = readJson<PackageLock>('package-lock.json');
expectEveryLockedVersionAtLeast(lock, 'vitest', '4.1.0');
expectEveryLockedVersionAtLeast(lock, '@vitest/coverage-v8', '4.1.0');
expectEveryLockedVersionAtLeast(lock, 'fastify', '5.8.5');
expectEveryLockedVersionAtLeast(lock, '@fastify/static', '9.1.3');
expectEveryLockedVersionAtLeast(lock, 'ip-address', '10.2.0');
expectEveryLockedVersionAtLeast(lock, 'uuid', '14.0.0');
expectEveryLockedVersionAtLeast(lock, 'ws', '8.20.1');
expectEveryLockedVersionAtLeast(lock, 'fast-uri', '3.1.2');
expectEveryLockedVersionAtLeast(lock, 'basic-ftp', '5.3.1');
expectEveryLockedVersionAtLeast(lock, 'flatted', '3.4.2');
expectNoVulnerableBraceExpansion(lock);
expectNoVulnerableVite(lock);
expectNoVulnerablePicomatch(lock);
});
it('keeps standalone workspace lockfiles on patched test tooling', () => {
const lock = readJson<PackageLock>('packages/xterm-zerolag-input/package-lock.json');
expect(lock.packages['']?.devDependencies?.vitest).toBe('^4.1.8');
expectEveryLockedVersionAtLeast(lock, 'vitest', '4.1.0');
expectEveryLockedVersionAtLeast(lock, 'ws', '8.20.1');
expectNoVulnerableVite(lock);
expectNoVulnerablePicomatch(lock);
});
});
+31
View File
@@ -0,0 +1,31 @@
import { execFileSync } from 'node:child_process';
import { readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { describe, expect, it } from 'vitest';
const repoRoot = resolve(import.meta.dirname, '..');
describe('frontend public asset tooling', () => {
it('exposes a public asset check script', () => {
const pkg = JSON.parse(readFileSync(resolve(repoRoot, 'package.json'), 'utf8')) as {
scripts?: Record<string, string>;
};
expect(pkg.scripts?.['check:public-assets']).toContain('scripts/check-public-assets.mjs');
});
it('keeps app.js free of literal NUL bytes', () => {
const appJs = readFileSync(resolve(repoRoot, 'src/web/public/app.js'));
expect(appJs.includes(0)).toBe(false);
});
it('runs the public asset check script', () => {
expect(() => {
execFileSync('npm', ['run', 'check:public-assets', '--silent'], {
cwd: repoRoot,
stdio: 'pipe',
});
}).not.toThrow();
});
});