This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
|
||||
import { parse } from 'yaml';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const workflowPath = new URL('../../.gitea/workflows/ci.yml', import.meta.url);
|
||||
|
||||
describe('CI workflow semantic security policy', () => {
|
||||
it('pins actions, minimizes permissions, and leaves no checkout credential', async () => {
|
||||
const source = await readFile(workflowPath, 'utf8');
|
||||
const workflow = parse(source);
|
||||
|
||||
expect(workflow.permissions).toEqual({ contents: 'read' });
|
||||
expect(workflow.on).toHaveProperty('pull_request');
|
||||
expect(source).not.toContain('${{ secrets.');
|
||||
|
||||
for (const job of Object.values(workflow.jobs)) {
|
||||
expect(job.permissions ?? workflow.permissions).toEqual({ contents: 'read' });
|
||||
for (const step of job.steps ?? []) {
|
||||
if (!step.uses) continue;
|
||||
expect(step.uses).toMatch(/^[^@\s]+@[a-f0-9]{40}$/);
|
||||
expect(step.uses).not.toMatch(/(?:^|\/)cache(?:\/(?:restore|save))?@/);
|
||||
expect(step.with ?? {}).not.toHaveProperty('cache');
|
||||
if (step.uses.startsWith('actions/checkout@')) {
|
||||
expect(step.with?.['persist-credentials']).toBe(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('does not invoke repository package hooks before policy or native rebuild', async () => {
|
||||
const source = await readFile(workflowPath, 'utf8');
|
||||
|
||||
expect(source).toContain(
|
||||
'node scripts/security/dependency-policy.mjs package-lock.json ui/package-lock.json',
|
||||
);
|
||||
expect(source).toContain('node scripts/security/rebuild-native.mjs');
|
||||
expect(source).not.toContain('npm run security:dependency-policy');
|
||||
expect(source).not.toContain('npm run ci:rebuild-native');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user