@paretools/shared
Advanced tools
| /** | ||
| * 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 | ||
| */ | ||
| export declare function assertNoFlagInjection(value: string, paramName: string): void; | ||
| export declare function assertAllowedCommand(command: string): void; | ||
| //# sourceMappingURL=validation.d.ts.map |
| {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAM5E;AAiCD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAe1D"} |
| /** | ||
| * 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 | ||
| */ | ||
| export function assertNoFlagInjection(value, paramName) { | ||
| if (value.startsWith("-")) { | ||
| throw new Error(`Invalid ${paramName}: "${value}". Values must not start with "-" to prevent argument injection.`); | ||
| } | ||
| } | ||
| /** | ||
| * Allowlist of known safe build commands. | ||
| * Prevents arbitrary command execution via the build tool's command parameter. | ||
| */ | ||
| const ALLOWED_BUILD_COMMANDS = new Set([ | ||
| "npm", | ||
| "npx", | ||
| "pnpm", | ||
| "yarn", | ||
| "bun", | ||
| "bunx", | ||
| "make", | ||
| "cmake", | ||
| "gradle", | ||
| "gradlew", | ||
| "mvn", | ||
| "ant", | ||
| "cargo", | ||
| "go", | ||
| "dotnet", | ||
| "msbuild", | ||
| "tsc", | ||
| "esbuild", | ||
| "vite", | ||
| "webpack", | ||
| "rollup", | ||
| "turbo", | ||
| "nx", | ||
| "bazel", | ||
| ]); | ||
| export function assertAllowedCommand(command) { | ||
| // Extract the base command name (handle paths like /usr/bin/npm or C:\npm.cmd) | ||
| const base = command | ||
| .replace(/\\/g, "/") | ||
| .split("/") | ||
| .pop() | ||
| ?.replace(/\.(cmd|exe|bat|sh)$/i, "") ?? ""; | ||
| if (!ALLOWED_BUILD_COMMANDS.has(base)) { | ||
| throw new Error(`Command "${command}" is not in the allowed build commands list. ` + | ||
| `Allowed: ${[...ALLOWED_BUILD_COMMANDS].sort().join(", ")}`); | ||
| } | ||
| } | ||
| //# sourceMappingURL=validation.js.map |
| {"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAa,EAAE,SAAiB;IACpE,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,WAAW,SAAS,MAAM,KAAK,kEAAkE,CAClG,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IACrC,KAAK;IACL,KAAK;IACL,MAAM;IACN,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,OAAO;IACP,QAAQ;IACR,SAAS;IACT,KAAK;IACL,KAAK;IACL,OAAO;IACP,IAAI;IACJ,QAAQ;IACR,SAAS;IACT,KAAK;IACL,SAAS;IACT,MAAM;IACN,SAAS;IACT,QAAQ;IACR,OAAO;IACP,IAAI;IACJ,OAAO;CACR,CAAC,CAAC;AAEH,MAAM,UAAU,oBAAoB,CAAC,OAAe;IAClD,+EAA+E;IAC/E,MAAM,IAAI,GACR,OAAO;SACJ,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,EAAE;QACN,EAAE,OAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;IAEhD,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CACb,YAAY,OAAO,+CAA+C;YAChE,YAAY,CAAC,GAAG,sBAAsB,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC9D,CAAC;IACJ,CAAC;AACH,CAAC"} |
+25
| # @paretools/shared | ||
| Shared utilities for Pare MCP servers. | ||
| ## Exports | ||
| - **`run(command, args, options)`** — Executes a CLI command via `execFile` (no shell injection) and returns `{ stdout, stderr, exitCode }` | ||
| - **`dualOutput(data, formatter)`** — Returns both `structuredContent` (typed JSON) and `content` (human-readable text) for MCP tool responses | ||
| - **`stripAnsi(text)`** — Removes ANSI escape codes from CLI output | ||
| ## Usage | ||
| This package is used internally by all `@paretools/*` server packages. You generally don't need to install it directly unless you're building a custom Pare server. | ||
| ```typescript | ||
| import { run, dualOutput, stripAnsi } from "@paretools/shared"; | ||
| ``` | ||
| ## Links | ||
| - [Pare monorepo](https://github.com/Dave-London/pare) | ||
| ## License | ||
| [MIT](https://github.com/Dave-London/pare/blob/main/LICENSE) |
+1
-0
| export { dualOutput } from "./output.js"; | ||
| export { run, type RunResult, type RunOptions } from "./runner.js"; | ||
| export { stripAnsi } from "./ansi.js"; | ||
| export { assertNoFlagInjection, assertAllowedCommand } from "./validation.js"; | ||
| export type { ToolOutput } from "./types.js"; | ||
| //# sourceMappingURL=index.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,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,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"} |
+1
-0
| export { dualOutput } from "./output.js"; | ||
| export { run } from "./runner.js"; | ||
| export { stripAnsi } from "./ansi.js"; | ||
| export { assertNoFlagInjection, assertAllowedCommand } from "./validation.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,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,GAAG,EAAmC,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"} | ||
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,GAAG,EAAmC,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"} |
+3
-0
@@ -16,4 +16,7 @@ export interface RunOptions { | ||
| * still passed as an array so they remain properly escaped. | ||
| * | ||
| * Throws on system-level errors (command not found, permission denied). | ||
| * Normal non-zero exit codes are returned in the result, not thrown. | ||
| */ | ||
| export declare function run(cmd: string, args: string[], opts?: RunOptions): Promise<RunResult>; | ||
| //# sourceMappingURL=runner.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAGA,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;CAC9B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAqBtF"} | ||
| {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAGA,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;CAC9B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;GAQG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAkDtF"} |
+23
-2
@@ -8,5 +8,8 @@ import { execFile } from "node:child_process"; | ||
| * still passed as an array so they remain properly escaped. | ||
| * | ||
| * Throws on system-level errors (command not found, permission denied). | ||
| * Normal non-zero exit codes are returned in the result, not thrown. | ||
| */ | ||
| export function run(cmd, args, opts) { | ||
| return new Promise((resolve) => { | ||
| return new Promise((resolve, reject) => { | ||
| execFile(cmd, args, { | ||
@@ -19,4 +22,22 @@ cwd: opts?.cwd, | ||
| }, (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; | ||
| } | ||
| // 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; | ||
| } | ||
| } | ||
| resolve({ | ||
| exitCode: error && "code" in error ? error.code : error ? 1 : 0, | ||
| exitCode: error ? (typeof error.code === "number" ? error.code : 1) : 0, | ||
| stdout: stripAnsi(stdout), | ||
@@ -23,0 +44,0 @@ stderr: stripAnsi(stderr), |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AActC;;;;;GAKG;AACH,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,IAAc,EAAE,IAAiB;IAChE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,QAAQ,CACN,GAAG,EACH,IAAI,EACJ;YACE,GAAG,EAAE,IAAI,EAAE,GAAG;YACd,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,MAAM;YAChC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS;YAC5D,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,QAAQ;YACrC,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO;SACpC,EACD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,OAAO,CAAC;gBACN,QAAQ,EAAE,KAAK,IAAI,MAAM,IAAI,KAAK,CAAC,CAAC,CAAE,KAAK,CAAC,IAAe,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC3E,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC;gBACzB,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC;aAC1B,CAAC,CAAC;QACL,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"} | ||
| {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AActC;;;;;;;;GAQG;AACH,MAAM,UAAU,GAAG,CAAC,GAAW,EAAE,IAAc,EAAE,IAAiB;IAChE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,QAAQ,CACN,GAAG,EACH,IAAI,EACJ;YACE,GAAG,EAAE,IAAI,EAAE,GAAG;YACd,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,MAAM;YAChC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS;YAC5D,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,QAAQ;YACrC,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO;SACpC,EACD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,KAAK,GAAG,KAA8B,CAAC;gBAE7C,wDAAwD;gBACxD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBAC5B,MAAM,CACJ,IAAI,KAAK,CACP,uBAAuB,GAAG,uDAAuD,CAClF,CACF,CAAC;oBACF,OAAO;gBACT,CAAC;gBACD,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBACtD,MAAM,CAAC,IAAI,KAAK,CAAC,gCAAgC,GAAG,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;oBAC5E,OAAO;gBACT,CAAC;gBAED,4DAA4D;gBAC5D,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;gBACtC,IAAI,WAAW,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;oBAC9C,MAAM,CACJ,IAAI,KAAK,CACP,uBAAuB,GAAG,uDAAuD,CAClF,CACF,CAAC;oBACF,OAAO;gBACT,CAAC;YACH,CAAC;YAED,OAAO,CAAC;gBACN,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACvE,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC;gBACzB,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC;aAC1B,CAAC,CAAC;QACL,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"} |
+12
-2
| { | ||
| "name": "@paretools/shared", | ||
| "version": "0.2.0", | ||
| "description": "Shared utilities for pare MCP servers", | ||
| "version": "0.3.0", | ||
| "description": "Shared utilities for Pare MCP servers", | ||
| "license": "MIT", | ||
| "keywords": [ | ||
| "mcp", | ||
| "mcp-server", | ||
| "model-context-protocol", | ||
| "structured-output" | ||
| ], | ||
| "homepage": "https://github.com/Dave-London/pare/tree/main/packages/shared", | ||
| "engines": { | ||
| "node": ">=18" | ||
| }, | ||
| "type": "module", | ||
@@ -7,0 +17,0 @@ "exports": { |
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
No website
QualityPackage does not have a website.
15688
68.4%27
22.73%185
85%1
-50%0
-100%26
Infinity%