42 lines
1.7 KiB
JavaScript
42 lines
1.7 KiB
JavaScript
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');
|
|
});
|
|
});
|