@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.
- package/dist/atlisp-mcp.js +145 -55
- package/dist/cad-worker.js +6 -3
- package/dist/config.js +6 -0
- package/dist/lisp-security.js +66 -12
- package/package.json +1 -1
package/dist/atlisp-mcp.js
CHANGED
|
@@ -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
|
|
8165
|
-
|
|
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:
|
|
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,
|
|
14727
|
-
{ re: /\bvl-bt\b/i,
|
|
14728
|
-
{ re: /\bvlax-import-type-library\b/i,
|
|
14729
|
-
{ re: /\bdos_command_string\s*[(\s]/i,
|
|
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
|
|
14823
|
+
severity,
|
|
14759
14824
|
message: rule.msg,
|
|
14760
|
-
blocked:
|
|
14761
|
-
warned:
|
|
14762
|
-
allowed:
|
|
14825
|
+
blocked: severity === THREAT_BLOCK,
|
|
14826
|
+
warned: severity === THREAT_WARN,
|
|
14827
|
+
allowed: severity === THREAT_ALLOW
|
|
14763
14828
|
});
|
|
14764
|
-
if (
|
|
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
|
|
18887
|
-
-h, --help
|
|
18888
|
-
|
|
18889
|
-
--
|
|
18890
|
-
--
|
|
18891
|
-
|
|
18892
|
-
|
|
18893
|
-
|
|
18894
|
-
|
|
18895
|
-
|
|
18896
|
-
|
|
18897
|
-
|
|
18898
|
-
|
|
18899
|
-
|
|
18900
|
-
|
|
18901
|
-
|
|
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
|
-
|
|
18905
|
-
|
|
18906
|
-
|
|
18907
|
-
|
|
18908
|
-
|
|
18909
|
-
|
|
18910
|
-
|
|
18911
|
-
|
|
18912
|
-
|
|
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
|
|
18916
|
-
atlisp-mcp --port 3000
|
|
18917
|
-
atlisp-mcp --stdio
|
|
18918
|
-
atlisp-mcp --config ~/my.json
|
|
18919
|
-
atlisp-mcp --debug
|
|
18920
|
-
|
|
18921
|
-
atlisp-mcp
|
|
18922
|
-
TRANSPORT=ws atlisp-mcp
|
|
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
|
|
18926
|
-
GET /metrics
|
|
18927
|
-
POST /mcp
|
|
18928
|
-
GET /sse
|
|
18929
|
-
POST /message
|
|
18930
|
-
WS /ws
|
|
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
|
}
|
package/dist/cad-worker.js
CHANGED
|
@@ -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
|
|
583
|
-
if (
|
|
584
|
-
|
|
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;
|
package/dist/lisp-security.js
CHANGED
|
@@ -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,
|
|
9
|
-
{ re: /\bvl-bt\b/i,
|
|
10
|
-
{ re: /\bvlax-import-type-library\b/i,
|
|
11
|
-
{ re: /\bdos_command_string\s*[(\s]/i,
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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 (
|
|
24
|
-
return { valid: false, error: msg };
|
|
68
|
+
if (effectiveSeverity === THREAT_BLOCK) {
|
|
69
|
+
return { valid: false, error: rule.msg };
|
|
25
70
|
}
|
|
26
|
-
if (
|
|
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;
|