deadbyte-mcp 0.15.65 → 0.15.67
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 +68 -21
- package/PROVENANCE.json +35 -9
- package/README.txt +31 -18
- package/RELEASE.json +43 -7
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +45 -2
- package/bridge/package.json +1 -1
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R168-ACTIVATION-RECONCILIATION-BLOCKER-20261026.md +39 -0
- package/docs/R168-CRASH-JOURNAL-PROTECTED-PREFLIGHT-20261026.md +46 -0
- package/docs/R168-FIVE-PLANE-FENCE-20261026.md +41 -0
- package/docs/R168-NATIVE-EXCLUSION-TERMINAL-CONTRACT-20261026.md +53 -0
- package/docs/R168-NPX-REMOTE-SKEW-HOTFIX-20261026.md +41 -0
- package/docs/R168-READONLY-AUDIT-AND-RESUME-20261026.md +56 -0
- package/docs/R168-SIGNED-RECONCILIATION-PRECOMMIT-20261026.md +45 -0
- package/docs/R169-NPX-ACTIVE-CONTROLLER-DELEGATION-20261026.md +57 -0
- package/node_modules/which/CHANGELOG.md +166 -0
- package/package.json +5 -2
- package/scripts/gate-windows-r168.ps1 +75 -0
- package/scripts/npm-release-r48.mjs +23 -3
- package/scripts/npm-release-readiness-r168.mjs +70 -0
- package/scripts/r168-audit-readonly.mjs +32 -0
- package/scripts/r168-npm-registry-readonly.mjs +19 -0
- package/scripts/r168-protected-preflight.mjs +65 -0
- package/scripts/r168-release-scan.mjs +28 -0
- package/scripts/r168-source-full-test-runner.mjs +46 -0
- package/scripts/r168-subject-readonly.mjs +49 -0
- package/scripts/r172-protected-preflight.mjs +42 -0
- package/src/activation-continuity-abort-r172.mjs +108 -0
- package/src/activation-lock-r65.mjs +5 -1
- package/src/activation-preflight-r65.mjs +2 -1
- package/src/activation-reconciliation-audit-r168.mjs +200 -0
- package/src/activation-reconciliation-consent-r168.mjs +235 -0
- package/src/activation-reconciliation-fence-r168.mjs +87 -0
- package/src/activation-reconciliation-journal-r168.mjs +100 -0
- package/src/activation-reconciliation-receipt-store-r168.mjs +42 -0
- package/src/activation-reconciliation-terminal-r168.mjs +111 -0
- package/src/active-controller-delegation-r169.mjs +57 -0
- package/src/deadbyte-cli.mjs +38 -0
- package/src/npm-publication-resume-r168.mjs +25 -0
- package/src/remote-one-shot-coherence-r168.mjs +43 -0
- package/src/version.mjs +1 -1
- package/test/r130-release-security-contract.test.mjs +7 -7
- package/test/r132-release-identity.test.mjs +8 -8
- package/test/r153-prepublish-identity.test.mjs +1 -1
- package/test/r163-isolated-slot-rollback-restore.test.mjs +2 -2
- package/test/r165-live-cli-skew.test.mjs +7 -7
- package/test/r167-release-contract.test.mjs +11 -10
- package/test/r168-combined-targeted-runner.mjs +14 -0
- package/test/r168-crash-journal.test.mjs +149 -0
- package/test/r168-five-plane-fence-runner.mjs +11 -0
- package/test/r168-five-plane-fence.test.mjs +128 -0
- package/test/r168-focused-suite.test.mjs +4 -0
- package/test/r168-native-five-plane-runner.mjs +11 -0
- package/test/r168-native-five-plane-scratch.test.mjs +49 -0
- package/test/r168-native-r85-protected-scratch.test.mjs +44 -0
- package/test/r168-npm-resume-identity.test.mjs +49 -0
- package/test/r168-owner-reconciliation-contract.test.mjs +250 -0
- package/test/r168-protected-entrypoint.test.mjs +53 -0
- package/test/r168-reconciliation-audit.test.mjs +42 -0
- package/test/r168-release-contract.test.mjs +46 -0
- package/test/r168-remote-one-shot-coherence.test.mjs +66 -0
- package/test/r169-delegate-active.test.mjs +36 -0
- package/test/r169-delegate-cli-integration.test.mjs +93 -0
- package/test/r169-targeted-runner.mjs +16 -0
- package/test/r172-activation-recovery.test.mjs +95 -0
- package/test/r172-release-contract.test.mjs +35 -0
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/r63-one-shot-coherence.test.mjs +10 -5
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#requires -Version 5.1
|
|
2
|
+
[CmdletBinding()]
|
|
3
|
+
param(
|
|
4
|
+
[ValidateSet('preflight','focused','full','manifest','doctor','all')][string]$Stage='preflight',
|
|
5
|
+
[string]$ExpectedDevelopmentRoot
|
|
6
|
+
)
|
|
7
|
+
Set-StrictMode -Version Latest
|
|
8
|
+
$ErrorActionPreference='Stop'
|
|
9
|
+
$repo=Split-Path -Parent $PSScriptRoot
|
|
10
|
+
if([string]::IsNullOrWhiteSpace($ExpectedDevelopmentRoot)){$ExpectedDevelopmentRoot=$repo}
|
|
11
|
+
$ExpectedDevelopmentRoot=[IO.Path]::GetFullPath($ExpectedDevelopmentRoot)
|
|
12
|
+
$state=Join-Path $env:USERPROFILE '.deadbyte-mcp'
|
|
13
|
+
$parentId='v0.15.61-055d1ff0aa99e60b'
|
|
14
|
+
$parentSha='055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf'
|
|
15
|
+
$parentGen='f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4'
|
|
16
|
+
$sourceManifest='3316654646cc870584a7c969c2032af6c795229ff8c65e5a8b794f97c2458c41'
|
|
17
|
+
$nativeNode='C:\Program Files\nodejs\node.exe'
|
|
18
|
+
$node=if(Test-Path -LiteralPath $nativeNode -PathType Leaf){$nativeNode}else{(Get-Command node -ErrorAction Stop).Source}
|
|
19
|
+
function Sha([string]$file){
|
|
20
|
+
$sha=[Security.Cryptography.SHA256]::Create()
|
|
21
|
+
try{$stream=[IO.File]::OpenRead($file);try{return ([BitConverter]::ToString($sha.ComputeHash($stream))).Replace('-','').ToLowerInvariant()}finally{$stream.Dispose()}}finally{$sha.Dispose()}
|
|
22
|
+
}
|
|
23
|
+
function Run-Node([string]$label,[string[]]$nodeArgs){
|
|
24
|
+
if($null -eq $nodeArgs -or $nodeArgs.Count -lt 1){throw "R168_${label}_NO_NODE_ARGUMENTS"}
|
|
25
|
+
Write-Output "R168_STAGE_START=$label"
|
|
26
|
+
Write-Output "R168_STAGE_ARGS=$($nodeArgs.Count)"
|
|
27
|
+
$global:LASTEXITCODE=0
|
|
28
|
+
& $node @nodeArgs
|
|
29
|
+
$exit=$global:LASTEXITCODE
|
|
30
|
+
if($exit -ne 0){throw "R168_${label}_FAILED exit=$exit"}
|
|
31
|
+
Write-Output "R168_STAGE_PASS=$label"
|
|
32
|
+
}
|
|
33
|
+
Push-Location $repo
|
|
34
|
+
$previousExpectedDevelopmentRoot=$env:DEADBYTE_EXPECTED_DEVELOPMENT_ROOT
|
|
35
|
+
try{
|
|
36
|
+
$env:DEADBYTE_EXPECTED_DEVELOPMENT_ROOT=$ExpectedDevelopmentRoot
|
|
37
|
+
$pkg=Get-Content -LiteralPath 'package.json' -Raw | ConvertFrom-Json
|
|
38
|
+
$release=Get-Content -LiteralPath 'RELEASE.json' -Raw | ConvertFrom-Json
|
|
39
|
+
if($pkg.version -ne '0.15.66' -or $release.version -ne '0.15.66' -or $release.train -ne 'R168'){throw 'R168_CANDIDATE_VERSION_DRIFT'}
|
|
40
|
+
if($release.source_predecessor.version -ne '0.15.65' -or $release.source_predecessor.train -ne 'R167' -or $release.r168.source_predecessor_manifest_sha256 -ne $sourceManifest){throw 'R168_SOURCE_PREDECESSOR_IDENTITY_DRIFT'}
|
|
41
|
+
if($release.runtime_predecessor.release_id -ne $parentId -or $release.runtime_predecessor.manifest_sha256 -ne $parentSha -or $release.runtime_predecessor.generation_manifest_sha256 -ne $parentGen){throw 'R168_RUNTIME_PREDECESSOR_IDENTITY_DRIFT'}
|
|
42
|
+
$active=Get-Content -LiteralPath (Join-Path $state 'active-release.json') -Raw | ConvertFrom-Json
|
|
43
|
+
if($active.version -ne '0.15.61' -or $active.release_id -ne $parentId -or $active.manifest_sha256 -ne $parentSha -or $active.generation_manifest_sha256 -ne $parentGen){throw 'R168_ACTIVE_RUNTIME_DRIFT'}
|
|
44
|
+
if((Sha (Join-Path $active.release_root 'MANIFEST.SHA256')) -ne $parentSha){throw 'R168_ACTIVE_MANIFEST_MISMATCH'}
|
|
45
|
+
Write-Output 'R168_PREDECESSOR_PREFLIGHT=PASS'
|
|
46
|
+
if($Stage -eq 'preflight'){return}
|
|
47
|
+
if($Stage -in @('focused','all')){
|
|
48
|
+
Run-Node -label 'focused' -nodeArgs @('--test','--test-reporter=tap','--test-concurrency=1','test/r167-activation-path.test.mjs','test/r168-release-contract.test.mjs','test/r168-remote-one-shot-coherence.test.mjs','test/r160-release-contract.test.mjs','test/r158-epoch-bootstrap.test.mjs','test/r65-activation-orchestration.test.mjs','test/r89-privileged-install-boundary.test.mjs','test/r119-recovery-crash-after-commit.test.mjs','test/r59-tool-surface-compat.test.mjs')
|
|
49
|
+
if($Stage -eq 'focused'){return}
|
|
50
|
+
}
|
|
51
|
+
if($Stage -in @('full','all')){
|
|
52
|
+
$previousCrossVolume=$env:DEADBYTE_TEST_CROSS_VOLUME_ROOT
|
|
53
|
+
$tempDrive=[IO.Path]::GetPathRoot([IO.Path]::GetFullPath($env:TEMP))
|
|
54
|
+
$scratchDrive=if($tempDrive -ieq 'D:\'){'C:\'}else{'D:\'}
|
|
55
|
+
if(-not (Test-Path -LiteralPath $scratchDrive -PathType Container)){throw 'R168_CROSS_VOLUME_DRIVE_UNAVAILABLE'}
|
|
56
|
+
$crossVolume=Join-Path $scratchDrive 'DEADBYTE-R168-CrossVolume'
|
|
57
|
+
try{
|
|
58
|
+
[IO.Directory]::CreateDirectory($crossVolume)|Out-Null
|
|
59
|
+
$env:DEADBYTE_TEST_CROSS_VOLUME_ROOT=$crossVolume
|
|
60
|
+
Run-Node -label 'full' -nodeArgs @('--test','--test-reporter=tap','--test-concurrency=4','test/*.test.mjs')
|
|
61
|
+
}finally{$env:DEADBYTE_TEST_CROSS_VOLUME_ROOT=$previousCrossVolume}
|
|
62
|
+
if($Stage -eq 'full'){return}
|
|
63
|
+
}
|
|
64
|
+
if($Stage -in @('manifest','all')){
|
|
65
|
+
Run-Node -label 'manifest' -nodeArgs @('scripts/release-manifest.mjs','verify')
|
|
66
|
+
if($Stage -eq 'manifest'){return}
|
|
67
|
+
}
|
|
68
|
+
if($Stage -in @('doctor','all')){
|
|
69
|
+
$previous=$env:DEADBYTE_STATE_ROOT
|
|
70
|
+
$isolated=Join-Path $env:TEMP ('deadbyte-r168-doctor-'+[guid]::NewGuid().ToString('N'))
|
|
71
|
+
try{[IO.Directory]::CreateDirectory($isolated)|Out-Null;$env:DEADBYTE_STATE_ROOT=$isolated;Run-Node -label 'isolated_doctor' -nodeArgs @('src/deadbyte-cli.mjs','doctor')}
|
|
72
|
+
finally{$env:DEADBYTE_STATE_ROOT=$previous;Remove-Item -LiteralPath $isolated -Recurse -Force -ErrorAction SilentlyContinue}
|
|
73
|
+
}
|
|
74
|
+
Write-Output 'R168_WINDOWS_CANDIDATE_GATE=PASS_PREPUBLICATION'
|
|
75
|
+
}finally{$env:DEADBYTE_EXPECTED_DEVELOPMENT_ROOT=$previousExpectedDevelopmentRoot;Pop-Location}
|
|
@@ -11,6 +11,8 @@ import {assertR163NpmPublishReady} from './npm-release-readiness-r163.mjs';
|
|
|
11
11
|
import {assertR165NpmPublishReady} from './npm-release-readiness-r165.mjs';
|
|
12
12
|
import {assertR166NpmPublishReady} from './npm-release-readiness-r166.mjs';
|
|
13
13
|
import {assertR167NpmPublishReady,assertR167PackedIdentity} from './npm-release-readiness-r167.mjs';
|
|
14
|
+
import {assertR168NpmPublishReady,assertR168PackedIdentity} from './npm-release-readiness-r168.mjs';
|
|
15
|
+
import {resumePackRecord} from '../src/npm-publication-resume-r168.mjs';
|
|
14
16
|
import { existsSync } from 'node:fs';
|
|
15
17
|
import { resolveStateRoot } from '../src/state-root.mjs';
|
|
16
18
|
import path from 'node:path';
|
|
@@ -116,6 +118,11 @@ async function main() {
|
|
|
116
118
|
if (!publishOnly) throw new Error('PUBLISH_BLOCKED R167 requires --publish-only; activation is separate');
|
|
117
119
|
r167Readiness=await assertR167NpmPublishReady(release,{root});
|
|
118
120
|
}
|
|
121
|
+
let r168Readiness=null;
|
|
122
|
+
if (!isVerifyOnly && version==='0.15.66') {
|
|
123
|
+
if (!publishOnly) throw new Error('PUBLISH_BLOCKED R168 requires --publish-only; activation is separate');
|
|
124
|
+
r168Readiness=await assertR168NpmPublishReady(release,{root});
|
|
125
|
+
}
|
|
119
126
|
if (release.parent?.release_id !== provenance.parent_release_id ||
|
|
120
127
|
release.parent?.manifest_sha256 !== provenance.parent_manifest_sha256 ||
|
|
121
128
|
release.parent?.generation_manifest_sha256 !== provenance.parent_generation_manifest_sha256) {
|
|
@@ -145,8 +152,14 @@ async function main() {
|
|
|
145
152
|
if(resuming&&!existsSync(preservedTarball))throw new Error('immutable npm publication claim exists but exact local tarball is missing; manual artifact reconciliation required');
|
|
146
153
|
if(!resuming&&preservedTarball&&existsSync(preservedTarball))throw new Error('unclaimed preserved npm tarball exists; inspect artifact evidence before another pack');
|
|
147
154
|
if(!resuming)await assertVacantPackDestination({root,filename:`deadbyte-mcp-${version}.tgz`});
|
|
155
|
+
const resumeClaim=resuming?JSON.parse(await readFile(path.join(publicationDir,'claim.json'),'utf8')):null;
|
|
156
|
+
const preservedRecord=resuming?resumePackRecord({
|
|
157
|
+
filename:path.basename(preservedTarball),localBytes:await readFile(preservedTarball),
|
|
158
|
+
claim:resumeClaim,version,
|
|
159
|
+
manifestSha256:createHash('sha256').update(await readFile(path.join(root,'MANIFEST.SHA256'))).digest('hex')
|
|
160
|
+
}):null;
|
|
148
161
|
const packed=resuming?null:parseJson(runNpm(['pack', '--json'], { timeout: 180000 }));
|
|
149
|
-
const packRecord=resuming?
|
|
162
|
+
const packRecord=resuming?preservedRecord:(Array.isArray(packed) ? packed[0] : packed?.[pkg.name]);
|
|
150
163
|
const filename = packRecord?.filename;
|
|
151
164
|
if (!filename) throw new Error('npm pack did not return a tarball filename');
|
|
152
165
|
const tarball = resuming?preservedTarball:path.join(root, filename);
|
|
@@ -157,8 +170,11 @@ async function main() {
|
|
|
157
170
|
try {
|
|
158
171
|
console.log('[4/7] Verifying packed artifact file surface against MANIFEST.SHA256 and packaging contract...');
|
|
159
172
|
const manifestText = await readFile(path.join(root, 'MANIFEST.SHA256'), 'utf8');
|
|
160
|
-
const claimData =
|
|
161
|
-
const
|
|
173
|
+
const claimData = resumeClaim;
|
|
174
|
+
const currentManifestSha256 = createHash('sha256').update(manifestText).digest('hex');
|
|
175
|
+
if(resuming&&claimData.manifest_sha256!==currentManifestSha256)
|
|
176
|
+
throw new Error('immutable npm resume claim manifest changed during artifact verification');
|
|
177
|
+
const manifestSha256 = resuming ? claimData.manifest_sha256 : currentManifestSha256;
|
|
162
178
|
const manifestEntries = new Set();
|
|
163
179
|
for (const line of manifestText.split(/\r?\n/)) {
|
|
164
180
|
if (!line) continue;
|
|
@@ -242,6 +258,10 @@ async function main() {
|
|
|
242
258
|
...r167Readiness,observedSha256:createHash('sha256').update(localBytes).digest('hex'),
|
|
243
259
|
observedIntegrity:localIntegrity,observedShasum:packRecord.shasum
|
|
244
260
|
});
|
|
261
|
+
if(r168Readiness)assertR168PackedIdentity({
|
|
262
|
+
...r168Readiness,observedSha256:createHash('sha256').update(localBytes).digest('hex'),
|
|
263
|
+
observedIntegrity:localIntegrity,observedShasum:packRecord.shasum
|
|
264
|
+
});
|
|
245
265
|
|
|
246
266
|
console.log('[6/7] Publishing artifact to npm registry...');
|
|
247
267
|
publishAttempted = true;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {createHash} from 'node:crypto';
|
|
2
|
+
import {lstat,readFile} from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
6
|
+
const hex=value=>typeof value==='string'&&/^[0-9a-f]{64}$/.test(value);
|
|
7
|
+
const required=(value,message)=>{if(!value)throw new Error(`PUBLISH_BLOCKED R168 ${message}`);};
|
|
8
|
+
|
|
9
|
+
async function checkedFile(file,expected,{maxBytes=32*1024*1024}={}){
|
|
10
|
+
required(hex(expected),'evidence digest missing');
|
|
11
|
+
const info=await lstat(file);
|
|
12
|
+
required(info.isFile()&&!info.isSymbolicLink()&&info.size>0&&info.size<maxBytes,'evidence file invalid');
|
|
13
|
+
const bytes=await readFile(file);
|
|
14
|
+
required(sha256(bytes)===expected,'evidence digest differs');
|
|
15
|
+
return bytes;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function assertR168PackedIdentity({
|
|
19
|
+
tarball_sha256,tarball_integrity,tarball_shasum,
|
|
20
|
+
observedSha256,observedIntegrity,observedShasum
|
|
21
|
+
}={}){
|
|
22
|
+
required(hex(tarball_sha256)&&typeof tarball_integrity==='string'&&tarball_integrity.startsWith('sha512-')&&
|
|
23
|
+
typeof tarball_shasum==='string'&&/^[0-9a-f]{40}$/.test(tarball_shasum),'verified tarball identity invalid');
|
|
24
|
+
required(observedSha256===tarball_sha256&&observedIntegrity===tarball_integrity&&observedShasum===tarball_shasum,
|
|
25
|
+
'repacked tarball differs from verified closure bytes');
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function assertR168NpmPublishReady(release,{root}={}){
|
|
30
|
+
required(release?.version==='0.15.66'&&release?.train==='R168'&&
|
|
31
|
+
release?.r168?.scope==='REMOTE_VERSION_SKEW_FAIL_CLOSED_AND_READONLY_RECONCILIATION',
|
|
32
|
+
'candidate metadata invalid');
|
|
33
|
+
required(release?.parent?.release_id==='v0.15.61-055d1ff0aa99e60b'&&
|
|
34
|
+
release?.source_predecessor?.version==='0.15.65'&&release?.source_predecessor?.train==='R167',
|
|
35
|
+
'lineage mismatch');
|
|
36
|
+
const evidence=process.env.DEADBYTE_R168_CLOSURE_ROOT;
|
|
37
|
+
required(root&&path.isAbsolute(root)&&evidence&&path.isAbsolute(evidence),'closure root absent');
|
|
38
|
+
const allowed=path.resolve(root,'..','..','testing','r168-final-gate-fix1');
|
|
39
|
+
required(path.resolve(evidence).toLowerCase()===allowed.toLowerCase(),'closure root not in scoped testing folder');
|
|
40
|
+
const closure=JSON.parse(await readFile(path.join(evidence,'closure.json'),'utf8'));
|
|
41
|
+
required(closure?.schema==='deadbyte.r168-release-closure.v1'&&closure.status==='PASS'&&closure.version==='0.15.66',
|
|
42
|
+
'closure status mismatch');
|
|
43
|
+
required(closure.production_release_id===release.parent.release_id&&hex(closure.manifest_sha256),'predecessor or manifest invalid');
|
|
44
|
+
required(closure.manifest_sha256===sha256(await readFile(path.join(root,'MANIFEST.SHA256'))),'final manifest drift');
|
|
45
|
+
|
|
46
|
+
const receiptBytes=await checkedFile(path.join(evidence,'protected-install-receipt.json'),closure.protected_receipt_sha256);
|
|
47
|
+
const receipt=JSON.parse(receiptBytes.toString('utf8'));
|
|
48
|
+
required(receipt?.status==='PASS'&&receipt.package_version==='0.15.66'&&
|
|
49
|
+
receipt.source_manifest_sha256===closure.manifest_sha256&&receipt.protected_manifest_sha256===closure.manifest_sha256,
|
|
50
|
+
'protected exact-byte receipt mismatch');
|
|
51
|
+
|
|
52
|
+
const focused=(await checkedFile(path.join(evidence,'windows-focused.stdout.log'),closure.focused_stdout_sha256)).toString('utf8');
|
|
53
|
+
const full=(await checkedFile(path.join(evidence,'windows-full.stdout.log'),closure.full_stdout_sha256)).toString('utf8');
|
|
54
|
+
const verify=(await checkedFile(path.join(evidence,'npm-verify.stdout.log'),closure.verify_stdout_sha256)).toString('utf8');
|
|
55
|
+
required(focused.includes('R168_STAGE_PASS=focused')&&focused.includes('# fail 0'),'focused protected gate incomplete');
|
|
56
|
+
required(full.includes('R168_STAGE_PASS=full')&&full.includes('# fail 0'),'full protected gate incomplete');
|
|
57
|
+
const summaries=[...full.matchAll(/^# tests (\d+)\s*\r?\n# suites (\d+)\s*\r?\n# pass (\d+)\s*\r?\n# fail (\d+)\s*\r?\n# cancelled (\d+)\s*\r?\n# skipped (\d+)/gm)];
|
|
58
|
+
required(summaries.length>0,'full TAP summary absent');
|
|
59
|
+
const final=summaries.at(-1),tests=Number(final[1]),passes=Number(final[3]),fails=Number(final[4]),skips=Number(final[6]);
|
|
60
|
+
required(tests>=1975&&passes>=1925&&fails===0&&skips<=65&&passes+skips===tests,'full regression count mismatch');
|
|
61
|
+
required(verify.includes('VERIFY-ONLY PIPELINE PASS')&&verify.includes('Artifact isolation gate PASS')&&verify.includes('VERIFIED_TARBALL '),
|
|
62
|
+
'npm exact-artifact verification incomplete');
|
|
63
|
+
const line=verify.split(/\r?\n/).find(value=>value.startsWith('VERIFIED_TARBALL '));
|
|
64
|
+
let artifact;try{artifact=JSON.parse(line.slice('VERIFIED_TARBALL '.length));}catch{required(false,'tarball evidence malformed');}
|
|
65
|
+
required(artifact.filename==='deadbyte-mcp-0.15.66.tgz'&&artifact.sha256===closure.tarball_sha256&&
|
|
66
|
+
artifact.integrity===closure.tarball_integrity&&artifact.shasum===closure.tarball_shasum&&artifact.bytes>0,
|
|
67
|
+
'tarball identity mismatch');
|
|
68
|
+
return Object.freeze({status:'R168_RELEASE_READY_EVIDENCE_BOUND',manifest_sha256:closure.manifest_sha256,
|
|
69
|
+
tarball_sha256:artifact.sha256,tarball_integrity:artifact.integrity,tarball_shasum:artifact.shasum,tests,passes,skips});
|
|
70
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Standalone R168 *read-only* diagnostic. Never signs/releases activation locks.
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {createHash} from 'node:crypto';
|
|
5
|
+
import {readFile} from 'node:fs/promises';
|
|
6
|
+
import {inspectR168UnknownWithoutMutation} from '../src/activation-reconciliation-audit-r168.mjs';
|
|
7
|
+
|
|
8
|
+
const auditModule=new URL('../src/activation-reconciliation-audit-r168.mjs',import.meta.url);
|
|
9
|
+
const expectedModuleHash='467268f6f195e9359b819e35c8b418c81e1cb40c9ba04b990fd2656e354c65a5';
|
|
10
|
+
const state=path.join(os.homedir(),'.deadbyte-mcp');
|
|
11
|
+
try{
|
|
12
|
+
const actualModuleHash=createHash('sha256').update(await readFile(auditModule)).digest('hex');
|
|
13
|
+
if(actualModuleHash!==expectedModuleHash)throw new Error('R168 audit module source hash changed');
|
|
14
|
+
const result=await inspectR168UnknownWithoutMutation({
|
|
15
|
+
stateRoot:state,
|
|
16
|
+
expectedOperationId:'c84d7ffa4eee80a2f467acda3ddd9332',
|
|
17
|
+
expectedSubjectSha256:'fba80e0128da9657dcbfcaa27d3a4dc8d9d8939146e81a097293fa5fcb6ae8fc',
|
|
18
|
+
expectedOutcomeFilename:'r65-activation-outcome-1792993876612-cc7c498632c7757d.json',
|
|
19
|
+
expectedContinuityId:'r65-ef42bc84451ce024c4dee736d4047b36',
|
|
20
|
+
expectedPredecessorReleaseId:'v0.15.61-055d1ff0aa99e60b',
|
|
21
|
+
expectedPredecessorManifestSha256:'055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf',
|
|
22
|
+
expectedPredecessorGenerationSha256:'f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4',
|
|
23
|
+
expectedCandidateManifestSha256:'ad3c091fbbe2bac6d234548724008b67305fd483ad6542f92d5aeb1aa84f5849'
|
|
24
|
+
});
|
|
25
|
+
console.log(JSON.stringify(result,null,2));
|
|
26
|
+
console.log('R168_NO_MUTATION_DIAGNOSTIC=OBSERVED_NOT_SIGNED');
|
|
27
|
+
console.log('R168_ACTIVATION_CLAIM=UNCHANGED');
|
|
28
|
+
console.log('R168_PRODUCTION_ACTIVATION=NOT_RUN');
|
|
29
|
+
}catch(error){
|
|
30
|
+
console.error('R168_READONLY_AUDIT=BLOCKED '+String(error?.message??error));
|
|
31
|
+
process.exitCode=1;
|
|
32
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// R168 read-only exact registry preflight. No npm publish here.
|
|
2
|
+
import {spawnSync} from 'node:child_process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
const cli=path.join(path.dirname(process.execPath),'node_modules','npm','bin','npm-cli.js');
|
|
5
|
+
const calls=[
|
|
6
|
+
['view','deadbyte-mcp@0.15.66','version','--json'],
|
|
7
|
+
['view','deadbyte-mcp','dist-tags.latest','--json'],
|
|
8
|
+
['whoami']
|
|
9
|
+
];
|
|
10
|
+
for(const args of calls){
|
|
11
|
+
const r=spawnSync(process.execPath,[cli,...args],{
|
|
12
|
+
shell:false,windowsHide:true,encoding:'utf8',timeout:60000,maxBuffer:1024*1024
|
|
13
|
+
});
|
|
14
|
+
console.log(JSON.stringify({args:args.slice(0,3),exit:r.status,
|
|
15
|
+
stdout:String(r.stdout??'').trim().slice(0,250),
|
|
16
|
+
stderr:String(r.stderr??'').trim().slice(0,250)}));
|
|
17
|
+
if(r.error)throw r.error;
|
|
18
|
+
if(r.status!==0&&args[0]!=='view')process.exitCode=1;
|
|
19
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// R168 privileged preflight; read-only. Never invokes native CAS or changes grants.
|
|
2
|
+
// This code must be part of a signed manifest in a protected Program Files install.
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {readFile} from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {fileURLToPath} from 'node:url';
|
|
7
|
+
import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
|
|
8
|
+
import {inspectTrustedInstallation,privilegedEpochInstallFiles} from '../src/privileged-install-trust-r89.mjs';
|
|
9
|
+
|
|
10
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
11
|
+
const version='0.15.66';
|
|
12
|
+
const expectedTrain='R168';
|
|
13
|
+
const mandatory=['scripts/r168-protected-preflight.mjs',
|
|
14
|
+
'src/activation-reconciliation-journal-r168.mjs',
|
|
15
|
+
'src/activation-reconciliation-consent-r168.mjs',
|
|
16
|
+
'src/activation-reconciliation-terminal-r168.mjs',
|
|
17
|
+
'src/activation-reconciliation-fence-r168.mjs',
|
|
18
|
+
'src/activation-reconciliation-audit-r168.mjs',
|
|
19
|
+
'src/activation-reconciliation-receipt-store-r168.mjs'];
|
|
20
|
+
function denied(reason){throw new Error('R168_'+reason);}
|
|
21
|
+
function actualProtectedRoot(){
|
|
22
|
+
const trusted='C:\\Program Files';
|
|
23
|
+
const rel=path.win32.relative(trusted,root);
|
|
24
|
+
if(!rel||rel==='..'||rel.startsWith('..'+path.win32.sep)||path.win32.isAbsolute(rel))
|
|
25
|
+
denied('ROOT_UNPROTECTED');
|
|
26
|
+
return root;
|
|
27
|
+
}
|
|
28
|
+
async function main(){
|
|
29
|
+
if(process.platform!=='win32'||process.arch!=='x64')
|
|
30
|
+
denied('WINDOWS_X64_REQUIRED');
|
|
31
|
+
if(process.argv.length!==2)denied('UNKNOWN_OPTION');
|
|
32
|
+
const verifiedRoot=actualProtectedRoot();
|
|
33
|
+
const packageInfo=JSON.parse(await readFile(path.join(verifiedRoot,'package.json'),'utf8'));
|
|
34
|
+
const release=JSON.parse(await readFile(path.join(verifiedRoot,'RELEASE.json'),'utf8'));
|
|
35
|
+
if(packageInfo.name!=='deadbyte-mcp'||packageInfo.version!==version||
|
|
36
|
+
release.name!=='deadbyte-mcp'||release.version!==version||release.train!==expectedTrain)
|
|
37
|
+
denied('VERSION_DRIFT');
|
|
38
|
+
const manifest=await readFile(path.join(verifiedRoot,'MANIFEST.SHA256'));
|
|
39
|
+
const entries=parseManifest(manifest.toString('utf8'));
|
|
40
|
+
for(const name of mandatory){
|
|
41
|
+
if(!entries.has(name))denied('R168_SOURCE_NOT_IN_MANIFEST');
|
|
42
|
+
}
|
|
43
|
+
// The installed package is checked against its complete manifest, not a
|
|
44
|
+
// handpicked critical-file subset or a writable development source root.
|
|
45
|
+
await verifyManifestTreeExact(verifiedRoot,entries,{allowedExtraPaths:['.npmignore','package-lock.json']});
|
|
46
|
+
const important=[
|
|
47
|
+
...privilegedEpochInstallFiles(verifiedRoot,process.execPath),
|
|
48
|
+
...mandatory.map(rel=>path.win32.join(verifiedRoot,...rel.split('/'))),
|
|
49
|
+
path.win32.join(verifiedRoot,'MANIFEST.SHA256'),
|
|
50
|
+
path.win32.join(verifiedRoot,'RELEASE.json'),
|
|
51
|
+
path.win32.join(verifiedRoot,'package.json')
|
|
52
|
+
];
|
|
53
|
+
// OS ACL trust is checked *after* content verification, under the protected
|
|
54
|
+
// root. No user-controlled environment may override this inventory.
|
|
55
|
+
await inspectTrustedInstallation(important);
|
|
56
|
+
const hash=createHash('sha256').update(manifest).digest('hex');
|
|
57
|
+
process.stdout.write(JSON.stringify({status:'R168_PROTECTED_PREFLIGHT_READONLY_PASS',
|
|
58
|
+
version,train:expectedTrain,manifest_sha256:hash,files:entries.size,
|
|
59
|
+
terminalization_authorized:false,activation_authorized:false})+'\n');
|
|
60
|
+
}
|
|
61
|
+
try{await main();}
|
|
62
|
+
catch(error){
|
|
63
|
+
process.stderr.write('R168_PROTECTED_PREFLIGHT=BLOCKED '+String(error?.message??error)+'\n');
|
|
64
|
+
process.exitCode=1;
|
|
65
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// R168 bounded read-only scan of current version-bound source.
|
|
2
|
+
import {readdir,readFile,lstat} from 'node:fs/promises';
|
|
3
|
+
import {fileURLToPath} from 'node:url';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
const root=fileURLToPath(new URL('..',import.meta.url));
|
|
6
|
+
let count=0, results=[];
|
|
7
|
+
const scopes=['test','src','scripts','controller','bridge'];
|
|
8
|
+
for(const scope of scopes){
|
|
9
|
+
const visit=async(dir,depth)=>{
|
|
10
|
+
if(depth>14)throw Error('scan depth exceeded');
|
|
11
|
+
const entries=await readdir(dir,{withFileTypes:true});
|
|
12
|
+
if(entries.length>3000)throw Error('inventory exceeds budget');
|
|
13
|
+
for(const e of entries){
|
|
14
|
+
if(e.isSymbolicLink())throw Error('symlink in bounded scan');
|
|
15
|
+
const file=path.join(dir,e.name),rel=path.relative(root,file).replaceAll('\\','/');
|
|
16
|
+
if(e.isDirectory()) {if(!['node_modules','.git','tmp'].includes(e.name))await visit(file,depth+1);continue}
|
|
17
|
+
if(!e.isFile()||!/(\.mjs|\.ps1|\.json)$/.test(e.name))continue;
|
|
18
|
+
const stats=await lstat(file);if(stats.size>300000)continue;
|
|
19
|
+
const text=await readFile(file,'utf8');
|
|
20
|
+
const lines=text.split(/\r?\n/);
|
|
21
|
+
const matches=lines.map((s,j)=>({line:j+1,s})).filter(q=>/0\.15\.65|R167.*version|R167_CANDIDATE_VERSION_DRIFT|r167:0\.15\.65|R167_STAGE_PASS=/.test(q.s));
|
|
22
|
+
if(matches.length)results.push({file:rel,lines:matches.slice(0,7)});
|
|
23
|
+
count++;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
await visit(path.join(root,scope),0);
|
|
27
|
+
}
|
|
28
|
+
console.log(JSON.stringify({scannedFiles:count,matchedFiles:results.length,results},null,2));
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// R168 Windows unprotected SOURCE gate. No publication, activation or claims.
|
|
2
|
+
import {spawnSync} from 'node:child_process';
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {closeSync,fsyncSync,openSync} from 'node:fs';
|
|
5
|
+
import {mkdir,readFile,writeFile} from 'node:fs/promises';
|
|
6
|
+
import {fileURLToPath} from 'node:url';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
|
|
9
|
+
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
10
|
+
const outDir=path.resolve(root,'..','..','testing','r168-source-full-20261026-fix1');
|
|
11
|
+
const manifestBytes=await readFile(path.join(root,'MANIFEST.SHA256'));
|
|
12
|
+
const manifestHash=createHash('sha256').update(manifestBytes).digest('hex');
|
|
13
|
+
const fileCount=manifestBytes.toString('utf8').trimEnd().split(/\r?\n/).length;
|
|
14
|
+
const digest=b=>createHash('sha256').update(b).digest('hex');
|
|
15
|
+
const release=JSON.parse(await readFile(path.join(root,'RELEASE.json'),'utf8'));
|
|
16
|
+
if(process.platform!=='win32'||release.version!=='0.15.66'||release.train!=='R168')
|
|
17
|
+
throw new Error('R168 Windows source release identity mismatch');
|
|
18
|
+
const manifestCheck=spawnSync(process.execPath,[path.join(root,'scripts','release-manifest.mjs'),'verify'],{cwd:root,shell:false,windowsHide:true,encoding:'utf8',timeout:180000});
|
|
19
|
+
if(manifestCheck.status!==0)throw new Error('R168 manifest verify failed '+String(manifestCheck.stderr??manifestCheck.stdout).slice(0,1200));
|
|
20
|
+
await mkdir(outDir,{recursive:true});
|
|
21
|
+
const stdoutPath=path.join(outDir,'windows-full.stdout.log');
|
|
22
|
+
const stderrPath=path.join(outDir,'windows-full.stderr.log');
|
|
23
|
+
let o=null,e=null;
|
|
24
|
+
try{
|
|
25
|
+
o=openSync(stdoutPath,'wx');e=openSync(stderrPath,'wx');
|
|
26
|
+
const cross=path.join(root,'..','..','testing','r168-crossvolume-scratch');
|
|
27
|
+
await mkdir(cross,{recursive:true});
|
|
28
|
+
const result=spawnSync(process.execPath,
|
|
29
|
+
['--test','--test-reporter=tap','--test-concurrency=4','test/*.test.mjs'],{
|
|
30
|
+
cwd:root,shell:false,windowsHide:true,stdio:['ignore',o,e],
|
|
31
|
+
timeout:480000,env:{...process.env,DEADBYTE_TEST_CROSS_VOLUME_ROOT:cross}
|
|
32
|
+
});
|
|
33
|
+
fsyncSync(o);fsyncSync(e);
|
|
34
|
+
closeSync(o);closeSync(e);o=e=null;
|
|
35
|
+
const full=await readFile(stdoutPath,'utf8');
|
|
36
|
+
const match=[...full.matchAll(/^# tests (\d+)\s*\r?\n# suites (\d+)\s*\r?\n# pass (\d+)\s*\r?\n# fail (\d+)\s*\r?\n# cancelled (\d+)\s*\r?\n# skipped (\d+)/gm)].at(-1);
|
|
37
|
+
const summary={status:result.status===0?'SOURCE_FULL_PASS':'SOURCE_FULL_FAIL',
|
|
38
|
+
exit_code:result.status,terminated_signal:result.signal??null,
|
|
39
|
+
files_checked:fileCount,manifest_sha256:manifestHash,
|
|
40
|
+
tests:match?Number(match[1]):null,passes:match?Number(match[3]):null,
|
|
41
|
+
failures:match?Number(match[4]):null,skipped:match?Number(match[6]):null,
|
|
42
|
+
stdout_sha256:digest(await readFile(stdoutPath)),stderr_sha256:digest(await readFile(stderrPath))};
|
|
43
|
+
console.log(JSON.stringify(summary,null,2));
|
|
44
|
+
if(result.error)throw result.error;
|
|
45
|
+
if(result.status!==0)process.exitCode=1;
|
|
46
|
+
}catch(error){if(o!==null)closeSync(o);if(e!==null)closeSync(e);throw error;}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// R168 read-only subject preparation. No signing keys or activation mutation.
|
|
2
|
+
import {readFile} from 'node:fs/promises';
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {readPinnedEpochLockSnapshot} from '../src/foreground-marker-cas-win32.mjs';
|
|
7
|
+
import {inspectR168UnknownWithoutMutation} from '../src/activation-reconciliation-audit-r168.mjs';
|
|
8
|
+
import {createR168ReconciliationSubject,makeR168ConsentChallenge} from '../src/activation-reconciliation-consent-r168.mjs';
|
|
9
|
+
|
|
10
|
+
const sha=b=>createHash('sha256').update(b).digest('hex');
|
|
11
|
+
const sourceExpected={
|
|
12
|
+
'activation-reconciliation-audit-r168.mjs':'467268f6f195e9359b819e35c8b418c81e1cb40c9ba04b990fd2656e354c65a5',
|
|
13
|
+
'activation-reconciliation-consent-r168.mjs':'a82b05e1c521cc77f3398985d6faa1beb0fe8f65595f50bed474227550d462c5'
|
|
14
|
+
};
|
|
15
|
+
const stateRoot=path.join(os.homedir(),'.deadbyte-mcp');
|
|
16
|
+
const lockPath=path.join(stateRoot,'evidence','updates','r65-activation.lock');
|
|
17
|
+
try {
|
|
18
|
+
if(process.platform!=='win32')throw new Error('Windows-only R168 subject');
|
|
19
|
+
for(const [name,expected] of Object.entries(sourceExpected)){
|
|
20
|
+
const bytes=await readFile(new URL('../src/'+name,import.meta.url));
|
|
21
|
+
if(sha(bytes)!==expected)throw new Error('R168 source identity changed: '+name);
|
|
22
|
+
}
|
|
23
|
+
const pinnedBefore=readPinnedEpochLockSnapshot(lockPath);
|
|
24
|
+
if(!pinnedBefore)throw new Error('old claim missing');
|
|
25
|
+
const observation=await inspectR168UnknownWithoutMutation({
|
|
26
|
+
stateRoot,
|
|
27
|
+
expectedOperationId:'c84d7ffa4eee80a2f467acda3ddd9332',
|
|
28
|
+
expectedSubjectSha256:'fba80e0128da9657dcbfcaa27d3a4dc8d9d8939146e81a097293fa5fcb6ae8fc',
|
|
29
|
+
expectedOutcomeFilename:'r65-activation-outcome-1792993876612-cc7c498632c7757d.json',
|
|
30
|
+
expectedContinuityId:'r65-ef42bc84451ce024c4dee736d4047b36',
|
|
31
|
+
expectedPredecessorReleaseId:'v0.15.61-055d1ff0aa99e60b',
|
|
32
|
+
expectedPredecessorManifestSha256:'055d1ff0aa99e60bbf9e9f136b02ebd4fffb8391712560f7607cff30af94dcdf',
|
|
33
|
+
expectedPredecessorGenerationSha256:'f940e794daac2dda097a66301c4b85c4b29a2df7c669e549503c05a2a55ff6c4',
|
|
34
|
+
expectedCandidateManifestSha256:'ad3c091fbbe2bac6d234548724008b67305fd483ad6542f92d5aeb1aa84f5849'
|
|
35
|
+
});
|
|
36
|
+
const pinnedAfter=readPinnedEpochLockSnapshot(lockPath);
|
|
37
|
+
if(!pinnedAfter||pinnedAfter.sha256!==pinnedBefore.sha256||pinnedAfter.fileId!==pinnedBefore.fileId)
|
|
38
|
+
throw new Error('R168 lock changed during subject derivation');
|
|
39
|
+
const subject=createR168ReconciliationSubject({observation,claimSnapshot:pinnedBefore,stateRoot});
|
|
40
|
+
console.log(JSON.stringify({status:'R168_SUBJECT_READY_READONLY',claim_operation_id:subject.claim_operation_id,
|
|
41
|
+
observation_sha256:observation.observation_sha256,subject_sha256:subject.subject_sha256,
|
|
42
|
+
native_claim_sha256:pinnedBefore.sha256,native_claim_file_id:pinnedBefore.fileId,
|
|
43
|
+
verified_signed_events:observation.signed_event_count,
|
|
44
|
+
approval_challenge:makeR168ConsentChallenge(subject),
|
|
45
|
+
owner_signature:'NOT_PRESENT',terminalization_committed:false,activation_authorized:false},null,2));
|
|
46
|
+
}catch(error){
|
|
47
|
+
console.error('R168_SUBJECT_READONLY_BLOCKED '+String(error?.message??error));
|
|
48
|
+
process.exitCode=1;
|
|
49
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// R172 privileged preflight; read-only. Never invokes native CAS or changes grants.
|
|
2
|
+
import {createHash} from 'node:crypto';
|
|
3
|
+
import {readFile} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
|
|
7
|
+
import {inspectTrustedInstallation,privilegedEpochInstallFiles} from '../src/privileged-install-trust-r89.mjs';
|
|
8
|
+
|
|
9
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
10
|
+
const version='0.15.67',expectedTrain='R172';
|
|
11
|
+
const mandatory=['scripts/r172-protected-preflight.mjs','src/activation-continuity-abort-r172.mjs',
|
|
12
|
+
'src/activation-lock-r65.mjs','src/activation-preflight-r65.mjs',
|
|
13
|
+
'src/activation-reconciliation-journal-r168.mjs','src/activation-reconciliation-terminal-r168.mjs',
|
|
14
|
+
'src/activation-reconciliation-fence-r168.mjs','src/activation-reconciliation-audit-r168.mjs'];
|
|
15
|
+
function denied(reason){throw new Error('R172_'+reason);}
|
|
16
|
+
function actualProtectedRoot(){
|
|
17
|
+
const trusted='C:\\Program Files',rel=path.win32.relative(trusted,root);
|
|
18
|
+
if(!rel||rel==='..'||rel.startsWith('..'+path.win32.sep)||path.win32.isAbsolute(rel))denied('ROOT_UNPROTECTED');
|
|
19
|
+
return root;
|
|
20
|
+
}
|
|
21
|
+
async function main(){
|
|
22
|
+
if(process.platform!=='win32'||process.arch!=='x64')denied('WINDOWS_X64_REQUIRED');
|
|
23
|
+
if(process.argv.length!==2)denied('UNKNOWN_OPTION');
|
|
24
|
+
const verifiedRoot=actualProtectedRoot();
|
|
25
|
+
const pkg=JSON.parse(await readFile(path.join(verifiedRoot,'package.json'),'utf8'));
|
|
26
|
+
const release=JSON.parse(await readFile(path.join(verifiedRoot,'RELEASE.json'),'utf8'));
|
|
27
|
+
if(pkg.name!=='deadbyte-mcp'||pkg.version!==version||release.name!=='deadbyte-mcp'||
|
|
28
|
+
release.version!==version||release.train!==expectedTrain)denied('VERSION_DRIFT');
|
|
29
|
+
const manifest=await readFile(path.join(verifiedRoot,'MANIFEST.SHA256'));
|
|
30
|
+
const entries=parseManifest(manifest.toString('utf8'));
|
|
31
|
+
for(const name of mandatory)if(!entries.has(name))denied('SOURCE_NOT_IN_MANIFEST');
|
|
32
|
+
await verifyManifestTreeExact(verifiedRoot,entries,{allowedExtraPaths:['.npmignore','package-lock.json']});
|
|
33
|
+
const important=[...privilegedEpochInstallFiles(verifiedRoot,process.execPath),
|
|
34
|
+
...mandatory.map(rel=>path.win32.join(verifiedRoot,...rel.split('/'))),
|
|
35
|
+
path.win32.join(verifiedRoot,'MANIFEST.SHA256'),path.win32.join(verifiedRoot,'RELEASE.json'),
|
|
36
|
+
path.win32.join(verifiedRoot,'package.json')];
|
|
37
|
+
await inspectTrustedInstallation(important);
|
|
38
|
+
const hash=createHash('sha256').update(manifest).digest('hex');
|
|
39
|
+
process.stdout.write(JSON.stringify({status:'R172_PROTECTED_PREFLIGHT_READONLY_PASS',version,train:expectedTrain,
|
|
40
|
+
manifest_sha256:hash,files:entries.size,terminalization_authorized:false,activation_authorized:false})+'\n');
|
|
41
|
+
}
|
|
42
|
+
try{await main();}catch(error){process.stderr.write('R172_PROTECTED_PREFLIGHT=BLOCKED '+String(error?.message??error)+'\n');process.exitCode=1;}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {createHash} from 'node:crypto';
|
|
2
|
+
import {lstat, readFile, readdir} from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {createAuthorityEpochStore} from './authority-continuity-r65.mjs';
|
|
5
|
+
|
|
6
|
+
const PLANES = Object.freeze(['process', 'coding', 'autonomous', 'desktop', 'host']);
|
|
7
|
+
const GRANT_FIELD = Object.freeze({
|
|
8
|
+
process: 'grant_file', coding: 'lease_file', autonomous: 'lease_file',
|
|
9
|
+
desktop: 'grant_file', host: 'arm_file'
|
|
10
|
+
});
|
|
11
|
+
const HEX64 = /^[0-9a-f]{64}$/u;
|
|
12
|
+
const CONTINUITY = /^r65-[0-9a-f]{32}$/u;
|
|
13
|
+
const sha = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
14
|
+
|
|
15
|
+
function parse(bytes, label) {
|
|
16
|
+
let text = bytes.toString('utf8');
|
|
17
|
+
if (text.charCodeAt(0) === 0xfeff) text = text.slice(1);
|
|
18
|
+
try { return JSON.parse(text); }
|
|
19
|
+
catch { throw new Error(`R172 ${label} JSON invalid`); }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function inside(root, target) {
|
|
23
|
+
const relative = path.relative(path.resolve(root), path.resolve(target));
|
|
24
|
+
return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function optionalBytes(file) {
|
|
28
|
+
try { return await readFile(file); }
|
|
29
|
+
catch (error) { if (error?.code === 'ENOENT') return null; throw error; }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function verifyR172PredispatchContinuityAbort({outcome, lock, stateRoot} = {}) {
|
|
33
|
+
if (!outcome || !lock || typeof stateRoot !== 'string' || !path.isAbsolute(stateRoot)) return false;
|
|
34
|
+
if (outcome.schema !== 'deadbyte.r65-activation-outcome.v1' ||
|
|
35
|
+
outcome.claim_operation_id !== lock.operation_id ||
|
|
36
|
+
outcome.preflight_subject_sha256 !== lock.subject_sha256 ||
|
|
37
|
+
outcome.outcome?.state !== 'ACTIVATION_OUTCOME_UNKNOWN' ||
|
|
38
|
+
outcome.outcome?.preflight !== 'AUTHORITY_UNVERIFIED' ||
|
|
39
|
+
outcome.update_id !== null || outcome.state_path !== null ||
|
|
40
|
+
!CONTINUITY.test(String(outcome.continuity_update_id ?? '')) ||
|
|
41
|
+
!HEX64.test(String(outcome.candidate_manifest_sha256 ?? '')) ||
|
|
42
|
+
typeof outcome.candidate_version !== 'string' || typeof outcome.predecessor_release_id !== 'string') return false;
|
|
43
|
+
|
|
44
|
+
const created = Date.parse(lock.created_at_utc), observed = Date.parse(outcome.observed_at_utc);
|
|
45
|
+
if (!Number.isFinite(created) || !Number.isFinite(observed) || observed < created || observed - created > 120_000) return false;
|
|
46
|
+
const state = path.resolve(stateRoot), pointerBytes = await readFile(path.join(state, 'active-release.json'));
|
|
47
|
+
const pointer = parse(pointerBytes, 'active pointer');
|
|
48
|
+
if (pointer.release_id !== outcome.predecessor_release_id ||
|
|
49
|
+
!HEX64.test(String(pointer.manifest_sha256 ?? '')) ||
|
|
50
|
+
!HEX64.test(String(pointer.generation_manifest_sha256 ?? ''))) return false;
|
|
51
|
+
const generation = path.resolve(String(pointer.generation_root ?? ''));
|
|
52
|
+
if (!inside(path.join(state, 'generations'), generation)) return false;
|
|
53
|
+
|
|
54
|
+
const updates = path.join(state, 'evidence', 'updates');
|
|
55
|
+
const candidatePrefix = `r27-${outcome.candidate_manifest_sha256.slice(0, 16)}-`;
|
|
56
|
+
const updateEntries = await readdir(updates, {withFileTypes: true});
|
|
57
|
+
if (updateEntries.some(entry => entry.name.startsWith(candidatePrefix))) return false;
|
|
58
|
+
|
|
59
|
+
const runtime = path.join(state, 'runtime');
|
|
60
|
+
const store = createAuthorityEpochStore({runtimeDir: runtime});
|
|
61
|
+
const revoked = new Map();
|
|
62
|
+
for (const plane of PLANES) {
|
|
63
|
+
const audit = await store.auditHistory({plane});
|
|
64
|
+
if (!['PASS', 'EMPTY'].includes(audit.status)) return false;
|
|
65
|
+
const eventsDir = path.join(runtime, 'authority-epochs', plane, 'events');
|
|
66
|
+
let entries;
|
|
67
|
+
try { entries = await readdir(eventsDir, {withFileTypes: true}); }
|
|
68
|
+
catch (error) { if (error?.code === 'ENOENT' && audit.status === 'EMPTY') entries = []; else throw error; }
|
|
69
|
+
if (entries.length !== audit.verified_events || entries.length > 10_000) return false;
|
|
70
|
+
for (const entry of entries) {
|
|
71
|
+
if (!entry.isFile() || entry.isSymbolicLink()) return false;
|
|
72
|
+
const match = /^(\d{16})-([0-9a-f]{64})\.json$/u.exec(entry.name);
|
|
73
|
+
if (!match) return false;
|
|
74
|
+
const bytes = await readFile(path.join(eventsDir, entry.name));
|
|
75
|
+
if (sha(bytes) !== match[2]) return false;
|
|
76
|
+
const event = parse(bytes, `${plane} epoch event`);
|
|
77
|
+
if (event.update_id !== outcome.continuity_update_id) continue;
|
|
78
|
+
const when = Date.parse(event.recorded_at_utc);
|
|
79
|
+
if (event.plane !== plane || event.action !== 'transaction_revoke' ||
|
|
80
|
+
!Number.isFinite(when) || when < created || when > observed || revoked.has(plane)) return false;
|
|
81
|
+
revoked.set(plane, event);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (revoked.size < 1) return false;
|
|
85
|
+
|
|
86
|
+
for (const [plane, event] of revoked) {
|
|
87
|
+
const policyFile = path.join(generation, `${plane}-policy.json`);
|
|
88
|
+
const policyBytes = await readFile(policyFile), policy = parse(policyBytes, `${plane} policy`);
|
|
89
|
+
const grantFile = path.resolve(String(policy?.[GRANT_FIELD[plane]] ?? ''));
|
|
90
|
+
if (!inside(runtime, grantFile)) return false;
|
|
91
|
+
const planeStore = createAuthorityEpochStore({runtimeDir: runtime, policyBytes});
|
|
92
|
+
const head = await planeStore.readHead({plane});
|
|
93
|
+
if (!head || head.seq <= event.seq || head.previous_event_sha256 === null) return false;
|
|
94
|
+
if (head.action === 'user_revoke') {
|
|
95
|
+
if (await optionalBytes(grantFile) !== null) return false;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (!['grant', 'transaction_restore'].includes(head.action)) return false;
|
|
99
|
+
const grantBytes = await optionalBytes(grantFile);
|
|
100
|
+
if (!grantBytes) return false;
|
|
101
|
+
const grant = parse(grantBytes, `${plane} grant`);
|
|
102
|
+
const verified = await planeStore.verifyGrant({
|
|
103
|
+
plane, grantBytes, grantInstanceNonce: grant.instance_nonce, policyBytes
|
|
104
|
+
});
|
|
105
|
+
if (verified.trusted !== true || verified.event_sha256 !== head.event_sha256) return false;
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
@@ -2,6 +2,7 @@ import {createPublicKey,randomBytes} from 'node:crypto';
|
|
|
2
2
|
import {mkdir,open,readFile,readdir,unlink} from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import {verifySlotUpdateEnvelope} from './runtime-update.mjs';
|
|
5
|
+
import {verifyR172PredispatchContinuityAbort} from './activation-continuity-abort-r172.mjs';
|
|
5
6
|
|
|
6
7
|
const HEX64=/^[0-9a-f]{64}$/;
|
|
7
8
|
const OP=/^[0-9a-f]{32}$/;
|
|
@@ -130,8 +131,11 @@ async function findTerminalOutcome(updatesDir,lock,stateRoot){
|
|
|
130
131
|
catch{continue;}
|
|
131
132
|
if(value?.claim_operation_id!==lock.operation_id)continue;
|
|
132
133
|
if(value?.preflight_subject_sha256!==lock.subject_sha256){foreign=true;continue;}
|
|
134
|
+
let continuityAbort=false;
|
|
135
|
+
try{continuityAbort=await verifyR172PredispatchContinuityAbort({outcome:value,lock,stateRoot});}
|
|
136
|
+
catch{continuityAbort=false;}
|
|
133
137
|
if(isSafePreMutationReject(value,lock)||await isSafeTerminalActivation(value,lock,stateRoot)||
|
|
134
|
-
await isSafeSignedPreconditionReject(value,lock,stateRoot))matched=true;
|
|
138
|
+
await isSafeSignedPreconditionReject(value,lock,stateRoot)||continuityAbort)matched=true;
|
|
135
139
|
else unknown=true;
|
|
136
140
|
}
|
|
137
141
|
return {matched,foreign,unknown};
|