mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const { afterEach, test } = require('node:test');
|
||||
|
||||
const {
|
||||
ImageOsVulnerabilityPolicyError,
|
||||
auditImageOsVulnerabilityPolicy,
|
||||
parseArguments,
|
||||
renderTrivyIgnore,
|
||||
runCli,
|
||||
} = require('../../scripts/ql3-image-os-vulnerability-policy.cjs');
|
||||
|
||||
const NOW = Date.parse('2026-08-01T12:00:00.000Z');
|
||||
const temporaryDirectories = [];
|
||||
|
||||
function exception(overrides = {}) {
|
||||
return {
|
||||
id: 'CVE-2026-12345',
|
||||
images: ['admin', 'control'],
|
||||
purls: ['pkg:deb/debian/libssl3@3.0.0-1'],
|
||||
owner: 'security/platform',
|
||||
ticket: 'QLSEC-123',
|
||||
expiresOn: '2026-08-15',
|
||||
rationale: 'Temporary exposure accepted while the fixed base image is qualified.',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function policy(exceptions = []) {
|
||||
return {
|
||||
schemaVersion: 1,
|
||||
fixture: 'qinglong/image-os-vulnerability-exceptions@v1',
|
||||
exceptions,
|
||||
};
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
for (const directory of temporaryDirectories.splice(0)) {
|
||||
fs.rmSync(directory, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('accepts the empty fail-closed production exception policy', () => {
|
||||
const audit = auditImageOsVulnerabilityPolicy(policy(), { now: () => NOW });
|
||||
assert.deepEqual(audit, {
|
||||
compatible: true,
|
||||
findings: [],
|
||||
exceptionCount: 0,
|
||||
imageExceptionCounts: {
|
||||
admin: 0,
|
||||
control: 0,
|
||||
'control-ai': 0,
|
||||
local: 0,
|
||||
},
|
||||
});
|
||||
assert.equal(
|
||||
renderTrivyIgnore(policy(), 'local', { now: () => NOW }),
|
||||
'vulnerabilities:\n []\n',
|
||||
);
|
||||
});
|
||||
|
||||
test('renders only one image scoped active exception with lifecycle metadata', () => {
|
||||
const document = policy([exception()]);
|
||||
const admin = renderTrivyIgnore(document, 'admin', { now: () => NOW });
|
||||
assert.match(admin, /CVE-2026-12345/);
|
||||
assert.match(admin, /pkg:deb\/debian\/libssl3@3\.0\.0-1/);
|
||||
assert.match(admin, /expired_at: 2026-08-15/);
|
||||
assert.match(admin, /owner=security\/platform; ticket=QLSEC-123/);
|
||||
assert.equal(
|
||||
renderTrivyIgnore(document, 'local', { now: () => NOW }),
|
||||
'vulnerabilities:\n []\n',
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects expired, same-day and overlong exceptions', () => {
|
||||
for (const expiresOn of ['2026-07-31', '2026-08-01', '2026-09-01']) {
|
||||
const audit = auditImageOsVulnerabilityPolicy(
|
||||
policy([exception({ expiresOn })]),
|
||||
{ now: () => NOW },
|
||||
);
|
||||
assert.equal(audit.compatible, false);
|
||||
assert.equal(
|
||||
audit.findings.some(
|
||||
(finding) =>
|
||||
finding.code === 'QL3_IMAGE_OS_VULNERABILITY_EXCEPTION_EXPIRY',
|
||||
),
|
||||
true,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('rejects missing ownership, ticket and meaningful rationale', () => {
|
||||
const audit = auditImageOsVulnerabilityPolicy(
|
||||
policy([
|
||||
exception({ owner: 'UPPER', ticket: 'none', rationale: 'temporary' }),
|
||||
]),
|
||||
{ now: () => NOW },
|
||||
);
|
||||
assert.deepEqual(
|
||||
audit.findings.map((finding) => finding.code),
|
||||
[
|
||||
'QL3_IMAGE_OS_VULNERABILITY_EXCEPTION_OWNER',
|
||||
'QL3_IMAGE_OS_VULNERABILITY_EXCEPTION_TICKET',
|
||||
'QL3_IMAGE_OS_VULNERABILITY_EXCEPTION_RATIONALE',
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects unscoped images and non-OS package purls', () => {
|
||||
const audit = auditImageOsVulnerabilityPolicy(
|
||||
policy([
|
||||
exception({
|
||||
images: ['unknown'],
|
||||
purls: ['pkg:npm/example@1.0.0'],
|
||||
}),
|
||||
]),
|
||||
{ now: () => NOW },
|
||||
);
|
||||
assert.deepEqual(
|
||||
audit.findings.map((finding) => finding.code),
|
||||
[
|
||||
'QL3_IMAGE_OS_VULNERABILITY_EXCEPTION_IMAGES',
|
||||
'QL3_IMAGE_OS_VULNERABILITY_EXCEPTION_PURLS',
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects duplicate, unsorted and extensible exception identities', () => {
|
||||
for (const exceptions of [
|
||||
[exception(), exception()],
|
||||
[
|
||||
exception({ id: 'CVE-2026-99999' }),
|
||||
exception({ id: 'CVE-2026-12345' }),
|
||||
],
|
||||
[{ ...exception(), extra: true }],
|
||||
]) {
|
||||
const audit = auditImageOsVulnerabilityPolicy(policy(exceptions), {
|
||||
now: () => NOW,
|
||||
});
|
||||
assert.equal(audit.compatible, false);
|
||||
assert.equal(
|
||||
audit.findings.some(
|
||||
(finding) =>
|
||||
finding.code === 'QL3_IMAGE_OS_VULNERABILITY_EXCEPTION_ID',
|
||||
),
|
||||
true,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('creates one private no-replace Trivy ignore file through the exact CLI', () => {
|
||||
const directory = fs.realpathSync(
|
||||
fs.mkdtempSync(path.join(os.tmpdir(), 'ql3-os-policy-')),
|
||||
);
|
||||
temporaryDirectories.push(directory);
|
||||
const policyDirectory = path.join(directory, 'deploy/containers');
|
||||
fs.mkdirSync(policyDirectory, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(policyDirectory, 'ql3-os-vulnerability-exceptions.json'),
|
||||
`${JSON.stringify(policy([exception()]))}\n`,
|
||||
);
|
||||
const output = path.join(directory, 'admin.trivyignore.yaml');
|
||||
runCli([`--image=admin`, `--output=${output}`], directory, {
|
||||
now: () => NOW,
|
||||
});
|
||||
assert.equal(fs.statSync(output).mode & 0o777, 0o600);
|
||||
assert.match(fs.readFileSync(output, 'utf8'), /QLSEC-123/);
|
||||
assert.throws(
|
||||
() =>
|
||||
runCli([`--image=admin`, `--output=${output}`], directory, {
|
||||
now: () => NOW,
|
||||
}),
|
||||
/output path must be unused/,
|
||||
);
|
||||
});
|
||||
|
||||
test('parses only audit mode or exact image/output render arguments', () => {
|
||||
assert.deepEqual(parseArguments([]), { mode: 'audit' });
|
||||
assert.equal(
|
||||
parseArguments(['--image=control', '--output=/tmp/ignore.yaml']).image,
|
||||
'control',
|
||||
);
|
||||
assert.throws(
|
||||
() => parseArguments(['--image=control']),
|
||||
ImageOsVulnerabilityPolicyError,
|
||||
);
|
||||
assert.throws(
|
||||
() => parseArguments(['--image=control', '--output=/tmp/a', '--extra=x']),
|
||||
ImageOsVulnerabilityPolicyError,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user