ci: require npm audit signature checks

Require npm registry signature verification wherever workflow npm audit checks run.

- add npm audit signatures to CI Security Scan and maintenance security audit jobs
- teach the workflow security validator to reject npm audit without signature verification
- keep the repair and Copilot prompt tests portable across Windows path/case and CRLF frontmatter behavior

Validation:
- node tests/run-all.js (2376 passed, 0 failed)
- CI current-head matrix green on #1846
This commit is contained in:
Affaan Mustafa
2026-05-12 23:48:56 -04:00
committed by GitHub
parent 766f4ee1d8
commit 797f283036
6 changed files with 44 additions and 3 deletions

View File

@@ -122,6 +122,21 @@ function run() {
assert.match(result.stderr, /id-token: write must not restore or save shared dependency caches/);
})) passed++; else failed++;
if (test('rejects npm audit without registry signature verification', () => {
const result = runValidator({
'unsafe-audit.yml': `name: Unsafe\non:\n push:\njobs:\n audit:\n runs-on: ubuntu-latest\n steps:\n - run: npm audit --audit-level=high\n`,
});
assert.notStrictEqual(result.status, 0, 'Expected validator to fail when npm audit signatures is missing');
assert.match(result.stderr, /npm audit must also verify registry signatures/);
})) passed++; else failed++;
if (test('allows npm audit when registry signatures are verified', () => {
const result = runValidator({
'safe-audit.yml': `name: Safe\non:\n push:\njobs:\n audit:\n runs-on: ubuntu-latest\n steps:\n - run: |\n npm audit signatures\n npm audit --audit-level=high\n`,
});
assert.strictEqual(result.status, 0, result.stderr || result.stdout);
})) passed++; else failed++;
console.log(`\nPassed: ${passed}`);
console.log(`Failed: ${failed}`);

View File

@@ -27,7 +27,8 @@ function read(relativePath) {
}
function parseSimpleFrontmatter(source, relativePath) {
const match = source.match(/^---\n([\s\S]*?)\n---\n/);
const normalizedSource = source.replace(/^\uFEFF/, '').replace(/\r\n/g, '\n');
const match = normalizedSource.match(/^---\n([\s\S]*?)\n---\n/);
assert.ok(match, `${relativePath} must start with YAML frontmatter`);
const fields = {};

View File

@@ -64,6 +64,16 @@ function runNode(scriptPath, args = [], options = {}) {
}
}
function normalizeComparablePath(filePath) {
const normalized = path.normalize(filePath);
return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
}
function pathListIncludes(paths, expectedPath) {
const normalizedExpected = normalizeComparablePath(expectedPath);
return paths.some(filePath => normalizeComparablePath(filePath) === normalizedExpected);
}
function test(name, fn) {
try {
fn();
@@ -117,7 +127,7 @@ function runTests() {
const parsed = JSON.parse(repairResult.stdout);
assert.strictEqual(parsed.results[0].status, 'repaired');
assert.ok(parsed.results[0].repairedPaths.includes(managedPath));
assert.ok(pathListIncludes(parsed.results[0].repairedPaths, managedPath));
assert.strictEqual(fs.readFileSync(managedPath, 'utf8'), expectedContent);
assert.ok(fs.existsSync(statePath));
} finally {