deadbyte-mcp 0.15.71 → 0.15.74
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 +81 -28
- package/PROVENANCE.json +56 -16
- package/README.txt +24 -18
- package/RELEASE.json +67 -18
- package/SBOM.cdx.json +14 -2
- package/TEST-MATRIX.json +44 -2
- package/bin/deadbyte-owner-token-r193.exe +0 -0
- package/bridge/package.json +1 -1
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R184-package-lock.json +2 -2
- package/docs/R192-LIVE-LOG-HARDENING.txt +39 -0
- package/native/deadbyte-owner-token-r193.cpp +156 -0
- package/package.json +2 -2
- package/scripts/gate-windows-r193.ps1 +108 -0
- package/scripts/gate-windows-r194.ps1 +112 -0
- package/scripts/npm-interactive-r194.mjs +13 -0
- package/scripts/npm-publication-r65.mjs +64 -4
- package/scripts/npm-release-r48.mjs +58 -11
- package/scripts/npm-release-readiness-r193.mjs +80 -0
- package/scripts/npm-release-readiness-r194.mjs +80 -0
- package/scripts/r188-owner-uac-review.ps1 +91 -0
- package/scripts/r188-uac-review-preview.mjs +65 -0
- package/scripts/r192-focused.mjs +10 -0
- package/scripts/r192-release-tests.mjs +8 -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 +50 -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/src/deadbyte-cli.mjs +2 -1
- 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/remote-live-log.mjs +22 -1
- package/src/version.mjs +1 -1
- package/test/process-policy-refresh.test.mjs +1 -1
- 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 -2
- 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 +11 -11
- 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-live-log-hardening.test.mjs +50 -0
- package/test/r192-release-identity.test.mjs +40 -0
- 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 +56 -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 +43 -0
- package/test/r194-release-readiness.test.mjs +87 -0
- package/test/r194-windows-gate.test.mjs +21 -0
- package/test/r59-tool-surface-compat.test.mjs +1 -1
|
@@ -55,7 +55,7 @@ function result(state,extra={}){return Object.freeze({state,...extra});}
|
|
|
55
55
|
// The no-clobber claim is the dispatch fence: once it exists, no invocation may
|
|
56
56
|
// submit the same immutable npm version again, even if the first process dies.
|
|
57
57
|
export async function reconcileNpmPublication({recordPath,version,manifestSha256,localBytes,publish,lookup,tags,fetchTarball,
|
|
58
|
-
timeoutMs=180000,pollMs=1500,sleep=pause,now=()=>performance.now()}){
|
|
58
|
+
timeoutMs=180000,pollMs=1500,sleep=pause,now=()=>performance.now(),authorizeAuthRetry=null,authorizePublish=null}){
|
|
59
59
|
if(!path.isAbsolute(recordPath)||!/^\d+\.\d+\.\d+$/.test(version)||!HEX64.test(String(manifestSha256))||!Buffer.isBuffer(localBytes)||
|
|
60
60
|
!Number.isSafeInteger(timeoutMs)||timeoutMs<0||timeoutMs>900000||!Number.isSafeInteger(pollMs)||pollMs<1||pollMs>30000||
|
|
61
61
|
[publish,lookup,tags,fetchTarball,sleep,now].some(fn=>typeof fn!=='function'))throw new Error('npm publication reconciliation inputs invalid');
|
|
@@ -97,6 +97,14 @@ export async function reconcileNpmPublication({recordPath,version,manifestSha256
|
|
|
97
97
|
}
|
|
98
98
|
if(!claim){
|
|
99
99
|
if(observed)return observed; // Registry outage: do not submit while identity is unknown.
|
|
100
|
+
// R194: check authenticated package ownership before creating the irreversible claim.
|
|
101
|
+
if(authorizePublish!==null){
|
|
102
|
+
if(typeof authorizePublish!=='function')throw new Error('npm owner preflight invalid');
|
|
103
|
+
let authorized=false;
|
|
104
|
+
try{authorized=(await authorizePublish(Object.freeze({version,manifest_sha256:manifestSha256,tarball_sha256:tarballSha256})))===true;}
|
|
105
|
+
catch{return result('AUTH_REQUIRED',{reason:'owner authentication preflight failed before submission'});}
|
|
106
|
+
if(!authorized)return result('AUTH_REQUIRED',{reason:'owner authentication preflight is not confirmed'});
|
|
107
|
+
}
|
|
100
108
|
let created=false;
|
|
101
109
|
try{
|
|
102
110
|
await publishClaim(claimFile,{schema:'deadbyte.npm-publication-claim.r65.v1',version,manifest_sha256:manifestSha256,tarball_sha256:tarballSha256,created_at_utc:new Date().toISOString()});
|
|
@@ -114,15 +122,67 @@ export async function reconcileNpmPublication({recordPath,version,manifestSha256
|
|
|
114
122
|
await publish();
|
|
115
123
|
await event(recordPath,'SUBMISSION_ACCEPTED',{tarball_sha256:tarballSha256});
|
|
116
124
|
}catch(error){
|
|
117
|
-
const auth=/E401|E403|EOTP|authentication|one-time password|2FA/i.test(String(error?.code??'')+' '+String(error?.message??''));
|
|
125
|
+
const auth=/E401|E403|EOTP|EAUTH|ENEEDAUTH|authentication|one-time password|2FA/i.test(String(error?.code??'')+' '+String(error?.message??''));
|
|
118
126
|
await event(recordPath,auth?'AUTH_REQUIRED':'OUTCOME_UNKNOWN',{error_code:String(error?.code??'UNKNOWN').slice(0,64)});
|
|
119
127
|
return result(auth?'AUTH_REQUIRED':'OUTCOME_UNKNOWN',{reason:String(error?.code??error?.message??error)});
|
|
120
128
|
}
|
|
121
129
|
}
|
|
122
130
|
}
|
|
123
131
|
const states=await priorState(recordPath);
|
|
124
|
-
|
|
125
|
-
|
|
132
|
+
const authRejected=states.some(item=>item.state==='AUTH_REQUIRED');
|
|
133
|
+
let accepted=states.some(item=>item.state==='SUBMISSION_ACCEPTED');
|
|
134
|
+
if(authRejected&&!accepted){
|
|
135
|
+
// An original ambiguous outcome cannot become retryable via a later login.
|
|
136
|
+
if(states.some(item=>item.state==='OUTCOME_UNKNOWN'))
|
|
137
|
+
return result('OUTCOME_UNKNOWN',{reason:'submission outcome is ambiguous; original claim retained'});
|
|
138
|
+
const retryFile=path.join(recordPath,'auth-retry-claim.json');
|
|
139
|
+
const retry=await readClaim(retryFile);
|
|
140
|
+
if(retry?.incomplete)return result('OUTCOME_UNKNOWN',{reason:'incomplete auth retry claim'});
|
|
141
|
+
if(retry){
|
|
142
|
+
if(retry.schema!=='deadbyte.npm-auth-retry-claim.r194.v1'||retry.version!==version||
|
|
143
|
+
retry.manifest_sha256!==manifestSha256||retry.tarball_sha256!==tarballSha256)
|
|
144
|
+
return result('REGISTRY_REJECTED',{reason:'auth retry claim artifact mismatch'});
|
|
145
|
+
return result('AUTH_REQUIRED',{reason:'one-time auth retry was already dispatched or claimed; do not retry'});
|
|
146
|
+
}
|
|
147
|
+
if(typeof authorizeAuthRetry!=='function')return result('AUTH_REQUIRED');
|
|
148
|
+
let allowed=false;
|
|
149
|
+
try{
|
|
150
|
+
allowed=(await authorizeAuthRetry(Object.freeze({
|
|
151
|
+
version,manifest_sha256:manifestSha256,tarball_sha256:tarballSha256
|
|
152
|
+
})))===true;
|
|
153
|
+
}catch{return result('AUTH_REQUIRED',{reason:'fresh authenticated owner check failed'});}
|
|
154
|
+
if(!allowed)return result('AUTH_REQUIRED',{reason:'authenticated owner preflight not confirmed'});
|
|
155
|
+
// Re-read registry just before the one-time retry fence.
|
|
156
|
+
const beforeRetry=await check();
|
|
157
|
+
if(beforeRetry?.state==='REGISTRY_IDENTITY_VERIFIED'||beforeRetry?.state==='REGISTRY_REJECTED')return beforeRetry;
|
|
158
|
+
if(beforeRetry?.state==='OUTCOME_UNKNOWN')return beforeRetry;
|
|
159
|
+
try{
|
|
160
|
+
await writeExclusive(retryFile,{
|
|
161
|
+
schema:'deadbyte.npm-auth-retry-claim.r194.v1',version,
|
|
162
|
+
manifest_sha256:manifestSha256,tarball_sha256:tarballSha256,
|
|
163
|
+
created_at_utc:new Date().toISOString()
|
|
164
|
+
});
|
|
165
|
+
}catch(error){
|
|
166
|
+
if(error?.code==='EEXIST')return result('AUTH_REQUIRED',{reason:'concurrent auth retry already claimed; do not dispatch'});
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
// A durable retry claim is written BEFORE any additional network effect.
|
|
170
|
+
await event(recordPath,'AUTH_RETRY_STARTED',{tarball_sha256:tarballSha256});
|
|
171
|
+
try{
|
|
172
|
+
await publish();
|
|
173
|
+
await event(recordPath,'SUBMISSION_ACCEPTED',{tarball_sha256:tarballSha256,retry:'auth-only'});
|
|
174
|
+
accepted=true;
|
|
175
|
+
}catch(error){
|
|
176
|
+
const auth=/E401|E403|EOTP|EAUTH|ENEEDAUTH|authentication|one-time password|2FA/i.test(
|
|
177
|
+
String(error?.code??'')+' '+String(error?.message??''));
|
|
178
|
+
await event(recordPath,auth?'AUTH_REQUIRED':'OUTCOME_UNKNOWN',{
|
|
179
|
+
error_code:String(error?.code??'UNKNOWN').slice(0,64),retry:'auth-only'
|
|
180
|
+
});
|
|
181
|
+
return result(auth?'AUTH_REQUIRED':'OUTCOME_UNKNOWN',{
|
|
182
|
+
reason:'one-time auth retry rejected or unverified; retry claim retained'
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
126
186
|
const started=now();
|
|
127
187
|
do{
|
|
128
188
|
observed=await check();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { reconcileNpmPublication } from './npm-publication-r65.mjs';
|
|
4
|
+
import { classifyNpmPublishFailure } from './npm-interactive-r194.mjs';
|
|
4
5
|
import { mkdtemp, readFile, rm, unlink, mkdir } from 'node:fs/promises';
|
|
5
6
|
import {preserveNpmTarball,assertVacantPackDestination} from './npm-preserve-artifact-r67.mjs';
|
|
6
7
|
import {assertR67NpmPublishReady} from './npm-release-readiness-r67.mjs';
|
|
@@ -12,8 +13,10 @@ import {assertR165NpmPublishReady} from './npm-release-readiness-r165.mjs';
|
|
|
12
13
|
import {assertR166NpmPublishReady} from './npm-release-readiness-r166.mjs';
|
|
13
14
|
import {assertR167NpmPublishReady,assertR167PackedIdentity} from './npm-release-readiness-r167.mjs';
|
|
14
15
|
import {assertR168NpmPublishReady,assertR168PackedIdentity} from './npm-release-readiness-r168.mjs';
|
|
16
|
+
import {assertR193NpmPublishReady,assertR193PackedIdentity} from './npm-release-readiness-r193.mjs';
|
|
17
|
+
import {assertR194NpmPublishReady,assertR194PackedIdentity} from './npm-release-readiness-r194.mjs';
|
|
15
18
|
import {resumePackRecord} from '../src/npm-publication-resume-r168.mjs';
|
|
16
|
-
import { existsSync } from 'node:fs';
|
|
19
|
+
import { existsSync,mkdtempSync,readdirSync,readFileSync,rmSync } from 'node:fs';
|
|
17
20
|
import { resolveStateRoot } from '../src/state-root.mjs';
|
|
18
21
|
import path from 'node:path';
|
|
19
22
|
import os from 'node:os';
|
|
@@ -46,18 +49,39 @@ function runNpm(args, options = {}) {
|
|
|
46
49
|
return run(node, [npmCli, ...args], options);
|
|
47
50
|
}
|
|
48
51
|
|
|
52
|
+
function verifyNpmOwnerPreflight(){
|
|
53
|
+
try{
|
|
54
|
+
const registry=runNpm(['config','get','registry'],{timeout:30000}).trim();
|
|
55
|
+
if(registry!=='https://registry.npmjs.org/')return false;
|
|
56
|
+
const name=runNpm(['whoami','--registry=https://registry.npmjs.org/'],{timeout:30000}).trim();
|
|
57
|
+
if(name!=='deadbytes')return false;
|
|
58
|
+
const owners=runNpm(['owner','ls','deadbyte-mcp','--registry=https://registry.npmjs.org/'],{timeout:30000});
|
|
59
|
+
return owners.split(/\r?\n/).some(line=>/^\s*deadbytes(?:\s|$)/.test(line));
|
|
60
|
+
}catch{return false;}
|
|
61
|
+
}
|
|
62
|
+
|
|
49
63
|
function runNpmInteractive(args, options = {}) {
|
|
50
64
|
const { cwd = root, timeout = 300000 } = options;
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
const logDir=mkdtempSync(path.join(os.tmpdir(),'deadbyte-r194-npm-diagnostic-'));
|
|
66
|
+
try{
|
|
67
|
+
// Preserve interactive browser/2FA prompts; classify failures from the
|
|
68
|
+
// isolated npm debug log without printing or preserving credential data.
|
|
69
|
+
const r=spawnSync(node,[npmCli,...args,'--logs-dir',logDir],{
|
|
70
|
+
cwd,timeout,stdio:'inherit',shell:false,windowsHide:false
|
|
71
|
+
});
|
|
72
|
+
if(r.error)throw r.error;
|
|
73
|
+
if(r.status!==0){
|
|
74
|
+
const debug=readdirSync(logDir).filter(name=>name.endsWith('-debug-0.log')).slice(-3)
|
|
75
|
+
.map(name=>readFileSync(path.join(logDir,name),'utf8').slice(-16000)).join('\n');
|
|
76
|
+
const reason=classifyNpmPublishFailure({exitCode:r.status,output:debug});
|
|
77
|
+
const error=new Error('Interactive npm publish did not complete; classified '+reason);
|
|
78
|
+
error.code=reason;
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
return '';
|
|
82
|
+
}finally{
|
|
83
|
+
rmSync(logDir,{recursive:true,force:true});
|
|
84
|
+
}
|
|
61
85
|
}
|
|
62
86
|
|
|
63
87
|
function parseJson(text) {
|
|
@@ -123,6 +147,16 @@ async function main() {
|
|
|
123
147
|
if (!publishOnly) throw new Error('PUBLISH_BLOCKED R168 requires --publish-only; activation is separate');
|
|
124
148
|
r168Readiness=await assertR168NpmPublishReady(release,{root});
|
|
125
149
|
}
|
|
150
|
+
let r193Readiness=null;
|
|
151
|
+
if (!isVerifyOnly && version==='0.15.73') {
|
|
152
|
+
if (!publishOnly) throw new Error('PUBLISH_BLOCKED R193 requires --publish-only; owner enrollment, runtime migration, and activation are separate authority boundaries');
|
|
153
|
+
r193Readiness=await assertR193NpmPublishReady(release,{root});
|
|
154
|
+
}
|
|
155
|
+
let r194Readiness=null;
|
|
156
|
+
if (!isVerifyOnly && version==='0.15.74') {
|
|
157
|
+
if (!publishOnly) throw new Error('PUBLISH_BLOCKED R194 requires --publish-only; activation remains separately authorized');
|
|
158
|
+
r194Readiness=await assertR194NpmPublishReady(release,{root});
|
|
159
|
+
}
|
|
126
160
|
if (release.parent?.release_id !== provenance.parent_release_id ||
|
|
127
161
|
release.parent?.manifest_sha256 !== provenance.parent_manifest_sha256 ||
|
|
128
162
|
release.parent?.generation_manifest_sha256 !== provenance.parent_generation_manifest_sha256) {
|
|
@@ -262,10 +296,23 @@ async function main() {
|
|
|
262
296
|
...r168Readiness,observedSha256:createHash('sha256').update(localBytes).digest('hex'),
|
|
263
297
|
observedIntegrity:localIntegrity,observedShasum:packRecord.shasum
|
|
264
298
|
});
|
|
299
|
+
if(r193Readiness)assertR193PackedIdentity({
|
|
300
|
+
...r193Readiness,observedSha256:createHash('sha256').update(localBytes).digest('hex'),
|
|
301
|
+
observedIntegrity:localIntegrity,observedShasum:packRecord.shasum
|
|
302
|
+
});
|
|
303
|
+
if(r194Readiness)assertR194PackedIdentity({
|
|
304
|
+
...r194Readiness,observedSha256:createHash('sha256').update(localBytes).digest('hex'),
|
|
305
|
+
observedIntegrity:localIntegrity,observedShasum:packRecord.shasum
|
|
306
|
+
});
|
|
265
307
|
|
|
266
308
|
console.log('[6/7] Publishing artifact to npm registry...');
|
|
267
309
|
publishAttempted = true;
|
|
310
|
+
const ownerAuth=async subject=>
|
|
311
|
+
subject.version===version&&subject.manifest_sha256===manifestSha256&&
|
|
312
|
+
subject.tarball_sha256===createHash('sha256').update(localBytes).digest('hex')&&
|
|
313
|
+
verifyNpmOwnerPreflight();
|
|
268
314
|
const reconciliation=await reconcileNpmPublication({recordPath:publicationDir,version,manifestSha256,localBytes,
|
|
315
|
+
authorizePublish:ownerAuth,authorizeAuthRetry:ownerAuth,
|
|
269
316
|
publish:async()=>runNpmInteractive(['publish',publishTarball,'--access','public','--auth-type=web'],{timeout:300000}),
|
|
270
317
|
lookup:async()=>{
|
|
271
318
|
try{
|
|
@@ -0,0 +1,80 @@
|
|
|
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 hex64=value=>typeof value==='string'&&/^[0-9a-f]{64}$/.test(value);
|
|
7
|
+
const fail=message=>{throw new Error(`PUBLISH_BLOCKED R193 ${message}`);};
|
|
8
|
+
const required=(value,message)=>{if(!value)fail(message);};
|
|
9
|
+
|
|
10
|
+
async function checkedFile(file,expected,{maxBytes=64*1024*1024}={}){
|
|
11
|
+
required(hex64(expected),'evidence digest missing');
|
|
12
|
+
let info;try{info=await lstat(file);}catch{fail('evidence file absent');}
|
|
13
|
+
required(info.isFile()&&!info.isSymbolicLink()&&info.size>0&&info.size<=maxBytes,'evidence file invalid');
|
|
14
|
+
const bytes=await readFile(file);
|
|
15
|
+
required(sha256(bytes)===expected,'evidence digest differs');
|
|
16
|
+
return bytes;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function tapSummary(text,label){
|
|
20
|
+
const summaries=[...text.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)];
|
|
21
|
+
required(summaries.length>0,`${label} TAP summary absent`);
|
|
22
|
+
const last=summaries.at(-1);
|
|
23
|
+
return {tests:Number(last[1]),passes:Number(last[3]),fails:Number(last[4]),cancelled:Number(last[5]),skips:Number(last[6])};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function assertR193PackedIdentity({tarball_sha256,tarball_integrity,tarball_shasum,observedSha256,observedIntegrity,observedShasum}={}){
|
|
27
|
+
required(hex64(tarball_sha256)&&typeof tarball_integrity==='string'&&tarball_integrity.startsWith('sha512-')&&
|
|
28
|
+
typeof tarball_shasum==='string'&&/^[0-9a-f]{40}$/.test(tarball_shasum),'verified tarball identity invalid');
|
|
29
|
+
required(observedSha256===tarball_sha256&&observedIntegrity===tarball_integrity&&observedShasum===tarball_shasum,
|
|
30
|
+
'repacked tarball differs from verified closure bytes');
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function assertR193NpmPublishReady(release,{root,closureRoot=process.env.DEADBYTE_R193_CLOSURE_ROOT}={}){
|
|
35
|
+
required(release?.version==='0.15.73'&&release?.train==='R193'&&
|
|
36
|
+
release?.r193?.scope==='PROTECTED_OWNER_AUTHORIZATION_AND_LIMITED_RUNTIME_MIGRATION','candidate metadata invalid');
|
|
37
|
+
required(release?.r193?.public_tool_delta===0&&release?.r193?.authority_scope_delta===0,'public authority surface widened');
|
|
38
|
+
required(release?.parent?.version==='0.15.72'&&
|
|
39
|
+
release?.parent?.release_id==='v0.15.72-0a5d9436e06e2d9e'&&
|
|
40
|
+
release?.parent?.manifest_sha256==='0a5d9436e06e2d9e7cf9179417465955d8ca5ba88655b13dd019190243c1cc90'&&
|
|
41
|
+
release?.parent?.generation_manifest_sha256==='aec7b998306a3bbbcc59088648e7bc46ca7e203d749d4550b5b5f372032f92b0','lineage mismatch');
|
|
42
|
+
required(root&&path.isAbsolute(root)&&closureRoot&&path.isAbsolute(closureRoot),'closure root absent');
|
|
43
|
+
const expected=path.resolve(root,'..','..','testing','r193-final-closure');
|
|
44
|
+
required(path.resolve(closureRoot).toLowerCase()===expected.toLowerCase(),'closure root outside scoped testing folder');
|
|
45
|
+
|
|
46
|
+
let closure;try{closure=JSON.parse(await readFile(path.join(closureRoot,'closure.json'),'utf8'));}catch{fail('closure unreadable');}
|
|
47
|
+
required(closure?.schema==='deadbyte.r193-release-closure.v1'&&closure.status==='RELEASE_READY'&&
|
|
48
|
+
closure.version==='0.15.73'&&closure.train==='R193','closure status mismatch');
|
|
49
|
+
required(closure.production_release_id===release.parent.release_id&&
|
|
50
|
+
closure.production_manifest_sha256===release.parent.manifest_sha256&&
|
|
51
|
+
closure.production_generation_manifest_sha256===release.parent.generation_manifest_sha256,'production predecessor mismatch');
|
|
52
|
+
required(hex64(closure.manifest_sha256)&&closure.manifest_sha256===sha256(await readFile(path.join(root,'MANIFEST.SHA256'))),'final manifest drift');
|
|
53
|
+
required(closure.compact_tools===43&&closure.full_tools===117,'catalog count mismatch');
|
|
54
|
+
required(closure.owner_enrollment==='NOT_RUN'&&closure.limited_runtime_migration==='NOT_RUN'&&
|
|
55
|
+
closure.production_activation==='NOT_RUN','prepublication closure crossed production authority boundary');
|
|
56
|
+
|
|
57
|
+
const receipt=JSON.parse((await checkedFile(path.join(closureRoot,'protected-install-receipt.json'),closure.protected_receipt_sha256)).toString('utf8'));
|
|
58
|
+
required(receipt?.status==='PASS'&&receipt.package_version==='0.15.73'&&
|
|
59
|
+
receipt.source_manifest_sha256===closure.manifest_sha256&&receipt.protected_manifest_sha256===closure.manifest_sha256,
|
|
60
|
+
'protected exact-byte receipt mismatch');
|
|
61
|
+
const focused=(await checkedFile(path.join(closureRoot,'windows-focused.stdout.log'),closure.focused_stdout_sha256)).toString('utf8');
|
|
62
|
+
const full=(await checkedFile(path.join(closureRoot,'windows-full.stdout.log'),closure.full_stdout_sha256)).toString('utf8');
|
|
63
|
+
const verify=(await checkedFile(path.join(closureRoot,'npm-verify.stdout.log'),closure.verify_stdout_sha256)).toString('utf8');
|
|
64
|
+
required(focused.includes('R193_STAGE_PASS=focused'),'focused protected gate incomplete');
|
|
65
|
+
const focusedSummary=tapSummary(focused,'focused');
|
|
66
|
+
required(focusedSummary.fails===0&&focusedSummary.cancelled===0&&focusedSummary.passes>=60&&focusedSummary.skips<=2,'focused regression count mismatch');
|
|
67
|
+
required(full.includes('R193_STAGE_PASS=full'),'full protected gate incomplete');
|
|
68
|
+
const summary=tapSummary(full,'full');
|
|
69
|
+
required(summary.tests>=2150&&summary.passes>=2100&&summary.fails===0&&summary.cancelled===0&&summary.skips<=70&&summary.passes+summary.skips===summary.tests,
|
|
70
|
+
'full regression count mismatch');
|
|
71
|
+
required(verify.includes('VERIFY-ONLY PIPELINE PASS')&&verify.includes('Artifact isolation gate PASS')&&verify.includes('VERIFIED_TARBALL '),
|
|
72
|
+
'npm exact-artifact verification incomplete');
|
|
73
|
+
const line=verify.split(/\r?\n/).find(value=>value.startsWith('VERIFIED_TARBALL '));
|
|
74
|
+
let artifact;try{artifact=JSON.parse(line.slice('VERIFIED_TARBALL '.length));}catch{fail('tarball evidence malformed');}
|
|
75
|
+
required(artifact.filename==='deadbyte-mcp-0.15.73.tgz'&&artifact.bytes>0&&artifact.sha256===closure.tarball_sha256&&
|
|
76
|
+
artifact.integrity===closure.tarball_integrity&&artifact.shasum===closure.tarball_shasum,'tarball identity mismatch');
|
|
77
|
+
return Object.freeze({status:'R193_RELEASE_READY_EVIDENCE_BOUND',manifest_sha256:closure.manifest_sha256,
|
|
78
|
+
tarball_sha256:artifact.sha256,tarball_integrity:artifact.integrity,tarball_shasum:artifact.shasum,
|
|
79
|
+
tests:summary.tests,passes:summary.passes,skips:summary.skips});
|
|
80
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
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 hex64=value=>typeof value==='string'&&/^[0-9a-f]{64}$/.test(value);
|
|
7
|
+
const fail=message=>{throw new Error(`PUBLISH_BLOCKED R194 ${message}`);};
|
|
8
|
+
const required=(value,message)=>{if(!value)fail(message);};
|
|
9
|
+
|
|
10
|
+
async function checkedFile(file,expected,{maxBytes=64*1024*1024}={}){
|
|
11
|
+
required(hex64(expected),'evidence digest missing');
|
|
12
|
+
let info;try{info=await lstat(file);}catch{fail('evidence file absent');}
|
|
13
|
+
required(info.isFile()&&!info.isSymbolicLink()&&info.size>0&&info.size<=maxBytes,'evidence file invalid');
|
|
14
|
+
const bytes=await readFile(file);
|
|
15
|
+
required(sha256(bytes)===expected,'evidence digest differs');
|
|
16
|
+
return bytes;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function tapSummary(text,label){
|
|
20
|
+
const summaries=[...text.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)];
|
|
21
|
+
required(summaries.length>0,`${label} TAP summary absent`);
|
|
22
|
+
const last=summaries.at(-1);
|
|
23
|
+
return {tests:Number(last[1]),passes:Number(last[3]),fails:Number(last[4]),cancelled:Number(last[5]),skips:Number(last[6])};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function assertR194PackedIdentity({tarball_sha256,tarball_integrity,tarball_shasum,observedSha256,observedIntegrity,observedShasum}={}){
|
|
27
|
+
required(hex64(tarball_sha256)&&typeof tarball_integrity==='string'&&tarball_integrity.startsWith('sha512-')&&
|
|
28
|
+
typeof tarball_shasum==='string'&&/^[0-9a-f]{40}$/.test(tarball_shasum),'verified tarball identity invalid');
|
|
29
|
+
required(observedSha256===tarball_sha256&&observedIntegrity===tarball_integrity&&observedShasum===tarball_shasum,
|
|
30
|
+
'repacked tarball differs from verified closure bytes');
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function assertR194NpmPublishReady(release,{root,closureRoot=process.env.DEADBYTE_R194_CLOSURE_ROOT}={}){
|
|
35
|
+
required(release?.version==='0.15.74'&&release?.train==='R194'&&
|
|
36
|
+
release?.r194?.scope==='NPM_AUTH_RECOVERY_AND_OWNER_AUTHORIZATION','candidate metadata invalid');
|
|
37
|
+
required(release?.r194?.public_tool_delta===0&&release?.r194?.authority_scope_delta===0,'public authority surface widened');
|
|
38
|
+
required(release?.parent?.version==='0.15.72'&&
|
|
39
|
+
release?.parent?.release_id==='v0.15.72-0a5d9436e06e2d9e'&&
|
|
40
|
+
release?.parent?.manifest_sha256==='0a5d9436e06e2d9e7cf9179417465955d8ca5ba88655b13dd019190243c1cc90'&&
|
|
41
|
+
release?.parent?.generation_manifest_sha256==='aec7b998306a3bbbcc59088648e7bc46ca7e203d749d4550b5b5f372032f92b0','lineage mismatch');
|
|
42
|
+
required(root&&path.isAbsolute(root)&&closureRoot&&path.isAbsolute(closureRoot),'closure root absent');
|
|
43
|
+
const expected=path.resolve(root,'..','..','testing','r194-final-closure');
|
|
44
|
+
required(path.resolve(closureRoot).toLowerCase()===expected.toLowerCase(),'closure root outside scoped testing folder');
|
|
45
|
+
|
|
46
|
+
let closure;try{closure=JSON.parse(await readFile(path.join(closureRoot,'closure.json'),'utf8'));}catch{fail('closure unreadable');}
|
|
47
|
+
required(closure?.schema==='deadbyte.r194-release-closure.v1'&&closure.status==='RELEASE_READY'&&
|
|
48
|
+
closure.version==='0.15.74'&&closure.train==='R194','closure status mismatch');
|
|
49
|
+
required(closure.production_release_id===release.parent.release_id&&
|
|
50
|
+
closure.production_manifest_sha256===release.parent.manifest_sha256&&
|
|
51
|
+
closure.production_generation_manifest_sha256===release.parent.generation_manifest_sha256,'production predecessor mismatch');
|
|
52
|
+
required(hex64(closure.manifest_sha256)&&closure.manifest_sha256===sha256(await readFile(path.join(root,'MANIFEST.SHA256'))),'final manifest drift');
|
|
53
|
+
required(closure.compact_tools===43&&closure.full_tools===117,'catalog count mismatch');
|
|
54
|
+
required(closure.owner_enrollment==='NOT_RUN'&&closure.limited_runtime_migration==='NOT_RUN'&&
|
|
55
|
+
closure.production_activation==='NOT_RUN','prepublication closure crossed production authority boundary');
|
|
56
|
+
|
|
57
|
+
const receipt=JSON.parse((await checkedFile(path.join(closureRoot,'protected-install-receipt.json'),closure.protected_receipt_sha256)).toString('utf8'));
|
|
58
|
+
required(receipt?.status==='PASS'&&receipt.package_version==='0.15.74'&&
|
|
59
|
+
receipt.source_manifest_sha256===closure.manifest_sha256&&receipt.protected_manifest_sha256===closure.manifest_sha256,
|
|
60
|
+
'protected exact-byte receipt mismatch');
|
|
61
|
+
const focused=(await checkedFile(path.join(closureRoot,'windows-focused.stdout.log'),closure.focused_stdout_sha256)).toString('utf8');
|
|
62
|
+
const full=(await checkedFile(path.join(closureRoot,'windows-full.stdout.log'),closure.full_stdout_sha256)).toString('utf8');
|
|
63
|
+
const verify=(await checkedFile(path.join(closureRoot,'npm-verify.stdout.log'),closure.verify_stdout_sha256)).toString('utf8');
|
|
64
|
+
required(focused.includes('R194_STAGE_PASS=focused'),'focused protected gate incomplete');
|
|
65
|
+
const focusedSummary=tapSummary(focused,'focused');
|
|
66
|
+
required(focusedSummary.fails===0&&focusedSummary.cancelled===0&&focusedSummary.passes>=60&&focusedSummary.skips<=2,'focused regression count mismatch');
|
|
67
|
+
required(full.includes('R194_STAGE_PASS=full'),'full protected gate incomplete');
|
|
68
|
+
const summary=tapSummary(full,'full');
|
|
69
|
+
required(summary.tests>=2150&&summary.passes>=2100&&summary.fails===0&&summary.cancelled===0&&summary.skips<=70&&summary.passes+summary.skips===summary.tests,
|
|
70
|
+
'full regression count mismatch');
|
|
71
|
+
required(verify.includes('VERIFY-ONLY PIPELINE PASS')&&verify.includes('Artifact isolation gate PASS')&&verify.includes('VERIFIED_TARBALL '),
|
|
72
|
+
'npm exact-artifact verification incomplete');
|
|
73
|
+
const line=verify.split(/\r?\n/).find(value=>value.startsWith('VERIFIED_TARBALL '));
|
|
74
|
+
let artifact;try{artifact=JSON.parse(line.slice('VERIFIED_TARBALL '.length));}catch{fail('tarball evidence malformed');}
|
|
75
|
+
required(artifact.filename==='deadbyte-mcp-0.15.74.tgz'&&artifact.bytes>0&&artifact.sha256===closure.tarball_sha256&&
|
|
76
|
+
artifact.integrity===closure.tarball_integrity&&artifact.shasum===closure.tarball_shasum,'tarball identity mismatch');
|
|
77
|
+
return Object.freeze({status:'R194_RELEASE_READY_EVIDENCE_BOUND',manifest_sha256:closure.manifest_sha256,
|
|
78
|
+
tarball_sha256:artifact.sha256,tarball_integrity:artifact.integrity,tarball_shasum:artifact.shasum,
|
|
79
|
+
tests:summary.tests,passes:summary.passes,skips:summary.skips});
|
|
80
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#requires -Version 5.1
|
|
2
|
+
#requires -RunAsAdministrator
|
|
3
|
+
# R188: owner-operated elevated REVIEW receipt only. Never issues an agent grant.
|
|
4
|
+
# Invoke by hand from a trusted interactive desktop; not through an agent or MCP.
|
|
5
|
+
[CmdletBinding()]
|
|
6
|
+
param([Parameter(Mandatory=$true)][string]$RequestPath)
|
|
7
|
+
$ErrorActionPreference='Stop'
|
|
8
|
+
Set-StrictMode -Version Latest
|
|
9
|
+
$root=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
|
10
|
+
$program=[IO.Path]::GetFullPath($env:ProgramFiles).TrimEnd('\')+'\'
|
|
11
|
+
if(-not ($root.TrimEnd('\')+'\').StartsWith($program,[StringComparison]::OrdinalIgnoreCase)){
|
|
12
|
+
throw 'R188 must run from an installed Program Files release, not a writable development directory'
|
|
13
|
+
}
|
|
14
|
+
$settings=Get-ItemProperty -LiteralPath 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
|
|
15
|
+
if([int]$settings.EnableLUA -ne 1 -or [int]$settings.PromptOnSecureDesktop -ne 1 -or
|
|
16
|
+
[int]$settings.ConsentPromptBehaviorAdmin -eq 0){
|
|
17
|
+
throw 'R188 UAC secure desktop and nonautomatic admin consent must be enabled'
|
|
18
|
+
}
|
|
19
|
+
if(-not [Environment]::UserInteractive){throw 'R188 requires a local interactive owner session'}
|
|
20
|
+
$node='C:\Program Files\nodejs\node.exe'
|
|
21
|
+
if(-not (Test-Path -LiteralPath $node -PathType Leaf)){throw 'R188 trusted Node executable missing'}
|
|
22
|
+
$request=[IO.Path]::GetFullPath($RequestPath)
|
|
23
|
+
$preflight=Join-Path $root 'scripts\r188-uac-review-preview.mjs'
|
|
24
|
+
$raw=(& $node $preflight $request 2>&1 | Out-String)
|
|
25
|
+
if($LASTEXITCODE -ne 0){throw ('R188 preflight blocked: '+$raw.Substring(0,[Math]::Min(300,$raw.Length)))}
|
|
26
|
+
$preview=$raw|ConvertFrom-Json
|
|
27
|
+
if([string]$preview.status -cne 'R188_PROTECTED_REVIEW_PREVIEW_ONLY' -or
|
|
28
|
+
[bool]$preview.authority_granted -or [bool]$preview.owner_approved -or
|
|
29
|
+
[bool]$preview.live_epoch_verified -or [bool]$preview.live_policies_verified){
|
|
30
|
+
throw 'R188 preview unexpectedly claims grant/authorization'
|
|
31
|
+
}
|
|
32
|
+
$phrase=[string]$preview.review_phrase
|
|
33
|
+
if($phrase -cnotmatch '^APPROVE-[A-F0-9]{12}$'){throw 'R188 owner review phrase invalid'}
|
|
34
|
+
Write-Host ''
|
|
35
|
+
Write-Host 'DEADBYTE R188 / OWNER UAC REVIEW' -ForegroundColor Yellow
|
|
36
|
+
Write-Host 'This is a read-only scope REVIEW. It will NOT arm Autonomous.'
|
|
37
|
+
Write-Host ('Request SHA-256: '+[string]$preview.request_sha256)
|
|
38
|
+
Write-Host ('Project: '+[string]$preview.project_root)
|
|
39
|
+
Write-Host ('Action: '+[string]$preview.operation)
|
|
40
|
+
Write-Host ('Duration cap seconds: '+[string]$preview.max_seconds)
|
|
41
|
+
Write-Host ('Expires UTC: '+[string]$preview.expires_at_utc)
|
|
42
|
+
Write-Host ('Release manifest SHA-256: '+[string]$preview.installed_manifest_sha256)
|
|
43
|
+
Write-Host 'Coding policy and live machine epoch still require an independent trusted runtime read.'
|
|
44
|
+
Write-Host 'Do not approve requests you did not initiate and inspect yourself.'
|
|
45
|
+
$entered=Read-Host ('If you recognize this exact request, type '+$phrase+' (anything else rejects)')
|
|
46
|
+
if($entered -cne $phrase){throw 'R188 owner rejected request'}
|
|
47
|
+
$target='C:\Program Files\DEADBYTE-R188-OwnerReview'
|
|
48
|
+
if(-not (Test-Path -LiteralPath $target -PathType Container)){
|
|
49
|
+
$admins=New-Object Security.Principal.SecurityIdentifier('S-1-5-32-544')
|
|
50
|
+
$system=New-Object Security.Principal.SecurityIdentifier('S-1-5-18')
|
|
51
|
+
$users=New-Object Security.Principal.SecurityIdentifier('S-1-5-32-545')
|
|
52
|
+
New-Item -ItemType Directory -Path $target -ErrorAction Stop | Out-Null
|
|
53
|
+
$acl=New-Object Security.AccessControl.DirectorySecurity
|
|
54
|
+
$acl.SetAccessRuleProtection($true,$false)
|
|
55
|
+
$acl.SetOwner($admins)
|
|
56
|
+
$inherit=[Security.AccessControl.InheritanceFlags]'ContainerInherit, ObjectInherit'
|
|
57
|
+
$none=[Security.AccessControl.PropagationFlags]::None
|
|
58
|
+
$allow=[Security.AccessControl.AccessControlType]::Allow
|
|
59
|
+
$acl.AddAccessRule((New-Object Security.AccessControl.FileSystemAccessRule($admins,'FullControl',$inherit,$none,$allow)))
|
|
60
|
+
$acl.AddAccessRule((New-Object Security.AccessControl.FileSystemAccessRule($system,'FullControl',$inherit,$none,$allow)))
|
|
61
|
+
$acl.AddAccessRule((New-Object Security.AccessControl.FileSystemAccessRule($users,'ReadAndExecute',$inherit,$none,$allow)))
|
|
62
|
+
Set-Acl -LiteralPath $target -AclObject $acl
|
|
63
|
+
}
|
|
64
|
+
$receipt=[ordered]@{
|
|
65
|
+
schema='deadbyte.r188-uac-owner-review.v1'
|
|
66
|
+
status='REVIEWED_NO_GRANT'
|
|
67
|
+
request_sha256=[string]$preview.request_sha256
|
|
68
|
+
release_manifest_sha256=[string]$preview.installed_manifest_sha256
|
|
69
|
+
reviewed_scope=[string]$preview.operation
|
|
70
|
+
project_root=[string]$preview.project_root
|
|
71
|
+
duration_cap_seconds=[int]$preview.max_seconds
|
|
72
|
+
reviewed_at_utc=[DateTime]::UtcNow.ToString('o')
|
|
73
|
+
live_epoch_verified=$false
|
|
74
|
+
live_policies_verified=$false
|
|
75
|
+
owner_reviewed=$true
|
|
76
|
+
authority_granted=$false
|
|
77
|
+
grant_issued=$false
|
|
78
|
+
}
|
|
79
|
+
$name=([string]$preview.request_sha256)+'.review.json'
|
|
80
|
+
$dest=Join-Path $target $name
|
|
81
|
+
$text=$receipt|ConvertTo-Json -Compress
|
|
82
|
+
$utf8=New-Object Text.UTF8Encoding($false)
|
|
83
|
+
$stream=[IO.File]::Open($dest,[IO.FileMode]::CreateNew,[IO.FileAccess]::Write,[IO.FileShare]::None)
|
|
84
|
+
try{
|
|
85
|
+
$bytes=$utf8.GetBytes($text+[Environment]::NewLine)
|
|
86
|
+
$stream.Write($bytes,0,$bytes.Length)
|
|
87
|
+
$stream.Flush($true)
|
|
88
|
+
}finally{$stream.Dispose()}
|
|
89
|
+
Write-Host 'R188_OWNER_REVIEWED_NO_GRANT' -ForegroundColor Yellow
|
|
90
|
+
Write-Host ('Receipt: '+$dest)
|
|
91
|
+
Write-Host 'No PEM, Windows Hello, Autonomous grant, npm publish, or production activation performed.'
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Protected owner's manual UAC review: verify a request without granting.
|
|
2
|
+
// Requested policy / epoch hashes are UNTRUSTED previews until a protected
|
|
3
|
+
// runtime reader binds them; never interpret this as an authorization token.
|
|
4
|
+
import {readFile,lstat} from 'node:fs/promises';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {fileURLToPath} from 'node:url';
|
|
7
|
+
import {inspectTrustedInstallation} from '../src/privileged-install-trust-r89.mjs';
|
|
8
|
+
import {parseManifest,verifyManifestTreeExact} from '../src/release-slot.mjs';
|
|
9
|
+
import {verifyR188OwnerUacRequest} from '../src/r188-owner-uac.mjs';
|
|
10
|
+
const thisFile=fileURLToPath(import.meta.url);
|
|
11
|
+
const root=path.resolve(path.dirname(thisFile),'..');
|
|
12
|
+
const args=process.argv.slice(2);
|
|
13
|
+
function block(reason){throw Error('R188_UAC_REVIEW_BLOCKED '+reason);}
|
|
14
|
+
async function bounded(file,max){
|
|
15
|
+
const st=await lstat(file);
|
|
16
|
+
if(!st.isFile()||st.isSymbolicLink()||st.size<1||st.size>max)block('UNSAFE_FILE');
|
|
17
|
+
return readFile(file);
|
|
18
|
+
}
|
|
19
|
+
async function main(){
|
|
20
|
+
if(process.platform!=='win32'||process.arch!=='x64')block('WINDOWS_X64_REQUIRED');
|
|
21
|
+
if(args.length!==1||!path.win32.isAbsolute(args[0]))block('ABSOLUTE_REQUEST_REQUIRED');
|
|
22
|
+
const os=await inspectTrustedInstallation([
|
|
23
|
+
process.execPath,thisFile,
|
|
24
|
+
path.join(root,'MANIFEST.SHA256'),
|
|
25
|
+
path.join(root,'src','r188-owner-uac.mjs'),
|
|
26
|
+
path.join(root,'src','canonical-json.mjs'),
|
|
27
|
+
path.join(root,'src','privileged-install-trust-r89.mjs')
|
|
28
|
+
]);
|
|
29
|
+
if(!os.trusted)block('R89_PROTECTED_INSTALLATION_REQUIRED');
|
|
30
|
+
const manifestBytes=await bounded(path.join(root,'MANIFEST.SHA256'),512*1024);
|
|
31
|
+
const manifest=parseManifest(manifestBytes.toString('utf8'));
|
|
32
|
+
await verifyManifestTreeExact(root,manifest);
|
|
33
|
+
const request=JSON.parse((await bounded(args[0],64*1024)).toString('utf8'));
|
|
34
|
+
const subject=request.subject;
|
|
35
|
+
if(!subject||typeof subject!=='object')block('SUBJECT_MISSING');
|
|
36
|
+
const expected={
|
|
37
|
+
machineEpoch:subject.machine_epoch,
|
|
38
|
+
releaseManifestSha256:subject.release_manifest_sha256,
|
|
39
|
+
codingPolicySha256:subject.coding_policy_sha256,
|
|
40
|
+
autonomousPolicySha256:subject.autonomous_policy_sha256,
|
|
41
|
+
rootId:subject.root_id,projectRoot:subject.project_root,
|
|
42
|
+
maxSeconds:subject.max_seconds
|
|
43
|
+
};
|
|
44
|
+
// Strict self-consistency proof, NOT yet verification of the live epoch/policy.
|
|
45
|
+
const verified=verifyR188OwnerUacRequest({request,expected,now:new Date()});
|
|
46
|
+
const {createHash}=await import('node:crypto');
|
|
47
|
+
const actualManifest=createHash('sha256').update(manifestBytes).digest('hex');
|
|
48
|
+
if(subject.release_manifest_sha256!==actualManifest)block('INSTALLED_RELEASE_NOT_REVIEWED_RELEASE');
|
|
49
|
+
process.stdout.write(JSON.stringify({
|
|
50
|
+
status:'R188_PROTECTED_REVIEW_PREVIEW_ONLY',
|
|
51
|
+
request_sha256:verified.request_sha256,
|
|
52
|
+
review_phrase:verified.review_phrase,
|
|
53
|
+
root_id:subject.root_id,project_root:subject.project_root,
|
|
54
|
+
max_seconds:subject.max_seconds,operation:subject.operation,
|
|
55
|
+
expires_at_utc:subject.expires_at_utc,
|
|
56
|
+
requested_machine_epoch:subject.machine_epoch,
|
|
57
|
+
requested_coding_policy_sha256:subject.coding_policy_sha256,
|
|
58
|
+
requested_autonomous_policy_sha256:subject.autonomous_policy_sha256,
|
|
59
|
+
installed_manifest_sha256:actualManifest,
|
|
60
|
+
os_acl_trusted:true,
|
|
61
|
+
live_epoch_verified:false,live_policies_verified:false,
|
|
62
|
+
authority_granted:false,owner_approved:false
|
|
63
|
+
})+'\n');
|
|
64
|
+
}
|
|
65
|
+
main().catch(e=>{console.error(String(e?.message||e).slice(0,320));process.exitCode=2;});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {spawnSync} from 'node:child_process';
|
|
2
|
+
import {fileURLToPath} from 'node:url';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
5
|
+
const files=['test/r192-live-log-hardening.test.mjs',
|
|
6
|
+
'test/remote-live-log-r37.test.mjs','test/r51-live-log-polling.test.mjs',
|
|
7
|
+
'test/remote-log-decode-r60.test.mjs'];
|
|
8
|
+
const r=spawnSync(process.execPath,['--test','--test-concurrency=1',...files],{cwd:root,encoding:'utf8',windowsHide:true,shell:false,timeout:130000,maxBuffer:700000});
|
|
9
|
+
console.log(JSON.stringify({state:r.status===0?'PASS':'FAIL',exit:r.status,error:r.error?.message??null,stdout:r.stdout?.slice(-16000),stderr:r.stderr?.slice(-4000)}));
|
|
10
|
+
if(r.error||r.status!==0)process.exitCode=1;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import {spawnSync} from 'node:child_process';import path from 'node:path';import {fileURLToPath} from 'node:url';
|
|
2
|
+
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
3
|
+
const tests=['r192-release-identity','r192-live-log-hardening','r130-release-security-contract','r132-release-identity','r153-prepublish-identity','r167-release-contract','r172-release-contract','r173-release-contract','r179-release-contract','r190-release-contract','r191-release-contract','process-policy-refresh','remote-live-log-r37','r51-live-log-polling'];
|
|
4
|
+
const r=spawnSync(process.execPath,['--test','--test-concurrency=1',...tests.map(t=>'test/'+t+'.test.mjs')],{
|
|
5
|
+
cwd:root,encoding:'utf8',windowsHide:true,shell:false,timeout:220000,maxBuffer:1400000});
|
|
6
|
+
console.log(JSON.stringify({status:r.status===0?'PASS':'FAIL',exit:r.status,error:r.error?.message??null,
|
|
7
|
+
stdout:r.stdout?.slice(-18000),stderr:r.stderr?.slice(-2200)}));
|
|
8
|
+
if(r.error||r.status!==0)process.exitCode=1;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
$ErrorActionPreference='Stop'
|
|
2
|
+
$root=[IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
|
3
|
+
$source=Join-Path $root 'native\deadbyte-owner-token-r193.cpp'
|
|
4
|
+
$dest=Join-Path $root 'bin\deadbyte-owner-token-r193.exe'
|
|
5
|
+
$sourceDigest='99d1ec196e3e3242a9d44a6445d60abd1b3fbbd53dc9be126d4cc5bb91074099'
|
|
6
|
+
if ((Get-FileHash $source).Hash.ToLowerInvariant() -cne $sourceDigest) { throw 'R193_SOURCE_DRIFT' }
|
|
7
|
+
$gpp='C:\Users\DEADBYTE\scoop\apps\w64devkit\current\bin\g++.exe'
|
|
8
|
+
if(-not(Test-Path $gpp -PathType Leaf)){throw 'R193_TOOLCHAIN_MISSING'}
|
|
9
|
+
if(Test-Path $dest){throw 'R193_BINARY_EXISTS - do not overwrite without explicit inspection'}
|
|
10
|
+
& $gpp -std=c++17 -O2 -Wall -Wextra -Werror -static -s -o $dest $source -ladvapi32
|
|
11
|
+
if($LASTEXITCODE -ne 0){throw "R193_COMPILER_FAILED exit=$LASTEXITCODE"}
|
|
12
|
+
$raw=@(& $dest --report)
|
|
13
|
+
if($LASTEXITCODE -ne 0){throw "R193_REPORT_FAILED exit=$LASTEXITCODE"}
|
|
14
|
+
$report=($raw -join [Environment]::NewLine)|ConvertFrom-Json
|
|
15
|
+
if($report.schema -cne 'deadbyte.r193-native-token.v1' -or $report.authority_granted -or $report.owner_approved -or $report.parent_independence_verified){throw 'R193_INVALID_TOKEN_REPORT'}
|
|
16
|
+
$strictRaw=@(& $dest --require-standard)
|
|
17
|
+
$strictExit=$LASTEXITCODE
|
|
18
|
+
$strict=($strictRaw -join [Environment]::NewLine) | ConvertFrom-Json
|
|
19
|
+
if($report.status -cne $strict.status){throw 'R193_SUBJECT_STATUS_CHANGED'}
|
|
20
|
+
if($report.status -eq 'BLOCKED' -and $strictExit -ne 3){throw 'R193_FAIL_CLOSED_EXPECTED_EXIT3'}
|
|
21
|
+
if($report.status -eq 'STANDARD_TOKEN_OBSERVED' -and $strictExit -ne 0){throw 'R193_STANDARD_EXPECTED_EXIT0'}
|
|
22
|
+
[pscustomobject]@{schema='deadbyte.r193-native-token-acceptance.v1';status='PASS';source_sha256=$sourceDigest;binary_sha256=(Get-FileHash $dest).Hash.ToLowerInvariant();binary_bytes=(Get-Item $dest).Length;native_report=$report;strict_exit=$strictExit;no_grant=$true}|ConvertTo-Json -Depth 7 -Compress
|
|
@@ -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
|