deadbyte-mcp 0.15.43 → 0.15.44
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 +128 -67
- package/PROVENANCE.json +23 -12
- package/README.txt +18 -18
- package/RELEASE.json +28 -14
- package/SBOM.cdx.json +14 -2
- package/TEST-MATRIX.json +41 -10
- 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-native-inspect-r67.exe +0 -0
- package/bin/deadbyte-native-inspect-r67.obj +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/bridge/package.json +1 -1
- package/controller/deadbyte-desktop-policy.json +5 -5
- 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/native/deadbyte-native-inspect-r67.cpp +252 -0
- package/package.json +6 -3
- package/proof/CONTAINMENT-BUILD.txt +6 -6
- package/scripts/build-native-inspector-r67.ps1 +28 -0
- package/scripts/gate-windows-r67.ps1 +56 -0
- package/scripts/npm-preserve-artifact-r67.mjs +99 -0
- package/scripts/npm-release-r48.mjs +10 -2
- package/scripts/npm-release-readiness-r67.mjs +26 -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/src/activation-continuity-engine-r65.mjs +2 -1
- package/src/autonomous-runtime.mjs +1 -1
- package/src/deadbyte-cli.mjs +33 -1
- 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-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/remote-live-log.mjs +14 -10
- package/src/tool-audit.mjs +1 -1
- package/src/version.mjs +1 -1
- package/test/agent-runtime.test.mjs +1 -1
- package/test/autonomous-loop.test.mjs +11 -6
- package/test/autonomous-runtime.test.mjs +1 -1
- package/test/coding-plane.test.mjs +1 -1
- package/test/full-max-catalog-r46.test.mjs +1 -1
- 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 +1 -1
- 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-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 +62 -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 +38 -0
- package/test/r67-regression-shards.test.mjs +184 -0
- package/test/r67-release-contract.test.mjs +44 -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/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/semantic-multilang.test.mjs +1 -1
|
@@ -12,7 +12,7 @@ const PARENT={
|
|
|
12
12
|
generation_manifest_sha256:'19ae2efcdcde9f80c474f0e55851d4ada51c22813d3e05af03b11acb28c3fca5'
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
test('R65 release identity binds exact production R64 parent and durable authority-continuity train',{skip:currentPackage.version!=='0.15.42'},async()=>{
|
|
15
|
+
test('R65 release identity binds exact production R64 parent and durable authority-continuity train',{skip:currentPackage.version!=='0.15.42' ? 'Historical R65 release contract requires 0.15.42; current '+currentPackage.version : false},async()=>{
|
|
16
16
|
const [pkg,lock,bridge,release,provenance,matrix,sbom,version,gate,gaps,design]=await Promise.all([
|
|
17
17
|
json('package.json'),json('package-lock.json'),json('bridge/package.json'),json('RELEASE.json'),
|
|
18
18
|
json('PROVENANCE.json'),json('TEST-MATRIX.json'),json('SBOM.cdx.json'),read('src/version.mjs'),
|
|
@@ -4,6 +4,7 @@ import {readFile} from 'node:fs/promises';
|
|
|
4
4
|
|
|
5
5
|
const read=rel=>readFile(new URL('../'+rel,import.meta.url),'utf8');
|
|
6
6
|
const json=async rel=>JSON.parse((await read(rel)).replace(/^\uFEFF/,''));
|
|
7
|
+
const currentPackage=await json('package.json');
|
|
7
8
|
const PARENT={
|
|
8
9
|
version:'0.15.42',
|
|
9
10
|
release_id:'v0.15.42-1b4754103dd4ca43',
|
|
@@ -11,7 +12,7 @@ const PARENT={
|
|
|
11
12
|
generation_manifest_sha256:'62dc68be55ad627f8d872b8be8c1369ecca0edf08fdedc8336c9ccee2d2809fb'
|
|
12
13
|
};
|
|
13
14
|
|
|
14
|
-
test('R66 identity is an exact prepublish doctor hotfix of sealed R65',async()=>{
|
|
15
|
+
test('R66 identity is an exact prepublish doctor hotfix of sealed R65',{skip:currentPackage.version!=='0.15.43' ? 'Historical R66 release contract requires 0.15.43; current '+currentPackage.version : false},async()=>{
|
|
15
16
|
const [pkg,lock,bridge,release,provenance,matrix,sbom,version,gate]=await Promise.all([
|
|
16
17
|
json('package.json'),json('package-lock.json'),json('bridge/package.json'),json('RELEASE.json'),
|
|
17
18
|
json('PROVENANCE.json'),json('TEST-MATRIX.json'),json('SBOM.cdx.json'),read('src/version.mjs'),
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {spawnSync} from 'node:child_process';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {nativeInspectArgs} from '../src/native-inspector-r67.mjs';
|
|
7
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
8
|
+
const cli=path.join(root,'src','deadbyte-cli.mjs');
|
|
9
|
+
const exe=path.join(root,'bin','deadbyte-native-inspect-r67.exe');
|
|
10
|
+
function invoke(...args){
|
|
11
|
+
const r=spawnSync(process.execPath,[cli,'inspect',...args],{cwd:root,shell:false,windowsHide:true,encoding:'utf8',timeout:30000,maxBuffer:2097152});
|
|
12
|
+
assert.equal(r.error,undefined,r.error?.message);return {r,value:JSON.parse(r.stdout.trim())};
|
|
13
|
+
}
|
|
14
|
+
test('R67 CLI inspect exposes native process snapshot without MCP tool additions',()=>{
|
|
15
|
+
const {r,value}=invoke('process','4');
|
|
16
|
+
assert.equal(r.status,0,r.stderr);assert.equal(value.status,'ok');assert.equal(value.operation,'process');assert.ok(value.data.processes.length<=4);
|
|
17
|
+
});
|
|
18
|
+
test('R67 CLI inspect memory self is metadata-only and PE uses actual bytes',()=>{
|
|
19
|
+
const m=invoke('memory','self','4');
|
|
20
|
+
assert.equal(m.r.status,0,m.r.stderr);assert.equal(m.value.status,'ok');assert.ok(m.value.data.regions.length<=4);
|
|
21
|
+
const p=invoke('pe',exe);
|
|
22
|
+
assert.equal(p.r.status,0,p.r.stderr);assert.equal(p.value.status,'ok');assert.match(p.value.data.file_sha256,/^[a-f0-9]{64}$/);
|
|
23
|
+
});
|
|
24
|
+
test('R67 CLI typed input rejects invalid selectors before spawning native process',()=>{
|
|
25
|
+
for(const selector of [['process','0'],['process','999999'],['memory','0'],['memory','self','9999'],['memory','4148'],['identity'],['identity','0'],['memory','4148','0'],['memory','4148','18446744073709551616'],['pe'],['unknown']]){
|
|
26
|
+
assert.throws(()=>nativeInspectArgs(selector));
|
|
27
|
+
}
|
|
28
|
+
const error=spawnSync(process.execPath,[cli,'inspect','memory','0'],{cwd:root,shell:false,windowsHide:true,encoding:'utf8',timeout:30000});
|
|
29
|
+
assert.equal(error.status,1);assert.match(error.stderr,/invalid positive limit|limit out of range|creation_filetime/);
|
|
30
|
+
});
|
|
31
|
+
test('R67 CLI preserves raw Windows error instead of fabricating data',()=>{
|
|
32
|
+
const {r,value}=invoke('memory','4294967295','1','4');
|
|
33
|
+
assert.equal(r.status,2);assert.equal(value.status,'failed');assert.match(value.error,/win32|PID/);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('R67 CLI rejects stale PID identity before successful region inspection',()=>{
|
|
37
|
+
const {r,value}=invoke('memory',String(process.pid),'1','4');
|
|
38
|
+
assert.equal(r.status,2);
|
|
39
|
+
assert.equal(value.error_code,'PROCESS_IDENTITY_MISMATCH');
|
|
40
|
+
assert.equal(value.win32_error,0);
|
|
41
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {spawnSync} from 'node:child_process';
|
|
4
|
+
import {mkdtemp,mkdir,writeFile,readFile,rm} from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {fileURLToPath} from 'node:url';
|
|
8
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
9
|
+
const cli=path.join(root,'src','deadbyte-cli.mjs');
|
|
10
|
+
test('R67 offline diagnostic CLI exports without activating remote, uploading or revealing raw log bytes',async()=>{
|
|
11
|
+
const folder=await mkdtemp(path.join(os.tmpdir(),'r67-diagnostic-cli-'));
|
|
12
|
+
try{
|
|
13
|
+
const state=path.join(folder,'state');
|
|
14
|
+
const output=path.join(folder,'report.zip');
|
|
15
|
+
await mkdir(path.join(state,'runtime','logs'),{recursive:true});
|
|
16
|
+
await writeFile(path.join(state,'active-release.json'),JSON.stringify({release_id:'v0.15.43-'+'a'.repeat(16),manifest_sha256:'a'.repeat(64)}));
|
|
17
|
+
await writeFile(path.join(state,'runtime','logs','controller-start.stderr.log'),'WARN reconnect win32=109 SECRET_CANARY_52917\n');
|
|
18
|
+
const run=spawnSync(process.execPath,[cli,'diagnostics','export','--output',output],{
|
|
19
|
+
cwd:root,env:{...process.env,DEADBYTE_STATE_ROOT:state},encoding:'utf8',shell:false,windowsHide:true,timeout:20000
|
|
20
|
+
});
|
|
21
|
+
assert.equal(run.status,0,run.stderr);
|
|
22
|
+
const result=JSON.parse(run.stdout);
|
|
23
|
+
assert.equal(result.status,'written_local_only');
|
|
24
|
+
assert.equal(result.uploaded,false);
|
|
25
|
+
const b=await readFile(output);
|
|
26
|
+
assert.equal(b.includes(Buffer.from('SECRET_CANARY_52917')),false);
|
|
27
|
+
assert.equal(b.readUInt32LE(0),0x04034b50);
|
|
28
|
+
const reject=spawnSync(process.execPath,[cli,'diagnostics','export','--output',path.join(folder,'second.zip'),'--upload'],{
|
|
29
|
+
cwd:root,env:{...process.env,DEADBYTE_STATE_ROOT:state},encoding:'utf8',shell:false,windowsHide:true,timeout:20000
|
|
30
|
+
});
|
|
31
|
+
assert.equal(reject.status,1);
|
|
32
|
+
assert.match(reject.stderr,/upload|syntax|diagnostics/i);
|
|
33
|
+
}finally{await rm(folder,{recursive:true,force:true});}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('R67 diagnostic CLI verify checks local ZIP without starting remote session',async()=>{
|
|
37
|
+
const {makeOfflineDiagnosticBundle}=await import('../src/r67-diagnostic-export.mjs');
|
|
38
|
+
const folder=await mkdtemp(path.join(os.tmpdir(),'r67-diagnostic-verify-cli-'));
|
|
39
|
+
try{
|
|
40
|
+
const file=path.join(folder,'report.zip');
|
|
41
|
+
const bundle=makeOfflineDiagnosticBundle({
|
|
42
|
+
releaseId:'v0.15.43-'+'a'.repeat(16),manifestSha256:'a'.repeat(64),logs:[]
|
|
43
|
+
});
|
|
44
|
+
await writeFile(file,bundle.zip);
|
|
45
|
+
const r=spawnSync(process.execPath,[cli,'diagnostics','verify','--input',file],{
|
|
46
|
+
cwd:root,encoding:'utf8',shell:false,windowsHide:true,timeout:20000
|
|
47
|
+
});
|
|
48
|
+
assert.equal(r.status,0,r.stderr);
|
|
49
|
+
const result=JSON.parse(r.stdout);
|
|
50
|
+
assert.equal(result.status,'VERIFIED_LOCAL_STRUCTURE');
|
|
51
|
+
assert.equal(result.archive_sha256,bundle.archive_sha256);
|
|
52
|
+
const broken=Buffer.from(bundle.zip);broken[50]^=1;
|
|
53
|
+
const brokenPath=path.join(folder,'corrupt.zip');await writeFile(brokenPath,broken);
|
|
54
|
+
const denial=spawnSync(process.execPath,[cli,'diagnostics','verify','--input',brokenPath],{
|
|
55
|
+
cwd:root,encoding:'utf8',shell:false,windowsHide:true,timeout:20000
|
|
56
|
+
});
|
|
57
|
+
assert.equal(denial.status,1);
|
|
58
|
+
assert.match(denial.stderr,/integrity|ZIP|unsafe/i);
|
|
59
|
+
}finally{await rm(folder,{recursive:true,force:true});}
|
|
60
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {makeOfflineDiagnosticBundle} from '../src/r67-diagnostic-export.mjs';
|
|
4
|
+
import {verifyOfflineDiagnosticsZip} from '../src/r67-diagnostics-verify.mjs';
|
|
5
|
+
const sha='a'.repeat(64);
|
|
6
|
+
const archive=()=>makeOfflineDiagnosticBundle({releaseId:'v0.15.43-'+'a'.repeat(16),manifestSha256:sha,
|
|
7
|
+
logs:[{source:'controller-start.stderr.log',content:'2026-10-11T03:04:05.000Z WARN reconnect win32=109 PASSWORD_CANARY_91020'}]}).zip;
|
|
8
|
+
test('R67 offline diagnostic independently verifies ZIP CRC, bounded entries and fixed schemas',()=>{
|
|
9
|
+
const v=verifyOfflineDiagnosticsZip(archive());
|
|
10
|
+
assert.equal(v.status,'VERIFIED_LOCAL_STRUCTURE');
|
|
11
|
+
assert.equal(v.uploaded,false);
|
|
12
|
+
assert.equal(v.entry_count,2);
|
|
13
|
+
assert.equal(v.summary.redaction,'FIXED_FIELDS_ONLY');
|
|
14
|
+
assert.equal(v.summary.network_upload,'NEVER');
|
|
15
|
+
assert.match(v.archive_sha256,/^[a-f0-9]{64}$/);
|
|
16
|
+
assert.equal(v.summary.raw_log_lines_exported,0);
|
|
17
|
+
});
|
|
18
|
+
test('R67 verifier rejects byte tampering without an updated CRC',()=>{
|
|
19
|
+
const corrupted=Buffer.from(archive());
|
|
20
|
+
const phrase=Buffer.from('FIXED_FIELDS_ONLY');
|
|
21
|
+
const offset=corrupted.indexOf(phrase);
|
|
22
|
+
assert.ok(offset>0);
|
|
23
|
+
corrupted[offset]^=1;
|
|
24
|
+
assert.throws(()=>verifyOfflineDiagnosticsZip(corrupted),/CRC|hash|corrupt/i);
|
|
25
|
+
});
|
|
26
|
+
test('R67 verifier rejects trailing junk, extra archive entries and invalid size bounds',()=>{
|
|
27
|
+
const good=archive();
|
|
28
|
+
assert.throws(()=>verifyOfflineDiagnosticsZip(Buffer.concat([good,Buffer.alloc(20)])),/trailing|eocd|boundary|ZIP/i);
|
|
29
|
+
assert.throws(()=>verifyOfflineDiagnosticsZip(good.subarray(0,good.length-5)),/truncated|eocd|boundary|ZIP/i);
|
|
30
|
+
assert.throws(()=>verifyOfflineDiagnosticsZip(Buffer.alloc(4*1024*1024+1)),/size|large/i);
|
|
31
|
+
});
|
|
32
|
+
test('R67 verify rejects semantic tampering even when attacker recomputes all ZIP CRCs',()=>{
|
|
33
|
+
const buf=Buffer.from(archive());
|
|
34
|
+
const find=Buffer.from('FIXED_FIELDS_ONLY');
|
|
35
|
+
const at=buf.indexOf(find);
|
|
36
|
+
assert.ok(at>0);
|
|
37
|
+
buf.write('ALLOW_RAW_LOGS___',at,'utf8');
|
|
38
|
+
const dataStart=30+buf.readUInt16LE(26),dataLength=buf.readUInt32LE(18);
|
|
39
|
+
let crc=0xffffffff;
|
|
40
|
+
for(const byte of buf.subarray(dataStart,dataStart+dataLength)){
|
|
41
|
+
crc^=byte;
|
|
42
|
+
for(let j=0;j<8;j++)crc=(crc>>>1)^((crc&1)?0xedb88320:0);
|
|
43
|
+
}
|
|
44
|
+
crc=(crc^0xffffffff)>>>0;
|
|
45
|
+
const dir=buf.readUInt32LE(buf.length-22+16);
|
|
46
|
+
buf.writeUInt32LE(crc,14);
|
|
47
|
+
buf.writeUInt32LE(crc,dir+16);
|
|
48
|
+
assert.throws(()=>verifyOfflineDiagnosticsZip(buf),/unsafe diagnostic summary/i);
|
|
49
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {spawnSync} from 'node:child_process';
|
|
4
|
+
import {mkdtemp,writeFile,rm} from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import {makeOfflineDiagnosticBundle} from '../src/r67-diagnostic-export.mjs';
|
|
8
|
+
test('R67 ZIP interoperates with independent Windows .NET ZipArchive reader',async()=>{
|
|
9
|
+
const folder=await mkdtemp(path.join(os.tmpdir(),'r67-dotnet-zip-'));
|
|
10
|
+
try{
|
|
11
|
+
const file=path.join(folder,'diag.zip');
|
|
12
|
+
const x=makeOfflineDiagnosticBundle({releaseId:'v0.15.43-'+'a'.repeat(16),manifestSha256:'a'.repeat(64),logs:[
|
|
13
|
+
{source:'controller-start.stderr.log',content:'2026-10-11T01:02:03.000Z ERROR timeout win32=109 TOKEN_CANARY_7841'}
|
|
14
|
+
]});
|
|
15
|
+
await writeFile(file,x.zip);
|
|
16
|
+
const escaped=file.replaceAll("'","''");
|
|
17
|
+
const ps=[
|
|
18
|
+
'$ErrorActionPreference="Stop"',
|
|
19
|
+
'Add-Type -AssemblyName System.IO.Compression.FileSystem',
|
|
20
|
+
"$zip=[System.IO.Compression.ZipFile]::OpenRead('"+escaped+"')",
|
|
21
|
+
"try{",
|
|
22
|
+
" $entries=@($zip.Entries|ForEach-Object {$_.FullName})",
|
|
23
|
+
" if($entries.Count -ne 2 -or $entries[0] -ne 'diagnostics.json' -or $entries[1] -ne 'redacted-events.json'){throw 'ZIP_ENTRY_MISMATCH'}",
|
|
24
|
+
" foreach($entry in $zip.Entries){",
|
|
25
|
+
" $reader=New-Object System.IO.StreamReader($entry.Open())",
|
|
26
|
+
" try{$body=$reader.ReadToEnd();$parsed=ConvertFrom-Json -InputObject $body;if(!$parsed){throw 'INVALID_JSON'}}finally{$reader.Dispose()}",
|
|
27
|
+
" if($body.Contains('TOKEN_CANARY_7841')){throw 'SECRET_LEAK'}",
|
|
28
|
+
" }",
|
|
29
|
+
" Write-Output 'ZIP_ORACLE_PASS'",
|
|
30
|
+
"}finally{$zip.Dispose()}"
|
|
31
|
+
].join(';');
|
|
32
|
+
const r=spawnSync('powershell.exe',['-NoLogo','-NoProfile','-NonInteractive','-Command',ps],
|
|
33
|
+
{windowsHide:true,shell:false,encoding:'utf8',timeout:20000,maxBuffer:1024*1024});
|
|
34
|
+
assert.equal(r.error,undefined,r.error?.message);
|
|
35
|
+
assert.equal(r.status,0,r.stderr);
|
|
36
|
+
assert.match(r.stdout,/ZIP_ORACLE_PASS/);
|
|
37
|
+
}finally{await rm(folder,{recursive:true,force:true});}
|
|
38
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {mkdtemp,mkdir,writeFile,readFile,rm,lstat} from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {redactDiagnosticLine,makeOfflineDiagnosticBundle,exportOfflineDiagnostics} from '../src/r67-diagnostic-export.mjs';
|
|
7
|
+
const secret='SENTINEL_SECRET_CANARY_984031';
|
|
8
|
+
const token='sk-proj-FAKE_DIAGNOSTIC_SECRET';
|
|
9
|
+
const sha='a'.repeat(64);
|
|
10
|
+
test('R67 local diagnostics use irreversible token-free allowlist, not regex-redacted raw log',()=>{
|
|
11
|
+
const line='2026-10-11T05:44:03.000Z ERROR reconnect win32=109 Authorization: Bearer '+secret+' '+token+' C:\\Users\\DEADBYTE\\passwords.txt';
|
|
12
|
+
const output=redactDiagnosticLine(line);
|
|
13
|
+
assert.deepEqual(output,{timestamp:'2026-10-11T05:44:03.000Z',level:'ERROR',event:'reconnect',win32_error:109});
|
|
14
|
+
assert.ok(!JSON.stringify(output).includes('FAKE'));
|
|
15
|
+
const unknown=redactDiagnosticLine('password='+secret+' '+token+' cwd=/private/location');
|
|
16
|
+
assert.equal(unknown,null,'unknown free-form lines must be dropped');
|
|
17
|
+
});
|
|
18
|
+
test('R67 offline ZIP contains only fixed names, derived summaries, no raw secrets or network URL',()=>{
|
|
19
|
+
const bundle=makeOfflineDiagnosticBundle({
|
|
20
|
+
releaseId:'v0.15.43-'+'a'.repeat(16),manifestSha256:sha,
|
|
21
|
+
logs:[{source:'controller-start.stderr.log',content:'ERROR reconnect win32=109 Bearer '+secret+'\nINFO ready '+token+'\npassword='+secret+'\n'}]
|
|
22
|
+
});
|
|
23
|
+
assert.ok(Buffer.isBuffer(bundle.zip));
|
|
24
|
+
assert.equal(bundle.zip.readUInt32LE(0),0x04034b50,'ZIP local file header');
|
|
25
|
+
assert.ok(bundle.zip.includes(Buffer.from('diagnostics.json')));
|
|
26
|
+
assert.ok(bundle.zip.includes(Buffer.from('redacted-events.json')));
|
|
27
|
+
for(const value of [secret,token,'password=','Bearer','C:\\Users\\','https://'])
|
|
28
|
+
assert.equal(bundle.zip.includes(Buffer.from(value)),false,'raw or sensitive text leaked');
|
|
29
|
+
assert.equal(bundle.summary.schema,'deadbyte.r67-offline-diagnostics.v1');
|
|
30
|
+
assert.equal(bundle.summary.network_upload,'NEVER');
|
|
31
|
+
assert.equal(bundle.summary.source_count,1);
|
|
32
|
+
assert.equal(bundle.summary.redacted_event_count,2);
|
|
33
|
+
assert.match(bundle.archive_sha256,/^[a-f0-9]{64}$/);
|
|
34
|
+
});
|
|
35
|
+
test('R67 exports only to owner-selected local path, absent target and no arbitrary logs',async()=>{
|
|
36
|
+
const folder=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r67-diagnostic-test-'));
|
|
37
|
+
try{
|
|
38
|
+
const stateRoot=path.join(folder,'state');
|
|
39
|
+
await mkdir(path.join(stateRoot,'runtime','logs'),{recursive:true});
|
|
40
|
+
await writeFile(path.join(stateRoot,'active-release.json'),JSON.stringify({release_id:'v0.15.43-'+'a'.repeat(16),manifest_sha256:sha}));
|
|
41
|
+
await writeFile(path.join(stateRoot,'runtime','logs','controller-start.stderr.log'),'ERROR timeout Authorization: Bearer '+secret+'\nINFO connected '+token+'\n');
|
|
42
|
+
await writeFile(path.join(stateRoot,'runtime','logs','arbitrary.log'),'ERROR THIS MUST NOT BE INCLUDED '+secret+'\n');
|
|
43
|
+
const output=path.join(folder,'owner-approved.zip');
|
|
44
|
+
const result=await exportOfflineDiagnostics({stateRoot,outputPath:output});
|
|
45
|
+
assert.equal(result.status,'written_local_only');
|
|
46
|
+
assert.equal(result.uploaded,false);
|
|
47
|
+
const actual=await readFile(output);
|
|
48
|
+
assert.equal(actual.readUInt32LE(0),0x04034b50);
|
|
49
|
+
assert.equal(actual.includes(Buffer.from(secret)),false);
|
|
50
|
+
assert.equal(actual.includes(Buffer.from(token)),false);
|
|
51
|
+
assert.equal(actual.includes(Buffer.from('arbitrary.log')),false);
|
|
52
|
+
const info=await lstat(output);
|
|
53
|
+
assert.ok(info.isFile()&&!info.isSymbolicLink());
|
|
54
|
+
await assert.rejects(()=>exportOfflineDiagnostics({stateRoot,outputPath:output}),/EEXIST|already exists/);
|
|
55
|
+
}finally{await rm(folder,{recursive:true,force:true});}
|
|
56
|
+
});
|
|
57
|
+
test('R67 without output path fails before read and upload option is always forbidden',async()=>{
|
|
58
|
+
await assert.rejects(()=>exportOfflineDiagnostics({stateRoot:'C:\\a'}),/outputPath/);
|
|
59
|
+
await assert.rejects(()=>exportOfflineDiagnostics({stateRoot:'C:\\a',outputPath:'X',upload:true}),/upload.*disabled|no network/i);
|
|
60
|
+
});
|
|
@@ -0,0 +1,186 @@
|
|
|
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} from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import {createR67ShardPlan,r67RegressionTreeHash,r67ShardTimeoutMs} from '../src/r67-regression-shards.mjs';
|
|
8
|
+
import {reconcileR67Evidence} from '../src/r67-evidence-reconcile.mjs';
|
|
9
|
+
|
|
10
|
+
const sha=b=>createHash('sha256').update(b).digest('hex');
|
|
11
|
+
const op='r67-e2e-fixture-contract';
|
|
12
|
+
async function fixture(){
|
|
13
|
+
const parent=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r67-e2e-'));
|
|
14
|
+
const root=path.join(parent,'candidate'),evidenceRoot=path.join(parent,'evidence');
|
|
15
|
+
await mkdir(path.join(root,'test'),{recursive:true});
|
|
16
|
+
await mkdir(path.join(root,'src'));
|
|
17
|
+
await mkdir(evidenceRoot);
|
|
18
|
+
await writeFile(path.join(root,'package.json'),'{}\n');
|
|
19
|
+
await writeFile(path.join(root,'src','core.mjs'),'export const r67 = true;\n');
|
|
20
|
+
const names=Array.from({length:32},(_,i)=>'test/fixture-'+String(i).padStart(2,'0')+'.test.mjs');
|
|
21
|
+
for(const name of names)await writeFile(path.join(root,name),'import test from "node:test"; test("fixture",()=>{});\n');
|
|
22
|
+
const plan=createR67ShardPlan(names,32);
|
|
23
|
+
const hashes=await Promise.all(names.map(async name=>[name,sha(await readFile(path.join(root,name)))]));
|
|
24
|
+
const testTree=sha(Buffer.from(JSON.stringify(hashes)));
|
|
25
|
+
const opts={roots:['src','test'],topLevel:['package.json'],extra:[]};
|
|
26
|
+
const tree=await r67RegressionTreeHash(root,opts);
|
|
27
|
+
async function writeShard(index,{kind='valid',signed=false}={}){
|
|
28
|
+
const shard=plan.shards[index],folder=path.join(evidenceRoot,op+'-shard-'+String(index).padStart(2,'0'));
|
|
29
|
+
await mkdir(folder,{recursive:true});
|
|
30
|
+
const intent={schema:'deadbyte.r67-shard-intent.v1',operation:op,shard_index:index,plan_sha256:plan.plan_sha256,
|
|
31
|
+
files_sha256:shard.files_sha256,test_tree_sha256:testTree,source_tree_sha256:tree.sha256,
|
|
32
|
+
source_file_count:tree.file_count,started_at_utc:'2026-10-11T00:00:00.000Z',files:shard.files};
|
|
33
|
+
await writeFile(path.join(folder,'intent.json'),JSON.stringify(intent));
|
|
34
|
+
const status=kind==='unknown'?'OUTCOME_UNKNOWN':'PASS';
|
|
35
|
+
const result={schema:kind==='v1'?'deadbyte.r67-shard-result.v1':'deadbyte.r67-shard-result.v2',
|
|
36
|
+
operation_id:op,shard_index:index,plan_sha256:plan.plan_sha256,
|
|
37
|
+
files_sha256:shard.files_sha256,test_tree_sha256:testTree,source_tree_sha256:tree.sha256,
|
|
38
|
+
source_file_count:tree.file_count,status,exit_code:status==='PASS'?0:null,
|
|
39
|
+
test_count:status==='PASS'?1:null,passed:status==='PASS'?1:null,failed:status==='PASS'?0:null,
|
|
40
|
+
skipped:status==='PASS'?0:null,cancelled:status==='PASS'?0:null,todo:status==='PASS'?0:null,
|
|
41
|
+
configured_timeout_ms:r67ShardTimeoutMs(shard),timed_out:false,failure_reason:null,elapsed_ms:1,
|
|
42
|
+
stdout_sha256:sha(Buffer.from('fixture output')),stderr_sha256:sha(Buffer.alloc(0))};
|
|
43
|
+
if(kind==='tampered')result.files_sha256='f'.repeat(64);
|
|
44
|
+
const receipt_sha256=sha(Buffer.from(JSON.stringify(result)));
|
|
45
|
+
await writeFile(path.join(folder,'result.json'),JSON.stringify({...result,receipt_sha256}));
|
|
46
|
+
return folder;
|
|
47
|
+
}
|
|
48
|
+
return {parent,root,evidenceRoot,plan,opts,writeShard,cleanup:()=>rm(parent,{recursive:true,force:true})};
|
|
49
|
+
}
|
|
50
|
+
test('R67 real reconciliation consumes 32 durable v2 receipts and reports PASS from fixture source bytes',async()=>{
|
|
51
|
+
const x=await fixture();
|
|
52
|
+
try{
|
|
53
|
+
for(let i=0;i<32;i++)await x.writeShard(i);
|
|
54
|
+
const r=await reconcileR67Evidence({root:x.root,evidenceRoot:x.evidenceRoot,operationId:op,sourceHashOptions:x.opts});
|
|
55
|
+
assert.equal(r.status,'PASS',JSON.stringify(r));
|
|
56
|
+
assert.equal(r.verified_receipts,32);
|
|
57
|
+
assert.equal(r.verdict.test_count,32);
|
|
58
|
+
assert.deepEqual(r.integrity_faults,[]);
|
|
59
|
+
}finally{await x.cleanup();}
|
|
60
|
+
});
|
|
61
|
+
test('R67 verified legacy v1 receipts cannot count toward a new full suite PASS',async()=>{
|
|
62
|
+
const x=await fixture();
|
|
63
|
+
try{
|
|
64
|
+
for(let i=0;i<32;i++)await x.writeShard(i,{kind:i===5?'v1':'valid'});
|
|
65
|
+
const r=await reconcileR67Evidence({root:x.root,evidenceRoot:x.evidenceRoot,operationId:op,sourceHashOptions:x.opts});
|
|
66
|
+
assert.equal(r.status,'STALE_SOURCE');
|
|
67
|
+
assert.deepEqual(r.stale_indices,[5]);
|
|
68
|
+
assert.equal(r.verified_receipts,31);
|
|
69
|
+
}finally{await x.cleanup();}
|
|
70
|
+
});
|
|
71
|
+
test('R67 rejects changed receipts before considering source staleness',async()=>{
|
|
72
|
+
const x=await fixture();
|
|
73
|
+
try{
|
|
74
|
+
for(let i=0;i<32;i++)await x.writeShard(i,{kind:i===7?'tampered':'valid'});
|
|
75
|
+
const file=path.join(x.evidenceRoot,op+'-shard-07','result.json');
|
|
76
|
+
const broken=JSON.parse(await readFile(file,'utf8'));broken.result_sha256='0'.repeat(64);
|
|
77
|
+
await writeFile(file,JSON.stringify(broken));
|
|
78
|
+
await writeFile(path.join(x.root,'src','core.mjs'),'export const r67 = false;\n');
|
|
79
|
+
const r=await reconcileR67Evidence({root:x.root,evidenceRoot:x.evidenceRoot,operationId:op,sourceHashOptions:x.opts});
|
|
80
|
+
assert.equal(r.status,'FAILED_INTEGRITY');
|
|
81
|
+
assert.ok(r.integrity_faults.some(e=>e.index===7));
|
|
82
|
+
}finally{await x.cleanup();}
|
|
83
|
+
});
|
|
84
|
+
test('R67 source mutation, missing shard and unknown outcome never produce PASS',async()=>{
|
|
85
|
+
const x=await fixture();
|
|
86
|
+
try{
|
|
87
|
+
for(let i=0;i<31;i++)await x.writeShard(i);
|
|
88
|
+
let r=await reconcileR67Evidence({root:x.root,evidenceRoot:x.evidenceRoot,operationId:op,sourceHashOptions:x.opts});
|
|
89
|
+
assert.equal(r.status,'BLOCKED');
|
|
90
|
+
await x.writeShard(31,{kind:'unknown'});
|
|
91
|
+
r=await reconcileR67Evidence({root:x.root,evidenceRoot:x.evidenceRoot,operationId:op,sourceHashOptions:x.opts});
|
|
92
|
+
assert.equal(r.status,'OUTCOME_UNKNOWN');
|
|
93
|
+
await writeFile(path.join(x.root,'src','core.mjs'),'export const r67 = false;\n');
|
|
94
|
+
r=await reconcileR67Evidence({root:x.root,evidenceRoot:x.evidenceRoot,operationId:op,sourceHashOptions:x.opts});
|
|
95
|
+
assert.equal(r.status,'STALE_SOURCE');
|
|
96
|
+
}finally{await x.cleanup();}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('R67 changed test plan makes authentic historical v1 receipts STALE, not integrity failures',async()=>{
|
|
100
|
+
const x=await fixture();
|
|
101
|
+
try{
|
|
102
|
+
for(let i=0;i<32;i++)await x.writeShard(i,{kind:'v1'});
|
|
103
|
+
await writeFile(path.join(x.root,'test','fixture-added.test.mjs'),'import test from "node:test"; test("new",()=>{});\\n');
|
|
104
|
+
const r=await reconcileR67Evidence({root:x.root,evidenceRoot:x.evidenceRoot,operationId:op,sourceHashOptions:x.opts});
|
|
105
|
+
assert.equal(r.status,'STALE_SOURCE',JSON.stringify(r.integrity_faults));
|
|
106
|
+
assert.deepEqual(r.integrity_faults,[]);
|
|
107
|
+
assert.equal(r.stale_indices.length,32);
|
|
108
|
+
}finally{await x.cleanup();}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('R67 v2 PASS cannot coexist with timed_out=true even if receipt SHA is recomputed',async()=>{
|
|
112
|
+
const x=await fixture();
|
|
113
|
+
try{
|
|
114
|
+
for(let i=0;i<32;i++)await x.writeShard(i);
|
|
115
|
+
const file=path.join(x.evidenceRoot,op+'-shard-08','result.json');
|
|
116
|
+
const result=JSON.parse(await readFile(file,'utf8'));result.timed_out=true;
|
|
117
|
+
const {receipt_sha256,...body}=result;result.receipt_sha256=sha(Buffer.from(JSON.stringify(body)));
|
|
118
|
+
await writeFile(file,JSON.stringify(result));
|
|
119
|
+
const r=await reconcileR67Evidence({root:x.root,evidenceRoot:x.evidenceRoot,operationId:op,sourceHashOptions:x.opts});
|
|
120
|
+
assert.equal(r.status,'FAILED_INTEGRITY');
|
|
121
|
+
assert.ok(r.integrity_faults.some(v=>v.index===8));
|
|
122
|
+
}finally{await x.cleanup();}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('R67 v2 skips cannot silently produce a full regression PASS without classification',async()=>{
|
|
126
|
+
const x=await fixture();
|
|
127
|
+
try{
|
|
128
|
+
for(let i=0;i<32;i++)await x.writeShard(i);
|
|
129
|
+
const file=path.join(x.evidenceRoot,op+'-shard-09','result.json');
|
|
130
|
+
const result=JSON.parse(await readFile(file,'utf8'));result.passed=0;result.skipped=1;
|
|
131
|
+
const {receipt_sha256,...body}=result;result.receipt_sha256=sha(Buffer.from(JSON.stringify(body)));
|
|
132
|
+
await writeFile(file,JSON.stringify(result));
|
|
133
|
+
const r=await reconcileR67Evidence({root:x.root,evidenceRoot:x.evidenceRoot,operationId:op,sourceHashOptions:x.opts});
|
|
134
|
+
assert.equal(r.status,'BLOCKED_NONPASS_CLASSIFICATION');
|
|
135
|
+
assert.deepEqual(r.unclassified_nonpass,[{index:9,skipped:1,todo:0,cancelled:0}]);
|
|
136
|
+
}finally{await x.cleanup();}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('R67 case-level SKIP evidence stays visible and forged case counts fail integrity',async()=>{
|
|
140
|
+
const x=await fixture();
|
|
141
|
+
try{
|
|
142
|
+
for(let i=0;i<32;i++)await x.writeShard(i);
|
|
143
|
+
const file=path.join(x.evidenceRoot,op+'-shard-09','result.json');
|
|
144
|
+
const report=JSON.parse(await readFile(file,'utf8'));
|
|
145
|
+
report.passed=0;report.skipped=1;
|
|
146
|
+
report.nonpass_cases=[{kind:'SKIP',name:'offline fixture',reason:'unsupported platform'}];
|
|
147
|
+
report.nonpass_fully_classified=true;
|
|
148
|
+
const seal=async()=>{
|
|
149
|
+
const {receipt_sha256,...body}=report;report.receipt_sha256=sha(Buffer.from(JSON.stringify(body)));
|
|
150
|
+
await writeFile(file,JSON.stringify(report));
|
|
151
|
+
};
|
|
152
|
+
await seal();
|
|
153
|
+
const observed=await reconcileR67Evidence({root:x.root,evidenceRoot:x.evidenceRoot,operationId:op,sourceHashOptions:x.opts});
|
|
154
|
+
assert.equal(observed.status,'PASS','Fully reasoned skips must be honestly recorded but should not fabricate test failures');
|
|
155
|
+
assert.deepEqual(observed.classified_nonpass[0].cases,report.nonpass_cases);
|
|
156
|
+
assert.equal(observed.verdict.passed,31);
|
|
157
|
+
assert.equal(observed.verdict.test_count,32);
|
|
158
|
+
report.nonpass_cases.push({kind:'SKIP',name:'forged',reason:'unsupported platform'});
|
|
159
|
+
await seal();
|
|
160
|
+
const broken=await reconcileR67Evidence({root:x.root,evidenceRoot:x.evidenceRoot,operationId:op,sourceHashOptions:x.opts});
|
|
161
|
+
assert.equal(broken.status,'FAILED_INTEGRITY');
|
|
162
|
+
}finally{await x.cleanup();}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
test('R67 v2 FAILED result must bind a complete named failure case ledger, not an empty passing-shaped ledger',async()=>{
|
|
167
|
+
const x=await fixture();
|
|
168
|
+
try{
|
|
169
|
+
for(let i=0;i<32;i++)await x.writeShard(i);
|
|
170
|
+
const file=path.join(x.evidenceRoot,op+'-shard-07','result.json');
|
|
171
|
+
const result=JSON.parse(await readFile(file,'utf8'));
|
|
172
|
+
const name='remote ownership race';
|
|
173
|
+
result.status='FAILED';result.exit_code=1;result.failed=1;result.passed=0;
|
|
174
|
+
result.failure_cases=[{name,name_sha256:sha(Buffer.from(name,'utf8'))}];
|
|
175
|
+
result.failure_cases_complete=true;
|
|
176
|
+
const seal=async()=>{const {receipt_sha256,...body}=result;result.receipt_sha256=sha(Buffer.from(JSON.stringify(body)));await writeFile(file,JSON.stringify(result));};
|
|
177
|
+
await seal();
|
|
178
|
+
let observed=await reconcileR67Evidence({root:x.root,evidenceRoot:x.evidenceRoot,operationId:op,sourceHashOptions:x.opts});
|
|
179
|
+
assert.equal(observed.status,'FAILED');
|
|
180
|
+
assert.deepEqual(observed.integrity_faults,[]);
|
|
181
|
+
result.failure_cases=[];result.failure_cases_complete=true;await seal();
|
|
182
|
+
observed=await reconcileR67Evidence({root:x.root,evidenceRoot:x.evidenceRoot,operationId:op,sourceHashOptions:x.opts});
|
|
183
|
+
assert.equal(observed.status,'FAILED_INTEGRITY');
|
|
184
|
+
assert.ok(observed.integrity_faults.some(v=>v.index===7));
|
|
185
|
+
}finally{await x.cleanup();}
|
|
186
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
test('R67 isolated immutable slot activates, rolls back exact R66, and restores the same R67 bytes with no authority',async()=>{
|
|
26
|
+
const base=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r67-slot-parity-'));
|
|
27
|
+
const state=path.join(base,'state');
|
|
28
|
+
try{
|
|
29
|
+
const parentManifest=await readFile(path.join(parent,'MANIFEST.SHA256'));
|
|
30
|
+
const candidateManifest=await readFile(path.join(candidate,'MANIFEST.SHA256'));
|
|
31
|
+
assert.equal(sha(parentManifest),parentManifestSha);
|
|
32
|
+
const candidateManifestSha=sha(candidateManifest);
|
|
33
|
+
const parentSlot=await materializeReleaseSlot({source:parent,stateRoot:state,version:'0.15.43',manifestSha256:parentManifestSha});
|
|
34
|
+
const candidateSlot=await materializeReleaseSlot({source:candidate,stateRoot:state,version:'0.15.44',manifestSha256:candidateManifestSha});
|
|
35
|
+
const parentGeneration=await generation(state,'r66-parent');
|
|
36
|
+
const candidateGeneration=await generation(state,'r67-candidate');
|
|
37
|
+
const launcherSha=sha(Buffer.from(renderStableLauncherPs1(),'utf8'));
|
|
38
|
+
const pointerPath=path.join(state,'active-release.json');
|
|
39
|
+
const pointer=(slot,version,g)=>({schema:'deadbyte.active-release.v1',release_id:slot.release_id,version,
|
|
40
|
+
manifest_sha256:slot.manifest_sha256,release_root:slot.release_root,generation_root:g.root,
|
|
41
|
+
generation_manifest_sha256:g.manifest_sha256,launcher_sha256:launcherSha,config_path:g.config_path});
|
|
42
|
+
|
|
43
|
+
const activated=await atomicWriteActivePointer(pointerPath,pointer(candidateSlot,'0.15.44',candidateGeneration));
|
|
44
|
+
assert.equal(activated.release_id,candidateSlot.release_id);
|
|
45
|
+
await verifyManifestTreeExact(candidateSlot.release_root,parseManifest(candidateManifest.toString('utf8')));
|
|
46
|
+
|
|
47
|
+
const rolledBack=await atomicWriteActivePointer(pointerPath,pointer(parentSlot,'0.15.43',parentGeneration));
|
|
48
|
+
assert.equal(rolledBack.release_id,parentSlot.release_id);
|
|
49
|
+
assert.equal(rolledBack.manifest_sha256,parentManifestSha);
|
|
50
|
+
await verifyManifestTreeExact(parentSlot.release_root,parseManifest(parentManifest.toString('utf8')));
|
|
51
|
+
|
|
52
|
+
const restored=await atomicWriteActivePointer(pointerPath,pointer(candidateSlot,'0.15.44',candidateGeneration));
|
|
53
|
+
assert.deepEqual(restored,activated);
|
|
54
|
+
assert.deepEqual(parseActivePointer(await readFile(pointerPath,'utf8')),activated);
|
|
55
|
+
await verifyManifestTreeExact(candidateSlot.release_root,parseManifest(candidateManifest.toString('utf8')));
|
|
56
|
+
for(const name of ['process-grant.json','coding-grant.json','autonomous-grant.json','desktop-grant.json','host-grant.json']){
|
|
57
|
+
await assert.rejects(()=>lstat(path.join(state,'runtime',name)),{code:'ENOENT'});
|
|
58
|
+
}
|
|
59
|
+
}finally{
|
|
60
|
+
await rm(base,{recursive:true,force:true});
|
|
61
|
+
}
|
|
62
|
+
});
|
|
@@ -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
|
+
});
|