deadbyte-mcp 0.15.72 → 0.15.75
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 +143 -35
- package/PROVENANCE.json +47 -21
- package/README.txt +29 -18
- package/RELEASE.json +85 -18
- package/SBOM.cdx.json +14 -2
- package/TEST-MATRIX.json +50 -2
- package/bin/deadbyte-desktop-uia-r199-fixed.exe +0 -0
- package/bin/deadbyte-desktop-uia-r199-fixed.obj +0 -0
- package/bin/deadbyte-desktop-uia-r199.exe +0 -0
- package/bin/deadbyte-desktop-uia-r199.obj +0 -0
- package/bin/deadbyte-fs-read-r197-fixed.exe +0 -0
- package/bin/deadbyte-fs-read-r198.exe +0 -0
- package/bin/deadbyte-fs-read-r198.obj +0 -0
- package/bin/deadbyte-owner-token-r193.exe +0 -0
- package/bin/deadbyte-root-relative-probe-r197.exe +0 -0
- package/bridge/package.json +1 -1
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R184-package-lock.json +2 -8
- package/native/deadbyte-desktop-uia-r199.cpp +345 -0
- package/native/deadbyte-fs-read-r197.cpp +174 -0
- package/native/deadbyte-fs-read-r198.cpp +181 -0
- package/native/deadbyte-owner-token-r193.cpp +156 -0
- package/native/deadbyte-root-relative-probe-r197.cpp +97 -0
- package/package.json +4 -2
- package/scripts/build-native-desktop-uia-r199-fixed.ps1 +28 -0
- package/scripts/build-native-desktop-uia-r199.ps1 +28 -0
- package/scripts/build-native-fs-read-r197-fixed.ps1 +28 -0
- package/scripts/build-native-fs-read-r197.ps1 +28 -0
- package/scripts/build-native-fs-read-r198.ps1 +28 -0
- package/scripts/build-native-root-probe-r197.ps1 +28 -0
- package/scripts/gate-windows-r193.ps1 +108 -0
- package/scripts/gate-windows-r194.ps1 +112 -0
- package/scripts/gate-windows-r195.ps1 +114 -0
- package/scripts/gate-windows-r206.ps1 +116 -0
- package/scripts/init-desktop-policy-r199.ps1 +85 -0
- package/scripts/npm-interactive-r194.mjs +13 -0
- package/scripts/npm-publication-r65.mjs +64 -4
- package/scripts/npm-release-r48.mjs +75 -11
- package/scripts/npm-release-readiness-r193.mjs +80 -0
- package/scripts/npm-release-readiness-r194.mjs +80 -0
- package/scripts/npm-release-readiness-r195.mjs +72 -0
- package/scripts/npm-release-readiness-r206.mjs +76 -0
- package/scripts/r188-owner-uac-review.ps1 +91 -0
- package/scripts/r188-uac-review-preview.mjs +65 -0
- package/scripts/r193-compile-token.ps1 +22 -0
- package/scripts/r193-evidence-relocate.ps1 +12 -0
- package/scripts/r193-focused-gate.mjs +20 -0
- package/scripts/r193-import-r188-scripts.ps1 +14 -0
- package/scripts/r193-limited-subject-probe.ps1 +25 -0
- package/scripts/r193-migrate-runtime-limited.ps1 +103 -0
- package/scripts/r193-owner-keygen.mjs +43 -0
- package/scripts/r193-owner-request-live.mjs +49 -0
- package/scripts/r193-owner-uac-approve.ps1 +42 -0
- package/scripts/r193-owner-uac-broker.mjs +139 -0
- package/scripts/r193-owner-uac-enroll-and-approve.ps1 +62 -0
- package/scripts/r193-test-limited-token.ps1 +49 -0
- package/scripts/r193-toolchain-inventory.ps1 +7 -0
- package/scripts/r193-uac-bridge-focused.mjs +21 -0
- package/scripts/r205-candidate-selection.mjs +22 -0
- package/src/candidate-selection-evidence-r206.mjs +71 -0
- package/src/candidate-selection-r205.mjs +96 -0
- package/src/desktop-postcondition-r196.mjs +30 -0
- package/src/desktop-runtime-r40.mjs +44 -7
- package/src/desktop-uia-identity-r199.mjs +51 -0
- package/src/desktop-uia-r40.mjs +9 -1
- package/src/machine-fs-handle-read-r196.mjs +43 -0
- package/src/machine-fs-native-read-r197.mjs +73 -0
- package/src/machine-fs-native-read-r198.mjs +76 -0
- package/src/machine-fs-read-worker.mjs +5 -25
- package/src/machine-fs.mjs +31 -16
- package/src/r188-owner-uac.mjs +105 -0
- package/src/r188-uac-boundary.mjs +21 -0
- package/src/r193-live-owner-subject.mjs +110 -0
- package/src/r193-owner-secret-acl.mjs +67 -0
- package/src/r193-owner-uac-broker.mjs +85 -0
- package/src/r193-uac-r70-bridge.mjs +54 -0
- package/src/r195-canonical-owner-observation.mjs +15 -0
- package/src/remote-destination-journal-r202.mjs +120 -0
- package/src/remote-device-r44.mjs +89 -14
- package/src/remote-payload-boundary-r201.mjs +71 -0
- package/src/remote-terminal-evidence-r200.mjs +60 -0
- package/src/version.mjs +1 -1
- package/test/desktop-runtime-r40.test.mjs +111 -0
- package/test/desktop-uia-r40.test.mjs +15 -0
- package/test/fixtures/r197-junction-swap-worker.mjs +21 -0
- package/test/fixtures/r203-journal-writer.mjs +6 -0
- package/test/process-policy-refresh.test.mjs +4 -4
- package/test/r130-release-security-contract.test.mjs +8 -8
- package/test/r132-release-identity.test.mjs +7 -7
- package/test/r153-prepublish-identity.test.mjs +1 -1
- package/test/r160-release-contract.test.mjs +3 -3
- package/test/r165-live-cli-skew.test.mjs +1 -1
- package/test/r167-release-contract.test.mjs +11 -11
- package/test/r168-release-contract.test.mjs +4 -4
- package/test/r172-release-contract.test.mjs +6 -6
- package/test/r173-release-contract.test.mjs +16 -16
- package/test/r179-release-contract.test.mjs +12 -12
- package/test/r188-owner-uac.test.mjs +48 -0
- package/test/r188-uac-boundary.test.mjs +42 -0
- package/test/r190-release-contract.test.mjs +10 -10
- package/test/r191-release-contract.test.mjs +12 -12
- package/test/r192-release-identity.test.mjs +10 -10
- package/test/r193-limited-migration-contract.test.mjs +26 -0
- package/test/r193-live-owner-subject.test.mjs +61 -0
- package/test/r193-native-process-observer.test.mjs +36 -0
- package/test/r193-native-token.test.mjs +43 -0
- package/test/r193-owner-enrollment-entrypoint.test.mjs +38 -0
- package/test/r193-owner-secret-acl.test.mjs +23 -0
- package/test/r193-owner-uac-broker.test.mjs +82 -0
- package/test/r193-owner-uac-entrypoint.test.mjs +36 -0
- package/test/r193-release-identity.test.mjs +58 -0
- package/test/r193-release-readiness.test.mjs +87 -0
- package/test/r193-uac-r70-bridge.test.mjs +88 -0
- package/test/r193-windows-gate.test.mjs +30 -0
- package/test/r194-npm-auth-recovery.test.mjs +77 -0
- package/test/r194-npm-preauth.test.mjs +57 -0
- package/test/r194-release-identity.test.mjs +47 -0
- package/test/r194-release-readiness.test.mjs +87 -0
- package/test/r194-windows-gate.test.mjs +22 -0
- package/test/r195-canonical-owner-observation.test.mjs +32 -0
- package/test/r195-release-readiness.test.mjs +22 -0
- package/test/r196-desktop-postcondition.test.mjs +53 -0
- package/test/r196-handle-bound-read.test.mjs +99 -0
- package/test/r196-remote-outcome.test.mjs +94 -0
- package/test/r197-namespace-race.test.mjs +41 -0
- package/test/r197-native-dispatch-race.test.mjs +49 -0
- package/test/r197-native-file-read.test.mjs +60 -0
- package/test/r197-native-root-open.test.mjs +61 -0
- package/test/r198-root-identity.test.mjs +88 -0
- package/test/r199-native-uia-worker.test.mjs +28 -0
- package/test/r199-uia-identity.test.mjs +43 -0
- package/test/r200-remote-reconcile.test.mjs +106 -0
- package/test/r201-payload-boundary.test.mjs +78 -0
- package/test/r201-payload-fuzz.test.mjs +37 -0
- package/test/r201-tool-boundary-fuzz.test.mjs +120 -0
- package/test/r202-destination-journal.test.mjs +92 -0
- package/test/r203-atomic-journal.test.mjs +86 -0
- package/test/r205-candidate-selection-cli.test.mjs +31 -0
- package/test/r205-candidate-selection.test.mjs +83 -0
- package/test/r206-candidate-evidence.test.mjs +64 -0
- package/test/r206-release-contract.test.mjs +42 -0
- package/test/r206-release-readiness.test.mjs +23 -0
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/r64-machine-fs-large-read.test.mjs +26 -12
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
$ErrorActionPreference='Stop'
|
|
2
|
+
$source='D:\Projects\deadbyte-mcp\development\deadbyte-mcp-r193-owner-uac-dev\R193-STANDARD-TOKEN-PROBE.json'
|
|
3
|
+
$folder='D:\Projects\deadbyte-mcp\testing\r193'
|
|
4
|
+
$dest=Join-Path $folder 'R193-STANDARD-TOKEN-PROBE.json'
|
|
5
|
+
$item=Get-Item -LiteralPath $source -Force -ErrorAction Stop
|
|
6
|
+
if($item.PSIsContainer -or (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0)){throw 'R193_UNSAFE_EVIDENCE'}
|
|
7
|
+
$before=(Get-FileHash $source).Hash.ToLowerInvariant()
|
|
8
|
+
if(Test-Path $dest){throw 'R193_DEST_EVIDENCE_ALREADY_EXISTS'}
|
|
9
|
+
New-Item -ItemType Directory -Path $folder -Force -ErrorAction Stop | Out-Null
|
|
10
|
+
Move-Item -LiteralPath $source -Destination $dest -ErrorAction Stop
|
|
11
|
+
if((Get-FileHash $dest).Hash.ToLowerInvariant() -cne $before){throw 'R193_EVIDENCE_MOVE_CHANGED'}
|
|
12
|
+
[pscustomobject]@{schema='deadbyte.r193-evidence-relocate.v1';state='VERIFIED';evidence_path=$dest;sha256=$before;source_absent=(-not(Test-Path $source));production_untouched=$true}|ConvertTo-Json -Compress
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {spawnSync} from 'node:child_process';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {fileURLToPath} from 'node:url';
|
|
4
|
+
const root=fileURLToPath(new URL('..',import.meta.url));
|
|
5
|
+
const files=[
|
|
6
|
+
'test/r188-owner-uac.test.mjs',
|
|
7
|
+
'test/r188-uac-boundary.test.mjs',
|
|
8
|
+
'test/r193-native-token.test.mjs'
|
|
9
|
+
];
|
|
10
|
+
const result=spawnSync(process.execPath,['--test','--test-concurrency=1',...files],{
|
|
11
|
+
cwd:root,encoding:'utf8',windowsHide:true,timeout:120000,maxBuffer:1024*1024,shell:false
|
|
12
|
+
});
|
|
13
|
+
const output=(result.stdout??'')+'\n'+(result.stderr??'');
|
|
14
|
+
const summary=output.split(/\r?\n/).filter(x=>/^[ℹ#]\s*(tests|pass|fail|skipped)\s+/.test(x)).slice(-6);
|
|
15
|
+
const pass=result.status===0 && summary.some(x=>/pass\s+9\b/.test(x)) && summary.some(x=>/fail\s+0\b/.test(x));
|
|
16
|
+
console.log(JSON.stringify({schema:'deadbyte.r193-focused-gate.v1',
|
|
17
|
+
state:pass?'PASS':'FAIL',exit:result.status,summary,
|
|
18
|
+
output_tail:pass?null:output.slice(-11000),
|
|
19
|
+
runtime_source:'isolated_R193_only',release_activated:false,authority_granted:false}));
|
|
20
|
+
if(!pass)process.exitCode=1;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
$ErrorActionPreference='Stop'
|
|
2
|
+
$from='D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.69-r188-uac-consent'
|
|
3
|
+
$into='D:\Projects\deadbyte-mcp\development\deadbyte-mcp-r193-owner-uac-dev'
|
|
4
|
+
$names=@('scripts\r188-owner-uac-review.ps1','scripts\r188-uac-review-preview.mjs')
|
|
5
|
+
foreach($n in $names){
|
|
6
|
+
$s=Join-Path $from $n
|
|
7
|
+
$d=Join-Path $into $n
|
|
8
|
+
if(Test-Path $d){throw "R193_IMPORT_ALREADY_EXISTS $n"}
|
|
9
|
+
$item=Get-Item -LiteralPath $s
|
|
10
|
+
if($item.PSIsContainer -or ($item.Attributes -band [IO.FileAttributes]::ReparsePoint)){throw "R193_UNSAFE_SOURCE $n"}
|
|
11
|
+
Copy-Item -LiteralPath $s -Destination $d -ErrorAction Stop
|
|
12
|
+
if((Get-FileHash $s).Hash -cne (Get-FileHash $d).Hash){throw "R193_IMPORT_BYTES_MISMATCH $n"}
|
|
13
|
+
}
|
|
14
|
+
[pscustomobject]@{state='R188_SCRIPTS_IMPORTED';scripts=$names;overwrite=$false;production_untouched=$true}|ConvertTo-Json -Compress
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
$ErrorActionPreference='Stop'
|
|
2
|
+
$root=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
|
3
|
+
$exe=Join-Path $root 'bin\deadbyte-owner-token-r193.exe'
|
|
4
|
+
$output=Join-Path $root 'R193-STANDARD-TOKEN-PROBE.json'
|
|
5
|
+
$sha='d554d732872f3d671fd2fb63c11fc0dac1f8cae377259c6d64dd0c5b3762f2b8'
|
|
6
|
+
if((Get-FileHash -LiteralPath $exe -Algorithm SHA256).Hash.ToLowerInvariant() -cne $sha){throw 'R193_NATIVE_BINARY_CHANGED'}
|
|
7
|
+
if(Test-Path -LiteralPath $output){throw 'R193_PROBE_OUTPUT_ALREADY_EXISTS'}
|
|
8
|
+
$json=@(& $exe '--require-standard')
|
|
9
|
+
$exit=$LASTEXITCODE
|
|
10
|
+
if($json.Count -ne 1){throw 'R193_PROBE_OUTPUT_INVALID'}
|
|
11
|
+
$body=($json[0]|ConvertFrom-Json)
|
|
12
|
+
$record=[ordered]@{
|
|
13
|
+
schema='deadbyte.r193-standard-token-probe.v1'
|
|
14
|
+
process_report=$body
|
|
15
|
+
probe_exit=$exit
|
|
16
|
+
native_binary_sha256=$sha
|
|
17
|
+
expected_standard=($body.status -eq 'STANDARD_TOKEN_OBSERVED' -and $exit -eq 0)
|
|
18
|
+
authority_granted=$false
|
|
19
|
+
}
|
|
20
|
+
$text=$record|ConvertTo-Json -Depth 6 -Compress
|
|
21
|
+
$utf8=New-Object Text.UTF8Encoding($false)
|
|
22
|
+
$bytes=$utf8.GetBytes($text)
|
|
23
|
+
$stream=[IO.File]::Open($output,[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::None)
|
|
24
|
+
try{$stream.Write($bytes,0,$bytes.Length);$stream.Flush($true)}finally{$stream.Dispose()}
|
|
25
|
+
if(-not $record.expected_standard){exit 3}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#requires -Version 5.1
|
|
2
|
+
#requires -RunAsAdministrator
|
|
3
|
+
[CmdletBinding()]
|
|
4
|
+
param([switch]$Execute)
|
|
5
|
+
$ErrorActionPreference='Stop';Set-StrictMode -Version Latest
|
|
6
|
+
$root=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
|
7
|
+
$program=[IO.Path]::GetFullPath($env:ProgramFiles).TrimEnd('\')+'\'
|
|
8
|
+
if(-not ($root.TrimEnd('\')+'\').StartsWith($program,[StringComparison]::OrdinalIgnoreCase)){
|
|
9
|
+
throw 'R193 Limited migration must run from an R89-protected Program Files package'
|
|
10
|
+
}
|
|
11
|
+
$state='C:\Users\DEADBYTE\.deadbyte-mcp'
|
|
12
|
+
$runtime=Join-Path $state 'runtime'
|
|
13
|
+
$pointer=Get-Content -Raw -LiteralPath (Join-Path $state 'active-release.json')|ConvertFrom-Json
|
|
14
|
+
$manifest=Join-Path $root 'MANIFEST.SHA256'
|
|
15
|
+
if((Get-FileHash $manifest).Hash.ToLowerInvariant() -cne [string]$pointer.manifest_sha256){
|
|
16
|
+
throw 'R193 Limited migration package is not the exact active release'
|
|
17
|
+
}
|
|
18
|
+
$controller=Join-Path $root 'controller\deadbyte-controller.ps1'
|
|
19
|
+
$native=Join-Path $root 'bin\deadbyte-owner-token-r193.exe'
|
|
20
|
+
$env:DEADBYTE_CONTROLLER_CONFIG=[string]$pointer.config_path
|
|
21
|
+
$env:DEADBYTE_CONTROLLER_RUNTIME_ROOT=$runtime
|
|
22
|
+
function Invoke-Controller([string]$Command,[switch]$Json){
|
|
23
|
+
$args=@('-NoLogo','-NoProfile','-NonInteractive','-ExecutionPolicy','Bypass','-File',$controller,$Command)
|
|
24
|
+
if($Json){$args+='-Json'}
|
|
25
|
+
$raw=@(& 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' @args 2>&1)-join [Environment]::NewLine
|
|
26
|
+
if($LASTEXITCODE -ne 0){throw "R193 controller $Command failed: $($raw.Substring(0,[Math]::Min(400,$raw.Length)))"}
|
|
27
|
+
if($Json){return $raw|ConvertFrom-Json}
|
|
28
|
+
return $raw
|
|
29
|
+
}
|
|
30
|
+
function Token([int]$PidValue){
|
|
31
|
+
$raw=@(& $native --inspect-pid ([string]$PidValue) 2>&1)-join [Environment]::NewLine
|
|
32
|
+
if($LASTEXITCODE -ne 0){throw "R193 token inspection failed for PID $PidValue"}
|
|
33
|
+
return $raw|ConvertFrom-Json
|
|
34
|
+
}
|
|
35
|
+
function AuthorityFingerprint($status,[string]$plane){
|
|
36
|
+
$p=$status.authority.$plane
|
|
37
|
+
return '{0}|{1}|{2}|{3}|{4}' -f [bool]$p.configured,[bool]$p.armed,[string]$p.reason,[string]$p.epoch_seq,[string]$p.epoch_sha256
|
|
38
|
+
}
|
|
39
|
+
$before=Invoke-Controller status -Json
|
|
40
|
+
if([string]$before.state -cne 'ONLINE' -or -not $before.supervisor.running){throw 'R193 migration requires ONLINE runtime and verified supervisor'}
|
|
41
|
+
foreach($plane in @('process','coding','autonomous','desktop','host')){
|
|
42
|
+
if([string]$before.authority.$plane.reason -eq 'unverified'){throw "R193 migration authority $plane is unverified"}
|
|
43
|
+
}
|
|
44
|
+
$plan=[ordered]@{schema='deadbyte.r193-limited-migration-plan.v1';active_release=[string]$pointer.release_id;
|
|
45
|
+
manifest_sha256=[string]$pointer.manifest_sha256;state='PREFLIGHT_PASS';
|
|
46
|
+
desktop_will_be_disarmed=$true;other_authority_will_not_be_rearmed=$true;
|
|
47
|
+
production_mutation_requested=[bool]$Execute}
|
|
48
|
+
if(-not $Execute){$plan|ConvertTo-Json -Depth 5 -Compress;exit 0}
|
|
49
|
+
$transient='DEADBYTE-MCP-R193-Limited-Supervisor'
|
|
50
|
+
$stopped=$false
|
|
51
|
+
try{
|
|
52
|
+
Invoke-Controller supervisorstop|Out-Null
|
|
53
|
+
Invoke-Controller stop|Out-Null
|
|
54
|
+
$stopped=$true
|
|
55
|
+
$offline=Invoke-Controller status -Json
|
|
56
|
+
if([string]$offline.state -cne 'OFFLINE'){throw 'R193 migration did not reach OFFLINE'}
|
|
57
|
+
Start-ScheduledTask -TaskName 'DEADBYTE-MCP-Runtime-Logon'
|
|
58
|
+
$deadline=[DateTime]::UtcNow.AddSeconds(60)
|
|
59
|
+
do{
|
|
60
|
+
Start-Sleep -Milliseconds 500
|
|
61
|
+
try{$online=Invoke-Controller status -Json}catch{$online=$null}
|
|
62
|
+
}while(($null -eq $online -or [string]$online.state -cne 'ONLINE') -and [DateTime]::UtcNow -lt $deadline)
|
|
63
|
+
if($null -eq $online -or [string]$online.state -cne 'ONLINE'){throw 'R193 Limited runtime task did not become ONLINE'}
|
|
64
|
+
$action=New-ScheduledTaskAction -Execute 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe' -Argument ('-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "'+(Join-Path $state 'bin\DEADBYTE-MCP.ps1')+'" supervisorstart')
|
|
65
|
+
$principal=New-ScheduledTaskPrincipal -UserId ([Security.Principal.WindowsIdentity]::GetCurrent().Name) -LogonType Interactive -RunLevel Limited
|
|
66
|
+
Register-ScheduledTask -TaskName $transient -Action $action -Principal $principal -Force|Out-Null
|
|
67
|
+
Start-ScheduledTask -TaskName $transient
|
|
68
|
+
$deadline=[DateTime]::UtcNow.AddSeconds(30)
|
|
69
|
+
do{Start-Sleep -Milliseconds 500;$after=Invoke-Controller status -Json}
|
|
70
|
+
while(-not $after.supervisor.running -and [DateTime]::UtcNow -lt $deadline)
|
|
71
|
+
if(-not $after.supervisor.running){throw 'R193 Limited supervisor task did not become RUNNING'}
|
|
72
|
+
$supervisor=Get-Content -Raw -LiteralPath (Join-Path $runtime 'supervisor.instance.json')|ConvertFrom-Json
|
|
73
|
+
$tokens=@{
|
|
74
|
+
bridge=(Token ([int]$after.bridge.pid));core=(Token ([int]$after.core.pid));
|
|
75
|
+
cloud=(Token ([int]$after.cloud.pid));supervisor=(Token ([int]$supervisor.pid))
|
|
76
|
+
}
|
|
77
|
+
foreach($name in @('bridge','core','cloud','supervisor')){
|
|
78
|
+
$t=$tokens[$name]
|
|
79
|
+
if($t.elevated -or $t.admin_enabled -or [int]$t.integrity_rid -lt 8192 -or [int]$t.integrity_rid -ge 12288 -or [string]$t.token_type -cne 'primary'){
|
|
80
|
+
throw "R193 $name did not migrate to a Limited primary token"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
foreach($plane in @('process','coding','host')){
|
|
84
|
+
if((AuthorityFingerprint $before $plane) -cne (AuthorityFingerprint $after $plane)){
|
|
85
|
+
throw "R193 migration changed $plane authority"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if($after.authority.autonomous.armed){throw 'R193 migration unexpectedly armed Autonomous'}
|
|
89
|
+
if($after.authority.desktop.armed -or [string]$after.authority.desktop.reason -cne 'disarmed'){
|
|
90
|
+
throw 'R193 migration expected supported stop to leave Desktop DISARMED'
|
|
91
|
+
}
|
|
92
|
+
[ordered]@{schema='deadbyte.r193-limited-migration-result.v1';state='MIGRATED_LIMITED';
|
|
93
|
+
active_release=[string]$pointer.release_id;desktop_reapproval_required=$true;
|
|
94
|
+
process_preserved=$true;coding_preserved=$true;host_preserved=$true;
|
|
95
|
+
autonomous_armed=$false;tokens=$tokens}|ConvertTo-Json -Depth 8 -Compress
|
|
96
|
+
}catch{
|
|
97
|
+
if($stopped){
|
|
98
|
+
try{Invoke-Controller start|Out-Null;Invoke-Controller supervisorstart|Out-Null}catch{}
|
|
99
|
+
}
|
|
100
|
+
throw
|
|
101
|
+
}finally{
|
|
102
|
+
Unregister-ScheduledTask -TaskName $transient -Confirm:$false -ErrorAction SilentlyContinue
|
|
103
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// One-time protected owner key enrollment. No caller-selected paths.
|
|
2
|
+
import {generateKeyPairSync} from 'node:crypto';
|
|
3
|
+
import {lstat,open,readFile} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
import {canonicalJson} from '../src/canonical-json.mjs';
|
|
7
|
+
import {createR186OwnerAnchor} from '../src/r186-owner-anchor.mjs';
|
|
8
|
+
import {inspectTrustedInstallation} from '../src/privileged-install-trust-r89.mjs';
|
|
9
|
+
import {inspectR193OwnerSecretAcl} from '../src/r193-owner-secret-acl.mjs';
|
|
10
|
+
import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
|
|
11
|
+
const THIS=fileURLToPath(import.meta.url),ROOT=path.resolve(path.dirname(THIS),'..');
|
|
12
|
+
const OWNER='C:\\Program Files\\DEADBYTE-OwnerAuthority';
|
|
13
|
+
const PRIVATE=path.win32.join(OWNER,'owner-private.der');
|
|
14
|
+
const PUBLIC=path.win32.join(OWNER,'owner-public.pem');
|
|
15
|
+
const ANCHOR=path.win32.join(OWNER,'owner-anchor.json');
|
|
16
|
+
async function exclusive(file,bytes){
|
|
17
|
+
const handle=await open(file,'wx',0o600);
|
|
18
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
19
|
+
}
|
|
20
|
+
async function main(){
|
|
21
|
+
if(process.platform!=='win32'||process.arch!=='x64')throw Error('R193 owner keygen requires Windows x64');
|
|
22
|
+
if(process.argv.length!==2)throw Error('R193 owner keygen accepts no arguments');
|
|
23
|
+
await inspectTrustedInstallation(['C:\\Program Files\\nodejs\\node.exe',THIS,
|
|
24
|
+
path.join(ROOT,'MANIFEST.SHA256'),path.join(ROOT,'src','r186-owner-anchor.mjs'),
|
|
25
|
+
path.join(ROOT,'src','r193-owner-secret-acl.mjs'),
|
|
26
|
+
path.join(ROOT,'src','privileged-install-trust-r89.mjs'),
|
|
27
|
+
path.join(ROOT,'src','release-slot.mjs')]);
|
|
28
|
+
const manifest=await readFile(path.join(ROOT,'MANIFEST.SHA256'),'utf8');
|
|
29
|
+
await verifyManifestTreeExact(ROOT,parseManifest(manifest));
|
|
30
|
+
const owner=await lstat(OWNER);
|
|
31
|
+
if(!owner.isDirectory()||owner.isSymbolicLink())throw Error('R193 owner directory is not a real directory');
|
|
32
|
+
const pair=generateKeyPairSync('ed25519');
|
|
33
|
+
const privateDer=pair.privateKey.export({format:'der',type:'pkcs8'});
|
|
34
|
+
const material=createR186OwnerAnchor(pair.publicKey.export({format:'pem',type:'spki'}).toString());
|
|
35
|
+
await exclusive(PRIVATE,privateDer);
|
|
36
|
+
await exclusive(PUBLIC,Buffer.from(material.public_key_pem,'utf8'));
|
|
37
|
+
await exclusive(ANCHOR,Buffer.from(canonicalJson(material.anchor)+'\n','utf8'));
|
|
38
|
+
const secret=await inspectR193OwnerSecretAcl([OWNER,PRIVATE]);
|
|
39
|
+
process.stdout.write(JSON.stringify({status:'R193_OWNER_AUTHORITY_ENROLLED',
|
|
40
|
+
owner_key_id:material.anchor.owner_key_id,secret_acl:secret.reason,
|
|
41
|
+
private_key_exported:false,authority_granted:false})+'\n');
|
|
42
|
+
}
|
|
43
|
+
main().catch(error=>{console.error(String(error?.message??error).slice(0,500));process.exitCode=2;});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Protected, fixed-scope request generation after Limited migration.
|
|
2
|
+
import {createHash} from 'node:crypto';
|
|
3
|
+
import {lstat,open,readFile} from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
import {canonicalJson} from '../src/canonical-json.mjs';
|
|
7
|
+
import {inspectTrustedInstallation} from '../src/privileged-install-trust-r89.mjs';
|
|
8
|
+
import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
|
|
9
|
+
import {createR188OwnerUacRequest} from '../src/r188-owner-uac.mjs';
|
|
10
|
+
import {createR193LiveMachineEpoch} from '../src/r193-live-owner-subject.mjs';
|
|
11
|
+
import {observeCanonicalController} from '../src/r195-canonical-owner-observation.mjs';
|
|
12
|
+
import {execFile} from 'node:child_process';
|
|
13
|
+
import {promisify} from 'node:util';
|
|
14
|
+
const execFileAsync=promisify(execFile);
|
|
15
|
+
const THIS=fileURLToPath(import.meta.url),ROOT=path.resolve(path.dirname(THIS),'..');
|
|
16
|
+
const STATE='C:\\Users\\DEADBYTE\\.deadbyte-mcp',RUNTIME=path.win32.join(STATE,'runtime');
|
|
17
|
+
const PS='C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
|
|
18
|
+
const sha=b=>createHash('sha256').update(b).digest('hex');
|
|
19
|
+
async function bounded(file,limit){const s=await lstat(file);if(!s.isFile()||s.isSymbolicLink()||s.size<1||s.size>limit)throw Error('R193 request input unsafe');return readFile(file);}
|
|
20
|
+
async function main(){
|
|
21
|
+
if(process.platform!=='win32'||process.arch!=='x64'||process.argv.length!==2)throw Error('R193 live request accepts no arguments');
|
|
22
|
+
const manifestPath=path.join(ROOT,'MANIFEST.SHA256');
|
|
23
|
+
await inspectTrustedInstallation(['C:\\Program Files\\nodejs\\node.exe',THIS,manifestPath,
|
|
24
|
+
path.join(ROOT,'src','r188-owner-uac.mjs'),path.join(ROOT,'src','r193-live-owner-subject.mjs'),
|
|
25
|
+
path.join(ROOT,'src','r195-canonical-owner-observation.mjs'),
|
|
26
|
+
path.join(ROOT,'src','privileged-install-trust-r89.mjs')]);
|
|
27
|
+
const manifestBytes=await bounded(manifestPath,1024*1024);
|
|
28
|
+
await verifyManifestTreeExact(ROOT,parseManifest(manifestBytes.toString('utf8')));
|
|
29
|
+
const pointer=JSON.parse((await bounded(path.win32.join(STATE,'active-release.json'),64*1024)).toString('utf8'));
|
|
30
|
+
if(pointer.manifest_sha256!==sha(manifestBytes))throw Error('R193 request package is not exact active release');
|
|
31
|
+
const codingBytes=await bounded(path.win32.join(pointer.generation_root,'coding-policy.json'),1024*1024);
|
|
32
|
+
const autonomousBytes=await bounded(path.win32.join(pointer.generation_root,'autonomous-policy.json'),1024*1024);
|
|
33
|
+
const coding=JSON.parse(codingBytes),autonomous=JSON.parse(autonomousBytes);
|
|
34
|
+
if(!coding.roots?.core?.path||!Array.isArray(autonomous.root_ids)||autonomous.root_ids.length!==1||autonomous.root_ids[0]!=='core')
|
|
35
|
+
throw Error('R193 request policy root is not the bounded core project');
|
|
36
|
+
const env={...process.env,DEADBYTE_CONTROLLER_CONFIG:pointer.config_path,DEADBYTE_CONTROLLER_RUNTIME_ROOT:RUNTIME};
|
|
37
|
+
const observation=await observeCanonicalController({stateRoot:STATE,powershell:PS,env,execFileAsync});
|
|
38
|
+
if(observation.state!=='ONLINE'||!observation.supervisor?.running)throw Error('R193 request requires ONLINE Limited runtime');
|
|
39
|
+
const machineEpoch=createR193LiveMachineEpoch({activePointer:pointer,controllerObservation:observation});
|
|
40
|
+
const request=createR188OwnerUacRequest({machineEpoch,releaseManifestSha256:sha(manifestBytes),
|
|
41
|
+
codingPolicySha256:sha(codingBytes),autonomousPolicySha256:sha(autonomousBytes),
|
|
42
|
+
rootId:'core',projectRoot:coding.roots.core.path,maxSeconds:600,now:new Date(),expiresInMs:300000});
|
|
43
|
+
const out=path.win32.join(RUNTIME,'owner-consent','r193-request-'+request.request_sha256+'.json');
|
|
44
|
+
const handle=await open(out,'wx',0o600);try{await handle.writeFile(canonicalJson(request)+'\n');await handle.sync();}finally{await handle.close();}
|
|
45
|
+
process.stdout.write(JSON.stringify({status:'R193_OWNER_REQUEST_CREATED',request_path:out,
|
|
46
|
+
request_sha256:request.request_sha256,review_phrase:request.review_phrase,
|
|
47
|
+
expires_at_utc:request.subject.expires_at_utc,authority_granted:false})+'\n');
|
|
48
|
+
}
|
|
49
|
+
main().catch(error=>{console.error(String(error?.message??error).slice(0,500));process.exitCode=2;});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#requires -Version 5.1
|
|
2
|
+
#requires -RunAsAdministrator
|
|
3
|
+
[CmdletBinding()]
|
|
4
|
+
param([Parameter(Mandatory=$true)][string]$RequestPath)
|
|
5
|
+
$ErrorActionPreference='Stop';Set-StrictMode -Version Latest
|
|
6
|
+
$root=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
|
7
|
+
$program=[IO.Path]::GetFullPath($env:ProgramFiles).TrimEnd('\')+'\'
|
|
8
|
+
if(-not ($root.TrimEnd('\')+'\').StartsWith($program,[StringComparison]::OrdinalIgnoreCase)){
|
|
9
|
+
throw 'R193 owner broker must run from an R89-protected Program Files package'
|
|
10
|
+
}
|
|
11
|
+
if(-not [Environment]::UserInteractive){throw 'R193 owner approval requires an interactive desktop'}
|
|
12
|
+
$node='C:\Program Files\nodejs\node.exe'
|
|
13
|
+
$broker=Join-Path $root 'scripts\r193-owner-uac-broker.mjs'
|
|
14
|
+
$request=[IO.Path]::GetFullPath($RequestPath)
|
|
15
|
+
$raw=@(& $node $broker preflight $request 2>&1) -join [Environment]::NewLine
|
|
16
|
+
if($LASTEXITCODE -ne 0){throw ('R193 preflight blocked: '+$raw.Substring(0,[Math]::Min(500,$raw.Length)))}
|
|
17
|
+
$preview=$raw|ConvertFrom-Json
|
|
18
|
+
if([string]$preview.status -cne 'READY_FOR_OWNER_GESTURE' -or $preview.authority_granted -or $preview.owner_approved){
|
|
19
|
+
throw 'R193 owner preflight returned an invalid state'
|
|
20
|
+
}
|
|
21
|
+
Write-Host '';Write-Host 'DEADBYTE R193 / OWNER AUTONOMOUS APPROVAL' -ForegroundColor Yellow
|
|
22
|
+
Write-Host 'This signs one short-lived exact request. It does not itself arm Autonomous.'
|
|
23
|
+
Write-Host ('Request SHA-256: '+[string]$preview.request_sha256)
|
|
24
|
+
Write-Host ('Project: '+[string]$preview.project_root)
|
|
25
|
+
Write-Host ('Operation: '+[string]$preview.operation)
|
|
26
|
+
Write-Host ('Duration cap seconds: '+[string]$preview.max_seconds)
|
|
27
|
+
Write-Host ('Expires UTC: '+[string]$preview.expires_at_utc)
|
|
28
|
+
$phrase=[string]$preview.review_phrase
|
|
29
|
+
$entered=Read-Host ('Type '+$phrase+' to approve this exact request')
|
|
30
|
+
if($entered -cne $phrase){throw 'R193 owner rejected request'}
|
|
31
|
+
$owner='C:\Program Files\DEADBYTE-OwnerAuthority'
|
|
32
|
+
if(-not(Test-Path -LiteralPath $owner -PathType Container)){throw 'R193 owner authority is not enrolled'}
|
|
33
|
+
$gesture=[ordered]@{schema='deadbyte.r193-owner-gesture.v1';status='APPROVED';
|
|
34
|
+
request_sha256=[string]$preview.request_sha256;review_phrase=$phrase;
|
|
35
|
+
approved_at_utc=[DateTime]::UtcNow.ToString('o')}
|
|
36
|
+
$gesturePath=Join-Path $owner ('gesture-'+[string]$preview.request_sha256+'.json')
|
|
37
|
+
$bytes=(New-Object Text.UTF8Encoding($false)).GetBytes(($gesture|ConvertTo-Json -Compress)+[Environment]::NewLine)
|
|
38
|
+
$stream=[IO.File]::Open($gesturePath,[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::None)
|
|
39
|
+
try{$stream.Write($bytes,0,$bytes.Length);$stream.Flush($true)}finally{$stream.Dispose()}
|
|
40
|
+
$out=@(& $node $broker approve $request 2>&1) -join [Environment]::NewLine
|
|
41
|
+
if($LASTEXITCODE -ne 0){throw ('R193 signing blocked: '+$out.Substring(0,[Math]::Min(500,$out.Length)))}
|
|
42
|
+
Write-Output $out
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Protected R193 owner broker. This entrypoint is intentionally fixed to the
|
|
2
|
+
// local DEADBYTE state and owner-authority roots; callers choose only an exact
|
|
3
|
+
// request file. It never issues authority. The R186 transaction remains the
|
|
4
|
+
// sole grant/epoch mutation boundary.
|
|
5
|
+
import {createHash,createPrivateKey,createPublicKey} from 'node:crypto';
|
|
6
|
+
import {execFile} from 'node:child_process';
|
|
7
|
+
import {promisify} from 'node:util';
|
|
8
|
+
import {lstat,mkdir,open,readFile} from 'node:fs/promises';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import {fileURLToPath} from 'node:url';
|
|
11
|
+
import {canonicalJson} from '../src/canonical-json.mjs';
|
|
12
|
+
import {inspectTrustedInstallation} from '../src/privileged-install-trust-r89.mjs';
|
|
13
|
+
import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
|
|
14
|
+
import {buildR193LiveOwnerBoundary} from '../src/r193-live-owner-subject.mjs';
|
|
15
|
+
import {approveR193OwnerRequest} from '../src/r193-owner-uac-broker.mjs';
|
|
16
|
+
import {createR186OwnerAnchor} from '../src/r186-owner-anchor.mjs';
|
|
17
|
+
import {inspectR193OwnerSecretAcl} from '../src/r193-owner-secret-acl.mjs';
|
|
18
|
+
import {observeCanonicalController} from '../src/r195-canonical-owner-observation.mjs';
|
|
19
|
+
|
|
20
|
+
const execFileAsync=promisify(execFile);
|
|
21
|
+
const THIS=fileURLToPath(import.meta.url),ROOT=path.resolve(path.dirname(THIS),'..');
|
|
22
|
+
const STATE='C:\\Users\\DEADBYTE\\.deadbyte-mcp';
|
|
23
|
+
const RUNTIME=path.win32.join(STATE,'runtime');
|
|
24
|
+
const OWNER='C:\\Program Files\\DEADBYTE-OwnerAuthority';
|
|
25
|
+
const PRIVATE=path.win32.join(OWNER,'owner-private.der');
|
|
26
|
+
const PUBLIC=path.win32.join(OWNER,'owner-public.pem');
|
|
27
|
+
const ANCHOR=path.win32.join(OWNER,'owner-anchor.json');
|
|
28
|
+
const NATIVE=path.join(ROOT,'bin','deadbyte-owner-token-r193.exe');
|
|
29
|
+
const PS='C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
|
|
30
|
+
const NODE='C:\\Program Files\\nodejs\\node.exe';
|
|
31
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
32
|
+
const block=reason=>{throw new Error('R193_OWNER_UAC_BROKER_BLOCKED: '+reason);};
|
|
33
|
+
async function bounded(file,limit){
|
|
34
|
+
const stat=await lstat(file);
|
|
35
|
+
if(!stat.isFile()||stat.isSymbolicLink()||stat.size<1||stat.size>limit)block('unsafe input: '+path.basename(file));
|
|
36
|
+
return readFile(file);
|
|
37
|
+
}
|
|
38
|
+
async function exclusive(file,bytes){
|
|
39
|
+
await mkdir(path.dirname(file),{recursive:true});
|
|
40
|
+
const handle=await open(file,'wx',0o600);
|
|
41
|
+
try{await handle.writeFile(bytes);await handle.sync();}finally{await handle.close();}
|
|
42
|
+
}
|
|
43
|
+
async function exactOrWrite(file,bytes){
|
|
44
|
+
try{await exclusive(file,bytes);return 'CREATED';}
|
|
45
|
+
catch(error){
|
|
46
|
+
if(error?.code!=='EEXIST')throw error;
|
|
47
|
+
if(!(await readFile(file)).equals(bytes))block('existing output differs: '+path.basename(file));
|
|
48
|
+
return 'ALREADY_EXACT';
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function jsonExec(file,args,options={}){
|
|
52
|
+
const {stdout}=await execFileAsync(file,args,{windowsHide:true,timeout:20000,
|
|
53
|
+
maxBuffer:1024*1024,encoding:'utf8',...options});
|
|
54
|
+
try{return JSON.parse(stdout);}catch{block('invalid JSON from protected child');}
|
|
55
|
+
}
|
|
56
|
+
async function uacPolicy(){
|
|
57
|
+
const command=`$p=Get-ItemProperty -LiteralPath 'HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System';[pscustomobject]@{enable_uac=([int]$p.EnableLUA -eq 1);secure_desktop=([int]$p.PromptOnSecureDesktop -eq 1);prompt_mode=$(if([int]$p.ConsentPromptBehaviorAdmin -eq 0){'silent'}else{'explicit_secure_desktop'})}|ConvertTo-Json -Compress`;
|
|
58
|
+
return jsonExec(PS,['-NoLogo','-NoProfile','-NonInteractive','-Command',command]);
|
|
59
|
+
}
|
|
60
|
+
async function live(request){
|
|
61
|
+
const manifestPath=path.join(ROOT,'MANIFEST.SHA256');
|
|
62
|
+
const protectedFiles=[NODE,THIS,manifestPath,NATIVE,
|
|
63
|
+
path.join(ROOT,'src','r193-live-owner-subject.mjs'),
|
|
64
|
+
path.join(ROOT,'src','r193-owner-uac-broker.mjs'),
|
|
65
|
+
path.join(ROOT,'src','r195-canonical-owner-observation.mjs'),
|
|
66
|
+
path.join(ROOT,'src','r193-uac-r70-bridge.mjs'),
|
|
67
|
+
path.join(ROOT,'src','r193-owner-secret-acl.mjs'),
|
|
68
|
+
path.join(ROOT,'src','r188-owner-uac.mjs'),
|
|
69
|
+
path.join(ROOT,'src','r70-owner-approval.mjs'),
|
|
70
|
+
path.join(ROOT,'src','canonical-json.mjs'),
|
|
71
|
+
path.join(ROOT,'src','privileged-install-trust-r89.mjs'),
|
|
72
|
+
path.join(ROOT,'src','release-slot.mjs')];
|
|
73
|
+
const installation=await inspectTrustedInstallation(protectedFiles);
|
|
74
|
+
const manifestBytes=await bounded(manifestPath,1024*1024);
|
|
75
|
+
await verifyManifestTreeExact(ROOT,parseManifest(manifestBytes.toString('utf8')));
|
|
76
|
+
const pointer=JSON.parse((await bounded(path.win32.join(STATE,'active-release.json'),64*1024)).toString('utf8'));
|
|
77
|
+
if(pointer.manifest_sha256!==sha(manifestBytes))block('protected package is not exact active release');
|
|
78
|
+
const generation=pointer.generation_root;
|
|
79
|
+
const codingPath=path.win32.join(generation,'coding-policy.json');
|
|
80
|
+
const autonomousPath=path.win32.join(generation,'autonomous-policy.json');
|
|
81
|
+
const configPath=pointer.config_path;
|
|
82
|
+
const env={...process.env,DEADBYTE_CONTROLLER_CONFIG:configPath,
|
|
83
|
+
DEADBYTE_CONTROLLER_RUNTIME_ROOT:RUNTIME};
|
|
84
|
+
const observation=await observeCanonicalController({stateRoot:STATE,powershell:PS,env,execFileAsync});
|
|
85
|
+
const supervisor=JSON.parse((await bounded(path.win32.join(RUNTIME,'supervisor.instance.json'),64*1024)).toString('utf8'));
|
|
86
|
+
const sources={bridge:observation.bridge,core:observation.core,cloud:observation.cloud,supervisor};
|
|
87
|
+
const processReports={};
|
|
88
|
+
for(const component of ['bridge','core','cloud','supervisor'])
|
|
89
|
+
processReports[component]=await jsonExec(NATIVE,['--inspect-pid',String(sources[component]?.pid)]);
|
|
90
|
+
const [codingPolicyBytes,autonomousPolicyBytes,uac]=await Promise.all([
|
|
91
|
+
bounded(codingPath,1024*1024),bounded(autonomousPath,1024*1024),uacPolicy()]);
|
|
92
|
+
const built=buildR193LiveOwnerBoundary({request,activePointer:pointer,installedManifestBytes:manifestBytes,
|
|
93
|
+
codingPolicyBytes,autonomousPolicyBytes,controllerObservation:observation,
|
|
94
|
+
supervisorLedger:supervisor,processReports,brokerR89Trusted:installation.trusted,
|
|
95
|
+
keyAclTrusted:false,uac,ownerGesture:'pending',now:new Date()});
|
|
96
|
+
return {...built,pointer,manifestBytes,codingPolicyBytes,autonomousPolicyBytes,uac};
|
|
97
|
+
}
|
|
98
|
+
async function main(){
|
|
99
|
+
if(process.platform!=='win32'||process.arch!=='x64')block('Windows x64 required');
|
|
100
|
+
const [mode,requestPath]=process.argv.slice(2);
|
|
101
|
+
if(!['preflight','approve'].includes(mode)||process.argv.slice(2).length!==2||
|
|
102
|
+
!path.win32.isAbsolute(requestPath))block('usage: <preflight|approve> <absolute-request-json>');
|
|
103
|
+
await inspectTrustedInstallation([NODE,THIS,path.join(ROOT,'MANIFEST.SHA256'),NATIVE]);
|
|
104
|
+
const request=JSON.parse((await bounded(requestPath,64*1024)).toString('utf8'));
|
|
105
|
+
const state=await live(request);
|
|
106
|
+
if(mode==='preflight'){
|
|
107
|
+
process.stdout.write(JSON.stringify({status:'READY_FOR_OWNER_GESTURE',
|
|
108
|
+
request_sha256:request.request_sha256,review_phrase:request.review_phrase,
|
|
109
|
+
project_root:request.subject.project_root,operation:request.subject.operation,
|
|
110
|
+
max_seconds:request.subject.max_seconds,expires_at_utc:request.subject.expires_at_utc,
|
|
111
|
+
authority_granted:false,owner_approved:false})+'\n');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const gesturePath=path.win32.join(OWNER,'gesture-'+request.request_sha256+'.json');
|
|
115
|
+
const gesture=JSON.parse((await bounded(gesturePath,16*1024)).toString('utf8'));
|
|
116
|
+
if(gesture?.schema!=='deadbyte.r193-owner-gesture.v1'||gesture?.status!=='APPROVED'||
|
|
117
|
+
gesture?.request_sha256!==request.request_sha256||gesture?.review_phrase!==request.review_phrase||
|
|
118
|
+
typeof gesture?.approved_at_utc!=='string'||Date.now()-Date.parse(gesture.approved_at_utc)<0||
|
|
119
|
+
Date.now()-Date.parse(gesture.approved_at_utc)>60000)block('owner gesture missing, stale or mismatched');
|
|
120
|
+
const secret=await inspectR193OwnerSecretAcl([OWNER,PRIVATE]);
|
|
121
|
+
const [privateKeyDer,publicPem,anchorBytes]=await Promise.all([
|
|
122
|
+
bounded(PRIVATE,8192),bounded(PUBLIC,8192),bounded(ANCHOR,4096)]);
|
|
123
|
+
const privateKey=createPrivateKey({key:privateKeyDer,format:'der',type:'pkcs8'});
|
|
124
|
+
const derived=createR186OwnerAnchor(createPublicKey(privateKey).export({format:'pem',type:'spki'}).toString());
|
|
125
|
+
const anchor=JSON.parse(anchorBytes.toString('utf8'));
|
|
126
|
+
if(derived.public_key_pem!==publicPem.toString('utf8')||
|
|
127
|
+
canonicalJson(derived.anchor)!==canonicalJson(anchor))block('owner key enrollment mismatch');
|
|
128
|
+
const result=approveR193OwnerRequest({request,expected:state.expected,privateKeyDer,
|
|
129
|
+
boundary:{...state.boundary,key_acl_trusted:secret.trusted,owner_gesture:'approved'},now:new Date()});
|
|
130
|
+
const bytes=Buffer.from(canonicalJson(result.approval)+'\n','utf8');
|
|
131
|
+
const protectedApproval=path.win32.join(OWNER,'approval-'+request.request_sha256+'.json');
|
|
132
|
+
const runtimeApproval=path.win32.join(RUNTIME,'owner-consent','autonomous-approval.json');
|
|
133
|
+
const protected_state=await exactOrWrite(protectedApproval,bytes);
|
|
134
|
+
const runtime_state=await exactOrWrite(runtimeApproval,bytes);
|
|
135
|
+
process.stdout.write(JSON.stringify({...result,approval:undefined,protected_state,runtime_state,
|
|
136
|
+
protected_approval_sha256:sha(bytes),protected_approval_path:protectedApproval,
|
|
137
|
+
runtime_approval_path:runtimeApproval})+'\n');
|
|
138
|
+
}
|
|
139
|
+
main().catch(error=>{console.error(String(error?.message??error).slice(0,600));process.exitCode=2;});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#requires -Version 5.1
|
|
2
|
+
#requires -RunAsAdministrator
|
|
3
|
+
[CmdletBinding()]
|
|
4
|
+
param([Parameter(Mandatory=$true)][string]$RequestPath)
|
|
5
|
+
$ErrorActionPreference='Stop';Set-StrictMode -Version Latest
|
|
6
|
+
$root=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
|
7
|
+
$program=[IO.Path]::GetFullPath($env:ProgramFiles).TrimEnd('\')+'\'
|
|
8
|
+
if(-not ($root.TrimEnd('\')+'\').StartsWith($program,[StringComparison]::OrdinalIgnoreCase)){
|
|
9
|
+
throw 'R193 enrollment must run from an R89-protected Program Files package'
|
|
10
|
+
}
|
|
11
|
+
if(-not [Environment]::UserInteractive){throw 'R193 enrollment requires an interactive owner desktop'}
|
|
12
|
+
$node='C:\Program Files\nodejs\node.exe'
|
|
13
|
+
$broker=Join-Path $root 'scripts\r193-owner-uac-broker.mjs'
|
|
14
|
+
$keygen=Join-Path $root 'scripts\r193-owner-keygen.mjs'
|
|
15
|
+
$request=[IO.Path]::GetFullPath($RequestPath)
|
|
16
|
+
$raw=@(& $node $broker preflight $request 2>&1) -join [Environment]::NewLine
|
|
17
|
+
if($LASTEXITCODE -ne 0){throw ('R193 enrollment preflight blocked: '+$raw.Substring(0,[Math]::Min(500,$raw.Length)))}
|
|
18
|
+
$preview=$raw|ConvertFrom-Json
|
|
19
|
+
if([string]$preview.status -cne 'READY_FOR_OWNER_GESTURE' -or $preview.authority_granted -or $preview.owner_approved){
|
|
20
|
+
throw 'R193 enrollment preflight returned an invalid state'
|
|
21
|
+
}
|
|
22
|
+
Write-Host '';Write-Host 'DEADBYTE R193 / ENROLL OWNER AUTHORITY' -ForegroundColor Yellow
|
|
23
|
+
Write-Host 'A new owner-only Ed25519 identity will be created under Program Files.'
|
|
24
|
+
Write-Host 'Limited DEADBYTE processes will not be able to read or use the private key.'
|
|
25
|
+
Write-Host ('Request SHA-256: '+[string]$preview.request_sha256)
|
|
26
|
+
Write-Host ('Project: '+[string]$preview.project_root)
|
|
27
|
+
Write-Host ('Duration cap seconds: '+[string]$preview.max_seconds)
|
|
28
|
+
$phrase=[string]$preview.review_phrase
|
|
29
|
+
$entered=Read-Host ('Type '+$phrase+' to enroll and approve this exact request')
|
|
30
|
+
if($entered -cne $phrase){throw 'R193 owner rejected enrollment'}
|
|
31
|
+
$owner='C:\Program Files\DEADBYTE-OwnerAuthority'
|
|
32
|
+
if(Test-Path -LiteralPath $owner){throw 'R193 owner authority already exists; use the approval-only workflow'}
|
|
33
|
+
$admins=New-Object Security.Principal.SecurityIdentifier('S-1-5-32-544')
|
|
34
|
+
$system=New-Object Security.Principal.SecurityIdentifier('S-1-5-18')
|
|
35
|
+
$inherit=[Security.AccessControl.InheritanceFlags]'ContainerInherit, ObjectInherit'
|
|
36
|
+
$none=[Security.AccessControl.PropagationFlags]::None
|
|
37
|
+
$allow=[Security.AccessControl.AccessControlType]::Allow
|
|
38
|
+
New-Item -ItemType Directory -Path $owner -ErrorAction Stop | Out-Null
|
|
39
|
+
$acl=New-Object Security.AccessControl.DirectorySecurity
|
|
40
|
+
$acl.SetAccessRuleProtection($true,$false);$acl.SetOwner($admins)
|
|
41
|
+
$acl.AddAccessRule((New-Object Security.AccessControl.FileSystemAccessRule($admins,'FullControl',$inherit,$none,$allow)))
|
|
42
|
+
$acl.AddAccessRule((New-Object Security.AccessControl.FileSystemAccessRule($system,'FullControl',$inherit,$none,$allow)))
|
|
43
|
+
Set-Acl -LiteralPath $owner -AclObject $acl
|
|
44
|
+
# Keygen requires the directory to exist with its final ACL. It never accepts a
|
|
45
|
+
# target path and creates every key file with CreateNew/no-clobber semantics.
|
|
46
|
+
$keyRaw=@(& $node $keygen 2>&1) -join [Environment]::NewLine
|
|
47
|
+
if($LASTEXITCODE -ne 0){throw ('R193 protected key enrollment blocked: '+$keyRaw.Substring(0,[Math]::Min(500,$keyRaw.Length)))}
|
|
48
|
+
$keyResult=$keyRaw|ConvertFrom-Json
|
|
49
|
+
if([string]$keyResult.status -cne 'R193_OWNER_AUTHORITY_ENROLLED' -or $keyResult.private_key_exported -or $keyResult.authority_granted){
|
|
50
|
+
throw 'R193 key enrollment returned an invalid state'
|
|
51
|
+
}
|
|
52
|
+
Write-Host ('Owner key fingerprint: '+[string]$keyResult.owner_key_id) -ForegroundColor Yellow
|
|
53
|
+
$gesture=[ordered]@{schema='deadbyte.r193-owner-gesture.v1';status='APPROVED';
|
|
54
|
+
request_sha256=[string]$preview.request_sha256;review_phrase=$phrase;
|
|
55
|
+
approved_at_utc=[DateTime]::UtcNow.ToString('o')}
|
|
56
|
+
$gesturePath=Join-Path $owner ('gesture-'+[string]$preview.request_sha256+'.json')
|
|
57
|
+
$bytes=(New-Object Text.UTF8Encoding($false)).GetBytes(($gesture|ConvertTo-Json -Compress)+[Environment]::NewLine)
|
|
58
|
+
$stream=[IO.File]::Open($gesturePath,[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::None)
|
|
59
|
+
try{$stream.Write($bytes,0,$bytes.Length);$stream.Flush($true)}finally{$stream.Dispose()}
|
|
60
|
+
$out=@(& $node $broker approve $request 2>&1) -join [Environment]::NewLine
|
|
61
|
+
if($LASTEXITCODE -ne 0){throw ('R193 signing blocked after protected enrollment: '+$out.Substring(0,[Math]::Min(500,$out.Length)))}
|
|
62
|
+
Write-Output $out
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
$ErrorActionPreference='Stop'
|
|
2
|
+
$root=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
|
3
|
+
$probe=Join-Path $root 'scripts\r193-limited-subject-probe.ps1'
|
|
4
|
+
$expectedSha='16317bffb0765e3856a6f64353ef728754e346636161cf2214ec0d0d3e5c7826'
|
|
5
|
+
if((Get-FileHash $probe).Hash.ToLowerInvariant() -cne $expectedSha){throw 'R193_PROBE_SCRIPT_CHANGED'}
|
|
6
|
+
$output=Join-Path $root 'R193-STANDARD-TOKEN-PROBE.json'
|
|
7
|
+
if(Test-Path $output){throw 'R193_EXISTING_OUTPUT - read before retest'}
|
|
8
|
+
$name='DEADBYTE-R193-Limited-Token-Diagnostic'
|
|
9
|
+
if(Get-ScheduledTask -TaskName $name -TaskPath '\' -ErrorAction SilentlyContinue){throw 'R193_TASK_ALREADY_EXISTS'}
|
|
10
|
+
$identity=[Security.Principal.WindowsIdentity]::GetCurrent()
|
|
11
|
+
$account=$identity.Name
|
|
12
|
+
$ps=Join-Path $env:WINDIR 'System32\WindowsPowerShell\v1.0\powershell.exe'
|
|
13
|
+
$arg='-NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "'+$probe+'"'
|
|
14
|
+
$action=New-ScheduledTaskAction -Execute $ps -Argument $arg -WorkingDirectory $root
|
|
15
|
+
$trigger=New-ScheduledTaskTrigger -Once -At ((Get-Date).AddDays(1))
|
|
16
|
+
$principal=New-ScheduledTaskPrincipal -UserId $account -LogonType Interactive -RunLevel Limited
|
|
17
|
+
$settings=New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew -ExecutionTimeLimit (New-TimeSpan -Minutes 1)
|
|
18
|
+
$registered=$false
|
|
19
|
+
try {
|
|
20
|
+
$null=Register-ScheduledTask -TaskName $name -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Description 'One-shot R193 native Windows token diagnostic. No grants.' -ErrorAction Stop
|
|
21
|
+
$registered=$true
|
|
22
|
+
$task=Get-ScheduledTask -TaskName $name -ErrorAction Stop
|
|
23
|
+
if([string]$task.Principal.RunLevel -ne 'Limited' -or [string]$task.Principal.LogonType -ne 'Interactive'){throw 'R193_TASK_NOT_LIMITED'}
|
|
24
|
+
Start-ScheduledTask -TaskName $name -ErrorAction Stop
|
|
25
|
+
$done=$false
|
|
26
|
+
for($i=0;$i -lt 28;$i++){
|
|
27
|
+
Start-Sleep -Milliseconds 700
|
|
28
|
+
if(Test-Path -LiteralPath $output -PathType Leaf){$done=$true;break}
|
|
29
|
+
}
|
|
30
|
+
if(-not $done){throw 'R193_LIMITED_TASK_NO_REPORT'}
|
|
31
|
+
$report=Get-Content -LiteralPath $output -Raw | ConvertFrom-Json
|
|
32
|
+
$info=Get-ScheduledTaskInfo -TaskName $name -ErrorAction Stop
|
|
33
|
+
$pass=($report.expected_standard -eq $true -and $report.process_report.elevated -eq $false -and $report.process_report.admin_enabled -eq $false -and $report.authority_granted -eq $false -and $info.LastTaskResult -eq 0)
|
|
34
|
+
[pscustomobject]@{
|
|
35
|
+
schema='deadbyte.r193-limited-token-scheduler-smoke.v1'
|
|
36
|
+
state=$(if($pass){'PASS'}else{'FAIL'})
|
|
37
|
+
limited_task=$name
|
|
38
|
+
scheduled_runlevel=[string]$task.Principal.RunLevel
|
|
39
|
+
native_subject=$report.process_report
|
|
40
|
+
task_exit=$info.LastTaskResult
|
|
41
|
+
source_sha256=$expectedSha
|
|
42
|
+
no_grant=$true
|
|
43
|
+
production_untouched=$true
|
|
44
|
+
}|ConvertTo-Json -Depth 8 -Compress
|
|
45
|
+
if(-not $pass){throw 'R193_EXPECTED_STANDARD_TOKEN_MISSING'}
|
|
46
|
+
} finally {
|
|
47
|
+
if($registered){Unregister-ScheduledTask -TaskName $name -TaskPath '\' -Confirm:$false -ErrorAction Stop}
|
|
48
|
+
}
|
|
49
|
+
if(Get-ScheduledTask -TaskName $name -TaskPath '\' -ErrorAction SilentlyContinue){throw 'R193_PROBE_TASK_NOT_REMOVED'}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
$ErrorActionPreference='Stop'
|
|
2
|
+
$o=[ordered]@{schema='deadbyte.r193-toolchain.v1';toolchain=@{};disk_free_bytes=[int64](Get-PSDrive D).Free}
|
|
3
|
+
foreach($name in @('cl.exe','clang++.exe','g++.exe','cmake.exe','node.exe','powershell.exe')){
|
|
4
|
+
$q=Get-Command $name -ErrorAction SilentlyContinue
|
|
5
|
+
$o.toolchain[$name]=if($null -eq $q){'MISSING'}else{[string]$q.Source}
|
|
6
|
+
}
|
|
7
|
+
$o|ConvertTo-Json -Depth 4 -Compress
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {spawnSync} from 'node:child_process';
|
|
2
|
+
import {fileURLToPath} from 'node:url';
|
|
3
|
+
const root=fileURLToPath(new URL('..',import.meta.url));
|
|
4
|
+
const files=[
|
|
5
|
+
'test/r188-owner-uac.test.mjs',
|
|
6
|
+
'test/r188-uac-boundary.test.mjs',
|
|
7
|
+
'test/r186-owner-grant-broker.test.mjs',
|
|
8
|
+
'test/r193-uac-r70-bridge.test.mjs'
|
|
9
|
+
];
|
|
10
|
+
const r=spawnSync(process.execPath,['--test','--test-concurrency=1',...files],{
|
|
11
|
+
cwd:root,encoding:'utf8',timeout:150000,maxBuffer:1024*1024,windowsHide:true,shell:false
|
|
12
|
+
});
|
|
13
|
+
const output=(r.stdout??'')+'\n'+(r.stderr??'');
|
|
14
|
+
const get=n=>{const m=[...output.matchAll(new RegExp('(?:^|\\n)[ℹ#]\\s*'+n+'\\s+(\\d+)','g'))];return m.length?Number(m.at(-1)[1]):null;};
|
|
15
|
+
const summary={tests:get('tests'),pass:get('pass'),fail:get('fail'),skipped:get('skipped')};
|
|
16
|
+
const pass=r.status===0&&summary.tests>=12&&summary.fail===0&&summary.pass===summary.tests-summary.skipped;
|
|
17
|
+
console.log(JSON.stringify({schema:'deadbyte.r193-uac-bridge-focused.v1',
|
|
18
|
+
status:pass?'PASS':'FAIL',exit:r.status,summary,files,
|
|
19
|
+
no_real_owner_enrollment:true,authority_granted:false,
|
|
20
|
+
production_untouched:true,tail:pass?null:output.slice(-12000)}));
|
|
21
|
+
if(!pass)process.exitCode=1;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Read-only bounded stdin candidate comparison. Does not promote, publish, or grant authority.
|
|
2
|
+
import { evaluateCandidateSelectionR205 } from '../src/candidate-selection-r205.mjs';
|
|
3
|
+
|
|
4
|
+
const MAX_BYTES=64*1024;
|
|
5
|
+
let length=0;
|
|
6
|
+
const parts=[];
|
|
7
|
+
try{
|
|
8
|
+
for await (const chunk of process.stdin){
|
|
9
|
+
length+=chunk.length;
|
|
10
|
+
if(length>MAX_BYTES)throw Error('R205_INPUT_LIMIT_EXCEEDED');
|
|
11
|
+
parts.push(chunk);
|
|
12
|
+
}
|
|
13
|
+
if(length<1)throw Error('R205_INPUT_EMPTY');
|
|
14
|
+
const raw=Buffer.concat(parts).toString('utf8');
|
|
15
|
+
const parsed=JSON.parse(raw);
|
|
16
|
+
const result=evaluateCandidateSelectionR205(parsed);
|
|
17
|
+
process.stdout.write(JSON.stringify(result)+'\n');
|
|
18
|
+
if(result.state!=='PROVISIONAL_CANDIDATE')process.exitCode=2;
|
|
19
|
+
}catch(error){
|
|
20
|
+
process.stderr.write('R205_SELECTION_REFUSED '+String(error?.message??error)+'\n');
|
|
21
|
+
process.exitCode=3;
|
|
22
|
+
}
|