import fs from 'node:fs'; import crypto from 'node:crypto'; import os from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { spawnSync } from 'node:child_process'; const PURPOSE = 'KSF-credential-receive-20261031'; const ENDPOINT = 'https://kangshifu.fmode.cn/api/functions'; const APP_ID = 'FKieRbIoGt_main'; const ENTRY = '/opt/data/skills/ksf-workshop-runtime/scripts/ksf.mjs'; const ENTRY_SHA256 = '7ef128f7d022bfbc15c0ec813bfd87995c6297c6efe407a563c840f014aba477'; const sha256 = value => crypto.createHash('sha256').update(value).digest('hex'); const fail = code => { throw Object.assign(new Error(code), { code }); }; const assert = (condition, code) => { if (!condition) fail(code); }; export function decryptEnvelope(envelope, privateKey, { host, directory, publicKeySha256, now = Date.now() }) { const h = envelope?.header; assert(h?.version === 'ksf.credential-envelope.v1' && h.purpose === PURPOSE && h.keyAlgorithm === 'RSA-OAEP-SHA256' && h.contentAlgorithm === 'AES-256-GCM', 'BAD_ENVELOPE'); assert(h.recipientHost === host && h.recipientDirectory === directory, 'WRONG_RECIPIENT'); assert(privateKey.asymmetricKeyType === 'rsa' && privateKey.asymmetricKeyDetails.modulusLength >= 3072, 'BAD_PRIVATE_KEY'); const publicDer = crypto.createPublicKey(privateKey).export({ type: 'spki', format: 'der' }); assert(sha256(publicDer) === publicKeySha256 && h.publicKeySha256 === publicKeySha256, 'KEY_FINGERPRINT_MISMATCH'); const aad = Buffer.from(envelope.aadBase64, 'base64'); assert(aad.equals(Buffer.from(JSON.stringify(h))), 'HEADER_INCONSISTENT'); const iv = Buffer.from(envelope.ivBase64, 'base64'), tag = Buffer.from(envelope.tagBase64, 'base64'); assert(iv.length === 12 && tag.length === 16, 'BAD_CIPHER_PARAMETERS'); let key, plaintext; try { key = crypto.privateDecrypt({ key: privateKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING, oaepHash: 'sha256', oaepLabel: Buffer.from(PURPOSE) }, Buffer.from(envelope.wrappedKeyBase64, 'base64')); assert(key.length === 32, 'BAD_CONTENT_KEY'); const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv); decipher.setAAD(aad); decipher.setAuthTag(tag); plaintext = Buffer.concat([decipher.update(Buffer.from(envelope.ciphertextBase64, 'base64')), decipher.final()]); const payload = JSON.parse(plaintext.toString('utf8')); assert(payload.purpose === PURPOSE && payload.expiresAt === h.expiresAt && Number.isFinite(Date.parse(payload.expiresAt)) && Date.parse(payload.expiresAt) > now, 'CREDENTIAL_EXPIRED_OR_MISMATCH'); assert(payload.env && Object.keys(payload.env).sort().join(',') === 'KSF_FUNCTIONS_URL,KSF_PARSE_APP_ID,KSF_SERVICE_TOKEN', 'ENV_KEYS_NOT_ALLOWED'); assert(payload.env.KSF_FUNCTIONS_URL === ENDPOINT && payload.env.KSF_PARSE_APP_ID === APP_ID && /^[A-Za-z0-9_-]{43}$/.test(payload.env.KSF_SERVICE_TOKEN), 'BAD_SERVICE_CONFIG'); assert(typeof payload.credentialId === 'string' && payload.credentialId.length > 0, 'MISSING_CREDENTIAL_ID'); return payload; } finally { key?.fill(0); plaintext?.fill(0); } } function argsOf(values) { const args = {}; for (let i = 0; i < values.length; i += 2) { const name = values[i], value = values[i + 1]; assert(['--envelope', '--sha256', '--request-id'].includes(name) && value && !args[name], 'USAGE'); args[name] = value; } assert(args['--envelope'] && /^[a-f0-9]{64}$/.test(args['--sha256'] || '') && /^[\p{L}\p{N}_:@.-]{1,100}$/u.test(args['--request-id'] || ''), 'USAGE'); return args; } function privateKeyAt(directory) { assert(/^\/opt\/data\/ksf-key-receive-20261031-[A-Za-z0-9]+$/.test(directory || ''), 'BAD_KEY_DIRECTORY'); assert(fs.realpathSync(directory) === directory, 'SYMLINK_KEY_DIRECTORY'); const dir = fs.lstatSync(directory), file = path.join(directory, 'recipient-private.pem'), stat = fs.lstatSync(file); const uid = process.geteuid(); assert(dir.isDirectory() && dir.uid === uid && (dir.mode & 0o777) === 0o700, 'BAD_DIRECTORY_PERMISSIONS'); assert(stat.isFile() && !stat.isSymbolicLink() && stat.uid === uid && (stat.mode & 0o777) === 0o600, 'BAD_KEY_PERMISSIONS'); const bytes = fs.readFileSync(file); try { return crypto.createPrivateKey(bytes); } finally { bytes.fill(0); } } export async function runReadControls(env, requestId, fetchImpl = fetch) { const probe = { className: 'KfSource', objectId: 'auth-probe-' + crypto.randomUUID() }; const controls = []; for (const [identity, token] of [ ['anonymous', null], ['invalid-control', 'invalid-control-' + crypto.randomBytes(24).toString('base64url')], ['configured-restricted-service', env.KSF_SERVICE_TOKEN], ]) { const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': APP_ID }; if (token) headers['X-Ksf-Service-Token'] = token; const response = await fetchImpl(ENDPOINT, { method: 'POST', headers, body: JSON.stringify({ path: '/ksfGetRecord', params: { ...probe, requestId } }), redirect: 'error', signal: AbortSignal.timeout(45000) }); const result = (await response.json())?.result; assert(result?.ok === false && Array.isArray(result.errors), 'UNEXPECTED_CONTROL_RESPONSE'); const codes = result.errors.map(x => x.code); controls.push({ identity, operation: 'ksfGetRecord', http: response.status, businessOk: false, errorCodes: codes }); assert(identity === 'configured-restricted-service' ? response.status === 400 && codes.includes('REFERENCE_NOT_FOUND') : response.status === 401 && codes.includes('AUTH_REQUIRED'), 'AUTH_CONTROL_FAILED'); } return controls; } export function validateEntryResult(child, requestId) { assert(!child.error && [0, 1].includes(child.status), 'ENTRY_PROCESS_FAILED'); const output = child.status === 0 ? child.stdout : child.stderr; const result = JSON.parse(output); assert(result.entry === ENTRY && result.entrySha256 === ENTRY_SHA256 && result.command === 'connection' && result.requestId === requestId && typeof result.invocationId === 'string' && /^[a-f0-9-]{36}$/.test(result.invocationId), 'ENTRY_OUTPUT_MISMATCH'); assert(child.status === 0 && result.ok === true && result.readOnly === true && Number.isInteger(result.allowedClassCount) && result.allowedClassCount > 0, 'ENTRY_CONNECTION_FAILED'); return result; } async function main() { assert(process.platform === 'linux', 'LINUX_REQUIRED'); const args = argsOf(process.argv.slice(2)); const bytes = fs.readFileSync(args['--envelope']); assert(bytes.length < 32768 && sha256(bytes) === args['--sha256'], 'ENVELOPE_SHA256_MISMATCH'); const envelope = JSON.parse(bytes); const privateKey = privateKeyAt(envelope?.header?.recipientDirectory); const payload = decryptEnvelope(envelope, privateKey, { host: os.hostname(), directory: envelope.header.recipientDirectory, publicKeySha256: envelope.header.publicKeySha256 }); assert(sha256(fs.readFileSync(ENTRY)) === ENTRY_SHA256, 'ENTRY_HASH_MISMATCH'); const env = { ...process.env }; for (const name of ['KSF_SERVICE_TOKEN', 'KSF_PARSE_SESSION_TOKEN', 'KSF_PARSE_MASTER_KEY', 'KSF_FUNCTIONS_URL', 'KSF_PARSE_APP_ID']) delete env[name]; Object.assign(env, payload.env, { KSF_ADAPT_DIR: '/opt/data/ksf-adapt-training-20261031', KSF_R7_SKILL_DIR: '/opt/data/ksf-baseline/r7/unpacked/skill' }); try { const child = spawnSync(process.execPath, [ENTRY, 'connection', '--request-id', args['--request-id']], { env, encoding: 'utf8', timeout: 60000, maxBuffer: 1024 * 1024, windowsHide: true, }); assert(!String(child.stdout).includes(env.KSF_SERVICE_TOKEN) && !String(child.stderr).includes(env.KSF_SERVICE_TOKEN), 'OUTPUT_CONTAINS_SECRET'); const result = validateEntryResult(child, args['--request-id']); const controls = await runReadControls(payload.env, args['--request-id']); const output = JSON.stringify({ ok: true, operation: 'remote-memory-only-connection-check', at: new Date().toISOString(), host: os.hostname(), requestId: args['--request-id'], envelopeSha256: sha256(bytes), publicKeySha256: envelope.header.publicKeySha256, credentialExpiresAt: payload.expiresAt, entryExitCode: child.status, entryResult: result, authenticationVerified: true, authenticationControls: controls, controlRequestCount: controls.length, entryContractCallSucceeded: true, credentialWrittenToDisk: false, permanentEnvironmentConfigured: false, gatewayRestarted: false, databaseWrites: 0, published: false }, null, 2); assert(!output.includes(env.KSF_SERVICE_TOKEN), 'OUTPUT_CONTAINS_SECRET'); console.log(output); } finally { delete env.KSF_SERVICE_TOKEN; delete payload.env.KSF_SERVICE_TOKEN; } } if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { main().catch(error => { // Do not echo crypto errors, paths, server bodies, or child stdout/stderr on failure. console.error(JSON.stringify({ ok: false, error: { code: /^[A-Z][A-Z0-9_]+$/.test(error.code || '') ? error.code : 'SECURE_CONNECTION_CHECK_FAILED', message: '安全连接检查中止;未输出私钥、令牌、明文配置或原始服务器报文。', } })); process.exitCode = 1; }); }