deadbyte-mcp 0.15.50 → 0.15.51

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/SBOM.cdx.json CHANGED
@@ -1,25 +1,25 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.6",
4
- "serialNumber": "urn:cdx:deadbyte-mcp:r130:0.15.50",
4
+ "serialNumber": "urn:cdx:deadbyte-mcp:r132:0.15.51",
5
5
  "version": 1,
6
6
  "metadata": {
7
7
  "component": {
8
8
  "type": "application",
9
9
  "name": "deadbyte-mcp",
10
- "version": "0.15.50"
10
+ "version": "0.15.51"
11
11
  }
12
12
  },
13
13
  "components": [
14
14
  {
15
15
  "type": "library",
16
16
  "name": "@modelcontextprotocol/client",
17
- "version": "2.1.0"
17
+ "version": "2.3.1"
18
18
  },
19
19
  {
20
20
  "type": "library",
21
21
  "name": "@modelcontextprotocol/server",
22
- "version": "2.1.0"
22
+ "version": "2.3.1"
23
23
  },
24
24
  {
25
25
  "type": "library",
@@ -60,6 +60,11 @@
60
60
  }
61
61
  ],
62
62
  "evidence_status": "CANDIDATE_BINARY_PIN_ONLY_NOT_DEPLOYED"
63
+ },
64
+ {
65
+ "type": "library",
66
+ "name": "@modelcontextprotocol/core",
67
+ "version": "2.3.1"
63
68
  }
64
69
  ]
65
70
  }
package/TEST-MATRIX.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schema": "deadbyte.test-matrix.v1",
3
- "version": "0.15.50",
4
- "train": "R130",
3
+ "version": "0.15.51",
4
+ "train": "R132",
5
5
  "primary_platform": "win32-x64",
6
6
  "reference": {
7
7
  "package": "@wonderwhy-er/desktop-commander",
@@ -34,6 +34,21 @@
34
34
  "id": "r71-owner-grant",
35
35
  "command": "Protected owner authentication and project scope",
36
36
  "expected": "BLOCK unless independently owner enrolled and scope verified"
37
+ },
38
+ {
39
+ "id": "r132-protected-full",
40
+ "command": "node --test --test-concurrency=4 test/*.test.mjs on exact R132 protected fixture",
41
+ "expected": "Zero failures, historical skips recorded, manifest verify-installed pass"
42
+ },
43
+ {
44
+ "id": "r132-independent-rollback",
45
+ "command": "isolated activation -> rollback -> restore smoke on scratch state root",
46
+ "expected": "No production mutation; runtime predecessor preserved"
47
+ },
48
+ {
49
+ "id": "r132-artifact-parity",
50
+ "command": "node scripts/npm-release-r48.mjs --verify-only + fresh npm audit",
51
+ "expected": "Verified exact 0.15.51 tarball, SDK 2.3.1 and 0 vulnerabilities"
37
52
  }
38
53
  ],
39
54
  "observed_pre_release_evidence": {
@@ -0,0 +1,23 @@
1
+ # R132 / deadbyte-mcp@0.15.51 — manifest portability
2
+
3
+ Status: ISOLATED RELEASE CANDIDATE (no npm publish or production activation).
4
+ This release train follows published 0.15.50 and the independently tested R131 developer successor.
5
+
6
+ ## Defect reproduced
7
+ npm v12.1.0 built a package manifest on the source checkout that included `node_modules/which/CHANGELOG.md`. After installing into a separate protected Windows filesystem, a fresh `npm pack --dry-run` omitted that file even though its SHA-256 and bytes remained unchanged. Consequently the old development-package comparison failed in an installed environment (R130: 1,699 pass, 1 failure, 49 skipped). This is NOT evidence of a runtime executable/hash mutation.
8
+
9
+ ## Surgical fix (R131)
10
+ - Keep `release-manifest.mjs verify` unchanged for authors creating an npm tarball: full npm-pack list equality, no path exceptions.
11
+ - Introduce `release-manifest.mjs verify-installed` for protected installations: independently verify SHA-256 of EVERY manifested file and reject extraneous regular files unless their path is exactly `.npmignore` or `package-lock.json`. No directory-level exclusions; symlinks, unexpected runtime files and mutated bytes still fail.
12
+ - `npm-prepublish-contract-r74.mjs` source/installed mode is explicit; defaults to strict source verification. On protected Windows the test selects installed mode via `DEADBYTE_TEST_PROTECTED_MANIFEST=installed`. It still tests pinned native hashes, predecessor separation and private file leakage.
13
+ - R131 protected Windows full native 1,705 PASS, 0 FAIL, 49 SKIP (1,754 tests); source manifest 2,371 files PASS.
14
+ - R132 adds honest 0.15.51 release metadata, current 2.3.1 client/server/core SBOM, and new source-predecessor identity tests. R132 exact-byte gates must run independently; R131 results are not R132 results.
15
+
16
+ ## Immutable lineage
17
+ - npm published 0.15.50 tarball SHA-256 `133b9f4adedee14a3e4f2fd1c9433f7ebf102742a73398161b238fd68eb42d84`.
18
+ - npm 0.15.50 source manifest SHA-256 `98dd37c71adae980856adc17b9c59d58d167a01d46c61106055956d0f306fede`.
19
+ - R131 predecessor candidate source manifest SHA-256 `3e5edb9987aa7c9004dda3164a278da70a60495c0c128e2602426075a5222c10`.
20
+ - Production runtime predecessor remains v0.15.45; its pointer must not change during isolated gates.
21
+
22
+ ## Remaining release gates
23
+ Full Windows native regression on exact R132 protected fixture; isolated activation/rollback/restore tests with scratch state; npm pack manifest and independent doctor; SBOM parity and npm audit; no production activation, grant changes or npm publish until the respective gates and explicit actions.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deadbyte-mcp",
3
- "version": "0.15.50",
3
+ "version": "0.15.51",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Authenticated MCP agentic runtime with signed skills, persistent model backends, identity-bound remote devices, durable job supervision, immutable release slots, and separately revocable machine authority.",
@@ -7,7 +7,8 @@ import {fileURLToPath} from 'node:url';
7
7
  const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
8
8
  const sha256Bytes = bytes => createHash('sha256').update(bytes).digest('hex');
9
9
 
10
- export async function verifyR74PrepublishContract({rootPath = root} = {}) {
10
+ export async function verifyR74PrepublishContract({rootPath = root, manifestVerification = 'source'} = {}) {
11
+ if (!['source','installed'].includes(manifestVerification)) throw new Error('Unsupported prepublish manifest verification mode');
11
12
  const result = {
12
13
  schema: 'deadbyte.r74-prepublish-contract.v1',
13
14
  valid: false,
@@ -63,7 +64,11 @@ export async function verifyR74PrepublishContract({rootPath = root} = {}) {
63
64
  result.identities.manifest_sha256 = sha256Bytes(manifestBytes);
64
65
  result.checks.manifest_exists = true;
65
66
 
66
- const manifestVerifyResult = spawnSync(process.execPath, [path.join(rootPath, 'scripts', 'release-manifest.mjs'), 'verify'], {
67
+ // Authoring/release packaging retains strict npm-pack manifest equality.
68
+ // Isolated protected installations instead validate every pinned byte and
69
+ // reject unlisted files except two fixed non-runtime packaging metadata files.
70
+ const manifestCommand = manifestVerification === 'installed' ? 'verify-installed' : 'verify';
71
+ const manifestVerifyResult = spawnSync(process.execPath, [path.join(rootPath, 'scripts', 'release-manifest.mjs'), manifestCommand], {
67
72
  cwd: rootPath,
68
73
  encoding: 'utf8',
69
74
  timeout: 60000,
@@ -73,6 +78,7 @@ export async function verifyR74PrepublishContract({rootPath = root} = {}) {
73
78
  throw new Error(`Prepublish contract failed: working tree manifest verification failed: ${manifestVerifyResult.stderr || manifestVerifyResult.stdout}`);
74
79
  }
75
80
  result.checks.manifest_verified = true;
81
+ result.checks.manifest_verification_mode = manifestVerification;
76
82
 
77
83
  // 4. Packaged files boundary (no leaks of private/testing trees)
78
84
  const npmCli = process.env.npm_execpath || path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js');
@@ -175,6 +175,19 @@ async function verifyTree() {
175
175
  console.log(`MANIFEST_VERIFY_TREE_OK files=${entries.size} sha256=${sha256(actual)}`);
176
176
  }
177
177
 
178
+ // Installed-tree verification deliberately avoids regenerating the packlist
179
+ // (npm pack has environment-sensitive inclusion behavior after installation).
180
+ // Every manifested byte is still hash-checked; only precisely named
181
+ // packaging metadata, excluded from npm payload, may appear additionally.
182
+ async function verifyInstalled() {
183
+ const actual = await readFile(manifestPath);
184
+ const entries = parseSlotManifest(actual.toString('utf8'));
185
+ await verifyManifestTreeExact(root, entries, {
186
+ allowedExtraPaths:['.npmignore','package-lock.json']
187
+ });
188
+ console.log(`MANIFEST_VERIFY_INSTALLED_OK files=${entries.size} sha256=${sha256(actual)}`);
189
+ }
190
+
178
191
  const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : '';
179
192
  if (invokedPath === fileURLToPath(import.meta.url)) {
180
193
  const command = process.argv[2] ?? 'verify';
@@ -182,7 +195,8 @@ if (invokedPath === fileURLToPath(import.meta.url)) {
182
195
  if (command === 'write') await writeManifest();
183
196
  else if (command === 'verify') await verifyManifest();
184
197
  else if (command === 'verify-tree') await verifyTree();
185
- else throw new Error(`usage: node scripts/release-manifest.mjs [write|verify|verify-tree]`);
198
+ else if (command === 'verify-installed') await verifyInstalled();
199
+ else throw new Error(`usage: node scripts/release-manifest.mjs [write|verify|verify-tree|verify-installed]`);
186
200
  } catch (error) {
187
201
  console.error(`MANIFEST_${command.toUpperCase()}_FAIL ${error?.stack ?? error}`);
188
202
  process.exitCode = 1;
@@ -79,13 +79,23 @@ async function listManifestTreeFiles(root){
79
79
  return files.sort((a,b)=>a.localeCompare(b,'en'));
80
80
  }
81
81
 
82
- export async function verifyManifestTreeExact(root,manifest){
82
+ export async function verifyManifestTreeExact(root,manifest,{allowedExtraPaths=[]}={}){
83
+ // The only unmanifested files accepted in a protected test fixture are
84
+ // packaging inputs that npm intentionally excludes from published payloads.
85
+ // No subdirectory, wildcard, executable or mutable runtime state is exempt.
86
+ const allowedPackagingExtras=new Set(['.npmignore','package-lock.json']);
87
+ if(!Array.isArray(allowedExtraPaths)||allowedExtraPaths.some(rel=>!allowedPackagingExtras.has(rel))){
88
+ throw new Error('invalid allowed manifest extra');
89
+ }
90
+ const extras=new Set(allowedExtraPaths);
83
91
  await verifyManifestTree(root,manifest);
84
92
  const actual=await listManifestTreeFiles(root);
85
93
  const expected=[...manifest.keys()].sort((a,b)=>a.localeCompare(b,'en'));
86
- for(const rel of actual) if(!manifest.has(rel)) throw new Error(`unexpected manifest-tree file: ${rel}`);
94
+ for(const rel of actual) if(!manifest.has(rel)&&!extras.has(rel)) throw new Error(`unexpected manifest-tree file: ${rel}`);
87
95
  for(const rel of expected) if(!actual.includes(rel)) throw new Error(`missing manifest-tree file: ${rel}`);
88
- if(actual.length!==expected.length) throw new Error(`manifest-tree file count mismatch: expected=${expected.length} actual=${actual.length}`);
96
+ if(actual.length!==expected.length+actual.filter(rel=>extras.has(rel)&&!manifest.has(rel)).length){
97
+ throw new Error(`manifest-tree file count mismatch: expected=${expected.length} actual=${actual.length}`);
98
+ }
89
99
  return {files:manifest.size};
90
100
  }
91
101
  export async function copyManifestTree({source,destination,manifest}){
package/src/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const DEADBYTE_VERSION = '0.15.50';
1
+ export const DEADBYTE_VERSION = '0.15.51';
2
2
  export const MCP_PROTOCOL_VERSION = '2026-07-28';
3
3
  export const RECEIPT_SCHEMA_V1 = 'deadbyte.receipt.v1';
4
4
  export const RECEIPT_SCHEMA_V2 = 'deadbyte.receipt.v2';
@@ -13,14 +13,14 @@ test('R130 security patch has a new immutable package identity and truthful sour
13
13
  read('SBOM.cdx.json'),read('package-lock.json'),read('TEST-MATRIX.json')]);
14
14
  assert.equal(pkg.name,'deadbyte-mcp');
15
15
  for(const actual of [pkg.version,release.version,provenance.version,matrix.version,bom.metadata.component.version,lock.version,lock.packages[''].version,DEADBYTE_VERSION])
16
- assert.equal(actual,'0.15.50');
17
- assert.equal(release.train,'R130');
18
- assert.equal(matrix.train,'R130');
19
- assert.equal(release.source_predecessor.version,'0.15.49');
20
- assert.match(release.source_predecessor.source_path,/0\.15\.49/);
16
+ assert.equal(actual,'0.15.51');
17
+ assert.equal(release.train,'R132');
18
+ assert.equal(matrix.train,'R132');
19
+ assert.equal(release.source_predecessor.version,'0.15.50');
20
+ assert.match(release.source_predecessor.source_path,/0\.15\.50/);
21
21
  assert.equal(release.runtime_predecessor.version,'0.15.45');
22
- assert.equal(provenance.source_predecessor_version,'0.15.49');
23
- assert.equal(provenance.source_predecessor_manifest_file_sha256,'0c614697756731ee348f77cb36f9b1780f3037b5846e6482dc9a4c4e48cf01b6');
22
+ assert.equal(provenance.source_predecessor_version,'0.15.50');
23
+ assert.equal(provenance.source_predecessor_manifest_file_sha256,'98dd37c71adae980856adc17b9c59d58d167a01d46c61106055956d0f306fede');
24
24
  assert.equal(pkg.dependencies['@modelcontextprotocol/client'],'2.3.1');
25
25
  assert.equal(pkg.dependencies['@modelcontextprotocol/server'],'2.3.1');
26
26
  for(const dep of ['@modelcontextprotocol/client','@modelcontextprotocol/server'])
@@ -0,0 +1,65 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {mkdtemp, mkdir, rm, writeFile} from 'node:fs/promises';
4
+ import {createHash} from 'node:crypto';
5
+ import os from 'node:os';
6
+ import path from 'node:path';
7
+ import {verifyManifestTreeExact} from '../src/release-slot.mjs';
8
+
9
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
10
+
11
+ async function fixture(run) {
12
+ const root=await mkdtemp(path.join(os.tmpdir(),'deadbyte-r131-portable-manifest-'));
13
+ try {
14
+ await mkdir(path.join(root,'src'));
15
+ await writeFile(path.join(root,'src','payload.mjs'),'export const x=1;\n');
16
+ await writeFile(path.join(root,'.npmignore'),'# supplemental npm packaging data\n');
17
+ await writeFile(path.join(root,'package-lock.json'),'{"name":"fixture"}\n');
18
+ const manifest=new Map([['src/payload.mjs',sha(Buffer.from('export const x=1;\n'))]]);
19
+ return await run({root,manifest});
20
+ } finally {
21
+ await rm(root,{recursive:true,force:true});
22
+ }
23
+ }
24
+
25
+ test('R131 existing strict release-tree gate rejects any supplemental file', async()=>{
26
+ await fixture(async({root,manifest})=>{
27
+ await assert.rejects(verifyManifestTreeExact(root,manifest),/unexpected manifest-tree file/);
28
+ });
29
+ });
30
+
31
+ test('R131 protected-install gate verifies all manifest hashes but permits ONLY declared packaging extras',async()=>{
32
+ await fixture(async({root,manifest})=>{
33
+ const result=await verifyManifestTreeExact(root,manifest,{allowedExtraPaths:['.npmignore','package-lock.json']});
34
+ assert.deepEqual(result,{files:1});
35
+ });
36
+ });
37
+
38
+ test('R131 protected-install gate rejects unexpected executable file even with allowed packaging extras',async()=>{
39
+ await fixture(async({root,manifest})=>{
40
+ await writeFile(path.join(root,'src','rogue.mjs'),'export const privileged=true;\n');
41
+ await assert.rejects(
42
+ verifyManifestTreeExact(root,manifest,{allowedExtraPaths:['.npmignore','package-lock.json']}),
43
+ /unexpected manifest-tree file: src\/rogue\.mjs/
44
+ );
45
+ });
46
+ });
47
+
48
+ test('R131 protected-install gate rejects mutated manifested source bytes',async()=>{
49
+ await fixture(async({root,manifest})=>{
50
+ await writeFile(path.join(root,'src','payload.mjs'),'export const x=2;\n');
51
+ await assert.rejects(
52
+ verifyManifestTreeExact(root,manifest,{allowedExtraPaths:['.npmignore','package-lock.json']}),
53
+ /hash mismatch: src\/payload\.mjs/
54
+ );
55
+ });
56
+ });
57
+
58
+ test('R131 installed gate disallows broad directory exemptions',async()=>{
59
+ await fixture(async({root,manifest})=>{
60
+ await assert.rejects(
61
+ verifyManifestTreeExact(root,manifest,{allowedExtraPaths:['src/','.npmignore','package-lock.json']}),
62
+ /invalid allowed manifest extra/
63
+ );
64
+ });
65
+ });
@@ -0,0 +1,44 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import {readFile} from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import {fileURLToPath} from 'node:url';
6
+ import {DEADBYTE_VERSION} from '../src/version.mjs';
7
+
8
+ const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
9
+ const read=async rel=>JSON.parse(await readFile(path.join(root,rel),'utf8'));
10
+
11
+ test('R132 candidate identities and published source predecessor are consistent',async()=>{
12
+ const [pkg,lock,release,prov,bom,matrix]=await Promise.all([
13
+ read('package.json'),read('package-lock.json'),read('RELEASE.json'),
14
+ read('PROVENANCE.json'),read('SBOM.cdx.json'),read('TEST-MATRIX.json')]);
15
+ for(const value of [pkg.version,lock.version,lock.packages[''].version,release.version,prov.version,bom.metadata.component.version,matrix.version,DEADBYTE_VERSION]){
16
+ assert.equal(value,'0.15.51');
17
+ }
18
+ assert.equal(release.train,'R132');
19
+ assert.equal(matrix.train,'R132');
20
+ assert.equal(release.source_predecessor.version,'0.15.50');
21
+ assert.match(release.source_predecessor.source_path,/0\.15\.50/);
22
+ assert.equal(prov.source_predecessor_version,'0.15.50');
23
+ assert.equal(prov.source_predecessor_manifest_file_sha256,'98dd37c71adae980856adc17b9c59d58d167a01d46c61106055956d0f306fede');
24
+ assert.equal(prov.parent_tarball_sha256,'133b9f4adedee14a3e4f2fd1c9433f7ebf102742a73398161b238fd68eb42d84');
25
+ assert.equal(release.runtime_predecessor.version,'0.15.45');
26
+ assert.equal(prov.runtime_predecessor.release_id,release.runtime_predecessor.release_id);
27
+ assert.equal(prov.runtime_predecessor.manifest_sha256,release.runtime_predecessor.manifest_sha256);
28
+ assert.equal(prov.runtime_predecessor.generation_manifest_sha256,release.runtime_predecessor.generation_manifest_sha256);
29
+ assert.equal(release.r132.manifest_portability,'R131_PROTECTED_FULL_GREEN');
30
+ assert.equal(release.r132.production_activation,'NOT_RUN');
31
+ assert.equal(prov.r132.source_lineage_manifest_sha256,'3e5edb9987aa7c9004dda3164a278da70a60495c0c128e2602426075a5222c10');
32
+ assert.equal(matrix.required.some(t=>t.id==='r132-protected-full'),true);
33
+ });
34
+
35
+ test('R132 shipped SBOM matches the actual audited bundled MCP SDK',async()=>{
36
+ const [pkg,bom,lock]=await Promise.all([read('package.json'),read('SBOM.cdx.json'),read('package-lock.json')]);
37
+ for(const name of ['@modelcontextprotocol/client','@modelcontextprotocol/server','@modelcontextprotocol/core']){
38
+ const entry=bom.components.find(v=>v.name===name);
39
+ assert.ok(entry,'missing SBOM component: '+name);
40
+ assert.equal(entry.version,'2.3.1',name+' SBOM stale');
41
+ assert.equal(lock.packages['node_modules/'+name].version,'2.3.1',name+' lockfile stale');
42
+ if(name!=='@modelcontextprotocol/core')assert.equal(pkg.dependencies[name],'2.3.1');
43
+ }
44
+ });
@@ -83,7 +83,7 @@ test('R72 compact baseline permits only R64 read offset and authorized coding-on
83
83
  assert.deepEqual(diff.added,[]);
84
84
  assert.deepEqual(diff.removed,[]);
85
85
  const packageVersion=JSON.parse(await readFile(path.join(repoRoot,'package.json'),'utf8')).version;
86
- if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50'].includes(packageVersion)){
86
+ if(['0.15.41','0.15.42','0.15.43','0.15.44','0.15.45','0.15.46','0.15.47','0.15.48','0.15.49','0.15.50','0.15.51'].includes(packageVersion)){
87
87
  assert.deepEqual([...diff.schema_changed].sort(),['machine_fs','native_diff_preview']);
88
88
  assert.equal(diff.unchanged.length,41);
89
89
  const prior=baseline.tools.find(tool=>tool.name==='machine_fs');
@@ -9,7 +9,9 @@ import {verifyR74PrepublishContract} from '../scripts/npm-prepublish-contract-r7
9
9
  const repoRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
10
10
 
11
11
  test('R74 prepublish contract: positive path succeeds and verifies identities', async () => {
12
- const result = await verifyR74PrepublishContract({rootPath: repoRoot});
12
+ const manifestVerification = process.env.DEADBYTE_TEST_PROTECTED_MANIFEST === 'installed' ? 'installed' : 'source';
13
+ const result = await verifyR74PrepublishContract({rootPath: repoRoot, manifestVerification});
14
+ assert.equal(result.checks.manifest_verification_mode, manifestVerification);
13
15
  assert.equal(result.schema, 'deadbyte.r74-prepublish-contract.v1');
14
16
  assert.equal(result.valid, true);
15
17
  assert.equal(result.checks.predecessors_distinct, true);
@@ -19,8 +21,8 @@ test('R74 prepublish contract: positive path succeeds and verifies identities',
19
21
  assert.equal(result.checks.no_self_referential_tarball_hash, true);
20
22
  assert.equal(result.identities.package_name, 'deadbyte-mcp');
21
23
  // This R74 component contract remains scoped to train R74, even when
22
- // the containing npm package is the R130 0.15.50 security candidate.
23
- assert.equal(result.identities.package_version, '0.15.50');
24
+ // the containing npm package is the R132 0.15.51 candidate.
25
+ assert.equal(result.identities.package_version, '0.15.51');
24
26
  assert.equal(result.identities.target_train, 'R74');
25
27
  assert.equal(result.identities.published_source_predecessor, '0.15.46');
26
28
  assert.notEqual(result.identities.published_source_predecessor, result.identities.runtime_predecessor);