omitly-mcp 0.1.1 → 0.1.3
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/dist/paths.js +34 -4
- package/dist/paths.js.map +1 -1
- package/package.json +1 -1
- package/wasm/leakcheck_wasm_bg.wasm +0 -0
package/dist/paths.js
CHANGED
|
@@ -9,13 +9,28 @@
|
|
|
9
9
|
* otherwise — the directory the server was started in
|
|
10
10
|
*
|
|
11
11
|
* Checks resolve symlinks (realpath) before comparing, so a link inside the
|
|
12
|
-
* root that points outside it is rejected
|
|
13
|
-
*
|
|
12
|
+
* root that points outside it is rejected; an output path that IS a symlink is
|
|
13
|
+
* refused outright (only its parent can be realpath'd, so following it would
|
|
14
|
+
* escape — #661); and outputs must not clobber existing names, dangling
|
|
15
|
+
* symlinks included — the model is told to pick a fresh name instead. These are
|
|
14
16
|
* guardrails against confused-deputy mistakes, not a sandbox for a hostile
|
|
15
17
|
* local user (they own the machine; see docs/THREAT-MODEL.md).
|
|
16
18
|
*/
|
|
17
|
-
import {
|
|
19
|
+
import { lstatSync, realpathSync } from "node:fs";
|
|
18
20
|
import * as path from "node:path";
|
|
21
|
+
/** Is there a directory ENTRY at `p`? Deliberately `lstat`, not `existsSync`:
|
|
22
|
+
* `existsSync` follows symlinks, so a DANGLING link (a name that is very much
|
|
23
|
+
* taken, pointing at a target that does not exist yet) reports as absent.
|
|
24
|
+
* `lstat` describes the link itself, so an occupied name is seen as occupied
|
|
25
|
+
* whether or not its target resolves (#661). */
|
|
26
|
+
function entryExists(p) {
|
|
27
|
+
return lstatSync(p, { throwIfNoEntry: false }) !== undefined;
|
|
28
|
+
}
|
|
29
|
+
/** Is `p` itself a symlink? `lstat`, so the link is described rather than
|
|
30
|
+
* followed — true for a dangling link too. */
|
|
31
|
+
function isSymlink(p) {
|
|
32
|
+
return lstatSync(p, { throwIfNoEntry: false })?.isSymbolicLink() === true;
|
|
33
|
+
}
|
|
19
34
|
/** The confinement root, resolved once at startup. */
|
|
20
35
|
export function allowedRoot() {
|
|
21
36
|
const configured = process.env.OMITLY_ALLOWED_DIR?.trim();
|
|
@@ -87,8 +102,23 @@ export function confineOutput(p, root) {
|
|
|
87
102
|
const real = path.join(parent, path.basename(resolved));
|
|
88
103
|
if (!within(root, real))
|
|
89
104
|
throw refusal("output path", p, root);
|
|
105
|
+
// The parent is confirmed in-root, but the FINAL COMPONENT may itself be a
|
|
106
|
+
// symlink that escapes it — and only the parent went through realpathSync.
|
|
107
|
+
// A write through the returned path follows that link, so the escape has to
|
|
108
|
+
// be refused here rather than left to the no-clobber check below: that check
|
|
109
|
+
// used to be `existsSync`, which follows links and therefore reports a
|
|
110
|
+
// DANGLING one as absent, seeing neither the escape nor the taken name. The
|
|
111
|
+
// result was an in-root path that wrote outside the root (#661).
|
|
112
|
+
//
|
|
113
|
+
// Any symlink is refused, not just an escaping one: an output target is
|
|
114
|
+
// supposed to be a fresh name the model picked, resolving a link to decide
|
|
115
|
+
// re-opens the same follow-the-link question one level down, and the model
|
|
116
|
+
// can always pick another name.
|
|
117
|
+
if (isSymlink(real)) {
|
|
118
|
+
throw new Error(`refusing to write through a symlink: ${real}. Choose a new output name.`);
|
|
119
|
+
}
|
|
90
120
|
for (const candidate of [real, `${real}.audit.json`]) {
|
|
91
|
-
if (
|
|
121
|
+
if (entryExists(candidate)) {
|
|
92
122
|
throw new Error(`refusing to overwrite existing file: ${candidate}. Choose a new output name.`);
|
|
93
123
|
}
|
|
94
124
|
}
|
package/dist/paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omitly-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Model Context Protocol server exposing Omitly's local, verifiable PDF redaction to AI agents. Redaction runs on-device — documents are never uploaded.",
|
|
6
6
|
"keywords": [
|
|
Binary file
|