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 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, and outputs must not clobber
13
- * existing files — the model is told to pick a fresh name instead. These are
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 { existsSync, realpathSync } from "node:fs";
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 (existsSync(candidate)) {
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":"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"}
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.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