fix: restore current channel and host admission organs
This commit is contained in:
parent
88ffb1c97a
commit
f46eb1b7c1
54 changed files with 4499 additions and 8 deletions
50
server-tools/dark-core/local-file-host.mjs
Normal file
50
server-tools/dark-core/local-file-host.mjs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { digest } from './task-controller.mjs';
|
||||
|
||||
const hash = bytes => createHash('sha256').update(bytes).digest('hex');
|
||||
|
||||
// A deliberately bounded local adapter, not a shell, remote executor, or approval issuer.
|
||||
// Call only after the real host has authorized approvedPlan. No input file authenticates itself.
|
||||
export function localFileHost({ root, approvedPlan, approvalReceipt, verifyHuman }) {
|
||||
if (!path.isAbsolute(root) || !fs.statSync(root).isDirectory() || fs.realpathSync(root) !== root ||
|
||||
typeof approvalReceipt !== 'string' || !approvalReceipt.trim() || typeof verifyHuman !== 'function') {
|
||||
throw Error('TRUSTED_HOST_CONFIGURATION_REQUIRED');
|
||||
}
|
||||
const approved = structuredClone(approvedPlan);
|
||||
const expected = digest(approved);
|
||||
const registeredGrants = new WeakSet();
|
||||
for (const step of approved.steps) {
|
||||
if (step.capability !== 'CREATE_TEXT_FILE' || typeof step.args.text !== 'string' ||
|
||||
typeof step.args.path !== 'string' || path.dirname(step.args.path) !== root ||
|
||||
path.basename(step.args.path) === '.' || path.basename(step.args.path) === '..' ||
|
||||
step.args.path !== path.resolve(step.args.path)) throw Error('LOCAL_CAPABILITY_OUT_OF_SCOPE');
|
||||
}
|
||||
return {
|
||||
verifyHuman,
|
||||
async authorize({ plan, step, planHash, signal }) {
|
||||
signal.throwIfAborted();
|
||||
if (digest(plan) !== expected || planHash !== expected ||
|
||||
!approved.steps.some(s => digest(s) === digest(step))) throw Error('PLAN_NOT_AUTHORIZED');
|
||||
const grant = Object.freeze({ planHash, stepId: step.id, receipt: approvalReceipt });
|
||||
registeredGrants.add(grant);
|
||||
return grant;
|
||||
},
|
||||
async execute({ step, grant, signal }) {
|
||||
signal.throwIfAborted();
|
||||
if (!registeredGrants.has(grant)) throw Error('UNTRUSTED_GRANT');
|
||||
registeredGrants.delete(grant);
|
||||
// Recheck after authorization; wx never overwrites an existing file or symlink.
|
||||
if (fs.realpathSync(root) !== root) throw Error('ROOT_CHANGED');
|
||||
fs.writeFileSync(step.args.path, step.args.text, { flag: 'wx', mode: 0o600 });
|
||||
return { kind: 'FILE_CREATED', path: step.args.path, sha256: hash(fs.readFileSync(step.args.path)) };
|
||||
},
|
||||
async verify({ step, result }) {
|
||||
const stat = fs.lstatSync(step.args.path);
|
||||
return stat.isFile() && !stat.isSymbolicLink() && result?.kind === 'FILE_CREATED' &&
|
||||
result.path === step.args.path && result.sha256 === hash(Buffer.from(step.args.text)) &&
|
||||
hash(fs.readFileSync(step.args.path)) === result.sha256;
|
||||
},
|
||||
};
|
||||
}
|
||||
Loading…
Reference in a new issue