Sign In

@paretools/shared

Package Overview
Dependencies
Maintainers
1
Versions
35
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@paretools/shared - npm Package Compare versions

Comparing version
0.8.1
to
0.8.2
+57
dist/policy.d.ts
/**
* Security policy controls — opt-in hardening via environment variables.
*
* All controls follow the same global/per-server precedence pattern as
* tool filtering (see tool-filter.ts):
*
* 1. `PARE_{SETTING}` — global, applies to all servers.
* 2. `PARE_{SERVER}_{SETTING}` — per-server override.
* 3. No env var → permissive (default, no restriction).
*
* Global takes precedence over per-server when both are set.
*
* ## Available Controls
*
* ### PARE_ALLOWED_COMMANDS / PARE_{SERVER}_ALLOWED_COMMANDS
* Comma-separated list of allowed command names.
* When set, only listed commands may be executed.
* Example: `PARE_PROCESS_ALLOWED_COMMANDS=node,python,make`
* Example: `PARE_ALLOWED_COMMANDS=node,python,git,npm` (all servers)
*
* ### PARE_ALLOWED_ROOTS / PARE_{SERVER}_ALLOWED_ROOTS
* Comma-separated list of allowed root directories.
* When set, all path/cwd parameters must be under one of these roots.
* Example: `PARE_ALLOWED_ROOTS=/home/user/projects,/tmp/builds`
* Example: `PARE_PROCESS_ALLOWED_ROOTS=/home/user/safe-dir`
*
* ### PARE_BUILD_STRICT_PATH
* When set to "true", the build server rejects path-qualified commands
* (e.g., `/tmp/evil/npm`) and only allows bare command names resolved via PATH.
*/
/**
* Asserts that a command is allowed by the ALLOWED_COMMANDS policy.
* No-op when the policy is not configured (permissive default).
*
* @param command - The command to check (e.g., "node", "python").
* @param serverName - Server identifier for per-server overrides (e.g., "process").
* @throws Error if the command is not in the allowlist.
*/
export declare function assertAllowedByPolicy(command: string, serverName: string): void;
/**
* Asserts that a working directory path is under one of the allowed roots.
* No-op when the policy is not configured (permissive default).
*
* @param targetPath - The path/cwd to validate.
* @param serverName - Server identifier for per-server overrides (e.g., "process").
* @throws Error if the path is outside all allowed roots.
*/
export declare function assertAllowedRoot(targetPath: string, serverName: string): void;
/**
* Asserts that a command does not contain path separators (strict path mode).
* Used by build server when PARE_BUILD_STRICT_PATH=true.
*
* @param command - The command to check.
* @throws Error if the command contains `/` or `\`.
*/
export declare function assertNoPathQualifiedCommand(command: string): void;
//# sourceMappingURL=policy.d.ts.map
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AA+BH;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAmB/E;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAoB9E;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CASlE"}
/**
* Security policy controls — opt-in hardening via environment variables.
*
* All controls follow the same global/per-server precedence pattern as
* tool filtering (see tool-filter.ts):
*
* 1. `PARE_{SETTING}` — global, applies to all servers.
* 2. `PARE_{SERVER}_{SETTING}` — per-server override.
* 3. No env var → permissive (default, no restriction).
*
* Global takes precedence over per-server when both are set.
*
* ## Available Controls
*
* ### PARE_ALLOWED_COMMANDS / PARE_{SERVER}_ALLOWED_COMMANDS
* Comma-separated list of allowed command names.
* When set, only listed commands may be executed.
* Example: `PARE_PROCESS_ALLOWED_COMMANDS=node,python,make`
* Example: `PARE_ALLOWED_COMMANDS=node,python,git,npm` (all servers)
*
* ### PARE_ALLOWED_ROOTS / PARE_{SERVER}_ALLOWED_ROOTS
* Comma-separated list of allowed root directories.
* When set, all path/cwd parameters must be under one of these roots.
* Example: `PARE_ALLOWED_ROOTS=/home/user/projects,/tmp/builds`
* Example: `PARE_PROCESS_ALLOWED_ROOTS=/home/user/safe-dir`
*
* ### PARE_BUILD_STRICT_PATH
* When set to "true", the build server rejects path-qualified commands
* (e.g., `/tmp/evil/npm`) and only allows bare command names resolved via PATH.
*/
import { resolve, normalize } from "node:path";
/**
* Reads a policy env var with global/per-server precedence.
* Global (`PARE_{setting}`) wins over per-server (`PARE_{SERVER}_{setting}`).
* Returns undefined when neither is set (no restriction).
*/
function readPolicyVar(serverName, setting) {
const global = process.env[`PARE_${setting}`];
if (global !== undefined)
return global;
const envKey = `PARE_${serverName.toUpperCase().replace(/-/g, "_")}_${setting}`;
return process.env[envKey];
}
/**
* Parses a comma-separated env var value into a trimmed Set.
* Returns undefined if the raw value is undefined or empty.
*/
function parseList(raw) {
if (raw === undefined || raw.trim() === "")
return undefined;
return new Set(raw
.split(",")
.map((s) => s.trim())
.filter(Boolean));
}
/**
* Asserts that a command is allowed by the ALLOWED_COMMANDS policy.
* No-op when the policy is not configured (permissive default).
*
* @param command - The command to check (e.g., "node", "python").
* @param serverName - Server identifier for per-server overrides (e.g., "process").
* @throws Error if the command is not in the allowlist.
*/
export function assertAllowedByPolicy(command, serverName) {
const raw = readPolicyVar(serverName, "ALLOWED_COMMANDS");
const allowed = parseList(raw);
if (!allowed)
return; // No policy — everything allowed
// Extract basename for comparison (handle paths like /usr/bin/node)
const base = command
.replace(/\\/g, "/")
.split("/")
.pop()
?.replace(/\.(cmd|exe|bat|sh)$/i, "") ?? "";
if (!allowed.has(base) && !allowed.has(command)) {
throw new Error(`Command "${command}" is not allowed by ALLOWED_COMMANDS policy. ` +
`Allowed: ${[...allowed].sort().join(", ")}`);
}
}
/**
* Asserts that a working directory path is under one of the allowed roots.
* No-op when the policy is not configured (permissive default).
*
* @param targetPath - The path/cwd to validate.
* @param serverName - Server identifier for per-server overrides (e.g., "process").
* @throws Error if the path is outside all allowed roots.
*/
export function assertAllowedRoot(targetPath, serverName) {
const raw = readPolicyVar(serverName, "ALLOWED_ROOTS");
const roots = parseList(raw);
if (!roots)
return; // No policy — all paths allowed
const normalizedTarget = normalize(resolve(targetPath));
for (const root of roots) {
const normalizedRoot = normalize(resolve(root));
if (normalizedTarget === normalizedRoot ||
normalizedTarget.startsWith(normalizedRoot + (process.platform === "win32" ? "\\" : "/"))) {
return; // Path is under an allowed root
}
}
throw new Error(`Path "${targetPath}" is outside allowed roots. ` + `Allowed roots: ${[...roots].join(", ")}`);
}
/**
* Asserts that a command does not contain path separators (strict path mode).
* Used by build server when PARE_BUILD_STRICT_PATH=true.
*
* @param command - The command to check.
* @throws Error if the command contains `/` or `\`.
*/
export function assertNoPathQualifiedCommand(command) {
if (process.env.PARE_BUILD_STRICT_PATH === "true") {
if (command.includes("/") || command.includes("\\")) {
throw new Error(`Path-qualified commands are not allowed when PARE_BUILD_STRICT_PATH is enabled. ` +
`Use a bare command name (e.g., "npm" not "${command}") that resolves via PATH.`);
}
}
}
//# sourceMappingURL=policy.js.map
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}
+1
-0

@@ -8,3 +8,4 @@ export { dualOutput, estimateTokens, compactDualOutput } from "./output.js";

export { shouldRegisterTool } from "./tool-filter.js";
export { assertAllowedByPolicy, assertAllowedRoot, assertNoPathQualifiedCommand, } from "./policy.js";
export type { ToolOutput } from "./types.js";
//# sourceMappingURL=index.d.ts.map
+1
-1

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,4BAA4B,GAC7B,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}

@@ -8,2 +8,3 @@ export { dualOutput, estimateTokens, compactDualOutput } from "./output.js";

export { shouldRegisterTool } from "./tool-filter.js";
export { assertAllowedByPolicy, assertAllowedRoot, assertNoPathQualifiedCommand, } from "./policy.js";
//# sourceMappingURL=index.js.map

@@ -1,1 +0,1 @@

{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,GAAG,EAAE,YAAY,EAAmC,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,GAAG,EAAE,YAAY,EAAmC,MAAM,aAAa,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,4BAA4B,GAC7B,MAAM,aAAa,CAAC"}

@@ -14,2 +14,4 @@ /** Options for the command runner, including working directory, timeout, and environment overrides. */

shell?: boolean;
/** Maximum combined stdout+stderr buffer size in bytes. Defaults to 10 MB. */
maxBuffer?: number;
}

@@ -44,7 +46,10 @@ /** Result of a command execution, containing the exit code and ANSI-stripped stdout/stderr. */

* Executes a command and returns cleaned output with ANSI codes stripped.
* Uses execFile (not exec) to avoid shell injection. On Windows, shell is
* enabled so that .cmd/.bat wrappers (like npx) can be executed — args are
* still passed as an array so they remain properly escaped.
* Uses spawn (not exec) to avoid shell injection. The child is spawned in its
* own process group (`detached: true` on Unix) so that on timeout we can kill
* the entire group — preventing orphaned grandchild processes.
*
* Throws on system-level errors (command not found, permission denied).
* On Windows, shell is enabled so that .cmd/.bat wrappers (like npx) can be
* executed — args are still passed as an array so they remain properly escaped.
*
* Throws on system-level errors (command not found, permission denied, timeout).
* Normal non-zero exit codes are returned in the result, not thrown.

@@ -51,0 +56,0 @@ */

@@ -1,1 +0,1 @@

{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAIA,uGAAuG;AACvG,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;;mFAE+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;qFAEiF;IACjF,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,+FAA+F;AAC/F,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAqChD;AAED;;;;;;;;GAQG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CA8EtF"}
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAIA,uGAAuG;AACvG,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;;mFAE+E;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;qFAEiF;IACjF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA6BD,+FAA+F;AAC/F,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAqChD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CA+ItF"}

@@ -1,5 +0,35 @@

import { execFile } from "node:child_process";
import { spawn, execFile as nodeExecFile } from "node:child_process";
import { stripAnsi } from "./ansi.js";
import { sanitizeErrorOutput } from "./sanitize.js";
/**
* Kills an entire process group (Unix) or process tree (Windows).
*
* On Unix, `spawn({ detached: true })` calls `setsid(2)`, making the child
* a process group leader. Sending a signal to `-pid` reaches every process
* in that group, including grandchildren.
*
* On Windows, `taskkill /T /F` walks the native process tree.
*
* ESRCH (process already exited) is silently ignored.
*/
function killProcessGroup(pid) {
if (process.platform === "win32") {
try {
nodeExecFile("taskkill", ["/pid", String(pid), "/T", "/F"]);
}
catch {
/* best-effort */
}
}
else {
try {
process.kill(-pid, "SIGTERM");
}
catch (err) {
if (err.code !== "ESRCH")
throw err;
}
}
}
/**
* Escapes a single argument for safe use with cmd.exe on Windows.

@@ -59,7 +89,10 @@ *

* Executes a command and returns cleaned output with ANSI codes stripped.
* Uses execFile (not exec) to avoid shell injection. On Windows, shell is
* enabled so that .cmd/.bat wrappers (like npx) can be executed — args are
* still passed as an array so they remain properly escaped.
* Uses spawn (not exec) to avoid shell injection. The child is spawned in its
* own process group (`detached: true` on Unix) so that on timeout we can kill
* the entire group — preventing orphaned grandchild processes.
*
* Throws on system-level errors (command not found, permission denied).
* On Windows, shell is enabled so that .cmd/.bat wrappers (like npx) can be
* executed — args are still passed as an array so they remain properly escaped.
*
* Throws on system-level errors (command not found, permission denied, timeout).
* Normal non-zero exit codes are returned in the result, not thrown.

@@ -77,37 +110,97 @@ */

const safeArgs = useShell && process.platform === "win32" ? args.map(escapeCmdArg) : args;
const child = execFile(cmd, safeArgs, {
const child = spawn(cmd, safeArgs, {
cwd: opts?.cwd,
timeout: opts?.timeout ?? 60_000,
env: opts?.env ? { ...process.env, ...opts.env } : undefined,
maxBuffer: 10 * 1024 * 1024, // 10 MB
shell: useShell,
}, (error, stdout, stderr) => {
if (error) {
const errno = error;
// Unix: direct ENOENT from execFile (no shell wrapping)
if (errno.code === "ENOENT") {
reject(new Error(`Command not found: "${cmd}". Ensure it is installed and available in your PATH.`));
return;
}
if (errno.code === "EACCES" || errno.code === "EPERM") {
reject(new Error(`Permission denied executing "${cmd}": ${errno.message}`));
return;
}
// Timeout: execFile killed the child after the configured timeout.
// Surface this clearly instead of silently returning exitCode 1.
if (error.killed && error.signal) {
reject(new Error(`Command "${cmd}" timed out after ${opts?.timeout ?? 60_000}ms and was killed (${error.signal}).`));
return;
}
// Windows: cmd.exe masks ENOENT — detect via stderr message
const cleanStderr = stripAnsi(stderr);
if (cleanStderr.includes("is not recognized")) {
reject(new Error(`Command not found: "${cmd}". Ensure it is installed and available in your PATH.`));
return;
}
// Unix: creates a new process group via setsid(2) so we can kill the
// entire group on timeout (prevents orphaned grandchild processes).
// Windows: skip — detached on Windows creates a visible console window,
// not a process group. Use taskkill /T /F instead.
detached: process.platform !== "win32",
windowsHide: true,
stdio: ["pipe", "pipe", "pipe"],
});
// --- Buffer accumulation (replaces execFile's maxBuffer) ---
const maxBuffer = opts?.maxBuffer ?? 10 * 1024 * 1024; // 10 MB
const stdoutChunks = [];
const stderrChunks = [];
let stdoutLen = 0;
let stderrLen = 0;
let bufferExceeded = false;
child.stdout?.on("data", (chunk) => {
stdoutLen += chunk.length;
if (stdoutLen + stderrLen > maxBuffer && !bufferExceeded) {
bufferExceeded = true;
killProcessGroup(child.pid);
return;
}
if (!bufferExceeded)
stdoutChunks.push(chunk);
});
child.stderr?.on("data", (chunk) => {
stderrLen += chunk.length;
if (stdoutLen + stderrLen > maxBuffer && !bufferExceeded) {
bufferExceeded = true;
killProcessGroup(child.pid);
return;
}
if (!bufferExceeded)
stderrChunks.push(chunk);
});
// --- Timeout handling (replaces execFile's timeout) ---
let timedOut = false;
let timeoutSignal;
const timeoutMs = opts?.timeout ?? 60_000;
const timer = setTimeout(() => {
timedOut = true;
timeoutSignal = "SIGTERM";
killProcessGroup(child.pid);
}, timeoutMs);
// --- Settlement guard (prevents double-resolve from close + error) ---
let settled = false;
// Handle spawn errors (ENOENT, EACCES on Unix — spawn emits these as
// 'error' events, unlike execFile which passes them to the callback).
child.on("error", (err) => {
clearTimeout(timer);
if (settled)
return;
settled = true;
if (err.code === "ENOENT") {
reject(new Error(`Command not found: "${cmd}". Ensure it is installed and available in your PATH.`));
return;
}
if (err.code === "EACCES" || err.code === "EPERM") {
reject(new Error(`Permission denied executing "${cmd}": ${err.message}`));
return;
}
reject(err);
});
// Resolve on 'close' (not 'exit') — close fires after ALL stdio streams
// are flushed, guaranteeing no late data events.
child.on("close", (code, signal) => {
clearTimeout(timer);
if (settled)
return;
settled = true;
const stdout = stripAnsi(Buffer.concat(stdoutChunks).toString("utf-8"));
const stderr = sanitizeErrorOutput(stripAnsi(Buffer.concat(stderrChunks).toString("utf-8")));
// Timeout: we killed the process group after the configured timeout.
if (timedOut) {
reject(new Error(`Command "${cmd}" timed out after ${timeoutMs}ms and was killed (${timeoutSignal ?? signal ?? "SIGTERM"}).`));
return;
}
// maxBuffer exceeded
if (bufferExceeded) {
reject(new Error(`Command "${cmd}" output exceeded maxBuffer (${maxBuffer} bytes) and was killed.`));
return;
}
// Windows: cmd.exe masks ENOENT — detect via stderr message
if (code !== 0 && stderr.includes("is not recognized")) {
reject(new Error(`Command not found: "${cmd}". Ensure it is installed and available in your PATH.`));
return;
}
resolve({
exitCode: error ? (typeof error.code === "number" ? error.code : 1) : 0,
stdout: stripAnsi(stdout),
stderr: sanitizeErrorOutput(stripAnsi(stderr)),
exitCode: code ?? 1,
stdout,
stderr,
});

@@ -114,0 +207,0 @@ });

@@ -1,1 +0,1 @@

{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
/**
* Sanitizes error output by replacing sensitive filesystem paths with
* home-relative equivalents. This prevents leaking usernames and absolute
* home directory paths in error messages returned to MCP clients.
* Sanitizes error output by replacing sensitive filesystem paths.
*
* Replacements:
* Default mode (home paths only):
* /home/<user>/... → ~/...

@@ -11,4 +9,10 @@ * /Users/<user>/... → ~/...

* C:\Users\<user>\... → ~\...
*
* Broad mode (PARE_SANITIZE_ALL_PATHS=true):
* Also redacts other absolute paths outside home directories:
* /etc/..., /var/..., /opt/..., C:\Program Files\..., etc.
* These are replaced with <redacted-path>/basename to preserve
* the filename while hiding directory structure.
*/
export declare function sanitizeErrorOutput(text: string): string;
//# sourceMappingURL=sanitize.d.ts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcxD"}
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA6BxD"}
/**
* Sanitizes error output by replacing sensitive filesystem paths with
* home-relative equivalents. This prevents leaking usernames and absolute
* home directory paths in error messages returned to MCP clients.
* Sanitizes error output by replacing sensitive filesystem paths.
*
* Replacements:
* Default mode (home paths only):
* /home/<user>/... → ~/...

@@ -11,2 +9,8 @@ * /Users/<user>/... → ~/...

* C:\Users\<user>\... → ~\...
*
* Broad mode (PARE_SANITIZE_ALL_PATHS=true):
* Also redacts other absolute paths outside home directories:
* /etc/..., /var/..., /opt/..., C:\Program Files\..., etc.
* These are replaced with <redacted-path>/basename to preserve
* the filename while hiding directory structure.
*/

@@ -22,4 +26,17 @@ export function sanitizeErrorOutput(text) {

result = result.replace(/[A-Z]:\\Users\\[^\\:\s]+\\/gi, "~\\");
// Broad mode: redact remaining absolute paths
if (process.env.PARE_SANITIZE_ALL_PATHS === "true") {
// Unix absolute paths not already handled (e.g., /etc/foo/bar → <redacted>/bar)
result = result.replace(/\/(?:etc|var|opt|usr|tmp|srv|snap|nix)\/[^\s:]+/g, (match) => {
const basename = match.split("/").pop() ?? "";
return `<redacted-path>/${basename}`;
});
// Windows non-user absolute paths (e.g., C:\Program Files\foo → <redacted>\foo)
result = result.replace(/[A-Z]:\\(?!Users\\)[^\s:]+/gi, (match) => {
const basename = match.split("\\").pop() ?? "";
return `<redacted-path>\\${basename}`;
});
}
return result;
}
//# sourceMappingURL=sanitize.js.map

@@ -1,1 +0,1 @@

{"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,uCAAuC;IACvC,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC;IAEtD,yCAAyC;IACzC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;IAErD,4BAA4B;IAC5B,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAE3C,mFAAmF;IACnF,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;IAE/D,OAAO,MAAM,CAAC;AAChB,CAAC"}
{"version":3,"file":"sanitize.js","sourceRoot":"","sources":["../src/sanitize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,uCAAuC;IACvC,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,IAAI,CAAC,CAAC;IAEtD,yCAAyC;IACzC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC;IAErD,4BAA4B;IAC5B,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAE3C,mFAAmF;IACnF,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;IAE/D,8CAA8C;IAC9C,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,MAAM,EAAE,CAAC;QACnD,gFAAgF;QAChF,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,kDAAkD,EAAE,CAAC,KAAK,EAAE,EAAE;YACpF,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;YAC9C,OAAO,mBAAmB,QAAQ,EAAE,CAAC;QACvC,CAAC,CAAC,CAAC;QAEH,gFAAgF;QAChF,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,8BAA8B,EAAE,CAAC,KAAK,EAAE,EAAE;YAChE,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;YAC/C,OAAO,oBAAoB,QAAQ,EAAE,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
/**
* Validates that a string argument is safe to pass as a positional argument to a CLI tool.
* Prevents flag injection attacks (e.g., passing "--output=/etc/passwd" as a ref name).
* See: CVE-2025-68144, CVE-2025-68145
* See: [CVE-2025-68144](https://nvd.nist.gov/vuln/detail/CVE-2025-68144), [CVE-2025-68145](https://nvd.nist.gov/vuln/detail/CVE-2025-68145)
*/

@@ -6,0 +6,0 @@ export declare function assertNoFlagInjection(value: string, paramName: string): void;

/**
* Validates that a string argument is safe to pass as a positional argument to a CLI tool.
* Prevents flag injection attacks (e.g., passing "--output=/etc/passwd" as a ref name).
* See: CVE-2025-68144, CVE-2025-68145
* See: [CVE-2025-68144](https://nvd.nist.gov/vuln/detail/CVE-2025-68144), [CVE-2025-68145](https://nvd.nist.gov/vuln/detail/CVE-2025-68145)
*/

@@ -6,0 +6,0 @@ export function assertNoFlagInjection(value, paramName) {

{
"name": "@paretools/shared",
"version": "0.8.1",
"version": "0.8.2",
"description": "Shared utilities for Pare MCP servers",

@@ -5,0 +5,0 @@ "license": "MIT",