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,257 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const MAX_INPUT_BYTES = 16 * 1024 * 1024;
|
||||
const MAX_ADVISORIES = 10_000;
|
||||
const MAX_FINDINGS_PER_ADVISORY = 10_000;
|
||||
const MAX_PATHS_PER_FINDING = 10_000;
|
||||
const MAX_TEXT_LENGTH = 16_384;
|
||||
const BLOCKED_SEVERITIES = new Set(['high', 'critical']);
|
||||
const KNOWN_SEVERITIES = new Set([
|
||||
'info',
|
||||
'low',
|
||||
'moderate',
|
||||
'high',
|
||||
'critical',
|
||||
]);
|
||||
const PROFILE_IMPORTERS = Object.freeze([
|
||||
'packages/ql3-runtime-core',
|
||||
'packages/ql3-local-command-file',
|
||||
'packages/ql3-local-sqlite',
|
||||
'packages/ql3-local-secret',
|
||||
'packages/ql3-local-owner-console',
|
||||
'packages/ql3-local-owner-cli',
|
||||
'packages/ql3-local-owner-maintenance',
|
||||
'packages/ql3-local-admin',
|
||||
'packages/ql3-local-application',
|
||||
'packages/ql3-local-execution',
|
||||
'packages/ql3-local-process',
|
||||
'packages/ql3-cluster-postgres',
|
||||
'packages/ql3-cluster-control',
|
||||
'packages/ql3-cluster-admin',
|
||||
'packages/ql3-worker-runtime',
|
||||
]);
|
||||
|
||||
class ProfileVulnerabilityAuditError extends Error {
|
||||
constructor(message) {
|
||||
super(`QingLong profile vulnerability audit failed: ${message}`);
|
||||
this.name = 'ProfileVulnerabilityAuditError';
|
||||
}
|
||||
}
|
||||
|
||||
function boundedText(value, name) {
|
||||
if (
|
||||
typeof value !== 'string' ||
|
||||
value.length < 1 ||
|
||||
value.length > MAX_TEXT_LENGTH ||
|
||||
/[\0\r\n]/.test(value)
|
||||
) {
|
||||
throw new ProfileVulnerabilityAuditError(`${name} is invalid`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function boundedArray(value, maximum, name) {
|
||||
if (!Array.isArray(value) || value.length > maximum) {
|
||||
throw new ProfileVulnerabilityAuditError(`${name} is invalid`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function emptySummary() {
|
||||
return {
|
||||
info: new Set(),
|
||||
low: new Set(),
|
||||
moderate: new Set(),
|
||||
high: new Set(),
|
||||
critical: new Set(),
|
||||
modules: new Set(),
|
||||
pathCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
function publicSummary(summary) {
|
||||
return Object.freeze({
|
||||
advisories: Object.freeze({
|
||||
info: summary.info.size,
|
||||
low: summary.low.size,
|
||||
moderate: summary.moderate.size,
|
||||
high: summary.high.size,
|
||||
critical: summary.critical.size,
|
||||
}),
|
||||
modules: Object.freeze([...summary.modules].sort()),
|
||||
pathCount: summary.pathCount,
|
||||
compatible: summary.high.size === 0 && summary.critical.size === 0,
|
||||
});
|
||||
}
|
||||
|
||||
function importerFromPath(dependencyPath) {
|
||||
const separator = dependencyPath.indexOf(' > ');
|
||||
return separator === -1 ? dependencyPath : dependencyPath.slice(0, separator);
|
||||
}
|
||||
|
||||
function advisoryIdentity(advisory) {
|
||||
const value = advisory.github_advisory_id ?? advisory.id;
|
||||
if (
|
||||
(typeof value !== 'string' && typeof value !== 'number') ||
|
||||
String(value).length > 128
|
||||
) {
|
||||
throw new ProfileVulnerabilityAuditError('advisory identity is invalid');
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function auditProfileVulnerabilities(audit) {
|
||||
if (!audit || typeof audit !== 'object' || Array.isArray(audit)) {
|
||||
throw new ProfileVulnerabilityAuditError('audit document is invalid');
|
||||
}
|
||||
if (
|
||||
!audit.advisories ||
|
||||
typeof audit.advisories !== 'object' ||
|
||||
Array.isArray(audit.advisories)
|
||||
) {
|
||||
throw new ProfileVulnerabilityAuditError('advisories are unavailable');
|
||||
}
|
||||
const advisories = Object.values(audit.advisories);
|
||||
if (advisories.length > MAX_ADVISORIES) {
|
||||
throw new ProfileVulnerabilityAuditError('advisory budget exceeded');
|
||||
}
|
||||
|
||||
const summaries = new Map([
|
||||
['.', emptySummary()],
|
||||
...PROFILE_IMPORTERS.map((importer) => [importer, emptySummary()]),
|
||||
]);
|
||||
const findings = [];
|
||||
const findingKeys = new Set();
|
||||
const unknownImporters = new Set();
|
||||
|
||||
for (const advisory of advisories) {
|
||||
if (!advisory || typeof advisory !== 'object' || Array.isArray(advisory)) {
|
||||
throw new ProfileVulnerabilityAuditError('advisory is invalid');
|
||||
}
|
||||
const advisoryId = advisoryIdentity(advisory);
|
||||
const moduleName = boundedText(advisory.module_name, 'module name');
|
||||
const severity = boundedText(advisory.severity, 'severity');
|
||||
if (!KNOWN_SEVERITIES.has(severity)) {
|
||||
throw new ProfileVulnerabilityAuditError('severity is unsupported');
|
||||
}
|
||||
const advisoryFindings = boundedArray(
|
||||
advisory.findings,
|
||||
MAX_FINDINGS_PER_ADVISORY,
|
||||
'advisory findings',
|
||||
);
|
||||
for (const finding of advisoryFindings) {
|
||||
if (!finding || typeof finding !== 'object' || Array.isArray(finding)) {
|
||||
throw new ProfileVulnerabilityAuditError('finding is invalid');
|
||||
}
|
||||
const version = boundedText(finding.version, 'finding version');
|
||||
const paths = boundedArray(
|
||||
finding.paths,
|
||||
MAX_PATHS_PER_FINDING,
|
||||
'finding paths',
|
||||
);
|
||||
for (const dependencyPathValue of paths) {
|
||||
const dependencyPath = boundedText(
|
||||
dependencyPathValue,
|
||||
'dependency path',
|
||||
);
|
||||
const importer = importerFromPath(dependencyPath);
|
||||
const summary = summaries.get(importer);
|
||||
if (summary) {
|
||||
summary[severity].add(advisoryId);
|
||||
summary.modules.add(moduleName);
|
||||
summary.pathCount += 1;
|
||||
} else {
|
||||
unknownImporters.add(importer);
|
||||
}
|
||||
if (
|
||||
BLOCKED_SEVERITIES.has(severity) &&
|
||||
(importer !== '.' || !summary)
|
||||
) {
|
||||
const code = summary
|
||||
? 'PROFILE_HIGH_CRITICAL_ADVISORY'
|
||||
: 'UNREVIEWED_IMPORTER_HIGH_CRITICAL_ADVISORY';
|
||||
const findingKey = [
|
||||
code,
|
||||
importer,
|
||||
advisoryId,
|
||||
moduleName,
|
||||
version,
|
||||
].join('\0');
|
||||
if (!findingKeys.has(findingKey)) {
|
||||
findingKeys.add(findingKey);
|
||||
findings.push({
|
||||
code,
|
||||
importer,
|
||||
advisoryId,
|
||||
moduleName,
|
||||
severity,
|
||||
version,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findings.sort(
|
||||
(left, right) =>
|
||||
left.importer.localeCompare(right.importer) ||
|
||||
left.moduleName.localeCompare(right.moduleName) ||
|
||||
left.advisoryId.localeCompare(right.advisoryId),
|
||||
);
|
||||
const profileImporters = Object.fromEntries(
|
||||
PROFILE_IMPORTERS.map((importer) => [
|
||||
importer,
|
||||
publicSummary(summaries.get(importer)),
|
||||
]),
|
||||
);
|
||||
const legacyRoot = publicSummary(summaries.get('.'));
|
||||
return Object.freeze({
|
||||
schemaVersion: 1,
|
||||
threshold: 'high',
|
||||
profileImporters: Object.freeze(profileImporters),
|
||||
legacyRoot,
|
||||
unknownImporters: Object.freeze([...unknownImporters].sort()),
|
||||
findings: Object.freeze(findings),
|
||||
compatible: findings.length === 0,
|
||||
});
|
||||
}
|
||||
|
||||
function main() {
|
||||
let input = '';
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.on('data', (chunk) => {
|
||||
input += chunk;
|
||||
if (Buffer.byteLength(input) > MAX_INPUT_BYTES) {
|
||||
process.stdin.destroy(
|
||||
new ProfileVulnerabilityAuditError('input budget exceeded'),
|
||||
);
|
||||
}
|
||||
});
|
||||
process.stdin.on('end', () => {
|
||||
try {
|
||||
const report = auditProfileVulnerabilities(JSON.parse(input));
|
||||
process.stdout.write(`${JSON.stringify(report)}\n`);
|
||||
if (!report.compatible) process.exitCode = 1;
|
||||
} catch (error) {
|
||||
process.stderr.write(
|
||||
`${error instanceof Error ? error.message : String(error)}\n`,
|
||||
);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
});
|
||||
process.stdin.on('error', (error) => {
|
||||
process.stderr.write(
|
||||
`${error instanceof Error ? error.message : String(error)}\n`,
|
||||
);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
PROFILE_IMPORTERS,
|
||||
ProfileVulnerabilityAuditError,
|
||||
auditProfileVulnerabilities,
|
||||
};
|
||||
|
||||
if (require.main === module) main();
|
||||
Reference in New Issue
Block a user