deadbyte-mcp 0.15.47 → 0.15.49
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 +111 -48
- package/PROVENANCE.json +1 -1
- package/README.txt +18 -18
- package/RELEASE.json +3 -3
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +2 -2
- package/bench/r121-epoch-readhead.mjs +54 -0
- package/bench/r124-epoch-predecessor.mjs +53 -0
- package/bin/deadbyte-epoch-lock-r85.node +0 -0
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/HARNESS2-ADAPTATION-20261020.md +24 -0
- package/docs/R128-RELEASE-CANDIDATE.md +26 -0
- package/docs/R75-RECOVERY-HANDOFF.md +16 -0
- package/docs/R85-EPOCH-LOCK-BINARY.json +13 -0
- package/native/deadbyte-epoch-lock-r85.cpp +241 -0
- package/package.json +1 -1
- package/scripts/build-epoch-lock-r85.ps1 +34 -0
- package/scripts/deploy-r48.mjs +3 -1
- package/scripts/npm-release-r48.mjs +18 -4
- package/scripts/r75-npm-bin-integrity-gate.mjs +33 -0
- package/scripts/release-manifest.mjs +1 -1
- package/scripts/verify-authority-epoch-history.mjs +38 -0
- package/scripts/verify-r70-full-regression.ps1 +12 -2
- package/src/activation-authority-proof-r75.mjs +67 -0
- package/src/activation-continuity-engine-r65.mjs +17 -10
- package/src/activation-preflight-r65.mjs +13 -0
- package/src/agent-eval-r42.mjs +30 -0
- package/src/authority-continuity-r65.mjs +442 -29
- package/src/autonomous-local-supervisor.mjs +77 -47
- package/src/deadbyte-cli.mjs +73 -46
- package/src/epoch-lock-r85.mjs +48 -0
- package/src/foreground-marker-cas-win32.mjs +284 -0
- package/src/foreground-process-identity.mjs +3 -3
- package/src/privileged-install-trust-r89.mjs +196 -0
- package/src/remote-live-log.mjs +15 -8
- package/src/remote-log-decode-r60.mjs +65 -0
- package/src/remote-session-guardian.mjs +13 -12
- package/src/runtime-update.mjs +5 -22
- package/src/version.mjs +1 -1
- package/src/windows-powershell-environment.mjs +24 -0
- package/test/autonomous-local-supervisor.test.mjs +1 -1
- package/test/cli-entrypoint.test.mjs +1 -1
- package/test/harness2-eval-context-isolation.test.mjs +90 -0
- package/test/helpers/r75-authority-proof.mjs +9 -0
- package/test/r100-posix-owned-lock-race.test.mjs +72 -0
- package/test/r101-acl-probe-environment.test.mjs +51 -0
- package/test/r106-trusted-powershell-spawn.test.mjs +53 -0
- package/test/r107-updater-powershell-path.test.mjs +77 -0
- package/test/r108-remote-powershell-path.test.mjs +59 -0
- package/test/r110-supervisor-powershell-path.test.mjs +28 -0
- package/test/r111-supervisor-marker-cas.test.mjs +116 -0
- package/test/r112-supervisor-claim-cas.test.mjs +63 -0
- package/test/r113-supervisor-cleanup-terminality.test.mjs +100 -0
- package/test/r114-supervisor-absence-proof.test.mjs +45 -0
- package/test/r115-posix-continuity-entrypoint.test.mjs +17 -0
- package/test/r116-authority-crash-matrix.test.mjs +83 -0
- package/test/r117-concurrent-orphan-recovery.test.mjs +54 -0
- package/test/r118-cross-process-orphan-recovery.test.mjs +99 -0
- package/test/r119-recovery-crash-after-commit.test.mjs +91 -0
- package/test/r120-authority-history-audit.test.mjs +103 -0
- package/test/r121-signed-transition-invariant.test.mjs +132 -0
- package/test/r122-epoch-bounded-read.test.mjs +58 -0
- package/test/r123-recovery-bounded-read.test.mjs +64 -0
- package/test/r124-live-log-integrity.test.mjs +66 -0
- package/test/r124-live-log-tail-boundaries.test.mjs +57 -0
- package/test/r124-signed-predecessor-shape.test.mjs +66 -0
- package/test/r125-restore-binding-types.test.mjs +63 -0
- package/test/r126-restore-policy-envelope.test.mjs +89 -0
- package/test/r127-restore-append-policy-boundary.test.mjs +25 -0
- package/test/r33-closeout-regression.test.mjs +2 -1
- package/test/r54-offline-predecessor-self-exclusion.test.mjs +2 -2
- package/test/r55-offline-predecessor-guardian-family.test.mjs +2 -2
- package/test/r56-rollback-launcher-restore.test.mjs +2 -2
- package/test/r57-core-startup-race.test.mjs +2 -2
- package/test/r58-concurrent-recovery.test.mjs +10 -7
- package/test/r58-stale-owner.test.mjs +5 -6
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/r65-activation-orchestration.test.mjs +2 -1
- package/test/r65-activation-preflight.test.mjs +7 -6
- package/test/r65-authority-epoch-recovery.test.mjs +3 -1
- package/test/r65-authority-epoch-store.test.mjs +9 -6
- package/test/r65-authority-restored-safe.test.mjs +2 -1
- package/test/r65-deploy-preflight.test.mjs +13 -14
- package/test/r65-epoch-native-activation-integration.test.mjs +5 -4
- package/test/r65-legacy-authority-migration.test.mjs +2 -1
- package/test/r67-npm-cross-volume.test.mjs +9 -2
- package/test/r71-full-verifier-artifact.test.mjs +21 -15
- package/test/r71-isolated-slot-rollback-restore.test.mjs +3 -1
- package/test/r71-release-identity.test.mjs +3 -1
- package/test/r74-kernel-lock.test.mjs +12 -6
- package/test/r74-prepublish-contract.test.mjs +3 -1
- package/test/r75-activation-authority-proof.test.mjs +46 -0
- package/test/r75-authority-evidence.test.mjs +77 -0
- package/test/r76-pinned-marker-cas.test.mjs +78 -0
- package/test/r77-epoch-grant-cas.test.mjs +51 -0
- package/test/r78-parent-junction-cas.test.mjs +62 -0
- package/test/r79-orphan-revocation-reconcile.test.mjs +88 -0
- package/test/r79-windows-fsync-regression.test.mjs +45 -0
- package/test/r80-durable-epoch-commit.test.mjs +76 -0
- package/test/r82-epoch-lock-owned-cas.test.mjs +93 -0
- package/test/r83-epoch-lock-fileid-cas.test.mjs +92 -0
- package/test/r84-epoch-lock-created-fileid.test.mjs +112 -0
- package/test/r85-epoch-lock-release-lifetime.test.mjs +25 -0
- package/test/r85-epoch-repeat-append.test.mjs +30 -0
- package/test/r86-epoch-cleanup-and-loader.test.mjs +126 -0
- package/test/r87-crashed-orphan-revoke-reconcile.test.mjs +110 -0
- package/test/r88-native-disposition-failure.test.mjs +35 -0
- package/test/r89-privileged-install-boundary.test.mjs +123 -0
- package/test/r92-acl-ace-applicability.test.mjs +45 -0
- package/test/r94-win32-propagation-enum.test.mjs +51 -0
- package/test/r99-privileged-import-closure.test.mjs +45 -0
- package/test/remote-session-cli.test.mjs +69 -9
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {lstat} from 'node:fs/promises';
|
|
4
|
+
import {createAuthorityEpochStore} from '../src/authority-continuity-r65.mjs';
|
|
5
|
+
|
|
6
|
+
const planes = new Set(['process', 'coding', 'autonomous', 'desktop', 'host']);
|
|
7
|
+
const sha256 = /^[0-9a-f]{64}$/;
|
|
8
|
+
|
|
9
|
+
function parse(argv) {
|
|
10
|
+
if(argv.length!==6)throw new Error('expected --runtime-dir, --plane, and --expected-head');
|
|
11
|
+
const args = {};
|
|
12
|
+
for(let i=0;i<argv.length;i+=2){
|
|
13
|
+
const flag=argv[i],value=argv[i+1];
|
|
14
|
+
if(!['--runtime-dir','--plane','--expected-head'].includes(flag)||
|
|
15
|
+
Object.hasOwn(args,flag)||typeof value!=='string'||value.length===0)
|
|
16
|
+
throw new Error('authority history audit arguments invalid');
|
|
17
|
+
args[flag]=value;
|
|
18
|
+
}
|
|
19
|
+
const runtimeDir=args['--runtime-dir'],plane=args['--plane'],expectedHead=args['--expected-head'];
|
|
20
|
+
if(!path.isAbsolute(runtimeDir)||!planes.has(plane)||!sha256.test(expectedHead))
|
|
21
|
+
throw new Error('authority history audit subject invalid');
|
|
22
|
+
return {runtimeDir,plane,expectedHead};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function main() {
|
|
26
|
+
const {runtimeDir,plane,expectedHead}=parse(process.argv.slice(2));
|
|
27
|
+
const stat=await lstat(runtimeDir);
|
|
28
|
+
if(!stat.isDirectory()||stat.isSymbolicLink())throw new Error('authority history audit runtime root invalid');
|
|
29
|
+
const result=await createAuthorityEpochStore({runtimeDir}).auditHistory({plane});
|
|
30
|
+
if(result.status!=='PASS'||result.event_sha256!==expectedHead)
|
|
31
|
+
throw new Error('authority history audit expected head mismatch');
|
|
32
|
+
process.stdout.write(JSON.stringify(result)+'\n');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
main().catch(error=>{
|
|
36
|
+
process.stderr.write(`AUTHORITY_HISTORY_AUDIT_FAIL ${String(error?.message??error)}\n`);
|
|
37
|
+
process.exitCode=1;
|
|
38
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#requires -Version 5.1
|
|
2
2
|
# R70 verifier artifact: runs the candidate test suite without altering release manifests.
|
|
3
|
-
param([switch]$SelfTest)
|
|
3
|
+
param([switch]$SelfTest,[string]$EvidenceRoot='')
|
|
4
4
|
Set-StrictMode -Version Latest
|
|
5
5
|
$ErrorActionPreference = 'Continue'
|
|
6
6
|
Set-Location -LiteralPath (Split-Path -Parent $PSScriptRoot)
|
|
@@ -36,7 +36,17 @@ $selected = @($lines | Where-Object {
|
|
|
36
36
|
$summary = @($selected | Select-Object -Last 140 | ForEach-Object {
|
|
37
37
|
if ($_.Length -gt 512) { $_.Substring(0, 512) } else { $_ }
|
|
38
38
|
})
|
|
39
|
-
$
|
|
39
|
+
if ($EvidenceRoot -and -not $SelfTest) { throw 'EvidenceRoot override is self-test only' }
|
|
40
|
+
$artifactDir = if ($EvidenceRoot) {
|
|
41
|
+
if (-not [System.IO.Path]::IsPathRooted($EvidenceRoot)) { throw 'EvidenceRoot must be absolute' }
|
|
42
|
+
$resolved = [System.IO.Path]::GetFullPath($EvidenceRoot)
|
|
43
|
+
$checkout = [System.IO.Path]::GetFullPath((Get-Location).Path).TrimEnd('\')
|
|
44
|
+
if ($resolved.TrimEnd('\').Equals($checkout,[System.StringComparison]::OrdinalIgnoreCase) -or
|
|
45
|
+
$resolved.StartsWith($checkout + '\',[System.StringComparison]::OrdinalIgnoreCase)) {
|
|
46
|
+
throw 'EvidenceRoot self-test override must be outside the checkout'
|
|
47
|
+
}
|
|
48
|
+
$resolved
|
|
49
|
+
} else { Join-Path (Get-Location).Path 'testing\r70-verifier' }
|
|
40
50
|
if (-not (Test-Path -LiteralPath $artifactDir -PathType Container)) {
|
|
41
51
|
New-Item -ItemType Directory -Path $artifactDir -Force | Out-Null
|
|
42
52
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import {createHash} from 'node:crypto';
|
|
2
|
+
import {lstat,open,realpath} from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const FIELDS=Object.freeze({process:'grant_file',coding:'lease_file',autonomous:'lease_file',desktop:'grant_file',host:'arm_file'});
|
|
6
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
7
|
+
const within=(root,target)=>{const relative=path.relative(root,target);return relative!==''&&!relative.startsWith('..'+path.sep)&&relative!=='..'&&!path.isAbsolute(relative);};
|
|
8
|
+
const sameFile=(left,right)=>left.isFile()&&right.isFile()&&!left.isSymbolicLink()&&!right.isSymbolicLink()&&
|
|
9
|
+
left.dev===right.dev&&left.ino===right.ino&&left.size===right.size&&left.mtimeMs===right.mtimeMs&&
|
|
10
|
+
left.ctimeMs===right.ctimeMs&&left.birthtimeMs===right.birthtimeMs;
|
|
11
|
+
async function readRegularPinned(file,label){
|
|
12
|
+
const before=await lstat(file);
|
|
13
|
+
if(!before.isFile()||before.isSymbolicLink())throw new Error(`${label} file type invalid`);
|
|
14
|
+
const handle=await open(file,'r');
|
|
15
|
+
try{
|
|
16
|
+
const opened=await handle.stat();
|
|
17
|
+
if(!sameFile(before,opened))throw new Error(`${label} identity changed before read`);
|
|
18
|
+
const bytes=await handle.readFile();
|
|
19
|
+
const after=await handle.stat(),pathAfter=await lstat(file);
|
|
20
|
+
if(!sameFile(opened,after)||!sameFile(after,pathAfter))throw new Error(`${label} identity changed during read`);
|
|
21
|
+
return bytes;
|
|
22
|
+
}finally{await handle.close();}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function observeAuthorityEvidence({stateRoot,policyPaths,policySha256,authority}){
|
|
26
|
+
const runtimeRoot=await realpath(path.join(stateRoot,'runtime'));
|
|
27
|
+
const result={};
|
|
28
|
+
for(const [plane,field] of Object.entries(FIELDS)){
|
|
29
|
+
const policyPath=policyPaths?.[plane],expectedHash=policySha256?.[plane],entry=authority?.[plane];
|
|
30
|
+
if(typeof policyPath!=='string'||!path.isAbsolute(policyPath)||!(/^[0-9a-f]{64}$/).test(expectedHash??''))
|
|
31
|
+
throw new Error(`${plane} policy identity incomplete`);
|
|
32
|
+
const policyBytes=await readRegularPinned(policyPath,`${plane} policy`);
|
|
33
|
+
if(sha(policyBytes)!==expectedHash)throw new Error(`${plane} policy digest changed during observation`);
|
|
34
|
+
let policy;
|
|
35
|
+
try{policy=JSON.parse(policyBytes.toString('utf8').replace(/^\uFEFF/,''));}
|
|
36
|
+
catch{throw new Error(`${plane} policy JSON invalid`);}
|
|
37
|
+
const schemaOk=plane==='host'?['deadbyte.host-policy.v1','deadbyte.host-policy.v2'].includes(policy?.schema):
|
|
38
|
+
policy?.schema===`deadbyte.${plane}-policy.v1`;
|
|
39
|
+
if(!schemaOk||policy.enabled!==true||
|
|
40
|
+
typeof policy[field]!=='string'||!path.isAbsolute(policy[field]))
|
|
41
|
+
throw new Error(`${plane} policy grant path invalid`);
|
|
42
|
+
const grantPath=path.resolve(policy[field]);
|
|
43
|
+
const parent=await realpath(path.dirname(grantPath));
|
|
44
|
+
if(!within(runtimeRoot,parent)&&parent!==runtimeRoot)throw new Error(`${plane} policy grant path outside runtime`);
|
|
45
|
+
const checkedPath=path.join(parent,path.basename(grantPath));
|
|
46
|
+
let grantInfo;
|
|
47
|
+
try{grantInfo=await lstat(checkedPath);}
|
|
48
|
+
catch(error){if(error?.code!=='ENOENT')throw error;}
|
|
49
|
+
const grantAbsent=grantInfo===undefined;
|
|
50
|
+
if(grantInfo&&(!grantInfo.isFile()||grantInfo.isSymbolicLink()))throw new Error(`${plane} grant file type invalid`);
|
|
51
|
+
const validEntry=entry&&entry.configured===true&&typeof entry.armed==='boolean';
|
|
52
|
+
const validDisarmed=validEntry&&!entry.armed&&grantAbsent&&
|
|
53
|
+
(entry.reason==='disarmed'||(plane==='autonomous'&&entry.reason==='coding-disarmed'));
|
|
54
|
+
const validArmed=validEntry&&entry.armed&&!grantAbsent&&entry.reason==='standing-grant'&&
|
|
55
|
+
Number.isSafeInteger(entry.epoch_seq)&&entry.epoch_seq>0&&/^[0-9a-f]{64}$/.test(entry.epoch_sha256??'');
|
|
56
|
+
const grantSha=grantInfo?sha(await readRegularPinned(checkedPath,`${plane} grant`)):null;
|
|
57
|
+
let grantFinal;
|
|
58
|
+
try{grantFinal=await lstat(checkedPath);}
|
|
59
|
+
catch(error){if(error?.code!=='ENOENT')throw error;}
|
|
60
|
+
if(grantAbsent?grantFinal!==undefined:!grantFinal||!sameFile(grantInfo,grantFinal))
|
|
61
|
+
throw new Error(`${plane} grant identity changed during observation`);
|
|
62
|
+
result[plane]=Object.freeze({state:validDisarmed?'VERIFIED_DISARMED':validArmed?'VERIFIED_ARMED':'UNVERIFIED',
|
|
63
|
+
policy_sha256:expectedHash,grant_absent:grantAbsent,
|
|
64
|
+
...(grantSha?{grant_sha256:grantSha}:{})});
|
|
65
|
+
}
|
|
66
|
+
return Object.freeze(result);
|
|
67
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {createHash,randomBytes} from 'node:crypto';
|
|
2
|
-
import {open,readFile
|
|
2
|
+
import {open,readFile} from 'node:fs/promises';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import {canonicalJson} from './canonical-json.mjs';
|
|
5
5
|
import {decodeUtf8Strict} from './text-policy.mjs';
|
|
6
|
+
import {deletePinnedRemoteMarker} from './foreground-marker-cas-win32.mjs';
|
|
6
7
|
import {
|
|
7
8
|
captureStandingGrant,compareSecurityEnvelopes,createAuthorityEpochStore,deriveSecurityEnvelope
|
|
8
9
|
} from './authority-continuity-r65.mjs';
|
|
@@ -41,11 +42,14 @@ async function writeExclusive(file,bytes){
|
|
|
41
42
|
finally{await handle.close();}
|
|
42
43
|
}
|
|
43
44
|
async function removeIfExact(file,expectedSha256){
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if(
|
|
47
|
-
|
|
48
|
-
return {removed:true,reason:'matched'};
|
|
45
|
+
// The Windows native disposition pins a single handle from read/hash through deletion.
|
|
46
|
+
// Never use a pathname removal after confirming only a previous snapshot.
|
|
47
|
+
if(process.platform!=='win32')throw new Error('authority grant POSIX disposition unsupported');
|
|
48
|
+
const verdict=deletePinnedRemoteMarker(file,expectedSha256);
|
|
49
|
+
if(verdict==='REMOVED')return {removed:true,reason:'matched'};
|
|
50
|
+
if(verdict==='MISSING')return {removed:false,reason:'absent'};
|
|
51
|
+
if(verdict==='CHANGED')return {removed:false,reason:'replacement'};
|
|
52
|
+
throw new Error('authority grant native deletion verdict invalid');
|
|
49
53
|
}
|
|
50
54
|
function validateCapture(capture){
|
|
51
55
|
if(!plain(capture)||typeof capture.update_id!=='string'||!Array.isArray(capture.planes)||!plain(capture.records))throw new Error('authority continuity capture invalid');
|
|
@@ -88,6 +92,8 @@ function classifyConflict(head,record,updateId){
|
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
export function createEpochNativeContinuityEngine({stateRoot,predecessorPolicyPaths,successorPolicyPaths,updateId}={}){
|
|
95
|
+
if(process.platform!=='win32')
|
|
96
|
+
throw new Error('authority continuity POSIX mutation unsupported: native disposition unavailable');
|
|
91
97
|
const state=path.resolve(String(stateRoot??''));
|
|
92
98
|
if(!stateRoot||!plain(predecessorPolicyPaths)||typeof successorPolicyPaths!=='function'||
|
|
93
99
|
typeof updateId!=='string'||!/^[A-Za-z0-9._-]{1,96}$/.test(updateId))
|
|
@@ -193,10 +199,11 @@ export function createEpochNativeContinuityEngine({stateRoot,predecessorPolicyPa
|
|
|
193
199
|
if(conflict==='REVOKED_DURING_UPDATE')await removeIfExact(file,record.grant_sha256).catch(()=>{});
|
|
194
200
|
outcomes[plane]=conflict;reauth.push(plane);continue;
|
|
195
201
|
}
|
|
196
|
-
const
|
|
197
|
-
if(
|
|
198
|
-
|
|
199
|
-
|
|
202
|
+
const removed=await removeIfExact(file,record.grant_sha256);
|
|
203
|
+
if(!removed.removed){
|
|
204
|
+
outcomes[plane]=removed.reason==='absent'?'REVOKED_DURING_UPDATE':'REPLACED';
|
|
205
|
+
reauth.push(plane);continue;
|
|
206
|
+
}
|
|
200
207
|
const prepared=buildSuccessorGrant({plane,record,successorPolicyBytes,successorPolicy});
|
|
201
208
|
try{await writeExclusive(file,prepared.bytes);}
|
|
202
209
|
catch(error){if(error?.code==='EEXIST'){outcomes[plane]='REPLACED';reauth.push(plane);continue;}throw error;}
|
|
@@ -47,6 +47,19 @@ export function classifyActivationPreflight({pointer,expectedParent,candidate,ex
|
|
|
47
47
|
if(PLANES.some(plane=>typeof authority[plane]!=='boolean'))return Object.freeze({state:'UNKNOWN',safe_to_activate:false,reason:'authority observation incomplete'});
|
|
48
48
|
const subject={pointer,expectedParent,candidate,expectedCandidate,observation};
|
|
49
49
|
const bound={subject_sha256:digest(subject),authority:Object.freeze(authority)};
|
|
50
|
+
for(const plane of PLANES){
|
|
51
|
+
const entry=observation?.authority?.[plane],proof=observation?.authority_proof?.[plane];
|
|
52
|
+
if(entry&&typeof entry==='object'){
|
|
53
|
+
const knownReason=entry.armed?entry.reason==='standing-grant':entry.reason==='disarmed'||entry.reason==='coding-disarmed';
|
|
54
|
+
if(!knownReason)return Object.freeze({state:'AUTHORITY_UNVERIFIED',safe_to_activate:false,reason:`${plane} authority reason unverified`,...bound});
|
|
55
|
+
}
|
|
56
|
+
if(!proof)
|
|
57
|
+
return Object.freeze({state:'AUTHORITY_UNVERIFIED',safe_to_activate:false,reason:`${plane} authority proof missing`,...bound});
|
|
58
|
+
if(proof&&(proof.policy_sha256!==observation?.policy_sha256?.[plane]||
|
|
59
|
+
proof.state!==(authority[plane]?'VERIFIED_ARMED':'VERIFIED_DISARMED')||
|
|
60
|
+
(!authority[plane]&&proof.grant_absent!==true)))
|
|
61
|
+
return Object.freeze({state:'AUTHORITY_UNVERIFIED',safe_to_activate:false,reason:`${plane} authority proof unverified`,...bound});
|
|
62
|
+
}
|
|
50
63
|
if(PLANES.some(plane=>!HEX64.test(String(observation?.policy_sha256?.[plane]))))
|
|
51
64
|
return Object.freeze({state:'UNKNOWN',safe_to_activate:false,reason:'policy hash observation incomplete',...bound});
|
|
52
65
|
if(pointer.release_id!==expectedParent.release_id||pointer.version!==expectedParent.version||
|
package/src/agent-eval-r42.mjs
CHANGED
|
@@ -4,6 +4,11 @@ import { canonicalJson } from './canonical-json.mjs';
|
|
|
4
4
|
const HEX64=/^[0-9a-f]{64}$/;
|
|
5
5
|
const ID=/^[A-Za-z0-9._-]{1,96}$/;
|
|
6
6
|
const SEVERITY=new Set(['info','low','medium','high','critical']);
|
|
7
|
+
const JUDGE_ONLY_KEY=/(?:secret|token|password|answer[_-]?key|ground[_-]?truth)/i;
|
|
8
|
+
const JUDGE_ONLY_CONTEXT_KEYS=new Set([
|
|
9
|
+
'hidden_oracle','judge_result','grader_verdict','deterministic_eval_result',
|
|
10
|
+
'deterministic_eval_summary','judge_scope_and_deterministic_status'
|
|
11
|
+
]);
|
|
7
12
|
const sha=value=>createHash('sha256').update(Buffer.from(canonicalJson(value),'utf8')).digest('hex');
|
|
8
13
|
function object(value,label){if(!value||typeof value!=='object'||Array.isArray(value))throw new Error(`${label} invalid`);return value;}
|
|
9
14
|
function keys(value,allowed,label){const extra=Object.keys(value).filter(key=>!allowed.includes(key));if(extra.length)throw new Error(`${label} unknown field '${extra[0]}'`);}
|
|
@@ -11,6 +16,30 @@ function id(value,label){if(typeof value!=='string'||!ID.test(value))throw new E
|
|
|
11
16
|
function text(value,label,max=8192){if(typeof value!=='string'||value.length<1||value.length>max||value.includes('\0'))throw new Error(`${label} invalid`);return value;}
|
|
12
17
|
function textList(value,label,maxItems=32,maxLength=1024){if(!Array.isArray(value)||value.length>maxItems||value.some(item=>typeof item!=='string'||item.length<1||item.length>maxLength))throw new Error(`${label} invalid`);return [...value];}
|
|
13
18
|
|
|
19
|
+
function assertNoJudgeOnlyContextLeak(core){
|
|
20
|
+
const checkKeys=(value,depth=0)=>{
|
|
21
|
+
if(depth>32)throw new Error('eval visible context nesting exceeds safe limit');
|
|
22
|
+
if(Array.isArray(value)){for(const item of value)checkKeys(item,depth+1);return;}
|
|
23
|
+
if(value&&typeof value==='object')for(const [key,item] of Object.entries(value)){
|
|
24
|
+
if(JUDGE_ONLY_CONTEXT_KEYS.has(key.toLowerCase()))throw new Error('judge-only result leaked into generator context');
|
|
25
|
+
checkKeys(item,depth+1);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
checkKeys(core.allowed_context);
|
|
29
|
+
const visible=canonicalJson({task:core.task,allowed_context:core.allowed_context,acceptance:core.acceptance});
|
|
30
|
+
const visit=(value,secretKey=false,depth=0)=>{
|
|
31
|
+
if(depth>32)throw new Error('eval hidden oracle nesting exceeds safe limit');
|
|
32
|
+
if(typeof value==='string'){
|
|
33
|
+
if(secretKey&&value.length>0&&visible.includes(JSON.stringify(value).slice(1,-1)))
|
|
34
|
+
throw new Error('judge-only oracle value leaked into generator context');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if(Array.isArray(value)){for(const item of value)visit(item,secretKey,depth+1);return;}
|
|
38
|
+
if(value&&typeof value==='object')for(const [key,item] of Object.entries(value))visit(item,secretKey||JUDGE_ONLY_KEY.test(key),depth+1);
|
|
39
|
+
};
|
|
40
|
+
visit(core.hidden_oracle);
|
|
41
|
+
}
|
|
42
|
+
|
|
14
43
|
function normalizeCase(raw){
|
|
15
44
|
const value=object(raw,'agent eval case');keys(value,['schema','case_id','task','allowed_context','hidden_oracle','acceptance'],'agent eval case');
|
|
16
45
|
if(value.schema!=='deadbyte.agent-eval-case.v1')throw new Error('agent eval case schema invalid');
|
|
@@ -18,6 +47,7 @@ function normalizeCase(raw){
|
|
|
18
47
|
allowed_context:structuredClone(object(value.allowed_context,'eval allowed_context')),
|
|
19
48
|
hidden_oracle:structuredClone(object(value.hidden_oracle,'eval hidden_oracle')),
|
|
20
49
|
acceptance:textList(value.acceptance,'eval acceptance')};
|
|
50
|
+
assertNoJudgeOnlyContextLeak(core);
|
|
21
51
|
return Object.freeze({...core,case_sha256:sha(core)});
|
|
22
52
|
}
|
|
23
53
|
|