mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-14 02:10:07 +08:00
ci: harden workflow install boundaries
- run non-test workflow installs with npm ci --ignore-scripts where lifecycle scripts are not needed\n- reject plain npm ci in workflows with write permissions\n- reject actions/cache in id-token: write workflows to reduce OIDC publish cache-poisoning risk
This commit is contained in:
parent
33db548be3
commit
daf0355531
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -261,7 +261,7 @@ jobs:
|
|||||||
node-version: '20.x'
|
node-version: '20.x'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm ci
|
run: npm ci --ignore-scripts
|
||||||
|
|
||||||
- name: Run ESLint
|
- name: Run ESLint
|
||||||
run: npx eslint scripts/**/*.js tests/**/*.js
|
run: npx eslint scripts/**/*.js tests/**/*.js
|
||||||
|
|||||||
2
.github/workflows/maintenance.yml
vendored
2
.github/workflows/maintenance.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
|||||||
- name: Run security audit
|
- name: Run security audit
|
||||||
run: |
|
run: |
|
||||||
if [ -f package-lock.json ]; then
|
if [ -f package-lock.json ]; then
|
||||||
npm ci
|
npm ci --ignore-scripts
|
||||||
npm audit --audit-level=high
|
npm audit --audit-level=high
|
||||||
else
|
else
|
||||||
echo "No package-lock.json found; skipping npm audit"
|
echo "No package-lock.json found; skipping npm audit"
|
||||||
|
|||||||
@ -24,6 +24,11 @@ const RULES = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const WRITE_PERMISSION_PATTERN = /^\s*(?:contents|issues|pull-requests|actions|checks|deployments|discussions|id-token|packages|pages|repository-projects|security-events|statuses):\s*write\b/m;
|
||||||
|
const NPM_CI_PATTERN = /\bnpm\s+ci\b(?![^\n]*--ignore-scripts)/g;
|
||||||
|
const ACTIONS_CACHE_PATTERN = /uses:\s*['"]?actions\/cache@/m;
|
||||||
|
const ID_TOKEN_WRITE_PATTERN = /^\s*id-token:\s*write\b/m;
|
||||||
|
|
||||||
function getWorkflowFiles(workflowsDir) {
|
function getWorkflowFiles(workflowsDir) {
|
||||||
if (!fs.existsSync(workflowsDir)) {
|
if (!fs.existsSync(workflowsDir)) {
|
||||||
return [];
|
return [];
|
||||||
@ -100,6 +105,28 @@ function findViolations(filePath, source) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (WRITE_PERMISSION_PATTERN.test(source)) {
|
||||||
|
for (const match of source.matchAll(NPM_CI_PATTERN)) {
|
||||||
|
violations.push({
|
||||||
|
filePath,
|
||||||
|
event: 'write-permission install',
|
||||||
|
description: 'workflows with write permissions must install npm dependencies with --ignore-scripts',
|
||||||
|
expression: match[0],
|
||||||
|
line: getLineNumber(source, match.index),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ID_TOKEN_WRITE_PATTERN.test(source) && ACTIONS_CACHE_PATTERN.test(source)) {
|
||||||
|
violations.push({
|
||||||
|
filePath,
|
||||||
|
event: 'id-token cache',
|
||||||
|
description: 'workflows with id-token: write must not restore or save shared dependency caches',
|
||||||
|
expression: 'id-token: write + actions/cache',
|
||||||
|
line: getLineNumber(source, source.search(ID_TOKEN_WRITE_PATTERN)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return violations;
|
return violations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -99,6 +99,29 @@ function run() {
|
|||||||
assert.match(result.stderr, /pull_request\.head\.sha/);
|
assert.match(result.stderr, /pull_request\.head\.sha/);
|
||||||
})) passed++; else failed++;
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('rejects npm ci without ignore-scripts in workflows with write permissions', () => {
|
||||||
|
const result = runValidator({
|
||||||
|
'unsafe-write-install.yml': `name: Unsafe\non:\n workflow_dispatch:\npermissions:\n contents: read\n issues: write\njobs:\n audit:\n runs-on: ubuntu-latest\n steps:\n - run: npm ci\n`,
|
||||||
|
});
|
||||||
|
assert.notStrictEqual(result.status, 0, 'Expected validator to fail on npm ci without --ignore-scripts');
|
||||||
|
assert.match(result.stderr, /write permissions must install npm dependencies with --ignore-scripts/);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('allows npm ci with ignore-scripts in workflows with write permissions', () => {
|
||||||
|
const result = runValidator({
|
||||||
|
'safe-write-install.yml': `name: Safe\non:\n workflow_dispatch:\npermissions:\n contents: read\n issues: write\njobs:\n audit:\n runs-on: ubuntu-latest\n steps:\n - run: npm ci --ignore-scripts\n`,
|
||||||
|
});
|
||||||
|
assert.strictEqual(result.status, 0, result.stderr || result.stdout);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
|
if (test('rejects actions/cache in workflows with id-token write', () => {
|
||||||
|
const result = runValidator({
|
||||||
|
'unsafe-oidc-cache.yml': `name: Unsafe\non:\n push:\npermissions:\n contents: read\n id-token: write\njobs:\n release:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/cache@v5\n with:\n path: ~/.npm\n key: cache\n`,
|
||||||
|
});
|
||||||
|
assert.notStrictEqual(result.status, 0, 'Expected validator to fail on id-token workflow cache use');
|
||||||
|
assert.match(result.stderr, /id-token: write must not restore or save shared dependency caches/);
|
||||||
|
})) passed++; else failed++;
|
||||||
|
|
||||||
console.log(`\nPassed: ${passed}`);
|
console.log(`\nPassed: ${passed}`);
|
||||||
console.log(`Failed: ${failed}`);
|
console.log(`Failed: ${failed}`);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user