mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
254 lines
7.1 KiB
JavaScript
254 lines
7.1 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const test = require('node:test');
|
|
const {
|
|
auditClusterImageSbom,
|
|
componentRef,
|
|
createClusterImageSbom,
|
|
} = require('../../scripts/ql3-cluster-image-sbom.cjs');
|
|
|
|
const root = path.resolve(__dirname, '../..');
|
|
|
|
function clone(value) {
|
|
return JSON.parse(JSON.stringify(value));
|
|
}
|
|
|
|
test('generates the exact reviewed cluster image runtime closure', () => {
|
|
const document = createClusterImageSbom({ root });
|
|
const report = auditClusterImageSbom(document, { root });
|
|
|
|
assert.deepEqual(report, {
|
|
image: 'control',
|
|
root: 'pkg:npm/%40qinglong/cluster-control-image-dependencies@3.0.0-alpha.0',
|
|
components: 46,
|
|
externalComponents: 43,
|
|
internalComponents: 3,
|
|
dependencyNodes: 47,
|
|
inventoryVerified: false,
|
|
});
|
|
assert.equal(
|
|
document.components.some((component) => component.name === 'pg-protocol'),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
document.components.some((component) => component.name === 'typescript'),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test('generates the optional Cluster AI image runtime closure', () => {
|
|
const document = createClusterImageSbom({ root, image: 'control-ai' });
|
|
const report = auditClusterImageSbom(document, {
|
|
root,
|
|
image: 'control-ai',
|
|
});
|
|
|
|
assert.deepEqual(report, {
|
|
image: 'control-ai',
|
|
root: 'pkg:npm/%40qinglong/cluster-control-image-dependencies@3.0.0-alpha.0',
|
|
components: 47,
|
|
externalComponents: 43,
|
|
internalComponents: 4,
|
|
dependencyNodes: 48,
|
|
inventoryVerified: false,
|
|
});
|
|
assert.equal(
|
|
document.components.some((component) => component.name === '@qinglong/ai'),
|
|
true,
|
|
);
|
|
});
|
|
|
|
test('generates the independent reviewed cluster-admin image closure', () => {
|
|
const document = createClusterImageSbom({ root, image: 'admin' });
|
|
const report = auditClusterImageSbom(document, {
|
|
root,
|
|
image: 'admin',
|
|
});
|
|
|
|
assert.deepEqual(report, {
|
|
image: 'admin',
|
|
root: 'pkg:npm/%40qinglong/cluster-admin-image-dependencies@3.0.0-alpha.0',
|
|
components: 91,
|
|
externalComponents: 87,
|
|
internalComponents: 4,
|
|
dependencyNodes: 92,
|
|
inventoryVerified: false,
|
|
});
|
|
assert.equal(
|
|
document.components.some(
|
|
(component) => component.name === '@kubernetes/client-node',
|
|
),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
document.components.some(
|
|
(component) => component.name === '@modelcontextprotocol/server',
|
|
),
|
|
true,
|
|
);
|
|
assert.equal(
|
|
document.components.some(
|
|
(component) => component.name === '@aws-sdk/client-s3',
|
|
),
|
|
false,
|
|
);
|
|
});
|
|
|
|
test('generates the AI-excluded local application image closure', () => {
|
|
const document = createClusterImageSbom({ root, image: 'local' });
|
|
const report = auditClusterImageSbom(document, {
|
|
root,
|
|
image: 'local',
|
|
});
|
|
|
|
assert.deepEqual(report, {
|
|
image: 'local',
|
|
root: 'pkg:npm/%40qinglong/local-application-image@3.0.0-alpha.0',
|
|
components: 10,
|
|
externalComponents: 2,
|
|
internalComponents: 8,
|
|
dependencyNodes: 11,
|
|
inventoryVerified: false,
|
|
});
|
|
assert.deepEqual(
|
|
document.components
|
|
.filter((component) =>
|
|
['@qinglong/ai', 'drizzle-orm', 'typescript'].includes(component.name),
|
|
)
|
|
.map((component) => component.name),
|
|
[],
|
|
);
|
|
});
|
|
|
|
test('rejects a control SBOM presented as cluster-admin evidence', () => {
|
|
const document = createClusterImageSbom({ root });
|
|
assert.throws(
|
|
() =>
|
|
auditClusterImageSbom(document, {
|
|
root,
|
|
image: 'admin',
|
|
}),
|
|
/selected image profile|image manifest/,
|
|
);
|
|
});
|
|
|
|
test('rejects widened metadata and root component drift', () => {
|
|
const widened = createClusterImageSbom({ root });
|
|
widened.metadata.generatedBy = 'unreviewed';
|
|
assert.throws(
|
|
() => auditClusterImageSbom(widened, { root }),
|
|
/root component/,
|
|
);
|
|
|
|
const drifted = createClusterImageSbom({ root });
|
|
drifted.metadata.component.purl = 'pkg:npm/unrelated@1.0.0';
|
|
assert.throws(
|
|
() => auditClusterImageSbom(drifted, { root }),
|
|
/root component/,
|
|
);
|
|
});
|
|
|
|
test('rejects a missing internal dependency edge', () => {
|
|
const document = createClusterImageSbom({ root });
|
|
const controlRef = componentRef('@qinglong/cluster-control', '3.0.0-alpha.0');
|
|
const edge = document.dependencies.find((entry) => entry.ref === controlRef);
|
|
edge.dependsOn = edge.dependsOn.slice(1);
|
|
|
|
assert.throws(
|
|
() => auditClusterImageSbom(document, { root }),
|
|
/dependency edges.*reviewed runtime closure/,
|
|
);
|
|
});
|
|
|
|
test('rejects an unexpected development component', () => {
|
|
const document = createClusterImageSbom({ root });
|
|
document.components.push({
|
|
type: 'library',
|
|
'bom-ref': componentRef('typescript', '5.9.3'),
|
|
name: 'typescript',
|
|
version: '5.9.3',
|
|
purl: componentRef('typescript', '5.9.3'),
|
|
});
|
|
|
|
assert.throws(
|
|
() => auditClusterImageSbom(document, { root }),
|
|
/development component leaked/,
|
|
);
|
|
});
|
|
|
|
test('rejects tampered locked component metadata', () => {
|
|
const document = createClusterImageSbom({ root });
|
|
const pg = document.components.find((component) => component.name === 'pg');
|
|
pg.version = '8.21.0';
|
|
|
|
assert.throws(
|
|
() => auditClusterImageSbom(document, { root }),
|
|
/component metadata differs/,
|
|
);
|
|
});
|
|
|
|
test('rejects a missing or unreviewed runtime license', () => {
|
|
const missing = createClusterImageSbom({ root, image: 'local' });
|
|
delete missing.components[0].licenses;
|
|
assert.throws(
|
|
() => auditClusterImageSbom(missing, { root, image: 'local' }),
|
|
/unreviewed license/,
|
|
);
|
|
|
|
const unreviewed = createClusterImageSbom({ root, image: 'local' });
|
|
unreviewed.components[0].licenses = [{ license: { id: 'GPL-3.0-only' } }];
|
|
assert.throws(
|
|
() => auditClusterImageSbom(unreviewed, { root, image: 'local' }),
|
|
/unreviewed license/,
|
|
);
|
|
});
|
|
|
|
test('verifies a bounded package inventory against the SBOM', (t) => {
|
|
const document = createClusterImageSbom({ root });
|
|
const temporaryRoot = fs.mkdtempSync(
|
|
path.join(os.tmpdir(), 'ql3-image-inventory-'),
|
|
);
|
|
t.after(() => fs.rmSync(temporaryRoot, { recursive: true, force: true }));
|
|
|
|
for (const component of document.components) {
|
|
const packageDirectory = component.name.startsWith('@')
|
|
? path.join(temporaryRoot, ...component.name.split('/'))
|
|
: path.join(temporaryRoot, component.name);
|
|
fs.mkdirSync(packageDirectory, { recursive: true });
|
|
fs.writeFileSync(
|
|
path.join(packageDirectory, 'package.json'),
|
|
JSON.stringify({
|
|
name: component.name,
|
|
version: component.version,
|
|
}),
|
|
);
|
|
}
|
|
|
|
assert.equal(
|
|
auditClusterImageSbom(document, {
|
|
root,
|
|
inventoryRoot: temporaryRoot,
|
|
}).inventoryVerified,
|
|
true,
|
|
);
|
|
|
|
const unexpected = path.join(temporaryRoot, 'unexpected');
|
|
fs.mkdirSync(unexpected);
|
|
fs.writeFileSync(
|
|
path.join(unexpected, 'package.json'),
|
|
JSON.stringify({ name: 'unexpected', version: '1.0.0' }),
|
|
);
|
|
assert.throws(
|
|
() =>
|
|
auditClusterImageSbom(clone(document), {
|
|
root,
|
|
inventoryRoot: temporaryRoot,
|
|
}),
|
|
/runtime image package inventory differs/,
|
|
);
|
|
});
|