mlld
Version:
mlld: llm scripting language
454 lines (451 loc) • 14.6 kB
JavaScript
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