@paretools/shared 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/runner.d.ts +3 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +23 -2
- package/dist/runner.js.map +1 -1
- package/dist/validation.d.ts +8 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +53 -0
- package/dist/validation.js.map +1 -0
- package/package.json +12 -2
package/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# @paretools/shared
|
|
2
|
+
|
|
3
|
+
Shared utilities for Pare MCP servers.
|
|
4
|
+
|
|
5
|
+
## Exports
|
|
6
|
+
|
|
7
|
+
- **`run(command, args, options)`** — Executes a CLI command via `execFile` (no shell injection) and returns `{ stdout, stderr, exitCode }`
|
|
8
|
+
- **`dualOutput(data, formatter)`** — Returns both `structuredContent` (typed JSON) and `content` (human-readable text) for MCP tool responses
|
|
9
|
+
- **`stripAnsi(text)`** — Removes ANSI escape codes from CLI output
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
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.
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { run, dualOutput, stripAnsi } from "@paretools/shared";
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Links
|
|
20
|
+
|
|
21
|
+
- [Pare monorepo](https://github.com/Dave-London/pare)
|
|
22
|
+
|
|
23
|
+
## License
|
|
24
|
+
|
|
25
|
+
[MIT](https://github.com/Dave-London/pare/blob/main/LICENSE)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { dualOutput } from "./output.js";
|
|
2
2
|
export { run, type RunResult, type RunOptions } from "./runner.js";
|
|
3
3
|
export { stripAnsi } from "./ansi.js";
|
|
4
|
+
export { assertNoFlagInjection, assertAllowedCommand } from "./validation.js";
|
|
4
5
|
export type { ToolOutput } from "./types.js";
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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"}
|
|
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,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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"}
|
|
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;AACtC,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/runner.d.ts
CHANGED
|
@@ -13,6 +13,9 @@ export interface RunResult {
|
|
|
13
13
|
* Uses execFile (not exec) to avoid shell injection. On Windows, shell is
|
|
14
14
|
* enabled so that .cmd/.bat wrappers (like npx) can be executed — args are
|
|
15
15
|
* still passed as an array so they remain properly escaped.
|
|
16
|
+
*
|
|
17
|
+
* Throws on system-level errors (command not found, permission denied).
|
|
18
|
+
* Normal non-zero exit codes are returned in the result, not thrown.
|
|
16
19
|
*/
|
|
17
20
|
export declare function run(cmd: string, args: string[], opts?: RunOptions): Promise<RunResult>;
|
|
18
21
|
//# sourceMappingURL=runner.d.ts.map
|
package/dist/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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
|
|
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;;;;;;;;GAQG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAkDtF"}
|
package/dist/runner.js
CHANGED
|
@@ -5,9 +5,12 @@ import { stripAnsi } from "./ansi.js";
|
|
|
5
5
|
* Uses execFile (not exec) to avoid shell injection. On Windows, shell is
|
|
6
6
|
* enabled so that .cmd/.bat wrappers (like npx) can be executed — args are
|
|
7
7
|
* still passed as an array so they remain properly escaped.
|
|
8
|
+
*
|
|
9
|
+
* Throws on system-level errors (command not found, permission denied).
|
|
10
|
+
* Normal non-zero exit codes are returned in the result, not thrown.
|
|
8
11
|
*/
|
|
9
12
|
export function run(cmd, args, opts) {
|
|
10
|
-
return new Promise((resolve) => {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
11
14
|
execFile(cmd, args, {
|
|
12
15
|
cwd: opts?.cwd,
|
|
13
16
|
timeout: opts?.timeout ?? 30_000,
|
|
@@ -15,8 +18,26 @@ export function run(cmd, args, opts) {
|
|
|
15
18
|
maxBuffer: 10 * 1024 * 1024, // 10 MB
|
|
16
19
|
shell: process.platform === "win32",
|
|
17
20
|
}, (error, stdout, stderr) => {
|
|
21
|
+
if (error) {
|
|
22
|
+
const errno = error;
|
|
23
|
+
// Unix: direct ENOENT from execFile (no shell wrapping)
|
|
24
|
+
if (errno.code === "ENOENT") {
|
|
25
|
+
reject(new Error(`Command not found: "${cmd}". Ensure it is installed and available in your PATH.`));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (errno.code === "EACCES" || errno.code === "EPERM") {
|
|
29
|
+
reject(new Error(`Permission denied executing "${cmd}": ${errno.message}`));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Windows: cmd.exe masks ENOENT — detect via stderr message
|
|
33
|
+
const cleanStderr = stripAnsi(stderr);
|
|
34
|
+
if (cleanStderr.includes("is not recognized")) {
|
|
35
|
+
reject(new Error(`Command not found: "${cmd}". Ensure it is installed and available in your PATH.`));
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
18
39
|
resolve({
|
|
19
|
-
exitCode: error
|
|
40
|
+
exitCode: error ? (typeof error.code === "number" ? error.code : 1) : 0,
|
|
20
41
|
stdout: stripAnsi(stdout),
|
|
21
42
|
stderr: stripAnsi(stderr),
|
|
22
43
|
});
|
package/dist/runner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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
|
|
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;;;;;;;;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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates that a string argument is safe to pass as a positional argument to a CLI tool.
|
|
3
|
+
* Prevents flag injection attacks (e.g., passing "--output=/etc/passwd" as a ref name).
|
|
4
|
+
* See: CVE-2025-68144, CVE-2025-68145
|
|
5
|
+
*/
|
|
6
|
+
export declare function assertNoFlagInjection(value: string, paramName: string): void;
|
|
7
|
+
export declare function assertAllowedCommand(command: string): void;
|
|
8
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates that a string argument is safe to pass as a positional argument to a CLI tool.
|
|
3
|
+
* Prevents flag injection attacks (e.g., passing "--output=/etc/passwd" as a ref name).
|
|
4
|
+
* See: CVE-2025-68144, CVE-2025-68145
|
|
5
|
+
*/
|
|
6
|
+
export function assertNoFlagInjection(value, paramName) {
|
|
7
|
+
if (value.startsWith("-")) {
|
|
8
|
+
throw new Error(`Invalid ${paramName}: "${value}". Values must not start with "-" to prevent argument injection.`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Allowlist of known safe build commands.
|
|
13
|
+
* Prevents arbitrary command execution via the build tool's command parameter.
|
|
14
|
+
*/
|
|
15
|
+
const ALLOWED_BUILD_COMMANDS = new Set([
|
|
16
|
+
"npm",
|
|
17
|
+
"npx",
|
|
18
|
+
"pnpm",
|
|
19
|
+
"yarn",
|
|
20
|
+
"bun",
|
|
21
|
+
"bunx",
|
|
22
|
+
"make",
|
|
23
|
+
"cmake",
|
|
24
|
+
"gradle",
|
|
25
|
+
"gradlew",
|
|
26
|
+
"mvn",
|
|
27
|
+
"ant",
|
|
28
|
+
"cargo",
|
|
29
|
+
"go",
|
|
30
|
+
"dotnet",
|
|
31
|
+
"msbuild",
|
|
32
|
+
"tsc",
|
|
33
|
+
"esbuild",
|
|
34
|
+
"vite",
|
|
35
|
+
"webpack",
|
|
36
|
+
"rollup",
|
|
37
|
+
"turbo",
|
|
38
|
+
"nx",
|
|
39
|
+
"bazel",
|
|
40
|
+
]);
|
|
41
|
+
export function assertAllowedCommand(command) {
|
|
42
|
+
// Extract the base command name (handle paths like /usr/bin/npm or C:\npm.cmd)
|
|
43
|
+
const base = command
|
|
44
|
+
.replace(/\\/g, "/")
|
|
45
|
+
.split("/")
|
|
46
|
+
.pop()
|
|
47
|
+
?.replace(/\.(cmd|exe|bat|sh)$/i, "") ?? "";
|
|
48
|
+
if (!ALLOWED_BUILD_COMMANDS.has(base)) {
|
|
49
|
+
throw new Error(`Command "${command}" is not in the allowed build commands list. ` +
|
|
50
|
+
`Allowed: ${[...ALLOWED_BUILD_COMMANDS].sort().join(", ")}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paretools/shared",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Shared utilities for
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Shared utilities for Pare MCP servers",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"mcp",
|
|
8
|
+
"mcp-server",
|
|
9
|
+
"model-context-protocol",
|
|
10
|
+
"structured-output"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/Dave-London/pare/tree/main/packages/shared",
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=18"
|
|
15
|
+
},
|
|
6
16
|
"type": "module",
|
|
7
17
|
"exports": {
|
|
8
18
|
".": {
|