UNPKG

mlld

Version:

mlld: llm scripting language

454 lines (451 loc) 14.6 kB
import { isFileLoadedValue } from './chunk-GLVJZ2CA.mjs'; import { isVariable } from './chunk-DUS6QZAC.mjs'; import { isStructuredValue, asData } from './chunk-RKGZ44GZ.mjs'; import { __name } from './chunk-NJQT543K.mjs'; import * as shellQuote from 'shell-quote'; var _CommandUtils = class _CommandUtils { /** * Validate and parse command for security * Blocks dangerous shell operators that could be used maliciously * Uses shell-quote library for accurate operator detection */ static validateAndParseCommand(command) { const parsed = shellQuote.parse(command); const bannedOperators = /* @__PURE__ */ new Set([ "&&", "||", ";", // '>', // Output redirect - Allowed: just writes to local files // '>>', // Append redirect - Allowed: just appends to local files // '<', // Input redirect - Allowed: just reads from local files "&" // Background execution - Still dangerous (zombie processes) ]); for (const token of parsed) { if (typeof token === "object" && "op" in token) { const operator = token.op; if (bannedOperators.has(operator)) { const operatorDescriptions = { "&&": "AND operator (&&)", "||": "OR operator (||)", ";": "semicolon (;)", "&": "background execution (&)" }; const description = operatorDescriptions[operator] || `operator (${operator})`; const errorMessage = [ `Shell ${description} is not allowed in /run commands`, "", "Command rejected:", ` ${command}`, "", `The ${description} character is not allowed in the /run context.`, "Use a full shell command with /run sh { ... } for less restrictive execution,", "or split into separate /run commands." ].join("\n"); throw new Error(errorMessage); } } } return command; } /** * Enhance shell code for command substitution * Adds stderr capture and output normalization for problematic patterns */ static enhanceShellCodeForCommandSubstitution(code) { const commandSubstitutionPattern = /\$\(([^)]*)\)/g; const enhancedCode = code.replace(commandSubstitutionPattern, (match, innerCommand) => { if (typeof innerCommand !== "string") { return match; } const interactivePatterns = [ /if\s*\[\s*-t\s+[01]\s*\]/, /echo\s+.*\s+>&2/, /\|\|\s*echo/, /python3?\s+-c/, /node\s+-e/, /sh\s+-c\s+.*>&2/, /echo.*&&.*echo.*>&2/ ]; const needsStderrCapture = interactivePatterns.some((pattern) => pattern.test(innerCommand)); const hasStderrRedirection = innerCommand.includes("2>&1") || innerCommand.includes("2>/dev/null") || innerCommand.includes("2>"); const hasTrailingStderrRedirection = /\s+2>&1\s*$/.test(innerCommand); if (needsStderrCapture && !hasStderrRedirection) { return `$(${innerCommand.trim()} 2>&1 | tr '\\n' ' ' | sed 's/[[:space:]]*$//')`; } else if (hasStderrRedirection && (needsStderrCapture || hasTrailingStderrRedirection)) { const cleanCommand = innerCommand.replace(/\s+2>&1\s*$/, "").trim(); return `$(${cleanCommand} 2>&1 | tr '\\n' ' ' | sed 's/[[:space:]]*$//')`; } else if (innerCommand.includes("&&") || innerCommand.includes("||")) { return `$({ ${innerCommand.trim()}; } | tr '\\n' ' ' | sed 's/[[:space:]]*$//')`; } return match; }); const hasDirectStderrPattern = /echo\s+.*\s+>&2/; if (hasDirectStderrPattern.test(code) && !code.includes("2>&1")) ; return enhancedCode; } /** * Check if a command appears to be safe for execution * This is a basic heuristic check, not a comprehensive security analysis */ static isSafeCommand(command) { try { _CommandUtils.validateAndParseCommand(command); return true; } catch { return false; } } /** * Extract command name from a command string * Returns the first word (command) from the command string */ static extractCommandName(command) { const trimmed = command.trim(); const firstSpace = trimmed.indexOf(" "); return firstSpace === -1 ? trimmed : trimmed.substring(0, firstSpace); } /** * Check if command requires shell enhancement */ static requiresShellEnhancement(command) { const stderrProducers = [ /curl\s+/, /wget\s+/, /git\s+/, /npm\s+/, /yarn\s+/, /pip\s+/, /docker\s+/, /kubectl\s+/, /python\s+[^|&;]+\.py/, /node\s+[^|&;]+\.js/ ]; return stderrProducers.some((pattern) => pattern.test(command)); } /** * Check if command contains an actual pipe operator (quote-aware) * Uses shell-quote to properly detect pipes vs pipe characters in strings */ static hasPipeOperator(command) { const parsed = shellQuote.parse(command); return parsed.some((token) => typeof token === "object" && "op" in token && token.op === "|"); } }; __name(_CommandUtils, "CommandUtils"); var CommandUtils = _CommandUtils; // interpreter/env/variable-proxy.ts function cloneValue(input) { if (input === void 0) { return void 0; } if (typeof structuredClone === "function") { try { return structuredClone(input); } catch { } } try { return JSON.parse(JSON.stringify(input)); } catch { return input; } } __name(cloneValue, "cloneValue"); var VARIABLE_PROXY_PROPS = { TYPE: "__mlld_type", SUBTYPE: "__mlld_subtype", METADATA: "__mlld_metadata", VARIABLE: "__mlld_variable", IS_VARIABLE: "__mlld_is_variable" }; function createVariableProxy(variable) { const value = variable.value; if (value === null || typeof value !== "object") { return value; } return new Proxy(value, { get(target, prop, receiver) { switch (prop) { case VARIABLE_PROXY_PROPS.TYPE: return variable.type; case VARIABLE_PROXY_PROPS.SUBTYPE: return variable.subtype; case VARIABLE_PROXY_PROPS.METADATA: return { mx: variable.mx, internal: variable.internal }; case VARIABLE_PROXY_PROPS.VARIABLE: return variable; case VARIABLE_PROXY_PROPS.IS_VARIABLE: return true; // Special handling for toString to preserve custom behavior case "toString": const customToString = variable.internal?.customToString; if (customToString) { return customToString.bind(target); } return Reflect.get(target, prop, receiver); // Special handling for toJSON case "toJSON": const customToJSON = variable.internal?.customToJSON; if (customToJSON) { return customToJSON; } return Reflect.get(target, prop, receiver); default: return Reflect.get(target, prop, receiver); } }, // Preserve normal array/object behavior for other operations set(target, prop, value2, receiver) { return Reflect.set(target, prop, value2, receiver); }, has(target, prop) { if (Object.values(VARIABLE_PROXY_PROPS).includes(prop)) { return true; } return Reflect.has(target, prop); }, ownKeys(target) { return Reflect.ownKeys(target); }, getOwnPropertyDescriptor(target, prop) { if (Object.values(VARIABLE_PROXY_PROPS).includes(prop)) { return { configurable: true, enumerable: false, get: /* @__PURE__ */ __name(() => this.get(target, prop, target), "get") }; } return Reflect.getOwnPropertyDescriptor(target, prop); } }); } __name(createVariableProxy, "createVariableProxy"); function recordPrimitiveMetadata(target, key, metadata) { if (!target.__mlldPrimitiveMetadata) { Object.defineProperty(target, "__mlldPrimitiveMetadata", { value: {}, enumerable: false, configurable: true }); } target.__mlldPrimitiveMetadata[key] = metadata; } __name(recordPrimitiveMetadata, "recordPrimitiveMetadata"); function prepareValueForShadow(value, key, target) { if (isVariable(value)) { if (value.type === "primitive" || value.type === "simple-text" || value.type === "interpolated-text") { if (target && key) { recordPrimitiveMetadata(target, key, { isVariable: true, type: value.type, subtype: value.primitiveType, mx: value.mx, internal: value.internal }); } return value.value; } return createVariableProxy(value); } if (isFileLoadedValue(value)) { if (isStructuredValue(value)) { if (target && key) { recordPrimitiveMetadata(target, key, { isVariable: false, type: "load-content", mx: value.mx, internal: value.internal, text: value.text }); } return value.text; } else { if (target && key) { recordPrimitiveMetadata(target, key, { isVariable: false, type: "load-content", mx: void 0, internal: void 0 }); } return value.content; } } if (isStructuredValue(value)) { if (value.type === "array") { const isLoadContentArray = value.mx?.source === "load-content" || value.metadata?.source === "load-content"; if (isLoadContentArray) { if (target && key) { recordPrimitiveMetadata(target, key, { isVariable: false, type: value.type, mx: value.mx, internal: value.internal, text: value.text }); } return value.text; } } const data = asData(value); if (target && key) { recordPrimitiveMetadata(target, key, { isVariable: false, type: value.type, mx: value.mx, internal: value.internal, text: value.text }); } return data; } return value; } __name(prepareValueForShadow, "prepareValueForShadow"); function prepareParamsForShadow(params) { const shadowParams = {}; for (const [key, value] of Object.entries(params)) { shadowParams[key] = prepareValueForShadow(value, key, shadowParams); } return shadowParams; } __name(prepareParamsForShadow, "prepareParamsForShadow"); function isVariableProxy(value) { if (value === null || typeof value !== "object") { return false; } try { return value[VARIABLE_PROXY_PROPS.IS_VARIABLE] === true; } catch { return false; } } __name(isVariableProxy, "isVariableProxy"); function getVariableType(value) { if (!isVariableProxy(value)) { return void 0; } try { return value[VARIABLE_PROXY_PROPS.TYPE]; } catch { return void 0; } } __name(getVariableType, "getVariableType"); function createMlldHelpers(primitiveMetadata) { const getProxyVariable = /* @__PURE__ */ __name((value) => { if (isVariableProxy(value)) { try { return value[VARIABLE_PROXY_PROPS.VARIABLE]; } catch { return void 0; } } return void 0; }, "getProxyVariable"); const helpers = { // Type checking - also check primitive metadata isVariable: /* @__PURE__ */ __name((value, name) => { if (isVariableProxy(value)) { return true; } if (name && primitiveMetadata && primitiveMetadata[name]) { return primitiveMetadata[name].isVariable === true; } return false; }, "isVariable"), getType: /* @__PURE__ */ __name((value, name) => { const proxyType = getVariableType(value); if (proxyType !== void 0) { return proxyType; } if (name && primitiveMetadata && primitiveMetadata[name]) { return primitiveMetadata[name].type; } return void 0; }, "getType"), // Property names for direct access TYPE: VARIABLE_PROXY_PROPS.TYPE, SUBTYPE: VARIABLE_PROXY_PROPS.SUBTYPE, METADATA: VARIABLE_PROXY_PROPS.METADATA, VARIABLE: VARIABLE_PROXY_PROPS.VARIABLE, // Metadata helpers - also check primitive metadata getCtx: /* @__PURE__ */ __name((value, name) => { const proxyVariable = getProxyVariable(value); if (proxyVariable) { return cloneValue(proxyVariable.mx); } if (name && primitiveMetadata && primitiveMetadata[name]) { return cloneValue(primitiveMetadata[name].mx); } return void 0; }, "getCtx"), getInternal: /* @__PURE__ */ __name((value, name) => { const proxyVariable = getProxyVariable(value); if (proxyVariable) { return cloneValue(proxyVariable.internal); } if (name && primitiveMetadata && primitiveMetadata[name]) { return cloneValue(primitiveMetadata[name].internal); } return void 0; }, "getInternal"), getMetadata: /* @__PURE__ */ __name((value, name) => { const mx = helpers.getCtx(value, name); const internal = helpers.getInternal(value, name); if (!mx && !internal) { return void 0; } return { mx, internal }; }, "getMetadata"), // Get subtype - also check primitive metadata getSubtype: /* @__PURE__ */ __name((value, name) => { if (isVariableProxy(value)) { try { return value[VARIABLE_PROXY_PROPS.SUBTYPE]; } catch { return void 0; } } if (name && primitiveMetadata && primitiveMetadata[name]) { return primitiveMetadata[name].subtype; } return void 0; }, "getSubtype"), // Get the full Variable object - or reconstruct from metadata getVariable: /* @__PURE__ */ __name((value, name) => { const proxyVariable = getProxyVariable(value); if (proxyVariable) { return proxyVariable; } if (name && primitiveMetadata && primitiveMetadata[name]) { const meta = primitiveMetadata[name]; return { name, value, type: meta.type, subtype: meta.subtype, mx: meta.mx || {}, internal: meta.internal || {}, isVariable: true }; } return void 0; }, "getVariable") }; return { ...helpers, mx: helpers.getCtx }; } __name(createMlldHelpers, "createMlldHelpers"); export { CommandUtils, createMlldHelpers, prepareParamsForShadow, prepareValueForShadow }; //# sourceMappingURL=chunk-ONLT7ZR3.mjs.map //# sourceMappingURL=chunk-ONLT7ZR3.mjs.map