deadbyte-mcp 0.15.55 → 0.15.59
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 +36 -23
- package/PROVENANCE.json +11 -11
- package/README.txt +18 -18
- package/RELEASE.json +24 -8
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +2 -2
- package/bridge/package.json +1 -1
- package/package.json +1 -1
- package/scripts/bootstrap-uac-migration-r153.ps1 +232 -0
- package/scripts/deploy-r48.mjs +133 -11
- package/scripts/issue-uac-migration-r153.mjs +107 -0
- package/scripts/r153-prepublish-identity.mjs +57 -0
- package/scripts/request-uac-migration-r153.ps1 +165 -0
- package/scripts/verify-protected-stage-r153.mjs +20 -0
- package/src/activation-preflight-r65.mjs +2 -1
- package/src/clean-activation-recovery-r153.mjs +297 -0
- package/src/deadbyte-cli.mjs +925 -720
- package/src/foreground-process-identity.mjs +31 -0
- package/src/legacy-offline-policy-proof-r146.mjs +31 -10
- package/src/runtime-update.mjs +19 -4
- package/src/uac-approval-contract-r153.mjs +104 -0
- package/src/version.mjs +1 -1
- package/test/commander-protocol-r47.test.mjs +6 -1
- package/test/r130-release-security-contract.test.mjs +8 -7
- package/test/r132-release-identity.test.mjs +8 -7
- package/test/r146-legacy-offline-policy-proof.test.mjs +31 -1
- package/test/r153-clean-activation-recovery.test.mjs +213 -0
- package/test/r153-prepublish-identity.test.mjs +40 -0
- package/test/r153-restricted-reused-pid.test.mjs +31 -0
- package/test/r153-staging-leaf-acl.test.mjs +47 -0
- package/test/r153-uac-approval-contract.test.mjs +92 -0
- package/test/r153-uac-installed-manifest-contract.test.mjs +19 -0
- package/test/r153-uac-launch-parameter-contract.test.mjs +59 -0
- package/test/r58-concurrent-recovery.test.mjs +1 -1
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/r65-activation-orchestration.test.mjs +20 -0
- package/test/remote-session-cli.test.mjs +1 -1
- package/node_modules/which/CHANGELOG.md +0 -166
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {readFile} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
import {verifyR153Lineage,verifyR153IdentityTree} from '../scripts/r153-prepublish-identity.mjs';
|
|
7
|
+
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
|
8
|
+
const pkg=JSON.parse(await readFile(path.join(root,'package.json'),'utf8'));
|
|
9
|
+
const release=JSON.parse(await readFile(path.join(root,'RELEASE.json'),'utf8'));
|
|
10
|
+
const bridge=JSON.parse(await readFile(path.join(root,'bridge','package.json'),'utf8'));
|
|
11
|
+
const copy=()=>structuredClone({pkg,release,bridge});
|
|
12
|
+
test('R153 exact source and runtime lineage: metadata-only PASS',()=>{
|
|
13
|
+
assert.equal(verifyR153Lineage(copy()).status,'PASS');
|
|
14
|
+
});
|
|
15
|
+
test('R153 rejects original R74 source predecessor 0.15.46',()=>{
|
|
16
|
+
const state=copy();state.release.source_predecessor.version='0.15.46';
|
|
17
|
+
assert.throws(()=>verifyR153Lineage(state),/source predecessor version/);
|
|
18
|
+
});
|
|
19
|
+
test('R153 rejects source predecessor tarball hash drift',()=>{
|
|
20
|
+
const state=copy();state.release.source_predecessor.tarball_sha256='f'.repeat(64);
|
|
21
|
+
assert.throws(()=>verifyR153Lineage(state),/source predecessor tarball/);
|
|
22
|
+
});
|
|
23
|
+
test('R153 rejects runtime predecessor or parent drift',()=>{
|
|
24
|
+
const state=copy();state.release.parent.release_id='other';
|
|
25
|
+
assert.throws(()=>verifyR153Lineage(state),/parent is not exact/);
|
|
26
|
+
const state2=copy();state2.release.runtime_predecessor.version='0.15.46';
|
|
27
|
+
assert.throws(()=>verifyR153Lineage(state2),/runtime predecessor version/);
|
|
28
|
+
});
|
|
29
|
+
test('R153 rejects version / train / bridge identity drift',()=>{
|
|
30
|
+
const state=copy();state.pkg.version='0.15.56';
|
|
31
|
+
assert.throws(()=>verifyR153Lineage(state),/package version/);
|
|
32
|
+
const state2=copy();state2.release.train='R154';
|
|
33
|
+
assert.throws(()=>verifyR153Lineage(state2),/train/);
|
|
34
|
+
const state3=copy();state3.bridge.version='0.15.45';
|
|
35
|
+
assert.throws(()=>verifyR153Lineage(state3),/bridge version/);
|
|
36
|
+
});
|
|
37
|
+
test('R153 rejects tarball self-reference',()=>{
|
|
38
|
+
const state=copy();state.release.tarball_sha256='f'.repeat(64);
|
|
39
|
+
assert.throws(()=>verifyR153Lineage(state),/self-referential/);
|
|
40
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {classifyRestrictedReusedPid} from '../src/foreground-process-identity.mjs';
|
|
4
|
+
|
|
5
|
+
const expected=()=>({
|
|
6
|
+
schema:'deadbyte.foreground-process.v1',
|
|
7
|
+
pid:13328,parent_pid:8632,
|
|
8
|
+
creation_utc:'2026-10-23T16:03:48.4677330Z',
|
|
9
|
+
executable_path:'C:\\Program Files\\nodejs\\node.exe',
|
|
10
|
+
command_line_sha256:'a'.repeat(64)
|
|
11
|
+
});
|
|
12
|
+
const observed=()=>({
|
|
13
|
+
pid:13328,name:'svchost.exe',
|
|
14
|
+
creation_utc:'2026-10-24T02:51:48.0000000Z'
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('R153 definite later Windows service PID reuse can prove original foreground owner absent',()=>{
|
|
18
|
+
assert.equal(classifyRestrictedReusedPid(expected(),observed()),'REUSED');
|
|
19
|
+
});
|
|
20
|
+
test('R153 cannot use unchanged, older, or malformed process birth as negative proof',()=>{
|
|
21
|
+
assert.throws(()=>classifyRestrictedReusedPid(expected(),{...observed(),creation_utc:expected().creation_utc}),/unverified|ambiguous|unchanged/i);
|
|
22
|
+
assert.throws(()=>classifyRestrictedReusedPid(expected(),{...observed(),creation_utc:'2026-10-22T00:00:00Z'}),/unverified|ambiguous|unchanged/i);
|
|
23
|
+
assert.throws(()=>classifyRestrictedReusedPid(expected(),{...observed(),creation_utc:'not-a-date'}),/invalid|unverified/i);
|
|
24
|
+
assert.throws(()=>classifyRestrictedReusedPid(expected(),null),/unverified|invalid/i);
|
|
25
|
+
});
|
|
26
|
+
test('R153 negative evidence binds exact PID, expected Node path and narrow service class',()=>{
|
|
27
|
+
assert.throws(()=>classifyRestrictedReusedPid(expected(),{...observed(),pid:9999}),/PID|mismatch/i);
|
|
28
|
+
assert.throws(()=>classifyRestrictedReusedPid(expected(),{...observed(),name:'node.exe'}),/unverified|service/i);
|
|
29
|
+
assert.throws(()=>classifyRestrictedReusedPid(expected(),{...observed(),name:'explorer.exe'}),/unverified|service/i);
|
|
30
|
+
assert.throws(()=>classifyRestrictedReusedPid({...expected(),executable_path:'C:\\Windows\\System32\\cmd.exe'},observed()),/unverified|Node/i);
|
|
31
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {readFileSync} from 'node:fs';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
import {spawnSync} from 'node:child_process';
|
|
6
|
+
|
|
7
|
+
const bootstrap=readFileSync(fileURLToPath(new URL('../scripts/bootstrap-uac-migration-r153.ps1',import.meta.url)),'utf8');
|
|
8
|
+
|
|
9
|
+
test('R153 ACL hotfix grants direct leaf read only to Administrators and SYSTEM before rehash',()=>{
|
|
10
|
+
const start=bootstrap.indexOf("R153 protected staging final ACL normalization failed");
|
|
11
|
+
const repair=bootstrap.indexOf("'*S-1-5-32-544:(F)' '*S-1-5-18:(F)' '/T' '/C'");
|
|
12
|
+
const rehash=bootstrap.indexOf('R153 staged package post-ACL verification failed');
|
|
13
|
+
assert.ok(start>=0&&repair>start&&rehash>repair,'ACL repair must follow recursive inheritance stripping and precede independent rehash');
|
|
14
|
+
assert.match(bootstrap,/R153 protected staging leaf ACL restoration failed/);
|
|
15
|
+
assert.doesNotMatch(bootstrap.slice(start,rehash),/S-1-5-32-545:\(F\)|Everyone:\(F\)/);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('R153 Windows ACL reproduction rejects stripped leaf ACE and restores direct read', {skip:process.platform!=='win32'},()=>{
|
|
19
|
+
const ps=String.raw`$ErrorActionPreference = 'Stop'
|
|
20
|
+
$root = Join-Path $env:TEMP ('r153-native-acl-test-' + [guid]::NewGuid().ToString('N'))
|
|
21
|
+
$sid = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value
|
|
22
|
+
$leaf = Join-Path $root 'child\file.txt'
|
|
23
|
+
try {
|
|
24
|
+
New-Item -ItemType Directory -Path (Split-Path -Parent $leaf) -Force | Out-Null
|
|
25
|
+
[IO.File]::WriteAllText($leaf,'read-only regression fixture')
|
|
26
|
+
& icacls.exe $root '/inheritance:r' '/grant:r' ('*'+$sid+':(OI)(CI)(F)') '/T' '/C' | Out-Null
|
|
27
|
+
if($LASTEXITCODE -ne 0){throw 'REPRO_SETUP_FAILED'}
|
|
28
|
+
$blocked = Get-FileHash -LiteralPath $leaf -Algorithm SHA256 -ErrorAction SilentlyContinue
|
|
29
|
+
if($null -ne $blocked){throw 'REPRO_DID_NOT_STRIP_LEAF_ACE'}
|
|
30
|
+
& icacls.exe $root '/grant' ('*'+$sid+':(F)') '/T' '/C' | Out-Null
|
|
31
|
+
if($LASTEXITCODE -ne 0){throw 'LEAF_REPAIR_FAILED'}
|
|
32
|
+
$restored = Get-FileHash -LiteralPath $leaf -Algorithm SHA256
|
|
33
|
+
if($null -eq $restored -or $restored.Hash.Length -ne 64){throw 'LEAF_NOT_READABLE_AFTER_REPAIR'}
|
|
34
|
+
$acl = Get-Acl -LiteralPath $leaf
|
|
35
|
+
$allow=@($acl.Access | Where-Object {$_.AccessControlType -eq 'Allow'})
|
|
36
|
+
if($allow.Count -ne 1){throw 'LEAF_HAS_UNEXPECTED_ALLOW_ACE_COUNT'}
|
|
37
|
+
if($allow[0].IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value -ne $sid){throw 'LEAF_HAS_UNEXPECTED_TRUSTEE'}
|
|
38
|
+
Write-Output 'R153_WIN32_LEAF_ACL_REPAIR_PASS'
|
|
39
|
+
} finally {
|
|
40
|
+
if(Test-Path -LiteralPath $root){Remove-Item -LiteralPath $root -Force -Recurse}
|
|
41
|
+
}`;
|
|
42
|
+
const encoded=Buffer.from(ps,'utf16le').toString('base64');
|
|
43
|
+
const result=spawnSync('powershell.exe',['-NoLogo','-NoProfile','-NonInteractive','-EncodedCommand',encoded],
|
|
44
|
+
{encoding:'utf8',timeout:30000,maxBuffer:256*1024});
|
|
45
|
+
assert.equal(result.status,0,result.stderr||result.stdout||String(result.error));
|
|
46
|
+
assert.match(result.stdout,/R153_WIN32_LEAF_ACL_REPAIR_PASS/);
|
|
47
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createUacApproval,verifyUacApproval,validateApprovalWindow,requireTrustedUacInstallation,protectedUacReceiptPath} from '../src/uac-approval-contract-r153.mjs';
|
|
4
|
+
import {readFileSync} from 'node:fs';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
const h=(n)=>n.repeat(64);
|
|
7
|
+
const base=()=>({stateRoot:'C:\\Users\\DEADBYTE\\.deadbyte-mcp',candidateRoot:'C:\\Program Files\\DEADBYTE-R153-TEST',candidateVersion:'0.15.59',candidateManifestSha256:h('a'),tarballSha256:h('b'),markerReceiptSha256:h('c'),predecessorPointerSha256:h('d'),predecessorPolicySha256:{process:h('1'),coding:h('2'),autonomous:h('3'),desktop:h('4'),host:h('5')},preflightSubjectSha256:h('e'),nodeSha256:h('f'),deployScriptSha256:h('0'),nonce:h('6'),issuedAt:'2026-10-24T00:00:00.000Z',expiresAt:'2026-10-24T00:05:00.000Z'});
|
|
8
|
+
test('R153 UAC receipt carries exact no-grants narrow migration subject',()=>{
|
|
9
|
+
const x=base(),receipt=createUacApproval(x);
|
|
10
|
+
assert.equal(receipt.schema,'deadbyte.r153-uac-approval.v1');
|
|
11
|
+
assert.equal(receipt.action,'narrow_legacy_policy_and_activate');
|
|
12
|
+
assert.deepEqual(receipt.allowed_authority_after,{process:false,coding:false,autonomous:false,desktop:false,host:false});
|
|
13
|
+
assert.match(receipt.subject_sha256,/^[0-9a-f]{64}$/);
|
|
14
|
+
assert.deepEqual(verifyUacApproval(receipt,x,{now:'2026-10-24T00:02:00.000Z'}),receipt);
|
|
15
|
+
});
|
|
16
|
+
test('R153 UAC receipt rejects drift across every authority/candidate/runtime input',()=>{
|
|
17
|
+
const x=base(),receipt=createUacApproval(x);
|
|
18
|
+
for(const [key,value] of Object.entries({stateRoot:'C:\\Other',candidateRoot:'C:\\Program Files\\Other',candidateVersion:'0.15.56',candidateManifestSha256:h('f'),tarballSha256:h('f'),markerReceiptSha256:h('f'),predecessorPointerSha256:h('f'),preflightSubjectSha256:h('f'),nodeSha256:h('a'),deployScriptSha256:h('a'),nonce:h('a'),issuedAt:'2026-10-24T00:00:01.000Z',expiresAt:'2026-10-24T00:06:00.000Z'})){
|
|
19
|
+
assert.throws(()=>verifyUacApproval(receipt,{...x,[key]:value},{now:'2026-10-24T00:02:00.000Z'}),undefined,key);
|
|
20
|
+
}
|
|
21
|
+
assert.throws(()=>verifyUacApproval(receipt,{...x,predecessorPolicySha256:{...x.predecessorPolicySha256,host:h('a')}},{now:'2026-10-24T00:02:00.000Z'}));
|
|
22
|
+
});
|
|
23
|
+
test('R153 UAC receipt fails closed on expiration, malformed, replay-like tamper and grant widening',()=>{
|
|
24
|
+
const x=base(),receipt=createUacApproval(x);
|
|
25
|
+
assert.throws(()=>verifyUacApproval(receipt,x,{now:'2026-10-24T00:05:00.000Z'}),/expired|time/i);
|
|
26
|
+
assert.throws(()=>verifyUacApproval({...receipt,action:'grant_host'},x,{now:'2026-10-24T00:02:00.000Z'}));
|
|
27
|
+
assert.throws(()=>verifyUacApproval({...receipt,allowed_authority_after:{...receipt.allowed_authority_after,coding:true}},x,{now:'2026-10-24T00:02:00.000Z'}));
|
|
28
|
+
assert.throws(()=>verifyUacApproval({...receipt,nonce:h('a')},x,{now:'2026-10-24T00:02:00.000Z'}));
|
|
29
|
+
assert.throws(()=>verifyUacApproval({...receipt,surprise:'admin'},x,{now:'2026-10-24T00:02:00.000Z'}));
|
|
30
|
+
assert.throws(()=>createUacApproval({...x,expiresAt:'2026-10-24T00:12:00.000Z'}));
|
|
31
|
+
assert.throws(()=>createUacApproval({...x,issuedAt:'not-a-date'}));
|
|
32
|
+
assert.throws(()=>createUacApproval({...x,nonce:'x'}));
|
|
33
|
+
});
|
|
34
|
+
test('R153 UAC scope only accepts protected root and exact v0.15.59',()=>{
|
|
35
|
+
assert.throws(()=>createUacApproval({...base(),candidateRoot:'C:\\Users\\DEADBYTE\\npm-cache'}),/protected/i);
|
|
36
|
+
assert.throws(()=>createUacApproval({...base(),candidateVersion:'0.15.55'}),/candidate/i);
|
|
37
|
+
assert.throws(()=>validateApprovalWindow('2026-10-24T00:00:00.000Z','2026-10-24T00:00:00.000Z','2026-10-24T00:00:00.000Z'));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
test('R153 UAC protected installation requires explicit positive R89 verdict',()=>{
|
|
42
|
+
assert.deepEqual(requireTrustedUacInstallation({trusted:true,reason:'PROTECTED_INSTALLATION_ACL_SNAPSHOT'}),
|
|
43
|
+
{trusted:true,reason:'PROTECTED_INSTALLATION_ACL_SNAPSHOT'});
|
|
44
|
+
for(const result of [undefined,null,{}, {trusted:false,reason:'UNTRUSTED_WRITE_ACE'},
|
|
45
|
+
{trusted:'true'}, {trusted:0}, {trusted:undefined}]) {
|
|
46
|
+
assert.throws(()=>requireTrustedUacInstallation(result),/R89|protected|trust/i);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('R153 CLI delegates UAC only to exact-version protected bootstrap contract',()=>{
|
|
51
|
+
const cli=readFileSync(fileURLToPath(new URL('../src/deadbyte-cli.mjs',import.meta.url)),'utf8');
|
|
52
|
+
assert.match(cli,/UAC_BOOTSTRAP_SCRIPT/);
|
|
53
|
+
assert.match(cli,/'-ExpectedVersion',DEADBYTE_VERSION/);
|
|
54
|
+
assert.match(cli,/'-ExpectedTarballSha256',artifactSha256/);
|
|
55
|
+
assert.match(cli,/'-ExpectedManifestSha256',packagedManifestSha256/);
|
|
56
|
+
assert.match(cli,/'-ExpectedBootstrapSha256',bootstrapMatch\[1\]/);
|
|
57
|
+
assert.match(cli,/REGISTRY_IDENTITY_VERIFIED/);
|
|
58
|
+
assert.doesNotMatch(cli,/protected 0\.15\.56 installation required before UAC/);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
test('R153 approval lives next to, never inside, the exact candidate tree',()=>{
|
|
63
|
+
const nonce=h('6');
|
|
64
|
+
const candidate='C:\\Program Files\\DEADBYTE-R153-TEST';
|
|
65
|
+
const receipt=protectedUacReceiptPath(candidate,nonce);
|
|
66
|
+
assert.equal(receipt.toLowerCase(),`c:\\program files\\deadbyte-r153-test.r153-uac-approval-${nonce}.json`);
|
|
67
|
+
assert.equal(receipt.toLowerCase().startsWith(candidate.toLowerCase()+'\\'),false);
|
|
68
|
+
assert.equal(receipt.toLowerCase().startsWith('c:\\program files\\'),true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('R153 approval path rejects unprotected roots and malformed nonces',()=>{
|
|
72
|
+
for(const candidate of ['D:\\Build\\candidate','C:\\Users\\DEADBYTE\\candidate',
|
|
73
|
+
'C:\\Program Files','C:\\Program Files\\..\\Windows','\\\\server\\share\\candidate']){
|
|
74
|
+
assert.throws(()=>protectedUacReceiptPath(candidate,h('6')),/protected|candidate|path/i);
|
|
75
|
+
}
|
|
76
|
+
for(const nonce of ['bad',h('F'),h('6')+'.json',h('6').slice(1)]){
|
|
77
|
+
assert.throws(()=>protectedUacReceiptPath('C:\\Program Files\\candidate',nonce),/nonce/i);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
test('R153 UAC issuer and deploy share exact protected sidecar boundary',()=>{
|
|
83
|
+
const issuer=readFileSync(fileURLToPath(new URL('../scripts/issue-uac-migration-r153.mjs',import.meta.url)),'utf8');
|
|
84
|
+
const deploy=readFileSync(fileURLToPath(new URL('../scripts/deploy-r48.mjs',import.meta.url)),'utf8');
|
|
85
|
+
assert.match(issuer,/const file=protectedUacReceiptPath\(root,nonce\)/);
|
|
86
|
+
assert.match(issuer,/open\(file,'wx',0o600\)/);
|
|
87
|
+
assert.match(issuer,/ownerFix\.error\|\|ownerFix\.status!==0/);
|
|
88
|
+
assert.match(issuer,/inspectTrustedInstallation\(\[file\]\)/);
|
|
89
|
+
assert.match(deploy,/const expectedApprovalPath=protectedUacReceiptPath\(root,deployArgs\.migrationNonce\)/);
|
|
90
|
+
assert.match(deploy,/requireTrustedUacInstallation\(await inspectTrustedInstallation\(\[expectedApprovalPath\]\)\)/);
|
|
91
|
+
assert.match(deploy,/UAC approval receipt path not exact candidate sidecar/);
|
|
92
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {readFile} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
7
|
+
|
|
8
|
+
test('R153 privileged issuer uses installed-tree verification, never npm pack dry-run',async()=>{
|
|
9
|
+
const issuer=await readFile(path.join(root,'scripts','issue-uac-migration-r153.mjs'),'utf8');
|
|
10
|
+
assert.match(issuer,/const verifyManifest=spawnSync\(process\.execPath,\[path\.join\(root,'scripts','release-manifest\.mjs'\),'verify-installed'\]/);
|
|
11
|
+
assert.doesNotMatch(issuer,/\[path\.join\(root,'scripts','release-manifest\.mjs'\),'verify'\]/);
|
|
12
|
+
});
|
|
13
|
+
test('R153 installed verifier checks exact manifested tree and has no write path',async()=>{
|
|
14
|
+
const src=await readFile(path.join(root,'scripts','release-manifest.mjs'),'utf8');
|
|
15
|
+
assert.match(src,/else if \(command === 'verify-installed'\) await verifyInstalled\(\)/);
|
|
16
|
+
assert.match(src,/async function verifyInstalled\(\) \{/);
|
|
17
|
+
assert.match(src,/await verifyManifestTreeExact\(root, entries, \{/);
|
|
18
|
+
assert.match(src,/allowedExtraPaths:\['\.npmignore','package-lock\.json'\]/);
|
|
19
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {readFileSync} from 'node:fs';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
|
|
6
|
+
const source=()=>readFileSync(fileURLToPath(new URL('../scripts/request-uac-migration-r153.ps1',import.meta.url)),'utf8');
|
|
7
|
+
const bootstrap=()=>readFileSync(fileURLToPath(new URL('../scripts/bootstrap-uac-migration-r153.ps1',import.meta.url)),'utf8');
|
|
8
|
+
|
|
9
|
+
test('R153 UAC RunAs does not combine mutually exclusive redirection parameters',()=>{
|
|
10
|
+
const script=source();
|
|
11
|
+
const start=script.indexOf('$child = Start-Process');
|
|
12
|
+
assert.ok(start>=0,'RunAs launch must exist');
|
|
13
|
+
const end=script.indexOf('if (Test-Path -LiteralPath $StdoutPath)',start);
|
|
14
|
+
assert.ok(end>start,'RunAs launch must retain result reading');
|
|
15
|
+
const launch=script.slice(start,end);
|
|
16
|
+
assert.match(launch,/-Verb RunAs\b/);
|
|
17
|
+
assert.doesNotMatch(launch,/-RedirectStandard(Output|Error)\b/i,
|
|
18
|
+
'Windows PowerShell 5.1 -Verb RunAs cannot use RedirectStandard*');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('R153 elevated UAC logging is scoped and no-clobber',()=>{
|
|
22
|
+
const script=source();
|
|
23
|
+
assert.match(script,/UAC evidence paths must remain inside state legacy-migration evidence/);
|
|
24
|
+
assert.match(script,/UAC stderr evidence identity mismatch/);
|
|
25
|
+
assert.match(script,/evidence directory ancestry contains reparse point/);
|
|
26
|
+
assert.match(script,/\[IO\.FileMode\]::CreateNew/);
|
|
27
|
+
assert.match(script,/Write-NoClobberEvidence \$StdoutPath \$stdoutText/);
|
|
28
|
+
assert.match(script,/Write-NoClobberEvidence \$StderrPath \$stderrText/);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('R153 native failure stays terminal and success waits for deploy exit zero',()=>{
|
|
32
|
+
const script=source();
|
|
33
|
+
assert.match(script,/if \(\$issuerExit -ne 0\) \{ throw/);
|
|
34
|
+
assert.match(script,/if \(\$deployExit -ne 0\)/);
|
|
35
|
+
assert.match(script,/\$exitCode = 0/);
|
|
36
|
+
assert.match(script,/exit \$exitCode/);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('R153 RED: mutable npx entrypoint stages exact registry package before privileged migration',()=>{
|
|
40
|
+
const script=bootstrap();
|
|
41
|
+
assert.match(script,/ExpectedTarballSha256/);
|
|
42
|
+
assert.match(script,/ExpectedManifestSha256/);
|
|
43
|
+
assert.match(script,/ExpectedBootstrapSha256/);
|
|
44
|
+
assert.match(script,/System32\\tar\.exe/);
|
|
45
|
+
assert.doesNotMatch(script,/npm-cli\.js|npm install/i);
|
|
46
|
+
assert.match(script,/Get-FileHash -LiteralPath \$file -Algorithm SHA256/);
|
|
47
|
+
assert.match(script,/NODE_OPTIONS/);
|
|
48
|
+
assert.match(script,/https:\/\/registry\.npmjs\.org\/deadbyte-mcp\/0\.15\.59/);
|
|
49
|
+
assert.match(script,/live npm registry tarball SHA-256 mismatch/);
|
|
50
|
+
assert.match(script,/live npm registry tarball integrity mismatch/);
|
|
51
|
+
assert.match(script,/DEADBYTE-R153-Stage-/);
|
|
52
|
+
assert.match(script,/fresh protected staging path collision/);
|
|
53
|
+
assert.match(script,/protected staging residue limit reached/);
|
|
54
|
+
assert.match(script,/'\/inheritance:r'/);
|
|
55
|
+
assert.match(script,/verify-protected-stage-r153\.mjs/);
|
|
56
|
+
assert.match(script,/request-uac-migration-r153\.ps1/);
|
|
57
|
+
assert.match(script,/& \$helper -Elevated/);
|
|
58
|
+
assert.match(script,/Remove-Item -LiteralPath \$prefix -Recurse -Force/);
|
|
59
|
+
});
|
|
@@ -52,7 +52,7 @@ exit 0
|
|
|
52
52
|
const commands=await readFile(trace,'utf8').catch(error=>error.code==='ENOENT'?'':Promise.reject(error));
|
|
53
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
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);
|
|
55
|
+
for(const output of outputs)assert.match(output.stderr,/ownership(?: identity)? unverified.*marker retained|already claimed \(startup\/recovery lock\)/i);
|
|
56
56
|
assert.equal(commands,'','neither attempt may touch authority');
|
|
57
57
|
assert.equal(JSON.parse(await readFile(markerPath,'utf8')).pid,2147483000);
|
|
58
58
|
}finally{
|
|
@@ -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'].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'].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');
|
|
@@ -231,3 +231,23 @@ test('R146 legacy offline scope proof drift after claim never dispatches activat
|
|
|
231
231
|
assert.equal(activated,false);
|
|
232
232
|
assert.equal(released,true);
|
|
233
233
|
});
|
|
234
|
+
|
|
235
|
+
test('R153 UAC-bound narrowing proof is accepted without pretending to be owner-signature approval',async()=>{
|
|
236
|
+
const planes=Object.keys(policies);
|
|
237
|
+
let observation={state:'OFFLINE',local_ready:false,
|
|
238
|
+
bridge:{alive:false},core:{alive:false},cloud:{alive:false},supervisor:{running:false},
|
|
239
|
+
authority:Object.fromEntries(planes.map(plane=>[plane,{configured:true,armed:false,reason:plane==='autonomous'?'unverified':'disarmed'}])),
|
|
240
|
+
authority_proof:Object.fromEntries(planes.map(plane=>[plane,{state:plane==='autonomous'?'UNVERIFIED':'VERIFIED_DISARMED',grant_absent:true,policy_sha256:policies[plane]}])),
|
|
241
|
+
policy_sha256:policies,active_release_id:parent.release_id,active_manifest_sha256:parent.manifest_sha256,
|
|
242
|
+
active_generation_manifest_sha256:parent.generation_manifest_sha256};
|
|
243
|
+
const result=await orchestrateActivation({pointer:parent,expectedParent:parent,candidate,expectedCandidate:candidate,
|
|
244
|
+
observe:async()=>observation,command:async()=>{},claim:async()=>async()=>{},recordOutcome:async()=>{},
|
|
245
|
+
activate:async()=>{observation={...observation,state:'ONLINE',local_ready:true,...ids,supervisor:{running:true},
|
|
246
|
+
authority:Object.fromEntries(planes.map(plane=>[plane,legacyEntry(false)])),
|
|
247
|
+
authority_proof:Object.fromEntries(planes.map(plane=>[plane,{state:'VERIFIED_DISARMED',policy_sha256:policies[plane],grant_absent:true}]))};
|
|
248
|
+
return {verified:true,release_id:'v0.15.42-r153'};},
|
|
249
|
+
legacyOfflineRecovery:async({preflight_subject_sha256})=>({state:'VERIFIED_OFFLINE_LEGACY_SCOPE_REPAIR',
|
|
250
|
+
preflight_subject_sha256,candidate_manifest_sha256:candidate.manifest_sha256,
|
|
251
|
+
migration_authorization_verified:true,migration_authorization:'windows_uac_elevated',subject_sha256:'b'.repeat(64)})});
|
|
252
|
+
assert.equal(result.state,'ACTIVATED');
|
|
253
|
+
});
|
|
@@ -571,7 +571,7 @@ winTest('bare remote retains an owner-unverified stale marker without starting o
|
|
|
571
571
|
const ended=await boundedCompletion(output.closed);
|
|
572
572
|
assert.equal(ended.timeout,undefined);
|
|
573
573
|
assert.notEqual(ended.code,0);
|
|
574
|
-
assert.match(output.stderr,/ownership unverified.*marker retained/i);
|
|
574
|
+
assert.match(output.stderr,/ownership(?: identity)? unverified.*marker retained/i);
|
|
575
575
|
assert.deepEqual(await traceCommands(fx.trace),[]);
|
|
576
576
|
assert.equal(JSON.parse(await readFile(path.join(runtime,'remote-session.json'),'utf8')).pid,2147483000);
|
|
577
577
|
}finally{
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
# Changes
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
## 2.0.2
|
|
5
|
-
|
|
6
|
-
* Rename bin to `node-which`
|
|
7
|
-
|
|
8
|
-
## 2.0.1
|
|
9
|
-
|
|
10
|
-
* generate changelog and publish on version bump
|
|
11
|
-
* enforce 100% test coverage
|
|
12
|
-
* Promise interface
|
|
13
|
-
|
|
14
|
-
## 2.0.0
|
|
15
|
-
|
|
16
|
-
* Parallel tests, modern JavaScript, and drop support for node < 8
|
|
17
|
-
|
|
18
|
-
## 1.3.1
|
|
19
|
-
|
|
20
|
-
* update deps
|
|
21
|
-
* update travis
|
|
22
|
-
|
|
23
|
-
## v1.3.0
|
|
24
|
-
|
|
25
|
-
* Add nothrow option to which.sync
|
|
26
|
-
* update tap
|
|
27
|
-
|
|
28
|
-
## v1.2.14
|
|
29
|
-
|
|
30
|
-
* appveyor: drop node 5 and 0.x
|
|
31
|
-
* travis-ci: add node 6, drop 0.x
|
|
32
|
-
|
|
33
|
-
## v1.2.13
|
|
34
|
-
|
|
35
|
-
* test: Pass missing option to pass on windows
|
|
36
|
-
* update tap
|
|
37
|
-
* update isexe to 2.0.0
|
|
38
|
-
* neveragain.tech pledge request
|
|
39
|
-
|
|
40
|
-
## v1.2.12
|
|
41
|
-
|
|
42
|
-
* Removed unused require
|
|
43
|
-
|
|
44
|
-
## v1.2.11
|
|
45
|
-
|
|
46
|
-
* Prevent changelog script from being included in package
|
|
47
|
-
|
|
48
|
-
## v1.2.10
|
|
49
|
-
|
|
50
|
-
* Use env.PATH only, not env.Path
|
|
51
|
-
|
|
52
|
-
## v1.2.9
|
|
53
|
-
|
|
54
|
-
* fix for paths starting with ../
|
|
55
|
-
* Remove unused `is-absolute` module
|
|
56
|
-
|
|
57
|
-
## v1.2.8
|
|
58
|
-
|
|
59
|
-
* bullet items in changelog that contain (but don't start with) #
|
|
60
|
-
|
|
61
|
-
## v1.2.7
|
|
62
|
-
|
|
63
|
-
* strip 'update changelog' changelog entries out of changelog
|
|
64
|
-
|
|
65
|
-
## v1.2.6
|
|
66
|
-
|
|
67
|
-
* make the changelog bulleted
|
|
68
|
-
|
|
69
|
-
## v1.2.5
|
|
70
|
-
|
|
71
|
-
* make a changelog, and keep it up to date
|
|
72
|
-
* don't include tests in package
|
|
73
|
-
* Properly handle relative-path executables
|
|
74
|
-
* appveyor
|
|
75
|
-
* Attach error code to Not Found error
|
|
76
|
-
* Make tests pass on Windows
|
|
77
|
-
|
|
78
|
-
## v1.2.4
|
|
79
|
-
|
|
80
|
-
* Fix typo
|
|
81
|
-
|
|
82
|
-
## v1.2.3
|
|
83
|
-
|
|
84
|
-
* update isexe, fix regression in pathExt handling
|
|
85
|
-
|
|
86
|
-
## v1.2.2
|
|
87
|
-
|
|
88
|
-
* update deps, use isexe module, test windows
|
|
89
|
-
|
|
90
|
-
## v1.2.1
|
|
91
|
-
|
|
92
|
-
* Sometimes windows PATH entries are quoted
|
|
93
|
-
* Fixed a bug in the check for group and user mode bits. This bug was introduced during refactoring for supporting strict mode.
|
|
94
|
-
* doc cli
|
|
95
|
-
|
|
96
|
-
## v1.2.0
|
|
97
|
-
|
|
98
|
-
* Add support for opt.all and -as cli flags
|
|
99
|
-
* test the bin
|
|
100
|
-
* update travis
|
|
101
|
-
* Allow checking for multiple programs in bin/which
|
|
102
|
-
* tap 2
|
|
103
|
-
|
|
104
|
-
## v1.1.2
|
|
105
|
-
|
|
106
|
-
* travis
|
|
107
|
-
* Refactored and fixed undefined error on Windows
|
|
108
|
-
* Support strict mode
|
|
109
|
-
|
|
110
|
-
## v1.1.1
|
|
111
|
-
|
|
112
|
-
* test +g exes against secondary groups, if available
|
|
113
|
-
* Use windows exe semantics on cygwin & msys
|
|
114
|
-
* cwd should be first in path on win32, not last
|
|
115
|
-
* Handle lower-case 'env.Path' on Windows
|
|
116
|
-
* Update docs
|
|
117
|
-
* use single-quotes
|
|
118
|
-
|
|
119
|
-
## v1.1.0
|
|
120
|
-
|
|
121
|
-
* Add tests, depend on is-absolute
|
|
122
|
-
|
|
123
|
-
## v1.0.9
|
|
124
|
-
|
|
125
|
-
* which.js: root is allowed to execute files owned by anyone
|
|
126
|
-
|
|
127
|
-
## v1.0.8
|
|
128
|
-
|
|
129
|
-
* don't use graceful-fs
|
|
130
|
-
|
|
131
|
-
## v1.0.7
|
|
132
|
-
|
|
133
|
-
* add license to package.json
|
|
134
|
-
|
|
135
|
-
## v1.0.6
|
|
136
|
-
|
|
137
|
-
* isc license
|
|
138
|
-
|
|
139
|
-
## 1.0.5
|
|
140
|
-
|
|
141
|
-
* Awful typo
|
|
142
|
-
|
|
143
|
-
## 1.0.4
|
|
144
|
-
|
|
145
|
-
* Test for path absoluteness properly
|
|
146
|
-
* win: Allow '' as a pathext if cmd has a . in it
|
|
147
|
-
|
|
148
|
-
## 1.0.3
|
|
149
|
-
|
|
150
|
-
* Remove references to execPath
|
|
151
|
-
* Make `which.sync()` work on Windows by honoring the PATHEXT variable.
|
|
152
|
-
* Make `isExe()` always return true on Windows.
|
|
153
|
-
* MIT
|
|
154
|
-
|
|
155
|
-
## 1.0.2
|
|
156
|
-
|
|
157
|
-
* Only files can be exes
|
|
158
|
-
|
|
159
|
-
## 1.0.1
|
|
160
|
-
|
|
161
|
-
* Respect the PATHEXT env for win32 support
|
|
162
|
-
* should 0755 the bin
|
|
163
|
-
* binary
|
|
164
|
-
* guts
|
|
165
|
-
* package
|
|
166
|
-
* 1st
|