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
124
server-tools/persona-host-write-admission/branch-event-door.mjs
Normal file
124
server-tools/persona-host-write-admission/branch-event-door.mjs
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#!/usr/bin/env node
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
const POLICY = JSON.parse(fs.readFileSync("/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/persona-host-write-boundary.json", "utf8"));
|
||||
const MEMORY_ROOT = "/Volumes/JZAO/HoloLake/persona-runtime/continuity-memory";
|
||||
const STORE = `${MEMORY_ROOT}/persona-daily-fractal/ICE-P-ZY001`;
|
||||
const RUNNER = "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/server-tools/persona-daily-fractal-memory/persona-daily-memory.mjs";
|
||||
|
||||
function argsValue(args, name) {
|
||||
const i = args.indexOf(name);
|
||||
return i >= 0 ? args[i + 1] : undefined;
|
||||
}
|
||||
|
||||
function processChain(pid = process.ppid) {
|
||||
const chain = [];
|
||||
const seen = new Set();
|
||||
while (pid > 1 && !seen.has(pid) && chain.length < 20) {
|
||||
seen.add(pid);
|
||||
const r = spawnSync("/bin/ps", ["-o", "ppid=", "-o", "command=", "-p", String(pid)], { encoding: "utf8" });
|
||||
const line = r.stdout.trim();
|
||||
const m = line.match(/^\s*(\d+)\s+(.+)$/s);
|
||||
if (!m) break;
|
||||
chain.push({ pid, command: m[2] });
|
||||
pid = Number(m[1]);
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
function callerHost(chain) {
|
||||
const text = chain.map((item) => item.command).join("\n");
|
||||
if (/\/Applications\/(?:ChatGPT\.app).*\/(?:codex|Codex)|codex-code-mode-host/i.test(text)) return "codex";
|
||||
if (/\/Applications\/Qianwen\.app|QianwenShell|agent_host\.app/i.test(text)) return "qwen";
|
||||
if (/Doubao\.app/i.test(text)) return "doubao";
|
||||
if (/(?:^|\/)zcode(?:\s|$)/i.test(text)) return "zcode";
|
||||
if (/QoderWork/i.test(text)) return "qoderwork";
|
||||
if (/Qoder/i.test(text)) return "qoder";
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
function expandPattern(value) {
|
||||
return value.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
||||
}
|
||||
|
||||
function validateQueuedEvent(host, eventPath) {
|
||||
const rule = POLICY.hosts[host];
|
||||
if (!rule || rule.write_mode !== "BRANCH_LOCAL_ONLY") throw new Error("HOST_NOT_ACTIVE_BRANCH");
|
||||
const real = fs.realpathSync(eventPath);
|
||||
if (!real.includes("/ingress/persona-events/pending/")) throw new Error("EVENT_NOT_IN_PENDING_INGRESS");
|
||||
const allowed = rule.allowed_write_roots.some((item) => new RegExp(`^${expandPattern(item)}(?:/.*)?$`).test(real));
|
||||
if (!allowed) throw new Error("EVENT_OUTSIDE_BRANCH_ROOT");
|
||||
const st = fs.lstatSync(real);
|
||||
if (!st.isFile() || st.isSymbolicLink() || st.size > 1024 * 1024) throw new Error("EVENT_FILE_INVALID");
|
||||
const event = JSON.parse(fs.readFileSync(real, "utf8"));
|
||||
const required = ["schema", "event_id", "persona_id", "human_anchor", "occurred_at", "session_id", "activity", "branch_id", "summary", "trigger", "emergence", "lock", "why", "rejected", "sources"];
|
||||
const missing = required.filter((key) => event[key] === undefined || event[key] === null || event[key] === "");
|
||||
if (missing.length) throw new Error(`EVENT_FIELDS_MISSING:${missing.join(",")}`);
|
||||
if (event.schema !== "guanghu.persona-daily-fractal-memory-event/v1") throw new Error("EVENT_SCHEMA_REJECTED");
|
||||
if (event.persona_id !== "ICE-P-ZY001" || event.human_anchor !== "ICE-GL∞") throw new Error("EVENT_IDENTITY_REJECTED");
|
||||
if (!String(event.session_id).toLowerCase().includes(host)) throw new Error("EVENT_HOST_COORDINATE_REJECTED");
|
||||
if (!/^ZY001-\d{8}-[A-Z0-9-]+$/.test(event.event_id)) throw new Error("EVENT_ID_REJECTED");
|
||||
if (!Array.isArray(event.sources) || event.sources.length === 0) throw new Error("EVENT_SOURCES_REJECTED");
|
||||
return { real, event };
|
||||
}
|
||||
|
||||
const [command, ...args] = process.argv.slice(2);
|
||||
const chain = processChain();
|
||||
const caller = callerHost(chain);
|
||||
|
||||
if (command === "caller") {
|
||||
console.log(JSON.stringify({ caller, chain }, null, 2));
|
||||
process.exit(caller === "codex" ? 0 : 2);
|
||||
}
|
||||
|
||||
if (command !== "accept") {
|
||||
process.stderr.write("usage: branch-event-door.mjs caller | accept --host HOST --event PENDING_JSON\n");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
if (caller !== "codex") {
|
||||
process.stderr.write(`BRANCH_EVENT_ACCEPT_REJECTED caller=${caller}; only current Codex primary task may accept\n`);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const host = argsValue(args, "--host");
|
||||
const eventPath = argsValue(args, "--event");
|
||||
if (!host || !eventPath) {
|
||||
process.stderr.write("accept requires --host and --event\n");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
let checked;
|
||||
try { checked = validateQueuedEvent(host, eventPath); }
|
||||
catch (error) {
|
||||
process.stderr.write(`BRANCH_EVENT_VALIDATION_REJECTED ${error.message}\n`);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const run = spawnSync(process.execPath, [RUNNER, "append", "--allowed-root", MEMORY_ROOT, "--store", STORE, "--event", checked.real], { encoding: "utf8" });
|
||||
if (run.status !== 0 || !run.stdout.includes('"outcome": "PASS"')) {
|
||||
process.stderr.write(run.stderr || run.stdout || "BRANCH_EVENT_APPEND_FAILED\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const acceptedDir = path.resolve(path.dirname(checked.real), "../accepted");
|
||||
fs.mkdirSync(acceptedDir, { recursive: true });
|
||||
const acceptedPath = path.join(acceptedDir, path.basename(checked.real));
|
||||
fs.renameSync(checked.real, acceptedPath);
|
||||
const receipt = {
|
||||
schema: "guanghu.branch-event-door-receipt/v1",
|
||||
outcome: "PASS",
|
||||
host,
|
||||
event_id: checked.event.event_id,
|
||||
accepted_path: acceptedPath,
|
||||
shared_store: STORE,
|
||||
accepted_by_runtime_host: "codex",
|
||||
source_tcs: POLICY.source_tcs,
|
||||
accepted_at: new Date().toISOString()
|
||||
};
|
||||
const receiptPath = `${acceptedPath}.receipt.json`;
|
||||
fs.writeFileSync(receiptPath, `${JSON.stringify(receipt, null, 2)}\n`, { flag: "wx" });
|
||||
process.stdout.write(`${run.stdout.trim()}\n${JSON.stringify(receipt, null, 2)}\n`);
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
#!/usr/bin/env node
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
|
||||
const POLICY_PATH = "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/persona-host-write-boundary.json";
|
||||
const policy = JSON.parse(fs.readFileSync(POLICY_PATH, "utf8"));
|
||||
|
||||
function expandHome(value) {
|
||||
return value.replace(/^~(?=\/|$)/, "/Users/bingshuolingdianyuanhe");
|
||||
}
|
||||
|
||||
function normal(value, cwd = process.cwd()) {
|
||||
const expanded = expandHome(String(value || ""));
|
||||
return path.resolve(cwd, expanded);
|
||||
}
|
||||
|
||||
function patternRegex(pattern) {
|
||||
const escaped = expandHome(pattern).replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
||||
return new RegExp(`^${escaped}(?:/.*)?$`);
|
||||
}
|
||||
|
||||
function isAllowed(host, target) {
|
||||
const rule = policy.hosts[host];
|
||||
if (!rule) return { allowed: false, code: "HOST_UNKNOWN" };
|
||||
if (rule.write_mode.startsWith("READ_ONLY")) return { allowed: false, code: "HOST_READ_ONLY" };
|
||||
const resolved = normal(target);
|
||||
const matched = rule.allowed_write_roots.find((item) => patternRegex(item).test(resolved));
|
||||
return matched
|
||||
? { allowed: true, code: "WITHIN_HOST_WRITE_ROOT", resolved, matched }
|
||||
: { allowed: false, code: "WRITE_OUTSIDE_HOST_ROOT", resolved };
|
||||
}
|
||||
|
||||
function emit(result, hook = false) {
|
||||
const reason = `${result.code}: ${result.host || "unknown"} -> ${result.resolved || result.path || "path-unresolved"}`;
|
||||
if (hook) {
|
||||
process.stdout.write(`${JSON.stringify({ hookSpecificOutput: {
|
||||
hookEventName: "PreToolUse",
|
||||
permissionDecision: result.allowed ? "allow" : "deny",
|
||||
permissionDecisionReason: reason,
|
||||
} })}\n`);
|
||||
} else {
|
||||
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
||||
}
|
||||
if (!result.allowed) process.exitCode = 2;
|
||||
}
|
||||
|
||||
function collectPathValues(value, out = []) {
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value) collectPathValues(item, out);
|
||||
} else if (value && typeof value === "object") {
|
||||
for (const [key, item] of Object.entries(value)) {
|
||||
if (typeof item === "string" && /(?:path|file|directory|cwd|workdir|target|destination)/i.test(key)) out.push(item);
|
||||
else collectPathValues(item, out);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function shellPaths(command) {
|
||||
const values = [];
|
||||
for (const match of command.matchAll(/["'](\/[^"']+)["']/g)) values.push(match[1]);
|
||||
for (const match of command.matchAll(/(?:^|[\s=])(\/[^\s;|&<>]+)/g)) values.push(match[1]);
|
||||
return [...new Set(values)];
|
||||
}
|
||||
|
||||
const READ_TOOLS = new Set(["Read", "Glob", "Grep", "Search", "WebSearch", "WebFetch"]);
|
||||
const MUTATING_SHELL = /(?:^|[;&|\s])(?:rm|mv|cp|install|mkdir|rmdir|touch|chmod|chown|ln|tee|truncate|dd|rsync|git\s+(?:add|commit|push|checkout|restore|reset|clean|apply|merge|rebase|tag)|sed\s+-i|perl\s+-i|python\d*\s+[^\n]*(?:write|append|unlink|remove|rename)|node\s+[^\n]*(?:write|install|deploy)|npm\s+(?:install|publish)|pnpm\s+(?:install|publish)|apply_patch)(?:\s|$)|(?:>>?|2>)\s*[^&]/i;
|
||||
|
||||
function evaluateHook(host, input) {
|
||||
const tool = String(input.tool_name || input.toolName || "");
|
||||
const toolInput = input.tool_input || input.toolInput || {};
|
||||
if (READ_TOOLS.has(tool)) return { allowed: true, code: "READ_ONLY_TOOL", host, tool };
|
||||
if (tool === "Bash") {
|
||||
const command = String(toolInput.command || "");
|
||||
if (!MUTATING_SHELL.test(command)) return { allowed: true, code: "READ_ONLY_SHELL", host, tool };
|
||||
const rule = policy.hosts[host];
|
||||
if (!rule || rule.write_mode.startsWith("READ_ONLY")) return { allowed: false, code: "HOST_READ_ONLY", host, tool };
|
||||
const candidates = shellPaths(command);
|
||||
const explicitCwd = toolInput.cwd || toolInput.workdir || toolInput.working_directory;
|
||||
const cdMatch = command.match(/(?:^|[;&|]\s*)cd\s+["']?(\/[^\n;&|"']+)/);
|
||||
const cwd = normal(explicitCwd || cdMatch?.[1] || process.cwd());
|
||||
const cwdCheck = isAllowed(host, cwd);
|
||||
const pathChecks = candidates.map((item) => isAllowed(host, item));
|
||||
const denied = pathChecks.find((item) => !item.allowed);
|
||||
if (denied) return { ...denied, host, tool };
|
||||
if (!cwdCheck.allowed && candidates.length === 0) return { ...cwdCheck, code: "MUTATING_SHELL_WITHOUT_ALLOWED_EXPLICIT_TARGET", host, tool };
|
||||
return { allowed: true, code: "MUTATING_SHELL_WITHIN_HOST_ROOT", host, tool, resolved: cwd };
|
||||
}
|
||||
const candidates = collectPathValues(toolInput);
|
||||
if (candidates.length === 0) return { allowed: false, code: "MUTATING_TOOL_TARGET_UNRESOLVED", host, tool };
|
||||
for (const candidate of candidates) {
|
||||
const check = isAllowed(host, candidate);
|
||||
if (!check.allowed) return { ...check, host, tool };
|
||||
}
|
||||
return { allowed: true, code: "TOOL_TARGETS_WITHIN_HOST_ROOT", host, tool };
|
||||
}
|
||||
|
||||
function valueAfter(args, flag) {
|
||||
const index = args.indexOf(flag);
|
||||
return index >= 0 ? args[index + 1] : undefined;
|
||||
}
|
||||
|
||||
const [mode, ...args] = process.argv.slice(2);
|
||||
if (mode === "check") {
|
||||
const host = valueAfter(args, "--host");
|
||||
const target = valueAfter(args, "--path");
|
||||
if (!host || !target) {
|
||||
process.stderr.write("usage: check --host HOST --path PATH\n");
|
||||
process.exit(2);
|
||||
}
|
||||
emit({ ...isAllowed(host, target), host, path: target });
|
||||
} else if (mode === "hook") {
|
||||
const host = valueAfter(args, "--host");
|
||||
if (!host) {
|
||||
process.stderr.write("usage: hook --host HOST\n");
|
||||
process.exit(2);
|
||||
}
|
||||
let raw = "";
|
||||
for await (const chunk of process.stdin) raw += chunk;
|
||||
let input;
|
||||
try { input = JSON.parse(raw || "{}"); }
|
||||
catch { emit({ allowed: false, code: "HOOK_INPUT_INVALID_JSON", host }, true); process.exit(2); }
|
||||
emit(evaluateHook(host, input), true);
|
||||
} else if (mode === "audit") {
|
||||
emit({ allowed: true, code: "POLICY_LOADED", policy_id: policy.policy_id, version: policy.version, policy_path: POLICY_PATH });
|
||||
} else {
|
||||
process.stderr.write("usage: host-write-admission.mjs check|hook|audit\n");
|
||||
process.exit(2);
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#!/usr/bin/env node
|
||||
import assert from "node:assert/strict";
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
const cli = "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/server-tools/persona-host-write-admission/host-write-admission.mjs";
|
||||
|
||||
function check(host, target) {
|
||||
const r = spawnSync(process.execPath, [cli, "check", "--host", host, "--path", target], { encoding: "utf8" });
|
||||
return { code: r.status, body: JSON.parse(r.stdout) };
|
||||
}
|
||||
|
||||
function hook(host, body) {
|
||||
const r = spawnSync(process.execPath, [cli, "hook", "--host", host], { input: JSON.stringify(body), encoding: "utf8" });
|
||||
return { code: r.status, body: JSON.parse(r.stdout) };
|
||||
}
|
||||
|
||||
assert.equal(check("qwen", "/Volumes/JZAO/铸渊-ICE-GL-ZY001/QWEN-DEV-20260905/a.tcs").code, 0);
|
||||
assert.equal(check("qwen", "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/a.json").code, 2);
|
||||
assert.equal(check("qwen", "/Volumes/JZAO/铸渊-ICE-GL-ZY001/BRIDGE/runtime-state/qwen/ice-ch-zc001/a.json").code, 0);
|
||||
assert.equal(check("qwen", "/Volumes/JZAO/铸渊-ICE-GL-ZY001/ZCODE-DEV-20260906/a.json").code, 2);
|
||||
assert.equal(check("zcode", "/Volumes/JZAO/铸渊-ICE-GL-ZY001/ZCODE-DEV-20260906/a.txt").code, 0);
|
||||
assert.equal(check("zcode", "/Volumes/JZAO/铸渊-ICE-GL-ZY001/AGENTS.md").code, 2);
|
||||
assert.equal(check("qoder", "/Users/bingshuolingdianyuanhe/.qoder/skills/a.txt").code, 2);
|
||||
assert.equal(check("codex", "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/a.json").code, 0);
|
||||
|
||||
const deniedEdit = hook("zcode", { tool_name: "Edit", tool_input: { file_path: "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/a.json" } });
|
||||
assert.equal(deniedEdit.code, 2);
|
||||
assert.equal(deniedEdit.body.hookSpecificOutput.permissionDecision, "deny");
|
||||
|
||||
const allowedEdit = hook("zcode", { tool_name: "Edit", tool_input: { file_path: "/Volumes/JZAO/铸渊-ICE-GL-ZY001/ZCODE-DEV-20260906/a.json" } });
|
||||
assert.equal(allowedEdit.code, 0);
|
||||
assert.equal(allowedEdit.body.hookSpecificOutput.permissionDecision, "allow");
|
||||
|
||||
const deniedShell = hook("zcode", { tool_name: "Bash", tool_input: { command: "touch /Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/a.json" } });
|
||||
assert.equal(deniedShell.code, 2);
|
||||
assert.equal(deniedShell.body.hookSpecificOutput.permissionDecision, "deny");
|
||||
|
||||
const allowedRead = hook("zcode", { tool_name: "Read", tool_input: { file_path: "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/a.json" } });
|
||||
assert.equal(allowedRead.code, 0);
|
||||
assert.equal(allowedRead.body.hookSpecificOutput.permissionDecision, "allow");
|
||||
|
||||
console.log("HOST_WRITE_ADMISSION_TESTS_PASS 12/12");
|
||||
Loading…
Reference in a new issue