obsidian-mcp-server 3.7.1 → 3.8.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/AGENTS.md +28 -13
- package/CLAUDE.md +28 -13
- package/Dockerfile +7 -0
- package/README.md +43 -99
- package/changelog/3.7.x/3.7.2.md +34 -0
- package/changelog/3.8.x/3.8.0.md +38 -0
- package/dist/config/server-config.d.ts +1 -0
- package/dist/config/server-config.d.ts.map +1 -1
- package/dist/config/server-config.js +24 -15
- package/dist/config/server-config.js.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts.map +1 -1
- package/dist/instructions.js +21 -7
- package/dist/instructions.js.map +1 -1
- package/dist/mcp-server/resources/definitions/index.d.ts +2 -2
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts +3 -2
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js +6 -5
- package/dist/mcp-server/resources/definitions/obsidian-tags.resource.js.map +1 -1
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.d.ts +2 -2
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.js +2 -2
- package/dist/mcp-server/resources/definitions/obsidian-vault-note.resource.js.map +1 -1
- package/dist/mcp-server/tools/definitions/_shared/markdown.d.ts +14 -0
- package/dist/mcp-server/tools/definitions/_shared/markdown.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/_shared/markdown.js +17 -0
- package/dist/mcp-server/tools/definitions/_shared/markdown.js.map +1 -0
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts +8 -1
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js +17 -6
- package/dist/mcp-server/tools/definitions/_shared/suggest-paths.js.map +1 -1
- package/dist/mcp-server/tools/definitions/index.d.ts +18 -18
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js +5 -4
- package/dist/mcp-server/tools/definitions/obsidian-append-to-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts +7 -6
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js +9 -8
- package/dist/mcp-server/tools/definitions/obsidian-delete-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.js +2 -1
- package/dist/mcp-server/tools/definitions/obsidian-execute-command.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js +38 -28
- package/dist/mcp-server/tools/definitions/obsidian-get-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.js +7 -4
- package/dist/mcp-server/tools/definitions/obsidian-list-commands.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts +6 -6
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js +15 -14
- package/dist/mcp-server/tools/definitions/obsidian-list-notes.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.d.ts +4 -4
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js +9 -8
- package/dist/mcp-server/tools/definitions/obsidian-list-tags.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js +9 -7
- package/dist/mcp-server/tools/definitions/obsidian-manage-frontmatter.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js +7 -5
- package/dist/mcp-server/tools/definitions/obsidian-manage-tags.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js +4 -3
- package/dist/mcp-server/tools/definitions/obsidian-open-in-ui.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js +5 -4
- package/dist/mcp-server/tools/definitions/obsidian-patch-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js +10 -4
- package/dist/mcp-server/tools/definitions/obsidian-replace-in-note.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts +46 -12
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js +93 -43
- package/dist/mcp-server/tools/definitions/obsidian-search-notes.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts +2 -2
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js +5 -4
- package/dist/mcp-server/tools/definitions/obsidian-write-note.tool.js.map +1 -1
- package/dist/services/obsidian/jsonlogic-evaluator.d.ts +33 -8
- package/dist/services/obsidian/jsonlogic-evaluator.d.ts.map +1 -1
- package/dist/services/obsidian/jsonlogic-evaluator.js +171 -32
- package/dist/services/obsidian/jsonlogic-evaluator.js.map +1 -1
- package/dist/services/obsidian/jsonlogic-worker.d.ts +6 -5
- package/dist/services/obsidian/jsonlogic-worker.d.ts.map +1 -1
- package/dist/services/obsidian/jsonlogic-worker.js +7 -5
- package/dist/services/obsidian/jsonlogic-worker.js.map +1 -1
- package/dist/services/obsidian/obsidian-service.d.ts +38 -22
- package/dist/services/obsidian/obsidian-service.d.ts.map +1 -1
- package/dist/services/obsidian/obsidian-service.js +293 -99
- package/dist/services/obsidian/obsidian-service.js.map +1 -1
- package/dist/services/obsidian/patch-instruction.d.ts +4 -1
- package/dist/services/obsidian/patch-instruction.d.ts.map +1 -1
- package/dist/services/obsidian/patch-instruction.js +5 -2
- package/dist/services/obsidian/patch-instruction.js.map +1 -1
- package/dist/services/obsidian/path-policy.d.ts +67 -23
- package/dist/services/obsidian/path-policy.d.ts.map +1 -1
- package/dist/services/obsidian/path-policy.js +183 -80
- package/dist/services/obsidian/path-policy.js.map +1 -1
- package/dist/services/obsidian/section-extractor.d.ts +6 -1
- package/dist/services/obsidian/section-extractor.d.ts.map +1 -1
- package/dist/services/obsidian/section-extractor.js +106 -11
- package/dist/services/obsidian/section-extractor.js.map +1 -1
- package/dist/services/obsidian/simple-search.d.ts +62 -0
- package/dist/services/obsidian/simple-search.d.ts.map +1 -0
- package/dist/services/obsidian/simple-search.js +264 -0
- package/dist/services/obsidian/simple-search.js.map +1 -0
- package/manifest.json +10 -2
- package/package.json +8 -7
- package/server.json +19 -7
|
@@ -1,46 +1,61 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Path-policy enforcement for the Obsidian Local REST API service.
|
|
3
|
-
* Single chokepoint for OBSIDIAN_READ_PATHS / OBSIDIAN_WRITE_PATHS /
|
|
4
|
-
* tools and resources call into the
|
|
5
|
-
*
|
|
3
|
+
* Single chokepoint for OBSIDIAN_READ_PATHS / OBSIDIAN_WRITE_PATHS /
|
|
4
|
+
* OBSIDIAN_DENY_PATHS / OBSIDIAN_READ_ONLY — tools and resources call into the
|
|
5
|
+
* service, the service consults this policy before every upstream HTTP call.
|
|
6
|
+
* See issues #40 and #76 for the spec.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
* are allowed; the LLM (or operator) can self-correct
|
|
8
|
+
* A denial outside the allowlists carries the active scope so error data
|
|
9
|
+
* echoes back which paths are allowed; the LLM (or operator) can self-correct
|
|
10
|
+
* without poking at logs. A denial by the deny list names no prefix: echoing
|
|
11
|
+
* the list would name exactly what it hides.
|
|
9
12
|
*
|
|
10
13
|
* @module services/obsidian/path-policy
|
|
11
14
|
*/
|
|
12
15
|
import { forbidden } from '@cyanheads/mcp-ts-core/errors';
|
|
13
16
|
/**
|
|
14
17
|
* Enforces folder-scoped read/write permissions on vault-relative paths.
|
|
15
|
-
* Constructed once from validated `ServerConfig`;
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
+
* Constructed once from validated `ServerConfig`; the path lists arrive
|
|
19
|
+
* already in `normalize()` form and deduplicated by the config parser, so
|
|
20
|
+
* matching is a straight prefix-or-equal compare. The deny list wins over
|
|
21
|
+
* every allow rule, write-implies-read included.
|
|
18
22
|
*/
|
|
19
23
|
export class PathPolicy {
|
|
20
24
|
#readPaths;
|
|
21
25
|
#writePaths;
|
|
26
|
+
#denyPaths;
|
|
22
27
|
#readOnly;
|
|
23
28
|
constructor(config) {
|
|
24
29
|
this.#readPaths = config.readPaths;
|
|
25
30
|
this.#writePaths = config.writePaths;
|
|
31
|
+
this.#denyPaths = config.denyPaths;
|
|
26
32
|
this.#readOnly = config.readOnly;
|
|
27
33
|
}
|
|
28
34
|
/** True when no path policy is active — every op falls through to the upstream. */
|
|
29
35
|
get isUnrestricted() {
|
|
30
|
-
return !this.#readOnly &&
|
|
36
|
+
return (!this.#readOnly &&
|
|
37
|
+
this.#readPaths === undefined &&
|
|
38
|
+
this.#writePaths === undefined &&
|
|
39
|
+
this.#denyPaths === undefined);
|
|
31
40
|
}
|
|
32
41
|
/**
|
|
33
|
-
* True when
|
|
34
|
-
* writes only, so they leave this false
|
|
42
|
+
* True when reads are gated: OBSIDIAN_READ_PATHS or OBSIDIAN_DENY_PATHS is
|
|
43
|
+
* set. Write paths and read-only gate writes only, so they leave this false
|
|
44
|
+
* and vault-wide reads stay vault-wide.
|
|
35
45
|
*/
|
|
36
46
|
get restrictsReads() {
|
|
37
|
-
return this.#readPaths !== undefined;
|
|
47
|
+
return this.#readPaths !== undefined || this.#denyPaths !== undefined;
|
|
38
48
|
}
|
|
39
|
-
/**
|
|
49
|
+
/** True when OBSIDIAN_DENY_PATHS withholds paths inside the scopes `effectiveScope` names. */
|
|
50
|
+
get withholdsPaths() {
|
|
51
|
+
return this.#denyPaths !== undefined;
|
|
52
|
+
}
|
|
53
|
+
/** Snapshot for startup-banner logging: the configured lists, `denyPaths` only when set. */
|
|
40
54
|
describe() {
|
|
41
55
|
return {
|
|
42
56
|
readPaths: this.#readPaths ?? 'full vault',
|
|
43
57
|
writePaths: this.#readOnly ? 'denied (read-only)' : (this.#writePaths ?? 'full vault'),
|
|
58
|
+
...(this.#denyPaths ? { denyPaths: this.#denyPaths } : {}),
|
|
44
59
|
readOnly: this.#readOnly,
|
|
45
60
|
};
|
|
46
61
|
}
|
|
@@ -48,41 +63,52 @@ export class PathPolicy {
|
|
|
48
63
|
get readOnlyShadowsWritePaths() {
|
|
49
64
|
return this.#readOnly && this.#writePaths !== undefined && this.#writePaths.length > 0;
|
|
50
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Configured allow prefixes a deny prefix covers: they grant nothing, so the
|
|
68
|
+
* startup log warns about each. Write paths are skipped under read-only,
|
|
69
|
+
* which ignores them already (`readOnlyShadowsWritePaths`).
|
|
70
|
+
*/
|
|
71
|
+
get denyShadowedPaths() {
|
|
72
|
+
const shadowed = (paths) => (paths ?? []).filter((prefix) => this.#isDenied(prefix));
|
|
73
|
+
return [
|
|
74
|
+
...shadowed(this.#readPaths).map((prefix) => ({
|
|
75
|
+
variable: 'OBSIDIAN_READ_PATHS',
|
|
76
|
+
prefix,
|
|
77
|
+
})),
|
|
78
|
+
...(this.#readOnly ? [] : shadowed(this.#writePaths)).map((prefix) => ({
|
|
79
|
+
variable: 'OBSIDIAN_WRITE_PATHS',
|
|
80
|
+
prefix,
|
|
81
|
+
})),
|
|
82
|
+
];
|
|
83
|
+
}
|
|
51
84
|
isReadable(path) {
|
|
52
85
|
const candidate = normalize(path);
|
|
53
|
-
|
|
54
|
-
return true;
|
|
55
|
-
if (matchesAny(candidate, this.#readPaths))
|
|
56
|
-
return true;
|
|
57
|
-
/** Write paths are implicitly readable — you can't sanely edit what you can't see. */
|
|
58
|
-
if (!this.#readOnly &&
|
|
59
|
-
this.#writePaths !== undefined &&
|
|
60
|
-
matchesAny(candidate, this.#writePaths)) {
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
86
|
+
return !this.#isDenied(candidate) && this.#inReadAllowlist(candidate);
|
|
64
87
|
}
|
|
65
88
|
isWritable(path) {
|
|
66
89
|
if (this.#readOnly)
|
|
67
90
|
return false;
|
|
68
91
|
const candidate = normalize(path);
|
|
69
|
-
|
|
70
|
-
return true;
|
|
71
|
-
return matchesAny(candidate, this.#writePaths);
|
|
92
|
+
return !this.#isDenied(candidate) && this.#inWriteAllowlist(candidate);
|
|
72
93
|
}
|
|
73
94
|
/**
|
|
74
95
|
* Throws `path_forbidden` if the path is not readable. `resolvedFrom` is the
|
|
75
96
|
* label of the `active` / `periodic` target `path` was resolved from; the
|
|
76
|
-
* denial names it in place of a path the caller never sent.
|
|
97
|
+
* denial names it in place of a path the caller never sent. A path outside
|
|
98
|
+
* the read allowlist is `outside_read_paths` whether or not it is also
|
|
99
|
+
* denied, so `denied_path` never confirms deny-list membership of a path the
|
|
100
|
+
* allowlist already refuses.
|
|
77
101
|
*/
|
|
78
102
|
assertReadable(path, resolvedFrom) {
|
|
79
103
|
if (this.isReadable(path))
|
|
80
104
|
return;
|
|
81
|
-
throw this.#deny(path, 'read',
|
|
105
|
+
throw this.#deny(path, 'read', this.#readSubreason(path), resolvedFrom);
|
|
82
106
|
}
|
|
83
107
|
/**
|
|
84
108
|
* Throws `path_forbidden` if the path is not writable (write tools also
|
|
85
109
|
* implicitly need read access). `resolvedFrom` as for `assertReadable`.
|
|
110
|
+
* Subreason precedence: `read_only_mode`, then `outside_write_paths`, then
|
|
111
|
+
* `denied_path`.
|
|
86
112
|
*/
|
|
87
113
|
assertWritable(path, resolvedFrom) {
|
|
88
114
|
if (this.#readOnly) {
|
|
@@ -90,7 +116,10 @@ export class PathPolicy {
|
|
|
90
116
|
}
|
|
91
117
|
if (this.isWritable(path))
|
|
92
118
|
return;
|
|
93
|
-
|
|
119
|
+
const subreason = this.#inWriteAllowlist(normalize(path))
|
|
120
|
+
? 'denied_path'
|
|
121
|
+
: 'outside_write_paths';
|
|
122
|
+
throw this.#deny(path, 'write', subreason, resolvedFrom);
|
|
94
123
|
}
|
|
95
124
|
/** Drop hits outside the read scope, silently. Used by `obsidian_search_notes`, `listTags`, and the scoped JSONLogic context fetch. */
|
|
96
125
|
filterReadable(hits) {
|
|
@@ -103,10 +132,13 @@ export class PathPolicy {
|
|
|
103
132
|
* when the op reaches the full vault, `[]` when it reaches nothing. Readable
|
|
104
133
|
* is the read paths plus the write paths (read paths alone under read-only).
|
|
105
134
|
* Writable is the write paths, or the readable scope when only reads are
|
|
106
|
-
* restricted, since a write also needs read access to its target.
|
|
135
|
+
* restricted, since a write also needs read access to its target. An allow
|
|
136
|
+
* prefix a deny prefix covers grants nothing and is left out; whatever else
|
|
137
|
+
* the deny list withholds inside these scopes, `withholdsPaths` flags
|
|
138
|
+
* without naming.
|
|
107
139
|
*/
|
|
108
140
|
effectiveScope() {
|
|
109
|
-
const readable = this
|
|
141
|
+
const readable = this.#readPaths !== undefined ? this.#scopeFor('read') : undefined;
|
|
110
142
|
let writable;
|
|
111
143
|
if (this.#readOnly)
|
|
112
144
|
writable = [];
|
|
@@ -121,23 +153,28 @@ export class PathPolicy {
|
|
|
121
153
|
* accepts, and possibly more: the `regexp` guard that narrows a scoped
|
|
122
154
|
* JSONLogic fetch upstream. It narrows the transfer only — callers still
|
|
123
155
|
* gate the results with `filterReadable`. See `prefixPattern` for how it
|
|
124
|
-
* stays a superset.
|
|
156
|
+
* stays a superset. With no read allowlist it matches every path; deny
|
|
157
|
+
* prefixes are never subtracted, since negating a superset would drop
|
|
158
|
+
* readable notes. When no allow prefix survives the deny list it matches
|
|
159
|
+
* no path — an empty alternation would match every one.
|
|
125
160
|
*/
|
|
126
161
|
readScopePattern() {
|
|
162
|
+
if (this.#readPaths === undefined)
|
|
163
|
+
return '^';
|
|
127
164
|
const alternatives = this.#scopeFor('read').map(prefixPattern);
|
|
165
|
+
if (alternatives.length === 0)
|
|
166
|
+
return '(?!)';
|
|
128
167
|
return `^[/\\\\]*(?:${alternatives.join('|')})`;
|
|
129
168
|
}
|
|
130
|
-
/** Keep the paths `isReadable` accepts — link targets and link text alike. */
|
|
131
|
-
keepReadable(paths) {
|
|
132
|
-
return paths.filter((p) => this.isReadable(p));
|
|
133
|
-
}
|
|
134
169
|
/**
|
|
135
170
|
* True when `path` is a folder on the way to the read scope: some readable
|
|
136
171
|
* prefix sits strictly below it, matched at segment boundaries and
|
|
137
172
|
* case-insensitively. The vault root is an ancestor whenever reads are
|
|
138
173
|
* restricted. Ancestors are listable so a nested scope can be browsed to;
|
|
139
174
|
* their names leak nothing the scope itself (in `instructions` and every
|
|
140
|
-
* `path_forbidden`) doesn't already state.
|
|
175
|
+
* `path_forbidden`) doesn't already state. An ancestor is never denied: a
|
|
176
|
+
* deny prefix covering it would cover the allow prefix below it too, and an
|
|
177
|
+
* allow prefix a deny prefix covers is not part of the scope.
|
|
141
178
|
*/
|
|
142
179
|
isScopeAncestor(path) {
|
|
143
180
|
if (!this.restrictsReads)
|
|
@@ -147,11 +184,14 @@ export class PathPolicy {
|
|
|
147
184
|
return true;
|
|
148
185
|
return this.#scopeFor('read').some((prefix) => prefix.startsWith(`${candidate}/`));
|
|
149
186
|
}
|
|
150
|
-
/**
|
|
187
|
+
/**
|
|
188
|
+
* Throws `path_forbidden` unless `dir` is readable or a folder on the way to
|
|
189
|
+
* the read scope, with the subreason `assertReadable` would give.
|
|
190
|
+
*/
|
|
151
191
|
assertListable(dir) {
|
|
152
192
|
if (this.isReadable(dir) || this.isScopeAncestor(dir))
|
|
153
193
|
return;
|
|
154
|
-
throw this.#deny(dir, 'read',
|
|
194
|
+
throw this.#deny(dir, 'read', this.#readSubreason(dir));
|
|
155
195
|
}
|
|
156
196
|
/**
|
|
157
197
|
* Keep the entries of a listing of `dir` a caller may see: readable entries,
|
|
@@ -167,57 +207,115 @@ export class PathPolicy {
|
|
|
167
207
|
return this.isReadable(path) || (isDir && this.isScopeAncestor(path));
|
|
168
208
|
});
|
|
169
209
|
}
|
|
170
|
-
/**
|
|
210
|
+
/**
|
|
211
|
+
* `data.path` and the message carry `resolvedFrom` when set, so a resolved
|
|
212
|
+
* path never reaches the caller. A `denied_path` denial carries no
|
|
213
|
+
* `activeScope`.
|
|
214
|
+
*/
|
|
171
215
|
#deny(path, op, subreason, resolvedFrom) {
|
|
216
|
+
const base = { reason: 'path_forbidden', path: resolvedFrom ?? path, op };
|
|
217
|
+
if (subreason === 'denied_path') {
|
|
218
|
+
const { message, recovery } = renderWithheld(path, resolvedFrom, op);
|
|
219
|
+
const data = { ...base, subreason, recovery: { hint: recovery } };
|
|
220
|
+
return forbidden(message, { ...data });
|
|
221
|
+
}
|
|
172
222
|
const activeScope = this.#scopeFor(op);
|
|
173
223
|
const { message, recovery } = renderDenial(path, resolvedFrom, op, subreason, activeScope);
|
|
174
224
|
const data = {
|
|
175
|
-
|
|
176
|
-
path: resolvedFrom ?? path,
|
|
177
|
-
op,
|
|
225
|
+
...base,
|
|
178
226
|
subreason,
|
|
179
227
|
activeScope,
|
|
180
228
|
recovery: { hint: recovery },
|
|
181
229
|
};
|
|
182
230
|
return forbidden(message, { ...data });
|
|
183
231
|
}
|
|
232
|
+
/** Allow prefixes for `op` that no deny prefix covers. */
|
|
184
233
|
#scopeFor(op) {
|
|
185
|
-
if (op === 'write') {
|
|
186
|
-
if (this.#readOnly)
|
|
187
|
-
return [];
|
|
188
|
-
return [...(this.#writePaths ?? [])];
|
|
189
|
-
}
|
|
190
234
|
const set = new Set();
|
|
191
|
-
if (this.#readPaths)
|
|
235
|
+
if (op === 'read' && this.#readPaths)
|
|
192
236
|
for (const p of this.#readPaths)
|
|
193
237
|
set.add(p);
|
|
194
238
|
if (!this.#readOnly && this.#writePaths)
|
|
195
239
|
for (const p of this.#writePaths)
|
|
196
240
|
set.add(p);
|
|
197
|
-
return [...set];
|
|
241
|
+
return [...set].filter((p) => !this.#isDenied(p));
|
|
242
|
+
}
|
|
243
|
+
#isDenied(candidate) {
|
|
244
|
+
return this.#denyPaths !== undefined && matchesAny(candidate, this.#denyPaths);
|
|
245
|
+
}
|
|
246
|
+
/** In the configured read allowlist: everywhere without one, write paths included unless read-only. */
|
|
247
|
+
#inReadAllowlist(candidate) {
|
|
248
|
+
if (this.#readPaths === undefined)
|
|
249
|
+
return true;
|
|
250
|
+
if (matchesAny(candidate, this.#readPaths))
|
|
251
|
+
return true;
|
|
252
|
+
/** Write paths are implicitly readable — you can't sanely edit what you can't see. */
|
|
253
|
+
return (!this.#readOnly && this.#writePaths !== undefined && matchesAny(candidate, this.#writePaths));
|
|
254
|
+
}
|
|
255
|
+
#inWriteAllowlist(candidate) {
|
|
256
|
+
return this.#writePaths === undefined || matchesAny(candidate, this.#writePaths);
|
|
257
|
+
}
|
|
258
|
+
/** The subreason for an unreadable path: `denied_path` only inside the read allowlist. */
|
|
259
|
+
#readSubreason(path) {
|
|
260
|
+
return this.#inReadAllowlist(normalize(path)) ? 'denied_path' : 'outside_read_paths';
|
|
198
261
|
}
|
|
199
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* A path in the form it reaches the plugin, which the config parser stores
|
|
265
|
+
* prefixes in: forward slashes (Windows-style `Public\sub\note.md` matches a
|
|
266
|
+
* `/`-configured prefix), no empty segments (`encodeVaultPath` drops them, so
|
|
267
|
+
* `a//b` is requested as `a/b`), lower case, NFC (the vault's filesystem
|
|
268
|
+
* resolves NFC and NFD spellings of a name to one file).
|
|
269
|
+
*/
|
|
200
270
|
function normalize(path) {
|
|
201
|
-
/**
|
|
202
|
-
* Match the parser's normalization rules so the candidate compares apples to
|
|
203
|
-
* apples against the configured prefixes. Backslashes collapse to forward
|
|
204
|
-
* slashes so Windows-style paths (`Public\sub\note.md`) match prefixes
|
|
205
|
-
* configured with `/` separators.
|
|
206
|
-
*/
|
|
207
271
|
return path
|
|
208
272
|
.replace(/\\/g, '/')
|
|
273
|
+
.replace(/\/{2,}/g, '/')
|
|
209
274
|
.replace(/^\/+|\/+$/g, '')
|
|
210
|
-
.toLowerCase()
|
|
275
|
+
.toLowerCase()
|
|
276
|
+
.normalize('NFC');
|
|
211
277
|
}
|
|
212
|
-
/** A single character that `
|
|
278
|
+
/** A single character that `foldedChar` escapes with a backslash to match it literally. */
|
|
213
279
|
const REGEX_METACHARACTER = /^[\\^$.*+?()[\]{}|-]$/;
|
|
214
280
|
/**
|
|
215
|
-
* A regex source
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
281
|
+
* A regex source for one ASCII character of lower-cased text, matching every
|
|
282
|
+
* character whose lower case is or starts with it. Built for the Local REST
|
|
283
|
+
* API's `regexp` operator, which compiles with no flags, so case is folded by
|
|
284
|
+
* hand: each ASCII letter matches either case, plus the two non-ASCII
|
|
219
285
|
* characters whose lower case starts with an ASCII letter (U+212A KELVIN SIGN
|
|
220
|
-
* → `k`, U+0130 → `i` + U+0307).
|
|
286
|
+
* → `k`, U+0130 → `i` + U+0307). No other character lower-cases to ASCII.
|
|
287
|
+
*/
|
|
288
|
+
function foldedChar(char) {
|
|
289
|
+
if (char === 'i')
|
|
290
|
+
return '[iIİ]';
|
|
291
|
+
if (char === 'k')
|
|
292
|
+
return '[kKK]';
|
|
293
|
+
if (char >= 'a' && char <= 'z')
|
|
294
|
+
return `[${char}${char.toUpperCase()}]`;
|
|
295
|
+
if (REGEX_METACHARACTER.test(char))
|
|
296
|
+
return `\\${char}`;
|
|
297
|
+
return char;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* A regex source matching every string whose `toLowerCase()` contains `text`,
|
|
301
|
+
* and possibly more, for lower-cased `text` (`foldedChar`). `undefined` when
|
|
302
|
+
* `text` holds a non-ASCII character, whose case folds include
|
|
303
|
+
* multi-character forms. The scoped text search's candidate guard builds one
|
|
304
|
+
* per query token.
|
|
305
|
+
*/
|
|
306
|
+
export function caseFoldedPattern(text) {
|
|
307
|
+
let pattern = '';
|
|
308
|
+
for (const char of text) {
|
|
309
|
+
if (char > '\u007f')
|
|
310
|
+
return;
|
|
311
|
+
pattern += foldedChar(char);
|
|
312
|
+
}
|
|
313
|
+
return pattern;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* A regex source matching every path whose `normalize()` form equals `prefix`
|
|
317
|
+
* or sits under it, as `matchesAny` decides, and possibly more. Case is
|
|
318
|
+
* folded by `foldedChar`, and a slash matches either slash, since
|
|
221
319
|
* `normalize()` turns backslashes into slashes. The first non-ASCII character
|
|
222
320
|
* ends the pattern with no boundary: its case folds include multi-character
|
|
223
321
|
* forms, so anything is accepted from there on rather than risk rejecting a
|
|
@@ -228,18 +326,7 @@ function prefixPattern(prefix) {
|
|
|
228
326
|
for (const char of prefix) {
|
|
229
327
|
if (char > '\u007f')
|
|
230
328
|
return pattern;
|
|
231
|
-
|
|
232
|
-
pattern += '[iIİ]';
|
|
233
|
-
else if (char === 'k')
|
|
234
|
-
pattern += '[kKK]';
|
|
235
|
-
else if (char >= 'a' && char <= 'z')
|
|
236
|
-
pattern += `[${char}${char.toUpperCase()}]`;
|
|
237
|
-
else if (char === '/')
|
|
238
|
-
pattern += '[/\\\\]';
|
|
239
|
-
else if (REGEX_METACHARACTER.test(char))
|
|
240
|
-
pattern += `\\${char}`;
|
|
241
|
-
else
|
|
242
|
-
pattern += char;
|
|
329
|
+
pattern += char === '/' ? '[/\\\\]' : foldedChar(char);
|
|
243
330
|
}
|
|
244
331
|
return `${pattern}(?:[/\\\\]|$)`;
|
|
245
332
|
}
|
|
@@ -260,10 +347,7 @@ function matchesAny(candidate, prefixes) {
|
|
|
260
347
|
* on a path resolved from a target names the target (`resolvedFrom`) instead.
|
|
261
348
|
*/
|
|
262
349
|
function renderDenial(path, resolvedFrom, op, subreason, activeScope) {
|
|
263
|
-
const
|
|
264
|
-
const subject = resolvedFrom === undefined
|
|
265
|
-
? `Path '${path}' is not ${opLabel}`
|
|
266
|
-
: `Target '${resolvedFrom}' resolves to a note that is not ${opLabel}`;
|
|
350
|
+
const subject = denialSubject(path, resolvedFrom, op);
|
|
267
351
|
if (subreason === 'read_only_mode') {
|
|
268
352
|
return {
|
|
269
353
|
message: `${subject}: server is in read-only mode (OBSIDIAN_READ_ONLY=true).`,
|
|
@@ -280,4 +364,23 @@ function renderDenial(path, resolvedFrom, op, subreason, activeScope) {
|
|
|
280
364
|
recovery: `Allowed prefixes: [${scopeRender}]. ${fix}`,
|
|
281
365
|
};
|
|
282
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* `renderDenial` for `denied_path`: names the variable, never a deny prefix.
|
|
369
|
+
* The message names the subreason too, since `content[]` renders the message
|
|
370
|
+
* and recovery but not `data.subreason`.
|
|
371
|
+
*/
|
|
372
|
+
function renderWithheld(path, resolvedFrom, op) {
|
|
373
|
+
return {
|
|
374
|
+
message: `${denialSubject(path, resolvedFrom, op)}: withheld by OBSIDIAN_DENY_PATHS (subreason denied_path).`,
|
|
375
|
+
recovery: resolvedFrom === undefined
|
|
376
|
+
? 'The operator withholds this path. Use a different path, or ask the operator to change OBSIDIAN_DENY_PATHS.'
|
|
377
|
+
: 'The operator withholds the note this target resolves to. Pass a path target to a different note, or ask the operator to change OBSIDIAN_DENY_PATHS.',
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function denialSubject(path, resolvedFrom, op) {
|
|
381
|
+
const opLabel = op === 'write' ? 'writable' : 'readable';
|
|
382
|
+
return resolvedFrom === undefined
|
|
383
|
+
? `Path '${path}' is not ${opLabel}`
|
|
384
|
+
: `Target '${resolvedFrom}' resolves to a note that is not ${opLabel}`;
|
|
385
|
+
}
|
|
283
386
|
//# sourceMappingURL=path-policy.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path-policy.js","sourceRoot":"","sources":["../../../src/services/obsidian/path-policy.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"path-policy.js","sourceRoot":"","sources":["../../../src/services/obsidian/path-policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -99,7 +99,12 @@ export declare function sectionBody(content: string, path: string): SectionBody
|
|
|
99
99
|
* The 1.x engine lexes the frontmatter-stripped body with its line endings
|
|
100
100
|
* normalized, then applies the token offsets to the note's own bytes unmapped;
|
|
101
101
|
* these offsets are computed the same way, so on a CRLF note they drift as the
|
|
102
|
-
* engine's do.
|
|
102
|
+
* engine's do. They also sum the kept tokens alone, as the engine does, so
|
|
103
|
+
* after a definition `marked` drops (a repeated label) they fall short by its
|
|
104
|
+
* length as the engine's do. A label first defined inside a list item, or in
|
|
105
|
+
* a blockquote the scan leaves unlexed, is the exception: the scan keeps the
|
|
106
|
+
* later definition the engine drops, so the span there stays on the note's
|
|
107
|
+
* headings.
|
|
103
108
|
*/
|
|
104
109
|
export declare function v1HeadingSpan(content: string, path: string): {
|
|
105
110
|
end: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section-extractor.d.ts","sourceRoot":"","sources":["../../../src/services/obsidian/section-extractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAK1D,sFAAsF;AACtF,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,GAAG,iBAAiB,CASxF;AAYD,kFAAkF;AAClF,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,sFAAsF;IACtF,KAAK,EAAE,MAAM,CAAC;IACd,iHAAiH;IACjH,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAaD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CAMvD;AA6BD,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAoBlF;AAED
|
|
1
|
+
{"version":3,"file":"section-extractor.d.ts","sourceRoot":"","sources":["../../../src/services/obsidian/section-extractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAK1D,sFAAsF;AACtF,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,GAAG,iBAAiB,CASxF;AAYD,kFAAkF;AAClF,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,sFAAsF;IACtF,KAAK,EAAE,MAAM,CAAC;IACd,iHAAiH;IACjH,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAaD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CAMvD;AA6BD,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAoBlF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,GACX;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAiB5C;AAED,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAIrD;AAoPD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAW/F;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAa3E;AAED,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;CACf;AAED,2FAA2F;AAC3F,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAKD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CActE;AAmJD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,SAAI,GAAG,OAAO,EAAE,CA0BrE"}
|