UNPKG

mlld

Version:

mlld: llm scripting language

387 lines (384 loc) 13.5 kB
import { wrapLoadContentValue } from './chunk-DPK4IGZA.mjs'; import { isVariable } from './chunk-AXDK3AHT.mjs'; import { asData, isStructuredValue } from './chunk-CQPPOI5P.mjs'; import { isLoadContentResult, isLoadContentResultArray } from './chunk-MNK7EBJ3.mjs'; import { __export, __esm, __name } from './chunk-NJQWMXLH.mjs'; import * as shellQuote from 'shell-quote'; // interpreter/env/CommandUtils.ts var CommandUtils_exports = {}; __export(CommandUtils_exports, { CommandUtils: () => CommandUtils }); var _CommandUtils, CommandUtils; var init_CommandUtils = __esm({ "interpreter/env/CommandUtils.ts"() { _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([ "&&", // AND operator "||", // OR operator ";", // Command separator // '>', // 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*\]/, // TTY detection: if [ -t 0 ] or if [ -t 1 ] /echo\s+.*\s+>&2/, // Direct stderr output: echo "..." >&2 /\|\|\s*echo/, // Fallback pattern: command || echo /python3?\s+-c/, // Python scripts that might detect TTY /node\s+-e/, // Node scripts that might detect TTY /sh\s+-c\s+.*>&2/, // Shell commands with stderr: sh -c '... >&2' /echo.*&&.*echo.*>&2/ // Commands with multiple echo, one to stderr ]; 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)); } }; __name(_CommandUtils, "CommandUtils"); CommandUtils = _CommandUtils; } }); // interpreter/env/variable-proxy.ts 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 variable.metadata || {}; 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": if (variable.metadata?.customToString) { return variable.metadata.customToString.bind(target); } return Reflect.get(target, prop, receiver); // Special handling for toJSON case "toJSON": if (variable.metadata?.customToJSON) { return variable.metadata.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, metadata: value.metadata || {} }); } return value.value; } return createVariableProxy(value); } if (isLoadContentResult(value) || isLoadContentResultArray(value)) { const wrapped = wrapLoadContentValue(value); const data = asData(wrapped); if (target && key) { recordPrimitiveMetadata(target, key, { isVariable: false, type: wrapped.type, metadata: wrapped.metadata || {}, text: wrapped.text }); } return data; } if (isStructuredValue(value)) { const data = asData(value); if (target && key) { recordPrimitiveMetadata(target, key, { isVariable: false, type: value.type, metadata: value.metadata || {}, 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) { return { // 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 getMetadata: /* @__PURE__ */ __name((value, name) => { if (isVariableProxy(value)) { try { return value[VARIABLE_PROXY_PROPS.METADATA]; } catch { return void 0; } } if (name && primitiveMetadata && primitiveMetadata[name]) { return primitiveMetadata[name].metadata || {}; } return void 0; }, "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) => { if (isVariableProxy(value)) { try { return value[VARIABLE_PROXY_PROPS.VARIABLE]; } catch { return void 0; } } if (name && primitiveMetadata && primitiveMetadata[name]) { const meta = primitiveMetadata[name]; return { name, value, type: meta.type, subtype: meta.subtype, metadata: meta.metadata, isVariable: true }; } return void 0; }, "getVariable") }; } __name(createMlldHelpers, "createMlldHelpers"); export { CommandUtils, CommandUtils_exports, createMlldHelpers, init_CommandUtils, prepareParamsForShadow, prepareValueForShadow }; //# sourceMappingURL=chunk-XVCWUTXF.mjs.map //# sourceMappingURL=chunk-XVCWUTXF.mjs.map