mlld
Version:
mlld: llm scripting language
387 lines (384 loc) • 13.5 kB
JavaScript
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