@yamo/mcp-server 1.3.9 → 1.3.10

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.
Files changed (2) hide show
  1. package/dist/index.js +33 -11
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -47,7 +47,7 @@ const crypto_1 = __importDefault(require("crypto"));
47
47
  const path_1 = __importDefault(require("path"));
48
48
  const pkg = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '../package.json'), 'utf8'));
49
49
  dotenv.config();
50
- // Validation helper for bytes32 hashes (Part 3: Security Fixes)
50
+ // Validation helpers
51
51
  function validateBytes32(value, fieldName) {
52
52
  if (!value.match(/^0x[a-fA-F0-9]{64}$/)) {
53
53
  throw new Error(`${fieldName} must be a valid bytes32 hash (0x + 64 hex chars). ` +
@@ -55,6 +55,21 @@ function validateBytes32(value, fieldName) {
55
55
  `\nDo NOT include algorithm prefixes like "sha256:"`);
56
56
  }
57
57
  }
58
+ function validateEthereumAddress(address, fieldName) {
59
+ if (!address || !address.match(/^0x[a-fA-F0-9]{40}$/)) {
60
+ throw new Error(`${fieldName} must be a valid Ethereum address (0x + 40 hex characters)`);
61
+ }
62
+ }
63
+ function validateEnvironment() {
64
+ const requiredEnvVars = ['CONTRACT_ADDRESS', 'RPC_URL', 'PRIVATE_KEY'];
65
+ const missing = requiredEnvVars.filter(v => !process.env[v]);
66
+ if (missing.length > 0) {
67
+ throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
68
+ }
69
+ // Validate contract address format
70
+ const contractAddress = process.env.CONTRACT_ADDRESS;
71
+ validateEthereumAddress(contractAddress, 'CONTRACT_ADDRESS');
72
+ }
58
73
  const SUBMIT_BLOCK_TOOL = {
59
74
  name: "yamo_submit_block",
60
75
  description: `Submits a YAMO block to the YAMORegistry smart contract.
@@ -253,6 +268,8 @@ class YamoMcpServer {
253
268
  // Cache for chain continuation: latest submitted block's contentHash
254
269
  latestContentHash = null;
255
270
  constructor() {
271
+ // Validate environment variables at startup
272
+ validateEnvironment();
256
273
  this.server = new index_js_1.Server({ name: "yamo", version: pkg.version }, { capabilities: { tools: {} } });
257
274
  this.ipfs = new core_1.IpfsManager();
258
275
  this.chain = new core_1.YamoChainClient();
@@ -270,24 +287,29 @@ class YamoMcpServer {
270
287
  // Process files - auto-read if they're file paths (with security fix)
271
288
  let processedFiles = files;
272
289
  if (files && Array.isArray(files)) {
273
- processedFiles = files.map((file) => {
290
+ processedFiles = await Promise.all(files.map(async (file) => {
274
291
  // Check if content is a file path that exists
275
292
  if (typeof file.content === 'string' && fs_1.default.existsSync(file.content)) {
276
- // Security: Resolve to absolute path and restrict to cwd (Part 3: Security Fixes)
277
- const filePath = path_1.default.resolve(file.content);
278
- const allowedDir = process.cwd();
293
+ // Security: Resolve symlinks and restrict to cwd (prevents symlink attacks and TOCTOU)
294
+ const filePath = fs_1.default.realpathSync(file.content);
295
+ const allowedDir = fs_1.default.realpathSync(process.cwd());
296
+ // Check for symlinks
297
+ const stats = fs_1.default.lstatSync(file.content);
298
+ if (stats.isSymbolicLink()) {
299
+ throw new Error(`Symbolic links are not allowed: ${file.content}`);
300
+ }
279
301
  if (!filePath.startsWith(allowedDir)) {
280
302
  throw new Error(`File path outside allowed directory: ${file.content}`);
281
303
  }
282
304
  console.error(`[DEBUG] Auto-reading file from path: ${file.content}`);
283
305
  return {
284
306
  name: file.name,
285
- content: fs_1.default.readFileSync(filePath, 'utf8')
307
+ content: await fs_1.default.promises.readFile(filePath, 'utf8')
286
308
  };
287
309
  }
288
310
  // Otherwise use content as-is
289
311
  return file;
290
- });
312
+ }));
291
313
  }
292
314
  // Input validation (Part 3: Security Fixes)
293
315
  validateBytes32(contentHash, "contentHash");
@@ -315,7 +337,7 @@ class YamoMcpServer {
315
337
  }
316
338
  }
317
339
  }
318
- else {
340
+ else if (previousBlock) {
319
341
  validateBytes32(previousBlock, "previousBlock");
320
342
  }
321
343
  let ipfsCID = undefined;
@@ -357,7 +379,7 @@ class YamoMcpServer {
357
379
  blockId,
358
380
  hint: "Verify the blockId or check if the block was submitted"
359
381
  }, null, 2) }],
360
- isError: false,
382
+ isError: true,
361
383
  };
362
384
  }
363
385
  return {
@@ -386,7 +408,7 @@ class YamoMcpServer {
386
408
  error: "No blocks found on-chain",
387
409
  hint: "The chain may be empty. Try submitting a genesis block first."
388
410
  }, null, 2) }],
389
- isError: false,
411
+ isError: true,
390
412
  };
391
413
  }
392
414
  return {
@@ -418,7 +440,7 @@ class YamoMcpServer {
418
440
  blockId,
419
441
  hint: "Cannot audit non-existent block"
420
442
  }, null, 2) }],
421
- isError: false,
443
+ isError: true,
422
444
  };
423
445
  }
424
446
  // If no IPFS CID, can't audit content
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yamo/mcp-server",
3
- "version": "1.3.9",
3
+ "version": "1.3.10",
4
4
  "description": "YAMO Protocol v0.4 - Model Context Protocol server for AI agents",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",