chainlesschain 0.166.89 → 0.166.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -5
- package/package.json +3 -3
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-C8AkN9R5.js → AIOps-DgV7xb8F.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BrAzSQor.js → ActionButton-CgANpkKu.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-Cu56X8Oh.js → Analytics-DnvpX7RB.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BP1Riofl.js → AppLayout-C2bHccBM.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-CIW-nNAz.js → Artifacts-BIhijC9g.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CJtDabk-.js → Audit-CKRjWGdp.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DYIHeQ_P.js → BackgroundAgents-CblYtgBE.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-B4CsFzj7.js → Backup-Cq35b78a.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-3eoJ5kfb.js → BaseInput-DcB7NoRz.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BBhX2L_E.js → Chat-tc3b-s7i.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-RN8cbv7w.js → ChatBubbleRenderer-fcc_Tvqu.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-5UvmwHVk.js → Checkbox-BLD5bH5u.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BFy8zsBN.js → Codegen-BLOsoazN.js} +1 -1
- package/src/assets/web-panel/assets/{Col-4M6bKRRO.js → Col-Bdr7JwIx.js} +1 -1
- package/src/assets/web-panel/assets/{Community-D6LC0j7_.js → Community-C84BsWE0.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-Dd48xMcv.js → Compact-Dzdwd04z.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Ba85ggmy.js → Compliance-CCG-R0EV.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-D1BXlkuK.js → Cowork-BYj72SK0.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Ca2Uy7Ia.js → Cron-B6iKAwOf.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-C2bDRXZg.js → Crosschain-BJ-oDC6u.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DySZKAJf.js → DID-BnMckGLr.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DDpYf-TT.js → Dashboard-Cyje-gCO.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DGjROfPv.js → Dropdown-BpNlenjK.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DxX9QYiB.js → EmailListRenderer-BhonO7Nv.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-TBCZyrSJ.js → EvolutionSettings-DVoNU-Id.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BeqwTXBa.js → FamilyGuardDashboard-tdn-KC67.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CiCpKJQ1.js → Federation--EEVrHhz.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CdTmbLkU.js → FormItemContext-Cy8zr0-4.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BwbkEY-5.js → GenericCardRenderer-BuUhpgFi.js} +1 -1
- package/src/assets/web-panel/assets/{Git-VnAga2vv.js → Git-Ctd3E5Pe.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-i-4Ebs-H.js → Governance-Ce7dV6aU.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CirjTgNw.js → Inference-CD1vExIH.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CunS7vhA.js → KnowledgeGraph-Dn64OdQ7.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DBuTF0c6.js → Logs-PmElVRrT.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-B-iE-tt_.js → MarkdownRenderer-BFiVKf5t.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BK6ZivTS.js → Marketplace-OW4jTXwx.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-CUnCRnd3.js → McpTools-BEq2Ib4x.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-CBGGr8Gy.js → Memory-Can_P2oK.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-reB6iaWD.js → MobileBridge-DJ8R-dn3.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-o1YdVB7i.js → MobileProjects-B7kV9GOt.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-if5OE3GH.js → Mtc-BfxamolS.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-BTZWpq0j.js → MtcAudit-DHtuv7av.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-8cvMAYFL.js → Multisig-DLkLpvxc.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BMvUXAWo.js → NLProgramming-u4XtJTIt.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BWWjColh.js → Notes-BwuuPJLS.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-Ccr4lIhS.js → NotificationSettings-HEeu-E9v.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DhksGy6D.js → OrderTableRenderer-BtvN-Qwh.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DciXQfdY.js → Organization-Yh-czs3b.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-8DR7uMSx.js → Overflow-DmE8mYVM.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DE6euHYl.js → P2P-CWVck8WN.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-ByuWoauq.js → PdhVaultBrowser-DgFqekod.js} +4 -4
- package/src/assets/web-panel/assets/{Permissions-DkjM87ac.js → Permissions-BtJBVOHV.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-2DLp0KTL.js → PersonalDataHub-6VBObXzd.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-Ci2Qlkfr.js → Pipeline-D222FmqY.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DlwDnDVA.js → Privacy-Bi9FMikp.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-CmMifW39.js → ProjectInit-Bo52H47H.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DJVFIh-6.js → ProjectSettings-BPkesfOc.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-P2nWBS70.js → Projects-GynohSFX.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-B2cxaGCU.js → Providers-DVdMg04n.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CpO77kcE.js → QrScannerModal-DqJCb595.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BDQl1hb0.js → QuickAsk-J7loQbXi.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DTD2f3IN.js → Recommend-BI5aURVc.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DGZAnwvs.js → RemoteSession-BN2sqGvz.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-B_R4vitb.js → Reputation-BGOND-Fi.js} +1 -1
- package/src/assets/web-panel/assets/{Row-4Jj6JyXN.js → Row-77dfwaUk.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-D1t1MCZ-.js → RssFeed-FtBAxS3g.js} +2 -2
- package/src/assets/web-panel/assets/{Search-CyOUlIn7.js → Search-BaYVLFb7.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BmknPT5Q.js → Security-LE3Vv6ab.js} +4 -4
- package/src/assets/web-panel/assets/{Services-DkEK8mpa.js → Services-C5pP-Whz.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Bvo5bpuk.js → Skeleton-CLqKUVsF.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-AMmG8fim.js → Skills-BPjN0SX5.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-Dl7-OKOV.js → Sla-14jD70kg.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DtiGKu0E.js → SpeechSettings-Dr9ij42W.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Ch7mp3ES.js → SyncSettings-BzLHP7B8.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-Ck-sEgj-.js → Tasks-YqV9b0rV.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BlV22Il8.js → Templates-CxxAE4PK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CaaAu0mX.js → Tenant-CRb3PcKJ.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-lA9g3FtJ.js → Terminal-CTzPpBaP.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-CG26IFC3.js → TimelineRenderer-DjFUj_hy.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-iaJUBLBm.js → Tokens-Bbb8tPvx.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DWQJtLK3.js → Trigger-CSeWGM0W.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CTjmUwqp.js → Trust-JgESMBo3.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CMnxufgD.js → UkeySign-CiObLk0T.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-diCFSWJw.js → VideoEditing-CFEZaQ2x.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Ba1oND1K.js → Wallet-DR8ZGDNn.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DZDqPS-S.js → WebAuthn-DxZukvu7.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CfN8fPMe.js → WorkflowEditor-CKgSZQwn.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BiqMsO_i.js → chat-CTxEQE66.js} +1 -1
- package/src/assets/web-panel/assets/{colors-By8LkjqH.js → colors-CAjKMUu3.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-B0mxvRe3.js → compact-item-BXuz49at.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-Li2ub348.js → createContext-CFN_Hwr9.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CfB8DxT2.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DYbHPkbl.js → hasIn-BwoSBSfP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVZNYRzM.js → index-B0QbLlWn.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGZzexNa.js → index-B2o_BBjg.js} +1 -1
- package/src/assets/web-panel/assets/{index-CYv3JIzl.js → index-B2tEnBPx.js} +1 -1
- package/src/assets/web-panel/assets/{index-1lVoRUrQ.js → index-B7kp-zRE.js} +1 -1
- package/src/assets/web-panel/assets/{index-CTVPeUh3.js → index-B9yR7zqr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFhksbXp.js → index-BK4ftQ4d.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0D0HkkM.js → index-Bj4hIiLW.js} +1 -1
- package/src/assets/web-panel/assets/{index-CEp_3fKJ.js → index-BmQITjMU.js} +1 -1
- package/src/assets/web-panel/assets/{index-DIImi0cd.js → index-BmXpdcHI.js} +1 -1
- package/src/assets/web-panel/assets/{index-D6qL3QBU.js → index-Bqv_Cray.js} +1 -1
- package/src/assets/web-panel/assets/{index-BSk0xuXP.js → index-Bu2P6u8m.js} +1 -1
- package/src/assets/web-panel/assets/{index-zSzXsF9b.js → index-Bw9s2d3c.js} +1 -1
- package/src/assets/web-panel/assets/{index-beDji-cB.js → index-C8er4W9u.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-RE3mFh.js → index-CB3kVd9c.js} +1 -1
- package/src/assets/web-panel/assets/index-CCsRfd0k.js +1 -0
- package/src/assets/web-panel/assets/{index-CA0AOURT.js → index-CF9lWi1o.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMh3VxgO.js → index-CN53dE_u.js} +1 -1
- package/src/assets/web-panel/assets/{index-KWt8Owo8.js → index-CSmFby3Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-DB4DCzqU.js → index-CehdwJ_Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-CHgiSV6R.js → index-Cgk0YEiq.js} +1 -1
- package/src/assets/web-panel/assets/{index-b4O_YBVR.js → index-Cp5y7lX_.js} +1 -1
- package/src/assets/web-panel/assets/index-CsmiZIQ4.js +1 -0
- package/src/assets/web-panel/assets/{index-DAOpBv3C.js → index-DTX9CJPx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZqTG7fP.js → index-DViK2bkv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bc6bxU24.js → index-DY-gBSm8.js} +1 -1
- package/src/assets/web-panel/assets/{index-Czt7WAtA.js → index-DjF5BOZx.js} +1 -1
- package/src/assets/web-panel/assets/{index-KqfqSdAC.js → index-DmkbAADP.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bd5znvCV.js → index-DpdDnZX_.js} +1 -1
- package/src/assets/web-panel/assets/{index-C5Se6Lnv.js → index-Dv7V2kg6.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cpfn4YbT.js → index-DxunxaTw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcOJEgBE.js → index-F2E5RCwz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgyrklzQ.js → index-IY9bd5az.js} +1 -1
- package/src/assets/web-panel/assets/{index-ClsJN8sW.js → index-IahILygB.js} +3 -3
- package/src/assets/web-panel/assets/{index-hw3sCYAz.js → index-T3MTE6jB.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bm2P2Kgp.js → index-Tz5dheZa.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRzewWeE.js → index-_ZtCyUdz.js} +1 -1
- package/src/assets/web-panel/assets/{index-DPRgnazi.js → index-o9DpkZKf.js} +1 -1
- package/src/assets/web-panel/assets/{index-ByqFKMWL.js → index-t2Ch_JKn.js} +1 -1
- package/src/assets/web-panel/assets/{index-DgMFnBOv.js → index-u9_DM3ZC.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-TgDL_7OY.js → initDefaultProps-BoiGJDoO.js} +1 -1
- package/src/assets/web-panel/assets/{motion-fHosyV5c.js → motion-5eL4E0pA.js} +1 -1
- package/src/assets/web-panel/assets/{move-C_QfW5qg.js → move-DjggDi_1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-u7ywDVOg.js → mtc-parser-Ce8Vo2T3.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BEJdUgE9.js → omit-D1yOBaO2.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DGkQFAJ5.js → pickAttrs-8NLceQe8.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DUyZXAjt.js → placementArrow-CsIdBRmQ.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-B4xDxO7Q.js → responsiveObserve-GM5NE8En.js} +1 -1
- package/src/assets/web-panel/assets/{slide-B8WuJtst.js → slide-44yj3u49.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DLUqy7JO.js → statusUtils-B8tqtWcX.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BYOJOyqL.js → styleChecker-NhDPBbyB.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-WChWQURv.js → useFlexGapSupport-C8Qz1GtN.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-vLNpm3dv.js → useFs-ClUv4kih.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DSZ8unoU.js → usePersonalDataHub-BwkOVEh6.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-C4Xf7Kg1.js → vnode--gMF33R0.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BulmeAGy.js → zoom-DXg3TqOP.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +5 -5
- package/src/commands/agent-process-ownership.js +121 -0
- package/src/commands/agent.js +2 -0
- package/src/commands/automation.js +35 -8
- package/src/commands/hub.js +224 -0
- package/src/commands/project.js +162 -0
- package/src/data/changelog.json +26 -0
- package/src/gateways/ws/personal-data-hub-protocol.js +54 -8
- package/src/lib/audit-logger.js +59 -46
- package/src/lib/audit-sanitizer.js +279 -0
- package/src/lib/automation-center.js +14 -4
- package/src/lib/automation-engine.js +151 -39
- package/src/lib/checkpoint-restore-saga.js +11 -1
- package/src/lib/eval/verify01-host-evidence.js +36 -3
- package/src/lib/knowledge-graph.js +27 -0
- package/src/lib/personal-data-hub-wiring.js +44 -1
- package/src/lib/process-execution-broker/index.js +91 -1
- package/src/lib/process-execution-broker/platform-sandbox.js +42 -0
- package/src/lib/process-execution-broker/windows-native-evaluator-capabilities.js +757 -0
- package/src/lib/process-execution-broker/windows-native-evaluator.js +316 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
- package/src/lib/process-execution-broker/windows-sandbox.cs +383 -11
- package/src/lib/scheduler-kernel/automation-adapter.js +15 -3
- package/src/lib/scheduler-kernel/automation-event-adapter.js +15 -3
- package/src/assets/web-panel/assets/devWarning-D90arbUg.js +0 -1
- package/src/assets/web-panel/assets/index-CeP1oROI.js +0 -1
- package/src/assets/web-panel/assets/index-Cu9gkyRc.js +0 -1
|
@@ -0,0 +1,757 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import { createWindowsNativeEvaluator } from "./windows-native-evaluator.js";
|
|
6
|
+
|
|
7
|
+
const SCHEMA = "chainlesschain.windows-native-evaluator-capabilities/v1";
|
|
8
|
+
const PREFIX = "CC_NATIVE_CAPABILITIES:";
|
|
9
|
+
const HASH = /^sha256:[a-f0-9]{64}$/u;
|
|
10
|
+
const JOURNAL_FILE = "capability-journal.jsonl";
|
|
11
|
+
const JOURNAL_MAX_BYTES = 64 * 1024;
|
|
12
|
+
const PROBES = [
|
|
13
|
+
"scratch-environment",
|
|
14
|
+
"esm",
|
|
15
|
+
"worker-threads",
|
|
16
|
+
"child-inherited-stdio",
|
|
17
|
+
"child-file-stdio",
|
|
18
|
+
"child-pipe-stdio",
|
|
19
|
+
"child-fork-ipc",
|
|
20
|
+
];
|
|
21
|
+
const sha256 = (value) =>
|
|
22
|
+
`sha256:${createHash("sha256").update(value).digest("hex")}`;
|
|
23
|
+
const requireCondition = (condition, message) => {
|
|
24
|
+
if (!condition) throw new Error(`Native capability diagnostic: ${message}`);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// These are diagnostic fixtures, never candidate answers or a replacement test runner.
|
|
28
|
+
const FIXTURES = Object.freeze({
|
|
29
|
+
"esm-probe.mjs": 'export const marker = "native-esm-ok";\n',
|
|
30
|
+
"worker-probe.cjs":
|
|
31
|
+
'const {parentPort,threadId}=require("node:worker_threads"); parentPort.postMessage({marker:"native-worker-ok",threadId,pid:process.pid});\n',
|
|
32
|
+
"child-probe.cjs":
|
|
33
|
+
'const fs=require("node:fs"); const value=JSON.stringify({marker:"native-child-ok",pid:process.pid}); if(process.argv[2])fs.writeFileSync(process.argv[2],value,{flag:"wx"}); else process.stdout.write(value);\n',
|
|
34
|
+
"fork-probe.cjs":
|
|
35
|
+
'if(typeof process.send!=="function")process.exit(72); process.send({marker:"native-fork-ok",pid:process.pid},error=>{if(error)process.exitCode=73;process.disconnect();});\n',
|
|
36
|
+
"anchor.txt": "private-native-capability-input\n",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
function checkSource(probeTimeoutMs) {
|
|
40
|
+
return `
|
|
41
|
+
const fs=require('node:fs');
|
|
42
|
+
const journalFd=fs.openSync(process.argv[3]+'/'+${JSON.stringify(JOURNAL_FILE)},'wx',0o600);
|
|
43
|
+
let journalBytes=0,journalSequence=0;
|
|
44
|
+
const journalStart=Date.now();
|
|
45
|
+
function journal(event,extra={}){
|
|
46
|
+
const bytes=Buffer.from(JSON.stringify({version:1,sequence:journalSequence+1,event,pid:process.pid,elapsedMs:Date.now()-journalStart,...extra})+'\\n');
|
|
47
|
+
if(bytes.length>16384||journalBytes+bytes.length>${JOURNAL_MAX_BYTES}||journalSequence>=17)throw new Error('Capability journal bound exceeded');
|
|
48
|
+
let offset=0;while(offset<bytes.length){const written=fs.writeSync(journalFd,bytes,offset,bytes.length-offset);if(written<=0)throw new Error('Capability journal write stalled');offset+=written;}
|
|
49
|
+
fs.fsyncSync(journalFd);journalBytes+=bytes.length;journalSequence++;
|
|
50
|
+
}
|
|
51
|
+
journal('initialization-started');
|
|
52
|
+
const path=require('node:path'),os=require('node:os');
|
|
53
|
+
const crypto=require('node:crypto'),cp=require('node:child_process');
|
|
54
|
+
const {pathToFileURL}=require('node:url'),{Worker}=require('node:worker_threads');
|
|
55
|
+
const workspace=process.argv[2],scratch=process.argv[3],timeout=${probeTimeoutMs};
|
|
56
|
+
const observations=[];
|
|
57
|
+
const hash=value=>'sha256:'+crypto.createHash('sha256').update(value).digest('hex');
|
|
58
|
+
journal('initialization-settled');
|
|
59
|
+
function settled(value){journal('settled',{id:value.id,observation:value});observations.push(value);}
|
|
60
|
+
function observe(id,operation){
|
|
61
|
+
const start=Date.now();
|
|
62
|
+
journal('started',{id});
|
|
63
|
+
return Promise.resolve().then(operation).then(value=>{
|
|
64
|
+
const output=String(value.output??'');
|
|
65
|
+
settled({id,status:value.supported?'supported':value.timedOut?'timed-out':['EACCES','EPERM'].includes(value.errorCode)?'blocked':'failed',timedOut:value.timedOut===true,code:value.code??null,signal:value.signal??null,errorCode:value.errorCode??null,error:value.error??null,output,outputSha256:hash(output),details:value.details??{},elapsedMs:Date.now()-start});
|
|
66
|
+
},error=>{
|
|
67
|
+
const output=String(error.stack||error);
|
|
68
|
+
settled({id,status:['EACCES','EPERM'].includes(error.code)?'blocked':'failed',timedOut:false,code:null,signal:null,errorCode:error.code??null,error:String(error.message||error),output,outputSha256:hash(output),details:{},elapsedMs:Date.now()-start});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function childResult(result,output){
|
|
72
|
+
let payload=null;try{payload=JSON.parse(output);}catch{}
|
|
73
|
+
return {supported:!result.error&&!result.signal&&result.status===0&&payload?.marker==='native-child-ok'&&Number.isSafeInteger(payload.pid)&&payload.pid!==process.pid,code:result.status,signal:result.signal,errorCode:result.error?.code??null,error:result.error?.message??null,timedOut:result.error?.code==='ETIMEDOUT',output,details:{stderr:result.stderr?.toString('utf8')||'',payload}};
|
|
74
|
+
}
|
|
75
|
+
function asyncProbe(create,valid){
|
|
76
|
+
return new Promise(resolve=>{
|
|
77
|
+
let child,settled=false,message=null,error=null,timedOut=false;
|
|
78
|
+
const done=(code,signal)=>{if(settled)return;settled=true;clearTimeout(timer);resolve({supported:!timedOut&&!error&&code===0&&!signal&&valid(message),timedOut,code,signal,errorCode:error?.code??null,error:error?String(error.stack||error):null,output:message===null?'':JSON.stringify(message),details:{message}});};
|
|
79
|
+
const timer=setTimeout(()=>{timedOut=true;if(child instanceof Worker)child.terminate().catch(()=>{});else child?.kill();done(null,null);},timeout);
|
|
80
|
+
try{child=create();child.on('message',value=>{message=value;});child.once('error',value=>{error=value;done(null,null);});child.once('exit',done);}catch(value){error=value;done(null,null);}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
(async()=>{
|
|
84
|
+
await observe('scratch-environment',()=>{
|
|
85
|
+
const inheritedKeys=Object.keys(process.env).sort();
|
|
86
|
+
const credentialKeys=inheritedKeys.filter(key=>/TOKEN|SECRET|PASSWORD|CREDENTIAL|API_KEY|AUTHORIZATION/i.test(key));
|
|
87
|
+
const derived={TMP:path.join(scratch,'tmp'),TEMP:path.join(scratch,'tmp'),HOME:path.join(scratch,'home'),USERPROFILE:path.join(scratch,'home'),APPDATA:path.join(scratch,'home','AppData','Roaming'),LOCALAPPDATA:path.join(scratch,'home','AppData','Local')};
|
|
88
|
+
for(const directory of new Set(Object.values(derived)))fs.mkdirSync(directory,{recursive:true});
|
|
89
|
+
for(const [key,value]of Object.entries(derived))process.env[key]=value;
|
|
90
|
+
const proofs=[];
|
|
91
|
+
for(const [key,value]of Object.entries(derived)){const file=path.join(value,key+'.txt');fs.writeFileSync(file,key,{flag:'wx'});proofs.push({key,path:value,value:fs.readFileSync(file,'utf8')});}
|
|
92
|
+
const details={credentialKeys,inheritedKeys,derived,proofs,tmpdir:os.tmpdir(),homedir:os.homedir(),credentialsAbsent:credentialKeys.length===0,nodeOptionsAbsent:!Object.hasOwn(process.env,'NODE_OPTIONS')};
|
|
93
|
+
return {supported:details.credentialsAbsent&&details.nodeOptionsAbsent&&os.tmpdir()===derived.TEMP&&os.homedir()===derived.USERPROFILE,code:0,output:JSON.stringify(details),details};
|
|
94
|
+
});
|
|
95
|
+
await observe('esm',async()=>{
|
|
96
|
+
let timer;
|
|
97
|
+
try{return await Promise.race([
|
|
98
|
+
import(pathToFileURL(path.join(workspace,'esm-probe.mjs')).href).then(value=>({supported:value.marker==='native-esm-ok',code:0,output:JSON.stringify({marker:value.marker})})),
|
|
99
|
+
new Promise(resolve=>{timer=setTimeout(()=>resolve({supported:false,timedOut:true,code:null,errorCode:'ETIMEDOUT',error:'ESM import deadline exceeded',output:''}),timeout);})
|
|
100
|
+
]);}finally{clearTimeout(timer);}
|
|
101
|
+
});
|
|
102
|
+
await observe('worker-threads',()=>asyncProbe(()=>new Worker(path.join(workspace,'worker-probe.cjs'),{execArgv:['--preserve-symlinks','--preserve-symlinks-main']}),value=>value?.marker==='native-worker-ok'&&value.pid===process.pid&&Number.isSafeInteger(value.threadId)&&value.threadId>0));
|
|
103
|
+
await observe('child-inherited-stdio',()=>{
|
|
104
|
+
const proof=path.join(scratch,'inherited-child.json');
|
|
105
|
+
const result=cp.spawnSync(process.execPath,['--preserve-symlinks','--preserve-symlinks-main',path.join(workspace,'child-probe.cjs'),proof],{stdio:'inherit',windowsHide:true,timeout});
|
|
106
|
+
return childResult(result,fs.existsSync(proof)?fs.readFileSync(proof,'utf8'):'');
|
|
107
|
+
});
|
|
108
|
+
await observe('child-file-stdio',()=>{
|
|
109
|
+
const outputFile=path.join(scratch,'fd-stdout.txt'),errorFile=path.join(scratch,'fd-stderr.txt');
|
|
110
|
+
const out=fs.openSync(outputFile,'wx'),err=fs.openSync(errorFile,'wx');let result;
|
|
111
|
+
try{result=cp.spawnSync(process.execPath,['--preserve-symlinks','--preserve-symlinks-main',path.join(workspace,'child-probe.cjs')],{stdio:['ignore',out,err],windowsHide:true,timeout});}finally{fs.closeSync(out);fs.closeSync(err);}
|
|
112
|
+
result.stderr=fs.readFileSync(errorFile);return childResult(result,fs.readFileSync(outputFile,'utf8'));
|
|
113
|
+
});
|
|
114
|
+
await observe('child-pipe-stdio',()=>{
|
|
115
|
+
const result=cp.spawnSync(process.execPath,['--preserve-symlinks','--preserve-symlinks-main',path.join(workspace,'child-probe.cjs')],{windowsHide:true,encoding:'utf8',timeout,maxBuffer:65536});
|
|
116
|
+
return childResult(result,result.stdout||'');
|
|
117
|
+
});
|
|
118
|
+
await observe('child-fork-ipc',()=>asyncProbe(()=>cp.fork(path.join(workspace,'fork-probe.cjs'),[],{execArgv:['--preserve-symlinks','--preserve-symlinks-main'],windowsHide:true,stdio:['inherit','inherit','inherit','ipc']}),value=>value?.marker==='native-fork-ok'&&Number.isSafeInteger(value.pid)&&value.pid!==process.pid));
|
|
119
|
+
journal('completed');
|
|
120
|
+
process.stdout.write(${JSON.stringify(PREFIX)}+JSON.stringify({version:1,pid:process.pid,workspace,scratch,observations})+'\\n');
|
|
121
|
+
})().catch(error=>{console.error(error.stack||error);process.exitCode=74;}).finally(()=>fs.closeSync(journalFd));
|
|
122
|
+
`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function validateJournal(report, complete) {
|
|
126
|
+
const artifact = report.journal;
|
|
127
|
+
if (!artifact) {
|
|
128
|
+
requireCondition(!complete, "completed target omitted its journal");
|
|
129
|
+
return { available: false, records: [], lastPhase: null, complete: false };
|
|
130
|
+
}
|
|
131
|
+
requireCondition(
|
|
132
|
+
artifact.name === JOURNAL_FILE &&
|
|
133
|
+
typeof artifact.raw === "string" &&
|
|
134
|
+
Buffer.byteLength(artifact.raw) <= JOURNAL_MAX_BYTES &&
|
|
135
|
+
artifact.sha256 === sha256(artifact.raw),
|
|
136
|
+
"journal byte binding or bound changed",
|
|
137
|
+
);
|
|
138
|
+
const boundary = artifact.raw.lastIndexOf("\n");
|
|
139
|
+
const lines = boundary < 0 ? [] : artifact.raw.slice(0, boundary).split("\n");
|
|
140
|
+
const tail = artifact.raw.slice(boundary + 1);
|
|
141
|
+
const expected = [
|
|
142
|
+
{ event: "initialization-started" },
|
|
143
|
+
{ event: "initialization-settled" },
|
|
144
|
+
...PROBES.flatMap((id) => [
|
|
145
|
+
{ event: "started", id },
|
|
146
|
+
{ event: "settled", id },
|
|
147
|
+
]),
|
|
148
|
+
{ event: "completed" },
|
|
149
|
+
];
|
|
150
|
+
requireCondition(lines.length <= expected.length, "journal has extra events");
|
|
151
|
+
let previousElapsed = -1;
|
|
152
|
+
const records = lines.map((line, index) => {
|
|
153
|
+
requireCondition(
|
|
154
|
+
Buffer.byteLength(line) <= 16384,
|
|
155
|
+
"journal event exceeds bound",
|
|
156
|
+
);
|
|
157
|
+
const row = JSON.parse(line),
|
|
158
|
+
wanted = expected[index];
|
|
159
|
+
requireCondition(
|
|
160
|
+
row.version === 1 &&
|
|
161
|
+
row.sequence === index + 1 &&
|
|
162
|
+
row.pid === report.settlement.targetPid &&
|
|
163
|
+
row.event === wanted.event &&
|
|
164
|
+
row.id === wanted.id &&
|
|
165
|
+
Number.isSafeInteger(row.elapsedMs) &&
|
|
166
|
+
row.elapsedMs >= previousElapsed,
|
|
167
|
+
"journal phase, sequence or process identity differs",
|
|
168
|
+
);
|
|
169
|
+
previousElapsed = row.elapsedMs;
|
|
170
|
+
if (row.event === "settled") {
|
|
171
|
+
const observed = row.observation;
|
|
172
|
+
requireCondition(
|
|
173
|
+
observed?.id === row.id &&
|
|
174
|
+
typeof observed.output === "string" &&
|
|
175
|
+
sha256(observed.output) === observed.outputSha256 &&
|
|
176
|
+
["supported", "blocked", "timed-out", "failed"].includes(
|
|
177
|
+
observed.status,
|
|
178
|
+
) &&
|
|
179
|
+
typeof observed.timedOut === "boolean" &&
|
|
180
|
+
Number.isSafeInteger(observed.elapsedMs) &&
|
|
181
|
+
observed.elapsedMs >= 0,
|
|
182
|
+
"journal observation identity or digest changed",
|
|
183
|
+
);
|
|
184
|
+
} else
|
|
185
|
+
requireCondition(
|
|
186
|
+
row.observation === undefined,
|
|
187
|
+
"unexpected journal observation",
|
|
188
|
+
);
|
|
189
|
+
return row;
|
|
190
|
+
});
|
|
191
|
+
if (complete)
|
|
192
|
+
requireCondition(
|
|
193
|
+
lines.length === expected.length && tail === "",
|
|
194
|
+
"target journal incomplete",
|
|
195
|
+
);
|
|
196
|
+
return {
|
|
197
|
+
available: true,
|
|
198
|
+
records,
|
|
199
|
+
lastPhase: records.at(-1)?.id || records.at(-1)?.event || null,
|
|
200
|
+
complete: records.length === expected.length && tail === "",
|
|
201
|
+
trailingPartialBytes: Buffer.byteLength(tail),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function sameJournalIdentity(left, right) {
|
|
206
|
+
return (
|
|
207
|
+
left.dev === right.dev &&
|
|
208
|
+
left.ino === right.ino &&
|
|
209
|
+
left.size === right.size &&
|
|
210
|
+
left.mtimeNs === right.mtimeNs &&
|
|
211
|
+
left.ctimeNs === right.ctimeNs &&
|
|
212
|
+
left.nlink === 1n &&
|
|
213
|
+
right.nlink === 1n &&
|
|
214
|
+
left.isFile() &&
|
|
215
|
+
right.isFile()
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function journalDescriptorIdentity(file) {
|
|
220
|
+
const fd = fs.openSync(file, "r");
|
|
221
|
+
try {
|
|
222
|
+
return fs.fstatSync(fd, { bigint: true });
|
|
223
|
+
} finally {
|
|
224
|
+
fs.closeSync(fd);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function readJournalAfterSettlement(report) {
|
|
229
|
+
requireCondition(
|
|
230
|
+
report.settlement?.cleanupConfirmed === true &&
|
|
231
|
+
report.settlement.manifestDigest === report.manifestDigest.slice(7),
|
|
232
|
+
"scratch read requires confirmed native cleanup",
|
|
233
|
+
);
|
|
234
|
+
const scratch = report.stage.scratch;
|
|
235
|
+
const expected = report.manifest.directories.find(
|
|
236
|
+
(entry) => entry.path === scratch,
|
|
237
|
+
);
|
|
238
|
+
const directory = fs.lstatSync(scratch, { bigint: true });
|
|
239
|
+
requireCondition(
|
|
240
|
+
expected &&
|
|
241
|
+
directory.isDirectory() &&
|
|
242
|
+
!directory.isSymbolicLink() &&
|
|
243
|
+
String(directory.dev) === expected.dev &&
|
|
244
|
+
String(directory.ino) === expected.ino &&
|
|
245
|
+
fs.realpathSync.native(scratch).toLowerCase() === scratch.toLowerCase(),
|
|
246
|
+
"scratch identity changed before journal read",
|
|
247
|
+
);
|
|
248
|
+
const file = path.join(scratch, JOURNAL_FILE);
|
|
249
|
+
let before;
|
|
250
|
+
try {
|
|
251
|
+
before = fs.lstatSync(file, { bigint: true });
|
|
252
|
+
} catch (error) {
|
|
253
|
+
if (error.code === "ENOENT") return null;
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
requireCondition(
|
|
257
|
+
before.isFile() &&
|
|
258
|
+
!before.isSymbolicLink() &&
|
|
259
|
+
before.nlink === 1n &&
|
|
260
|
+
before.size <= BigInt(JOURNAL_MAX_BYTES) &&
|
|
261
|
+
fs.realpathSync.native(file).toLowerCase() === file.toLowerCase(),
|
|
262
|
+
"journal is linked or exceeds byte bound",
|
|
263
|
+
);
|
|
264
|
+
const fd = fs.openSync(file, "r");
|
|
265
|
+
try {
|
|
266
|
+
const opened = fs.fstatSync(fd, { bigint: true });
|
|
267
|
+
// Windows pathname and descriptor stat can expose different dev/ino
|
|
268
|
+
// projections. Bind the reader to another handle opened by name, and
|
|
269
|
+
// compare each stat domain only with itself before and after the read.
|
|
270
|
+
requireCondition(
|
|
271
|
+
sameJournalIdentity(opened, journalDescriptorIdentity(file)) &&
|
|
272
|
+
opened.size === before.size &&
|
|
273
|
+
opened.size <= BigInt(JOURNAL_MAX_BYTES),
|
|
274
|
+
"journal identity changed while opening",
|
|
275
|
+
);
|
|
276
|
+
const bytes = Buffer.alloc(Number(opened.size));
|
|
277
|
+
let offset = 0;
|
|
278
|
+
while (offset < bytes.length) {
|
|
279
|
+
const read = fs.readSync(
|
|
280
|
+
fd,
|
|
281
|
+
bytes,
|
|
282
|
+
offset,
|
|
283
|
+
bytes.length - offset,
|
|
284
|
+
offset,
|
|
285
|
+
);
|
|
286
|
+
requireCondition(read > 0, "journal truncated while reading");
|
|
287
|
+
offset += read;
|
|
288
|
+
}
|
|
289
|
+
const after = fs.fstatSync(fd, { bigint: true });
|
|
290
|
+
const named = fs.lstatSync(file, { bigint: true });
|
|
291
|
+
requireCondition(
|
|
292
|
+
sameJournalIdentity(after, opened) &&
|
|
293
|
+
sameJournalIdentity(named, before) &&
|
|
294
|
+
sameJournalIdentity(after, journalDescriptorIdentity(file)) &&
|
|
295
|
+
!named.isSymbolicLink() &&
|
|
296
|
+
fs.realpathSync.native(file).toLowerCase() === file.toLowerCase(),
|
|
297
|
+
"journal changed during bounded read",
|
|
298
|
+
);
|
|
299
|
+
const raw = bytes.toString("utf8");
|
|
300
|
+
requireCondition(
|
|
301
|
+
Buffer.from(raw, "utf8").equals(bytes),
|
|
302
|
+
"journal is not valid UTF-8",
|
|
303
|
+
);
|
|
304
|
+
return {
|
|
305
|
+
name: JOURNAL_FILE,
|
|
306
|
+
raw,
|
|
307
|
+
bytes: bytes.length,
|
|
308
|
+
sha256: sha256(bytes),
|
|
309
|
+
};
|
|
310
|
+
} finally {
|
|
311
|
+
fs.closeSync(fd);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** Incomplete captures may be inspected explicitly, but never grant capabilities. */
|
|
316
|
+
export function validateWindowsNativeEvaluatorCapabilitiesReport(
|
|
317
|
+
report,
|
|
318
|
+
{ allowPartial = false } = {},
|
|
319
|
+
) {
|
|
320
|
+
requireCondition(report?.schema === SCHEMA, "unknown report schema");
|
|
321
|
+
requireCondition(
|
|
322
|
+
report.formalSample === false &&
|
|
323
|
+
report.providerAssessed === false &&
|
|
324
|
+
report.fullReviewPackAssessed === false,
|
|
325
|
+
"diagnostic scope changed",
|
|
326
|
+
);
|
|
327
|
+
requireCondition(HASH.test(report.manifestDigest), "manifest digest missing");
|
|
328
|
+
requireCondition(
|
|
329
|
+
sha256(JSON.stringify(report.manifest)) === report.manifestDigest &&
|
|
330
|
+
report.checkSourceSha256 ===
|
|
331
|
+
`sha256:${report.manifest.files.find((file) => file.path === report.manifest.check)?.sha256}`,
|
|
332
|
+
"staged source manifest digest mismatch",
|
|
333
|
+
);
|
|
334
|
+
requireCondition(
|
|
335
|
+
report.settlement?.manifestDigest === report.manifestDigest.slice(7) &&
|
|
336
|
+
report.settlement.cleanupConfirmed === true &&
|
|
337
|
+
report.settlement.capabilityCount === 0 &&
|
|
338
|
+
report.settlement.loopbackExemptionAbsent === true &&
|
|
339
|
+
/^[a-f0-9]{64}$/u.test(report.settlement.supervisorUserSidSha256),
|
|
340
|
+
"native cleanup settlement is not confirmed",
|
|
341
|
+
);
|
|
342
|
+
const execution = report.execution;
|
|
343
|
+
requireCondition(
|
|
344
|
+
execution &&
|
|
345
|
+
typeof execution.stdout === "string" &&
|
|
346
|
+
typeof execution.stderr === "string" &&
|
|
347
|
+
Buffer.byteLength(execution.stdout) <= 8 * 1024 * 1024 &&
|
|
348
|
+
Buffer.byteLength(execution.stderr) <= 8 * 1024 * 1024,
|
|
349
|
+
"target execution incomplete",
|
|
350
|
+
);
|
|
351
|
+
requireCondition(
|
|
352
|
+
sha256(execution.stdout) === execution.stdoutSha256 &&
|
|
353
|
+
sha256(execution.stderr) === execution.stderrSha256,
|
|
354
|
+
"raw output digest mismatch",
|
|
355
|
+
);
|
|
356
|
+
const executionCompleted =
|
|
357
|
+
execution.status === 0 &&
|
|
358
|
+
execution.signal === null &&
|
|
359
|
+
execution.error === null &&
|
|
360
|
+
report.settlement.targetExitCode === 0 &&
|
|
361
|
+
report.settlement.executionFailed === false;
|
|
362
|
+
if (!executionCompleted) {
|
|
363
|
+
const timedOut = /Windows sandbox wall-time limit exceeded/u.test(
|
|
364
|
+
execution.stderr,
|
|
365
|
+
);
|
|
366
|
+
requireCondition(
|
|
367
|
+
allowPartial,
|
|
368
|
+
timedOut
|
|
369
|
+
? "target execution timed out; native cleanup confirmed"
|
|
370
|
+
: "target execution incomplete; native cleanup confirmed",
|
|
371
|
+
);
|
|
372
|
+
const journal = validateJournal(report, false);
|
|
373
|
+
return {
|
|
374
|
+
diagnosticCompleted: false,
|
|
375
|
+
allCapabilitiesSupported: false,
|
|
376
|
+
capabilities: {},
|
|
377
|
+
failureKind: timedOut ? "execution-timeout" : "execution-failure",
|
|
378
|
+
cleanupConfirmed: true,
|
|
379
|
+
journal,
|
|
380
|
+
observedPrefix: journal.records
|
|
381
|
+
.filter((row) => row.event === "settled")
|
|
382
|
+
.map((row) => row.observation),
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
const lines = execution.stdout.trim().split(/\r?\n/u);
|
|
386
|
+
requireCondition(
|
|
387
|
+
lines.length === 1 && lines[0].startsWith(PREFIX),
|
|
388
|
+
"expected exactly one target report",
|
|
389
|
+
);
|
|
390
|
+
const target = JSON.parse(lines[0].slice(PREFIX.length));
|
|
391
|
+
requireCondition(
|
|
392
|
+
target.version === 1 &&
|
|
393
|
+
Number.isSafeInteger(target.pid) &&
|
|
394
|
+
target.pid > 0 &&
|
|
395
|
+
target.pid === report.settlement.targetPid &&
|
|
396
|
+
report.stage.workspace === report.manifest.workspace &&
|
|
397
|
+
report.stage.scratch === report.manifest.scratch &&
|
|
398
|
+
target.workspace === report.stage.workspace &&
|
|
399
|
+
target.scratch === report.stage.scratch &&
|
|
400
|
+
Array.isArray(target.observations) &&
|
|
401
|
+
JSON.stringify(target.observations.map((item) => item.id)) ===
|
|
402
|
+
JSON.stringify(PROBES),
|
|
403
|
+
"target identity or probe sequence mismatch",
|
|
404
|
+
);
|
|
405
|
+
for (const observation of target.observations) {
|
|
406
|
+
requireCondition(
|
|
407
|
+
["supported", "blocked", "timed-out", "failed"].includes(
|
|
408
|
+
observation.status,
|
|
409
|
+
) &&
|
|
410
|
+
(observation.code === null || Number.isInteger(observation.code)) &&
|
|
411
|
+
(observation.signal === null ||
|
|
412
|
+
typeof observation.signal === "string") &&
|
|
413
|
+
typeof observation.timedOut === "boolean" &&
|
|
414
|
+
typeof observation.output === "string" &&
|
|
415
|
+
sha256(observation.output) === observation.outputSha256 &&
|
|
416
|
+
Number.isSafeInteger(observation.elapsedMs) &&
|
|
417
|
+
observation.elapsedMs >= 0,
|
|
418
|
+
"malformed probe observation",
|
|
419
|
+
);
|
|
420
|
+
if (observation.status === "supported") {
|
|
421
|
+
requireCondition(
|
|
422
|
+
observation.code === 0 &&
|
|
423
|
+
observation.signal === null &&
|
|
424
|
+
observation.error === null &&
|
|
425
|
+
observation.errorCode === null,
|
|
426
|
+
"failed observation cannot advertise support",
|
|
427
|
+
);
|
|
428
|
+
const payload = JSON.parse(observation.output);
|
|
429
|
+
if (observation.id === "scratch-environment") {
|
|
430
|
+
requireCondition(
|
|
431
|
+
payload.credentialsAbsent === true &&
|
|
432
|
+
payload.nodeOptionsAbsent === true &&
|
|
433
|
+
Array.isArray(payload.credentialKeys) &&
|
|
434
|
+
payload.credentialKeys.length === 0,
|
|
435
|
+
"credential environment was not isolated",
|
|
436
|
+
);
|
|
437
|
+
for (const name of [
|
|
438
|
+
"TMP",
|
|
439
|
+
"TEMP",
|
|
440
|
+
"HOME",
|
|
441
|
+
"USERPROFILE",
|
|
442
|
+
"APPDATA",
|
|
443
|
+
"LOCALAPPDATA",
|
|
444
|
+
]) {
|
|
445
|
+
const relative = path.win32.relative(
|
|
446
|
+
target.scratch,
|
|
447
|
+
payload.derived[name],
|
|
448
|
+
);
|
|
449
|
+
requireCondition(
|
|
450
|
+
relative &&
|
|
451
|
+
!relative.startsWith("..") &&
|
|
452
|
+
!path.win32.isAbsolute(relative) &&
|
|
453
|
+
payload.proofs.some(
|
|
454
|
+
(proof) =>
|
|
455
|
+
proof.key === name &&
|
|
456
|
+
proof.path === payload.derived[name] &&
|
|
457
|
+
proof.value === name,
|
|
458
|
+
),
|
|
459
|
+
"environment escaped scratch or omitted a write/read proof",
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
requireCondition(
|
|
463
|
+
payload.tmpdir === payload.derived.TEMP &&
|
|
464
|
+
payload.homedir === payload.derived.USERPROFILE,
|
|
465
|
+
"temporary or home directory differs from isolated environment",
|
|
466
|
+
);
|
|
467
|
+
} else {
|
|
468
|
+
const expected =
|
|
469
|
+
observation.id === "esm"
|
|
470
|
+
? "native-esm-ok"
|
|
471
|
+
: observation.id === "worker-threads"
|
|
472
|
+
? "native-worker-ok"
|
|
473
|
+
: observation.id === "child-fork-ipc"
|
|
474
|
+
? "native-fork-ok"
|
|
475
|
+
: "native-child-ok";
|
|
476
|
+
requireCondition(payload.marker === expected, "probe marker mismatch");
|
|
477
|
+
if (observation.id === "worker-threads")
|
|
478
|
+
requireCondition(
|
|
479
|
+
payload.pid === target.pid &&
|
|
480
|
+
Number.isSafeInteger(payload.threadId) &&
|
|
481
|
+
payload.threadId > 0,
|
|
482
|
+
"worker identity mismatch",
|
|
483
|
+
);
|
|
484
|
+
else if (observation.id !== "esm")
|
|
485
|
+
requireCondition(
|
|
486
|
+
Number.isSafeInteger(payload.pid) &&
|
|
487
|
+
payload.pid > 0 &&
|
|
488
|
+
payload.pid !== target.pid,
|
|
489
|
+
"child identity mismatch",
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (observation.status === "blocked")
|
|
494
|
+
requireCondition(
|
|
495
|
+
["EPERM", "EACCES"].includes(observation.errorCode),
|
|
496
|
+
"blocked probe lacks its actual access error",
|
|
497
|
+
);
|
|
498
|
+
let payload;
|
|
499
|
+
try {
|
|
500
|
+
payload = JSON.parse(observation.output);
|
|
501
|
+
} catch {
|
|
502
|
+
payload = null;
|
|
503
|
+
}
|
|
504
|
+
const expectedMarker =
|
|
505
|
+
observation.id === "esm"
|
|
506
|
+
? "native-esm-ok"
|
|
507
|
+
: observation.id === "worker-threads"
|
|
508
|
+
? "native-worker-ok"
|
|
509
|
+
: observation.id === "child-fork-ipc"
|
|
510
|
+
? "native-fork-ok"
|
|
511
|
+
: "native-child-ok";
|
|
512
|
+
const successOutput =
|
|
513
|
+
observation.id === "scratch-environment"
|
|
514
|
+
? payload?.credentialsAbsent === true &&
|
|
515
|
+
payload?.nodeOptionsAbsent === true &&
|
|
516
|
+
payload?.tmpdir === payload?.derived?.TEMP &&
|
|
517
|
+
payload?.homedir === payload?.derived?.USERPROFILE
|
|
518
|
+
: payload?.marker === expectedMarker;
|
|
519
|
+
const actualSuccess =
|
|
520
|
+
observation.code === 0 &&
|
|
521
|
+
observation.signal === null &&
|
|
522
|
+
observation.error === null &&
|
|
523
|
+
observation.errorCode === null &&
|
|
524
|
+
!observation.timedOut &&
|
|
525
|
+
successOutput;
|
|
526
|
+
const actualStatus = actualSuccess
|
|
527
|
+
? "supported"
|
|
528
|
+
: observation.timedOut
|
|
529
|
+
? "timed-out"
|
|
530
|
+
: ["EPERM", "EACCES"].includes(observation.errorCode)
|
|
531
|
+
? "blocked"
|
|
532
|
+
: "failed";
|
|
533
|
+
requireCondition(
|
|
534
|
+
observation.status === actualStatus,
|
|
535
|
+
"probe status contradicts raw result",
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
requireCondition(
|
|
539
|
+
JSON.stringify(report.observations) === JSON.stringify(target.observations),
|
|
540
|
+
"report observations differ from raw output",
|
|
541
|
+
);
|
|
542
|
+
requireCondition(
|
|
543
|
+
target.observations[0].status === "supported",
|
|
544
|
+
"scratch environment or credential isolation failed",
|
|
545
|
+
);
|
|
546
|
+
const journal = validateJournal(report, true);
|
|
547
|
+
requireCondition(
|
|
548
|
+
JSON.stringify(
|
|
549
|
+
journal.records
|
|
550
|
+
.filter((row) => row.event === "settled")
|
|
551
|
+
.map((row) => row.observation),
|
|
552
|
+
) === JSON.stringify(target.observations),
|
|
553
|
+
"journal observations differ from final target report",
|
|
554
|
+
);
|
|
555
|
+
return {
|
|
556
|
+
diagnosticCompleted: true,
|
|
557
|
+
allCapabilitiesSupported: target.observations.every(
|
|
558
|
+
(item) => item.status === "supported",
|
|
559
|
+
),
|
|
560
|
+
capabilities: Object.fromEntries(
|
|
561
|
+
target.observations.map((item) => [item.id, item.status]),
|
|
562
|
+
),
|
|
563
|
+
journal,
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/** Execute only through the existing one-use AppContainer factory.
|
|
568
|
+
* evidenceDirectory, when supplied, must be a new directory. A failed native
|
|
569
|
+
* settlement retains its stage; there is no unsandboxed fallback or provider.
|
|
570
|
+
*/
|
|
571
|
+
export async function runWindowsNativeEvaluatorCapabilities({
|
|
572
|
+
wallTimeMs = 15000,
|
|
573
|
+
probeTimeoutMs = 1200,
|
|
574
|
+
evidenceDirectory,
|
|
575
|
+
} = {}) {
|
|
576
|
+
requireCondition(process.platform === "win32", "Windows host required");
|
|
577
|
+
requireCondition(
|
|
578
|
+
Number.isSafeInteger(probeTimeoutMs) &&
|
|
579
|
+
probeTimeoutMs >= 100 &&
|
|
580
|
+
probeTimeoutMs <= 5000 &&
|
|
581
|
+
Number.isSafeInteger(wallTimeMs) &&
|
|
582
|
+
wallTimeMs >= probeTimeoutMs * 5 + 3000 &&
|
|
583
|
+
wallTimeMs <= 60000,
|
|
584
|
+
"invalid bounded diagnostic deadlines",
|
|
585
|
+
);
|
|
586
|
+
if (evidenceDirectory) {
|
|
587
|
+
requireCondition(
|
|
588
|
+
path.isAbsolute(evidenceDirectory),
|
|
589
|
+
"absolute evidence path required",
|
|
590
|
+
);
|
|
591
|
+
fs.mkdirSync(evidenceDirectory, { mode: 0o700 });
|
|
592
|
+
}
|
|
593
|
+
const sourceRoot = fs.mkdtempSync(
|
|
594
|
+
path.join(
|
|
595
|
+
fs.realpathSync.native(os.tmpdir()),
|
|
596
|
+
"cc-native-capability-source-",
|
|
597
|
+
),
|
|
598
|
+
);
|
|
599
|
+
const sourceIdentity = fs.lstatSync(sourceRoot, { bigint: true });
|
|
600
|
+
let evaluator;
|
|
601
|
+
const report = {
|
|
602
|
+
schema: SCHEMA,
|
|
603
|
+
startedAt: new Date().toISOString(),
|
|
604
|
+
platform: process.platform,
|
|
605
|
+
osRelease: os.release(),
|
|
606
|
+
nodeVersion: process.version,
|
|
607
|
+
architecture: process.arch,
|
|
608
|
+
formalSample: false,
|
|
609
|
+
providerAssessed: false,
|
|
610
|
+
fullReviewPackAssessed: false,
|
|
611
|
+
diagnosticCompleted: false,
|
|
612
|
+
wallTimeMs,
|
|
613
|
+
probeTimeoutMs,
|
|
614
|
+
observations: [],
|
|
615
|
+
stageRetained: false,
|
|
616
|
+
sourceFiles: Object.entries(FIXTURES).map(([name, bytes]) => ({
|
|
617
|
+
name,
|
|
618
|
+
bytes: Buffer.byteLength(bytes),
|
|
619
|
+
sha256: sha256(bytes),
|
|
620
|
+
})),
|
|
621
|
+
brokerFiles: [],
|
|
622
|
+
};
|
|
623
|
+
try {
|
|
624
|
+
report.brokerFiles = [
|
|
625
|
+
"windows-native-evaluator-capabilities.js",
|
|
626
|
+
"windows-native-evaluator.js",
|
|
627
|
+
"index.js",
|
|
628
|
+
"platform-sandbox.js",
|
|
629
|
+
"windows-sandbox.cs",
|
|
630
|
+
"windows-sandbox-helper.exe",
|
|
631
|
+
"windows-sandbox-helper.dll",
|
|
632
|
+
].map((name) => ({
|
|
633
|
+
name,
|
|
634
|
+
sha256: sha256(fs.readFileSync(new URL(name, import.meta.url))),
|
|
635
|
+
}));
|
|
636
|
+
for (const [name, bytes] of Object.entries(FIXTURES))
|
|
637
|
+
fs.writeFileSync(path.join(sourceRoot, name), bytes, { flag: "wx" });
|
|
638
|
+
const source = checkSource(probeTimeoutMs);
|
|
639
|
+
report.checkSourceSha256 = sha256(source);
|
|
640
|
+
evaluator = createWindowsNativeEvaluator({
|
|
641
|
+
sourceRoot,
|
|
642
|
+
files: Object.keys(FIXTURES),
|
|
643
|
+
checkSource: source,
|
|
644
|
+
wallTimeMs,
|
|
645
|
+
});
|
|
646
|
+
report.manifestDigest = `sha256:${evaluator.manifestDigest}`;
|
|
647
|
+
report.stage = {
|
|
648
|
+
root: evaluator.root,
|
|
649
|
+
workspace: evaluator.manifest.workspace,
|
|
650
|
+
scratch: evaluator.manifest.scratch,
|
|
651
|
+
};
|
|
652
|
+
report.manifest = evaluator.manifest;
|
|
653
|
+
const outcome = await evaluator.execute();
|
|
654
|
+
report.settlement = outcome.receipt;
|
|
655
|
+
const { result } = outcome;
|
|
656
|
+
report.execution = {
|
|
657
|
+
status: result.status ?? null,
|
|
658
|
+
signal: result.signal ?? null,
|
|
659
|
+
error: result.error?.message ?? null,
|
|
660
|
+
errorCode: result.error?.code ?? null,
|
|
661
|
+
stdout: String(result.stdout || ""),
|
|
662
|
+
stderr: String(result.stderr || ""),
|
|
663
|
+
};
|
|
664
|
+
report.execution.stdoutSha256 = sha256(report.execution.stdout);
|
|
665
|
+
report.execution.stderrSha256 = sha256(report.execution.stderr);
|
|
666
|
+
report.journal = readJournalAfterSettlement(report);
|
|
667
|
+
report.journalReadAfterCleanup = true;
|
|
668
|
+
const line = report.execution.stdout
|
|
669
|
+
.trim()
|
|
670
|
+
.split(/\r?\n/u)
|
|
671
|
+
.find((value) => value.startsWith(PREFIX));
|
|
672
|
+
if (line)
|
|
673
|
+
report.observations = JSON.parse(line.slice(PREFIX.length)).observations;
|
|
674
|
+
report.validation = validateWindowsNativeEvaluatorCapabilitiesReport(
|
|
675
|
+
report,
|
|
676
|
+
{ allowPartial: true },
|
|
677
|
+
);
|
|
678
|
+
requireCondition(
|
|
679
|
+
report.brokerFiles.every(
|
|
680
|
+
({ name, sha256: expected }) =>
|
|
681
|
+
sha256(fs.readFileSync(new URL(name, import.meta.url))) === expected,
|
|
682
|
+
),
|
|
683
|
+
"broker source or binary changed during probe execution",
|
|
684
|
+
);
|
|
685
|
+
report.diagnosticCompleted = report.validation.diagnosticCompleted;
|
|
686
|
+
if (!report.diagnosticCompleted) {
|
|
687
|
+
report.failureKind = report.validation.failureKind;
|
|
688
|
+
report.error =
|
|
689
|
+
report.failureKind === "execution-timeout"
|
|
690
|
+
? "Target execution timed out; native cleanup confirmed"
|
|
691
|
+
: "Target execution incomplete; native cleanup confirmed";
|
|
692
|
+
}
|
|
693
|
+
} catch (error) {
|
|
694
|
+
report.error = String(error.stack || error);
|
|
695
|
+
report.errorCode = error.code ?? null;
|
|
696
|
+
report.failureKind = evaluator
|
|
697
|
+
? report.settlement?.cleanupConfirmed === true
|
|
698
|
+
? "diagnostic-incomplete"
|
|
699
|
+
: "cleanup-unconfirmed"
|
|
700
|
+
: "preparation-failure";
|
|
701
|
+
} finally {
|
|
702
|
+
if (evaluator) {
|
|
703
|
+
if (!report.diagnosticCompleted) {
|
|
704
|
+
report.stageRetained = true;
|
|
705
|
+
report.retentionReason =
|
|
706
|
+
report.settlement?.cleanupConfirmed === true
|
|
707
|
+
? "diagnostic-incomplete"
|
|
708
|
+
: "cleanup-unconfirmed";
|
|
709
|
+
} else {
|
|
710
|
+
try {
|
|
711
|
+
evaluator.dispose();
|
|
712
|
+
} catch (error) {
|
|
713
|
+
report.stageRetained = true;
|
|
714
|
+
report.retentionReason = "stage-disposal-failed";
|
|
715
|
+
report.cleanupError = String(error.stack || error);
|
|
716
|
+
report.diagnosticCompleted = false;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
try {
|
|
721
|
+
const current = fs.lstatSync(sourceRoot, { bigint: true });
|
|
722
|
+
requireCondition(
|
|
723
|
+
current.dev === sourceIdentity.dev &&
|
|
724
|
+
current.ino === sourceIdentity.ino &&
|
|
725
|
+
!current.isSymbolicLink() &&
|
|
726
|
+
fs.realpathSync.native(sourceRoot) === sourceRoot,
|
|
727
|
+
"private fixture source identity changed; retained",
|
|
728
|
+
);
|
|
729
|
+
fs.rmSync(sourceRoot, { recursive: true });
|
|
730
|
+
} catch (error) {
|
|
731
|
+
report.sourceRetained = true;
|
|
732
|
+
report.sourceCleanupError = String(error.stack || error);
|
|
733
|
+
report.diagnosticCompleted = false;
|
|
734
|
+
}
|
|
735
|
+
report.finishedAt = new Date().toISOString();
|
|
736
|
+
if (evidenceDirectory) {
|
|
737
|
+
for (const stream of ["stdout", "stderr"])
|
|
738
|
+
fs.writeFileSync(
|
|
739
|
+
path.join(evidenceDirectory, `${stream}.txt`),
|
|
740
|
+
report.execution?.[stream] || "",
|
|
741
|
+
{ flag: "wx" },
|
|
742
|
+
);
|
|
743
|
+
if (report.journal)
|
|
744
|
+
fs.writeFileSync(
|
|
745
|
+
path.join(evidenceDirectory, JOURNAL_FILE),
|
|
746
|
+
report.journal.raw,
|
|
747
|
+
{ flag: "wx" },
|
|
748
|
+
);
|
|
749
|
+
fs.writeFileSync(
|
|
750
|
+
path.join(evidenceDirectory, "report.json"),
|
|
751
|
+
`${JSON.stringify(report, null, 2)}\n`,
|
|
752
|
+
{ flag: "wx" },
|
|
753
|
+
);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return report;
|
|
757
|
+
}
|