@atlisp/mcp 1.8.20 → 1.8.21

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.
@@ -64,6 +64,9 @@ function parseArgs() {
64
64
  i++;
65
65
  } else if (args[i] === "--no-lint" || args[i] === "--lint-off") {
66
66
  result.noLint = true;
67
+ } else if (args[i] === "--security-level" && args[i + 1]) {
68
+ result.securityLevel = args[i + 1];
69
+ i++;
67
70
  }
68
71
  }
69
72
  return result;
@@ -101,6 +104,9 @@ function reloadConfig() {
101
104
  configReloadLock = false;
102
105
  return false;
103
106
  }
107
+ function onConfigChange(callback) {
108
+ configWatchers.push(callback);
109
+ }
104
110
  var envSchema, configSchema, cliArgs, envResult, env, configFilePath, fileConfig, config, configWatchers, configReloadLock, config_default;
105
111
  var init_config = __esm({
106
112
  "src/config.js"() {
@@ -138,7 +144,8 @@ var init_config = __esm({
138
144
  LINT_PRESET: z.string().optional().default(""),
139
145
  LINT_CONFIG_PATH: z.string().optional().default(""),
140
146
  LINT_LEVEL: z.string().optional().default(""),
141
- NO_LINT: z.string().optional().default("")
147
+ NO_LINT: z.string().optional().default(""),
148
+ SECURITY_LEVEL: z.string().optional().default("strict")
142
149
  });
143
150
  configSchema = z.object({
144
151
  port: z.number().optional(),
@@ -180,7 +187,8 @@ var init_config = __esm({
180
187
  lintPreset: z.string().optional(),
181
188
  lintConfigPath: z.string().optional(),
182
189
  lintLevel: z.string().optional(),
183
- noLint: z.boolean().optional()
190
+ noLint: z.boolean().optional(),
191
+ securityLevel: z.enum(["strict", "standard", "relaxed"]).optional()
184
192
  });
185
193
  cliArgs = parseArgs();
186
194
  envResult = envSchema.safeParse(process.env);
@@ -227,7 +235,8 @@ var init_config = __esm({
227
235
  lintPreset: fileConfig?.lintPreset || cliArgs.lintPreset || env.LINT_PRESET || void 0,
228
236
  lintConfigPath: fileConfig?.lintConfigPath || cliArgs.lintConfigPath || env.LINT_CONFIG_PATH || void 0,
229
237
  lintLevel: fileConfig?.lintLevel || cliArgs.lintLevel || env.LINT_LEVEL || void 0,
230
- noLint: fileConfig?.noLint ?? cliArgs.noLint ?? env.NO_LINT === "true"
238
+ noLint: fileConfig?.noLint ?? cliArgs.noLint ?? env.NO_LINT === "true",
239
+ securityLevel: fileConfig?.securityLevel ?? cliArgs.securityLevel ?? (env.SECURITY_LEVEL || "strict")
231
240
  };
232
241
  if (config.lintConfigPath) process.env.LINT_CONFIG_PATH = config.lintConfigPath;
233
242
  if (config.lintPreset) process.env.LINT_PRESET = config.lintPreset;
@@ -8160,17 +8169,27 @@ function analyzeLispCode(code, options = {}) {
8160
8169
  if (process.env.NO_LINT === "true" && !options.force) {
8161
8170
  return { valid: true, errors: [], warnings: [], issues: [], summary: "lint \u5DF2\u8DF3\u8FC7" };
8162
8171
  }
8172
+ const securityLevel = config_default?.securityLevel || "strict";
8173
+ if (securityLevel === "relaxed" && !options.force) {
8174
+ return { valid: true, errors: [], warnings: [], issues: [], summary: "\u5B89\u5168\u9650\u5236\u5DF2\u5173\u95ED (relaxed)" };
8175
+ }
8163
8176
  try {
8164
- const config2 = options.lintConfig ? mergeConfig(getDefaultConfig(), options.lintConfig) : getDefaultConfig();
8165
- const lintIssues = runChecks(code, options.file || "<eval>", config2);
8177
+ const lintConfig = options.lintConfig ? mergeConfig(getDefaultConfig(), options.lintConfig) : getDefaultConfig();
8178
+ if (securityLevel === "standard") {
8179
+ for (const key of Object.keys(REQUIRED_ERROR_CHECKS)) {
8180
+ lintConfig.checks[key] = "warn";
8181
+ }
8182
+ }
8183
+ const lintIssues = runChecks(code, options.file || "<eval>", lintConfig);
8166
8184
  for (const rule of CUSTOM_RULES) {
8185
+ const effectiveSeverity = securityLevel === "standard" ? "warn" : rule.severity;
8167
8186
  const match = code.match(rule.re);
8168
8187
  if (match) {
8169
8188
  const line = code.slice(0, match.index).split("\n").length;
8170
8189
  lintIssues.push({
8171
8190
  file: options.file || "<eval>",
8172
8191
  line,
8173
- severity: rule.severity,
8192
+ severity: effectiveSeverity,
8174
8193
  rule: rule.rule,
8175
8194
  message: rule.msg
8176
8195
  });
@@ -8206,6 +8225,7 @@ function mergeConfig(base, overrides) {
8206
8225
  var CUSTOM_RULES, defaultConfig, REQUIRED_ERROR_CHECKS;
8207
8226
  var init_lint_analyzer = __esm({
8208
8227
  "src/lint-analyzer.js"() {
8228
+ init_config();
8209
8229
  CUSTOM_RULES = [
8210
8230
  { rule: "command_shell", severity: "error", re: /\(\s*command\s+"\s*[_.]*\s*shell\s*"/i, msg: '(command "shell" ...) \u4F1A\u542F\u52A8\u7CFB\u7EDF Shell' },
8211
8231
  { rule: "vl_registry_write", severity: "error", re: /\(\s*vl-registry-write[\s\)]/i, msg: "vl-registry-write \u4F1A\u4FEE\u6539 Windows \u6CE8\u518C\u8868" },
@@ -14712,22 +14732,65 @@ var init_trigger_engine = __esm({
14712
14732
  });
14713
14733
 
14714
14734
  // src/lisp-security.js
14735
+ function getLevel() {
14736
+ const level = config_default?.securityLevel || "strict";
14737
+ return CVT[level] || THREAT_BLOCK;
14738
+ }
14739
+ function resolveSeverity(rule) {
14740
+ if (_cachedLevel) return _cachedLevel;
14741
+ _cachedLevel = getLevel();
14742
+ return _cachedLevel;
14743
+ }
14744
+ function getEffectiveSeverity() {
14745
+ return resolveSeverity();
14746
+ }
14715
14747
  function stripStringsAndComments(code) {
14716
14748
  const withoutStrings = code.replace(/"([^"\\]*(?:\\.[^"\\]*)*)"/g, "");
14717
14749
  return withoutStrings.replace(/;.*$/gm, "");
14718
14750
  }
14719
- var THREAT_BLOCK, THREAT_WARN, SECURITY_RULES;
14751
+ var THREAT_BLOCK, THREAT_WARN, THREAT_ALLOW, CVT, SECURITY_RULES, _cachedLevel;
14720
14752
  var init_lisp_security = __esm({
14721
14753
  "src/lisp-security.js"() {
14722
14754
  init_logger();
14755
+ init_config();
14723
14756
  THREAT_BLOCK = "block";
14724
14757
  THREAT_WARN = "warn";
14758
+ THREAT_ALLOW = "allow";
14759
+ CVT = {
14760
+ strict: THREAT_BLOCK,
14761
+ standard: THREAT_WARN,
14762
+ relaxed: THREAT_ALLOW
14763
+ };
14725
14764
  SECURITY_RULES = [
14726
- { re: /\bstartapp\s*[(\s]/i, severity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 startapp \u51FD\u6570" },
14727
- { re: /\bvl-bt\b/i, severity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-bt \u51FD\u6570" },
14728
- { re: /\bvlax-import-type-library\b/i, severity: THREAT_BLOCK, msg: "\u7981\u6B62\u5BFC\u5165\u7C7B\u578B\u5E93" },
14729
- { re: /\bdos_command_string\s*[(\s]/i, severity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 dos_command_string" }
14765
+ { re: /\bstartapp\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 startapp \u51FD\u6570" },
14766
+ { re: /\bvl-bt\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-bt \u51FD\u6570" },
14767
+ { re: /\bvlax-import-type-library\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u5BFC\u5165\u7C7B\u578B\u5E93" },
14768
+ { re: /\bdos_command_string\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 dos_command_string" },
14769
+ { re: /\beval\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 eval \u51FD\u6570" },
14770
+ { re: /\bcommand\s*["\s]*[_(]?\s*["']?\.?\s*_?(?:shell|quit|exit|close|open|new|recover|audit|purge|wblock|export)/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528\u5371\u9669 CAD \u547D\u4EE4" },
14771
+ { re: /\bload\s*[(\s"]/i, baseSeverity: THREAT_BLOCK, msg: "load \u51FD\u6570\u53EF\u80FD\u52A0\u8F7D\u5916\u90E8\u4EE3\u7801" },
14772
+ { re: /\b(?:read-line|read-char|write-line|write-char)\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u6587\u4EF6 I/O \u64CD\u4F5C\u53EF\u80FD\u5F71\u54CD\u7CFB\u7EDF" },
14773
+ { re: /\bvl-file-copy\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u590D\u5236" },
14774
+ { re: /\bvl-file-delete\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u5220\u9664" },
14775
+ { re: /\bvl-file-rename\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6587\u4EF6\u91CD\u547D\u540D" },
14776
+ { re: /\bvl-registry-write[\s\)]/i, baseSeverity: THREAT_BLOCK, msg: "vl-registry-write \u4F1A\u4FEE\u6539\u6CE8\u518C\u8868" },
14777
+ { re: /\bvl-exit-with-value\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-exit-with-value" },
14778
+ { re: /\bvl-exit-with-error\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 vl-exit-with-error" },
14779
+ { re: /\bvlax-add-cmd\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u6CE8\u518C\u65B0\u547D\u4EE4" },
14780
+ { re: /\bvla-Delete\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u76F4\u63A5\u5220\u9664 vla \u5BF9\u8C61" },
14781
+ { re: /\bvla-Save\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u76F4\u63A5\u4FDD\u5B58\u6587\u6863" },
14782
+ { re: /\bdirectory\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u4F7F\u7528 directory \u51FD\u6570" },
14783
+ { re: /\bvla-deletefile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u5220\u9664\u6587\u4EF6" },
14784
+ { re: /\bvla-copyfile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u590D\u5236\u6587\u4EF6" },
14785
+ { re: /\bvla-movefile\b/i, baseSeverity: THREAT_BLOCK, msg: "\u7981\u6B62\u79FB\u52A8\u6587\u4EF6" }
14730
14786
  ];
14787
+ _cachedLevel = null;
14788
+ try {
14789
+ onConfigChange(() => {
14790
+ _cachedLevel = null;
14791
+ });
14792
+ } catch (_) {
14793
+ }
14731
14794
  }
14732
14795
  });
14733
14796
 
@@ -14747,21 +14810,23 @@ function clearSessionAllowlist(sessionId) {
14747
14810
  }
14748
14811
  function validateLispCode(code, sessionId = null) {
14749
14812
  const withoutComments = stripStringsAndComments(code);
14813
+ const effectiveSeverity = getEffectiveSeverity();
14750
14814
  const results = [];
14751
14815
  let blocked = false;
14752
14816
  for (let i = 0; i < RULES2.length; i++) {
14753
14817
  const rule = RULES2[i];
14754
14818
  if (rule.re.test(withoutComments)) {
14755
14819
  const isAllowed = sessionId && getSessionAllowlist(sessionId).has(i);
14820
+ const severity = effectiveSeverity === THREAT_ALLOW ? THREAT_ALLOW : isAllowed ? THREAT_ALLOW : effectiveSeverity;
14756
14821
  results.push({
14757
14822
  ruleIndex: i,
14758
- severity: rule.severity,
14823
+ severity,
14759
14824
  message: rule.msg,
14760
- blocked: !isAllowed && rule.severity === THREAT_BLOCK,
14761
- warned: !isAllowed && rule.severity === THREAT_WARN,
14762
- allowed: isAllowed
14825
+ blocked: severity === THREAT_BLOCK,
14826
+ warned: severity === THREAT_WARN,
14827
+ allowed: severity === THREAT_ALLOW
14763
14828
  });
14764
- if (!isAllowed && rule.severity === THREAT_BLOCK) {
14829
+ if (severity === THREAT_BLOCK) {
14765
14830
  blocked = true;
14766
14831
  }
14767
14832
  }
@@ -14777,6 +14842,7 @@ var RULES2, sessionAllowlist;
14777
14842
  var init_lisp_sandbox = __esm({
14778
14843
  "src/lisp-sandbox.js"() {
14779
14844
  init_logger();
14845
+ init_config();
14780
14846
  init_lisp_security();
14781
14847
  RULES2 = SECURITY_RULES;
14782
14848
  sessionAllowlist = /* @__PURE__ */ new Map();
@@ -18883,51 +18949,65 @@ if (isMcpScript) {
18883
18949
  Usage: atlisp-mcp [options]
18884
18950
 
18885
18951
  Options:
18886
- -v, --version Show version number
18887
- -h, --help Show this help message
18888
- --transport <type> Transport: http, stdio, or ws (default: http)
18889
- --port <port> HTTP port (default: 8110)
18890
- --host <host> HTTP host (default: 0.0.0.0)
18891
- --stdio Shorthand for --transport stdio
18892
- --ws Shorthand for --transport ws
18893
- --config <path> Path to config file (default: ~/.atlisp/atlisp.config.json)
18894
- --ssl-key <path> SSL key file path (enables HTTPS)
18895
- --ssl-cert <path> SSL certificate file path
18896
- --debug Enable debug mode
18897
- --lint-preset <type> Lint preset: recommended, strict, or relaxed
18898
- --lint-config <path> Path to atlisp-lint.json config file
18899
- --lint-level <level> Lint level: error, warn, or off
18900
- --no-lint Disable lint pre-check for eval_lisp
18901
- --lint-off Same as --no-lint
18952
+ -v, --version Show version number
18953
+ -h, --help Show this help message
18954
+ --transport <type> Transport: http, stdio, ws (default: http)
18955
+ --stdio Shorthand for --transport stdio
18956
+ --ws Shorthand for --transport ws
18957
+ --port <port> HTTP port (default: 8110)
18958
+ --host <host> HTTP host (default: 0.0.0.0)
18959
+ --config <path> Path to config file (default: ~/.atlisp/atlisp.config.json)
18960
+ --ssl-key <path> SSL key file path (enables HTTPS)
18961
+ --ssl-cert <path> SSL certificate file path
18962
+ --debug Enable debug mode
18963
+ --metrics-enabled Enable /metrics endpoint (true/false)
18964
+ --lint-preset <type> Lint preset: recommended, strict, relaxed
18965
+ --lint-config <path> Path to atlisp-lint.json config file
18966
+ --lint-level <level> Lint level: error, warn, off
18967
+ --no-lint Disable lint pre-check for eval_lisp
18968
+ --lint-off Same as --no-lint
18969
+ --api-key <key> API key for authentication
18970
+ --security-level <type> Security level: strict, standard, relaxed (default: strict)
18902
18971
 
18903
18972
  Environment Variables:
18904
- TRANSPORT Transport mode (http/stdio/ws)
18905
- PORT HTTP port
18906
- HOST HTTP host
18907
- MCP_API_KEY API key for authentication
18908
- ENABLE_SSE Enable SSE (true/false)
18909
- DEBUG Enable debug logging (true/false)
18910
- METRICS_ENABLED Enable /metrics endpoint (true/false)
18911
- SSL_KEY_PATH SSL key file path
18912
- SSL_CERT_PATH SSL certificate file path
18973
+ TRANSPORT Transport mode (http/stdio/ws)
18974
+ PORT HTTP port (default: 8110)
18975
+ HOST HTTP host (default: 0.0.0.0)
18976
+ MCP_API_KEY API key for authentication
18977
+ ENABLE_SSE Enable SSE (true/false, default: true)
18978
+ DEBUG Enable debug logging (true/false)
18979
+ DEBUG_FILE Debug log file path
18980
+ METRICS_ENABLED Enable /metrics endpoint (true/false, default: true)
18981
+ SSL_KEY_PATH SSL key file path
18982
+ SSL_CERT_PATH SSL certificate file path
18983
+ SECURITY_LEVEL Security level: strict, standard, relaxed (default: strict)
18984
+ CONFIG_FILE Path to config file
18985
+ LINT_PRESET Lint preset (recommended/strict/relaxed)
18986
+ LINT_CONFIG_PATH Path to atlisp-lint.json
18987
+ LINT_LEVEL Lint level (error/warn/off)
18988
+ NO_LINT Disable lint (true/false)
18989
+ REQUEST_LOG_ENABLED Enable request logging (true/false, default: true)
18990
+ CORS_ORIGIN CORS origin (default: *)
18991
+ RATE_LIMIT_WINDOW Rate limit window in ms (default: 60000)
18992
+ RATE_LIMIT_MAX Max requests per window (default: 100)
18913
18993
 
18914
18994
  Examples:
18915
- atlisp-mcp Start HTTP server on port 8110
18916
- atlisp-mcp --port 3000 Start HTTP server on port 3000
18917
- atlisp-mcp --stdio Start in stdio mode for MCP clients
18918
- atlisp-mcp --config ~/my.json Use custom config file
18919
- atlisp-mcp --debug Enable debug output
18920
- TRANSPORT=stdio atlisp-mcp Via env var for stdio mode
18921
- atlisp-mcp --ws Start WebSocket server on port 8110
18922
- TRANSPORT=ws atlisp-mcp Via env var for WebSocket mode
18995
+ atlisp-mcp Start HTTP server on port 8110
18996
+ atlisp-mcp --port 3000 Start HTTP server on port 3000
18997
+ atlisp-mcp --stdio Start in stdio mode for MCP clients
18998
+ atlisp-mcp --config ~/my.json Use custom config file
18999
+ atlisp-mcp --debug Enable debug output
19000
+ atlisp-mcp --ssl-key key.pem --ssl-cert cert.pem Start HTTPS server
19001
+ TRANSPORT=stdio atlisp-mcp Via env var for stdio mode
19002
+ TRANSPORT=ws atlisp-mcp Via env var for WebSocket mode
18923
19003
 
18924
19004
  API Endpoints:
18925
- GET /health Health check
18926
- GET /metrics Prometheus metrics (if enabled)
18927
- POST /mcp MCP JSON-RPC endpoint
18928
- GET /sse Server-Sent Events endpoint
18929
- POST /message SSE message endpoint
18930
- WS /ws WebSocket endpoint
19005
+ GET /health Health check
19006
+ GET /metrics Prometheus metrics (if enabled)
19007
+ POST /mcp MCP JSON-RPC endpoint
19008
+ GET /sse Server-Sent Events endpoint
19009
+ POST /message SSE message endpoint
19010
+ WS /ws WebSocket endpoint
18931
19011
  POST /config/reload Reload configuration
18932
19012
 
18933
19013
  For more info: https://atlisp.cn
@@ -18955,6 +19035,16 @@ For more info: https://atlisp.cn
18955
19035
  process.env.LINT_LEVEL = cliArgs2[++i];
18956
19036
  } else if (arg === "--no-lint" || arg === "--lint-off") {
18957
19037
  process.env.NO_LINT = "true";
19038
+ } else if (arg === "--security-level" && cliArgs2[i + 1]) {
19039
+ process.env.SECURITY_LEVEL = cliArgs2[++i];
19040
+ } else if (arg === "--ssl-key" && cliArgs2[i + 1]) {
19041
+ process.env.SSL_KEY_PATH = cliArgs2[++i];
19042
+ } else if (arg === "--ssl-cert" && cliArgs2[i + 1]) {
19043
+ process.env.SSL_CERT_PATH = cliArgs2[++i];
19044
+ } else if (arg === "--api-key" && cliArgs2[i + 1]) {
19045
+ process.env.MCP_API_KEY = cliArgs2[++i];
19046
+ } else if (arg === "--metrics-enabled" && cliArgs2[i + 1]) {
19047
+ process.env.METRICS_ENABLED = cliArgs2[++i];
18958
19048
  }
18959
19049
  }
18960
19050
  }
@@ -579,9 +579,12 @@ async function handleMessage(msg) {
579
579
  if (!parenCheck.valid) {
580
580
  return { success: false, error: `括号错误: ${parenCheck.error} (位置 ${parenCheck.pos})` };
581
581
  }
582
- const injectionCheck = validateLispCodeWorker(msg.code);
583
- if (!injectionCheck.valid) {
584
- return { success: false, error: `安全限制: ${injectionCheck.error}` };
582
+ const securityLevel = process.env.SECURITY_LEVEL || 'strict';
583
+ if (securityLevel !== 'relaxed') {
584
+ const injectionCheck = validateLispCodeWorker(msg.code);
585
+ if (!injectionCheck.valid) {
586
+ return { success: false, error: `安全限制: ${injectionCheck.error}` };
587
+ }
585
588
  }
586
589
  if (msg.code.length > 255) {
587
590
  const cacheEnabled = process.env.LONG_CODE_CACHE === '1';
package/dist/config.js CHANGED
@@ -38,6 +38,7 @@ const envSchema = z.object({
38
38
  LINT_CONFIG_PATH: z.string().optional().default(''),
39
39
  LINT_LEVEL: z.string().optional().default(''),
40
40
  NO_LINT: z.string().optional().default(''),
41
+ SECURITY_LEVEL: z.string().optional().default('strict'),
41
42
  });
42
43
 
43
44
  const configSchema = z.object({
@@ -81,6 +82,7 @@ const configSchema = z.object({
81
82
  lintConfigPath: z.string().optional(),
82
83
  lintLevel: z.string().optional(),
83
84
  noLint: z.boolean().optional(),
85
+ securityLevel: z.enum(['strict', 'standard', 'relaxed']).optional(),
84
86
  });
85
87
 
86
88
  function parseArgs() {
@@ -122,6 +124,9 @@ function parseArgs() {
122
124
  i++;
123
125
  } else if (args[i] === '--no-lint' || args[i] === '--lint-off') {
124
126
  result.noLint = true;
127
+ } else if (args[i] === '--security-level' && args[i + 1]) {
128
+ result.securityLevel = args[i + 1];
129
+ i++;
125
130
  }
126
131
  }
127
132
  return result;
@@ -197,6 +202,7 @@ const config = {
197
202
  lintConfigPath: fileConfig?.lintConfigPath || cliArgs.lintConfigPath || env.LINT_CONFIG_PATH || undefined,
198
203
  lintLevel: fileConfig?.lintLevel || cliArgs.lintLevel || env.LINT_LEVEL || undefined,
199
204
  noLint: fileConfig?.noLint ?? cliArgs.noLint ?? (env.NO_LINT === 'true'),
205
+ securityLevel: fileConfig?.securityLevel ?? cliArgs.securityLevel ?? (env.SECURITY_LEVEL || 'strict'),
200
206
  };
201
207
 
202
208
  if (config.lintConfigPath) process.env.LINT_CONFIG_PATH = config.lintConfigPath;
@@ -1,36 +1,90 @@
1
1
  import { log, error as logError } from './logger.js';
2
+ import config, { onConfigChange } from './config.js';
2
3
 
3
4
  export const THREAT_BLOCK = 'block';
4
5
  export const THREAT_WARN = 'warn';
5
6
  export const THREAT_ALLOW = 'allow';
6
7
 
8
+ export const SECURITY_LEVELS = ['strict', 'standard', 'relaxed'];
9
+
10
+ const CVT = {
11
+ strict: THREAT_BLOCK,
12
+ standard: THREAT_WARN,
13
+ relaxed: THREAT_ALLOW,
14
+ };
15
+
16
+ function getLevel() {
17
+ const level = config?.securityLevel || 'strict';
18
+ return CVT[level] || THREAT_BLOCK;
19
+ }
20
+
7
21
  export const SECURITY_RULES = [
8
- { re: /\bstartapp\s*[(\s]/i, severity: THREAT_BLOCK, msg: '禁止使用 startapp 函数' },
9
- { re: /\bvl-bt\b/i, severity: THREAT_BLOCK, msg: '禁止使用 vl-bt 函数' },
10
- { re: /\bvlax-import-type-library\b/i, severity: THREAT_BLOCK, msg: '禁止导入类型库' },
11
- { re: /\bdos_command_string\s*[(\s]/i, severity: THREAT_BLOCK, msg: '禁止使用 dos_command_string' },
22
+ { re: /\bstartapp\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: '禁止使用 startapp 函数' },
23
+ { re: /\bvl-bt\b/i, baseSeverity: THREAT_BLOCK, msg: '禁止使用 vl-bt 函数' },
24
+ { re: /\bvlax-import-type-library\b/i, baseSeverity: THREAT_BLOCK, msg: '禁止导入类型库' },
25
+ { re: /\bdos_command_string\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: '禁止使用 dos_command_string' },
26
+ { re: /\beval\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: '禁止使用 eval 函数' },
27
+ { re: /\bcommand\s*["\s]*[_(]?\s*["']?\.?\s*_?(?:shell|quit|exit|close|open|new|recover|audit|purge|wblock|export)/i, baseSeverity: THREAT_BLOCK, msg: '禁止使用危险 CAD 命令' },
28
+ { re: /\bload\s*[(\s"]/i, baseSeverity: THREAT_BLOCK, msg: 'load 函数可能加载外部代码' },
29
+ { re: /\b(?:read-line|read-char|write-line|write-char)\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: '文件 I/O 操作可能影响系统' },
30
+ { re: /\bvl-file-copy\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: '禁止文件复制' },
31
+ { re: /\bvl-file-delete\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: '禁止文件删除' },
32
+ { re: /\bvl-file-rename\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: '禁止文件重命名' },
33
+ { re: /\bvl-registry-write[\s\)]/i, baseSeverity: THREAT_BLOCK, msg: 'vl-registry-write 会修改注册表' },
34
+ { re: /\bvl-exit-with-value\b/i, baseSeverity: THREAT_BLOCK, msg: '禁止使用 vl-exit-with-value' },
35
+ { re: /\bvl-exit-with-error\b/i, baseSeverity: THREAT_BLOCK, msg: '禁止使用 vl-exit-with-error' },
36
+ { re: /\bvlax-add-cmd\b/i, baseSeverity: THREAT_BLOCK, msg: '禁止注册新命令' },
37
+ { re: /\bvla-Delete\b/i, baseSeverity: THREAT_BLOCK, msg: '禁止直接删除 vla 对象' },
38
+ { re: /\bvla-Save\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: '禁止直接保存文档' },
39
+ { re: /\bdirectory\s*[(\s]/i, baseSeverity: THREAT_BLOCK, msg: '禁止使用 directory 函数' },
40
+ { re: /\bvla-deletefile\b/i, baseSeverity: THREAT_BLOCK, msg: '禁止删除文件' },
41
+ { re: /\bvla-copyfile\b/i, baseSeverity: THREAT_BLOCK, msg: '禁止复制文件' },
42
+ { re: /\bvla-movefile\b/i, baseSeverity: THREAT_BLOCK, msg: '禁止移动文件' },
12
43
  ];
13
44
 
14
- export function stripStringsAndComments(code) {
15
- const withoutStrings = code.replace(/"([^"\\]*(?:\\.[^"\\]*)*)"/g, '');
16
- return withoutStrings.replace(/;.*$/gm, '');
45
+ let _cachedLevel = null;
46
+
47
+ export function refreshConfig() {
48
+ _cachedLevel = null;
49
+ }
50
+
51
+ try {
52
+ onConfigChange(() => { _cachedLevel = null; });
53
+ } catch (_) {}
54
+
55
+ function resolveSeverity(rule) {
56
+ if (_cachedLevel) return _cachedLevel;
57
+ _cachedLevel = getLevel();
58
+ return _cachedLevel;
17
59
  }
18
60
 
19
61
  export function validateLispCodeWorker(code) {
20
62
  const clean = stripStringsAndComments(code);
21
- for (const { re, severity, msg } of SECURITY_RULES) {
63
+ const effectiveSeverity = resolveSeverity();
64
+
65
+ for (const rule of SECURITY_RULES) {
66
+ const re = rule.re;
22
67
  if (re.test(clean)) {
23
- if (severity === THREAT_BLOCK) {
24
- return { valid: false, error: msg };
68
+ if (effectiveSeverity === THREAT_BLOCK) {
69
+ return { valid: false, error: rule.msg };
25
70
  }
26
- if (severity === THREAT_WARN) {
27
- log('WARN: ' + msg);
71
+ if (effectiveSeverity === THREAT_WARN) {
72
+ log('WARN: ' + rule.msg);
28
73
  }
29
74
  }
30
75
  }
31
76
  return { valid: true };
32
77
  }
33
78
 
79
+ export function getEffectiveSeverity() {
80
+ return resolveSeverity();
81
+ }
82
+
83
+ export function stripStringsAndComments(code) {
84
+ const withoutStrings = code.replace(/"([^"\\]*(?:\\.[^"\\]*)*)"/g, '');
85
+ return withoutStrings.replace(/;.*$/gm, '');
86
+ }
87
+
34
88
  export function checkParens(code) {
35
89
  let depth = 0;
36
90
  let inString = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlisp/mcp",
3
- "version": "1.8.20",
3
+ "version": "1.8.21",
4
4
  "description": "MCP Server for @lisp on CAD,support AutoCAD/GstarCAD/ZWCAD/BricsCAD or CAD platform compatible with AutoLISP",
5
5
  "type": "module",
6
6
  "bin": {