// Read-only release inventory and bounded probes. No writes to materials or storage. import { createHash } from 'node:crypto' import { readFile } from 'node:fs/promises' import { createRequire } from 'node:module' import path from 'node:path' import { fileURLToPath } from 'node:url' import { checkMaterialPreviewHealth, directFetch } from './check-material-preview-health.mjs' import { collectMaterialPreviewInventory } from './export-material-preview-health-manifest.mjs' import { LEGACY_DOMAINS } from './check-legacy-storage-boundaries.mjs' export const POLICY = Object.freeze({ version: 1, concurrency: 4, probes: 768, bytes: 128 * 1024 * 1024, wallMs: 360_000 }) export const CRITICAL_MATERIAL_IDS = Object.freeze([ '6e763f52-a21f-4b4e-99fb-634478f68eed', '82f3a6b7-6323-4cf5-a741-d990182f382c', 'd3a0addb-b4f0-4110-958d-3f897490fa4c', '72a39203-7f61-43b5-b243-73fe19ffde99', 'a6b97e8a-bd68-483f-b8f8-743e8baef850', '0329debe-054d-4ee7-9411-59f7cb2cdcc1', 'b9a9947c-ef68-4616-9144-a0d1fd912799', 'ac19c78a-c09f-4ed9-a3c3-12bf9c950a56', 'ea4cb1d2-fd25-46cb-aeba-93508c0793f8', '6c2675ab-c495-4c63-8d63-24ddee9e6535', 'c9abdd91-a666-4f36-a475-6e082906a94e', 'd0e929f7-1186-4eac-acef-3af74a9567ee', ]) export const DELIVERY_FILES = Object.freeze(['lib/url-rewrite.ts', 'lib/proxy-media-security.ts', 'app/api/proxy-media/route.ts', 'lib/s3.ts', 'lib/network-detection.ts']) export const KNOWN_FRP_FAILURE = Object.freeze({ bytes: 489, bodySha256: '967e369d3be4fab5eb1229ec015b2923abf6bc81ebe3aaa662cb095cbec81023' }) export const KNOWN_COMPATIBILITY_PROOF = Object.freeze({ materialId: '6e763f52-a21f-4b4e-99fb-634478f68eed', bodySha256: 'b40687cac0cbd537e209f8caa56c642bf501a658d0c3585140e8b9950937da53', width: 768, height: 1600 }) export const KNOWN_LEGACY_MEMBERS_SHA256 = '953e6b1c9e10ea05e30e289ebbd5364569d918b5dd4a1273af52abc531fe475c' export const DELIVERY_CONFIG_KEYS = Object.freeze(['S3_ENDPOINT', 'S3_BUCKET', 'S3_FILE_PATH', 'S3_PUBLIC_DOMAIN', 'S3_REGION', 'SERVER_IP', 'MINIO_API_PORT', 'APP_PUBLIC_URL']) // bundleReleaseGate replaces this literal with hashes from the exact build checkout. const PINNED_TARGET_DELIVERY_CODE = null const sha = value => createHash('sha256').update(value).digest('hex') const digest = value => sha(JSON.stringify(value)) const runtimeRequire = createRequire(import.meta.url) export class GateError extends Error { constructor(code) { super(code); this.code = code } } const fail = code => { throw new GateError(code) } const sourceKey = row => `${row.materialId}:${row.sourceSha256}` const probeKey = row => `${row.materialId}:${row.role}:${row.sourceSha256}` async function readKnownLegacyProof(root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')) { const policy = JSON.parse(await readFile(path.join(root, 'scripts/fixtures/material-preview-known-legacy-20261023.json'), 'utf8')) if (policy?.version !== 1 || policy.reviewedAuditSha256 !== '40eb8d834d5c6bd9edcdc49e69ae227a83be2374bb9b525a0cec0bcd652c4e0d' || policy.members?.length !== 472 || new Set(policy.members.map(row => row.materialId)).size !== 472 || digest(policy.members) !== KNOWN_LEGACY_MEMBERS_SHA256 || policy.membersSha256 !== KNOWN_LEGACY_MEMBERS_SHA256) fail('reviewed_legacy_membership_proof_changed') return policy.members } // The exact-checkout bundler inlines this reviewed ID/hash-only proof. const PINNED_KNOWN_LEGACY_MEMBERS = await readKnownLegacyProof() export function normalizeDeliveryConfig(raw) { if (!raw || Object.keys(raw).sort().join('|') !== [...DELIVERY_CONFIG_KEYS].sort().join('|')) fail('complete_delivery_configuration_required') const values = Object.fromEntries(DELIVERY_CONFIG_KEYS.map(key => { if (typeof raw[key] !== 'string' || raw[key].length > 2048 || /[\x00-\x1f\x7f]/.test(raw[key])) fail('invalid_delivery_configuration') return [key, raw[key].trim()] })) for (const key of ['S3_ENDPOINT', 'S3_PUBLIC_DOMAIN', 'APP_PUBLIC_URL']) { let url try { url = new URL(values[key]) } catch { fail('invalid_delivery_configuration_url') } if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.search || url.hash) fail('credential_or_query_in_delivery_configuration') values[key] = url.href.replace(/\/$/, '') } if (!/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(values.S3_BUCKET) || values.S3_BUCKET.includes('..')) fail('invalid_delivery_bucket') if (values.SERVER_IP && !/^[A-Za-z0-9.:\[\]-]+$/.test(values.SERVER_IP)) fail('invalid_delivery_server') values.MINIO_API_PORT ||= '9000' if (!/^\d{1,5}$/.test(values.MINIO_API_PORT) || Number(values.MINIO_API_PORT) < 1 || Number(values.MINIO_API_PORT) > 65535) fail('invalid_delivery_port') values.S3_REGION ||= 'us-east-1' return values } export function canonicalDeliverySource(source, publicDomain) { const current = new URL(publicDomain) if (current.href.replace(/\/$/, '') !== 'https://s3.jiansun.vip/gongxiangpan') fail('untrusted_public_bucket_configuration') const url = new URL(source) // Independent historical bucket is deliberately never mapped to the current bucket. if (url.hostname === LEGACY_DOMAINS[0] && url.pathname.startsWith('/jsxiangche/')) return url.href if (!url.pathname.startsWith('/gongxiangpan/')) return url.href url.protocol = current.protocol; url.hostname = current.hostname; url.port = '' return url.href } export function inventoryIndex(inventory) { const { audit, manifests } = inventory if (audit?.inventoryComplete !== true || audit.activeCount !== audit.exportedCount + audit.uncheckableCount || manifests.reduce((n, batch) => n + batch.materials.length, 0) !== audit.exportedCount) fail('inventory_coverage_incomplete') const index = [ ...manifests.flatMap(batch => batch.materials.map(row => ({ materialId: row.materialId, sourceSha256: sha(row.registeredSource), registeredSource: row.registeredSource }))), ...audit.uncheckable.map(row => ({ materialId: row.materialId, sourceSha256: row.sourceSha256, uncheckable: { classification: row.classification, code: row.code } })), ].sort((a, b) => a.materialId.localeCompare(b.materialId, 'en')) if (index.length !== audit.activeCount || new Set(index.map(row => row.materialId)).size !== index.length) fail('inventory_coverage_incomplete') return index } function validateIndex(index) { if (!Array.isArray(index) || new Set(index.map(row => row.materialId)).size !== index.length || index.some(row => typeof row.materialId !== 'string' || !/^[a-f0-9]{64}$/.test(row.sourceSha256) || (row.registeredSource ? sha(row.registeredSource) !== row.sourceSha256 : !row.uncheckable))) fail('invalid_inventory_index') } export function buildPlan(index, prior, { publicDomain, criticalIds = CRITICAL_MATERIAL_IDS, expandedScope = false, bootstrap = false } = {}) { validateIndex(index) if (!bootstrap) validateAccepted(prior) const previous = new Map((prior?.inventory || []).map(row => [row.materialId, row])) const required = new Set(criticalIds), selected = [], unverified = [], changedUncheckable = [] for (const row of index) { const old = previous.get(row.materialId) const changed = !old || sourceKey(old) !== sourceKey(row) if (!row.registeredSource) { if (!bootstrap && changed) changedUncheckable.push(row.materialId) unverified.push({ materialId: row.materialId, reason: 'unsupported_or_missing_registered_source' }) continue } const source = new URL(row.registeredSource) const independentLegacy = source.hostname === LEGACY_DOMAINS[0] && source.pathname.startsWith('/jsxiangche/') const reasons = [...(independentLegacy ? ['independent_legacy_storage'] : []), ...(required.has(row.materialId) ? ['critical_material'] : []), ...(!bootstrap && changed ? ['new_or_changed_source'] : []), ...(expandedScope ? ['delivery_dependency_changed'] : [])] if (!reasons.length) { unverified.push({ materialId: row.materialId, reason: 'unchanged_current_source_outside_bounded_scope' }); continue } const delivery = canonicalDeliverySource(row.registeredSource, publicDomain) selected.push({ materialId: row.materialId, registeredSource: row.registeredSource, sourceSha256: row.sourceSha256, reasons, deliverySource: delivery, independentLegacy }) } const present = new Set(selected.map(row => row.materialId)) if ([...required].some(id => !present.has(id))) fail('critical_material_coverage_missing') if (changedUncheckable.length) fail('new_or_changed_uncheckable_preview') const probes = selected.flatMap(row => [ { materialId: row.materialId, role: 'registered', registeredSource: row.registeredSource, sourceSha256: row.sourceSha256 }, ...(row.deliverySource === row.registeredSource ? [] : [{ materialId: row.materialId, role: 'canonical', registeredSource: row.deliverySource, sourceSha256: sha(row.deliverySource) }]), ]) if (!probes.length || probes.length > POLICY.probes) fail('probe_budget_requires_independent_full_acceptance') return { selected, probes, unverified, expandedScope, publicDomain, criticalIds: [...criticalIds] } } function failureSignature(result) { return digest([result.classification, result.code, result.status, result.contentType, result.bytes, result.bodySha256 ?? null]) } function validateChecks(plan, checks) { if (!Array.isArray(checks) || checks.length !== plan.probes.length) fail('probe_coverage_incomplete') const expected = new Set(plan.probes.map(probeKey)), seen = new Set() for (const check of checks) { const key = probeKey(check) if (!expected.has(key) || seen.has(key) || check.result?.source?.sha256 !== check.sourceSha256 || check.result.attempts !== 1 || check.result.materialId !== check.materialId || typeof check.result.ok !== 'boolean') fail('probe_identity_or_coverage_mismatch') if (check.result.ok && (!/^[a-f0-9]{64}$/.test(check.result.bodySha256) || check.result.classification !== 'healthy' || check.result.status < 200 || check.result.status >= 300 || !Number.isSafeInteger(check.result.decoded?.width) || !Number.isSafeInteger(check.result.decoded?.height) || check.result.decoded.width < 1 || check.result.decoded.height < 1 || check.result.decoded.width * check.result.decoded.height > 25_000_000)) fail('undecoded_healthy_probe') seen.add(key) } } export function validateAccepted(artifact) { if (artifact?.version !== 1 || artifact.policyVersion !== POLICY.version || artifact.passed !== true || !['bootstrap', 'post'].includes(artifact.stage) || !/^[a-f0-9]{64}$/.test(artifact.inventorySha256)) fail('reviewed_baseline_required') validateIndex(artifact.inventory) if (digest(normalizeDeliveryConfig(artifact.deliveryConfig)) !== artifact.deliveryConfigSha256 || artifact.knownLegacyMembersSha256 !== KNOWN_LEGACY_MEMBERS_SHA256) fail('baseline_delivery_or_legacy_proof_mismatch') if (digest(artifact.inventory) !== artifact.inventorySha256 || digest(artifact.plan) !== artifact.planSha256) fail('baseline_digest_mismatch') validateChecks(artifact.plan, artifact.checks) if (digest(artifact.checks) !== artifact.checksSha256) fail('baseline_checks_digest_mismatch') } export function compareChecks(plan, checks, baseline, { bootstrap = false, legacyFailureProof = KNOWN_FRP_FAILURE, compatibilityProof = KNOWN_COMPATIBILITY_PROOF, legacyMembers = PINNED_KNOWN_LEGACY_MEMBERS } = {}) { validateChecks(plan, checks) const known = new Map((baseline?.checks || []).map(row => [probeKey(row), row.result])) const checksMap = new Map(checks.map(row => [probeKey(row), row.result])) const legacyIdentities = new Set(legacyMembers.map(sourceKey)) const problems = [], knownFailures = [] for (const check of checks) { const row = plan.selected.find(item => item.materialId === check.materialId) const old = known.get(probeKey(check)), result = check.result if (['decoder_unavailable', 'invalid_manifest', 'source_rejected'].includes(result.classification)) { problems.push({ materialId: check.materialId, role: check.role, code: 'detector_or_source_validation_failure' }); continue } if (result.ok) { if (old?.ok && result.bodySha256 !== old.bodySha256) problems.push({ materialId: check.materialId, role: check.role, code: 'unchanged_url_content_changed' }) continue } // Bootstrap is explicitly installed by a human after reviewing its exact artifact SHA. // It may document the incident's known FRP misses, never arbitrary new failures. const legacyFrp = row.independentLegacy && result.classification === 'routing_failure' && result.code === 'frp_default_404' && result.status === 404 && result.bytes === legacyFailureProof.bytes && result.bodySha256 === legacyFailureProof.bodySha256 && legacyIdentities.has(sourceKey(row)) const canonical = checksMap.get(probeKey({ materialId: row.materialId, role: 'canonical', sourceSha256: sha(row.deliverySource) })) const rawCompatUnavailable = check.role === 'registered' && row.deliverySource !== row.registeredSource && canonical?.ok && row.materialId === compatibilityProof.materialId && canonical.bodySha256 === compatibilityProof.bodySha256 && canonical.decoded.width === compatibilityProof.width && canonical.decoded.height === compatibilityProof.height && ['network_failure', 'timeout'].includes(result.classification) if (bootstrap ? (legacyFrp || rawCompatUnavailable) : old && !old.ok && failureSignature(result) === failureSignature(old)) { knownFailures.push({ materialId: check.materialId, role: check.role, classification: result.classification, code: result.code, status: result.status }) } else problems.push({ materialId: check.materialId, role: check.role, code: 'new_or_changed_failure' }) } // Canonical delivery must be healthy except for the explicitly documented independent legacy incident. for (const row of plan.selected) { const role = row.deliverySource === row.registeredSource ? 'registered' : 'canonical' const result = checksMap.get(probeKey({ materialId: row.materialId, role, sourceSha256: sha(row.deliverySource) })) if (!result?.ok && !row.independentLegacy) problems.push({ materialId: row.materialId, role, code: 'delivery_preview_unavailable' }) } return { passed: problems.length === 0, problems, knownFailures, allSelectedHealthy: checks.every(row => row.result.ok) } } export async function runProbes(plan, { fetchImpl = directFetch, decodeImage, now = Date.now } = {}) { if (!Array.isArray(plan?.probes) || !plan.probes.length || plan.probes.length > POLICY.probes) fail('probe_budget_requires_independent_full_acceptance') const start = now(), controller = new AbortController() let bytes = 0, cursor = 0, budgetFailure = null const timer = setTimeout(() => { budgetFailure = 'release_probe_wall_budget'; controller.abort() }, POLICY.wallMs) const checks = new Array(plan.probes.length) const guardedFetch = async (url, options) => { const response = await fetchImpl(url, { ...options, signal: AbortSignal.any([options.signal, controller.signal]) }) if (!response.body) return response const guarded = new Response(response.body.pipeThrough(new TransformStream({ transform(chunk, output) { bytes += chunk.byteLength if (bytes > POLICY.bytes) { budgetFailure = 'release_probe_byte_budget'; controller.abort(); throw new GateError(budgetFailure) } output.enqueue(chunk) } })), { status: response.status, headers: response.headers }) Object.defineProperties(guarded, { redirected: { value: response.redirected }, url: { value: response.url } }) return guarded } try { await Promise.all(Array.from({ length: Math.min(POLICY.concurrency, plan.probes.length) }, async () => { while (cursor < plan.probes.length && !controller.signal.aborted) { if (now() - start >= POLICY.wallMs) { budgetFailure = 'release_probe_wall_budget'; controller.abort(); break } const index = cursor++, probe = plan.probes[index] const manifest = { version: 1, reviewed: true, materials: [{ materialId: probe.materialId, registeredSource: probe.registeredSource }] } const report = await checkMaterialPreviewHealth(manifest, { fetchImpl: guardedFetch, ...(decodeImage ? { decodeImage } : {}) }) checks[index] = { ...probe, result: report.results[0] } } })) } finally { clearTimeout(timer); controller.abort() } if (budgetFailure) fail(budgetFailure) validateChecks(plan, checks) return { checks, budget: { ...POLICY, actualBytes: bytes, elapsedMs: now() - start, requests: checks.length, retries: 0 } } } async function deliveryCode(root) { return Object.fromEntries(await Promise.all(DELIVERY_FILES.map(async file => [file, sha(await readFile(path.join(root, file)))]))) } export async function runReleaseStage(input, { prismaClient, collectInventory = collectMaterialPreviewInventory, probe = runProbes, root = '/app', readDeliveryCode = deliveryCode, environment = process.env, targetDeliveryCode = PINNED_TARGET_DELIVERY_CODE, legacyFailureProof = KNOWN_FRP_FAILURE, compatibilityProof = KNOWN_COMPATIBILITY_PROOF, legacyMembers = PINNED_KNOWN_LEGACY_MEMBERS } = {}) { if (input?.version !== 1 || !['bootstrap', 'capture', 'candidate', 'post'].includes(input.stage) || !/^[a-f0-9]{40}$/.test(input.commit)) fail('invalid_release_stage') const bootstrap = input.stage === 'bootstrap' if (!bootstrap) validateAccepted(input.accepted) const owned = !prismaClient const prisma = prismaClient || new (runtimeRequire('@prisma/client').PrismaClient)({ log: [] }) let collected try { collected = await collectInventory(prisma) } finally { if (owned) await prisma.$disconnect() } const inventory = inventoryIndex(collected), inventorySha256 = digest(inventory) const code = await readDeliveryCode(root) if (Object.keys(code).sort().join('|') !== [...DELIVERY_FILES].sort().join('|') || Object.values(code).some(v => !/^[a-f0-9]{64}$/.test(v))) fail('delivery_code_identity_unavailable') const deliveryConfig = normalizeDeliveryConfig(Object.fromEntries(DELIVERY_CONFIG_KEYS.map(key => [key, environment[key] ?? '']))) const publicDomain = deliveryConfig.S3_PUBLIC_DOMAIN const targetConfig = bootstrap ? deliveryConfig : normalizeDeliveryConfig(input.targetDeliveryConfig) let plan, expected if (bootstrap || input.stage === 'capture') { const expandedScope = !bootstrap && (!targetDeliveryCode || digest(code) !== digest(targetDeliveryCode) || digest(input.accepted.deliveryCode) !== digest(code) || digest(input.accepted.deliveryConfig) !== digest(deliveryConfig) || digest(targetConfig) !== digest(deliveryConfig)) plan = buildPlan(inventory, input.accepted, { publicDomain, expandedScope, bootstrap }) expected = input.accepted } else { const before = input.capture if (before?.stage !== 'capture' || before.passed !== true || before.commit !== input.commit || before.inventorySha256 !== inventorySha256 || before.metadataAudit?.inventorySha256 !== collected.audit.inventorySha256 || digest(before.inventory) !== inventorySha256 || digest(before.plan) !== before.planSha256 || before.acceptedArtifactSha256 !== digest(input.accepted)) fail('inventory_or_capture_drift') if (!targetDeliveryCode || digest(code) !== digest(targetDeliveryCode) || digest(deliveryConfig) !== digest(before.targetDeliveryConfig) || digest(targetConfig) !== digest(before.targetDeliveryConfig) || publicDomain !== before.plan.publicDomain) fail('candidate_delivery_identity_mismatch') validateChecks(before.plan, before.checks) if (digest(before.checks) !== before.checksSha256) fail('capture_checks_digest_mismatch') plan = before.plan expected = before if (input.stage === 'post') { if (input.candidate?.stage !== 'candidate' || input.candidate.passed !== true || input.candidate.planSha256 !== before.planSha256 || input.candidate.commit !== input.commit || input.candidate.acceptedArtifactSha256 !== digest(input.accepted) || digest(input.candidate.plan) !== before.planSha256 || digest(input.candidate.inventory) !== inventorySha256 || input.candidate.metadataAudit?.inventorySha256 !== collected.audit.inventorySha256 || input.candidate.inventorySha256 !== inventorySha256 || digest(input.candidate.checks) !== input.candidate.checksSha256) fail('candidate_receipt_required') validateChecks(plan, input.candidate.checks) expected = input.candidate } } const { checks, budget } = await probe(plan) const comparison = compareChecks(plan, checks, expected, { bootstrap, legacyFailureProof, compatibilityProof, legacyMembers }) const artifact = { version: 1, policyVersion: POLICY.version, readOnly: true, stage: input.stage, commit: input.commit, capturedAt: new Date().toISOString(), ...comparison, inventory, inventorySha256, metadataAudit: collected.audit, plan, planSha256: digest(plan), checks, checksSha256: digest(checks), budget, deliveryCode: code, deliveryConfig, deliveryConfigSha256: digest(deliveryConfig), targetDeliveryConfig: targetConfig, knownLegacyMembersSha256: KNOWN_LEGACY_MEMBERS_SHA256, ...(bootstrap ? { bootstrapRequiresExplicitShaReview: true } : { acceptedArtifactSha256: digest(input.accepted) }), scopeNotice: 'Only selected previews were fetched; all remaining metadata rows are explicitly unverified. Known incident failures are not healthy.' } return artifact } /** Package only exact-checkout code, with dependency hashes; old images need no new files. */ export async function bundleReleaseGate(root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'), { runtimeBase = 'file:///app/package.json' } = {}) { const moduleUrl = source => 'data:text/javascript;base64,' + Buffer.from(source).toString('base64') const adapt = source => source.replaceAll('import.meta.url', JSON.stringify(runtimeBase)) const registry = moduleUrl(`export const LEGACY_DOMAINS=${JSON.stringify(LEGACY_DOMAINS)}`) const checker = moduleUrl(adapt(await readFile(path.join(root, 'scripts/check-material-preview-health.mjs'), 'utf8')).replace("'./check-legacy-storage-boundaries.mjs'", JSON.stringify(registry))) const exporter = moduleUrl(adapt(await readFile(path.join(root, 'scripts/export-material-preview-health-manifest.mjs'), 'utf8')) .replace("'./check-legacy-storage-boundaries.mjs'", JSON.stringify(registry)).replace("'./check-material-preview-health.mjs'", JSON.stringify(checker))) const gate = adapt((await readFile(path.join(root, 'scripts/material-preview-release-gate.mjs'), 'utf8')) .replace('const PINNED_KNOWN_LEGACY_MEMBERS = await readKnownLegacyProof()', `const PINNED_KNOWN_LEGACY_MEMBERS = ${JSON.stringify(await readKnownLegacyProof(root))}`)) .replace('const PINNED_TARGET_DELIVERY_CODE = null', `const PINNED_TARGET_DELIVERY_CODE = ${JSON.stringify(await deliveryCode(root))}`) .replace("'./check-legacy-storage-boundaries.mjs'", JSON.stringify(registry)) .replace("'./check-material-preview-health.mjs'", JSON.stringify(checker)) .replace("'./export-material-preview-health-manifest.mjs'", JSON.stringify(exporter)) return moduleUrl(gate) } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { if (process.argv.length === 3 && process.argv[2] === 'bundle') process.stdout.write(await bundleReleaseGate()) else { console.error('Usage: node scripts/material-preview-release-gate.mjs bundle'); process.exitCode = 1 } }