deadbyte-mcp 0.15.43 → 0.15.45
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/MANIFEST.SHA256 +160 -161
- package/PROVENANCE.json +257 -232
- package/README.txt +18 -18
- package/RELEASE.json +348 -320
- package/SBOM.cdx.json +14 -2
- package/TEST-MATRIX.json +503 -460
- package/bin/deadbyte-native-inspect-r67.exe +0 -0
- package/bridge/package.json +1 -1
- package/controller/deadbyte-controller.ps1 +1857 -1769
- package/controller/deadbyte-desktop-policy.json +6 -6
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R67-ALPHA-REFERENCE-DIFFERENTIAL.md +42 -0
- package/docs/R67-ALPHA-STATIC-COMPARISON.json +205 -0
- package/docs/R67-CANDIDATE-PARENT.json +10 -0
- package/docs/R67-EVIDENCE-RECONCILE-CLOSEOUT-20261011.md +64 -0
- package/docs/R67-FULL-REGRESSION-UNKNOWN.json +27 -0
- package/docs/R67-NATIVE-INSPECT-BINARY.json +11 -0
- package/docs/R67-NPM-EXDEV-PRESERVATION-20261011.md +39 -0
- package/docs/R67-OBSERVATION-TRUTH.md +82 -0
- package/docs/R67-REFERENCE-GAP-MATRIX.json +57 -0
- package/docs/R67-REGRESSION-CLOSURE-20261011.md +24 -0
- package/docs/R68-COMPAT-HOST-EXEC-CANDIDATE.md +63 -0
- package/docs/R69-LIVE-OUTCOME-R70-POLICY-TRANSFER.md +136 -0
- package/docs/R70-CODING-DIGEST-SUPERVISOR-CHECKPOINT-20261013.md +42 -0
- package/docs/R70-FINITE-GRANT-OWNER-BOUNDARY-20261013.md +80 -0
- package/docs/R70-OWNER-BOOTSTRAP-CHECKPOINT-20261013.md +60 -0
- package/docs/R70-REFERENCE-MECHANISMS.json +95 -0
- package/docs/R70-TRUST-GUARD-EXPIRY-CHECKPOINT-20261013.md +47 -0
- package/native/deadbyte-native-inspect-r67.cpp +252 -0
- package/package.json +6 -3
- package/scripts/benchmark-l0-r71.mjs +76 -0
- package/scripts/build-native-inspector-r67.ps1 +28 -0
- package/scripts/gate-windows-r67.ps1 +56 -0
- package/scripts/gate-windows-r71.ps1 +64 -0
- package/scripts/npm-preserve-artifact-r67.mjs +99 -0
- package/scripts/npm-release-r48.mjs +12 -2
- package/scripts/npm-release-readiness-r67.mjs +26 -0
- package/scripts/npm-release-readiness-r71.mjs +21 -0
- package/scripts/r67-acquire-alpha-reference.mjs +95 -0
- package/scripts/r67-audit-nonpass-source.mjs +39 -0
- package/scripts/r67-compare-desktop-alpha.mjs +48 -0
- package/scripts/r67-diagnose-failed-shard.mjs +23 -0
- package/scripts/r67-durable-batch.mjs +42 -0
- package/scripts/r67-focused-gate.mjs +58 -0
- package/scripts/r67-full-regression.mjs +28 -0
- package/scripts/r67-independent-observation-verify.mjs +36 -0
- package/scripts/r67-independent-runtime-observation-verify.mjs +63 -0
- package/scripts/r67-nonpass-adjudicate.mjs +20 -0
- package/scripts/r67-publish-boundary-audit.mjs +32 -0
- package/scripts/r67-regression-reconcile.mjs +10 -0
- package/scripts/r67-regression-shard-run.mjs +70 -0
- package/scripts/r67-run-shard-batch.mjs +18 -0
- package/scripts/r67-stage-candidate.mjs +30 -0
- package/scripts/r67-verify-parent-ping.mjs +22 -0
- package/scripts/release-manifest.mjs +1 -0
- package/scripts/verify-r70-full-regression.ps1 +72 -0
- package/src/activation-continuity-engine-r65.mjs +2 -1
- package/src/authority-continuity-r65.mjs +28 -8
- package/src/autonomous-mcp-tools.mjs +1 -1
- package/src/autonomous-output-contracts.mjs +4 -1
- package/src/autonomous-policy.mjs +54 -7
- package/src/autonomous-runtime.mjs +1 -1
- package/src/capability-ledger.mjs +1 -1
- package/src/capability-resolver.mjs +2 -1
- package/src/commander-compat-runtime.mjs +30 -2
- package/src/deadbyte-cli.mjs +41 -7
- package/src/machine-fs-smoke-client.mjs +13 -5
- package/src/machine-identity.mjs +59 -2
- package/src/machine-mcp-tools.mjs +8 -0
- package/src/mcp-server.mjs +11 -2
- package/src/native-diff-v2.mjs +18 -5
- package/src/native-diff.mjs +21 -5
- package/src/native-inspector-r67.mjs +66 -0
- package/src/r67-diagnostic-export.mjs +118 -0
- package/src/r67-diagnostics-verify.mjs +82 -0
- package/src/r67-evidence-reconcile.mjs +128 -0
- package/src/r67-native-observation-authority.mjs +45 -0
- package/src/r67-observation-receipt.mjs +43 -0
- package/src/r67-regression-shards.mjs +176 -0
- package/src/r70-owner-approval.mjs +78 -0
- package/src/remote-live-log.mjs +62 -24
- package/src/remote-result-journal.mjs +1 -1
- package/src/runtime-update.mjs +2051 -1891
- package/src/tool-audit.mjs +3 -1
- package/src/version.mjs +1 -1
- package/test/agent-runtime.test.mjs +1 -1
- package/test/autonomous-local-supervisor.test.mjs +2 -1
- package/test/autonomous-loop.test.mjs +11 -6
- package/test/autonomous-output-contract.test.mjs +17 -1
- package/test/autonomous-runtime.test.mjs +1 -1
- package/test/autonomous-supervisor-integration.test.mjs +2 -1
- package/test/capability-resolver.test.mjs +17 -0
- package/test/codex-autonomous-r44.test.mjs +1 -1
- package/test/coding-plane.test.mjs +1 -1
- package/test/commander-compat-runtime.test.mjs +42 -0
- package/test/controller-policy-preflight-r59.test.mjs +1 -0
- package/test/full-max-catalog-r46.test.mjs +1 -1
- package/test/native-diff-topology-r36.test.mjs +35 -2
- package/test/native-diff.test.mjs +83 -3
- package/test/output-schema.test.mjs +1 -1
- package/test/r51-live-log-polling.test.mjs +12 -0
- package/test/r53-release-contract.test.mjs +1 -1
- package/test/r54-release-contract.test.mjs +1 -1
- package/test/r55-release-contract.test.mjs +1 -1
- package/test/r56-release-contract.test.mjs +1 -1
- package/test/r57-release-contract.test.mjs +1 -1
- package/test/r58-bundled-bridge.test.mjs +1 -1
- package/test/r58-release-contract.test.mjs +2 -2
- package/test/r58-windows-gate.test.mjs +1 -1
- package/test/r59-tool-surface-compat.test.mjs +9 -5
- package/test/r60-release-contract.test.mjs +3 -3
- package/test/r61-release-contract.test.mjs +1 -1
- package/test/r62-release-contract.test.mjs +1 -1
- package/test/r63-release-contract.test.mjs +1 -1
- package/test/r64-release-contract.test.mjs +1 -1
- package/test/r65-authority-runtime-epoch.test.mjs +9 -1
- package/test/r65-epoch-native-activation-integration.test.mjs +4 -4
- package/test/r65-release-contract.test.mjs +1 -1
- package/test/r66-release-contract.test.mjs +2 -1
- package/test/r67-cli-integration.test.mjs +41 -0
- package/test/r67-diagnostics-cli.test.mjs +60 -0
- package/test/r67-diagnostics-verify.test.mjs +49 -0
- package/test/r67-diagnostics-zip-oracle.test.mjs +38 -0
- package/test/r67-diagnostics.test.mjs +60 -0
- package/test/r67-evidence-reconcile.test.mjs +186 -0
- package/test/r67-isolated-slot-rollback-restore.test.mjs +63 -0
- package/test/r67-live-log-secret-canary.test.mjs +44 -0
- package/test/r67-machine-fs-smoke-epoch.test.mjs +22 -0
- package/test/r67-native-inspector.test.mjs +131 -0
- package/test/r67-native-observation-authority.test.mjs +49 -0
- package/test/r67-npm-cross-volume.test.mjs +74 -0
- package/test/r67-npm-publish-gate.test.mjs +40 -0
- package/test/r67-regression-shards.test.mjs +184 -0
- package/test/r67-release-contract.test.mjs +45 -0
- package/test/r67-runtime-observation-binding.test.mjs +86 -0
- package/test/r67-server-observation-wiring.test.mjs +12 -0
- package/test/r67-signed-receipt.test.mjs +66 -0
- package/test/r67-skip-rationale.test.mjs +31 -0
- package/test/r67-tool-annotations.test.mjs +21 -0
- package/test/r67-windows-gate-argv.test.mjs +13 -0
- package/test/r69-live-outcome.test.mjs +137 -0
- package/test/r70-autonomous-grant-expiry.test.mjs +89 -0
- package/test/r70-autonomous-policy-bootstrap.test.mjs +166 -0
- package/test/r70-controller-autonomous-root.test.mjs +104 -0
- package/test/r70-controller-grant-subset.test.mjs +28 -0
- package/test/r70-controller-owner-issue-boundary.test.mjs +80 -0
- package/test/r70-controller-policy-snapshot.test.mjs +66 -0
- package/test/r70-owner-approval.test.mjs +67 -0
- package/test/r71-full-verifier-artifact.test.mjs +28 -0
- package/test/r71-isolated-slot-rollback-restore.test.mjs +62 -0
- package/test/r71-native-diff-mcp-wire.test.mjs +75 -0
- package/test/r71-publish-fence.test.mjs +38 -0
- package/test/r71-release-identity.test.mjs +31 -0
- package/test/release-version.test.mjs +1 -1
- package/test/remote-live-log-r36.test.mjs +10 -0
- package/test/remote-live-log-r37.test.mjs +29 -0
- package/test/remote-result-journal.test.mjs +12 -0
- package/test/remote-session-cli.test.mjs +2 -1
- package/test/semantic-multilang.test.mjs +1 -1
- package/test/standing-grants.test.mjs +2 -2
- package/test/tool-audit.test.mjs +14 -0
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-desktop-input-r40.obj +0 -0
- package/bin/deadbyte-desktop-observer-r40.obj +0 -0
- package/bin/deadbyte-desktop-uia-r40.obj +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-hardware-observer-r45.obj +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/conpty-input-observer.obj +0 -0
- package/miniconpty.obj +0 -0
- package/proof/CONTAINMENT-BUILD.txt +0 -163
- package/proof/MCP-SMOKE-STATUS.txt +0 -9
- package/proof/NODE-SYNTAX.txt +0 -4
- package/proof/PROOF.txt +0 -6
- package/proof/R34-TASK8-SUPERVISOR-CHECKPOINT.json +0 -29
- package/proof/R34-TASK9-MCP-SURFACE-CHECKPOINT.json +0 -28
- package/proof/SECRET-SCAN.txt +0 -4
- package/proof/SOURCE-GATES.txt +0 -16
- package/proof/STATUS.txt +0 -11
- package/proof/TARGET-V0313-CONTAINMENT-PASS.sha256 +0 -1
- package/proof/TARGET-V0313-CONTAINMENT-PASS.txt +0 -215
- package/proof/TARGET-V040-CONTAINED-IPC-FAIL.sha256 +0 -1
- package/proof/TARGET-V040-CONTAINED-IPC-FAIL.txt +0 -299
- package/proof/TARGET-V041-CONTAINED-CREATEPROCESS-203.sha256 +0 -1
- package/proof/TARGET-V041-CONTAINED-CREATEPROCESS-203.txt +0 -311
- package/proof/TARGET-V042-CONTAINED-CREATEPROCESS-203.sha256 +0 -1
- package/proof/TARGET-V042-CONTAINED-CREATEPROCESS-203.txt +0 -313
- package/proof/TARGET-V043-CONTAINED-CREATEPROCESS-203.sha256 +0 -1
- package/proof/TARGET-V043-CONTAINED-CREATEPROCESS-203.txt +0 -308
- package/proof/TARGET-V044-CONTAINED-PASS.sha256 +0 -1
- package/proof/TARGET-V044-CONTAINED-PASS.txt +0 -432
- package/proof/TARGET-V045-DUAL-OPERATION-PASS.sha256 +0 -1
- package/proof/TARGET-V045-DUAL-OPERATION-PASS.txt +0 -481
- package/proof/TARGET-V046-FIXED-EXECUTABLE-REGISTRY-PASS.sha256 +0 -1
- package/proof/TARGET-V046-FIXED-EXECUTABLE-REGISTRY-PASS.txt +0 -610
- package/proof/TARGET-V047-RUNTIME-MANIFEST-PASS.sha256 +0 -1
- package/proof/TARGET-V047-RUNTIME-MANIFEST-PASS.txt +0 -642
- package/proof/TARGET-V050-BATCH-GATE-PASS.sha256 +0 -1
- package/proof/TARGET-V050-BATCH-GATE-PASS.txt +0 -713
- package/proof/TESTS.txt +0 -686
- package/proof/TRUST-EPHEMERAL.txt +0 -4
- package/proof/V03-FREEZE.txt +0 -27
- package/proof/V041-IPC-HOTFIX.txt +0 -16
- package/proof/V042-ENV-HOTFIX.txt +0 -15
- package/proof/V043-DRIVE-CWD-HOTFIX.txt +0 -14
- package/proof/V044-LOCALAPPDATA-HOTFIX.txt +0 -18
- package/proof/V045-OPERATION-REGISTRY.txt +0 -7
- package/proof/V046-WORKER-REGISTRY.txt +0 -12
- package/proof/V047-WORKER-MANIFEST.txt +0 -18
- package/proof/V050-BATCH-TRUST-CLOSURE.txt +0 -21
- package/proof/V060-ACTIVE-NETWORK-AUTHORITY.txt +0 -29
- package/proof/V070-NODE-SYNTAX.txt +0 -36
- package/proof/V070-NODE-TESTS.txt +0 -752
- package/proof/V070-PACK-DRY-RUN.txt +0 -126
- package/proof/V070-SOURCE-GATE.txt +0 -30
- package/proof/V070-STATIC-SCAN.txt +0 -4
- package/proof/V070-WINDOWS-GATE-PENDING.txt +0 -25
- package/proof/V071-NODE-SYNTAX.txt +0 -1
- package/proof/V071-NODE-TESTS.txt +0 -796
- package/proof/V071-PACK-DRY-RUN.json +0 -656
- package/proof/V071-SOURCE-GATE.txt +0 -16
- package/proof/V071-WINDOWS-GATE-PENDING.txt +0 -24
- package/proof/V073-NODE-SYNTAX.txt +0 -4
- package/proof/V073-SOURCE-GATE.txt +0 -21
- package/proof/V073-STATIC-SCAN.txt +0 -10
- package/proof/V073-WINDOWS-GATE-PENDING.txt +0 -5
- package/proof/VERIFY.txt +0 -7
- package/proof/WINDOWS-CONTAINED-STATUS.txt +0 -5
- package/proof/WINDOWS-CONTAINMENT-STATUS.txt +0 -3
- package/proof/commander-closure-20260927/CONTINUATION.json +0 -43
- package/proof/commander-closure-20260927/RESULT.json +0 -30
- package/proof/commander-closure-20260927/SEARCH-CONTINUATION.json +0 -30
- package/proof/commander-closure-20260927/XLS-REFERENCE-PROBE.json +0 -13
- package/proof/commander-closure-20260928/DIFFERENTIAL-CHECKPOINT.json +0 -122
- package/proof/r59-full-regression-current.json +0 -13
- package/proof/r59-full-regression-current.stderr.log +0 -0
- package/proof/r59-full-regression-current.stdout.log +0 -1268
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {mkdtemp,mkdir,readFile,rm,writeFile,lstat} from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {fileURLToPath} from 'node:url';
|
|
8
|
+
import {atomicWriteActivePointer,materializeReleaseSlot,parseActivePointer,parseManifest,renderStableLauncherPs1,verifyManifestTreeExact} from '../src/release-slot.mjs';
|
|
9
|
+
|
|
10
|
+
const candidate=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
11
|
+
const parent=path.resolve(candidate,'..','deadbyte-mcp-0.15.43-r66');
|
|
12
|
+
const parentManifestSha='4c4a64a4c8e9f5a6e9eecce85fd0007ebcac99121b29c07f1a1fc81d1643cd33';
|
|
13
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
14
|
+
|
|
15
|
+
async function generation(state,label){
|
|
16
|
+
const root=path.join(state,'generations',label);
|
|
17
|
+
await mkdir(root,{recursive:true});
|
|
18
|
+
const config=Buffer.from(`@{ Label = '${label}' }\r\n`,'utf8');
|
|
19
|
+
await writeFile(path.join(root,'controller-config.psd1'),config,{flag:'wx'});
|
|
20
|
+
const manifest=Buffer.from(`${sha(config)} controller-config.psd1\n`,'utf8');
|
|
21
|
+
await writeFile(path.join(root,'GENERATION.SHA256'),manifest,{flag:'wx'});
|
|
22
|
+
return {root,manifest_sha256:sha(manifest),config_path:path.join(root,'controller-config.psd1')};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const historicalR67=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8')).version==='0.15.44';
|
|
26
|
+
test('R67 isolated immutable slot activates, rolls back exact R66, and restores the same R67 bytes with no authority',{skip:historicalR67?false:'Historical R67 regression applies only to package version 0.15.44; R71 test is separate'},async()=>{
|
|
27
|
+
const base=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r67-slot-parity-'));
|
|
28
|
+
const state=path.join(base,'state');
|
|
29
|
+
try{
|
|
30
|
+
const parentManifest=await readFile(path.join(parent,'MANIFEST.SHA256'));
|
|
31
|
+
const candidateManifest=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
32
|
+
assert.equal(sha(parentManifest),parentManifestSha);
|
|
33
|
+
const candidateManifestSha=sha(candidateManifest);
|
|
34
|
+
const parentSlot=await materializeReleaseSlot({source:parent,stateRoot:state,version:'0.15.43',manifestSha256:parentManifestSha});
|
|
35
|
+
const candidateSlot=await materializeReleaseSlot({source:candidate,stateRoot:state,version:'0.15.44',manifestSha256:candidateManifestSha});
|
|
36
|
+
const parentGeneration=await generation(state,'r66-parent');
|
|
37
|
+
const candidateGeneration=await generation(state,'r67-candidate');
|
|
38
|
+
const launcherSha=sha(Buffer.from(renderStableLauncherPs1(),'utf8'));
|
|
39
|
+
const pointerPath=path.join(state,'active-release.json');
|
|
40
|
+
const pointer=(slot,version,g)=>({schema:'deadbyte.active-release.v1',release_id:slot.release_id,version,
|
|
41
|
+
manifest_sha256:slot.manifest_sha256,release_root:slot.release_root,generation_root:g.root,
|
|
42
|
+
generation_manifest_sha256:g.manifest_sha256,launcher_sha256:launcherSha,config_path:g.config_path});
|
|
43
|
+
|
|
44
|
+
const activated=await atomicWriteActivePointer(pointerPath,pointer(candidateSlot,'0.15.44',candidateGeneration));
|
|
45
|
+
assert.equal(activated.release_id,candidateSlot.release_id);
|
|
46
|
+
await verifyManifestTreeExact(candidateSlot.release_root,parseManifest(candidateManifest.toString('utf8')));
|
|
47
|
+
|
|
48
|
+
const rolledBack=await atomicWriteActivePointer(pointerPath,pointer(parentSlot,'0.15.43',parentGeneration));
|
|
49
|
+
assert.equal(rolledBack.release_id,parentSlot.release_id);
|
|
50
|
+
assert.equal(rolledBack.manifest_sha256,parentManifestSha);
|
|
51
|
+
await verifyManifestTreeExact(parentSlot.release_root,parseManifest(parentManifest.toString('utf8')));
|
|
52
|
+
|
|
53
|
+
const restored=await atomicWriteActivePointer(pointerPath,pointer(candidateSlot,'0.15.44',candidateGeneration));
|
|
54
|
+
assert.deepEqual(restored,activated);
|
|
55
|
+
assert.deepEqual(parseActivePointer(await readFile(pointerPath,'utf8')),activated);
|
|
56
|
+
await verifyManifestTreeExact(candidateSlot.release_root,parseManifest(candidateManifest.toString('utf8')));
|
|
57
|
+
for(const name of ['process-grant.json','coding-grant.json','autonomous-grant.json','desktop-grant.json','host-grant.json']){
|
|
58
|
+
await assert.rejects(()=>lstat(path.join(state,'runtime',name)),{code:'ENOENT'});
|
|
59
|
+
}
|
|
60
|
+
}finally{
|
|
61
|
+
await rm(base,{recursive:true,force:true});
|
|
62
|
+
}
|
|
63
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {formatRemoteToolCall,createLiveTimelineTracker} from '../src/remote-live-log.mjs';
|
|
4
|
+
const token='sk-proj-SENTINEL_CANARY_NEVER_PRINT_8675309';
|
|
5
|
+
test('R67 live log does not echo credential-shaped token in path argument, including verbose call',()=>{
|
|
6
|
+
const evt={event_type:'tool.request',request_id:'canary-00000001',tool:'coding_file_read',args:{root_id:'projects',path:'sensitive/'+token+'/file.txt'}};
|
|
7
|
+
const verbose=formatRemoteToolCall(evt);
|
|
8
|
+
assert.equal(typeof verbose,'string');
|
|
9
|
+
assert.doesNotMatch(verbose,/SENTINEL_CANARY_NEVER_PRINT/);
|
|
10
|
+
const track=createLiveTimelineTracker({clock:()=>1000,verbose:true});
|
|
11
|
+
const start=track.observe(evt,{sequence:10});
|
|
12
|
+
const end=track.observe({event_type:'tool.result',request_id:evt.request_id,tool:evt.tool,outcome:{is_error:false,duration_ms:1}},{sequence:11});
|
|
13
|
+
assert.doesNotMatch(String(start)+String(end),/SENTINEL_CANARY_NEVER_PRINT/);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('R67 compact live activity refuses credential in path and pattern even when caller passes unsanitized event',async()=>{
|
|
17
|
+
const {formatCompactAuditEvent,createLiveActivityTracker}=await import('../src/remote-live-log.mjs');
|
|
18
|
+
const sentinel='ghp_PRIVATE_CANARY_ABCDEF0123456789Q';
|
|
19
|
+
const event={event_type:'tool.request',request_id:'span-safe-121',tool:'coding_search',
|
|
20
|
+
args:{root_id:'projects',path:'source/'+sentinel+'/target',pattern:'token='+sentinel}};
|
|
21
|
+
const line=formatCompactAuditEvent(event);
|
|
22
|
+
assert.equal(typeof line,'string');
|
|
23
|
+
assert.doesNotMatch(line,/PRIVATE_CANARY/);
|
|
24
|
+
const activity=createLiveActivityTracker();
|
|
25
|
+
assert.doesNotMatch(activity.observe(event),/PRIVATE_CANARY/);
|
|
26
|
+
});
|
|
27
|
+
test('R67 trace-enabled live log does not disclose credential-shaped user session id or script_path',async()=>{
|
|
28
|
+
const event={event_type:'tool.request',request_id:'good-span-5678',tool:'coding_command_run',
|
|
29
|
+
context:{trace_id:'sk-proj-SENTINEL_TRACE_CANARY_3456789'},
|
|
30
|
+
args:{root_id:'projects',command_id:'node_script',script_path:'tmp/github_pat_PRIVATE_SCRIPT_CANARY_1234567890.ps1'}};
|
|
31
|
+
const tracker=createLiveTimelineTracker({clock:()=>1000,trace:true});
|
|
32
|
+
const line=tracker.observe(event,{sequence:77});
|
|
33
|
+
assert.equal(typeof line,'string');
|
|
34
|
+
assert.doesNotMatch(line,/SENTINEL_TRACE_CANARY|PRIVATE_SCRIPT_CANARY/);
|
|
35
|
+
});
|
|
36
|
+
test('R67 result error reason does not reveal github token in user supplied text',()=>{
|
|
37
|
+
const token='github_pat_SECRET_RESULT_CANARY_ABCDEFGHIJKLMNOP';
|
|
38
|
+
const tracker=createLiveTimelineTracker({clock:()=>1000,trace:true});
|
|
39
|
+
tracker.observe({event_type:'tool.request',request_id:'foo-1',tool:'coding_command_run',args:{root_id:'projects',command_id:'node_script'}},{sequence:4});
|
|
40
|
+
const output=tracker.observe({event_type:'tool.result',request_id:'foo-1',tool:'coding_command_run',
|
|
41
|
+
outcome:{is_error:true,reason:'failure after credential '+token,duration_ms:33}},{sequence:5});
|
|
42
|
+
assert.equal(typeof output,'string');
|
|
43
|
+
assert.doesNotMatch(output,/SECRET_RESULT_CANARY/);
|
|
44
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {spawnSync} from 'node:child_process';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
|
|
7
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
8
|
+
|
|
9
|
+
test('R67 isolated machine-fs smoke binds its Coding grant to a durable authority epoch',()=>{
|
|
10
|
+
const env={...process.env};
|
|
11
|
+
delete env.DEADBYTE_STATE_ROOT;
|
|
12
|
+
const result=spawnSync(process.execPath,['src/machine-fs-smoke-client.mjs'],{
|
|
13
|
+
cwd:root,env,encoding:'utf8',windowsHide:true,shell:false,timeout:30000,maxBuffer:4*1024*1024
|
|
14
|
+
});
|
|
15
|
+
assert.equal(result.error,undefined,String(result.error??''));
|
|
16
|
+
assert.equal(result.status,0,`${result.stdout}\n${result.stderr}`);
|
|
17
|
+
const receipt=JSON.parse(String(result.stdout));
|
|
18
|
+
assert.equal(receipt.status,'passed');
|
|
19
|
+
assert.equal(receipt.mutation_receipts,5);
|
|
20
|
+
assert.equal(receipt.signature_valid,true);
|
|
21
|
+
assert.doesNotMatch(`${result.stdout}\n${result.stderr}`,/legacy-no-epoch/);
|
|
22
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {readFile,writeFile,mkdtemp,rm} from 'node:fs/promises';
|
|
5
|
+
import {tmpdir} from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {fileURLToPath} from 'node:url';
|
|
8
|
+
import {spawnSync} from 'node:child_process';
|
|
9
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
10
|
+
const exe=path.join(root,'bin','deadbyte-native-inspect-r67.exe');
|
|
11
|
+
const hash=b=>createHash('sha256').update(b).digest('hex');
|
|
12
|
+
function run(...args){
|
|
13
|
+
const r=spawnSync(exe,args,{windowsHide:true,shell:false,encoding:'utf8',timeout:30000,maxBuffer:4*1024*1024});
|
|
14
|
+
assert.equal(r.error,undefined,r.error?.message);
|
|
15
|
+
const out=JSON.parse((r.stdout??'').trim());
|
|
16
|
+
assert.equal(out.schema,'deadbyte.native-inspect.r67.v1');
|
|
17
|
+
if(out.status==='ok')assert.equal(out.data_sha256,hash(Buffer.from(JSON.stringify(out.data),'utf8')),'independent serialized data digest');
|
|
18
|
+
return {...out,exit_code:r.status};
|
|
19
|
+
}
|
|
20
|
+
test('R67 native process snapshot is bounded, identity-bearing, and honestly partial',()=>{
|
|
21
|
+
const r=run('process','48');
|
|
22
|
+
assert.equal(r.status,'ok');
|
|
23
|
+
assert.equal(r.exit_code,0);
|
|
24
|
+
assert.equal(r.operation,'process');
|
|
25
|
+
assert.equal(r.data.api,'CreateToolhelp32Snapshot+OpenProcess+GetProcessTimes+QueryFullProcessImageNameW');
|
|
26
|
+
assert.ok(r.data.processes.length>=1&&r.data.processes.length<=48);
|
|
27
|
+
for(const p of r.data.processes){
|
|
28
|
+
assert.ok(Number.isSafeInteger(p.pid)&&p.pid>=0);
|
|
29
|
+
assert.ok(Number.isSafeInteger(p.parent_pid)&&p.parent_pid>=0);
|
|
30
|
+
assert.ok(['observed','unavailable','stale_or_reused'].includes(p.state));
|
|
31
|
+
if(p.state==='unavailable')assert.ok(Number.isInteger(p.win32_error)&&p.win32_error>0);
|
|
32
|
+
else if(p.state==='stale_or_reused'){assert.equal(p.snapshot_binding,'NAME_MISMATCH');assert.equal(p.win32_error,0);}
|
|
33
|
+
else {assert.equal(p.snapshot_binding,'UNVERIFIED');assert.match(p.creation_time_utc,/^\d{4}-\d\d-\d\dT/);assert.match(p.creation_filetime,/^\d+$/);assert.equal(p.win32_error,0);}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
test('R67 VirtualQueryEx memory map records process identity, monotone bounded regions, and zero private memory data',()=>{
|
|
37
|
+
const r=run('map','self','64');
|
|
38
|
+
assert.equal(r.status,'ok');
|
|
39
|
+
assert.equal(r.exit_code,0);
|
|
40
|
+
assert.equal(r.operation,'map');
|
|
41
|
+
assert.equal(r.data.api,'VirtualQueryEx');
|
|
42
|
+
assert.match(r.data.creation_filetime,/^\d+$/);
|
|
43
|
+
assert.ok(r.data.regions.length>=1&&r.data.regions.length<=64);
|
|
44
|
+
let prev=-1n;
|
|
45
|
+
for(const row of r.data.regions){
|
|
46
|
+
const base=BigInt(row.base),size=BigInt(row.size);
|
|
47
|
+
assert.ok(base>prev&&size>0n);
|
|
48
|
+
prev=base;
|
|
49
|
+
assert.ok(Number.isInteger(row.state)&&Number.isInteger(row.protect)&&Number.isInteger(row.type));
|
|
50
|
+
assert.deepEqual(Object.keys(row),['base','allocation_base','size','state','protect','type']);
|
|
51
|
+
}
|
|
52
|
+
assert.equal(r.data.process_exited,false);
|
|
53
|
+
assert.equal(r.data.outcome,'HANDLE_IDENTITY_CONFIRMED');
|
|
54
|
+
});
|
|
55
|
+
test('R67 PE bytes and section table agree with an independent raw-file oracle',async()=>{
|
|
56
|
+
const b=await readFile(exe);
|
|
57
|
+
assert.equal(b.readUInt16LE(0),0x5a4d);
|
|
58
|
+
const p=b.readInt32LE(0x3c);
|
|
59
|
+
assert.ok(p>=64&&p+24<b.length);
|
|
60
|
+
assert.equal(b.readUInt32LE(p),0x4550);
|
|
61
|
+
const machine=b.readUInt16LE(p+4),sectionCount=b.readUInt16LE(p+6);
|
|
62
|
+
const optLength=b.readUInt16LE(p+20),magic=b.readUInt16LE(p+24);
|
|
63
|
+
const sectionOff=p+24+optLength;
|
|
64
|
+
const r=run('pe',exe);
|
|
65
|
+
assert.equal(r.status,'ok');assert.equal(r.exit_code,0);
|
|
66
|
+
assert.equal(r.data.file_sha256,hash(b));
|
|
67
|
+
assert.equal(r.data.file_size,b.length);
|
|
68
|
+
assert.equal(r.data.machine,machine);
|
|
69
|
+
assert.equal(r.data.number_of_sections,sectionCount);
|
|
70
|
+
assert.equal(r.data.format,magic===0x20b?'PE32+':'PE32');
|
|
71
|
+
assert.equal(r.data.sections.length,sectionCount);
|
|
72
|
+
for(let i=0;i<sectionCount;i++){
|
|
73
|
+
const off=sectionOff+i*40,row=r.data.sections[i];
|
|
74
|
+
assert.equal(row.rva,'0x'+b.readUInt32LE(off+12).toString(16));
|
|
75
|
+
assert.equal(row.raw_size,b.readUInt32LE(off+16));
|
|
76
|
+
assert.equal(row.raw_offset,b.readUInt32LE(off+20));
|
|
77
|
+
assert.equal(row.characteristics,b.readUInt32LE(off+36));
|
|
78
|
+
assert.ok(!row.raw_size||row.raw_offset+row.raw_size<=b.length);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
test('R67 returns failed/error for inaccessible PID and malformed PE, never fabricated empty observations',async()=>{
|
|
82
|
+
const bad=run('map','4294967295','1','4');
|
|
83
|
+
assert.equal(bad.status,'failed');assert.equal(bad.exit_code,2);
|
|
84
|
+
assert.match(bad.error,/OpenProcess.*win32=|invalid PID/);
|
|
85
|
+
assert.ok(['ACCESS_DENIED','INVALID_PARAMETER','WIN32_FAILURE'].includes(bad.error_code));
|
|
86
|
+
assert.ok(Number.isInteger(bad.win32_error)&&bad.win32_error>0);
|
|
87
|
+
const folder=await mkdtemp(path.join(tmpdir(),'deadbyte-r67-pe-'));
|
|
88
|
+
try{
|
|
89
|
+
const file=path.join(folder,'invalid.exe');await writeFile(file,Buffer.alloc(128,0));
|
|
90
|
+
const invalid=run('pe',file);
|
|
91
|
+
assert.equal(invalid.status,'failed');assert.equal(invalid.exit_code,2);assert.match(invalid.error,/MZ|PE/);
|
|
92
|
+
assert.equal(invalid.error_code,'INVALID_INPUT');assert.equal(invalid.win32_error,0);
|
|
93
|
+
}finally{await rm(folder,{recursive:true,force:true});}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('R67 external PID memory map fails closed on missing or mismatched creation FILETIME',()=>{
|
|
97
|
+
const missing=run('map',String(process.pid));
|
|
98
|
+
assert.equal(missing.status,'failed');assert.equal(missing.exit_code,2);
|
|
99
|
+
assert.match(missing.error,/creation_filetime/);
|
|
100
|
+
const swapped=run('map',String(process.pid),'1','4');
|
|
101
|
+
assert.equal(swapped.status,'failed');assert.equal(swapped.exit_code,2);
|
|
102
|
+
assert.equal(swapped.error_code,'PROCESS_IDENTITY_MISMATCH');
|
|
103
|
+
assert.equal(swapped.win32_error,0);
|
|
104
|
+
const observed=run('identity',String(process.pid));
|
|
105
|
+
assert.equal(observed.status,'ok',observed.error);
|
|
106
|
+
assert.equal(observed.data.pid,process.pid);
|
|
107
|
+
const valid=run('map',String(process.pid),observed.data.creation_filetime,'4');
|
|
108
|
+
assert.equal(valid.status,'ok',valid.error);
|
|
109
|
+
assert.equal(valid.data.creation_filetime,observed.data.creation_filetime);
|
|
110
|
+
assert.equal(valid.data.pid,process.pid);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('R67 exited process cannot silently turn into a successful PID-reused memory map',async()=>{
|
|
114
|
+
const {spawn}=await import('node:child_process');
|
|
115
|
+
const {once}=await import('node:events');
|
|
116
|
+
const child=spawn(process.execPath,['-e','setInterval(()=>{},500)'],{shell:false,windowsHide:true,stdio:'ignore'});
|
|
117
|
+
try{
|
|
118
|
+
await once(child,'spawn');
|
|
119
|
+
const identity=run('identity',String(child.pid));
|
|
120
|
+
assert.equal(identity.status,'ok',identity.error);
|
|
121
|
+
assert.equal(identity.data.pid,child.pid);
|
|
122
|
+
const stopped=once(child,'exit');
|
|
123
|
+
assert.equal(child.kill(),true);
|
|
124
|
+
await stopped;
|
|
125
|
+
const after=run('map',String(child.pid),identity.data.creation_filetime,'4');
|
|
126
|
+
assert.equal(after.status,'failed');
|
|
127
|
+
assert.equal(after.exit_code,2);
|
|
128
|
+
assert.equal(Object.hasOwn(after,'data'),false);
|
|
129
|
+
assert.ok(['WIN32_FAILURE','INVALID_PARAMETER','PROCESS_EXITED','PROCESS_IDENTITY_MISMATCH'].includes(after.error_code),after.error_code);
|
|
130
|
+
}finally{if(child.exitCode===null)child.kill();}
|
|
131
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createR67NativeObservationAuthority} from '../src/r67-native-observation-authority.mjs';
|
|
4
|
+
|
|
5
|
+
const hex=c=>c.repeat(64);
|
|
6
|
+
const policy={policy_sha256:hex('a'),roots:{projects:{read:true}}};
|
|
7
|
+
let alive=true,generation=17;
|
|
8
|
+
const getSession=async()=>alive?{
|
|
9
|
+
armed:true,standing:true,reason:'standing-grant',instance_nonce:hex('b'),root_ids:['projects'],
|
|
10
|
+
authority_epoch:{trusted:true,seq:generation,event_sha256:hex('c')}
|
|
11
|
+
}:{armed:false,reason:'disarmed'};
|
|
12
|
+
|
|
13
|
+
test('R67 observer fails closed without explicit owner opt-in, and denies other PID reads',async()=>{
|
|
14
|
+
const disabled=createR67NativeObservationAuthority({getCodingPolicy:()=>policy,sessionStatus:getSession});
|
|
15
|
+
await assert.rejects(()=>disabled({operation:'process',args:['process','4']}),/DISABLED/);
|
|
16
|
+
const enabled=createR67NativeObservationAuthority({getCodingPolicy:()=>policy,sessionStatus:getSession,enabled:true});
|
|
17
|
+
await assert.rejects(()=>enabled({operation:'map',args:['map','4148','3']}),/OTHER_PID/);
|
|
18
|
+
await assert.rejects(()=>enabled({operation:'identity',args:['identity','4148']}),/OTHER_PID/);
|
|
19
|
+
await assert.rejects(()=>enabled({operation:'exec',args:['exec','whoami']}),/SCOPE_DENIED/);
|
|
20
|
+
});
|
|
21
|
+
test('R67 observer binds machine-supplied authority to real standing epoch, not client grant',async()=>{
|
|
22
|
+
alive=true;
|
|
23
|
+
const enabled=createR67NativeObservationAuthority({getCodingPolicy:()=>policy,sessionStatus:getSession,enabled:true});
|
|
24
|
+
const first=await enabled({operation:'map',args:['map','self','4']});
|
|
25
|
+
assert.equal(first.scope,'native-observe-readonly');
|
|
26
|
+
assert.equal(first.policy_sha256,policy.policy_sha256);
|
|
27
|
+
assert.equal(first.authority_generation,generation);
|
|
28
|
+
assert.match(first.grant_id,/^coding\.r67\.[0-9a-f]{32}$/);
|
|
29
|
+
const next=await enabled({operation:'map',args:['map','self','4']});
|
|
30
|
+
assert.deepEqual(first,next);
|
|
31
|
+
generation+=1;
|
|
32
|
+
const rotated=await enabled({operation:'map',args:['map','self','4']});
|
|
33
|
+
assert.notEqual(first.authority_generation,rotated.authority_generation);
|
|
34
|
+
alive=false;
|
|
35
|
+
await assert.rejects(()=>enabled({operation:'process',args:['process','4']}),/AUTHORITY_REVOKED/);
|
|
36
|
+
});
|
|
37
|
+
test('R67 observer rejects unverified lease epoch or changed policy',async()=>{
|
|
38
|
+
const disabled=createR67NativeObservationAuthority({
|
|
39
|
+
getCodingPolicy:()=>policy,enabled:true,sessionStatus:async()=>({
|
|
40
|
+
armed:true,standing:true,reason:'standing-grant',instance_nonce:hex('b'),root_ids:['projects'],
|
|
41
|
+
authority_epoch:{trusted:false,seq:100,event_sha256:hex('c')}
|
|
42
|
+
})
|
|
43
|
+
});
|
|
44
|
+
await assert.rejects(()=>disabled({operation:'process',args:['process','4']}),/AUTHORITY_REVOKED/);
|
|
45
|
+
const malformed=createR67NativeObservationAuthority({
|
|
46
|
+
getCodingPolicy:()=>({policy_sha256:hex('a'),roots:{}}),enabled:true,sessionStatus:getSession
|
|
47
|
+
});
|
|
48
|
+
await assert.rejects(()=>malformed({operation:'process',args:['process','4']}),/POLICY_MISSING/);
|
|
49
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {mkdtemp,mkdir,readFile,writeFile,rm,lstat,stat,readdir,symlink} from 'node:fs/promises';
|
|
5
|
+
import {tmpdir} from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {fileURLToPath} from 'node:url';
|
|
8
|
+
import {preserveNpmTarball,assertVacantPackDestination} from '../scripts/npm-preserve-artifact-r67.mjs';
|
|
9
|
+
|
|
10
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
11
|
+
const digest=(bytes,algo='sha256')=>createHash(algo).update(bytes).digest(algo==='sha512'?'base64':'hex');
|
|
12
|
+
const artifact=Buffer.concat([Buffer.from('R67 immutable npm tarball fixture\\n'),Buffer.alloc(4194305,0x5a)]);
|
|
13
|
+
const integrity='sha512-'+digest(artifact,'sha512');
|
|
14
|
+
|
|
15
|
+
async function fixture(fn){
|
|
16
|
+
await mkdir(path.join(root,'tmp'),{recursive:true});
|
|
17
|
+
const srcDir=await mkdtemp(path.join(root,'tmp','r67-xdev-src-'));
|
|
18
|
+
const dstDir=await mkdtemp(path.join(tmpdir(),'r67-xdev-dst-'));
|
|
19
|
+
const source=path.join(srcDir,'deadbyte-mcp-0.15.44.tgz');
|
|
20
|
+
const destination=path.join(dstDir,'deadbyte-mcp-0.15.44.tgz');
|
|
21
|
+
try{return await fn({source,destination,srcDir,dstDir});}
|
|
22
|
+
finally{await rm(srcDir,{recursive:true,force:true});await rm(dstDir,{recursive:true,force:true});}
|
|
23
|
+
}
|
|
24
|
+
test('R67 preserves immutable package across Windows drives without EXDEV, hash-verifies, and removes source only after successful commit',()=>fixture(async ({source,destination})=>{
|
|
25
|
+
await writeFile(source,artifact);
|
|
26
|
+
const srcDrive=(await stat(path.dirname(source))).dev,dstDrive=(await stat(path.dirname(destination))).dev;
|
|
27
|
+
if(process.platform==='win32')assert.notEqual(srcDrive,dstDrive,'Windows test must cover different volumes D and C');
|
|
28
|
+
const result=await preserveNpmTarball({source,destination,expectedIntegrity:integrity});
|
|
29
|
+
assert.equal(result.status,'VERIFIED_PRESERVED');
|
|
30
|
+
assert.equal(result.sha256,digest(artifact));
|
|
31
|
+
assert.equal(result.bytes,artifact.length);
|
|
32
|
+
assert.deepEqual(await readFile(destination),artifact);
|
|
33
|
+
await assert.rejects(()=>lstat(source),{code:'ENOENT'});
|
|
34
|
+
assert.deepEqual((await readdir(path.dirname(destination))).sort(),[path.basename(destination)]);
|
|
35
|
+
}));
|
|
36
|
+
test('R67 rejects incorrect npm-integrity before writing any destination and retains source',()=>fixture(async ({source,destination})=>{
|
|
37
|
+
await writeFile(source,artifact);
|
|
38
|
+
await assert.rejects(()=>preserveNpmTarball({source,destination,expectedIntegrity:'sha512-'+digest(Buffer.from('wrong'),'sha512')}),/integrity/i);
|
|
39
|
+
assert.deepEqual(await readFile(source),artifact);
|
|
40
|
+
await assert.rejects(()=>lstat(destination),{code:'ENOENT'});
|
|
41
|
+
}));
|
|
42
|
+
test('R67 never clobbers pre-existing different destination bytes',()=>fixture(async ({source,destination})=>{
|
|
43
|
+
await writeFile(source,artifact);await writeFile(destination,Buffer.from('other release artifact'));
|
|
44
|
+
await assert.rejects(()=>preserveNpmTarball({source,destination,expectedIntegrity:integrity}),/exists|conflict|different/i);
|
|
45
|
+
assert.deepEqual(await readFile(source),artifact);
|
|
46
|
+
assert.equal((await readFile(destination)).toString(),'other release artifact');
|
|
47
|
+
}));
|
|
48
|
+
test('R67 repairs post-commit/pre-cleanup crash without repeating publication and validates immutable destination',()=>fixture(async ({source,destination})=>{
|
|
49
|
+
await writeFile(source,artifact);await writeFile(destination,artifact);
|
|
50
|
+
const result=await preserveNpmTarball({source,destination,expectedIntegrity:integrity});
|
|
51
|
+
assert.equal(result.status,'VERIFIED_ALREADY_PRESERVED');
|
|
52
|
+
assert.deepEqual(await readFile(destination),artifact);
|
|
53
|
+
await assert.rejects(()=>lstat(source),{code:'ENOENT'});
|
|
54
|
+
}));
|
|
55
|
+
test('R67 rejects a symlink/reparse source and leaves destination absent',()=>fixture(async ({source,destination})=>{
|
|
56
|
+
await writeFile(source+'.real',artifact);
|
|
57
|
+
try{await symlink(source+'.real',source);}
|
|
58
|
+
catch(error){
|
|
59
|
+
if(process.platform!=='win32'||error?.code!=='EPERM')throw error;
|
|
60
|
+
const junctionTarget=source+'.junction-target';
|
|
61
|
+
await mkdir(junctionTarget);
|
|
62
|
+
await symlink(junctionTarget,source,'junction');
|
|
63
|
+
}
|
|
64
|
+
await assert.rejects(()=>preserveNpmTarball({source,destination,expectedIntegrity:integrity}),/symlink|regular/i);
|
|
65
|
+
await assert.rejects(()=>lstat(destination),{code:'ENOENT'});
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
test('R67 npm pack never overwrites pre-existing unclaimed artifact after EXDEV',()=>fixture(async ({source})=>{
|
|
69
|
+
await writeFile(source,artifact);
|
|
70
|
+
await assert.rejects(()=>assertVacantPackDestination({root:path.dirname(source),filename:path.basename(source)}),/PACK_BLOCKED|preserv|exists/i);
|
|
71
|
+
assert.deepEqual(await readFile(source),artifact);
|
|
72
|
+
const fresh=await assertVacantPackDestination({root:path.dirname(source),filename:'new-artifact.tgz'});
|
|
73
|
+
assert.equal(fresh,path.join(path.dirname(source),'new-artifact.tgz'));
|
|
74
|
+
}));
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {readFile} from 'node:fs/promises';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import {assertR67NpmPublishReady} from '../scripts/npm-release-readiness-r67.mjs';
|
|
8
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
9
|
+
test('sealed R67 predecessor retains its exact npm publish-readiness metadata gate',async()=>{
|
|
10
|
+
const sealed=path.join(os.homedir(),'.deadbyte-mcp','releases','v0.15.44-9f500af433df3e77');
|
|
11
|
+
const release=JSON.parse(await readFile(path.join(sealed,'RELEASE.json'),'utf8'));
|
|
12
|
+
assert.equal(release.r67_native_observation.status,'CANDIDATE_SEALED');
|
|
13
|
+
assert.doesNotThrow(()=>assertR67NpmPublishReady(release));
|
|
14
|
+
});
|
|
15
|
+
test('R67 exact mandatory gate flags must all pass before candidate npm publication',()=>{
|
|
16
|
+
const seed={version:'0.15.44',train:'R67',r67_native_observation:{
|
|
17
|
+
status:'CANDIDATE_SEALED',full_release_gate:'PASS',current_source_full_shards:'PASS',
|
|
18
|
+
native_fault_injection:'PASS',protocol_parity:'PASS',isolated_activation_rollback_restore:'PASS',
|
|
19
|
+
independent_machine_observation:'PASS'}};
|
|
20
|
+
assert.doesNotThrow(()=>assertR67NpmPublishReady(seed));
|
|
21
|
+
for(const k of Object.keys(seed.r67_native_observation)){
|
|
22
|
+
const bad=structuredClone(seed);bad.r67_native_observation[k]='PENDING';
|
|
23
|
+
assert.throws(()=>assertR67NpmPublishReady(bad),/PUBLISH_BLOCKED/);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
test('R67 real npm publisher must enforce publish readiness before npm pack / dispatch',async()=>{
|
|
27
|
+
const src=await readFile(path.join(root,'scripts','npm-release-r48.mjs'),'utf8');
|
|
28
|
+
const guard=src.indexOf('assertR67NpmPublishReady(release)');
|
|
29
|
+
const pack=src.indexOf("'pack', '--json'");
|
|
30
|
+
const submit=src.indexOf("'publish',publishTarball");
|
|
31
|
+
assert.ok(guard>0&&guard<pack&&guard<submit,'trusted metadata gate precedes network/npm pack and publication');
|
|
32
|
+
assert.ok(src.includes('preserveNpmTarball({source:tarball,destination:preservedTarball'));
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('R67 publisher checks for an existing tarball before npm pack can overwrite historical evidence',async()=>{
|
|
36
|
+
const src=await readFile(path.join(root,'scripts','npm-release-r48.mjs'),'utf8');
|
|
37
|
+
const guard=src.indexOf('await assertVacantPackDestination(');
|
|
38
|
+
const pack=src.indexOf("'pack', '--json'");
|
|
39
|
+
assert.ok(guard>0&&guard<pack,'pre-existing tarball guard must run before npm pack');
|
|
40
|
+
});
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createR67ShardPlan,reconcileR67Shards,r67ShardTimeoutMs,r67RegressionTreeHash,parseR67TapSummary} from '../src/r67-regression-shards.mjs';
|
|
4
|
+
import {mkdtemp,mkdir,writeFile,rm,symlink} from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
const names=Array.from({length:326},(_,i)=>'test/f-'+String(i).padStart(3,'0')+'.test.mjs');
|
|
8
|
+
test('R67 deterministic shards cover 326 files exactly once and remain stable under input reordering',()=>{
|
|
9
|
+
const plan=createR67ShardPlan(names,32);
|
|
10
|
+
const other=createR67ShardPlan([...names].reverse(),32);
|
|
11
|
+
assert.deepEqual(other,plan);
|
|
12
|
+
assert.equal(plan.shards.length,32);
|
|
13
|
+
assert.deepEqual(plan.shards.flatMap(x=>x.files).sort(),names);
|
|
14
|
+
assert.equal(new Set(plan.shards.flatMap(x=>x.files)).size,326);
|
|
15
|
+
assert.match(plan.plan_sha256,/^[a-f0-9]{64}$/);
|
|
16
|
+
for(const shard of plan.shards){
|
|
17
|
+
assert.ok(shard.files.length>=10&&shard.files.length<=11);
|
|
18
|
+
assert.match(shard.files_sha256,/^[a-f0-9]{64}$/);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
test('R67 verifier must BLOCK incomplete coverage, reject duplicates and never forge full PASS',()=>{
|
|
22
|
+
const plan=createR67ShardPlan(names,32);
|
|
23
|
+
const make=shard=>({shard_index:shard.index,plan_sha256:plan.plan_sha256,
|
|
24
|
+
files_sha256:shard.files_sha256,status:'PASS',exit_code:0,test_count:shard.files.length,passed:shard.files.length,failed:0});
|
|
25
|
+
const receipts=plan.shards.slice(0,31).map(make);
|
|
26
|
+
const incomplete=reconcileR67Shards(plan,receipts);
|
|
27
|
+
assert.equal(incomplete.status,'BLOCKED');
|
|
28
|
+
assert.deepEqual(incomplete.missing,[31]);
|
|
29
|
+
assert.throws(()=>reconcileR67Shards(plan,[...receipts,receipts[0]]),/duplicate/i);
|
|
30
|
+
const complete=reconcileR67Shards(plan,plan.shards.map(make));
|
|
31
|
+
assert.equal(complete.status,'PASS');
|
|
32
|
+
assert.equal(complete.file_count,326);
|
|
33
|
+
assert.equal(complete.failed,0);
|
|
34
|
+
});
|
|
35
|
+
test('R67 verifier fails closed for tampered test partition, nonzero exit and UNKNOWN outcomes',()=>{
|
|
36
|
+
const plan=createR67ShardPlan(names,32);
|
|
37
|
+
const good=plan.shards.map(shard=>({shard_index:shard.index,plan_sha256:plan.plan_sha256,
|
|
38
|
+
files_sha256:shard.files_sha256,status:'PASS',exit_code:0,test_count:shard.files.length,passed:shard.files.length,failed:0}));
|
|
39
|
+
const altered=structuredClone(good);altered[5].files_sha256='0'.repeat(64);
|
|
40
|
+
assert.equal(reconcileR67Shards(plan,altered).status,'FAILED');
|
|
41
|
+
const failed=structuredClone(good);failed[3].exit_code=1;failed[3].status='FAILED';failed[3].failed=1;
|
|
42
|
+
assert.equal(reconcileR67Shards(plan,failed).status,'FAILED');
|
|
43
|
+
const unknown=structuredClone(good);unknown[2].status='OUTCOME_UNKNOWN';unknown[2].exit_code=null;
|
|
44
|
+
assert.equal(reconcileR67Shards(plan,unknown).status,'OUTCOME_UNKNOWN');
|
|
45
|
+
assert.throws(()=>createR67ShardPlan([...names,names[0]],32),/duplicate/);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('R67 real timed-out shard with null TAP counts remains OUTCOME_UNKNOWN, not forged FAILED',()=>{
|
|
49
|
+
const plan=createR67ShardPlan(names,32);
|
|
50
|
+
const receipts=plan.shards.map(shard=>({shard_index:shard.index,plan_sha256:plan.plan_sha256,
|
|
51
|
+
files_sha256:shard.files_sha256,status:'PASS',exit_code:0,test_count:shard.files.length,passed:shard.files.length,failed:0}));
|
|
52
|
+
receipts[5]={...receipts[5],status:'OUTCOME_UNKNOWN',exit_code:null,test_count:null,passed:null,failed:null};
|
|
53
|
+
const result=reconcileR67Shards(plan,receipts);
|
|
54
|
+
assert.equal(result.status,'OUTCOME_UNKNOWN');
|
|
55
|
+
assert.equal(result.integrity_failed,false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('R67 measured 110.4s remote and 53.2s controller integration suites get isolated bounded deadlines',()=>{
|
|
59
|
+
const remote='test/remote-session-cli.test.mjs';
|
|
60
|
+
const controller='test/r58-controller-live-local.test.mjs';
|
|
61
|
+
const input=[...names,remote,controller];
|
|
62
|
+
const first=createR67ShardPlan(input,32);
|
|
63
|
+
const repeat=createR67ShardPlan([...input].reverse(),32);
|
|
64
|
+
assert.deepEqual(repeat,first);
|
|
65
|
+
const remoteShard=first.shards.find(s=>s.files.includes(remote));
|
|
66
|
+
const controllerShard=first.shards.find(s=>s.files.includes(controller));
|
|
67
|
+
assert.deepEqual(remoteShard.files,[remote]);
|
|
68
|
+
assert.deepEqual(controllerShard.files,[controller]);
|
|
69
|
+
assert.notEqual(remoteShard.index,controllerShard.index);
|
|
70
|
+
assert.equal(r67ShardTimeoutMs(remoteShard),165000);
|
|
71
|
+
assert.equal(r67ShardTimeoutMs(controllerShard),120000);
|
|
72
|
+
assert.deepEqual(first.shards.flatMap(s=>s.files).sort(),[...input].sort());
|
|
73
|
+
for(const shard of first.shards.filter(s=>s.index!==remoteShard.index&&s.index!==controllerShard.index)){
|
|
74
|
+
assert.ok(shard.files.length>=10&&shard.files.length<=11);
|
|
75
|
+
assert.equal(r67ShardTimeoutMs(shard),58000);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('R67 regression fingerprint detects implementation-only source drift even if tests are untouched',async()=>{
|
|
80
|
+
const dir=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r67-fingerprint-'));
|
|
81
|
+
try{
|
|
82
|
+
await mkdir(path.join(dir,'src'));await mkdir(path.join(dir,'test'));
|
|
83
|
+
await writeFile(path.join(dir,'package.json'),'{}');
|
|
84
|
+
await writeFile(path.join(dir,'src','core.mjs'),'export const number = 1;');
|
|
85
|
+
await writeFile(path.join(dir,'test','core.test.mjs'),'test does not change');
|
|
86
|
+
const opts={roots:['src','test'],topLevel:['package.json'],extra:[]};
|
|
87
|
+
const baseline=await r67RegressionTreeHash(dir,opts);
|
|
88
|
+
assert.match(baseline.sha256,/^[a-f0-9]{64}$/);
|
|
89
|
+
assert.equal(baseline.file_count,3);
|
|
90
|
+
const same=await r67RegressionTreeHash(dir,opts);
|
|
91
|
+
assert.deepEqual(same,baseline);
|
|
92
|
+
await writeFile(path.join(dir,'src','core.mjs'),'export const number = 2;');
|
|
93
|
+
const drift=await r67RegressionTreeHash(dir,opts);
|
|
94
|
+
assert.notEqual(drift.sha256,baseline.sha256,'changing only source must invalidate receipts');
|
|
95
|
+
try{await symlink(path.join(dir,'package.json'),path.join(dir,'src','backdoor.mjs'));}
|
|
96
|
+
catch(error){
|
|
97
|
+
if(process.platform!=='win32'||error?.code!=='EPERM')throw error;
|
|
98
|
+
const junctionTarget=path.join(dir,'junction-target');
|
|
99
|
+
await mkdir(junctionTarget);
|
|
100
|
+
await symlink(junctionTarget,path.join(dir,'src','backdoor'),'junction');
|
|
101
|
+
}
|
|
102
|
+
await assert.rejects(()=>r67RegressionTreeHash(dir,opts),/symlink/i);
|
|
103
|
+
}finally{await rm(dir,{recursive:true,force:true});}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('R67 terminal TAP summary enforces complete counts and rejects forged/truncated PASS',()=>{
|
|
107
|
+
const ok=['✔ fixture succeeded (1ms)','ℹ tests 5','ℹ suites 0','ℹ pass 4','ℹ fail 0','ℹ cancelled 0','ℹ skipped 1','ℹ todo 0'].join('\n');
|
|
108
|
+
const parsed=parseR67TapSummary(ok);
|
|
109
|
+
assert.deepEqual(parsed,{tests:5,pass:4,fail:0,cancelled:0,skipped:1,todo:0});
|
|
110
|
+
assert.equal(parseR67TapSummary('test does not carry terminal TAP'),null);
|
|
111
|
+
assert.equal(parseR67TapSummary(ok.replace('ℹ tests 5','ℹ tests 6')),null);
|
|
112
|
+
assert.equal(parseR67TapSummary(ok.replace('ℹ cancelled 0','ℹ cancelled 1')),null);
|
|
113
|
+
assert.equal(parseR67TapSummary(ok.replace('ℹ todo 0','ℹ todo 1')),null);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('R67 v2 shard receipt rejects inconsistent skip totals and cancelled outcomes',()=>{
|
|
117
|
+
const plan=createR67ShardPlan(names,32);
|
|
118
|
+
const good=plan.shards.map(shard=>({schema:'deadbyte.r67-shard-result.v2',shard_index:shard.index,
|
|
119
|
+
plan_sha256:plan.plan_sha256,files_sha256:shard.files_sha256,status:'PASS',exit_code:0,
|
|
120
|
+
test_count:shard.files.length,passed:shard.files.length,failed:0,skipped:0,cancelled:0,todo:0}));
|
|
121
|
+
assert.equal(reconcileR67Shards(plan,good).status,'PASS');
|
|
122
|
+
const altered=structuredClone(good);altered[1].skipped=1;
|
|
123
|
+
assert.equal(reconcileR67Shards(plan,altered).status,'FAILED');
|
|
124
|
+
const cancelled=structuredClone(good);cancelled[1].passed-=1;cancelled[1].cancelled=1;
|
|
125
|
+
assert.equal(reconcileR67Shards(plan,cancelled).status,'FAILED');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('R67 CLI reconciliation delegates to same independently tested v2 evaluator',async()=>{
|
|
129
|
+
const {readFile}=await import('node:fs/promises');
|
|
130
|
+
const {fileURLToPath}=await import('node:url');
|
|
131
|
+
const here=path.dirname(fileURLToPath(import.meta.url));
|
|
132
|
+
const runner=await readFile(path.join(here,'..','scripts','r67-regression-shard-run.mjs'),'utf8');
|
|
133
|
+
const cli=await readFile(path.join(here,'..','scripts','r67-regression-reconcile.mjs'),'utf8');
|
|
134
|
+
const core=await readFile(path.join(here,'..','src','r67-evidence-reconcile.mjs'),'utf8');
|
|
135
|
+
assert.ok(runner.includes("schema:'deadbyte.r67-shard-result.v2'"));
|
|
136
|
+
assert.ok(cli.includes("import {reconcileR67Evidence} from"));
|
|
137
|
+
assert.ok(core.includes("result.schema!=='deadbyte.r67-shard-result.v2'"));
|
|
138
|
+
assert.ok(cli.includes("if(result.status!=='PASS')process.exitCode=2;"));
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test('R67 evidence reconciler fails closed on absent operation identity',async()=>{
|
|
142
|
+
const {reconcileR67Evidence}=await import('../src/r67-evidence-reconcile.mjs');
|
|
143
|
+
await assert.rejects(()=>reconcileR67Evidence({root:'X',evidenceRoot:'Y',operationId:'../escape'}),/bounded root|operation identity/);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('R67 TAP nonpass ledger preserves test name and explicit SKIP reason, rejects unnamed skips',async()=>{
|
|
147
|
+
const {parseR67NonpassCases}=await import('../src/r67-regression-shards.mjs');
|
|
148
|
+
const tap=[
|
|
149
|
+
'TAP version 13',
|
|
150
|
+
'ok 1 - works',
|
|
151
|
+
'ok 2 - Windows-only hook # SKIP unavailable on platform',
|
|
152
|
+
' ok 1 - case without reason # SKIP',
|
|
153
|
+
'ok 3 - not yet implemented # TODO waiting on API',
|
|
154
|
+
'# tests 4','# pass 1','# fail 0','# cancelled 0','# skipped 2','# todo 1'
|
|
155
|
+
].join('\n');
|
|
156
|
+
const v=parseR67NonpassCases(tap);
|
|
157
|
+
assert.equal(v.cases.length,3);
|
|
158
|
+
assert.equal(v.cases[0].name,'Windows-only hook');
|
|
159
|
+
assert.equal(v.cases[0].reason,'unavailable on platform');
|
|
160
|
+
assert.equal(v.cases[1].reason,null);
|
|
161
|
+
assert.equal(v.cases[2].kind,'TODO');
|
|
162
|
+
assert.equal(v.fully_classified,false);
|
|
163
|
+
assert.equal(v.unclassified_count,1);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('R67 preserves bounded case identity for a TAP FAILED leaf instead of empty nonpass evidence',async()=>{
|
|
167
|
+
const {parseR67FailedCases}=await import('../src/r67-regression-shards.mjs');
|
|
168
|
+
const tap=['TAP version 13','# Subtest: remote test','not ok 4 - remote owner cleanup is durable',
|
|
169
|
+
' ---',' error: \'sensitive runtime stack omitted\'',' ...','# tests 26','# pass 25','# fail 1','# cancelled 0','# skipped 0','# todo 0'].join('\n');
|
|
170
|
+
const parsed=parseR67FailedCases(tap);
|
|
171
|
+
assert.equal(parsed.count,1);
|
|
172
|
+
assert.equal(parsed.cases[0].name,'remote owner cleanup is durable');
|
|
173
|
+
assert.match(parsed.cases[0].name_sha256,/^[a-f0-9]{64}$/);
|
|
174
|
+
assert.ok(!JSON.stringify(parsed).includes('sensitive runtime stack'));
|
|
175
|
+
});
|
|
176
|
+
test('R67 failed-case parser remains fail closed on missing TAP and secret-shaped titles',async()=>{
|
|
177
|
+
const {parseR67FailedCases}=await import('../src/r67-regression-shards.mjs');
|
|
178
|
+
const missing=parseR67FailedCases('TAP version 13\n# tests 2\n# pass 1\n# fail 1\n');
|
|
179
|
+
assert.equal(missing.complete,false);
|
|
180
|
+
const title='remote failed sk-proj-secret-token-value';
|
|
181
|
+
const guarded=parseR67FailedCases('not ok 1 - '+title+'\n# tests 1\n# pass 0\n# fail 1\n# cancelled 0\n# skipped 0\n# todo 0');
|
|
182
|
+
assert.equal(guarded.cases[0].name,'[redacted]');
|
|
183
|
+
assert.equal(guarded.count,1);
|
|
184
|
+
});
|