@atlisp/mcp 1.8.6 → 1.8.8

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.
@@ -3,10 +3,31 @@ import edge from 'edge-js';
3
3
  import process from 'process';
4
4
  import fs from 'fs';
5
5
  import path from 'path';
6
+ import crypto from 'crypto';
6
7
 
7
8
  const BUSY_RETRIES = parseInt(process.env.BUSY_RETRIES || '10', 10);
8
9
  const BUSY_DELAY = parseInt(process.env.BUSY_DELAY || '500', 10);
9
10
 
11
+ // Lisp expression cache (hash -> compiled Lisp code)
12
+ const LISP_CACHE = new Map();
13
+ const LISP_CACHE_MAX = parseInt(process.env.LISP_CACHE_MAX || '500', 10);
14
+
15
+ function getCachedLisp(code) {
16
+ const hash = crypto.createHash('sha256').update(code).digest('hex');
17
+ if (LISP_CACHE.has(hash)) return LISP_CACHE.get(hash);
18
+ return null;
19
+ }
20
+
21
+ function setCachedLisp(code, compiled) {
22
+ const hash = crypto.createHash('sha256').update(code).digest('hex');
23
+ if (LISP_CACHE.size >= LISP_CACHE_MAX) {
24
+ const firstKey = LISP_CACHE.keys().next().value;
25
+ LISP_CACHE.delete(firstKey);
26
+ }
27
+ LISP_CACHE.set(hash, compiled);
28
+ return compiled;
29
+ }
30
+
10
31
  const DEBUG_FILE = process.env.DEBUG_FILE || path.join(process.env.TEMP || '/tmp', 'cad-worker-debug.log');
11
32
 
12
33
  function log(msg) {
@@ -107,10 +128,9 @@ string code = d.code.ToString();
107
128
  dynamic cad = System.Runtime.InteropServices.Marshal.GetActiveObject(progId);
108
129
  if (cad != null) {
109
130
  dynamic doc = cad.ActiveDocument;
110
- if (doc != null) {
111
- doc.SendCommand("(if(null @::load-module)(progn(vl-load-com)(setq s strcat h\"http\" o(vlax-create-object \"winhttp.winhttprequest.5.1\")v vlax-invoke e eval r read)(v o'open \"get\" \"http://atlisp.cn/cloud\":vlax-true)(v o'send)(v o'WaitforResponse 1000)(e(r(vlax-get o'ResponseText)))))\n");
112
- doc.SendCommand(code);
113
- return new { success = true };
131
+ if (doc != null) {
132
+ doc.SendCommand(code);
133
+ return new { success = true };
114
134
  }
115
135
  }
116
136
  } catch (Exception ex) {
@@ -156,10 +176,8 @@ string encoding = d.encoding != null ? d.encoding.ToString() : "";
156
176
  dynamic cad = System.Runtime.InteropServices.Marshal.GetActiveObject(progId);
157
177
  if (cad != null) {
158
178
  dynamic doc = cad.ActiveDocument;
159
- if (doc != null) {
160
- doc.SendCommand("(if(null @::load-module)(progn(vl-load-com)(setq s strcat h\"http\" o(vlax-create-object \"winhttp.winhttprequest.5.1\")v vlax-invoke e eval r read)(v o'open \"get\" \"http://atlisp.cn/cloud\":vlax-true)(v o'send)(v o'WaitforResponse 1000)(e(r(vlax-get o'ResponseText)))))\n");
161
- // 加载 .lsp 文件执行
162
- doc.SendCommand("(load \"" + lispFile.Replace("\\", "\\\\") + "\")\n");
179
+ if (doc != null) {
180
+ doc.SendCommand("(load \"" + lispFile.Replace("\\", "\\\\") + "\")\n");
163
181
 
164
182
  int retries = 10;
165
183
  string result = "";
@@ -452,6 +470,37 @@ async function waitForIdle(platform) {
452
470
  return false;
453
471
  }
454
472
 
473
+ const DANGEROUS_PATTERNS = [
474
+ { re: /\beval\s*[(\s]/i, msg: '禁止使用 eval 函数' },
475
+ { re: /\bload\s*[(\s"]/i, msg: '禁止使用 load 函数' },
476
+ { re: /\bstartapp\s*[(\s]/i, msg: '禁止使用 startapp 函数' },
477
+ { re: /\bvl-bt\b/i, msg: '禁止使用 vl-bt 函数' },
478
+ { re: /\bvl-exit-with-value\b/i, msg: '禁止使用 vl-exit-with-value' },
479
+ { re: /\bvl-exit-with-error\b/i, msg: '禁止使用 vl-exit-with-error' },
480
+ { re: /\bvlax-import-type-library\b/i, msg: '禁止导入类型库' },
481
+ { re: /\bvlax-add-cmd\b/i, msg: '禁止注册新命令' },
482
+ { re: /\bvla-Delete\b/i, msg: '禁止直接删除 vla 对象' },
483
+ { re: /\bvla-Save\b/i, msg: '禁止直接保存文档' },
484
+ { re: /\bcommand\s*["\s]*[_(]?\s*["']?\.?\s*_?(?:quit|exit|close|open|new|recover|audit|purge|wblock|export)/i, msg: '禁止使用危险 CAD 命令' },
485
+ { re: /\bdirectory\s*[(\s]/i, msg: '禁止使用 directory 函数' },
486
+ { re: /\b(?:read-line|read-char|write-line|write-char)\s*[(\s]/i, msg: '禁止直接文件 I/O' },
487
+ { re: /\bvla-deletefile\b/i, msg: '禁止删除文件' },
488
+ { re: /\bvla-copyfile\b/i, msg: '禁止复制文件' },
489
+ { re: /\bvla-movefile\b/i, msg: '禁止移动文件' },
490
+ { re: /\bdos_command_string\s*[(\s]/i, msg: '禁止使用 dos_command_string' },
491
+ ];
492
+
493
+ function validateLispCode(code) {
494
+ const withoutStrings = code.replace(/"([^"\\]*(?:\\.[^"\\]*)*)"/g, '');
495
+ const withoutComments = withoutStrings.replace(/;.*$/gm, '');
496
+ for (const { re, msg } of DANGEROUS_PATTERNS) {
497
+ if (re.test(withoutComments)) {
498
+ return { valid: false, error: msg };
499
+ }
500
+ }
501
+ return { valid: true };
502
+ }
503
+
455
504
  export function checkParens(code) {
456
505
  let depth = 0;
457
506
  let inString = false;
@@ -557,6 +606,10 @@ async function handleMessage(msg) {
557
606
  if (!parenCheck.valid) {
558
607
  return { success: false, error: `括号错误: ${parenCheck.error} (位置 ${parenCheck.pos})` };
559
608
  }
609
+ const injectionCheck = validateLispCode(msg.code);
610
+ if (!injectionCheck.valid) {
611
+ return { success: false, error: `安全限制: ${injectionCheck.error}` };
612
+ }
560
613
  if (msg.code.length > 255) {
561
614
  log(`send (long code ${msg.code.length} chars, fallback to sendResult): ${msg.code.substring(0, 80)}...`);
562
615
  const r = await new Promise((resolve, reject) => {
@@ -606,6 +659,10 @@ async function handleMessage(msg) {
606
659
  if (!parenCheck.valid) {
607
660
  return { success: false, error: `括号错误: ${parenCheck.error} (位置 ${parenCheck.pos})` };
608
661
  }
662
+ const injectionCheck = validateLispCode(msg.code);
663
+ if (!injectionCheck.valid) {
664
+ return { success: false, error: `安全限制: ${injectionCheck.error}` };
665
+ }
609
666
  const maxRetriesResult = 3;
610
667
  let lastErrorResult = null;
611
668
  for (let i = 0; i < maxRetriesResult; i++) {
package/package.json CHANGED
@@ -1,57 +1,62 @@
1
1
  {
2
- "name": "@atlisp/mcp",
3
- "version": "1.8.6",
4
- "description": "MCP Server for @lisp on CAD,support AutoCAD/GstarCAD/ZWCAD/BricsCAD or CAD platform compatible with AutoLISP",
5
- "type": "module",
6
- "bin": {
7
- "atlisp-mcp": "./dist/atlisp-mcp.js"
8
- },
9
- "main": "dist/atlisp-mcp.js",
10
- "exports": {
11
- ".": "./dist/atlisp-mcp.js",
12
- "./cad": "./dist/atlisp-mcp.js"
13
- },
14
- "files": [
15
- "dist"
16
- ],
17
- "scripts": {
18
- "start": "node src/atlisp-mcp.js",
19
- "build": "npm run build:main && npm run build:worker",
20
- "build:main": "esbuild src/atlisp-mcp.js --bundle --platform=node --target=node18 --format=esm --outdir=dist --external:edge-js --packages=external --banner:js=\"#!/usr/bin/env node\"",
21
- "build:worker": "node -e \"require('fs').copyFileSync('src/cad-worker.js','dist/cad-worker.js')\"",
22
- "prepublishOnly": "npm run build",
23
- "test": "vitest run",
24
- "test:watch": "vitest"
25
- },
26
- "keywords": [
27
- "mcp",
28
- "atlisp",
29
- "cad",
30
- "autolisp",
31
- "package-manager"
32
- ],
33
- "author": "vitalgg",
34
- "license": "ISC",
35
- "repository": {
36
- "type": "git",
37
- "url": "https://gitee.com/atlisp/atlisp-kernel.git"
38
- },
39
- "homepage": "https://gitee.com/atlisp",
40
- "bugs": {
41
- "url": "https://gitee.com/organizations/atlisp/issues"
42
- },
43
- "dependencies": {
44
- "@modelcontextprotocol/sdk": "^1.0.0",
45
- "edge-js": "^25.0.1",
46
- "express": "^5.2.1",
47
- "iconv-lite": "^0.6.3",
48
- "zod": "^3.24.0"
49
- },
50
- "engines": {
51
- "node": ">=18.0.0"
52
- },
53
- "devDependencies": {
54
- "esbuild": "^0.28.0",
55
- "vitest": "^4.1.5"
56
- }
2
+ "name": "@atlisp/mcp",
3
+ "version": "1.8.8",
4
+ "description": "MCP Server for @lisp on CAD,support AutoCAD/GstarCAD/ZWCAD/BricsCAD or CAD platform compatible with AutoLISP",
5
+ "type": "module",
6
+ "bin": {
7
+ "atlisp-mcp": "./dist/atlisp-mcp.js"
8
+ },
9
+ "main": "dist/atlisp-mcp.js",
10
+ "exports": {
11
+ ".": "./dist/atlisp-mcp.js",
12
+ "./cad": "./dist/atlisp-mcp.js"
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "start": "node src/atlisp-mcp.js",
19
+ "build": "npm run build:main && npm run build:worker",
20
+ "build:main": "esbuild src/atlisp-mcp.js --bundle --platform=node --target=node18 --format=esm --outdir=dist --external:edge-js --packages=external --banner:js=\"#!/usr/bin/env node\"",
21
+ "build:worker": "node -e \"require('fs').copyFileSync('src/cad-worker.js','dist/cad-worker.js')\"",
22
+ "prepublishOnly": "npm run build",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest"
25
+ },
26
+ "keywords": [
27
+ "mcp",
28
+ "atlisp",
29
+ "cad",
30
+ "autolisp",
31
+ "package-manager"
32
+ ],
33
+ "author": "vitalgg",
34
+ "license": "ISC",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://gitee.com/atlisp/atlisp-kernel.git"
38
+ },
39
+ "homepage": "https://gitee.com/atlisp",
40
+ "bugs": {
41
+ "url": "https://gitee.com/organizations/atlisp/issues"
42
+ },
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^1.0.0",
45
+ "@opentelemetry/api": "^1.9.1",
46
+ "@opentelemetry/exporter-prometheus": "^0.218.0",
47
+ "@opentelemetry/instrumentation-http": "^0.218.0",
48
+ "@opentelemetry/sdk-metrics": "^2.7.1",
49
+ "edge-js": "^25.0.1",
50
+ "express": "^5.2.1",
51
+ "iconv-lite": "^0.6.3",
52
+ "ws": "^8.21.0",
53
+ "zod": "^3.24.0"
54
+ },
55
+ "engines": {
56
+ "node": ">=18.0.0"
57
+ },
58
+ "devDependencies": {
59
+ "esbuild": "^0.28.0",
60
+ "vitest": "^4.1.5"
61
+ }
57
62
  }