import fs from 'node:fs'; import path from 'node:path'; import crypto from 'node:crypto'; import { fileURLToPath } from 'node:url'; import { sealCredential, sha256 } from './lib/workshop-credential-envelope.mjs'; import { request, invoke, appId, functionsUrl } from '../server/scripts/platform-client.mjs'; const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const LONGTERM = process.argv[2] === '--longterm'; const RECEIVED = path.join(ROOT, 'work/workshop-auth-envelope-recovery-20261108-r2/received'); const RELEASE_NAME = LONGTERM ? '20261108-r3-longterm' : '20261108-r2'; const PACKAGE = path.join(ROOT, 'artifacts/ksf-workshop-auth-envelope-renewal-' + RELEASE_NAME + '/package'); const must = (condition, code) => { if (!condition) throw Object.assign(new Error(code), { code }); }; const readJson = file => JSON.parse(fs.readFileSync(file, 'utf8')); const EXPECTED_SCOPES = ['source:write', 'ingest:write', 'data:read', 'report:ingest', 'report:publish', 'noise:write', 'noise:read']; async function main() { must(process.argv.length <= 3 && (!process.argv[2] || LONGTERM), 'USAGE'); const manifest = readJson(path.join(RECEIVED, 'manifest.json')); const tar = fs.readFileSync(path.join(RECEIVED, 'wg-auth-return-5files.tar.gz')); must(tar.length === 5527 && sha256(tar) === 'c6e54d85bfec49a9a35def804d24d7e056b71e41239f4bb5c55fce7554dfbaad', 'RETURN_ARCHIVE_MISMATCH'); for (const item of manifest.files) { must(!item.path.includes('..') && !path.isAbsolute(item.path), 'UNSAFE_RETURN_PATH'); const bytes = fs.readFileSync(path.join(RECEIVED, 'files', item.path)); must(bytes.length === item.bytes && sha256(bytes) === item.sha256, 'RETURN_MEMBER_MISMATCH'); } const recipient = readJson(path.join(RECEIVED, 'files/public-recipient/recipient.json')); const publicPem = fs.readFileSync(path.join(RECEIVED, 'files/public-recipient/recipient-public.pem')); const publicKey = crypto.createPublicKey(publicPem); must(recipient.host === '043bb9231c09' && recipient.publicKeySha256 === '1dbefc093985a077fdd7f7433bb024e93e69bf69e89ae8f7e25f749819025bae' && sha256(publicKey.export({ type: 'spki', format: 'der' })) === recipient.publicKeySha256, 'RECIPIENT_MISMATCH'); must(recipient.bindingSha256 === 'e2c8455e31525d292d84cb85ea9e2a61434b596788d13ef731b2fd4703ff6758' && recipient.decryptModuleSha256 === '43e462c21053c2829402e218b05aaa4438aed9552f561d54582675e687a63f79', 'BINDING_EVIDENCE_MISMATCH'); let baseline = { expectedBindingSha256: recipient.bindingSha256, oldEnvelopeFile: recipient.envelopeFile, oldEnvelopeSha256: recipient.envelopeSha256 }; if (LONGTERM) { const installedReceiptBytes = fs.readFileSync(path.join(ROOT, 'docs/workshop-auth-recovery-20261108-r2/receipts/agent-auth-receipt-r2.json')); must(sha256(installedReceiptBytes) === '1c5b466c538e7e62dbda56d2d6df50f89238308a1c64de3aae8f39f48bdc99cd', 'INSTALLED_RECEIPT_MISMATCH'); const installedReceipt = JSON.parse(installedReceiptBytes); const previousRelease = readJson(path.join(ROOT, 'artifacts/ksf-workshop-auth-envelope-renewal-20261108-r2/package/RELEASE.json')); must(installedReceipt.ok === true && installedReceipt.agentEncryptedEntryVerified === true && installedReceipt.host === recipient.host && installedReceipt.bindingSha256 === 'f4dac8ab49032481bcf0ab8115e126bb2563df8ac0e8aeb64ac8a23473347e29', 'PREVIOUS_INSTALL_NOT_VERIFIED'); baseline = { expectedBindingSha256: installedReceipt.bindingSha256, oldEnvelopeFile: previousRelease.installEnvelope, oldEnvelopeSha256: previousRelease.envelopeSha256 }; } const env = {}; for (const line of fs.readFileSync('F:/文档/康师傅数字人受限连接.env', 'utf8').split(/\r?\n/)) { const m = line.match(/^(KSF_SERVICE_TOKEN|KSF_FUNCTIONS_URL|KSF_PARSE_APP_ID)=(.*)$/); if (m) env[m[1]] = m[2].trim(); } must(env.KSF_SERVICE_TOKEN && env.KSF_FUNCTIONS_URL === functionsUrl && env.KSF_PARSE_APP_ID === appId, 'SERVICE_CONFIG_MISMATCH'); const result = await request('GET', '/classes/KsfServiceCredential?' + new URLSearchParams({ where: JSON.stringify({ tokenHash: sha256(env.KSF_SERVICE_TOKEN) }), limit: '2' })); must(result.results?.length === 1, 'CREDENTIAL_IDENTITY_MISMATCH'); const credential = result.results[0], expiresAt = credential.expiresAt?.iso || credential.expiresAt; must(credential.objectId === 'MqKRUv9JQc' && credential.enabled === true && Date.parse(expiresAt) > Date.now(), 'CREDENTIAL_NOT_ACTIVE'); must(JSON.stringify(credential.scopes) === JSON.stringify(EXPECTED_SCOPES) && JSON.stringify(credential.projectIds) === '["*"]', 'CREDENTIAL_SCOPE_CHANGED'); must(expiresAt === (LONGTERM ? '9999-12-31T00:00:00.000Z' : '2026-11-15T01:48:23.854Z'), 'UNEXPECTED_SERVER_EXPIRY'); const live = await invoke('ksfGetIngestContract', { requestId: 'auth-envelope-package-20261108-r2' }, { 'Content-Type': 'application/json', 'X-Parse-Application-Id': appId, 'X-Ksf-Service-Token': env.KSF_SERVICE_TOKEN, }); must(live.http === 200 && live.body.result?.ok === true, 'RESTRICTED_SERVICE_RECHECK_FAILED'); fs.mkdirSync(PACKAGE, { recursive: true }); const envelopePath = path.join(PACKAGE, 'credential-envelope.json'); let envelope; if (fs.existsSync(envelopePath)) { envelope = readJson(envelopePath); must(envelope.header?.recipientHost === recipient.host && envelope.header?.expiresAt === expiresAt && envelope.header?.publicKeySha256 === recipient.publicKeySha256, 'EXISTING_PACKAGE_CONFLICT'); } else { envelope = sealCredential({ publicKey: publicPem, host: recipient.host, directory: recipient.keyDirectory, credentialId: credential.objectId, expiresAt, env }); fs.writeFileSync(envelopePath, JSON.stringify(envelope, null, 2) + '\n', { flag: 'wx', mode: 0o600 }); } const wrapperSha256 = sha256(fs.readFileSync(path.join(ROOT, 'skills/ksf-workshop-runtime/scripts/ksf-secure.mjs'))); must(wrapperSha256 === manifest.originalExecution.wrapperSha256, 'WRAPPER_BASELINE_MISMATCH'); const release = { schema: 'ksf.workshop.encrypted-authorization-release.v1', release: RELEASE_NAME, builtAt: new Date().toISOString(), host: recipient.host, executionUid: 10000, containerNameObserved: manifest.instance.container, skillDirectory: '/opt/data/skills/ksf-workshop-runtime', wrapperSha256, ...baseline, keyDirectory: recipient.keyDirectory, publicKeySha256: recipient.publicKeySha256, decryptModule: recipient.decryptModule, decryptModuleSha256: recipient.decryptModuleSha256, credentialId: credential.objectId, expiresAt, envelopeSha256: sha256(fs.readFileSync(envelopePath)), installDirectory: '/opt/data/ksf-workshop-auth-envelope-' + RELEASE_NAME, installEnvelope: '/opt/data/ksf-workshop-auth-envelope-' + RELEASE_NAME + '/credential-envelope.json', bindingFieldsToChange: ['envelopeFile', 'envelopeSha256'], restrictedServiceProbe: { checkedAt: new Date().toISOString(), http: live.http, ok: true, allowedClassCount: live.body.result.data?.allowedClasses?.length }, serviceIdentityPreserved: true, tokenPreserved: true, scopesPreserved: true, privateKeyIncluded: false, plaintextCredentialIncluded: false, businessRecordWrites: 0, serverAuthorizationRecordWritesDuringPackaging: 0, paidCalls: 0, agentEncryptedEntryVerified: false, actualBusinessWriteVerified: false, authorizationMode: LONGTERM ? 'long-term-with-explicit-expiry' : 'seven-day-renewal', revocable: true, }; fs.writeFileSync(path.join(PACKAGE, 'RELEASE.json'), JSON.stringify(release, null, 2) + '\n'); fs.copyFileSync(path.join(ROOT, 'scripts/restore-workshop-credential-binding.mjs'), path.join(PACKAGE, 'restore-workshop-credential-binding.mjs')); fs.copyFileSync(path.join(ROOT, LONGTERM ? 'docs/workshop-longterm-authorization-20261108-r3/发给运维001-长期授权.md' : 'docs/workshop-auth-recovery-20261108-r2/发给运维001-加密授权更新.md'), path.join(PACKAGE, 'README.md')); const verification = readJson(path.join(ROOT, 'work/workshop-auth-envelope-recovery-20261108-r2/' + (LONGTERM ? 'local-verification-longterm.json' : 'local-verification.json'))); must(verification.ok === true && verification.cryptoTestsPassed === (LONGTERM ? 9 : 8), 'LOCAL_VERIFICATION_REQUIRED'); fs.writeFileSync(path.join(PACKAGE, 'local-verification.json'), JSON.stringify(verification, null, 2) + '\n'); const files = fs.readdirSync(PACKAGE).filter(name => name !== 'FILES.sha256').sort(); const lines = []; for (const name of files) { const bytes = fs.readFileSync(path.join(PACKAGE, name)); must(!bytes.includes(Buffer.from(env.KSF_SERVICE_TOKEN)) && !bytes.includes(Buffer.from('-----BEGIN PRIVATE KEY-----')), 'PACKAGE_CONTAINS_PLAINTEXT_SECRET'); lines.push(sha256(bytes) + ' ' + name); } fs.writeFileSync(path.join(PACKAGE, 'FILES.sha256'), lines.join('\n') + '\n'); delete env.KSF_SERVICE_TOKEN; console.log(JSON.stringify({ ok: true, packageDirectory: PACKAGE, files: files.length, envelopeSha256: release.envelopeSha256, credentialExpiresAt: expiresAt, publicRecipientVerified: true, plaintextSecretScanPassed: true, businessRecordWrites: 0, paidCalls: 0 }, null, 2)); } main().catch(error => { console.error(JSON.stringify({ ok: false, code: /^[A-Z][A-Z0-9_]+$/.test(error.code || '') ? error.code : 'ENCRYPTED_RENEWAL_PACKAGE_STOPPED', message: '封装停止;未输出令牌、管理配置或原始异常。' })); process.exitCode = 1; });