deadbyte-mcp 0.15.46 → 0.15.48
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 +97 -45
- package/PROVENANCE.json +22 -11
- package/README.txt +18 -18
- package/RELEASE.json +378 -369
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +2 -2
- package/bin/deadbyte-epoch-lock-r85.node +0 -0
- package/bin/deadbyte-kernel-lock-r74.exe +0 -0
- package/bridge/package.json +1 -1
- package/controller/deadbyte-process-policy.json +7 -7
- package/docs/R73-CAPABILITY-GAP-MATRIX.json +17 -0
- package/docs/R73-EXECUTION-CONTRACT.md +11 -0
- package/docs/R73-IMPLEMENTATION-CHANGELOG.md +12 -0
- package/docs/R73-REMAINING-DEFECTS.md +9 -0
- package/docs/R74-KERNEL-LOCK-BINARY.json +11 -0
- package/docs/R75-RECOVERY-HANDOFF.md +16 -0
- package/docs/R85-EPOCH-LOCK-BINARY.json +13 -0
- package/native/deadbyte-epoch-lock-r85.cpp +241 -0
- package/native/deadbyte-kernel-lock-r74.cpp +65 -0
- package/package.json +1 -1
- package/scripts/build-epoch-lock-r85.ps1 +34 -0
- package/scripts/build-kernel-lock-r74.ps1 +28 -0
- package/scripts/deploy-r48.mjs +3 -1
- package/scripts/gate-windows-r72.ps1 +2 -2
- package/scripts/npm-prepublish-contract-r74.mjs +113 -0
- package/scripts/npm-release-r48.mjs +24 -4
- package/scripts/npm-release-readiness-r108.mjs +134 -0
- package/scripts/npm-release-readiness-r74.mjs +46 -0
- package/scripts/r73-atomic-benchmark.mjs +39 -0
- package/scripts/r75-npm-bin-integrity-gate.mjs +33 -0
- package/scripts/release-manifest.mjs +1 -1
- package/scripts/verify-r70-full-regression.ps1 +12 -2
- package/src/activation-authority-proof-r75.mjs +67 -0
- package/src/activation-continuity-engine-r65.mjs +17 -4
- package/src/activation-preflight-r65.mjs +13 -0
- package/src/atomic-mutation.mjs +99 -9
- package/src/authority-continuity-r65.mjs +265 -21
- package/src/deadbyte-cli.mjs +55 -18
- package/src/epoch-lock-r85.mjs +48 -0
- package/src/foreground-marker-cas-win32.mjs +284 -0
- package/src/foreground-process-identity.mjs +3 -3
- package/src/kernel-lock-r74.mjs +125 -0
- package/src/privileged-install-trust-r89.mjs +196 -0
- package/src/remote-session-guardian.mjs +9 -7
- package/src/runtime-update.mjs +5 -22
- package/src/version.mjs +1 -1
- package/src/windows-powershell-environment.mjs +24 -0
- package/test/atomic-mutation.test.mjs +68 -2
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/commander-schema-differential-r48.test.mjs +1 -1
- package/test/helpers/r75-authority-proof.mjs +9 -0
- package/test/r100-posix-owned-lock-race.test.mjs +72 -0
- package/test/r101-acl-probe-environment.test.mjs +51 -0
- package/test/r106-trusted-powershell-spawn.test.mjs +53 -0
- package/test/r107-updater-powershell-path.test.mjs +77 -0
- package/test/r33-closeout-regression.test.mjs +2 -1
- package/test/r54-offline-predecessor-self-exclusion.test.mjs +2 -2
- package/test/r55-offline-predecessor-guardian-family.test.mjs +2 -2
- package/test/r56-rollback-launcher-restore.test.mjs +2 -2
- package/test/r57-core-startup-race.test.mjs +2 -2
- package/test/r58-concurrent-recovery.test.mjs +10 -7
- package/test/r58-stale-owner.test.mjs +5 -6
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/r65-activation-orchestration.test.mjs +2 -1
- package/test/r65-activation-preflight.test.mjs +7 -6
- package/test/r65-authority-epoch-recovery.test.mjs +3 -1
- package/test/r65-authority-restored-safe.test.mjs +2 -1
- package/test/r65-deploy-preflight.test.mjs +13 -14
- package/test/r65-epoch-native-activation-integration.test.mjs +5 -4
- package/test/r65-legacy-authority-migration.test.mjs +2 -1
- package/test/r67-npm-cross-volume.test.mjs +9 -2
- package/test/r71-full-verifier-artifact.test.mjs +21 -15
- package/test/r71-isolated-slot-rollback-restore.test.mjs +3 -1
- package/test/r71-release-identity.test.mjs +3 -1
- package/test/r73-package-boundary.test.mjs +22 -0
- package/test/r74-kernel-lock.test.mjs +156 -0
- package/test/r74-prepublish-contract.test.mjs +89 -0
- package/test/r75-activation-authority-proof.test.mjs +46 -0
- package/test/r75-authority-evidence.test.mjs +77 -0
- package/test/r76-pinned-marker-cas.test.mjs +78 -0
- package/test/r77-epoch-grant-cas.test.mjs +51 -0
- package/test/r78-parent-junction-cas.test.mjs +62 -0
- package/test/r79-orphan-revocation-reconcile.test.mjs +88 -0
- package/test/r79-windows-fsync-regression.test.mjs +45 -0
- package/test/r80-durable-epoch-commit.test.mjs +76 -0
- package/test/r82-epoch-lock-owned-cas.test.mjs +93 -0
- package/test/r83-epoch-lock-fileid-cas.test.mjs +92 -0
- package/test/r84-epoch-lock-created-fileid.test.mjs +112 -0
- package/test/r85-epoch-lock-release-lifetime.test.mjs +25 -0
- package/test/r85-epoch-repeat-append.test.mjs +30 -0
- package/test/r86-epoch-cleanup-and-loader.test.mjs +126 -0
- package/test/r87-crashed-orphan-revoke-reconcile.test.mjs +110 -0
- package/test/r88-native-disposition-failure.test.mjs +35 -0
- package/test/r89-privileged-install-boundary.test.mjs +123 -0
- package/test/r92-acl-ace-applicability.test.mjs +45 -0
- package/test/r94-win32-propagation-enum.test.mjs +51 -0
- package/test/r99-privileged-import-closure.test.mjs +45 -0
- package/test/remote-session-cli.test.mjs +69 -9
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import childProcess from 'node:child_process';
|
|
4
|
+
import {syncBuiltinESMExports} from 'node:module';
|
|
5
|
+
import {mkdtemp,writeFile,rm} from 'node:fs/promises';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import {loadPowerShellDataFile,runControllerStartNoCapture,windowsPowerShellEnvironment} from '../src/runtime-update.mjs';
|
|
9
|
+
|
|
10
|
+
test('updater PowerShell environment rejects case-colliding Windows root keys',()=>{
|
|
11
|
+
const environment=windowsPowerShellEnvironment({
|
|
12
|
+
SystemRoot:'C:\\Windows',SYSTEMROOT:'C:\\untrusted-root',
|
|
13
|
+
WINDIR:'C:\\Windows',windir:'C:\\untrusted-root'
|
|
14
|
+
});
|
|
15
|
+
assert.deepEqual(Object.keys(environment).filter(key=>key.toLowerCase()==='systemroot'),['SystemRoot']);
|
|
16
|
+
assert.deepEqual(Object.keys(environment).filter(key=>key.toLowerCase()==='windir'),['WINDIR']);
|
|
17
|
+
assert.equal(environment.SystemRoot,'C:\\Windows');
|
|
18
|
+
assert.equal(environment.WINDIR,'C:\\Windows');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('real updater PowerShell still loads read-only config under poisoned Windows roots',
|
|
22
|
+
{skip:process.platform!=='win32',concurrency:false},async()=>{
|
|
23
|
+
const scratch=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r107-ps-'));
|
|
24
|
+
const priorRoot=process.env.SystemRoot;
|
|
25
|
+
const priorWindir=process.env.WINDIR;
|
|
26
|
+
try{
|
|
27
|
+
const config=path.join(scratch,'config.psd1');
|
|
28
|
+
await writeFile(config,"@{ Schema = 'r107.test'; Enabled = $true }\n",{flag:'wx'});
|
|
29
|
+
process.env.SystemRoot='C:\\untrusted-root';
|
|
30
|
+
process.env.WINDIR='C:\\untrusted-root';
|
|
31
|
+
assert.deepEqual(await loadPowerShellDataFile(config),{Enabled:true,Schema:'r107.test'});
|
|
32
|
+
}finally{
|
|
33
|
+
if(priorRoot===undefined)delete process.env.SystemRoot;
|
|
34
|
+
else process.env.SystemRoot=priorRoot;
|
|
35
|
+
if(priorWindir===undefined)delete process.env.WINDIR;
|
|
36
|
+
else process.env.WINDIR=priorWindir;
|
|
37
|
+
await rm(scratch,{recursive:true,force:true});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('runtime updater cannot launch caller-selected PowerShell for config and controller work',
|
|
42
|
+
{skip:process.platform!=='win32',concurrency:false},async()=>{
|
|
43
|
+
const originalSpawn=childProcess.spawnSync;
|
|
44
|
+
const originalRoot=process.env.SystemRoot;
|
|
45
|
+
const originalWindir=process.env.WINDIR;
|
|
46
|
+
let call=null;
|
|
47
|
+
try{
|
|
48
|
+
process.env.SystemRoot='C:\\untrusted-root';
|
|
49
|
+
process.env.WINDIR='C:\\untrusted-root';
|
|
50
|
+
childProcess.spawnSync=(file,args,options)=>{
|
|
51
|
+
call={file,args,options};
|
|
52
|
+
return {status:0,signal:null,error:null,stdout:'{}\n',stderr:''};
|
|
53
|
+
};
|
|
54
|
+
syncBuiltinESMExports();
|
|
55
|
+
assert.deepEqual(await loadPowerShellDataFile('C:\\scratch\\controller.psd1'),{});
|
|
56
|
+
assert.ok(call,'updater must dispatch its PowerShell subprocess');
|
|
57
|
+
assert.equal(call.file,'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe');
|
|
58
|
+
assert.equal(call.options.env.SystemRoot,'C:\\Windows');
|
|
59
|
+
assert.equal(call.options.env.WINDIR,'C:\\Windows');
|
|
60
|
+
assert.equal(call.options.shell,undefined);
|
|
61
|
+
call=null;
|
|
62
|
+
const started=runControllerStartNoCapture({kind:'legacy',
|
|
63
|
+
controller_path:'C:\\scratch\\deadbyte-controller.ps1',cwd:'C:\\scratch'},1000);
|
|
64
|
+
assert.equal(started.status,0);
|
|
65
|
+
assert.equal(call.file,'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe');
|
|
66
|
+
assert.ok(call.args.includes('start'));
|
|
67
|
+
assert.equal(call.options.env.SystemRoot,'C:\\Windows');
|
|
68
|
+
assert.equal(call.options.env.WINDIR,'C:\\Windows');
|
|
69
|
+
}finally{
|
|
70
|
+
childProcess.spawnSync=originalSpawn;
|
|
71
|
+
syncBuiltinESMExports();
|
|
72
|
+
if(originalRoot===undefined)delete process.env.SystemRoot;
|
|
73
|
+
else process.env.SystemRoot=originalRoot;
|
|
74
|
+
if(originalWindir===undefined)delete process.env.WINDIR;
|
|
75
|
+
else process.env.WINDIR=originalWindir;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
@@ -4,13 +4,14 @@ import { createHash } from 'node:crypto';
|
|
|
4
4
|
import { mkdtemp, mkdir, readFile, rm, writeFile, readdir } from 'node:fs/promises';
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
+
import {fileURLToPath} from 'node:url';
|
|
7
8
|
import { spawn } from 'node:child_process';
|
|
8
9
|
import * as parity from '../scripts/release-parity-r39.mjs';
|
|
9
10
|
import { createToolAudit } from '../src/tool-audit.mjs';
|
|
10
11
|
import { verifyAuditChain } from '../src/audit-verify.mjs';
|
|
11
12
|
import { generateTrustKeypair } from '../src/trust.mjs';
|
|
12
13
|
|
|
13
|
-
const root = path.resolve(new URL('../', import.meta.url)
|
|
14
|
+
const root = path.resolve(fileURLToPath(new URL('../', import.meta.url)));
|
|
14
15
|
const sha256 = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
15
16
|
const temp = prefix => mkdtemp(path.join(os.tmpdir(), prefix));
|
|
16
17
|
const run = (command,args,options={}) => new Promise((resolve,reject)=>{
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { fileURLToPath,pathToFileURL } from 'node:url';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
|
|
6
6
|
const target=process.env.R54_RUNTIME_UPDATE_PATH
|
|
7
7
|
? path.resolve(process.env.R54_RUNTIME_UPDATE_PATH)
|
|
8
|
-
:
|
|
8
|
+
: fileURLToPath(new URL('../src/runtime-update.mjs',import.meta.url));
|
|
9
9
|
const runtimeUpdate=await import(pathToFileURL(target).href+'?r54='+Date.now());
|
|
10
10
|
|
|
11
11
|
test('R54 exposes behavior-testable offline recovery blocker selection',()=>{
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { fileURLToPath,pathToFileURL } from 'node:url';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
|
|
6
6
|
const target=process.env.R55_RUNTIME_UPDATE_PATH
|
|
7
7
|
? path.resolve(process.env.R55_RUNTIME_UPDATE_PATH)
|
|
8
|
-
:
|
|
8
|
+
: fileURLToPath(new URL('../src/runtime-update.mjs',import.meta.url));
|
|
9
9
|
const runtimeUpdate=await import(pathToFileURL(target).href+'?r55='+Date.now());
|
|
10
10
|
const nonce='a'.repeat(64);
|
|
11
11
|
|
|
@@ -2,13 +2,13 @@ import test from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { readFile } from 'node:fs/promises';
|
|
5
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { fileURLToPath,pathToFileURL } from 'node:url';
|
|
6
6
|
import os from 'node:os';
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
|
|
9
9
|
const target=process.env.R56_RUNTIME_UPDATE_PATH
|
|
10
10
|
? path.resolve(process.env.R56_RUNTIME_UPDATE_PATH)
|
|
11
|
-
:
|
|
11
|
+
: fileURLToPath(new URL('../src/runtime-update.mjs',import.meta.url));
|
|
12
12
|
const runtimeUpdate=await import(pathToFileURL(target).href+'?r56='+Date.now());
|
|
13
13
|
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
14
14
|
const stateRoot=path.resolve(process.env.R56_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { readFile } from 'node:fs/promises';
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { fileURLToPath,pathToFileURL } from 'node:url';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
|
|
7
7
|
const target=process.env.R57_RUNTIME_UPDATE_PATH
|
|
8
8
|
? path.resolve(process.env.R57_RUNTIME_UPDATE_PATH)
|
|
9
|
-
:
|
|
9
|
+
: fileURLToPath(new URL('../src/runtime-update.mjs',import.meta.url));
|
|
10
10
|
const runtimeUpdate=await import(pathToFileURL(target).href+'?r57='+Date.now());
|
|
11
11
|
const coreRoot='C:\\slot\\core';
|
|
12
12
|
const bridge={pid:20396,component:'bridge',script_path:'C:\\slot\\bridge\\src\\bridge.mjs',script_sha256:'a'.repeat(64)};
|
|
@@ -6,7 +6,7 @@ import {fileURLToPath} from 'node:url';
|
|
|
6
6
|
import {spawn} from 'node:child_process';
|
|
7
7
|
import {setTimeout as delay} from 'node:timers/promises';
|
|
8
8
|
|
|
9
|
-
test('
|
|
9
|
+
test('R75 simultaneous owner-unverified recovery attempts both retain authority and marker', {timeout:30000},async()=>{
|
|
10
10
|
const base='D:/Projects/deadbyte-mcp/testing/0.15.35/fixtures';await mkdir(base,{recursive:true});
|
|
11
11
|
const root=await mkdtemp(path.join(base,'concurrent-recovery-'));await mkdir(path.join(root,'bin'));await mkdir(path.join(root,'runtime'));
|
|
12
12
|
const trace=path.join(root,'trace.txt'),markerPath=path.join(root,'runtime','remote-session.json');
|
|
@@ -47,14 +47,17 @@ exit 0
|
|
|
47
47
|
const c=spawn(process.execPath,[fileURLToPath(new URL('../src/deadbyte-cli.mjs',import.meta.url)),'remote'],{env:{...process.env,DEADBYTE_STATE_ROOT:root},stdio:['pipe','pipe','pipe'],windowsHide:true});
|
|
48
48
|
const o={stdout:'',stderr:''};c.stdout.on('data',b=>{o.stdout+=b;});c.stderr.on('data',b=>{o.stderr+=b;});o.exit=new Promise(resolve=>c.once('exit',resolve));children.push(c);outputs.push(o);
|
|
49
49
|
}
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
await
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
const exits=await Promise.race([Promise.all(outputs.map(o=>o.exit)),delay(15000,null,{ref:false}).then(()=>null)]);
|
|
51
|
+
assert.ok(exits,'both owner-unverified attempts must terminate');
|
|
52
|
+
const commands=await readFile(trace,'utf8').catch(error=>error.code==='ENOENT'?'':Promise.reject(error));
|
|
53
|
+
await writeFile(path.join(root,'observation.json'),JSON.stringify({commands,exits,outputs:outputs.map(o=>({stdout:o.stdout,stderr:o.stderr})),simulation:'concurrent owner-unverified stale marker'}),{flag:'wx'});
|
|
54
|
+
assert.ok(exits.every(code=>code!==0));
|
|
55
|
+
for(const output of outputs)assert.match(output.stderr,/ownership unverified.*marker retained|already claimed \(startup\/recovery lock\)/i);
|
|
56
|
+
assert.equal(commands,'','neither attempt may touch authority');
|
|
57
|
+
assert.equal(JSON.parse(await readFile(markerPath,'utf8')).pid,2147483000);
|
|
55
58
|
}finally{
|
|
56
59
|
for(const c of children)if(c.exitCode===null)c.stdin.end();
|
|
57
|
-
await Promise.race([Promise.all(outputs.map(o=>o.exit)),delay(30000)]);
|
|
60
|
+
await Promise.race([Promise.all(outputs.map(o=>o.exit)),delay(30000,null,{ref:false})]);
|
|
58
61
|
for(const c of children)if(c.exitCode===null&&c.signalCode===null)c.kill();
|
|
59
62
|
}
|
|
60
63
|
});
|
|
@@ -46,7 +46,7 @@ test('R58 live unrelated PID is not accepted as a remote foreground owner', {tim
|
|
|
46
46
|
assert.notEqual(code,0);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
test('
|
|
49
|
+
test('R75 bound stale birth identity retains marker without touching a reused PID or authority', {timeout:60000}, async()=>{
|
|
50
50
|
const base='D:/Projects/deadbyte-mcp/testing/0.15.35/fixtures';
|
|
51
51
|
await mkdir(base,{recursive:true});const root=await mkdtemp(path.join(base,'stale-birth-'));
|
|
52
52
|
await mkdir(path.join(root,'runtime'));await mkdir(path.join(root,'bin'));
|
|
@@ -61,11 +61,10 @@ test('R58 bound stale birth identity reconciles on the first invocation without
|
|
|
61
61
|
let stdout='',stderr='';child.stdout.on('data',b=>{stdout+=b;});child.stderr.on('data',b=>{stderr+=b;});
|
|
62
62
|
const code=await new Promise(resolve=>child.once('close',resolve));
|
|
63
63
|
await writeFile(path.join(root,'observation.json'),JSON.stringify({code,stdout,stderr,simulation:'birth identity drift at a live unrelated numeric PID'}),{flag:'wx'});
|
|
64
|
-
assert.
|
|
65
|
-
assert.
|
|
66
|
-
assert.
|
|
67
|
-
assert.
|
|
68
|
-
await assert.rejects(readFile(markerPath),{code:'ENOENT'});
|
|
64
|
+
assert.notEqual(code,0);
|
|
65
|
+
assert.match(stderr,/owner identity mismatch; marker retained/i);
|
|
66
|
+
assert.equal(await readFile(trace,'utf8').catch(error=>error.code==='ENOENT'?'':Promise.reject(error)),'','identity drift must cause no authority commands');
|
|
67
|
+
assert.deepEqual(JSON.parse(await readFile(markerPath,'utf8')),marker);
|
|
69
68
|
assert.equal(foregroundAlive(),true,'unrelated live process must not be killed');
|
|
70
69
|
function foregroundAlive(){try{process.kill(process.pid,0);return true;}catch{return false;}}
|
|
71
70
|
});
|
|
@@ -83,7 +83,7 @@ test('R72 compact baseline permits only R64 read offset and authorized coding-on
|
|
|
83
83
|
assert.deepEqual(diff.added,[]);
|
|
84
84
|
assert.deepEqual(diff.removed,[]);
|
|
85
85
|
const packageVersion=JSON.parse(await readFile(path.join(repoRoot,'package.json'),'utf8')).version;
|
|
86
|
-
if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46'].includes(packageVersion)){
|
|
86
|
+
if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48'].includes(packageVersion)){
|
|
87
87
|
assert.deepEqual([...diff.schema_changed].sort(),['machine_fs','native_diff_preview']);
|
|
88
88
|
assert.equal(diff.unchanged.length,41);
|
|
89
89
|
const prior=baseline.tools.find(tool=>tool.name==='machine_fs');
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import {orchestrateActivation} from '../src/activation-preflight-r65.mjs';
|
|
4
|
+
import {withAuthorityProof} from './helpers/r75-authority-proof.mjs';
|
|
4
5
|
|
|
5
6
|
const parent={release_id:'v0.15.41-63a78ad159ad3003',version:'0.15.41',manifest_sha256:'63a78ad159ad3003db3fefafcd468bef22599c829a867f4f10a46c7a20748b3e',generation_manifest_sha256:'19ae2efcdcde9f80c474f0e55851d4ada51c22813d3e05af03b11acb28c3fca5'};
|
|
6
7
|
const candidate={version:'0.15.42',manifest_sha256:'a'.repeat(64)};
|
|
@@ -19,7 +20,7 @@ function fixture(initial={}){
|
|
|
19
20
|
let current={state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},authority:authorityEntries(initialVector,initial.epochNative===true),policy_sha256:policies,
|
|
20
21
|
active_release_id:parent.release_id,active_manifest_sha256:parent.manifest_sha256,active_generation_manifest_sha256:parent.generation_manifest_sha256,...initial};
|
|
21
22
|
if(initial.authority)current={...current,authority:authorityEntries(initial.authority,initial.epochNative===true)};
|
|
22
|
-
return {calls,observe:async()=>current,command:async name=>{
|
|
23
|
+
return {calls,observe:async()=>withAuthorityProof(current),command:async name=>{
|
|
23
24
|
calls.push(name);
|
|
24
25
|
if(name.startsWith('disarm')){
|
|
25
26
|
const plane=name.slice(6);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import {classifyActivationPreflight,authorityTransitionPlan} from '../src/activation-preflight-r65.mjs';
|
|
4
|
+
import {withAuthorityProof} from './helpers/r75-authority-proof.mjs';
|
|
4
5
|
|
|
5
6
|
const parent={release_id:'v0.15.41-63a78ad159ad3003',version:'0.15.41',manifest_sha256:'63a78ad159ad3003db3fefafcd468bef22599c829a867f4f10a46c7a20748b3e',generation_manifest_sha256:'19ae2efcdcde9f80c474f0e55851d4ada51c22813d3e05af03b11acb28c3fca5'};
|
|
6
7
|
const candidate={version:'0.15.42',manifest_sha256:'a'.repeat(64)};
|
|
@@ -8,7 +9,7 @@ const authority={process:true,coding:true,autonomous:true,desktop:false,host:fal
|
|
|
8
9
|
const policies={process:'1'.repeat(64),coding:'2'.repeat(64),autonomous:'3'.repeat(64),desktop:'4'.repeat(64),host:'5'.repeat(64)};
|
|
9
10
|
const online={state:'ONLINE',local_ready:true,bridge:{alive:true,reason:'verified',pid:10},core:{alive:true,reason:'verified',pid:11},cloud:{alive:true,reason:'verified',pid:12},supervisor:{running:true},authority,policy_sha256:policies,
|
|
10
11
|
active_release_id:parent.release_id,active_manifest_sha256:parent.manifest_sha256,active_generation_manifest_sha256:parent.generation_manifest_sha256};
|
|
11
|
-
const input={pointer:parent,expectedParent:parent,candidate,expectedCandidate:candidate,observation:online};
|
|
12
|
+
const input={pointer:parent,expectedParent:parent,candidate,expectedCandidate:candidate,observation:withAuthorityProof(online)};
|
|
12
13
|
|
|
13
14
|
test('preflight accepts exact online predecessor with full observed identity and authority vector',()=>{
|
|
14
15
|
const result=classifyActivationPreflight(input);
|
|
@@ -19,7 +20,7 @@ test('preflight accepts exact online predecessor with full observed identity and
|
|
|
19
20
|
|
|
20
21
|
test('host armed is not safe; plan only disarms Host and restores exact captured vector',()=>{
|
|
21
22
|
const armed={...authority,host:true};
|
|
22
|
-
const result=classifyActivationPreflight({...input,observation:{...online,authority:armed}});
|
|
23
|
+
const result=classifyActivationPreflight({...input,observation:withAuthorityProof({...online,authority:armed})});
|
|
23
24
|
assert.equal(result.state,'ONLINE_HOST_ARMED');assert.equal(result.safe_to_activate,false);
|
|
24
25
|
const plan=authorityTransitionPlan(result.authority);
|
|
25
26
|
assert.deepEqual(plan.before,['disarmhost']);
|
|
@@ -27,9 +28,9 @@ test('host armed is not safe; plan only disarms Host and restores exact captured
|
|
|
27
28
|
});
|
|
28
29
|
|
|
29
30
|
test('parked dirty, generation drift and identity loss fail closed without an activation command',()=>{
|
|
30
|
-
assert.equal(classifyActivationPreflight({...input,observation:{...online,state:'PARKED'}}).state,'PARKED_DIRTY');
|
|
31
|
+
assert.equal(classifyActivationPreflight({...input,observation:withAuthorityProof({...online,state:'PARKED'})}).state,'PARKED_DIRTY');
|
|
31
32
|
assert.equal(classifyActivationPreflight({...input,pointer:{...parent,generation_manifest_sha256:'b'.repeat(64)}}).state,'POINTER_DRIFT');
|
|
32
|
-
assert.equal(classifyActivationPreflight({...input,observation:{...online,active_release_id:'wrong'}}).state,'POINTER_DRIFT');
|
|
33
|
-
assert.equal(classifyActivationPreflight({...input,observation:{...online,policy_sha256:{...policies,host:null}}}).state,'UNKNOWN');
|
|
34
|
-
assert.equal(classifyActivationPreflight({...input,observation:{...online,cloud:{alive:false,reason:'missing'}}}).state,'RUNTIME_DRIFT');
|
|
33
|
+
assert.equal(classifyActivationPreflight({...input,observation:withAuthorityProof({...online,active_release_id:'wrong'})}).state,'POINTER_DRIFT');
|
|
34
|
+
assert.equal(classifyActivationPreflight({...input,observation:withAuthorityProof({...online,policy_sha256:{...policies,host:null}})}).state,'UNKNOWN');
|
|
35
|
+
assert.equal(classifyActivationPreflight({...input,observation:withAuthorityProof({...online,cloud:{alive:false,reason:'missing'}})}).state,'RUNTIME_DRIFT');
|
|
35
36
|
});
|
|
@@ -19,7 +19,9 @@ test('dead-owner epoch lock is recovered but a live-owner lock is never stolen',
|
|
|
19
19
|
const runtime=await prepareAuthorityTestTrust(root);
|
|
20
20
|
const dir=path.join(runtime,'authority-epochs','coding');
|
|
21
21
|
await mkdir(dir,{recursive:true});
|
|
22
|
-
await writeFile(path.join(dir,'.lock'),JSON.stringify({schema:'deadbyte.authority-epoch-lock.r65.v1',pid:2147483647,token:'dead',created_at_utc:'2026-10-06T00:00:00Z'
|
|
22
|
+
await writeFile(path.join(dir,'.lock'),JSON.stringify({schema:'deadbyte.authority-epoch-lock.r65.v1',pid:2147483647,token:'dead',created_at_utc:'2026-10-06T00:00:00Z',
|
|
23
|
+
owner_identity:{schema:'deadbyte.foreground-process.v1',pid:2147483647,parent_pid:0,creation_utc:'2026-01-01T00:00:00Z',
|
|
24
|
+
executable_path:process.execPath,command_line_sha256:'0'.repeat(64)}}));
|
|
23
25
|
const store=createAuthorityEpochStore({runtimeDir:runtime});
|
|
24
26
|
const nonce='1'.repeat(64),grant=grantBytes(nonce);
|
|
25
27
|
const head=await store.append({plane:'coding',action:'grant',grantBytes:grant,grantInstanceNonce:nonce,updateId:null});
|
|
@@ -7,6 +7,7 @@ import path from 'node:path';
|
|
|
7
7
|
import {orchestrateActivation} from '../src/activation-preflight-r65.mjs';
|
|
8
8
|
import {createAuthorityEpochStore} from '../src/authority-continuity-r65.mjs';
|
|
9
9
|
import {createEpochNativeContinuityEngine} from '../src/activation-continuity-engine-r65.mjs';
|
|
10
|
+
import {withAuthorityProof} from './helpers/r75-authority-proof.mjs';
|
|
10
11
|
|
|
11
12
|
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
12
13
|
const parent={release_id:'v0.15.41-parent',version:'0.15.41',manifest_sha256:'a'.repeat(64),generation_manifest_sha256:'b'.repeat(64)};
|
|
@@ -26,7 +27,7 @@ test('orchestrator reports RESTORED_SAFE when all restored authority is safe but
|
|
|
26
27
|
return {authority_outcomes:{process:'RESTORED_SAFE',coding:'NOT_APPLICABLE',autonomous:'NOT_APPLICABLE',desktop:'NOT_APPLICABLE',host:'NOT_APPLICABLE'},restored_nonces:{process:'8'.repeat(64)},reauth_planes:[]};}
|
|
27
28
|
};
|
|
28
29
|
const result=await orchestrateActivation({pointer:parent,expectedParent:parent,candidate,expectedCandidate:candidate,
|
|
29
|
-
observe:async()=>current,command:async()=>{throw new Error('generic command forbidden');},
|
|
30
|
+
observe:async()=>withAuthorityProof(current),command:async()=>{throw new Error('generic command forbidden');},
|
|
30
31
|
activate:async()=>({verified:true,release_id:'v0.15.42-candidate'}),continuityEngine});
|
|
31
32
|
assert.equal(result.state,'ACTIVATED');
|
|
32
33
|
assert.equal(result.authority_continuity,'RESTORED_SAFE');
|
|
@@ -11,8 +11,9 @@ const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
|
11
11
|
test('deploy dry preflight observes isolated authority without opening an update transaction', {skip:process.platform!=='win32'},async()=>{
|
|
12
12
|
const isolated=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r65-deploy-preflight-'));
|
|
13
13
|
try{
|
|
14
|
-
const state=path.join(isolated,'state'),bin=path.join(state,'bin');
|
|
14
|
+
const state=path.join(isolated,'state'),bin=path.join(state,'bin'),runtime=path.join(state,'runtime');
|
|
15
15
|
await mkdir(bin,{recursive:true});
|
|
16
|
+
await mkdir(runtime,{recursive:true});
|
|
16
17
|
const release=JSON.parse(await readFile(path.join(root,'RELEASE.json'),'utf8'));
|
|
17
18
|
const parent=release.parent;
|
|
18
19
|
const generationRoot=path.join(state,'generations',`${parent.release_id}-fixture`);
|
|
@@ -20,9 +21,11 @@ test('deploy dry preflight observes isolated authority without opening an update
|
|
|
20
21
|
await mkdir(generationRoot,{recursive:true});await mkdir(policyRoot,{recursive:true});
|
|
21
22
|
const fields={process:'ProcessPolicyFile',coding:'CodingPolicyFile',autonomous:'AutonomousPolicyFile',desktop:'DesktopPolicyFile',host:'HostPolicyFile'};
|
|
22
23
|
const policyLines=[];
|
|
24
|
+
const grantField={process:'grant_file',coding:'lease_file',autonomous:'lease_file',desktop:'grant_file',host:'arm_file'};
|
|
23
25
|
for(const [plane,key] of Object.entries(fields)){
|
|
24
26
|
const file=path.join(policyRoot,`${plane}.json`);
|
|
25
|
-
await writeFile(file,JSON.stringify({schema:`
|
|
27
|
+
await writeFile(file,JSON.stringify({schema:`deadbyte.${plane}-policy.v${plane==='host'?2:1}`,enabled:true,
|
|
28
|
+
[grantField[plane]]:path.join(runtime,`${plane}-grant.json`)}));
|
|
26
29
|
policyLines.push(`${key} = '${file.replaceAll("'","''")}'`);
|
|
27
30
|
}
|
|
28
31
|
await writeFile(path.join(generationRoot,'controller-config.psd1'),`@{ ${policyLines.join('; ')} }\n`);
|
|
@@ -30,26 +33,22 @@ test('deploy dry preflight observes isolated authority without opening an update
|
|
|
30
33
|
release_root:path.join(state,'releases',parent.release_id),generation_root:generationRoot,
|
|
31
34
|
launcher_sha256:'a'.repeat(64),config_path:path.join(generationRoot,'controller-config.psd1')}));
|
|
32
35
|
const ps1=`param([string]$Command,[switch]$Json)\nif($Command -ne 'status' -or -not $Json){throw 'unexpected mutating command'}\n@{schema='deadbyte.controller-observation.v1';state='ONLINE';local_ready=$true;bridge=@{alive=$true;reason='verified';pid=10};core=@{alive=$true;reason='verified';pid=11};cloud=@{alive=$true;reason='verified';pid=12};supervisor=@{running=$true};authority=@{process=@{armed=$true;configured=$true;reason='fixture'};coding=@{armed=$true;configured=$true;reason='fixture'};autonomous=@{armed=$true;configured=$true;reason='fixture'};desktop=@{armed=$false;configured=$true;reason='fixture'};host=@{armed=$false;configured=$true;reason='fixture'}}} | ConvertTo-Json -Depth 6 -Compress\n`;
|
|
33
|
-
|
|
36
|
+
const cleanPs1=ps1.replaceAll('armed=$true','armed=$false').replaceAll("reason='fixture'","reason='disarmed'");
|
|
37
|
+
await writeFile(path.join(bin,'DEADBYTE-MCP.ps1'),cleanPs1);
|
|
34
38
|
const run=spawnSync(process.execPath,[path.join(root,'scripts','deploy-r48.mjs'),'--preflight'],{
|
|
35
39
|
env:{...process.env,DEADBYTE_STATE_ROOT:state},cwd:root,encoding:'utf8',timeout:20000,windowsHide:true});
|
|
36
40
|
assert.equal(run.status,0,run.stderr);
|
|
37
41
|
const result=JSON.parse(run.stdout.trim());
|
|
38
42
|
assert.equal(result.state,'ONLINE_READY');
|
|
39
|
-
assert.equal(result.authority_continuity,'
|
|
40
|
-
assert.deepEqual(result.reauth_planes,[
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
.replace('autonomous=@{armed=$true','autonomous=@{armed=$false')
|
|
44
|
-
.replace('host=@{armed=$false','host=@{armed=$true');
|
|
45
|
-
await writeFile(path.join(bin,'DEADBYTE-MCP.ps1'),hostOnly);
|
|
43
|
+
assert.equal(result.authority_continuity,'NOT_APPLICABLE');
|
|
44
|
+
assert.deepEqual(result.reauth_planes,[]);
|
|
45
|
+
const unverified=cleanPs1.replace("host=@{armed=$false;configured=$true;reason='disarmed'", "host=@{armed=$false;configured=$true;reason='unverified'");
|
|
46
|
+
await writeFile(path.join(bin,'DEADBYTE-MCP.ps1'),unverified);
|
|
46
47
|
const hostRun=spawnSync(process.execPath,[path.join(root,'scripts','deploy-r48.mjs'),'--preflight'],{
|
|
47
48
|
env:{...process.env,DEADBYTE_STATE_ROOT:state},cwd:root,encoding:'utf8',timeout:20000,windowsHide:true});
|
|
48
49
|
assert.equal(hostRun.status,0,hostRun.stderr);
|
|
49
50
|
const hostResult=JSON.parse(hostRun.stdout.trim());
|
|
50
|
-
assert.equal(hostResult.state,'
|
|
51
|
-
assert.
|
|
52
|
-
assert.deepEqual(hostResult.reauth_planes,['host']);
|
|
53
|
-
assert.deepEqual((await readdir(state)).sort(),['active-release.json','bin','generations','policies']);
|
|
51
|
+
assert.equal(hostResult.state,'AUTHORITY_UNVERIFIED');
|
|
52
|
+
assert.deepEqual((await readdir(state)).sort(),['active-release.json','bin','generations','policies','runtime']);
|
|
54
53
|
}finally{await rm(isolated,{recursive:true,force:true});}
|
|
55
54
|
});
|
|
@@ -7,6 +7,7 @@ import path from 'node:path';
|
|
|
7
7
|
import {orchestrateActivation} from '../src/activation-preflight-r65.mjs';
|
|
8
8
|
import {createAuthorityEpochStore} from '../src/authority-continuity-r65.mjs';
|
|
9
9
|
import {createEpochNativeContinuityEngine} from '../src/activation-continuity-engine-r65.mjs';
|
|
10
|
+
import {withAuthorityProof} from './helpers/r75-authority-proof.mjs';
|
|
10
11
|
|
|
11
12
|
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
12
13
|
const parent={release_id:'v0.15.41-parent',version:'0.15.41',manifest_sha256:'a'.repeat(64),generation_manifest_sha256:'b'.repeat(64)};
|
|
@@ -44,7 +45,7 @@ test('epoch-native orchestrator handles BOM-encoded process grant with transacti
|
|
|
44
45
|
const grant=JSON.parse(new TextDecoder('utf-8',{fatal:true}).decode(bytes));
|
|
45
46
|
armed=(await store.verifyGrant({plane:'process',grantBytes:bytes,grantInstanceNonce:grant.instance_nonce,policyBytes})).trusted;
|
|
46
47
|
}
|
|
47
|
-
return {state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},authority:{
|
|
48
|
+
return withAuthorityProof({state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},authority:{
|
|
48
49
|
process:{configured:true,armed,reason:armed?'standing-grant':'disarmed',epoch_seq:armed?head.seq:null,epoch_sha256:armed?head.event_sha256:null},
|
|
49
50
|
coding:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
|
|
50
51
|
autonomous:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
|
|
@@ -53,7 +54,7 @@ test('epoch-native orchestrator handles BOM-encoded process grant with transacti
|
|
|
53
54
|
},policy_sha256:{process:sha(policyBytes),...dummy},
|
|
54
55
|
active_release_id:switched?'v0.15.42-candidate':parent.release_id,
|
|
55
56
|
active_manifest_sha256:switched?candidate.manifest_sha256:parent.manifest_sha256,
|
|
56
|
-
active_generation_manifest_sha256:switched?'9'.repeat(64):parent.generation_manifest_sha256};
|
|
57
|
+
active_generation_manifest_sha256:switched?'9'.repeat(64):parent.generation_manifest_sha256});
|
|
57
58
|
};
|
|
58
59
|
const engine=createEpochNativeContinuityEngine({stateRoot:state,
|
|
59
60
|
predecessorPolicyPaths:{process:beforePath,coding:null,autonomous:null,desktop:null,host:null},
|
|
@@ -107,7 +108,7 @@ test('epoch-native explicit user revoke after transaction revoke wins over orche
|
|
|
107
108
|
const grant=JSON.parse(new TextDecoder('utf-8',{fatal:true}).decode(bytes));
|
|
108
109
|
armed=(await store.verifyGrant({plane:'process',grantBytes:bytes,grantInstanceNonce:grant.instance_nonce,policyBytes})).trusted;
|
|
109
110
|
}
|
|
110
|
-
return {state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},authority:{
|
|
111
|
+
return withAuthorityProof({state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},authority:{
|
|
111
112
|
process:{configured:true,armed,reason:armed?'standing-grant':'disarmed',epoch_seq:armed?head.seq:null,epoch_sha256:armed?head.event_sha256:null},
|
|
112
113
|
coding:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
|
|
113
114
|
autonomous:{configured:true,armed:false,reason:'disarmed',epoch_seq:null,epoch_sha256:null},
|
|
@@ -116,7 +117,7 @@ test('epoch-native explicit user revoke after transaction revoke wins over orche
|
|
|
116
117
|
},policy_sha256:{process:sha(policyBytes),...dummy},
|
|
117
118
|
active_release_id:switched?'v0.15.42-candidate':parent.release_id,
|
|
118
119
|
active_manifest_sha256:switched?candidate.manifest_sha256:parent.manifest_sha256,
|
|
119
|
-
active_generation_manifest_sha256:switched?'9'.repeat(64):parent.generation_manifest_sha256};
|
|
120
|
+
active_generation_manifest_sha256:switched?'9'.repeat(64):parent.generation_manifest_sha256});
|
|
120
121
|
};
|
|
121
122
|
const engine=createEpochNativeContinuityEngine({stateRoot:state,
|
|
122
123
|
predecessorPolicyPaths:{process:beforePath,coding:null,autonomous:null,desktop:null,host:null},
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import {classifyAuthorityContinuity,orchestrateActivation} from '../src/activation-preflight-r65.mjs';
|
|
4
|
+
import {withAuthorityProof} from './helpers/r75-authority-proof.mjs';
|
|
4
5
|
|
|
5
6
|
const parent={release_id:'v0.15.41-parent',version:'0.15.41',manifest_sha256:'1'.repeat(64),generation_manifest_sha256:'2'.repeat(64)};
|
|
6
7
|
const candidate={version:'0.15.42',manifest_sha256:'3'.repeat(64)};
|
|
@@ -15,7 +16,7 @@ function fixture(vector){
|
|
|
15
16
|
authority:Object.fromEntries(Object.entries(vector).map(([plane,armed])=>[plane,legacyEntry(armed)]))};
|
|
16
17
|
return {
|
|
17
18
|
calls,
|
|
18
|
-
observe:async()=>observation,
|
|
19
|
+
observe:async()=>withAuthorityProof(observation),
|
|
19
20
|
command:async command=>{
|
|
20
21
|
calls.push(command);
|
|
21
22
|
if(command.startsWith('disarm')){
|
|
@@ -13,8 +13,15 @@ const artifact=Buffer.concat([Buffer.from('R67 immutable npm tarball fixture\\n'
|
|
|
13
13
|
const integrity='sha512-'+digest(artifact,'sha512');
|
|
14
14
|
|
|
15
15
|
async function fixture(fn){
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const scratch=path.resolve(process.env.DEADBYTE_TEST_CROSS_VOLUME_ROOT||path.join(root,'tmp'));
|
|
17
|
+
if(path.parse(scratch).root.toLowerCase()===path.parse(tmpdir()).root.toLowerCase())
|
|
18
|
+
throw new Error('R67 cross-volume test requires source scratch on a different volume from TEMP');
|
|
19
|
+
if(process.env.DEADBYTE_TEST_CROSS_VOLUME_ROOT&&
|
|
20
|
+
(scratch.toLowerCase()===root.toLowerCase()||
|
|
21
|
+
scratch.toLowerCase().startsWith((root+path.sep).toLowerCase())))
|
|
22
|
+
throw new Error('R67 explicit scratch must be outside the installation root');
|
|
23
|
+
await mkdir(scratch,{recursive:true});
|
|
24
|
+
const srcDir=await mkdtemp(path.join(scratch,'r67-xdev-src-'));
|
|
18
25
|
const dstDir=await mkdtemp(path.join(tmpdir(),'r67-xdev-dst-'));
|
|
19
26
|
const source=path.join(srcDir,'deadbyte-mcp-0.15.44.tgz');
|
|
20
27
|
const destination=path.join(dstDir,'deadbyte-mcp-0.15.44.tgz');
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import {createHash} from 'node:crypto';
|
|
4
|
-
import {readFile} from 'node:fs/promises';
|
|
4
|
+
import {mkdtemp,readFile,rm} from 'node:fs/promises';
|
|
5
5
|
import {spawnSync} from 'node:child_process';
|
|
6
|
+
import os from 'node:os';
|
|
6
7
|
import path from 'node:path';
|
|
7
8
|
import {fileURLToPath} from 'node:url';
|
|
8
9
|
|
|
@@ -11,18 +12,23 @@ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
|
11
12
|
|
|
12
13
|
test('R71 full verifier persists the complete stdout bytes beside its no-clobber result',async t=>{
|
|
13
14
|
if(process.platform!=='win32'){t.skip('Windows PowerShell verifier only');return;}
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
15
|
+
const scratch=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r71-verifier-'));
|
|
16
|
+
try{
|
|
17
|
+
const run=spawnSync('powershell.exe',['-NoProfile','-ExecutionPolicy','Bypass','-File',
|
|
18
|
+
path.join(root,'scripts','verify-r70-full-regression.ps1'),'-SelfTest',
|
|
19
|
+
'-EvidenceRoot',path.join(scratch,'evidence')],
|
|
20
|
+
{cwd:root,encoding:'utf8',windowsHide:true,timeout:30_000});
|
|
21
|
+
assert.equal(run.status,0,run.stderr||run.stdout);
|
|
22
|
+
const resultPath=/R70_FULL_TEST_ARTIFACT=([^\r\n]+)/.exec(run.stdout)?.[1];
|
|
23
|
+
assert.ok(resultPath,'durable result path missing');
|
|
24
|
+
assert.ok(path.resolve(resultPath).startsWith(path.resolve(scratch)+path.sep));
|
|
25
|
+
const result=JSON.parse(await readFile(resultPath,'utf8'));
|
|
26
|
+
assert.equal(result.mode,'synthetic-selftest');
|
|
27
|
+
assert.equal(result.exit_code,1);
|
|
28
|
+
assert.equal(typeof result.stdout_path,'string');
|
|
29
|
+
const output=await readFile(result.stdout_path);
|
|
30
|
+
assert.equal(sha(output),result.stdout_sha256);
|
|
31
|
+
assert.match(output.toString('utf8'),/hash mismatch: SYNTHETIC_TEST/);
|
|
32
|
+
assert.equal(output.toString('utf8').trimEnd().split('\n').length,result.total_line_count);
|
|
33
|
+
}finally{await rm(scratch,{recursive:true,force:true});}
|
|
28
34
|
});
|
|
@@ -8,6 +8,7 @@ import {fileURLToPath} from 'node:url';
|
|
|
8
8
|
import {atomicWriteActivePointer,materializeReleaseSlot,parseActivePointer,parseManifest,renderStableLauncherPs1,verifyManifestTreeExact} from '../src/release-slot.mjs';
|
|
9
9
|
|
|
10
10
|
const candidate=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
11
|
+
const currentVersion=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8')).version;
|
|
11
12
|
const parent=path.join(os.homedir(),'.deadbyte-mcp','releases','v0.15.45-34aa8d1241cfb8bf');
|
|
12
13
|
const parentManifestSha='34aa8d1241cfb8bf0b5ba303bf3c06e0e9e1bf01f64457bca659d6dd4d634fdc';
|
|
13
14
|
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
@@ -22,7 +23,8 @@ async function generation(state,label){
|
|
|
22
23
|
return {root,manifest_sha256:sha(manifest),config_path:path.join(root,'controller-config.psd1')};
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
test('R72 isolated immutable slot activates, rolls back exact active R71, and restores same R72 bytes with no authority',
|
|
26
|
+
test('R72 isolated immutable slot activates, rolls back exact active R71, and restores same R72 bytes with no authority',
|
|
27
|
+
{skip:currentVersion!=='0.15.46'?'historical R72 slot simulation; current development version differs':false},async()=>{
|
|
26
28
|
const base=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r72-slot-parity-'));
|
|
27
29
|
const state=path.join(base,'state');
|
|
28
30
|
try{
|
|
@@ -3,11 +3,13 @@ import assert from 'node:assert/strict';
|
|
|
3
3
|
import {readFile} from 'node:fs/promises';
|
|
4
4
|
|
|
5
5
|
const json=async rel=>JSON.parse(await readFile(new URL('../'+rel,import.meta.url),'utf8'));
|
|
6
|
+
const currentVersion=(await json('package.json')).version;
|
|
6
7
|
const parent={version:'0.15.45',release_id:'v0.15.45-34aa8d1241cfb8bf',
|
|
7
8
|
manifest_sha256:'34aa8d1241cfb8bf0b5ba303bf3c06e0e9e1bf01f64457bca659d6dd4d634fdc',
|
|
8
9
|
generation_manifest_sha256:'ff2b73410062688651abd7bc2dd7b2b8c87810a28e3808bfe1f4352a25b0e6e4'};
|
|
9
10
|
|
|
10
|
-
test('R72 current source, package, bridge, provenance and Windows gate bind exact predecessor',
|
|
11
|
+
test('R72 current source, package, bridge, provenance and Windows gate bind exact predecessor',
|
|
12
|
+
{skip:currentVersion!=='0.15.46'?'historical R72 release identity; current development version differs':false},async()=>{
|
|
11
13
|
const [pkg,lock,bridge,release,provenance,matrix,sbom,version]=await Promise.all([
|
|
12
14
|
json('package.json'),json('package-lock.json'),json('bridge/package.json'),json('RELEASE.json'),
|
|
13
15
|
json('PROVENANCE.json'),json('TEST-MATRIX.json'),json('SBOM.cdx.json'),
|
|
@@ -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
|
+
const npmCli=process.env.npm_execpath || path.join(path.dirname(process.execPath),'node_modules','npm','bin','npm-cli.js');
|
|
9
|
+
|
|
10
|
+
test('R73 npm dry-run package excludes generated test evidence and reference trees',()=>{
|
|
11
|
+
const result=spawnSync(process.execPath,[npmCli,'pack','--dry-run','--json'],{
|
|
12
|
+
cwd:root,encoding:'utf8',timeout:120000,windowsHide:true,env:{...process.env,npm_config_audit:'false'}
|
|
13
|
+
});
|
|
14
|
+
assert.equal(result.error,undefined,String(result.error));
|
|
15
|
+
assert.equal(result.status,0,result.stderr||result.stdout);
|
|
16
|
+
const parsed=JSON.parse(result.stdout);
|
|
17
|
+
const record=Array.isArray(parsed)?parsed[0]:parsed['deadbyte-mcp'];
|
|
18
|
+
assert.ok(record&&Array.isArray(record.files),'npm file inventory missing');
|
|
19
|
+
const leaked=record.files.map(row=>row.path).filter(name=>
|
|
20
|
+
/^(?:testing|references|\.git)(?:\/|$)|(?:^|\/)\.env(?:\.|$)/i.test(name));
|
|
21
|
+
assert.deepEqual(leaked,[],'generated evidence or private trees must not enter npm package');
|
|
22
|
+
});
|