deadbyte-mcp 0.15.0 → 0.15.2
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 +109 -64
- package/PROVENANCE.json +11 -0
- package/R45-PARENT.json +18 -0
- package/README.txt +36 -20
- package/RELEASE.json +53 -0
- package/SBOM.cdx.json +46 -0
- package/TEST-MATRIX.json +31 -0
- package/bin/deadbyte-hardware-observer-r45.exe +0 -0
- package/bin/deadbyte-hardware-observer-r45.obj +0 -0
- package/controller/README.TXT +2 -2
- package/controller/deadbyte-controller.ps1 +51 -3
- package/controller/deadbyte-process-policy.json +63 -1
- package/docs/ARCHITECTURE.md +3 -4
- package/native/deadbyte-hardware-observer.cpp +308 -0
- package/package.json +14 -7
- package/scripts/autonomous-tests.ps1 +2 -2
- package/scripts/build-hardware-r45.ps1 +71 -0
- package/scripts/deferred-slot-operation-r45.mjs +126 -0
- package/scripts/deferred-slot-operation-r46.mjs +126 -0
- package/scripts/derive-r45-parent.mjs +24 -0
- package/scripts/derive-r45-release.mjs +72 -0
- package/scripts/derive-r45-tests.mjs +20 -0
- package/scripts/final-closure-r45.mjs +121 -0
- package/scripts/final-closure-r46.mjs +98 -0
- package/scripts/final-closure-verify-r45.mjs +220 -0
- package/scripts/final-closure-verify-r46.mjs +119 -0
- package/scripts/finalize-r45.mjs +219 -0
- package/scripts/full-regression-r45.mjs +20 -0
- package/scripts/full-regression-r46.mjs +20 -0
- package/scripts/full-regression-summary-r45.mjs +14 -0
- package/scripts/gate-windows-r45.ps1 +44 -0
- package/scripts/gate-windows-r46.ps1 +56 -0
- package/scripts/init-host-policy.ps1 +39 -1
- package/scripts/inspect-active-release-r45.mjs +20 -0
- package/scripts/refresh-process-policy.mjs +64 -7
- package/scripts/release-manifest.mjs +3 -1
- package/scripts/release-parity-r45.mjs +253 -0
- package/scripts/release-parity-r46.mjs +187 -0
- package/scripts/release-parity-tests-r45.ps1 +8 -0
- package/scripts/verify-r45-parent.mjs +142 -0
- package/scripts/windows-gate-evidence-r45.mjs +60 -0
- package/scripts/windows-gate-evidence-r46.mjs +110 -0
- package/src/autonomous-runtime.mjs +12 -7
- package/src/capability-ledger.mjs +2 -0
- package/src/compact-gateway-mcp-tools.mjs +11 -7
- package/src/deadbyte-cli.mjs +24 -12
- package/src/doctor-r45.mjs +65 -0
- package/src/host-plane.mjs +133 -85
- package/src/machine-fs-mcp-tools.mjs +3 -3
- package/src/machine-fs-output-contracts.mjs +1 -1
- package/src/machine-fs-read-worker.mjs +46 -0
- package/src/machine-fs.mjs +63 -10
- package/src/mcp-server.mjs +68 -23
- package/src/mcp-smoke-client.mjs +4 -1
- package/src/native-diff-v2.mjs +9 -4
- package/src/native-diff.mjs +29 -7
- package/src/observation-mcp-tools.mjs +5 -1
- package/src/observation-output-contracts.mjs +8 -0
- package/src/observation-runtime.mjs +57 -5
- package/src/observation-smoke-client.mjs +6 -2
- package/src/output-contracts.mjs +27 -11
- package/src/process-mcp-tools.mjs +18 -5
- package/src/process-output-contracts.mjs +20 -3
- package/src/process-policy.mjs +3 -1
- package/src/process-runtime.mjs +99 -19
- package/src/process-smoke-client.mjs +67 -22
- package/src/r45-compact-disarmed-probe.mjs +74 -0
- package/src/r45-full-surface-probe.mjs +34 -0
- package/src/release-generation.mjs +23 -3
- package/src/remote-device-r44.mjs +53 -15
- package/src/remote-mcp-r45-parity-client.mjs +206 -0
- package/src/remote-mcp-smoke-client.mjs +1 -1
- package/src/runtime-update.mjs +36 -8
- package/src/stdio-console-guard.mjs +24 -0
- package/src/version.mjs +1 -1
- package/test/agent-runtime.test.mjs +1 -0
- package/test/autonomous-loop.test.mjs +24 -0
- package/test/autonomous-runtime.test.mjs +1 -0
- package/test/cli-entrypoint.test.mjs +4 -4
- package/test/coding-plane.test.mjs +1 -0
- package/test/compact-gateway-r36.test.mjs +28 -0
- package/test/compact-max-catalog-r45.test.mjs +99 -0
- package/test/contained.test.mjs +23 -11
- package/test/controller.test.mjs +5 -1
- package/test/core.test.mjs +1 -1
- package/test/full-max-catalog-r45.test.mjs +91 -0
- package/test/full-max-catalog-r46.test.mjs +92 -0
- package/test/hardware-observation-r45.test.mjs +44 -0
- package/test/host-inline-r45.test.mjs +67 -0
- package/test/machine-fs-mcp-tools.test.mjs +4 -1
- package/test/machine-fs-runtime.test.mjs +18 -2
- package/test/machine-mcp-integration.test.mjs +1 -0
- package/test/observation-mcp-tools.test.mjs +4 -3
- package/test/observation-runtime.test.mjs +29 -2
- package/test/output-schema.test.mjs +1 -0
- package/test/package-boundary.test.mjs +1 -1
- package/test/process-mcp-tools.test.mjs +23 -5
- package/test/process-policy-refresh.test.mjs +87 -10
- package/test/process-runtime.test.mjs +111 -1
- package/test/process-supervision.test.mjs +4 -2
- package/test/production-docs.test.mjs +3 -3
- package/test/r33-closeout-regression.test.mjs +20 -19
- package/test/r33-finalization.test.mjs +3 -3
- package/test/r45-autonomous-quick-test.test.mjs +10 -0
- package/test/r45-finalization.test.mjs +84 -0
- package/test/release-generation.test.mjs +26 -11
- package/test/release-version.test.mjs +112 -46
- package/test/remote-device-r44.test.mjs +75 -0
- package/test/remote-r24-catalog.test.mjs +2 -2
- package/test/runtime-host-bootstrap-r45.test.mjs +46 -0
|
@@ -15,14 +15,15 @@ function runtime(){
|
|
|
15
15
|
searchStatus:async()=>({status:'ok',search_id:hex('a'),state:'completed',result_count:0,files_scanned:0,bytes_scanned:0,truncated:false,request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d'),error:null}),
|
|
16
16
|
searchCancel:async()=>({status:'ok',search_id:hex('a'),state:'cancelled',cancel_requested:true}),
|
|
17
17
|
processList:async()=>({status:'ok',snapshot_id:hex('e'),observed_at_utc:new Date(0).toISOString(),processes:[],next_cursor:null,request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d')}),
|
|
18
|
-
systemStatus:async()=>({status:'ok',observed_at_utc:new Date(0).toISOString(),hostname:'x',platform:'win32',architecture:'x64',uptime_seconds:1,total_memory_bytes:2,free_memory_bytes:1,cpu_count:1,load_average:[0,0,0],request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d')})
|
|
18
|
+
systemStatus:async()=>({status:'ok',observed_at_utc:new Date(0).toISOString(),hostname:'x',platform:'win32',architecture:'x64',uptime_seconds:1,total_memory_bytes:2,free_memory_bytes:1,cpu_count:1,load_average:[0,0,0],request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d')}),
|
|
19
|
+
hardwareQuery:async({query})=>({status:'ok',query,observed_at_utc:new Date(0).toISOString(),observer_sha256:hex('e'),data:{vendor:'x'},request_sha256:hex('b'),result_head_sha256:hex('c'),receipt_id:receipt,receipt_sha256:hex('d')})
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
test('
|
|
23
|
+
test('R45 registers exactly seven bounded observation tools including typed native hardware',()=>{
|
|
23
24
|
const server=new FakeServer(); registerObservationTools(server,{runtime:runtime()});
|
|
24
25
|
assert.deepEqual([...server.tools.keys()].sort(),[
|
|
25
|
-
'machine_process_list','machine_search_cancel','machine_search_page','machine_search_start','machine_search_status','machine_system_status'
|
|
26
|
+
'machine_hardware_query','machine_process_list','machine_search_cancel','machine_search_page','machine_search_start','machine_search_status','machine_system_status'
|
|
26
27
|
]);
|
|
27
28
|
for(const [name,tool] of server.tools){ assert.ok(tool.spec.inputSchema,`${name} input`); assert.ok(tool.spec.outputSchema,`${name} output`); }
|
|
28
29
|
});
|
|
@@ -7,7 +7,7 @@ import { generateTrustKeypair } from '../src/trust.mjs';
|
|
|
7
7
|
import { loadCodingPolicy } from '../src/coding-plane.mjs';
|
|
8
8
|
import { createObservationRuntime, verifyObservationReceiptFile } from '../src/observation-runtime.mjs';
|
|
9
9
|
|
|
10
|
-
async function fixture() {
|
|
10
|
+
async function fixture(runtimeOptions={}) {
|
|
11
11
|
const root = await mkdtemp(path.join(os.tmpdir(), 'deadbyte-observation-'));
|
|
12
12
|
const project = path.join(root, 'project');
|
|
13
13
|
await mkdir(project); await mkdir(path.join(project, 'blocked'));
|
|
@@ -36,7 +36,8 @@ async function fixture() {
|
|
|
36
36
|
const runtime = createObservationRuntime({
|
|
37
37
|
policy, signingKeyPath:keys.privateKeyPath, verifyKeyPath:keys.publicKeyPath,
|
|
38
38
|
processRuntime, stateRoot:root, processSnapshotProvider, systemSnapshotProvider,
|
|
39
|
-
ownedRuntimePidProvider: async () => new Set([111])
|
|
39
|
+
ownedRuntimePidProvider: async () => new Set([111]),
|
|
40
|
+
...runtimeOptions
|
|
40
41
|
});
|
|
41
42
|
return { root, project, policy, keys, runtime, getProcessCalls:() => processCalls };
|
|
42
43
|
}
|
|
@@ -121,3 +122,29 @@ test('R32 system observation is timestamped and signed without mutation authorit
|
|
|
121
122
|
assert.equal(verified.ok,true);
|
|
122
123
|
assert.equal(verified.result_head_sha256,status.result_head_sha256);
|
|
123
124
|
});
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
test('R46 observation search sessions expire after bounded terminal TTL and free capacity', async () => {
|
|
128
|
+
let now=1000;
|
|
129
|
+
const f=await fixture({searchSessionTtlMs:50,maxSearchSessions:1,clockMs:()=>now});
|
|
130
|
+
await writeFile(path.join(f.project,'ttl.txt'),'needle\n');
|
|
131
|
+
const first=await f.runtime.searchStart({rootId:'project',relativePath:'.',pattern:'needle',maxResults:10,maxFiles:10,maxBytes:65536});
|
|
132
|
+
const done=await waitTerminal(f.runtime,first.search_id);
|
|
133
|
+
assert.equal(done.state,'completed');
|
|
134
|
+
now+=60;
|
|
135
|
+
await assert.rejects(f.runtime.searchStatus({searchId:first.search_id}),/unknown or expired search/);
|
|
136
|
+
const second=await f.runtime.searchStart({rootId:'project',relativePath:'.',pattern:'needle',maxResults:10,maxFiles:10,maxBytes:65536});
|
|
137
|
+
assert.match(second.search_id,/^[0-9a-f]{64}$/);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test('R46 observation search hard cap fails closed while all retained sessions are active', async () => {
|
|
141
|
+
const f=await fixture({searchSessionTtlMs:5000,maxSearchSessions:2});
|
|
142
|
+
await writeFile(path.join(f.project,'cap.txt'),'needle\n');
|
|
143
|
+
const a=await f.runtime.searchStart({rootId:'project',relativePath:'.',pattern:'needle'});
|
|
144
|
+
const b=await f.runtime.searchStart({rootId:'project',relativePath:'.',pattern:'needle'});
|
|
145
|
+
assert.match(a.search_id,/^[0-9a-f]{64}$/);
|
|
146
|
+
assert.match(b.search_id,/^[0-9a-f]{64}$/);
|
|
147
|
+
await assert.rejects(f.runtime.searchStart({rootId:'project',relativePath:'.',pattern:'needle'}),/session limit reached/);
|
|
148
|
+
await waitTerminal(f.runtime,a.search_id).catch(()=>{});
|
|
149
|
+
await waitTerminal(f.runtime,b.search_id).catch(()=>{});
|
|
150
|
+
});
|
|
@@ -39,6 +39,6 @@ test('R28 package boundary contains only immutable workspace seed files', () =>
|
|
|
39
39
|
|
|
40
40
|
test('publish boundary excludes live state, private keys, grants and non-example controller secrets',()=>{
|
|
41
41
|
const files=packFiles();
|
|
42
|
-
const forbidden=files.filter(rel
|
|
42
|
+
const forbidden=files.filter(rel=>rel.startsWith('tmp/')||rel.startsWith('.deadbyte-machine-fs-recovery/')||/(^|\/)(active-release\.json|runtime\/|audit\/|evidence\/)|\.pem$|(^|\/)deadbyte-controller\.config\.psd1$|(^|\/)deadbyte-host-policy\.json$|(^|\/)[^/]*(?:grant|arm)\.json$/i.test(rel));
|
|
43
43
|
assert.deepEqual(forbidden,[]);
|
|
44
44
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import test from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { registerProcessTools } from '../src/process-mcp-tools.mjs';
|
|
4
4
|
|
|
@@ -20,18 +20,19 @@ function runtime() {
|
|
|
20
20
|
return {
|
|
21
21
|
start: async () => session(), read: async () => ({
|
|
22
22
|
session_id: hex('a'), offset: 0, next_offset: 4, earliest_offset: 0, stream_head: 4,
|
|
23
|
-
stream_sha256: hex('b'), truncated: false, data_base64: 'VEVTVA==', state: 'running', exit_code: null
|
|
23
|
+
stream_sha256: hex('b'), truncated: false, wait_satisfied: true, data_base64: 'VEVTVA==', state: 'running', exit_code: null
|
|
24
24
|
}),
|
|
25
25
|
status: async () => session(), list: async () => ({ sessions: [session()] }),
|
|
26
|
+
write: async ({ input, appendNewline }) => ({ logical_bytes: Buffer.byteLength(input + (appendNewline ? '\r\n' : '')), logical_sha256: hex('e'), append_newline: appendNewline, session: session() }),
|
|
26
27
|
action: async ({ actionId }) => ({ action_id: actionId, logical_bytes: 4, logical_sha256: hex('d'), session: session() }),
|
|
27
28
|
terminate: async () => session('terminated')
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
|
-
test('
|
|
31
|
+
test('R46 registers exactly seven bounded process supervision tools', () => {
|
|
31
32
|
const server = new FakeServer();
|
|
32
33
|
registerProcessTools(server, { runtime: runtime() });
|
|
33
34
|
assert.deepEqual([...server.tools.keys()].sort(), [
|
|
34
|
-
'process_session_action','process_session_list','process_session_read','process_session_start','process_session_status','process_session_terminate'
|
|
35
|
+
'process_session_action','process_session_list','process_session_read','process_session_start','process_session_status','process_session_terminate','process_session_write'
|
|
35
36
|
]);
|
|
36
37
|
for (const [name, tool] of server.tools) {
|
|
37
38
|
assert.ok(tool.spec.inputSchema, `${name} input schema missing`);
|
|
@@ -41,7 +42,7 @@ test('R32 registers exactly six bounded process supervision tools', () => {
|
|
|
41
42
|
}
|
|
42
43
|
});
|
|
43
44
|
|
|
44
|
-
test('
|
|
45
|
+
test('R46 process tool inputs expose guarded raw input only through process_session_write', () => {
|
|
45
46
|
const server = new FakeServer();
|
|
46
47
|
registerProcessTools(server, { runtime: runtime() });
|
|
47
48
|
const start = server.tools.get('process_session_start').spec.inputSchema;
|
|
@@ -52,6 +53,10 @@ test('R32 process tool inputs expose IDs and bounds but no executable or raw inp
|
|
|
52
53
|
assert.equal(start.safeParse({ profile_id: 'hold', root_id: 'core', input: 'whoami' }).success, false);
|
|
53
54
|
assert.equal(terminate.safeParse({ session_id: hex('a') }).success, true);
|
|
54
55
|
assert.equal(terminate.safeParse({ pid: 1234 }).success, false);
|
|
56
|
+
const write = server.tools.get('process_session_write').spec.inputSchema;
|
|
57
|
+
assert.equal(write.safeParse({ session_id: hex('a'), input: 'Write-Output 42', append_newline: true }).success, true);
|
|
58
|
+
assert.equal(write.safeParse({ session_id: hex('a'), input: '' }).success, false);
|
|
59
|
+
assert.equal(write.safeParse({ session_id: hex('a'), input: 'x', pid: 1234 }).success, false);
|
|
55
60
|
assert.equal(action.safeParse({ session_id: hex('a'), action_id: 'ping' }).success, true);
|
|
56
61
|
for (const forbidden of ['input','command','executable','environment','pid']) {
|
|
57
62
|
assert.equal(action.safeParse({ session_id: hex('a'), action_id: 'ping', [forbidden]: 'forbidden' }).success, false);
|
|
@@ -69,3 +74,16 @@ test('R32 process action handler returns only strict logical identity and update
|
|
|
69
74
|
assert.equal(result.structuredContent.session.state, 'running');
|
|
70
75
|
assert.equal(tool.spec.outputSchema.safeParse({ ...result.structuredContent, raw_input: 'forbidden' }).success, false);
|
|
71
76
|
});
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
test('R46 process write handler returns only input identity and session state', async () => {
|
|
80
|
+
const server = new FakeServer();
|
|
81
|
+
registerProcessTools(server, { runtime: runtime() });
|
|
82
|
+
const tool = server.tools.get('process_session_write');
|
|
83
|
+
const result = await tool.handler({ session_id: hex('a'), input: 'Write-Output 42', append_newline: true });
|
|
84
|
+
assert.equal(result.isError, false);
|
|
85
|
+
assert.deepEqual(Object.keys(result.structuredContent).sort(), ['append_newline','logical_bytes','logical_sha256','session','status']);
|
|
86
|
+
assert.equal(result.structuredContent.append_newline, true);
|
|
87
|
+
assert.equal(result.structuredContent.session.state, 'running');
|
|
88
|
+
assert.equal(tool.spec.outputSchema.safeParse({ ...result.structuredContent, raw_input: 'forbidden' }).success, false);
|
|
89
|
+
});
|
|
@@ -13,18 +13,58 @@ async function fixture() {
|
|
|
13
13
|
const root = await mkdtemp(path.join(os.tmpdir(), 'deadbyte-process-refresh-'));
|
|
14
14
|
const bin = path.join(root, 'bin');
|
|
15
15
|
const controller = path.join(root, 'controller');
|
|
16
|
+
const systemRoot = path.join(root, 'Windows');
|
|
17
|
+
const system32 = path.join(systemRoot, 'System32');
|
|
18
|
+
const powershellDir = path.join(system32, 'WindowsPowerShell', 'v1.0');
|
|
16
19
|
await mkdir(bin, { recursive: true });
|
|
17
20
|
await mkdir(controller, { recursive: true });
|
|
21
|
+
await mkdir(powershellDir, { recursive: true });
|
|
22
|
+
|
|
18
23
|
const hostPath = path.join(bin, 'deadbyte-process-host.exe');
|
|
19
24
|
const hostBytes = Buffer.from('fresh-native-host\n');
|
|
25
|
+
const cmdPath = path.join(system32, 'cmd.exe');
|
|
26
|
+
const cmdBytes = Buffer.from('target-cmd\n');
|
|
27
|
+
const powershellPath = path.join(powershellDir, 'powershell.exe');
|
|
28
|
+
const powershellBytes = Buffer.from('target-powershell\n');
|
|
20
29
|
await writeFile(hostPath, hostBytes);
|
|
30
|
+
await writeFile(cmdPath, cmdBytes);
|
|
31
|
+
await writeFile(powershellPath, powershellBytes);
|
|
32
|
+
|
|
21
33
|
const policyPath = path.join(controller, 'deadbyte-process-policy.json');
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
34
|
+
const profile = executable => ({
|
|
35
|
+
executable,
|
|
36
|
+
executable_sha256:'1'.repeat(64),
|
|
37
|
+
root_ids:['core'],
|
|
38
|
+
prefix_args:[],
|
|
39
|
+
transport:'pipe',
|
|
40
|
+
max_args:1,
|
|
41
|
+
max_arg_bytes:8192,
|
|
42
|
+
max_sessions:1,
|
|
43
|
+
max_input_bytes:4096,
|
|
44
|
+
max_retained_output_bytes:4096,
|
|
45
|
+
env_allow:[],
|
|
46
|
+
input_actions:{}
|
|
47
|
+
});
|
|
48
|
+
const policy = {
|
|
49
|
+
schema:'deadbyte.process-policy.v1',
|
|
50
|
+
enabled:true,
|
|
51
|
+
grant_file:path.join(root,'runtime','grant.json'),
|
|
52
|
+
state_dir:path.join(root,'state'),
|
|
53
|
+
native_host:{ path:hostPath, sha256:'0'.repeat(64) },
|
|
54
|
+
roots:{core:{path:root,deny_paths:[]}},
|
|
55
|
+
profiles:{
|
|
56
|
+
ping_hold:profile(cmdPath),
|
|
57
|
+
powershell:profile(powershellPath),
|
|
58
|
+
powershell_repl:profile(powershellPath)
|
|
59
|
+
}
|
|
60
|
+
};
|
|
25
61
|
await writeFile(policyPath, `${JSON.stringify(policy,null,2)}\n`);
|
|
26
|
-
return {
|
|
62
|
+
return {
|
|
63
|
+
root, policyPath, hostPath, hostBytes, systemRoot,
|
|
64
|
+
cmdPath, cmdBytes, powershellPath, powershellBytes, policy
|
|
65
|
+
};
|
|
27
66
|
}
|
|
67
|
+
|
|
28
68
|
test('R33 candidate process policy self-binds to the current repository root', async () => {
|
|
29
69
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
30
70
|
const policyPath = path.join(repoRoot, 'controller', 'deadbyte-process-policy.json');
|
|
@@ -33,32 +73,69 @@ test('R33 candidate process policy self-binds to the current repository root', a
|
|
|
33
73
|
assert.equal(policy.state_dir, path.join(repoRoot, 'controller', 'runtime', 'process-state'));
|
|
34
74
|
assert.equal(policy.native_host.path, path.join(repoRoot, 'bin', 'deadbyte-process-host.exe'));
|
|
35
75
|
assert.equal(policy.roots.core.path, repoRoot);
|
|
76
|
+
assert.ok(policy.profiles.ping_hold);
|
|
77
|
+
assert.ok(policy.profiles.powershell);
|
|
78
|
+
assert.ok(policy.profiles.powershell_repl);
|
|
79
|
+
assert.equal(policy.profiles.powershell_repl.allow_raw_input,true);
|
|
80
|
+
assert.deepEqual(policy.profiles.powershell.prefix_args, ['-NoLogo','-NoProfile','-NonInteractive','-Command']);
|
|
81
|
+
assert.equal(policy.profiles.powershell.max_args, 1);
|
|
82
|
+
assert.equal(policy.profiles.powershell.max_arg_bytes, 8192);
|
|
36
83
|
});
|
|
37
84
|
|
|
38
|
-
test('
|
|
85
|
+
test('process policy refresh repins native host and Windows system executables', async () => {
|
|
39
86
|
const f = await fixture();
|
|
40
|
-
const result = await refreshProcessPolicy({ repoRoot:f.root, policyPath:f.policyPath });
|
|
87
|
+
const result = await refreshProcessPolicy({ repoRoot:f.root, policyPath:f.policyPath, systemRoot:f.systemRoot });
|
|
41
88
|
const after = JSON.parse(await readFile(f.policyPath,'utf8'));
|
|
42
89
|
assert.equal(after.native_host.path, f.hostPath);
|
|
43
90
|
assert.equal(after.native_host.sha256, sha256(f.hostBytes));
|
|
91
|
+
assert.equal(after.profiles.ping_hold.executable, f.cmdPath);
|
|
92
|
+
assert.equal(after.profiles.ping_hold.executable_sha256, sha256(f.cmdBytes));
|
|
93
|
+
assert.equal(after.profiles.powershell.executable, f.powershellPath);
|
|
94
|
+
assert.equal(after.profiles.powershell.executable_sha256, sha256(f.powershellBytes));
|
|
95
|
+
assert.equal(after.profiles.powershell_repl.executable, f.powershellPath);
|
|
96
|
+
assert.equal(after.profiles.powershell_repl.executable_sha256, sha256(f.powershellBytes));
|
|
44
97
|
assert.equal(result.native_host_sha256, sha256(f.hostBytes));
|
|
98
|
+
assert.equal(result.cmd_executable_sha256, sha256(f.cmdBytes));
|
|
99
|
+
assert.equal(result.powershell_executable_sha256, sha256(f.powershellBytes));
|
|
100
|
+
assert.equal(result.powershell_repl_executable_sha256, sha256(f.powershellBytes));
|
|
101
|
+
|
|
45
102
|
const expected = structuredClone(f.policy);
|
|
46
103
|
expected.native_host.sha256 = sha256(f.hostBytes);
|
|
104
|
+
expected.profiles.ping_hold.executable_sha256 = sha256(f.cmdBytes);
|
|
105
|
+
expected.profiles.powershell.executable_sha256 = sha256(f.powershellBytes);
|
|
106
|
+
expected.profiles.powershell_repl.executable_sha256 = sha256(f.powershellBytes);
|
|
47
107
|
assert.deepEqual(after, expected);
|
|
48
108
|
});
|
|
49
109
|
|
|
50
|
-
test('
|
|
110
|
+
test('process policy refresh fails closed when native-host path is not the candidate bin path', async () => {
|
|
51
111
|
const f = await fixture();
|
|
52
112
|
const raw = JSON.parse(await readFile(f.policyPath,'utf8'));
|
|
53
113
|
raw.native_host.path = process.execPath;
|
|
54
114
|
await writeFile(f.policyPath, `${JSON.stringify(raw,null,2)}\n`);
|
|
55
|
-
await assert.rejects(
|
|
115
|
+
await assert.rejects(
|
|
116
|
+
() => refreshProcessPolicy({ repoRoot:f.root, policyPath:f.policyPath, systemRoot:f.systemRoot }),
|
|
117
|
+
/native_host path must equal candidate bin/
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test('process policy refresh fails closed when a system profile path is not target-bound', async () => {
|
|
122
|
+
const f = await fixture();
|
|
123
|
+
const raw = JSON.parse(await readFile(f.policyPath,'utf8'));
|
|
124
|
+
raw.profiles.powershell.executable = process.execPath;
|
|
125
|
+
await writeFile(f.policyPath, `${JSON.stringify(raw,null,2)}\n`);
|
|
126
|
+
await assert.rejects(
|
|
127
|
+
() => refreshProcessPolicy({ repoRoot:f.root, policyPath:f.policyPath, systemRoot:f.systemRoot }),
|
|
128
|
+
/profile 'powershell' executable path mismatch/
|
|
129
|
+
);
|
|
56
130
|
});
|
|
57
131
|
|
|
58
|
-
test('
|
|
132
|
+
test('process policy refresh refuses a malformed or disabled process policy', async () => {
|
|
59
133
|
const f = await fixture();
|
|
60
134
|
const raw = JSON.parse(await readFile(f.policyPath,'utf8'));
|
|
61
135
|
raw.enabled = false;
|
|
62
136
|
await writeFile(f.policyPath, `${JSON.stringify(raw,null,2)}\n`);
|
|
63
|
-
await assert.rejects(
|
|
137
|
+
await assert.rejects(
|
|
138
|
+
() => refreshProcessPolicy({ repoRoot:f.root, policyPath:f.policyPath, systemRoot:f.systemRoot }),
|
|
139
|
+
/schema\/enabled mismatch/
|
|
140
|
+
);
|
|
64
141
|
});
|
|
@@ -12,6 +12,7 @@ import { generateTrustKeypair } from '../src/trust.mjs';
|
|
|
12
12
|
const repo = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
13
13
|
const host = path.join(repo, 'bin', 'deadbyte-process-host.exe');
|
|
14
14
|
const cmd = 'C:\\Windows\\System32\\cmd.exe';
|
|
15
|
+
const powershell = 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe';
|
|
15
16
|
const sha = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
16
17
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
17
18
|
|
|
@@ -25,6 +26,7 @@ async function fixture() {
|
|
|
25
26
|
const policyPath = path.join(root, 'process-policy.json');
|
|
26
27
|
const hostSha = sha(await readFile(host));
|
|
27
28
|
const cmdSha = sha(await readFile(cmd));
|
|
29
|
+
const powershellSha = sha(await readFile(powershell));
|
|
28
30
|
const raw = {
|
|
29
31
|
schema: PROCESS_POLICY_SCHEMA, enabled: true, grant_file: grantFile, state_dir: stateDir,
|
|
30
32
|
native_host: { path: host, sha256: hostSha },
|
|
@@ -47,6 +49,18 @@ async function fixture() {
|
|
|
47
49
|
prefix_args: ['/d', '/q'], max_args: 0, max_arg_bytes: 256,
|
|
48
50
|
max_sessions: 1, max_input_bytes: 4096, max_retained_output_bytes: 65536,
|
|
49
51
|
env_allow: ['PATH', 'TEMP', 'TMP'], input_actions: { ping: 'echo mustnotrun\r\n' }
|
|
52
|
+
},
|
|
53
|
+
powershell: {
|
|
54
|
+
executable: powershell, executable_sha256: powershellSha, root_ids: ['core'], transport: 'pipe',
|
|
55
|
+
prefix_args: ['-NoLogo','-NoProfile','-NonInteractive','-Command'], max_args: 1, max_arg_bytes: 8192,
|
|
56
|
+
max_sessions: 2, max_input_bytes: 65536, max_retained_output_bytes: 1048576,
|
|
57
|
+
env_allow: ['PATH','TEMP','TMP','SystemRoot','WINDIR','USERPROFILE','PSModulePath','COMSPEC','PATHEXT'], input_actions: {}
|
|
58
|
+
},
|
|
59
|
+
powershell_repl: {
|
|
60
|
+
executable: powershell, executable_sha256: powershellSha, root_ids: ['core'], transport: 'pipe', allow_raw_input: true,
|
|
61
|
+
prefix_args: ['-NoLogo','-NoProfile','-NonInteractive'], max_args: 0, max_arg_bytes: 256,
|
|
62
|
+
max_sessions: 2, max_input_bytes: 65536, max_retained_output_bytes: 4 * 1024 * 1024,
|
|
63
|
+
env_allow: ['PATH','TEMP','TMP','SystemRoot','WINDIR','USERPROFILE','PSModulePath','COMSPEC','PATHEXT'], input_actions: {}
|
|
50
64
|
}
|
|
51
65
|
}
|
|
52
66
|
};
|
|
@@ -54,7 +68,7 @@ async function fixture() {
|
|
|
54
68
|
const policy = await loadProcessPolicy(policyPath);
|
|
55
69
|
await writeFile(grantFile, JSON.stringify({
|
|
56
70
|
schema: PROCESS_GRANT_SCHEMA, policy_sha256: policy.policy_sha256,
|
|
57
|
-
instance_nonce: 'a'.repeat(64), profile_ids: ['echo', 'hold', 'conptyhold'], root_ids: ['core'],
|
|
71
|
+
instance_nonce: 'a'.repeat(64), profile_ids: ['echo', 'hold', 'conptyhold', 'powershell', 'powershell_repl'], root_ids: ['core'],
|
|
58
72
|
granted_at_utc: '2026-09-14T00:00:00Z'
|
|
59
73
|
}));
|
|
60
74
|
const keys = await generateTrustKeypair({
|
|
@@ -90,6 +104,102 @@ test('R31 runtime starts one approved profile and exposes ordered bounded output
|
|
|
90
104
|
assert.equal(observed.page.truncated, false);
|
|
91
105
|
});
|
|
92
106
|
|
|
107
|
+
test('R46 runtime executes fileless PowerShell source without ps1 materialization', { skip: process.platform !== 'win32' }, async () => {
|
|
108
|
+
const f = await fixture();
|
|
109
|
+
const source = "$ErrorActionPreference='Stop';$before=@(Get-ChildItem -LiteralPath $env:TEMP -Filter '*.ps1' -File -ErrorAction SilentlyContinue|ForEach-Object{$_.FullName});[Console]::WriteLine('DB_FILELESS_PS_TEST');[Console]::WriteLine('PSCommandPath=' + [string]$PSCommandPath);[Console]::WriteLine('InvocationPath=' + [string]$MyInvocation.MyCommand.Path);$after=@(Get-ChildItem -LiteralPath $env:TEMP -Filter '*.ps1' -File -ErrorAction SilentlyContinue|ForEach-Object{$_.FullName});$new=@($after|Where-Object{$_ -notin $before});[Console]::WriteLine('NEW_TEMP_PS1_COUNT=' + $new.Count)";
|
|
110
|
+
const started = await f.runtime.start({ profileId: 'powershell', rootId: 'core', cwd: '.', args: [source] });
|
|
111
|
+
const observed = await waitFor(f.runtime, started.session_id, text => text.includes('NEW_TEMP_PS1_COUNT=0'));
|
|
112
|
+
assert.match(observed.text, /DB_FILELESS_PS_TEST/);
|
|
113
|
+
assert.match(observed.text, /PSCommandPath=\r?\n/);
|
|
114
|
+
assert.match(observed.text, /InvocationPath=\r?\n/);
|
|
115
|
+
assert.match(observed.text, /NEW_TEMP_PS1_COUNT=0/);
|
|
116
|
+
const deadline = Date.now() + 5000;
|
|
117
|
+
let status = await f.runtime.status({ sessionId: started.session_id });
|
|
118
|
+
while (status.state === 'running' && Date.now() < deadline) {
|
|
119
|
+
await sleep(25);
|
|
120
|
+
status = await f.runtime.status({ sessionId: started.session_id });
|
|
121
|
+
}
|
|
122
|
+
assert.equal(status.state, 'exited');
|
|
123
|
+
assert.equal(status.exit_code, 0);
|
|
124
|
+
const verified = await f.runtime.verifyEventJournal({ sessionId: started.session_id });
|
|
125
|
+
assert.equal(verified.ok, true);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('R46 process output retention evicts oldest bytes without losing tail identity under multi-megabyte output', { skip: process.platform !== 'win32' }, async () => {
|
|
129
|
+
const f = await fixture();
|
|
130
|
+
const source = "[Console]::Write(('A'*2097152));[Console]::WriteLine('DB_RING_TAIL_MARK')";
|
|
131
|
+
const started = await f.runtime.start({ profileId: 'powershell', rootId: 'core', cwd: '.', args: [source] });
|
|
132
|
+
const deadline = Date.now() + 10000;
|
|
133
|
+
let status = await f.runtime.status({ sessionId: started.session_id });
|
|
134
|
+
while (status.state === 'running' && Date.now() < deadline) {
|
|
135
|
+
await sleep(25);
|
|
136
|
+
status = await f.runtime.status({ sessionId: started.session_id });
|
|
137
|
+
}
|
|
138
|
+
assert.equal(status.state, 'exited');
|
|
139
|
+
assert.equal(status.exit_code, 0);
|
|
140
|
+
assert.ok(status.stream_head > 2 * 1024 * 1024);
|
|
141
|
+
assert.ok(status.earliest_offset > 0);
|
|
142
|
+
assert.ok(status.stream_head - status.earliest_offset <= 1024 * 1024);
|
|
143
|
+
const fromStart = await f.runtime.read({ sessionId: started.session_id, offset: 0, maxBytes: 4096 });
|
|
144
|
+
assert.equal(fromStart.truncated, true);
|
|
145
|
+
assert.equal(fromStart.offset, status.earliest_offset);
|
|
146
|
+
const tail = await f.runtime.read({ sessionId: started.session_id, offset: -4096, maxBytes: 4096 });
|
|
147
|
+
const tailText = Buffer.from(tail.data_base64, 'base64').toString('utf8');
|
|
148
|
+
assert.match(tailText, /DB_RING_TAIL_MARK/);
|
|
149
|
+
const verified = await f.runtime.verifyEventJournal({ sessionId: started.session_id });
|
|
150
|
+
assert.equal(verified.ok, true);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('R46 process raw input keeps persistent PowerShell state with bounded wait and tail reads', { skip: process.platform !== 'win32' }, async () => {
|
|
154
|
+
const f = await fixture();
|
|
155
|
+
const started = await f.runtime.start({ profileId: 'powershell_repl', rootId: 'core', cwd: '.', args: [] });
|
|
156
|
+
try {
|
|
157
|
+
const first = await f.runtime.write({ sessionId: started.session_id, input: "$x=41;[Console]::WriteLine('DB_REPL_SET')", appendNewline: true });
|
|
158
|
+
assert.equal(first.session.state, 'running');
|
|
159
|
+
assert.match(first.logical_sha256, /^[0-9a-f]{64}$/);
|
|
160
|
+
const page1 = await f.runtime.read({ sessionId: started.session_id, offset: 0, maxBytes: 65536, waitMs: 3000, waitFor: 'DB_REPL_SET' });
|
|
161
|
+
const text1 = Buffer.from(page1.data_base64, 'base64').toString('utf8');
|
|
162
|
+
assert.match(text1, /DB_REPL_SET/);
|
|
163
|
+
|
|
164
|
+
const beforeSecond = (await f.runtime.status({ sessionId: started.session_id })).stream_head;
|
|
165
|
+
await f.runtime.write({ sessionId: started.session_id, input: "[Console]::WriteLine('DB_REPL_VALUE=' + ($x+1))", appendNewline: true });
|
|
166
|
+
const waited = await f.runtime.read({ sessionId: started.session_id, offset: beforeSecond, maxBytes: 65536, waitMs: 3000, waitFor: 'DB_REPL_VALUE=42' });
|
|
167
|
+
const waitedText = Buffer.from(waited.data_base64, 'base64').toString('utf8');
|
|
168
|
+
assert.equal(waited.wait_satisfied, true);
|
|
169
|
+
assert.match(waitedText, /DB_REPL_VALUE=42/);
|
|
170
|
+
const tail = await f.runtime.read({ sessionId: started.session_id, offset: -4096, maxBytes: 4096, waitMs: 0 });
|
|
171
|
+
const tailText = Buffer.from(tail.data_base64, 'base64').toString('utf8');
|
|
172
|
+
assert.match(tailText, /DB_REPL_VALUE=42/);
|
|
173
|
+
|
|
174
|
+
const journal = await readFile(path.join(f.stateDir, `${started.session_id}.jsonl`), 'utf8');
|
|
175
|
+
assert.doesNotMatch(journal, /DB_REPL_SET|DB_REPL_VALUE|\$x=41/);
|
|
176
|
+
const verified = await f.runtime.verifyEventJournal({ sessionId: started.session_id });
|
|
177
|
+
assert.equal(verified.ok, true);
|
|
178
|
+
assert.ok(verified.event_count >= 3);
|
|
179
|
+
} finally {
|
|
180
|
+
await f.runtime.terminate({ sessionId: started.session_id }).catch(() => {});
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test('R46 raw process input is profile-opt-in, bounded, and rechecks standing authority', { skip: process.platform !== 'win32' }, async () => {
|
|
185
|
+
const f = await fixture();
|
|
186
|
+
const hold = await f.runtime.start({ profileId: 'hold', rootId: 'core', cwd: '.', args: [] });
|
|
187
|
+
try {
|
|
188
|
+
await assert.rejects(() => f.runtime.write({ sessionId: hold.session_id, input: 'echo forbidden', appendNewline: true }), /does not allow raw input/);
|
|
189
|
+
} finally {
|
|
190
|
+
await f.runtime.terminate({ sessionId: hold.session_id }).catch(() => {});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const repl = await f.runtime.start({ profileId: 'powershell_repl', rootId: 'core', cwd: '.', args: [] });
|
|
194
|
+
try {
|
|
195
|
+
await assert.rejects(() => f.runtime.write({ sessionId: repl.session_id, input: 'x'.repeat(65536), appendNewline: true }), /exceeds profile input bound/);
|
|
196
|
+
await rm(f.grantFile);
|
|
197
|
+
await assert.rejects(() => f.runtime.write({ sessionId: repl.session_id, input: 'Write-Output denied', appendNewline: true }), /process plane is disarmed/);
|
|
198
|
+
} finally {
|
|
199
|
+
await f.runtime.terminate({ sessionId: repl.session_id }).catch(() => {});
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
93
203
|
test('R31 runtime lists, reports and terminates only its owned session', { skip: process.platform !== 'win32' }, async () => {
|
|
94
204
|
const f = await fixture();
|
|
95
205
|
const started = await f.runtime.start({ profileId: 'hold', rootId: 'core', cwd: '.', args: [] });
|
|
@@ -7,7 +7,7 @@ const runtimePath = fileURLToPath(new URL('../src/process-runtime.mjs', import.m
|
|
|
7
7
|
const serverPath = fileURLToPath(new URL('../src/mcp-server.mjs', import.meta.url));
|
|
8
8
|
const toolsPath = fileURLToPath(new URL('../src/process-mcp-tools.mjs', import.meta.url));
|
|
9
9
|
|
|
10
|
-
test('
|
|
10
|
+
test('R46 process supervision is policy-gated and exposes bounded action plus opt-in raw input', async () => {
|
|
11
11
|
const runtimeSource = await readFile(runtimePath, 'utf8');
|
|
12
12
|
const serverSource = await readFile(serverPath, 'utf8');
|
|
13
13
|
const toolsSource = await readFile(toolsPath, 'utf8');
|
|
@@ -20,5 +20,7 @@ test('R32 process supervision is policy-gated and registers the bounded named-ac
|
|
|
20
20
|
assert.match(serverSource, /processRuntime/);
|
|
21
21
|
assert.match(toolsSource, /process_session_action/);
|
|
22
22
|
assert.match(toolsSource, /action_id/);
|
|
23
|
-
assert.
|
|
23
|
+
assert.match(toolsSource, /process_session_write/);
|
|
24
|
+
assert.match(toolsSource, /append_newline/);
|
|
25
|
+
assert.doesNotMatch(toolsSource, /caller-selected executable|raw_stdin/);
|
|
24
26
|
});
|
|
@@ -9,9 +9,9 @@ const readme=()=>readFile(path.join(root,'README.txt'),'utf8');
|
|
|
9
9
|
|
|
10
10
|
test('production README documents one-command stdio configs for major MCP clients',async()=>{
|
|
11
11
|
const text=await readme();
|
|
12
|
-
assert.match(text,/npx -y deadbyte-mcp@0\.
|
|
12
|
+
assert.match(text,/npx -y deadbyte-mcp@0\.15\.2 mcp/);
|
|
13
13
|
assert.match(text,/CLAUDE DESKTOP/);
|
|
14
|
-
assert.match(text,/"mcpServers"[\s\S]*?"command"\s*:\s*"npx"[\s\S]*?"-y"[\s\S]*?"deadbyte-mcp@0\.
|
|
14
|
+
assert.match(text,/"mcpServers"[\s\S]*?"command"\s*:\s*"npx"[\s\S]*?"-y"[\s\S]*?"deadbyte-mcp@0\.15\.2"[\s\S]*?"mcp"/);
|
|
15
15
|
assert.match(text,/CURSOR/);
|
|
16
16
|
assert.match(text,/\.cursor\/mcp\.json/);
|
|
17
17
|
assert.match(text,/VS CODE/);
|
|
@@ -20,7 +20,7 @@ test('production README documents one-command stdio configs for major MCP client
|
|
|
20
20
|
assert.match(text,/CODEX/);
|
|
21
21
|
assert.match(text,/\[mcp_servers\.deadbyte\]/);
|
|
22
22
|
assert.match(text,/command\s*=\s*"npx"/);
|
|
23
|
-
assert.match(text,/args\s*=\s*\["-y",\s*"deadbyte-mcp@0\.
|
|
23
|
+
assert.match(text,/args\s*=\s*\["-y",\s*"deadbyte-mcp@0\.15\.2",\s*"mcp"\]/);
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
test('production README records tracer bullet, semantic scope, remote UX, safety and document extension',async()=>{
|
|
@@ -86,23 +86,24 @@ test('audit restart reconciles an orphan request with an explicit interrupted re
|
|
|
86
86
|
}finally{await rm(stateRoot,{recursive:true,force:true});}
|
|
87
87
|
});
|
|
88
88
|
|
|
89
|
-
test('Windows current
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
89
|
+
test('Windows current R45 gate binds exact sealed R44 parent and machine-freedom evidence contract', async()=>{
|
|
90
|
+
const gate=await readFile(path.join(root,'scripts','gate-windows-r45.ps1'),'utf8');
|
|
91
|
+
const evidenceSource=await readFile(path.join(root,'scripts','windows-gate-evidence-r45.mjs'),'utf8');
|
|
92
|
+
const parent=JSON.parse(await readFile(path.join(root,'R45-PARENT.json'),'utf8'));
|
|
93
|
+
assert.match(gate,/windows-gate-evidence-r45\.mjs/);
|
|
94
|
+
assert.match(gate,/r45-parent-verify/);
|
|
95
|
+
assert.match(gate,/host-policy-refresh/);
|
|
96
|
+
assert.match(gate,/hardware-build/);
|
|
97
|
+
assert.match(gate,/doctor/);
|
|
98
|
+
assert.match(evidenceSource,/deadbyte\.r45-windows-gate\.v1/);
|
|
99
|
+
assert.match(evidenceSource,/version:'0\.15\.1'/);
|
|
100
|
+
assert.match(evidenceSource,/DEADBYTE V0\.15\.1 \/ R45 MACHINE FREEDOM WINDOWS BATCH GATE: PASS/);
|
|
101
|
+
for(const key of ['host_inline_native','native_hardware_observation','doctor_release_truth']) assert.match(evidenceSource,new RegExp(key+':true'));
|
|
102
|
+
assert.equal(parent.parent_release_id,'v0.15.0-c15fa41719be61b3');
|
|
103
|
+
assert.equal(parent.parent_manifest_sha256,'c15fa41719be61b3186e49b2367a5f4c057365cfb465102dbefb7d7355f45483');
|
|
104
|
+
assert.equal(parent.parent_generation_manifest_sha256,'6d7429c050b7fe893492e102f00282019116e0c9e54e4f25a5791defa1661d30');
|
|
105
|
+
assert.equal(parent.parent_final_closure_sha256,'533b7e0ff9ded04086dd90ed34e3b87bc427acc3c55c25bb9f4ec66cb448e667');
|
|
106
|
+
const historical=await readFile(path.join(root,'scripts','windows-gate-evidence-r44.mjs'),'utf8');
|
|
107
|
+
assert.match(historical,/version:'0\.15\.0'/);
|
|
108
|
+
assert.match(historical,/deadbyte\.r44-windows-gate\.v1/);
|
|
108
109
|
});
|
|
@@ -19,9 +19,9 @@ test('R34 public parity catalog is exact, unique, and includes production coding
|
|
|
19
19
|
|
|
20
20
|
test('release parity and final verifier are package entry points', async () => {
|
|
21
21
|
const pkg = JSON.parse(await text('package.json'));
|
|
22
|
-
assert.equal(pkg.scripts['release:parity'], 'node scripts/release-parity-
|
|
23
|
-
assert.equal(pkg.scripts['release:final:write'], 'node scripts/final-closure-
|
|
24
|
-
assert.equal(pkg.scripts['release:final:verify'], 'node scripts/final-closure-verify-
|
|
22
|
+
assert.equal(pkg.scripts['release:parity'], 'node scripts/release-parity-r46.mjs');
|
|
23
|
+
assert.equal(pkg.scripts['release:final:write'], 'node scripts/final-closure-r46.mjs');
|
|
24
|
+
assert.equal(pkg.scripts['release:final:verify'], 'node scripts/final-closure-verify-r46.mjs');
|
|
25
25
|
assert.equal(typeof validateParityEvidence, 'function');
|
|
26
26
|
assert.equal(typeof verifyFinalClosureObject, 'function');
|
|
27
27
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
|
|
5
|
+
test('R45 autonomous quick-test is fixture-scoped and does not invoke legacy npm test matrix', async () => {
|
|
6
|
+
const text=await readFile(new URL('../scripts/autonomous-tests.ps1',import.meta.url),'utf8');
|
|
7
|
+
assert.match(text,/node\.exe --test test\/autonomous-live-fixture\.test\.mjs/);
|
|
8
|
+
assert.doesNotMatch(text,/npm\.cmd test/);
|
|
9
|
+
assert.match(text,/R24 QUICK TEST PASS/);
|
|
10
|
+
});
|