deadbyte-mcp 0.15.70 → 0.15.71
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 +46 -27
- package/PROVENANCE.json +43 -6
- package/README.txt +19 -11
- package/RELEASE.json +53 -9
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +44 -2
- package/bridge/package.json +1 -1
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R184-package-lock.json +109 -16
- package/package.json +1 -1
- package/scripts/deploy-r48.mjs +17 -4
- package/scripts/r191-acceptance-readonly.mjs +7 -0
- package/scripts/r191-existing-regression.mjs +5 -0
- package/scripts/r191-existing-stage-audit.mjs +21 -0
- package/scripts/r191-extended-security-gate.mjs +14 -0
- package/scripts/r191-focused-tests.mjs +3 -0
- package/scripts/r191-full-runner.mjs +18 -0
- package/scripts/r191-historical-release-regression.mjs +9 -0
- package/scripts/r191-live-claim-inspect.mjs +15 -0
- package/scripts/r191-npm-registry-readonly.mjs +14 -0
- package/scripts/r191-old-version-audit.mjs +12 -0
- package/scripts/r191-privilege-readonly.mjs +6 -0
- package/scripts/r191-process-policy-focused.mjs +1 -0
- package/scripts/r191-protected-failure-gate.mjs +3 -0
- package/scripts/r191-release-contract-runner.mjs +1 -0
- package/scripts/r191-unreached-focused.mjs +29 -0
- package/src/activation-lock-r65.mjs +9 -1
- package/src/deadbyte-cli.mjs +2 -1
- package/src/runtime-update.mjs +2 -2
- package/src/trusted-epoch-bootstrap-r158.mjs +8 -0
- package/src/version.mjs +1 -1
- package/test/process-policy-refresh.test.mjs +11 -1
- package/test/r130-release-security-contract.test.mjs +7 -7
- package/test/r132-release-identity.test.mjs +6 -6
- package/test/r153-prepublish-identity.test.mjs +1 -1
- package/test/r165-live-cli-skew.test.mjs +1 -1
- package/test/r167-release-contract.test.mjs +8 -8
- package/test/r172-release-contract.test.mjs +5 -5
- package/test/r173-release-contract.test.mjs +14 -14
- package/test/r179-release-contract.test.mjs +7 -7
- package/test/r190-foreground-cli.test.mjs +14 -4
- package/test/r190-release-contract.test.mjs +8 -8
- package/test/r191-epoch-root-selection.test.mjs +24 -0
- package/test/r191-epoch-source-claim.test.mjs +88 -0
- package/test/r191-inherited-epoch-preflight.test.mjs +65 -0
- package/test/r191-release-contract.test.mjs +31 -0
- package/test/r59-tool-surface-compat.test.mjs +1 -1
|
@@ -13,21 +13,21 @@ test('R190 package identity binds the exact published R179 source and live runti
|
|
|
13
13
|
json('package.json'),readReleaseLock(root),json('bridge/package.json'),json('RELEASE.json'),
|
|
14
14
|
json('PROVENANCE.json'),json('TEST-MATRIX.json'),json('SBOM.cdx.json')
|
|
15
15
|
]);
|
|
16
|
-
assert.equal(pkg.version,'0.15.
|
|
17
|
-
assert.equal(lock.version,'0.15.
|
|
18
|
-
assert.equal(lock.packages[''].version,'0.15.
|
|
19
|
-
assert.equal(bridge.version,'0.15.
|
|
20
|
-
assert.equal(release.version,'0.15.
|
|
21
|
-
assert.equal(release.train,'
|
|
22
|
-
assert.equal(matrix.version,'0.15.
|
|
23
|
-
assert.equal(matrix.train,'
|
|
24
|
-
assert.equal(sbom.metadata.component.version,'0.15.
|
|
25
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
26
|
-
assert.equal(release.source_predecessor.train,'
|
|
27
|
-
assert.equal(release.source_predecessor.manifest_sha256,'
|
|
28
|
-
assert.equal(release.source_predecessor.tarball_sha256,'
|
|
16
|
+
assert.equal(pkg.version,'0.15.71');
|
|
17
|
+
assert.equal(lock.version,'0.15.71');
|
|
18
|
+
assert.equal(lock.packages[''].version,'0.15.71');
|
|
19
|
+
assert.equal(bridge.version,'0.15.71');
|
|
20
|
+
assert.equal(release.version,'0.15.71');
|
|
21
|
+
assert.equal(release.train,'R191');
|
|
22
|
+
assert.equal(matrix.version,'0.15.71');
|
|
23
|
+
assert.equal(matrix.train,'R191');
|
|
24
|
+
assert.equal(sbom.metadata.component.version,'0.15.71');
|
|
25
|
+
assert.equal(release.source_predecessor.version,'0.15.70');
|
|
26
|
+
assert.equal(release.source_predecessor.train,'R190');
|
|
27
|
+
assert.equal(release.source_predecessor.manifest_sha256,'df15db35c7ddddd769650f5c86557159d10d99e9eef4dd232cae67faeb8ad6e3');
|
|
28
|
+
assert.equal(release.source_predecessor.tarball_sha256,'1a5f5f30bf4781ed951d6d201b8a972d088b2091555d7f4cc2e2bb899ed5707e');
|
|
29
29
|
assert.equal(release.runtime_predecessor.version,'0.15.68');
|
|
30
|
-
assert.equal(provenance.version,'0.15.
|
|
30
|
+
assert.equal(provenance.version,'0.15.71');
|
|
31
31
|
assert.equal(provenance.parent_tarball_sha256,release.source_predecessor.tarball_sha256);
|
|
32
32
|
assert.equal(provenance.source_predecessor_manifest_file_sha256,release.source_predecessor.manifest_sha256);
|
|
33
33
|
});
|
|
@@ -16,14 +16,14 @@ test('R190 version, lock snapshot, bridge, matrix, SBOM and provenance identify
|
|
|
16
16
|
read('package.json'),readReleaseLock(root),read('bridge/package.json'),read('RELEASE.json'),
|
|
17
17
|
read('PROVENANCE.json'),read('TEST-MATRIX.json'),read('SBOM.cdx.json')]);
|
|
18
18
|
for(const version of [pkg.version,lock.version,lock.packages[''].version,bridge.version,release.version,provenance.version,
|
|
19
|
-
matrix.version,sbom.metadata.component.version,DEADBYTE_VERSION])assert.equal(version,'0.15.
|
|
20
|
-
assert.equal(release.train,'
|
|
21
|
-
assert.equal(sbom.serialNumber,'urn:cdx:deadbyte-mcp:
|
|
19
|
+
matrix.version,sbom.metadata.component.version,DEADBYTE_VERSION])assert.equal(version,'0.15.71');
|
|
20
|
+
assert.equal(release.train,'R191');assert.equal(matrix.train,'R191');
|
|
21
|
+
assert.equal(sbom.serialNumber,'urn:cdx:deadbyte-mcp:r191:0.15.71');
|
|
22
22
|
assert.equal(release.parent.release_id,parent);assert.equal(release.parent.manifest_sha256,manifest);
|
|
23
23
|
assert.equal(release.parent.generation_manifest_sha256,generation);
|
|
24
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
25
|
-
assert.equal(release.source_predecessor.manifest_sha256,'
|
|
26
|
-
assert.equal(release.source_predecessor.tarball_sha256,'
|
|
24
|
+
assert.equal(release.source_predecessor.version,'0.15.70');
|
|
25
|
+
assert.equal(release.source_predecessor.manifest_sha256,'df15db35c7ddddd769650f5c86557159d10d99e9eef4dd232cae67faeb8ad6e3');
|
|
26
|
+
assert.equal(release.source_predecessor.tarball_sha256,'1a5f5f30bf4781ed951d6d201b8a972d088b2091555d7f4cc2e2bb899ed5707e');
|
|
27
27
|
assert.equal(release.runtime_predecessor.release_id,parent);
|
|
28
28
|
assert.equal(provenance.runtime_predecessor.release_id,parent);
|
|
29
29
|
assert.equal(provenance.parent_tarball_sha256,release.source_predecessor.tarball_sha256);
|
|
@@ -48,7 +48,7 @@ test('R190 retains historical R179 and R173 source evidence without promoting it
|
|
|
48
48
|
assert.equal(release.r173.source_predecessor_version,'0.15.67');
|
|
49
49
|
assert.equal(release.r173.source_predecessor_manifest_sha256,'4fd7bfc76b1de6401c142b607cc84e847434b28c888bc99ceb09c9e8e5adc8f8');
|
|
50
50
|
assert.equal(prov.r173.source_predecessor_version,'0.15.67');
|
|
51
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
51
|
+
assert.equal(release.source_predecessor.version,'0.15.70');
|
|
52
52
|
assert.notEqual(release.source_predecessor.tarball_sha256,release.r173.source_predecessor_tarball_sha256);
|
|
53
53
|
});
|
|
54
54
|
test('R184 ships a semantic lock snapshot matching installed package identity and dependencies',async()=>{
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import {spawnSync} from 'node:child_process';
|
|
4
|
+
import {mkdtemp,stat,rm} from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
4
6
|
import path from 'node:path';
|
|
5
7
|
import {fileURLToPath} from 'node:url';
|
|
6
8
|
import {inspectTrustedInstallation} from '../src/privileged-install-trust-r89.mjs';
|
|
7
9
|
|
|
8
10
|
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
9
11
|
|
|
10
|
-
test('R190 CLI
|
|
12
|
+
test('R190 CLI proves R89 before touching isolated controller and fails closed on absent launcher',async t=>{
|
|
13
|
+
const stateRoot=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r191-r190-cli-'));
|
|
14
|
+
t.after(()=>rm(stateRoot,{recursive:true,force:true}));
|
|
11
15
|
const installation=await inspectTrustedInstallation([
|
|
12
16
|
process.execPath,
|
|
13
17
|
path.join(root,'src','deadbyte-cli.mjs'),
|
|
@@ -21,11 +25,17 @@ test('R190 CLI exposes explicit marker recovery and applies the R89 trust bounda
|
|
|
21
25
|
path.join(root,'bin','deadbyte-epoch-lock-r85.node')
|
|
22
26
|
]).catch(()=>({trusted:false}));
|
|
23
27
|
const result=spawnSync(process.execPath,['src/deadbyte-cli.mjs','remote','reconcileforeground',
|
|
24
|
-
'--confirm-marker-sha256','f'.repeat(64)],{encoding:'utf8',windowsHide:true,shell:false
|
|
28
|
+
'--confirm-marker-sha256','f'.repeat(64)],{encoding:'utf8',windowsHide:true,shell:false,
|
|
29
|
+
env:{...process.env,DEADBYTE_STATE_ROOT:stateRoot}});
|
|
25
30
|
const output=String(result.stdout||'')+String(result.stderr||'');
|
|
26
31
|
assert.notEqual(result.status,0,output);
|
|
27
|
-
if(installation.trusted)
|
|
28
|
-
|
|
32
|
+
if(installation.trusted){
|
|
33
|
+
// R89 succeeded, but this isolated fixture has no controller launcher.
|
|
34
|
+
// The test must fail closed before foreground marker or production access.
|
|
35
|
+
assert.match(output,/ENOENT: no such file or directory, lstat .*DEADBYTE-MCP\.ps1/);
|
|
36
|
+
}else assert.match(output,/R190_PROTECTED_RECOVERY_REQUIRED/);
|
|
37
|
+
await assert.rejects(stat(path.join(stateRoot,'evidence','foreground-recovery-r190')),{code:'ENOENT'});
|
|
38
|
+
await assert.rejects(stat(path.join(stateRoot,'runtime','remote-session.json')),{code:'ENOENT'});
|
|
29
39
|
assert.doesNotMatch(output,/unsupported remote command|EXACT_ACTIVE_RELEASE_REQUIRED/);
|
|
30
40
|
});
|
|
31
41
|
|
|
@@ -8,17 +8,17 @@ test('R190 candidate identity separates published source 0.15.69 from active run
|
|
|
8
8
|
json('package.json'),json('bridge/package.json'),json('RELEASE.json'),json('PROVENANCE.json'),
|
|
9
9
|
json('TEST-MATRIX.json'),json('SBOM.cdx.json'),readFile(new URL('../src/version.mjs',import.meta.url),'utf8')]);
|
|
10
10
|
for(const value of [pkg.version,bridge.version,release.version,provenance.version,matrix.version,
|
|
11
|
-
sbom.metadata.component.version])assert.equal(value,'0.15.
|
|
12
|
-
assert.match(versionSource,/DEADBYTE_VERSION = '0\.15\.
|
|
13
|
-
assert.equal(release.train,'
|
|
14
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
15
|
-
assert.equal(release.source_predecessor.tarball_sha256,'
|
|
16
|
-
assert.equal(release.source_predecessor.manifest_sha256,'
|
|
11
|
+
sbom.metadata.component.version])assert.equal(value,'0.15.71');
|
|
12
|
+
assert.match(versionSource,/DEADBYTE_VERSION = '0\.15\.71'/);
|
|
13
|
+
assert.equal(release.train,'R191');assert.equal(matrix.train,'R191');
|
|
14
|
+
assert.equal(release.source_predecessor.version,'0.15.70');
|
|
15
|
+
assert.equal(release.source_predecessor.tarball_sha256,'1a5f5f30bf4781ed951d6d201b8a972d088b2091555d7f4cc2e2bb899ed5707e');
|
|
16
|
+
assert.equal(release.source_predecessor.manifest_sha256,'df15db35c7ddddd769650f5c86557159d10d99e9eef4dd232cae67faeb8ad6e3');
|
|
17
17
|
assert.equal(release.runtime_predecessor.version,'0.15.68');
|
|
18
18
|
assert.equal(release.runtime_predecessor.release_id,'v0.15.68-ff29dbb2fbb89862');
|
|
19
|
-
assert.equal(provenance.source_predecessor_version,'0.15.
|
|
19
|
+
assert.equal(provenance.source_predecessor_version,'0.15.70');
|
|
20
20
|
assert.equal(provenance.source_predecessor_tarball_sha256,release.source_predecessor.tarball_sha256);
|
|
21
|
-
assert.match(sbom.serialNumber,/
|
|
21
|
+
assert.match(sbom.serialNumber,/r191:0\.15\.71$/);
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
test('R190 release metadata records verified recovery without claiming publication or activation',async()=>{
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import * as epoch from '../src/trusted-epoch-bootstrap-r158.mjs';
|
|
4
|
+
|
|
5
|
+
const predecessor='C:\\Program Files\\DEADBYTE-R173-Protected-0.15.68-ff29dbb2fbb89862-Test';
|
|
6
|
+
const candidate='C:\\Program Files\\DEADBYTE-01571-REGISTRY-candidate';
|
|
7
|
+
test('R191 inherited predecessor epoch is rebound to exact successor staging root',()=>{
|
|
8
|
+
assert.equal(epoch.selectActivationEpochRoot({
|
|
9
|
+
configuredEpochRoot:predecessor,explicitEpochRoot:null,stageRoot:candidate
|
|
10
|
+
}),candidate);
|
|
11
|
+
});
|
|
12
|
+
test('R191 no inherited epoch does not create an authority epoch path',()=>{
|
|
13
|
+
assert.equal(epoch.selectActivationEpochRoot({
|
|
14
|
+
configuredEpochRoot:null,explicitEpochRoot:null,stageRoot:candidate
|
|
15
|
+
}),null);
|
|
16
|
+
});
|
|
17
|
+
test('R191 explicit epoch root remains exact and invalid paths reject',()=>{
|
|
18
|
+
assert.equal(epoch.selectActivationEpochRoot({
|
|
19
|
+
configuredEpochRoot:predecessor,explicitEpochRoot:candidate,stageRoot:candidate
|
|
20
|
+
}),candidate);
|
|
21
|
+
assert.throws(()=>epoch.selectActivationEpochRoot({
|
|
22
|
+
configuredEpochRoot:predecessor,explicitEpochRoot:'..\\writable',stageRoot:candidate
|
|
23
|
+
}),/absolute|root|path/i);
|
|
24
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createHash,generateKeyPairSync,sign} from 'node:crypto';
|
|
4
|
+
import {mkdir,mkdtemp,readFile,rm,writeFile} from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {canonicalJson} from '../src/canonical-json.mjs';
|
|
8
|
+
import {createActivationClaimManager} from '../src/activation-lock-r65.mjs';
|
|
9
|
+
|
|
10
|
+
const sha=v=>createHash('sha256').update(v).digest('hex');
|
|
11
|
+
const DOMAIN=Buffer.from('DEADBYTE-R27-SLOT-UPDATE-V1\\0'.replace('\\0','\0'),'utf8');
|
|
12
|
+
const manifest='d'.repeat(64);
|
|
13
|
+
function receipt(body,key){
|
|
14
|
+
const bytes=Buffer.from(canonicalJson(body));
|
|
15
|
+
const signed=sign(null,Buffer.concat([DOMAIN,bytes]),key.privateKey);
|
|
16
|
+
const payload={body,body_sha256:sha(bytes),
|
|
17
|
+
attestation:{algorithm:'ed25519',domain:'DEADBYTE-R27-SLOT-UPDATE-V1',
|
|
18
|
+
key_id:sha(key.publicKey.export({type:'spki',format:'der'})),signature_base64:signed.toString('base64')}};
|
|
19
|
+
payload.receipt_sha256=sha(Buffer.from(canonicalJson(payload)));
|
|
20
|
+
return payload;
|
|
21
|
+
}
|
|
22
|
+
async function fixture({tamper=false,drift=false}={}){
|
|
23
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r191-r65-'));
|
|
24
|
+
const updates=path.join(root,'evidence','updates'),trust=path.join(root,'trust');
|
|
25
|
+
await mkdir(updates,{recursive:true});await mkdir(trust,{recursive:true});
|
|
26
|
+
const key=generateKeyPairSync('ed25519');
|
|
27
|
+
await writeFile(path.join(trust,'ed25519-public.pem'),key.publicKey.export({type:'spki',format:'pem'}));
|
|
28
|
+
const prior={schema:'deadbyte.active-release.v1',release_id:'v0.15.68-ff29dbb2fbb89862',
|
|
29
|
+
version:'0.15.68',manifest_sha256:'f'.repeat(64),generation_manifest_sha256:'a'.repeat(64)};
|
|
30
|
+
await writeFile(path.join(root,'active-release.json'),JSON.stringify(drift?{...prior,release_id:'v0-other'}:prior));
|
|
31
|
+
const manager=createActivationClaimManager({stateRoot:root}),subject='b'.repeat(64);
|
|
32
|
+
const claim=await manager.claim(subject);
|
|
33
|
+
const updateId='r27-'+manifest.slice(0,16)+'-r191proof';
|
|
34
|
+
const dir=path.join(updates,updateId);await mkdir(dir);
|
|
35
|
+
const body={schema:'deadbyte.r27-slot-update-failure.v1',update_id:updateId,
|
|
36
|
+
release_version:'0.15.70',candidate_manifest_sha256:manifest,
|
|
37
|
+
failed_at_utc:new Date().toISOString(),result:'PRECONDITION_REJECTED',phase:'initial',
|
|
38
|
+
error:{name:'Error',code:null,message:'authority epoch exact release source hash mismatch: manifest.sha256'},
|
|
39
|
+
prior_pointer:prior,attempted_pointer:null,
|
|
40
|
+
rollback:{attempted:false,errors:[]}};
|
|
41
|
+
const signed=receipt(body,key);
|
|
42
|
+
if(tamper)signed.body.error.message='altered message';
|
|
43
|
+
await writeFile(path.join(dir,'failure-receipt.json'),JSON.stringify(signed));
|
|
44
|
+
await writeFile(path.join(updates,'r65-activation-outcome-r191.json'),JSON.stringify({
|
|
45
|
+
schema:'deadbyte.r65-activation-outcome.v1',claim_operation_id:claim.operation_id,
|
|
46
|
+
preflight_subject_sha256:subject,candidate_version:'0.15.70',
|
|
47
|
+
candidate_manifest_sha256:manifest,predecessor_release_id:prior.release_id,
|
|
48
|
+
outcome:{state:'ACTIVATION_OUTCOME_UNKNOWN',reason:body.error.message},
|
|
49
|
+
update_id:null,state_path:null,continuity_update_id:null,observed_at_utc:new Date().toISOString()
|
|
50
|
+
}));
|
|
51
|
+
return {root,manager,claim};
|
|
52
|
+
}
|
|
53
|
+
test('R191 signed initial epoch source mismatch may reconcile an R65 claim with unchanged predecessor',async()=>{
|
|
54
|
+
const f=await fixture();
|
|
55
|
+
try{const next=await f.manager.claim('c'.repeat(64));
|
|
56
|
+
assert.notEqual(next.operation_id,f.claim.operation_id);await next.release();
|
|
57
|
+
}finally{await rm(f.root,{recursive:true,force:true});}
|
|
58
|
+
});
|
|
59
|
+
test('R191 tampered signed failure receipt cannot reconcile R65 claim',async()=>{
|
|
60
|
+
const f=await fixture({tamper:true});
|
|
61
|
+
try{await assert.rejects(()=>f.manager.claim('c'.repeat(64)),/unresolved|in progress/);}
|
|
62
|
+
finally{await rm(f.root,{recursive:true,force:true});}
|
|
63
|
+
});
|
|
64
|
+
test('R191 changed predecessor pointer cannot reconcile R65 claim',async()=>{
|
|
65
|
+
const f=await fixture({drift:true});
|
|
66
|
+
try{await assert.rejects(()=>f.manager.claim('c'.repeat(64)),/unresolved|in progress/);}
|
|
67
|
+
finally{await rm(f.root,{recursive:true,force:true});}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
test('R191 read-only inspection reports a safe signed precondition claim without reclaiming it',async()=>{
|
|
72
|
+
const f=await fixture();
|
|
73
|
+
try{
|
|
74
|
+
const before=await readFile(path.join(f.root,'evidence','updates','r65-activation.lock'),'utf8');
|
|
75
|
+
const audit=await f.manager.inspect();
|
|
76
|
+
assert.equal(audit.status,'RECONCILABLE');
|
|
77
|
+
assert.equal(audit.operation_id,f.claim.operation_id);
|
|
78
|
+
const after=await readFile(path.join(f.root,'evidence','updates','r65-activation.lock'),'utf8');
|
|
79
|
+
assert.equal(after,before);
|
|
80
|
+
}finally{await rm(f.root,{recursive:true,force:true});}
|
|
81
|
+
});
|
|
82
|
+
test('R191 read-only inspection leaves a tampered evidence claim blocked',async()=>{
|
|
83
|
+
const f=await fixture({tamper:true});
|
|
84
|
+
try{
|
|
85
|
+
const audit=await f.manager.inspect();
|
|
86
|
+
assert.equal(audit.status,'UNRESOLVED');
|
|
87
|
+
}finally{await rm(f.root,{recursive:true,force:true});}
|
|
88
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {mkdtemp,mkdir,readFile,readdir,rm,writeFile} from 'node:fs/promises';
|
|
4
|
+
import {spawnSync} from 'node:child_process';
|
|
5
|
+
import {createHash} from 'node:crypto';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import {fileURLToPath} from 'node:url';
|
|
9
|
+
const src=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
10
|
+
const H=b=>createHash('sha256').update(b).digest('hex');
|
|
11
|
+
|
|
12
|
+
test('R191 real preflight rejects writable staging when predecessor has a protected epoch', {skip:process.platform!=='win32'}, async()=>{
|
|
13
|
+
const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r191-preflight-'));
|
|
14
|
+
try{
|
|
15
|
+
const state=path.join(root,'state'),bin=path.join(state,'bin'),gen=path.join(state,'generations','parent'),policies=path.join(state,'policies');
|
|
16
|
+
await Promise.all([mkdir(bin,{recursive:true}),mkdir(gen,{recursive:true}),mkdir(policies,{recursive:true}),mkdir(path.join(state,'runtime'),{recursive:true})]);
|
|
17
|
+
const release=JSON.parse(await readFile(path.join(src,'RELEASE.json'),'utf8'));
|
|
18
|
+
const configFields={process:'ProcessPolicyFile',coding:'CodingPolicyFile',autonomous:'AutonomousPolicyFile',desktop:'DesktopPolicyFile',host:'HostPolicyFile'};
|
|
19
|
+
const policyPaths={};
|
|
20
|
+
for(const plane of Object.keys(configFields)){
|
|
21
|
+
const f=path.join(policies,plane+'.json');
|
|
22
|
+
const grant=path.join(state,'runtime',plane+'-grant.json');
|
|
23
|
+
const grantField=plane==='host'?'arm_file':plane==='process'||plane==='desktop'?'grant_file':'lease_file';
|
|
24
|
+
await writeFile(f,JSON.stringify({schema:plane==='host'?'deadbyte.host-policy.v2':'deadbyte.'+plane+'-policy.v1',enabled:true,[grantField]:grant}));
|
|
25
|
+
policyPaths[configFields[plane]]=f;
|
|
26
|
+
}
|
|
27
|
+
const configPath=path.join(gen,'controller-config.psd1');
|
|
28
|
+
const epoch='C:\\Program Files\\DEADBYTE-R173-Protected-0.15.68-ff29dbb2fbb89862-Test';
|
|
29
|
+
await writeFile(configPath,'@{ '+Object.entries({...policyPaths,AuthorityEpochRoot:epoch}).map(([k,v])=>k+" = '"+v.replaceAll("'","''")+"'").join('; ')+' }');
|
|
30
|
+
const pointer={schema:'deadbyte.active-release.v1',...release.parent,release_root:path.join(state,'releases',release.parent.release_id),generation_root:gen,
|
|
31
|
+
launcher_sha256:H('launcher'),config_path:configPath};
|
|
32
|
+
const pointerBytes=Buffer.from(JSON.stringify(pointer));
|
|
33
|
+
await writeFile(path.join(state,'active-release.json'),pointerBytes);
|
|
34
|
+
const disarmed={configured:true,armed:false,reason:'disarmed'};
|
|
35
|
+
const observation={schema:'deadbyte.controller-observation.v1',state:'ONLINE',local_ready:true,
|
|
36
|
+
bridge:{alive:true,reason:'verified',pid:111},core:{alive:true,reason:'verified',pid:112},
|
|
37
|
+
cloud:{alive:true,reason:'verified',pid:113},supervisor:{running:true},
|
|
38
|
+
authority:Object.fromEntries(Object.keys(configFields).map(plane=>[plane,disarmed]))};
|
|
39
|
+
const observed=path.join(state,'observed.json');await writeFile(observed,JSON.stringify(observation));
|
|
40
|
+
const ps="param([string]$Command,[switch]$Json)\nif($Command -ne 'status' -or -not $Json){throw 'unexpected mutation'}\nGet-Content -Raw -LiteralPath '"+observed.replaceAll("'","''")+"'\n";
|
|
41
|
+
await writeFile(path.join(bin,'DEADBYTE-MCP.ps1'),ps);
|
|
42
|
+
const result=spawnSync(process.execPath,[path.join(src,'scripts','deploy-r48.mjs'),'--preflight'],{
|
|
43
|
+
env:{...process.env,DEADBYTE_STATE_ROOT:state},cwd:src,encoding:'utf8',timeout:30000,windowsHide:true});
|
|
44
|
+
assert.equal(result.status,0,result.stderr);
|
|
45
|
+
const preflight=JSON.parse(result.stdout.trim());
|
|
46
|
+
assert.equal(preflight.state,'ONLINE_READY');
|
|
47
|
+
assert.equal(preflight.authority_continuity,'NOT_APPLICABLE');
|
|
48
|
+
// The same exact release may execute from a writable developer tree
|
|
49
|
+
// or from an R89-protected immutable installation. The decision must
|
|
50
|
+
// follow OS ACL trust, not the test file's assumed extraction location.
|
|
51
|
+
const {inspectTrustedInstallation,privilegedEpochInstallFiles}=await import('../src/privileged-install-trust-r89.mjs');
|
|
52
|
+
let stageTrusted=false;
|
|
53
|
+
try {
|
|
54
|
+
const trust=await inspectTrustedInstallation(privilegedEpochInstallFiles(src,process.execPath));
|
|
55
|
+
stageTrusted=trust.trusted===true;
|
|
56
|
+
} catch {stageTrusted=false;}
|
|
57
|
+
assert.equal(preflight.safe_to_activate,stageTrusted);
|
|
58
|
+
assert.equal(preflight.activation_entrypoint_state,stageTrusted?'TRUSTED_READY':'REJECTED');
|
|
59
|
+
assert.equal(preflight.protected_activation_entrypoint,stageTrusted);
|
|
60
|
+
if(stageTrusted)assert.equal(preflight.activation_entrypoint_reason,null);
|
|
61
|
+
else assert.match(preflight.activation_entrypoint_reason,/R89|ACL|protected/i);
|
|
62
|
+
assert.equal(H(await readFile(path.join(state,'active-release.json'))),H(pointerBytes));
|
|
63
|
+
await assert.rejects(readdir(path.join(state,'evidence','updates')),e=>e?.code==='ENOENT');
|
|
64
|
+
} finally {await rm(root,{recursive:true,force:true});}
|
|
65
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {readFile} from 'node:fs/promises';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {DEADBYTE_VERSION} from '../src/version.mjs';
|
|
7
|
+
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
8
|
+
const json=async f=>JSON.parse(await readFile(path.join(root,f),'utf8'));
|
|
9
|
+
const predecessor='v0.15.68-ff29dbb2fbb89862';
|
|
10
|
+
const publishedManifest='df15db35c7ddddd769650f5c86557159d10d99e9eef4dd232cae67faeb8ad6e3';
|
|
11
|
+
const publishedTarball='1a5f5f30bf4781ed951d6d201b8a972d088b2091555d7f4cc2e2bb899ed5707e';
|
|
12
|
+
test('R191 exact version identity includes bundled bridge, lock snapshot, SBOM, provenance and matrix',async()=>{
|
|
13
|
+
const [pkg,bridge,lock,release,provenance,matrix,bom]=await Promise.all(['package.json','bridge/package.json','docs/R184-package-lock.json','RELEASE.json','PROVENANCE.json','TEST-MATRIX.json','SBOM.cdx.json'].map(json));
|
|
14
|
+
for(const ver of [pkg.version,bridge.version,lock.version,lock.packages[''].version,release.version,provenance.version,matrix.version,bom.metadata.component.version,DEADBYTE_VERSION])assert.equal(ver,'0.15.71');
|
|
15
|
+
for(const train of [release.train,matrix.train])assert.equal(train,'R191');
|
|
16
|
+
assert.equal(bom.serialNumber,'urn:cdx:deadbyte-mcp:r191:0.15.71');
|
|
17
|
+
assert.equal(release.parent.release_id,predecessor);
|
|
18
|
+
assert.equal(provenance.parent_release_id,predecessor);
|
|
19
|
+
assert.equal(release.source_predecessor.version,'0.15.70');
|
|
20
|
+
assert.equal(release.source_predecessor.manifest_sha256,publishedManifest);
|
|
21
|
+
assert.equal(release.source_predecessor.tarball_sha256,publishedTarball);
|
|
22
|
+
assert.equal(provenance.source_predecessor_version,'0.15.70');
|
|
23
|
+
assert.equal(provenance.parent_tarball_sha256,publishedTarball);
|
|
24
|
+
assert.equal(provenance.source_predecessor_tarball_sha256,publishedTarball);
|
|
25
|
+
assert.equal(provenance.source_predecessor_manifest_file_sha256,publishedManifest);
|
|
26
|
+
assert.equal(release.r191_activation_repair.npm_publish,'NOT_RUN');
|
|
27
|
+
assert.equal(release.r191_activation_repair.production_activation,'NOT_RUN');
|
|
28
|
+
assert.equal(release.r190_foreground_recovery.status,'PREPUBLISH_VERIFIED');
|
|
29
|
+
assert.equal(release.r179.stable_final_claim,false);
|
|
30
|
+
assert.equal(matrix.required.some(x=>x.id==='r191-trusted-successor-epoch-root'),true);
|
|
31
|
+
});
|
|
@@ -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','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53','0.15.54','0.15.55','0.15.56','0.15.57','0.15.58','0.15.59','0.15.60','0.15.61','0.15.62','0.15.63','0.15.64','0.15.65','0.15.66','0.15.67','0.15.68','0.15.69','0.15.70'].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','0.15.49','0.15.50','0.15.51','0.15.52','0.15.53','0.15.54','0.15.55','0.15.56','0.15.57','0.15.58','0.15.59','0.15.60','0.15.61','0.15.62','0.15.63','0.15.64','0.15.65','0.15.66','0.15.67','0.15.68','0.15.69','0.15.70','0.15.71'].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');
|