mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): make release evidence replay deterministic
This commit is contained in:
@@ -1303,6 +1303,7 @@ function auditReleaseWorkflow(source) {
|
||||
sourceDerived: true,
|
||||
sameRunRecords: true,
|
||||
sameRunPrivateEvidenceReceipts: true,
|
||||
deterministicPrivateEvidenceReceipts: true,
|
||||
exactScopeClosure: true,
|
||||
standaloneInspection: true,
|
||||
tagPromotionAuthority: 'complete_verified_release_set',
|
||||
@@ -1312,7 +1313,7 @@ function auditReleaseWorkflow(source) {
|
||||
},
|
||||
durableCatalog: {
|
||||
repository: 'qinglong3-release-catalog',
|
||||
artifactType: 'application/vnd.qinglong.release-set.v2+json',
|
||||
artifactType: 'application/vnd.qinglong.release-set.v3+json',
|
||||
basenameOnly: true,
|
||||
crossRunnerDeterministic: true,
|
||||
byteExactRoundTrip: true,
|
||||
|
||||
@@ -21,7 +21,7 @@ const {
|
||||
auditCertManagerSelection,
|
||||
} = require('./ql3-cert-manager-selection-audit.cjs');
|
||||
|
||||
const RECEIPT_SCHEMA = 'qinglong/private-release-evidence-receipt@v1';
|
||||
const RECEIPT_SCHEMA = 'qinglong/private-release-evidence-receipt@v2';
|
||||
const EVIDENCE_KINDS = Object.freeze([
|
||||
'worker-management',
|
||||
'cloudnativepg-disaster-recovery',
|
||||
@@ -182,12 +182,12 @@ function assembleReceipt(options) {
|
||||
fail('private evidence report digest is invalid');
|
||||
}
|
||||
const observedAtMs = Date.parse(options.observedAt);
|
||||
const validatedAtMs = options.nowMs;
|
||||
const validationClockMs = options.nowMs;
|
||||
if (
|
||||
!Number.isSafeInteger(validatedAtMs) ||
|
||||
!Number.isSafeInteger(validationClockMs) ||
|
||||
!Number.isFinite(observedAtMs) ||
|
||||
observedAtMs > validatedAtMs + MAX_FUTURE_SKEW_MS ||
|
||||
validatedAtMs - observedAtMs > MAX_EVIDENCE_AGE_SECONDS * 1000
|
||||
observedAtMs > validationClockMs + MAX_FUTURE_SKEW_MS ||
|
||||
validationClockMs - observedAtMs > MAX_EVIDENCE_AGE_SECONDS * 1000
|
||||
) {
|
||||
fail('private evidence is outside the release freshness window');
|
||||
}
|
||||
@@ -196,7 +196,7 @@ function assembleReceipt(options) {
|
||||
? staticAuditReceipts(options.root)
|
||||
: Object.freeze([]);
|
||||
const unsigned = {
|
||||
schemaVersion: 1,
|
||||
schemaVersion: 2,
|
||||
schema: RECEIPT_SCHEMA,
|
||||
release: {
|
||||
version: options.version,
|
||||
@@ -208,7 +208,6 @@ function assembleReceipt(options) {
|
||||
evidence: {
|
||||
fixture: options.fixture,
|
||||
observedAt: options.observedAt,
|
||||
validatedAt: new Date(validatedAtMs).toISOString(),
|
||||
maximumAgeSeconds: MAX_EVIDENCE_AGE_SECONDS,
|
||||
reportDigest: options.reportDigest,
|
||||
sourceReportsUploaded: false,
|
||||
@@ -217,6 +216,8 @@ function assembleReceipt(options) {
|
||||
verification: {
|
||||
sourceAwareAudit: true,
|
||||
privateEvidenceReplayed: true,
|
||||
freshnessValidatedAtCreation: true,
|
||||
durableValidationClockPublished: false,
|
||||
publicConsumerReplay: 'not_possible_without_private_reports',
|
||||
privateReportContentPublished: false,
|
||||
compatible: true,
|
||||
@@ -323,7 +324,7 @@ function inspectPrivateReleaseEvidenceReceipt(actual, options) {
|
||||
'verification',
|
||||
'receiptDigest',
|
||||
]) ||
|
||||
actual.schemaVersion !== 1 ||
|
||||
actual.schemaVersion !== 2 ||
|
||||
actual.schema !== RECEIPT_SCHEMA ||
|
||||
!exactKeys(actual.release, [
|
||||
'version',
|
||||
@@ -342,7 +343,6 @@ function inspectPrivateReleaseEvidenceReceipt(actual, options) {
|
||||
!exactKeys(actual.evidence, [
|
||||
'fixture',
|
||||
'observedAt',
|
||||
'validatedAt',
|
||||
'maximumAgeSeconds',
|
||||
'reportDigest',
|
||||
'sourceReportsUploaded',
|
||||
@@ -363,6 +363,8 @@ function inspectPrivateReleaseEvidenceReceipt(actual, options) {
|
||||
!exactKeys(actual.verification, [
|
||||
'sourceAwareAudit',
|
||||
'privateEvidenceReplayed',
|
||||
'freshnessValidatedAtCreation',
|
||||
'durableValidationClockPublished',
|
||||
'publicConsumerReplay',
|
||||
'privateReportContentPublished',
|
||||
'compatible',
|
||||
@@ -371,6 +373,8 @@ function inspectPrivateReleaseEvidenceReceipt(actual, options) {
|
||||
JSON.stringify({
|
||||
sourceAwareAudit: true,
|
||||
privateEvidenceReplayed: true,
|
||||
freshnessValidatedAtCreation: true,
|
||||
durableValidationClockPublished: false,
|
||||
publicConsumerReplay: 'not_possible_without_private_reports',
|
||||
privateReportContentPublished: false,
|
||||
compatible: true,
|
||||
@@ -378,14 +382,7 @@ function inspectPrivateReleaseEvidenceReceipt(actual, options) {
|
||||
) {
|
||||
fail('receipt shape or release binding is invalid');
|
||||
}
|
||||
const observedAtMs = Date.parse(actual.evidence.observedAt);
|
||||
const validatedAtMs = Date.parse(actual.evidence.validatedAt);
|
||||
if (
|
||||
!Number.isFinite(observedAtMs) ||
|
||||
!Number.isFinite(validatedAtMs) ||
|
||||
observedAtMs > validatedAtMs + MAX_FUTURE_SKEW_MS ||
|
||||
validatedAtMs - observedAtMs > MAX_EVIDENCE_AGE_SECONDS * 1000
|
||||
) {
|
||||
if (!Number.isFinite(Date.parse(actual.evidence.observedAt))) {
|
||||
fail('receipt freshness binding is invalid');
|
||||
}
|
||||
const { receiptDigest, ...unsigned } = actual;
|
||||
|
||||
@@ -12,7 +12,7 @@ const {
|
||||
|
||||
const CATALOG_PLAN_SCHEMA = 'qinglong/release-catalog-plan@v1';
|
||||
const CATALOG_RECEIPT_SCHEMA = 'qinglong/release-catalog-receipt@v1';
|
||||
const ARTIFACT_TYPE = 'application/vnd.qinglong.release-set.v2+json';
|
||||
const ARTIFACT_TYPE = 'application/vnd.qinglong.release-set.v3+json';
|
||||
const FILE_MEDIA_TYPE = ARTIFACT_TYPE;
|
||||
const OCI_MANIFEST_MEDIA_TYPE = 'application/vnd.oci.image.manifest.v1+json';
|
||||
const OCI_EMPTY_CONFIG_MEDIA_TYPE = 'application/vnd.oci.empty.v1+json';
|
||||
@@ -173,7 +173,7 @@ function createCatalogPlan(releaseSet, options) {
|
||||
discoveryTagAuthority: 'none',
|
||||
immutableDigestAuthority: 'required',
|
||||
roundTrip: 'byte_exact',
|
||||
recovery: 'republish_same_content_then_verify_digest',
|
||||
recovery: 'republish_deterministic_content_then_verify_digest',
|
||||
},
|
||||
};
|
||||
return Object.freeze({
|
||||
|
||||
@@ -20,7 +20,7 @@ const { VERSION_PATTERN } = require('./lib/ql3-release-identity.cjs');
|
||||
|
||||
const DEFAULT_ROOT = path.resolve(__dirname, '..');
|
||||
const IMAGE_RECORD_SCHEMA = 'qinglong/release-set-image-record@v1';
|
||||
const RELEASE_SET_SCHEMA = 'qinglong/release-set@v2';
|
||||
const RELEASE_SET_SCHEMA = 'qinglong/release-set@v3';
|
||||
const MAX_JSON_BYTES = 1024 * 1024;
|
||||
const DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/u;
|
||||
const OWNER_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,37}[a-z0-9])?$/u;
|
||||
|
||||
Reference in New Issue
Block a user