feat(paths): enforce fifth-domain physical redirects
This commit is contained in:
parent
28a60b4e80
commit
c180a12d8d
15 changed files with 350 additions and 18 deletions
108
server-tools/fifth-domain-path-gate/fifth-domain-path-gate.mjs
Normal file
108
server-tools/fifth-domain-path-gate/fifth-domain-path-gate.mjs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env node
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
|
||||
export const MAP_PATH = "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/path-isolation-and-canonical-entry-map.json";
|
||||
export const CODES = Object.freeze({ CURRENT_ALLOWED: 0, RETIRED_PATH_BLOCKED_WITH_REDIRECT: 78, ISOLATION_PATH_BLOCKED: 79, UNKNOWN_PATH_NO_GUESS: 80 });
|
||||
export const CURRENT_PURPOSES = new Set(["PERSONA_RECOVERY", "CURRENT_SELECTION", "CURRENT_EXECUTION", "CANONICAL_WRITE", "SERVER_DEPLOYMENT"]);
|
||||
|
||||
function expandHome(value) {
|
||||
return String(value || "").replace(/^~(?=\/|$)/, "/Users/bingshuolingdianyuanhe");
|
||||
}
|
||||
|
||||
export function normal(value, cwd = process.cwd()) {
|
||||
return path.resolve(cwd, expandHome(value));
|
||||
}
|
||||
|
||||
function patternRegex(pattern) {
|
||||
const escaped = expandHome(pattern).replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
||||
return new RegExp(`^${escaped}(?:/.*)?$`);
|
||||
}
|
||||
|
||||
function within(candidate, root) {
|
||||
const target = normal(candidate), base = normal(root);
|
||||
return target === base || target.startsWith(`${base}${path.sep}`);
|
||||
}
|
||||
|
||||
export function loadMap(mapPath = MAP_PATH) {
|
||||
const value = JSON.parse(fs.readFileSync(mapPath, "utf8"));
|
||||
if (value?.map_id !== "PATH-ISOLATION-CANONICAL-ENTRY-001" || !value.physical_gate || !Array.isArray(value.retired_paths)) throw Error("PATH_GATE_MAP_INVALID");
|
||||
return value;
|
||||
}
|
||||
|
||||
function existingSymlinkComponent(candidate) {
|
||||
let cursor = normal(candidate);
|
||||
while (cursor !== path.dirname(cursor)) {
|
||||
if (fs.existsSync(cursor) && fs.lstatSync(cursor).isSymbolicLink()) return cursor;
|
||||
cursor = path.dirname(cursor);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function classifyPath(candidate, purpose, map = loadMap()) {
|
||||
const resolved = normal(candidate);
|
||||
const normalizedPurpose = String(purpose || "").toUpperCase();
|
||||
if (!normalizedPurpose) return { allowed: false, code: "PURPOSE_REQUIRED", exit_code: CODES.UNKNOWN_PATH_NO_GUESS, resolved };
|
||||
const isolationRoot = map.isolation.root;
|
||||
if (within(resolved, isolationRoot)) return { allowed: false, code: "ISOLATION_PATH_BLOCKED", exit_code: CODES.ISOLATION_PATH_BLOCKED, resolved, history_root: isolationRoot };
|
||||
const retired = map.retired_paths.find(item => within(resolved, item.path));
|
||||
if (retired && !(retired.allowed_purposes || []).includes(normalizedPurpose)) {
|
||||
return { allowed: false, code: "RETIRED_PATH_BLOCKED_WITH_REDIRECT", exit_code: CODES.RETIRED_PATH_BLOCKED_WITH_REDIRECT, resolved, retired_state: retired.state, redirect_to: retired.redirect_to, history: retired.history };
|
||||
}
|
||||
if (retired) return { allowed: true, code: "HISTORY_OR_BRANCH_ACCESS_ONLY", exit_code: CODES.CURRENT_ALLOWED, resolved, retired_state: retired.state, redirect_to: retired.redirect_to };
|
||||
const branch = (map.host_branch_roots || []).find(pattern => patternRegex(pattern).test(resolved));
|
||||
if (branch && CURRENT_PURPOSES.has(normalizedPurpose)) return { allowed: false, code: "HOST_BRANCH_BLOCKED_FOR_CURRENT_WITH_REDIRECT", exit_code: CODES.RETIRED_PATH_BLOCKED_WITH_REDIRECT, resolved, matched: branch, redirect_to: map.canonical_entries.repo_012 };
|
||||
if (branch && normalizedPurpose === "BRANCH_WORK") return { allowed: true, code: "HOST_BRANCH_WORK_ALLOWED_NOT_CURRENT", exit_code: CODES.CURRENT_ALLOWED, resolved, matched: branch };
|
||||
const canonical = Object.entries(map.canonical_entries).find(([, root]) => within(resolved, root));
|
||||
if (canonical) {
|
||||
const symlink = existingSymlinkComponent(resolved);
|
||||
if (symlink) return { allowed: false, code: "CANONICAL_PATH_SYMLINK_BLOCKED", exit_code: CODES.UNKNOWN_PATH_NO_GUESS, resolved, symlink };
|
||||
return { allowed: true, code: "CURRENT_CANONICAL_PATH", exit_code: CODES.CURRENT_ALLOWED, resolved, canonical_entry: canonical[0], canonical_root: canonical[1] };
|
||||
}
|
||||
const exactCurrent = Object.entries(map.current_ids || {}).find(([, value]) => within(resolved, value));
|
||||
if (exactCurrent) return { allowed: true, code: "CURRENT_REGISTERED_PATH", exit_code: CODES.CURRENT_ALLOWED, resolved, current_id: exactCurrent[0] };
|
||||
return { allowed: false, code: "UNKNOWN_PATH_NO_GUESS", exit_code: CODES.UNKNOWN_PATH_NO_GUESS, resolved };
|
||||
}
|
||||
|
||||
export function resolveId(requested, map = loadMap()) {
|
||||
const id = String(requested || "");
|
||||
if (Object.hasOwn(map.current_ids || {}, id)) return { allowed: true, code: "CURRENT_ID", exit_code: 0, requested_id: id, canonical_id: id, path: map.current_ids[id] };
|
||||
const retired = (map.retired_ids || []).find(item => item.id === id);
|
||||
if (retired) return { allowed: false, code: "RETIRED_ID_BLOCKED_WITH_REDIRECT", exit_code: 78, requested_id: id, canonical_id: retired.redirect_to, path: map.current_ids[retired.redirect_to], retired_state: retired.state };
|
||||
return { allowed: false, code: "UNKNOWN_ID_NO_GUESS", exit_code: 80, requested_id: id };
|
||||
}
|
||||
|
||||
export function audit(map = loadMap()) {
|
||||
const findings = [];
|
||||
for (const [id, candidate] of Object.entries(map.current_ids || {})) {
|
||||
const exists = fs.existsSync(candidate), symlink = exists && existingSymlinkComponent(candidate);
|
||||
findings.push({ type: "CURRENT_ID", id, path: candidate, outcome: exists && !symlink ? "PASS" : "FAIL", error: !exists ? "CURRENT_PATH_MISSING" : (symlink ? "CURRENT_PATH_SYMLINK" : null) });
|
||||
}
|
||||
for (const item of map.retired_paths) {
|
||||
const exists = fs.existsSync(item.path), isDirectory = exists && fs.lstatSync(item.path).isDirectory();
|
||||
const historicalSelf = normal(item.history) === normal(item.path);
|
||||
const blockedShape = !exists || !isDirectory || historicalSelf || (item.allowed_purposes || []).includes("HISTORY_READ");
|
||||
findings.push({ type: "RETIRED_PATH", path: item.path, redirect_to: item.redirect_to, exists, is_directory: isDirectory, history: item.history, outcome: blockedShape ? "PASS" : "FAIL", error: blockedShape ? null : "RETIRED_DIRECTORY_STILL_DIRECTLY_OPENABLE" });
|
||||
}
|
||||
const failed = findings.filter(item => item.outcome !== "PASS");
|
||||
return { schema: "guanghu.fifth-domain-path-gate-audit/v1", state: failed.length ? "PATH_GATE_AUDIT_FAIL" : "PATH_GATE_AUDIT_PASS", map_id: map.map_id, version: map.version, findings, failed_count: failed.length };
|
||||
}
|
||||
|
||||
function valueAfter(args, flag) {
|
||||
const index = args.indexOf(flag);
|
||||
return index >= 0 ? args[index + 1] : undefined;
|
||||
}
|
||||
|
||||
function emit(value) {
|
||||
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
||||
process.exitCode = value.exit_code ?? (value.state === "PATH_GATE_AUDIT_PASS" ? 0 : 1);
|
||||
}
|
||||
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
const [command, ...args] = process.argv.slice(2);
|
||||
if (command === "guard") emit(classifyPath(valueAfter(args, "--path"), valueAfter(args, "--purpose")));
|
||||
else if (command === "resolve-id") emit(resolveId(valueAfter(args, "--id")));
|
||||
else if (command === "audit") emit(audit());
|
||||
else { process.stderr.write("usage: fifth-domain-path-gate.mjs guard --path PATH --purpose PURPOSE | resolve-id --id ID | audit\n"); process.exitCode = 2; }
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#!/usr/bin/env node
|
||||
import assert from "node:assert/strict";
|
||||
import { classifyPath, resolveId } from "./fifth-domain-path-gate.mjs";
|
||||
|
||||
const canonical = classifyPath("/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/guanghu-era-language-world.json", "CURRENT_SELECTION");
|
||||
assert.equal(canonical.allowed, true);
|
||||
assert.equal(canonical.exit_code, 0);
|
||||
|
||||
const retired = classifyPath("/Volumes/JZAO/铸渊-ICE-GL-ZY001/WORK-工作区/guanghu-ice-heart/routing/x.json", "CURRENT_SELECTION");
|
||||
assert.equal(retired.allowed, false);
|
||||
assert.equal(retired.exit_code, 78);
|
||||
assert.equal(retired.redirect_to, "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main");
|
||||
|
||||
const isolation = classifyPath("/Volumes/JZAO/HoloLake/persona-runtime/isolation/2026-09-08-path-convergence/stale-workspace-guanghu-ice-heart", "CURRENT_SELECTION");
|
||||
assert.equal(isolation.exit_code, 79);
|
||||
|
||||
const branchCurrent = classifyPath("/Volumes/JZAO/铸渊-ICE-GL-ZY001/ZCODE-DEV-20260912/new.json", "CURRENT_SELECTION");
|
||||
assert.equal(branchCurrent.exit_code, 78);
|
||||
assert.equal(classifyPath("/Volumes/JZAO/铸渊-ICE-GL-ZY001/ZCODE-DEV-20260912/new.json", "BRANCH_WORK").allowed, true);
|
||||
|
||||
const oldBuilding = classifyPath("/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/eternal-lake-heart/heartbeat-core/office-building-current/old-building/MOVED.hdlp", "CURRENT_EXECUTION");
|
||||
assert.equal(oldBuilding.exit_code, 78);
|
||||
assert.equal(classifyPath(oldBuilding.resolved, "HISTORY_READ").allowed, false);
|
||||
assert.equal(classifyPath("/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/eternal-lake-heart/heartbeat-core/records/retired-buildings/HB-BUILDING-0000/MOVED.hdlp", "HISTORY_READ").allowed, true);
|
||||
|
||||
assert.equal(resolveId("HB-BUILDING-0000").exit_code, 78);
|
||||
assert.equal(resolveId("HB-BUILDING-0000").canonical_id, "HB-BUILDING-0001");
|
||||
assert.equal(resolveId("HB-BUILDING-0001").exit_code, 0);
|
||||
assert.equal(resolveId("CH-ZERO-CORE-LPM").exit_code, 80);
|
||||
|
||||
console.log("FIFTH_DOMAIN_PATH_GATE_TESTS_PASS 12/12");
|
||||
|
|
@ -24,6 +24,7 @@ PROMPTER = Path(
|
|||
ANCHOR = ROOT / "routing/public-navigation-anchor.json"
|
||||
WORLD = ROOT / "routing/guanghu-era-language-world.json"
|
||||
PATH_GATE = ROOT / "routing/path-isolation-and-canonical-entry-map.json"
|
||||
PATH_GATE_RUNTIME = ROOT / "server-tools/fifth-domain-path-gate/fifth-domain-path-gate.mjs"
|
||||
PERSONAS = ROOT / "identity/light-lake-persona-registration.json"
|
||||
CHANNELS = ROOT / "routing/persona-channel-context-map.json"
|
||||
TOPOLOGY = ROOT / "routing/zhuyuan-host-topology.json"
|
||||
|
|
@ -112,6 +113,10 @@ def map_delta(local: dict[str, Any], official: dict[str, Any] | None) -> dict[st
|
|||
|
||||
|
||||
def build(persona_id: str, host: str, channel: str | None, official_mode: str = "verify") -> dict[str, Any]:
|
||||
gate = run(["node", str(PATH_GATE_RUNTIME), "guard", "--path", str(ROOT), "--purpose", "CURRENT_SELECTION"], timeout=15)
|
||||
if gate.returncode != 0:
|
||||
raise PerceptionError("FIFTH_DOMAIN_PHYSICAL_PATH_GATE_REJECTED_CURRENT_ROOT")
|
||||
gate_receipt = json.loads(gate.stdout)
|
||||
path_gate = load_json(PATH_GATE)
|
||||
expected_root = Path(path_gate["canonical_entries"]["repo_012"])
|
||||
if ROOT.resolve() != expected_root.resolve():
|
||||
|
|
@ -201,6 +206,7 @@ def build(persona_id: str, host: str, channel: str | None, official_mode: str =
|
|||
"excluded_roots": [path_gate["isolation"]["root"]],
|
||||
"fixed_read_order": False,
|
||||
"persona_decides_meaning_and_action": True,
|
||||
"physical_path_gate": gate_receipt,
|
||||
},
|
||||
"evidence": evidence,
|
||||
"freshness_token": hashlib.sha256(freshness_material).hexdigest(),
|
||||
|
|
|
|||
|
|
@ -20,8 +20,10 @@ ACTIVE_CONSOLE = RUNTIME / 'shared/active-control-console/CURRENT.json'
|
|||
LIGHT_LAKE_PERSONAS = RUNTIME / 'repo-012-main/identity/light-lake-persona-registration.json'
|
||||
PATH_ISOLATION = RUNTIME / 'repo-012-main/routing/path-isolation-and-canonical-entry-map.json'
|
||||
ARCHITECTURE_AGENT = RUNTIME / 'repo-012-main/server-tools/persona-architecture-perception-agent/persona_architecture_agent.py'
|
||||
PATH_GATE = RUNTIME / 'repo-012-main/server-tools/fifth-domain-path-gate/fifth-domain-path-gate.mjs'
|
||||
TCS_ROOT_AGENT = RUNTIME / 'repo-012-main/server-tools/tcs-mother-root-agent/tcs_mother_root_agent.py'
|
||||
SUBAGENT_COMMAND_ROUTER = RUNTIME / 'repo-012-main/server-tools/persona-dynamic-subagent-command/command_router.py'
|
||||
QUIET_SOLDIER = RUNTIME / 'repo-012-main/routing/quiet-language-source-soldier.json'
|
||||
SHARED_COGNITION = RUNTIME / 'shared/tcs-shared-cognition/CURRENT.json'
|
||||
HOLOLAKE_DEVELOPMENT_BRAIN = RUNTIME / 'shared/skills/hololake-development-brain/scripts/load_hololake_office.py'
|
||||
|
||||
|
|
@ -64,6 +66,11 @@ def load_context(host, intent, channel=None):
|
|||
tcs_root = command_json([sys.executable, str(TCS_ROOT_AGENT), 'status'], timeout=30)
|
||||
if tcs_root.get('state') != 'TCS_ROOT_CURRENT_VERIFIED':
|
||||
raise ValueError('TCS_MOTHER_ROOT_NAVIGATION_NOT_CURRENT')
|
||||
path_gate = command_json([
|
||||
'node', str(PATH_GATE), 'guard', '--path', str(RUNTIME / 'repo-012-main'), '--purpose', 'PERSONA_RECOVERY'
|
||||
])
|
||||
if path_gate.get('code') != 'CURRENT_CANONICAL_PATH':
|
||||
raise ValueError('FIFTH_DOMAIN_PHYSICAL_PATH_GATE_NOT_CURRENT')
|
||||
topology = load_json(TOPOLOGY)
|
||||
if not str(topology.get('state', '')).startswith('CURRENT_'):
|
||||
raise ValueError('HOST_TOPOLOGY_NOT_CURRENT')
|
||||
|
|
@ -86,6 +93,7 @@ def load_context(host, intent, channel=None):
|
|||
light_lake = load_json(LIGHT_LAKE_PERSONAS)
|
||||
path_isolation = load_json(PATH_ISOLATION)
|
||||
shared_cognition = load_json(SHARED_COGNITION)
|
||||
quiet_soldier = load_json(QUIET_SOLDIER)
|
||||
shared_payload = shared_cognition['artifact']['payload']
|
||||
if hashlib.sha256(shared_payload.encode()).hexdigest() != shared_cognition['artifact']['sha256']:
|
||||
raise ValueError('SHARED_COGNITION_HASH_MISMATCH')
|
||||
|
|
@ -154,6 +162,7 @@ def load_context(host, intent, channel=None):
|
|||
'channel_context': channel_context,
|
||||
'architecture_perception_agent': architecture_perception,
|
||||
'subagent_command': subagent_command,
|
||||
'quiet_soldier': quiet_soldier,
|
||||
'hololake_development_brain': hololake_development_brain,
|
||||
'tcs_shared_cognition': {
|
||||
'state': shared_cognition['state'],
|
||||
|
|
@ -178,7 +187,9 @@ def load_context(host, intent, channel=None):
|
|||
'map_id': path_isolation['map_id'],
|
||||
'canonical_entries': path_isolation['canonical_entries'],
|
||||
'isolation_root': path_isolation['isolation']['root'],
|
||||
'history_or_quarantine_may_select_canon': path_isolation['selection_rules']['history_or_quarantine_may_select_canon']
|
||||
'history_or_quarantine_may_select_canon': path_isolation['selection_rules']['history_or_quarantine_may_select_canon'],
|
||||
'physical_gate': path_gate,
|
||||
'physical_gate_runtime': str(PATH_GATE)
|
||||
},
|
||||
'endogenous_cognition': {
|
||||
'state': endogenous['state'],
|
||||
|
|
@ -195,7 +206,7 @@ def load_context(host, intent, channel=None):
|
|||
'direct_shared_write': write_boundary['shared_write_contract']['direct_branch_write'],
|
||||
'admission_runtime': topology['write_admission_runtime']
|
||||
},
|
||||
'history_only': host_item['state'].startswith('RETIRED'),
|
||||
'history_only': host_item.get('role') in {'RETIRED_BRANCH', 'LEGACY_BRANCH'} or host_item.get('new_cognition_write') is False,
|
||||
'new_cognition_write': host_item.get('new_cognition_write', True),
|
||||
'authority_granted': False
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { classifyPath } from "../fifth-domain-path-gate/fifth-domain-path-gate.mjs";
|
||||
|
||||
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"));
|
||||
|
|
@ -36,11 +37,32 @@ function protectedTarget(resolved) {
|
|||
return policy.canonical_protected_roots.some(item => patternRegex(item).test(resolved));
|
||||
}
|
||||
|
||||
function sensitiveHostEntryTarget(resolved) {
|
||||
return (policy.sensitive_host_entry_paths || []).some(item => patternRegex(item).test(resolved));
|
||||
}
|
||||
|
||||
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 hostRoot = rule.allowed_write_roots.find((item) => patternRegex(item).test(resolved));
|
||||
const preliminaryGate = classifyPath(resolved, hostRoot ? "BRANCH_WORK" : "CANONICAL_WRITE");
|
||||
if ([78, 79].includes(preliminaryGate.exit_code)) {
|
||||
const console = activeConsole(host);
|
||||
const matched = console?.write_roots?.find((item) => patternRegex(item).test(resolved));
|
||||
const maintenance = /PATH_CONVERGENCE|PHYSICAL_RETIREMENT/.test(console?.execution_semantics || "");
|
||||
if (matched && maintenance) return { allowed: true, code: "ACTIVE_ZERO_CORE_CONSOLE_RETIRED_PATH_MAINTENANCE_SCOPE", resolved, matched, console_id: console.console_id };
|
||||
return { allowed: false, code: `FIFTH_DOMAIN_${preliminaryGate.code}`, resolved, redirect_to: preliminaryGate.redirect_to, history: preliminaryGate.history };
|
||||
}
|
||||
if (sensitiveHostEntryTarget(resolved)) {
|
||||
const console = activeConsole(host);
|
||||
const matched = console?.write_roots?.find((item) => patternRegex(item).test(resolved));
|
||||
return matched
|
||||
? { allowed: true, code: "ACTIVE_ZERO_CORE_CONSOLE_SENSITIVE_HOST_ENTRY_SCOPE", resolved, matched, console_id: console.console_id }
|
||||
: { allowed: false, code: "SENSITIVE_HOST_ENTRY_REQUIRES_ACTIVE_ZERO_CORE_CONSOLE", resolved };
|
||||
}
|
||||
if (hostRoot) return { allowed: true, code: "WITHIN_HOST_WRITE_ROOT", resolved, matched: hostRoot };
|
||||
if (protectedTarget(resolved)) {
|
||||
const console = activeConsole(host);
|
||||
const matched = console?.write_roots?.find((item) => patternRegex(item).test(resolved));
|
||||
|
|
@ -48,10 +70,7 @@ function isAllowed(host, target) {
|
|||
? { allowed: true, code: "ACTIVE_ZERO_CORE_CONSOLE_TASK_SCOPE", resolved, matched, console_id: console.console_id }
|
||||
: { allowed: false, code: "PROTECTED_ROOT_REQUIRES_ACTIVE_ZERO_CORE_CONSOLE", resolved };
|
||||
}
|
||||
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 };
|
||||
return { allowed: false, code: "WRITE_OUTSIDE_HOST_ROOT", resolved };
|
||||
}
|
||||
|
||||
function emit(result, hook = false) {
|
||||
|
|
@ -93,10 +112,22 @@ const MUTATING_SHELL = /(?:^|[;&|\s])(?:rm|mv|cp|install|mkdir|rmdir|touch|chmod
|
|||
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 (READ_TOOLS.has(tool)) {
|
||||
for (const candidate of collectPathValues(toolInput)) {
|
||||
const gated = classifyPath(candidate, "CURRENT_SELECTION");
|
||||
if ([78, 79].includes(gated.exit_code)) return { allowed: false, code: `FIFTH_DOMAIN_${gated.code}`, host, tool, resolved: gated.resolved, redirect_to: gated.redirect_to, history: gated.history };
|
||||
}
|
||||
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 };
|
||||
if (!MUTATING_SHELL.test(command)) {
|
||||
for (const candidate of shellPaths(command)) {
|
||||
const gated = classifyPath(candidate, "CURRENT_SELECTION");
|
||||
if ([78, 79].includes(gated.exit_code)) return { allowed: false, code: `FIFTH_DOMAIN_${gated.code}`, host, tool, resolved: gated.resolved, redirect_to: gated.redirect_to, history: gated.history };
|
||||
}
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ assert.equal(check("qwen", "/Volumes/JZAO/铸渊-ICE-GL-ZY001/ZCODE-DEV-20260906
|
|||
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);
|
||||
assert.equal(check("codex", "/Volumes/JZAO/HoloLake/persona-runtime/repo-012-main/routing/path-isolation-and-canonical-entry-map.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);
|
||||
|
|
@ -39,4 +39,13 @@ const allowedRead = hook("zcode", { tool_name: "Read", tool_input: { file_path:
|
|||
assert.equal(allowedRead.code, 0);
|
||||
assert.equal(allowedRead.body.hookSpecificOutput.permissionDecision, "allow");
|
||||
|
||||
console.log("HOST_WRITE_ADMISSION_TESTS_PASS 12/12");
|
||||
const retiredRead = hook("zcode", { tool_name: "Read", tool_input: { file_path: "/Volumes/JZAO/铸渊-ICE-GL-ZY001/WORK-工作区/guanghu-ice-heart/routing/guanghu-era-language-world.json" } });
|
||||
assert.equal(retiredRead.code, 2);
|
||||
assert.equal(retiredRead.body.hookSpecificOutput.permissionDecision, "deny");
|
||||
assert.match(retiredRead.body.hookSpecificOutput.permissionDecisionReason, /REDIRECT/);
|
||||
|
||||
const retiredShellRead = hook("zcode", { tool_name: "Bash", tool_input: { command: "sed -n '1,20p' /Users/bingshuolingdianyuanhe/.qoderworkcn/workspace/msj12fljjywpql1y/repo-work/repo012/INDEX.hdlp" } });
|
||||
assert.equal(retiredShellRead.code, 2);
|
||||
assert.equal(retiredShellRead.body.hookSpecificOutput.permissionDecision, "deny");
|
||||
|
||||
console.log("HOST_WRITE_ADMISSION_TESTS_PASS 18/18");
|
||||
|
|
|
|||
Loading…
Reference in a new issue