import fs from 'node:fs'; import path from 'node:path'; import crypto from 'node:crypto'; import os from 'node:os'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { spawnSync } from 'node:child_process'; const self = fileURLToPath(import.meta.url); const sha256 = bytes => crypto.createHash('sha256').update(bytes).digest('hex'); const ENTRY_HASH = '7ef128f7d022bfbc15c0ec813bfd87995c6297c6efe407a563c840f014aba477'; const LOCK_HASH = '2605cbe8fd7be43b5969a4991f978dc0768dee5b4d65d18f7af15f75e9004a79'; const WORKSHOP_ENTRY_HASH = '7a04eb22d3429cdced23513717f36594f639539738994d45712a8bea738100a1'; const IDENTITY_MODULE_HASH = '9224ef89a96425890d54961fda1f4fcae3403aa7c753fe1d70ac62082f2ff77e'; const WORKSHOP_COMMANDS = ['workshop-start','workshop-save','project-create','project-progress','concept-save','project-status','workshop-import','workshop-dedup','workshop-search','workshop-read','workshop-contract','workshop-schema','employee-read','employee-name']; const fail = code => { throw Object.assign(new Error(code), { code }); }; const requireThat = (value, code) => { if (!value) fail(code); }; export function parseArgs(argv) { const [command, ...rest] = argv, args = { command }; requireThat(['status','demo','connection','ingest','report-preview','report-submit',...WORKSHOP_COMMANDS].includes(command), 'USAGE'); for (let i = 0; i < rest.length; i += 2) { const key = rest[i], value = rest[i + 1]; requireThat(['--input','--request-id','--agent-profile'].includes(key) && value && !value.startsWith('--') && !args[key], 'USAGE'); args[key] = value; } requireThat(['ingest','report-preview','report-submit',...WORKSHOP_COMMANDS].includes(command) === Boolean(args['--input']), 'USAGE'); if(WORKSHOP_COMMANDS.includes(command))requireThat(Boolean(args['--request-id']), 'BAD_REQUEST_ID'); if(args['--agent-profile'])requireThat(WORKSHOP_COMMANDS.includes(command)&&/^agent-(?:registration:[a-f0-9-]{36}|account:[a-f0-9]{64})$/.test(args['--agent-profile']),'INVALID_AGENT_KEY'); if(WORKSHOP_COMMANDS.includes(command)&&!['project-status','workshop-search','workshop-read','workshop-contract','workshop-dedup','workshop-schema'].includes(command))requireThat(Boolean(args['--agent-profile']),'AGENT_PROFILE_REQUIRED'); if (args['--input']) requireThat(path.isAbsolute(args['--input']), 'INPUT_PATH_MUST_BE_ABSOLUTE'); if (args['--request-id']) requireThat(/^[\p{L}\p{N}_:@.-]{1,100}$/u.test(args['--request-id']), 'BAD_REQUEST_ID'); return args; } function readPrivateKey(directory) { requireThat(/^\/opt\/data\/ksf-key-receive-20261031-[A-Za-z0-9]+$/.test(directory), 'BAD_KEY_DIRECTORY'); requireThat(fs.realpathSync(directory) === directory, 'KEY_DIRECTORY_IS_SYMLINK'); const dir = fs.lstatSync(directory), file = path.join(directory, 'recipient-private.pem'), stat = fs.lstatSync(file); requireThat(dir.isDirectory() && dir.uid === process.geteuid() && (dir.mode & 0o777) === 0o700, 'BAD_KEY_DIRECTORY_PERMISSIONS'); requireThat(stat.isFile() && !stat.isSymbolicLink() && stat.uid === process.geteuid() && (stat.mode & 0o777) === 0o600, 'BAD_KEY_FILE_PERMISSIONS'); const bytes = fs.readFileSync(file); try { return crypto.createPrivateKey(bytes); } finally { bytes.fill(0); } } // Test dependencies are module arguments only; the executable accepts no override flags. export async function executeSecure(argv, dependencies = {}) { const args = parseArgs(argv); const skillDir = dependencies.skillDir || path.resolve(path.dirname(self), '..'); const readBytes = dependencies.readBytes || (file => fs.readFileSync(file)); const spawn = dependencies.spawn || spawnSync; const workshop = WORKSHOP_COMMANDS.includes(args.command); const entry = path.join(skillDir, 'scripts', workshop ? 'ksf-workshop.mjs' : 'ksf.mjs'); const expectedEntryHash = workshop ? WORKSHOP_ENTRY_HASH : ENTRY_HASH; requireThat(sha256(readBytes(entry)) === expectedEntryHash, 'ENTRY_HASH_MISMATCH'); if(workshop)requireThat(sha256(readBytes(path.join(skillDir,'scripts','ksf-identity.mjs'))) === IDENTITY_MODULE_HASH, 'IDENTITY_MODULE_HASH_MISMATCH'); requireThat(sha256(readBytes(path.join(skillDir, 'runtime-lock.json'))) === LOCK_HASH, 'RUNTIME_LOCK_HASH_MISMATCH'); const inheritedEnv = dependencies.inheritedEnv || process.env; const env = { ...inheritedEnv }; for (const name of Object.keys(env)) if (name.startsWith('KSF_')) delete env[name]; Object.assign(env, { KSF_ADAPT_DIR: dependencies.runtimeDir || '/opt/data/ksf-adapt-training-20261031', KSF_R7_SKILL_DIR: dependencies.r7Dir || '/opt/data/ksf-baseline/r7/unpacked/skill', }); let payload = null, binding = null; try { if (args.command !== 'demo') { binding = JSON.parse(readBytes(path.join(skillDir, 'credential-binding.json'))); requireThat(binding.version === 'ksf.encrypted-entry.v1' && binding.purpose === 'KSF-credential-receive-20261031', 'BAD_BINDING'); requireThat(binding.host === (dependencies.hostname || os.hostname()), 'WRONG_CONTAINER'); requireThat(/^\/opt\/data\/.+/.test(binding.envelopeFile) && /^\/opt\/data\/.+/.test(binding.decryptModule), 'BAD_BINDING_PATHS'); requireThat(/^[a-f0-9]{64}$/.test(binding.envelopeSha256) && /^[a-f0-9]{64}$/.test(binding.decryptModuleSha256) && /^[a-f0-9]{64}$/.test(binding.publicKeySha256), 'BAD_BINDING_HASHES'); const envelopeBytes = readBytes(binding.envelopeFile); requireThat(envelopeBytes.length < 32768 && sha256(envelopeBytes) === binding.envelopeSha256, 'ENVELOPE_HASH_MISMATCH'); const envelope = JSON.parse(envelopeBytes); requireThat(envelope.header?.recipientHost === binding.host && envelope.header?.recipientDirectory === binding.keyDirectory && envelope.header?.publicKeySha256 === binding.publicKeySha256, 'ENVELOPE_BINDING_MISMATCH'); requireThat(sha256(readBytes(binding.decryptModule)) === binding.decryptModuleSha256, 'DECRYPT_MODULE_HASH_MISMATCH'); const module = await (dependencies.importDecrypt || (file => import(pathToFileURL(file).href)))(binding.decryptModule); const key = (dependencies.readPrivateKey || readPrivateKey)(binding.keyDirectory); payload = module.decryptEnvelope(envelope, key, { host: binding.host, directory: binding.keyDirectory, publicKeySha256: binding.publicKeySha256 }); Object.assign(env, payload.env); } const child = spawn(process.execPath, [entry, ...argv], { env, encoding: 'utf8', timeout: 600000, maxBuffer: 4 * 1024 * 1024, windowsHide: true, }); requireThat(!child.error && [0,1].includes(child.status), 'ENTRY_PROCESS_FAILED'); if (payload) requireThat(!String(child.stdout).includes(payload.env.KSF_SERVICE_TOKEN) && !String(child.stderr).includes(payload.env.KSF_SERVICE_TOKEN), 'OUTPUT_CONTAINS_SECRET'); const result = JSON.parse(child.status === 0 ? child.stdout : child.stderr); requireThat(result.skill === 'ksf-workshop-runtime' && result.entry === entry && result.entrySha256 === expectedEntryHash && result.command === args.command && typeof result.invocationId === 'string' && /^[a-f0-9-]{36}$/.test(result.invocationId) && (!args['--request-id'] || result.requestId === args['--request-id']), 'ENTRY_OUTPUT_MISMATCH'); requireThat(typeof result.ok === 'boolean' && (child.status === 0) === result.ok, 'ENTRY_EXIT_STATUS_MISMATCH'); const output = { skill: 'ksf-workshop-runtime', invocationId: crypto.randomUUID(), at: new Date().toISOString(), wrapper: self, wrapperSha256: sha256(readBytes(self)), command: args.command, ...(args['--request-id'] ? { requestId: args['--request-id'] } : {}), ok: result.ok, credentialMode: payload ? 'encrypted-file-decrypted-per-command' : 'not-needed-for-demo', credentialLoadedForChild: Boolean(payload), credentialWrittenToDisk: false, gatewayEnvironmentModified: false, gatewayRestarted: false, ...(payload ? { credentialExpiresAt: payload.expiresAt, envelopeSha256: binding.envelopeSha256 } : {}), entryExitCode: child.status, entryResult: result, }; if (payload) requireThat(!JSON.stringify(output).includes(payload.env.KSF_SERVICE_TOKEN), 'OUTPUT_CONTAINS_SECRET'); return output; } finally { delete env.KSF_SERVICE_TOKEN; if (payload) delete payload.env.KSF_SERVICE_TOKEN; } } if (process.argv[1] && path.resolve(process.argv[1]) === self) { const base = { skill: 'ksf-workshop-runtime', invocationId: crypto.randomUUID(), at: new Date().toISOString(), wrapper: self, wrapperSha256: sha256(fs.readFileSync(self)) }; try { requireThat(process.platform === 'linux', 'LINUX_REQUIRED'); const result = await executeSecure(process.argv.slice(2)); console.log(JSON.stringify(result, null, 2)); if (!result.ok) process.exitCode = 1; } catch (error) { console.error(JSON.stringify({ ...base, ok: false, error: { code: /^[A-Z][A-Z0-9_]+$/.test(error.code || '') ? error.code : 'SECURE_ENTRY_FAILED', message: '执行停止;未显示凭据或原始异常。凭据过期需由管理端续期或轮换;请勿改指纹或绕过校验。', } }, null, 2)); process.exitCode = 1; } }