@dollhousemcp/mcp-server 2.0.36 → 2.0.38
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/CHANGELOG.md +10 -0
- package/dist/cli/convert.js +20 -6
- package/dist/elements/templates/Template.d.ts +2 -0
- package/dist/elements/templates/Template.d.ts.map +1 -1
- package/dist/elements/templates/Template.js +9 -1
- package/dist/generated/version.d.ts +2 -2
- package/dist/generated/version.js +3 -3
- package/dist/handlers/element-crud/editElement.d.ts.map +1 -1
- package/dist/handlers/element-crud/editElement.js +24 -11
- package/dist/security/contentValidator.d.ts.map +1 -1
- package/dist/security/contentValidator.js +6 -3
- package/dist/utils/pathSecurity.d.ts +46 -1
- package/dist/utils/pathSecurity.d.ts.map +1 -1
- package/dist/utils/pathSecurity.js +172 -2
- package/dist/web/routes/setupRoutes.d.ts +23 -7
- package/dist/web/routes/setupRoutes.d.ts.map +1 -1
- package/dist/web/routes/setupRoutes.js +138 -20
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -1,8 +1,97 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
1
2
|
import * as path from 'node:path';
|
|
3
|
+
function isMissingPathError(error) {
|
|
4
|
+
const code = typeof error === 'object' && error !== null && 'code' in error
|
|
5
|
+
? error.code
|
|
6
|
+
: undefined;
|
|
7
|
+
return code === 'ENOENT' || code === 'ENOTDIR';
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* lstat of the deepest ancestor of `startPath` that exists, or null when
|
|
11
|
+
* nothing up to the filesystem root exists. Used to vet paths that will be
|
|
12
|
+
* created later: their containment depends on the directory they will be
|
|
13
|
+
* created inside.
|
|
14
|
+
*
|
|
15
|
+
* KNOWN LIMIT (#2344): this stops at the first ancestor lstat can see, and
|
|
16
|
+
* lstat resolves intermediate symlinks — so a symlink whose target already
|
|
17
|
+
* contains a matching subdirectory is reported as a plain directory and the
|
|
18
|
+
* symlink component itself is never inspected. Walking every lexical ancestor
|
|
19
|
+
* instead is not an option: system symlinks (macOS /tmp -> /private/tmp,
|
|
20
|
+
* /var -> /private/var) sit above every temp path and would false-positive.
|
|
21
|
+
* Full protection needs a caller-known boundary, which is exactly what
|
|
22
|
+
* vetOutputBase() provides — see the contract note on resolvePathWithinBase.
|
|
23
|
+
*/
|
|
24
|
+
function nearestExistingAncestorStats(startPath) {
|
|
25
|
+
let previous = startPath;
|
|
26
|
+
let current = path.dirname(startPath);
|
|
27
|
+
while (current !== previous) {
|
|
28
|
+
try {
|
|
29
|
+
return fs.lstatSync(current);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (!isMissingPathError(error)) {
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
previous = current;
|
|
37
|
+
current = path.dirname(current);
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
function assertNotSymlink(pathToCheck, message) {
|
|
42
|
+
let stats;
|
|
43
|
+
try {
|
|
44
|
+
stats = fs.lstatSync(pathToCheck);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (isMissingPathError(error)) {
|
|
48
|
+
// The path does not exist yet, so it will be created by a later
|
|
49
|
+
// mkdir/write. That is only safe if the directory it gets created
|
|
50
|
+
// inside is real: a symlinked existing ancestor would redirect the
|
|
51
|
+
// recursive create outside the containment boundary (#2342).
|
|
52
|
+
const ancestorStats = nearestExistingAncestorStats(pathToCheck);
|
|
53
|
+
if (ancestorStats?.isSymbolicLink()) {
|
|
54
|
+
throw new Error(message);
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
if (stats.isSymbolicLink()) {
|
|
61
|
+
throw new Error(message);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function assertNoSymlinkedDescendants(resolvedBase, resolvedTarget) {
|
|
65
|
+
const relativePath = path.relative(resolvedBase, resolvedTarget);
|
|
66
|
+
if (!relativePath) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
let currentPath = resolvedBase;
|
|
70
|
+
for (const segment of relativePath.split(path.sep)) {
|
|
71
|
+
if (!segment) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
currentPath = path.join(currentPath, segment);
|
|
75
|
+
assertNotSymlink(currentPath, 'Path segment resolves through a symbolic link');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
2
78
|
/**
|
|
3
79
|
* Resolve a target path and verify that it remains inside the intended base
|
|
4
80
|
* directory. This is separator-aware, so sibling paths such as `/tmp/base2`
|
|
5
|
-
* are not treated as children of `/tmp/base`.
|
|
81
|
+
* are not treated as children of `/tmp/base`. Existing symlinked base paths
|
|
82
|
+
* and symlinked descendants under the base are rejected because writes would
|
|
83
|
+
* follow them outside the lexical containment boundary. A base that does not
|
|
84
|
+
* exist yet is vetted through its nearest existing ancestor: if that ancestor
|
|
85
|
+
* is a symlink, the recursive mkdir/write that follows would be redirected
|
|
86
|
+
* outside the intended tree, so it is rejected too (#2342).
|
|
87
|
+
*
|
|
88
|
+
* CONTRACT (#2344): the base's own ancestry can only be best-effort vetted
|
|
89
|
+
* here — a symlink whose target pre-contains matching subdirectories is not
|
|
90
|
+
* detectable without a caller-known boundary (see nearestExistingAncestorStats
|
|
91
|
+
* for why walking every ancestor false-positives on system symlinks). Do NOT
|
|
92
|
+
* pass user-controlled base paths directly to this function: vet them first
|
|
93
|
+
* with vetOutputBase(), which contains them canonically against an anchor and
|
|
94
|
+
* returns the canonical base to use here. Every current caller does this.
|
|
6
95
|
*/
|
|
7
96
|
export function resolvePathWithinBase(baseDir, ...segments) {
|
|
8
97
|
if (!baseDir || typeof baseDir !== 'string') {
|
|
@@ -23,8 +112,89 @@ export function resolvePathWithinBase(baseDir, ...segments) {
|
|
|
23
112
|
|| relativePath.startsWith('..' + path.sep)
|
|
24
113
|
|| path.isAbsolute(relativePath);
|
|
25
114
|
if (!isTraversal) {
|
|
115
|
+
assertNotSymlink(resolvedBase, 'Base directory resolves through a symbolic link');
|
|
116
|
+
assertNoSymlinkedDescendants(resolvedBase, resolvedTarget);
|
|
26
117
|
return resolvedTarget;
|
|
27
118
|
}
|
|
28
119
|
throw new Error('Resolved path escapes the base directory');
|
|
29
120
|
}
|
|
30
|
-
|
|
121
|
+
/** True when a base-relative path points outside the base. */
|
|
122
|
+
function escapesBase(relativePath) {
|
|
123
|
+
return relativePath === '..'
|
|
124
|
+
|| relativePath.startsWith('..' + path.sep)
|
|
125
|
+
|| path.isAbsolute(relativePath);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Canonical (realpath) form of a path that may not fully exist yet: the
|
|
129
|
+
* deepest existing ancestor is resolved through every symlink, and the
|
|
130
|
+
* not-yet-created suffix is appended unchanged. This answers "where would a
|
|
131
|
+
* recursive mkdir/write on this path REALLY land?" — which per-component
|
|
132
|
+
* lstat checks cannot, because lstat resolves intermediate symlinks and only
|
|
133
|
+
* reports on the final component (#2344).
|
|
134
|
+
*/
|
|
135
|
+
export function canonicalizePath(inputPath) {
|
|
136
|
+
const resolved = path.resolve(inputPath);
|
|
137
|
+
const missing = [];
|
|
138
|
+
let current = resolved;
|
|
139
|
+
for (;;) {
|
|
140
|
+
try {
|
|
141
|
+
const canonical = fs.realpathSync(current);
|
|
142
|
+
return missing.length ? path.join(canonical, ...missing) : canonical;
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
if (!isMissingPathError(error)) {
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const parent = path.dirname(current);
|
|
150
|
+
if (parent === current) {
|
|
151
|
+
// Nothing on the chain exists (should not happen for absolute paths —
|
|
152
|
+
// the root exists — but keep a lexical fallback rather than throwing).
|
|
153
|
+
return missing.length ? path.join(current, ...missing) : current;
|
|
154
|
+
}
|
|
155
|
+
missing.unshift(path.basename(current));
|
|
156
|
+
current = parent;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Vets a user-supplied output base for CLI writes and returns the canonical
|
|
161
|
+
* path all subsequent writes should use (#2344).
|
|
162
|
+
*
|
|
163
|
+
* Two regimes, split by what the user lexically named:
|
|
164
|
+
*
|
|
165
|
+
* - Base inside the anchor (relative outputs like the './anthropic-skills'
|
|
166
|
+
* default): the canonical base must stay inside the canonical anchor.
|
|
167
|
+
* A symlink that redirects it elsewhere — including one whose target
|
|
168
|
+
* already contains matching subdirectories, the case per-component lstat
|
|
169
|
+
* checks miss — is rejected. Comparing in canonical space means system
|
|
170
|
+
* links above the anchor (macOS /tmp -> /private/tmp) never false-positive.
|
|
171
|
+
*
|
|
172
|
+
* - Base outside the anchor (explicit absolute or ../ outputs): the user
|
|
173
|
+
* named that destination, so there is no boundary to defend. Instead of
|
|
174
|
+
* guessing intent, the real destination is disclosed via `onDisclose`
|
|
175
|
+
* whenever it differs from the lexical path, and the canonical path is
|
|
176
|
+
* returned so what was vetted is what gets written.
|
|
177
|
+
*/
|
|
178
|
+
export function vetOutputBase(baseDir, options) {
|
|
179
|
+
if (!baseDir || typeof baseDir !== 'string') {
|
|
180
|
+
throw new TypeError('Base directory must be a non-empty string');
|
|
181
|
+
}
|
|
182
|
+
if (baseDir.includes('\0')) {
|
|
183
|
+
throw new Error('Path segment contains a null byte');
|
|
184
|
+
}
|
|
185
|
+
const resolvedAnchor = path.resolve(options?.anchor ?? process.cwd());
|
|
186
|
+
const resolvedBase = path.resolve(baseDir);
|
|
187
|
+
const canonicalBase = canonicalizePath(resolvedBase);
|
|
188
|
+
if (!escapesBase(path.relative(resolvedAnchor, resolvedBase))) {
|
|
189
|
+
const canonicalAnchor = canonicalizePath(resolvedAnchor);
|
|
190
|
+
if (escapesBase(path.relative(canonicalAnchor, canonicalBase))) {
|
|
191
|
+
throw new Error(`Output path resolves outside the working directory through a symbolic link (real destination: ${canonicalBase})`);
|
|
192
|
+
}
|
|
193
|
+
return canonicalBase;
|
|
194
|
+
}
|
|
195
|
+
if (canonicalBase !== resolvedBase) {
|
|
196
|
+
options?.onDisclose?.(canonicalBase);
|
|
197
|
+
}
|
|
198
|
+
return canonicalBase;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGF0aFNlY3VyaXR5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL3BhdGhTZWN1cml0eS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUM5QixPQUFPLEtBQUssSUFBSSxNQUFNLFdBQVcsQ0FBQztBQUVsQyxTQUFTLGtCQUFrQixDQUFDLEtBQWM7SUFDeEMsTUFBTSxJQUFJLEdBQUcsT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksTUFBTSxJQUFJLEtBQUs7UUFDekUsQ0FBQyxDQUFFLEtBQStCLENBQUMsSUFBSTtRQUN2QyxDQUFDLENBQUMsU0FBUyxDQUFDO0lBQ2QsT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLElBQUksS0FBSyxTQUFTLENBQUM7QUFDakQsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7OztHQWNHO0FBQ0gsU0FBUyw0QkFBNEIsQ0FBQyxTQUFpQjtJQUNyRCxJQUFJLFFBQVEsR0FBRyxTQUFTLENBQUM7SUFDekIsSUFBSSxPQUFPLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQztJQUN0QyxPQUFPLE9BQU8sS0FBSyxRQUFRLEVBQUUsQ0FBQztRQUM1QixJQUFJLENBQUM7WUFDSCxPQUFPLEVBQUUsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDL0IsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixJQUFJLENBQUMsa0JBQWtCLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztnQkFDL0IsTUFBTSxLQUFLLENBQUM7WUFDZCxDQUFDO1FBQ0gsQ0FBQztRQUNELFFBQVEsR0FBRyxPQUFPLENBQUM7UUFDbkIsT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDbEMsQ0FBQztJQUNELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztB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QyxRQUFRLENBQUMsZUFBZSxFQUFFLGFBQWEsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUMvRCxNQUFNLElBQUksS0FBSyxDQUNiLGlHQUFpRyxhQUFhLEdBQUcsQ0FDbEgsQ0FBQztRQUNKLENBQUM7UUFDRCxPQUFPLGFBQWEsQ0FBQztJQUN2QixDQUFDO0lBRUQsSUFBSSxhQUFhLEtBQUssWUFBWSxFQUFFLENBQUM7UUFDbkMsT0FBTyxFQUFFLFVBQVUsRUFBRSxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ3ZDLENBQUM7SUFDRCxPQUFPLGFBQWEsQ0FBQztBQUN2QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgZnMgZnJvbSAnbm9kZTpmcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ25vZGU6cGF0aCc7XG5cbmZ1bmN0aW9uIGlzTWlzc2luZ1BhdGhFcnJvcihlcnJvcjogdW5rbm93bik6IGJvb2xlYW4ge1xuICBjb25zdCBjb2RlID0gdHlwZW9mIGVycm9yID09PSAnb2JqZWN0JyAmJiBlcnJvciAhPT0gbnVsbCAmJiAnY29kZScgaW4gZXJyb3JcbiAgICA/IChlcnJvciBhcyBOb2RlSlMuRXJybm9FeGNlcHRpb24pLmNvZGVcbiAgICA6IHVuZGVmaW5lZDtcbiAgcmV0dXJuIGNvZGUgPT09ICdFTk9FTlQnIHx8IGNvZGUgPT09ICdFTk9URElSJztcbn1cblxuLyoqXG4gKiBsc3RhdCBvZiB0aGUgZGVlcGVzdCBhbmNlc3RvciBvZiBgc3RhcnRQYXRoYCB0aGF0IGV4aXN0cywgb3IgbnVsbCB3aGVuXG4gKiBub3RoaW5nIHVwIHRvIHRoZSBmaWxlc3lzdGVtIHJvb3QgZXhpc3RzLiBVc2VkIHRvIHZldCBwYXRocyB0aGF0IHdpbGwgYmVcbiAqIGNyZWF0ZWQgbGF0ZXI6IHRoZWlyIGNvbnRhaW5tZW50IGRlcGVuZHMgb24gdGhlIGRpcmVjdG9yeSB0aGV5IHdpbGwgYmVcbiAqIGNyZWF0ZWQgaW5zaWRlLlxuICpcbiAqIEtOT1dOIExJTUlUICgjMjM0NCk6IHRoaXMgc3RvcHMgYXQgdGhlIGZpcnN0IGFuY2VzdG9yIGxzdGF0IGNhbiBzZWUsIGFuZFxuICogbHN0YXQgcmVzb2x2ZXMgaW50ZXJtZWRpYXRlIHN5bWxpbmtzIOKAlCBzbyBhIHN5bWxpbmsgd2hvc2UgdGFyZ2V0IGFscmVhZHlcbiAqIGNvbnRhaW5zIGEgbWF0Y2hpbmcgc3ViZGlyZWN0b3J5IGlzIHJlcG9ydGVkIGFzIGEgcGxhaW4gZGlyZWN0b3J5IGFuZCB0aGVcbiAqIHN5bWxpbmsgY29tcG9uZW50IGl0c2VsZiBpcyBuZXZlciBpbnNwZWN0ZWQuIFdhbGtpbmcgZXZlcnkgbGV4aWNhbCBhbmNlc3RvclxuICogaW5zdGVhZCBpcyBub3QgYW4gb3B0aW9uOiBzeXN0ZW0gc3ltbGlua3MgKG1hY09TIC90bXAgLT4gL3ByaXZhdGUvdG1wLFxuICogL3ZhciAtPiAvcHJpdmF0ZS92YXIpIHNpdCBhYm92ZSBldmVyeSB0ZW1wIHBhdGggYW5kIHdvdWxkIGZhbHNlLXBvc2l0aXZlLlxuICogRnVsbCBwcm90ZWN0aW9uIG5lZWRzIGEgY2FsbGVyLWtub3duIGJvdW5kYXJ5LCB3aGljaCBpcyBleGFjdGx5IHdoYXRcbiAqIHZldE91dHB1dEJhc2UoKSBwcm92aWRlcyDigJQgc2VlIHRoZSBjb250cmFjdCBub3RlIG9uIHJlc29sdmVQYXRoV2l0aGluQmFzZS5cbiAqL1xuZnVuY3Rpb24gbmVhcmVzdEV4aXN0aW5nQW5jZXN0b3JTdGF0cyhzdGFydFBhdGg6IHN0cmluZyk6IGZzLlN0YXRzIHwgbnVsbCB7XG4gIGxldCBwcmV2aW91cyA9IHN0YXJ0UGF0aDtcbiAgbGV0IGN1cnJlbnQgPSBwYXRoLmRpcm5hbWUoc3RhcnRQYXRoKTtcbiAgd2hpbGUgKGN1cnJlbnQgIT09IHByZXZpb3VzKSB7XG4gICAgdHJ5IHtcbiAgICAgIHJldHVybiBmcy5sc3RhdFN5bmMoY3VycmVudCk7XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmICghaXNNaXNzaW5nUGF0aEVycm9yKGVycm9yKSkge1xuICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgIH1cbiAgICB9XG4gICAgcHJldmlvdXMgPSBjdXJyZW50O1xuICAgIGN1cnJlbnQgPSBwYXRoLmRpcm5hbWUoY3VycmVudCk7XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59XG5cbmZ1bmN0aW9uIGFzc2VydE5vdFN5bWxpbmsocGF0aFRvQ2hlY2s6IHN0cmluZywgbWVzc2FnZTogc3RyaW5nKTogdm9pZCB7XG4gIGxldCBzdGF0czogZnMuU3RhdHM7XG4gIHRyeSB7XG4gICAgc3RhdHMgPSBmcy5sc3RhdFN5bmMocGF0aFRvQ2hlY2spO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIGlmIChpc01pc3NpbmdQYXRoRXJyb3IoZXJyb3IpKSB7XG4gICAgICAvLyBUaGUgcGF0aCBkb2VzIG5vdCBleGlzdCB5ZXQsIHNvIGl0IHdpbGwgYmUgY3JlYXRlZCBieSBhIGxhdGVyXG4gICAgICAvLyBta2Rpci93cml0ZS4gVGhhdCBpcyBvbmx5IHNhZmUgaWYgdGhlIGRpcmVjdG9yeSBpdCBnZXRzIGNyZWF0ZWRcbiAgICAgIC8vIGluc2lkZSBpcyByZWFsOiBhIHN5bWxpbmtlZCBleGlzdGluZyBhbmNlc3RvciB3b3VsZCByZWRpcmVjdCB0aGVcbiAgICAgIC8vIHJlY3Vyc2l2ZSBjcmVhdGUgb3V0c2lkZSB0aGUgY29udGFpbm1lbnQgYm91bmRhcnkgKCMyMzQyKS5cbiAgICAgIGNvbnN0IGFuY2VzdG9yU3RhdHMgPSBuZWFyZXN0RXhpc3RpbmdBbmNlc3RvclN0YXRzKHBhdGhUb0NoZWNrKTtcbiAgICAgIGlmIChhbmNlc3RvclN0YXRzPy5pc1N5bWJvbGljTGluaygpKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihtZXNzYWdlKTtcbiAgICAgIH1cbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdGhyb3cgZXJyb3I7XG4gIH1cbiAgaWYgKHN0YXRzLmlzU3ltYm9saWNMaW5rKCkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IobWVzc2FnZSk7XG4gIH1cbn1cblxuZnVuY3Rpb24gYXNzZXJ0Tm9TeW1saW5rZWREZXNjZW5kYW50cyhyZXNvbHZlZEJhc2U6IHN0cmluZywgcmVzb2x2ZWRUYXJnZXQ6IHN0cmluZyk6IHZvaWQge1xuICBjb25zdCByZWxhdGl2ZVBhdGggPSBwYXRoLnJlbGF0aXZlKHJlc29sdmVkQmFzZSwgcmVzb2x2ZWRUYXJnZXQpO1xuICBpZiAoIXJlbGF0aXZlUGF0aCkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGxldCBjdXJyZW50UGF0aCA9IHJlc29sdmVkQmFzZTtcbiAgZm9yIChjb25zdCBzZWdtZW50IG9mIHJlbGF0aXZlUGF0aC5zcGxpdChwYXRoLnNlcCkpIHtcbiAgICBpZiAoIXNlZ21lbnQpIHtcbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cblxuICAgIGN1cnJlbnRQYXRoID0gcGF0aC5qb2luKGN1cnJlbnRQYXRoLCBzZWdtZW50KTtcbiAgICBhc3NlcnROb3RTeW1saW5rKGN1cnJlbnRQYXRoLCAnUGF0aCBzZWdtZW50IHJlc29sdmVzIHRocm91Z2ggYSBzeW1ib2xpYyBsaW5rJyk7XG4gIH1cbn1cblxuLyoqXG4gKiBSZXNvbHZlIGEgdGFyZ2V0IHBhdGggYW5kIHZlcmlmeSB0aGF0IGl0IHJlbWFpbnMgaW5zaWRlIHRoZSBpbnRlbmRlZCBiYXNlXG4gKiBkaXJlY3RvcnkuIFRoaXMgaXMgc2VwYXJhdG9yLWF3YXJlLCBzbyBzaWJsaW5nIHBhdGhzIHN1Y2ggYXMgYC90bXAvYmFzZTJgXG4gKiBhcmUgbm90IHRyZWF0ZWQgYXMgY2hpbGRyZW4gb2YgYC90bXAvYmFzZWAuIEV4aXN0aW5nIHN5bWxpbmtlZCBiYXNlIHBhdGhzXG4gKiBhbmQgc3ltbGlua2VkIGRlc2NlbmRhbnRzIHVuZGVyIHRoZSBiYXNlIGFyZSByZWplY3RlZCBiZWNhdXNlIHdyaXRlcyB3b3VsZFxuICogZm9sbG93IHRoZW0gb3V0c2lkZSB0aGUgbGV4aWNhbCBjb250YWlubWVudCBib3VuZGFyeS4gQSBiYXNlIHRoYXQgZG9lcyBub3RcbiAqIGV4aXN0IHlldCBpcyB2ZXR0ZWQgdGhyb3VnaCBpdHMgbmVhcmVzdCBleGlzdGluZyBhbmNlc3RvcjogaWYgdGhhdCBhbmNlc3RvclxuICogaXMgYSBzeW1saW5rLCB0aGUgcmVjdXJzaXZlIG1rZGlyL3dyaXRlIHRoYXQgZm9sbG93cyB3b3VsZCBiZSByZWRpcmVjdGVkXG4gKiBvdXRzaWRlIHRoZSBpbnRlbmRlZCB0cmVlLCBzbyBpdCBpcyByZWplY3RlZCB0b28gKCMyMzQyKS5cbiAqXG4gKiBDT05UUkFDVCAoIzIzNDQpOiB0aGUgYmFzZSdzIG93biBhbmNlc3RyeSBjYW4gb25seSBiZSBiZXN0LWVmZm9ydCB2ZXR0ZWRcbiAqIGhlcmUg4oCUIGEgc3ltbGluayB3aG9zZSB0YXJnZXQgcHJlLWNvbnRhaW5zIG1hdGNoaW5nIHN1YmRpcmVjdG9yaWVzIGlzIG5vdFxuICogZGV0ZWN0YWJsZSB3aXRob3V0IGEgY2FsbGVyLWtub3duIGJvdW5kYXJ5IChzZWUgbmVhcmVzdEV4aXN0aW5nQW5jZXN0b3JTdGF0c1xuICogZm9yIHdoeSB3YWxraW5nIGV2ZXJ5IGFuY2VzdG9yIGZhbHNlLXBvc2l0aXZlcyBvbiBzeXN0ZW0gc3ltbGlua3MpLiBEbyBOT1RcbiAqIHBhc3MgdXNlci1jb250cm9sbGVkIGJhc2UgcGF0aHMgZGlyZWN0bHkgdG8gdGhpcyBmdW5jdGlvbjogdmV0IHRoZW0gZmlyc3RcbiAqIHdpdGggdmV0T3V0cHV0QmFzZSgpLCB3aGljaCBjb250YWlucyB0aGVtIGNhbm9uaWNhbGx5IGFnYWluc3QgYW4gYW5jaG9yIGFuZFxuICogcmV0dXJucyB0aGUgY2Fub25pY2FsIGJhc2UgdG8gdXNlIGhlcmUuIEV2ZXJ5IGN1cnJlbnQgY2FsbGVyIGRvZXMgdGhpcy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlc29sdmVQYXRoV2l0aGluQmFzZShiYXNlRGlyOiBzdHJpbmcsIC4uLnNlZ21lbnRzOiBzdHJpbmdbXSk6IHN0cmluZyB7XG4gIGlmICghYmFzZURpciB8fCB0eXBlb2YgYmFzZURpciAhPT0gJ3N0cmluZycpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdCYXNlIGRpcmVjdG9yeSBtdXN0IGJlIGEgbm9uLWVtcHR5IHN0cmluZycpO1xuICB9XG5cbiAgZm9yIChjb25zdCBzZWdtZW50IG9mIHNlZ21lbnRzKSB7XG4gICAgaWYgKHR5cGVvZiBzZWdtZW50ICE9PSAnc3RyaW5nJykge1xuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignUGF0aCBzZWdtZW50cyBtdXN0IGJlIHN0cmluZ3MnKTtcbiAgICB9XG4gICAgaWYgKHNlZ21lbnQuaW5jbHVkZXMoJ1xcMCcpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BhdGggc2VnbWVudCBjb250YWlucyBhIG51bGwgYnl0ZScpO1xuICAgIH1cbiAgfVxuXG4gIGNvbnN0IHJlc29sdmVkQmFzZSA9IHBhdGgucmVzb2x2ZShiYXNlRGlyKTtcbiAgY29uc3QgcmVzb2x2ZWRUYXJnZXQgPSBwYXRoLnJlc29sdmUocmVzb2x2ZWRCYXNlLCAuLi5zZWdtZW50cyk7XG4gIGNvbnN0IHJlbGF0aXZlUGF0aCA9IHBhdGgucmVsYXRpdmUocmVzb2x2ZWRCYXNlLCByZXNvbHZlZFRhcmdldCk7XG4gIGNvbnN0IGlzVHJhdmVyc2FsID0gcmVsYXRpdmVQYXRoID09PSAnLi4nXG4gICAgfHwgcmVsYXRpdmVQYXRoLnN0YXJ0c1dpdGgoJy4uJyArIHBhdGguc2VwKVxuICAgIHx8IHBhdGguaXNBYnNvbHV0ZShyZWxhdGl2ZVBhdGgpO1xuXG4gIGlmICghaXNUcmF2ZXJzYWwpIHtcbiAgICBhc3NlcnROb3RTeW1saW5rKHJlc29sdmVkQmFzZSwgJ0Jhc2UgZGlyZWN0b3J5IHJlc29sdmVzIHRocm91Z2ggYSBzeW1ib2xpYyBsaW5rJyk7XG4gICAgYXNzZXJ0Tm9TeW1saW5rZWREZXNjZW5kYW50cyhyZXNvbHZlZEJhc2UsIHJlc29sdmVkVGFyZ2V0KTtcbiAgICByZXR1cm4gcmVzb2x2ZWRUYXJnZXQ7XG4gIH1cblxuICB0aHJvdyBuZXcgRXJyb3IoJ1Jlc29sdmVkIHBhdGggZXNjYXBlcyB0aGUgYmFzZSBkaXJlY3RvcnknKTtcbn1cblxuLyoqIFRydWUgd2hlbiBhIGJhc2UtcmVsYXRpdmUgcGF0aCBwb2ludHMgb3V0c2lkZSB0aGUgYmFzZS4gKi9cbmZ1bmN0aW9uIGVzY2FwZXNCYXNlKHJlbGF0aXZlUGF0aDogc3RyaW5nKTogYm9vbGVhbiB7XG4gIHJldHVybiByZWxhdGl2ZVBhdGggPT09ICcuLidcbiAgICB8fCByZWxhdGl2ZVBhdGguc3RhcnRzV2l0aCgnLi4nICsgcGF0aC5zZXApXG4gICAgfHwgcGF0aC5pc0Fic29sdXRlKHJlbGF0aXZlUGF0aCk7XG59XG5cbi8qKlxuICogQ2Fub25pY2FsIChyZWFscGF0aCkgZm9ybSBvZiBhIHBhdGggdGhhdCBtYXkgbm90IGZ1bGx5IGV4aXN0IHlldDogdGhlXG4gKiBkZWVwZXN0IGV4aXN0aW5nIGFuY2VzdG9yIGlzIHJlc29sdmVkIHRocm91Z2ggZXZlcnkgc3ltbGluaywgYW5kIHRoZVxuICogbm90LXlldC1jcmVhdGVkIHN1ZmZpeCBpcyBhcHBlbmRlZCB1bmNoYW5nZWQuIFRoaXMgYW5zd2VycyBcIndoZXJlIHdvdWxkIGFcbiAqIHJlY3Vyc2l2ZSBta2Rpci93cml0ZSBvbiB0aGlzIHBhdGggUkVBTExZIGxhbmQ/XCIg4oCUIHdoaWNoIHBlci1jb21wb25lbnRcbiAqIGxzdGF0IGNoZWNrcyBjYW5ub3QsIGJlY2F1c2UgbHN0YXQgcmVzb2x2ZXMgaW50ZXJtZWRpYXRlIHN5bWxpbmtzIGFuZCBvbmx5XG4gKiByZXBvcnRzIG9uIHRoZSBmaW5hbCBjb21wb25lbnQgKCMyMzQ0KS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNhbm9uaWNhbGl6ZVBhdGgoaW5wdXRQYXRoOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCByZXNvbHZlZCA9IHBhdGgucmVzb2x2ZShpbnB1dFBhdGgpO1xuICBjb25zdCBtaXNzaW5nOiBzdHJpbmdbXSA9IFtdO1xuICBsZXQgY3VycmVudCA9IHJlc29sdmVkO1xuICBmb3IgKDs7KSB7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGNhbm9uaWNhbCA9IGZzLnJlYWxwYXRoU3luYyhjdXJyZW50KTtcbiAgICAgIHJldHVybiBtaXNzaW5nLmxlbmd0aCA/IHBhdGguam9pbihjYW5vbmljYWwsIC4uLm1pc3NpbmcpIDogY2Fub25pY2FsO1xuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICBpZiAoIWlzTWlzc2luZ1BhdGhFcnJvcihlcnJvcikpIHtcbiAgICAgICAgdGhyb3cgZXJyb3I7XG4gICAgICB9XG4gICAgfVxuICAgIGNvbnN0IHBhcmVudCA9IHBhdGguZGlybmFtZShjdXJyZW50KTtcbiAgICBpZiAocGFyZW50ID09PSBjdXJyZW50KSB7XG4gICAgICAvLyBOb3RoaW5nIG9uIHRoZSBjaGFpbiBleGlzdHMgKHNob3VsZCBub3QgaGFwcGVuIGZvciBhYnNvbHV0ZSBwYXRocyDigJRcbiAgICAgIC8vIHRoZSByb290IGV4aXN0cyDigJQgYnV0IGtlZXAgYSBsZXhpY2FsIGZhbGxiYWNrIHJhdGhlciB0aGFuIHRocm93aW5nKS5cbiAgICAgIHJldHVybiBtaXNzaW5nLmxlbmd0aCA/IHBhdGguam9pbihjdXJyZW50LCAuLi5taXNzaW5nKSA6IGN1cnJlbnQ7XG4gICAgfVxuICAgIG1pc3NpbmcudW5zaGlmdChwYXRoLmJhc2VuYW1lKGN1cnJlbnQpKTtcbiAgICBjdXJyZW50ID0gcGFyZW50O1xuICB9XG59XG5cbi8qKlxuICogVmV0cyBhIHVzZXItc3VwcGxpZWQgb3V0cHV0IGJhc2UgZm9yIENMSSB3cml0ZXMgYW5kIHJldHVybnMgdGhlIGNhbm9uaWNhbFxuICogcGF0aCBhbGwgc3Vic2VxdWVudCB3cml0ZXMgc2hvdWxkIHVzZSAoIzIzNDQpLlxuICpcbiAqIFR3byByZWdpbWVzLCBzcGxpdCBieSB3aGF0IHRoZSB1c2VyIGxleGljYWxseSBuYW1lZDpcbiAqXG4gKiAgLSBCYXNlIGluc2lkZSB0aGUgYW5jaG9yIChyZWxhdGl2ZSBvdXRwdXRzIGxpa2UgdGhlICcuL2FudGhyb3BpYy1za2lsbHMnXG4gKiAgICBkZWZhdWx0KTogdGhlIGNhbm9uaWNhbCBiYXNlIG11c3Qgc3RheSBpbnNpZGUgdGhlIGNhbm9uaWNhbCBhbmNob3IuXG4gKiAgICBBIHN5bWxpbmsgdGhhdCByZWRpcmVjdHMgaXQgZWxzZXdoZXJlIOKAlCBpbmNsdWRpbmcgb25lIHdob3NlIHRhcmdldFxuICogICAgYWxyZWFkeSBjb250YWlucyBtYXRjaGluZyBzdWJkaXJlY3RvcmllcywgdGhlIGNhc2UgcGVyLWNvbXBvbmVudCBsc3RhdFxuICogICAgY2hlY2tzIG1pc3Mg4oCUIGlzIHJlamVjdGVkLiBDb21wYXJpbmcgaW4gY2Fub25pY2FsIHNwYWNlIG1lYW5zIHN5c3RlbVxuICogICAgbGlua3MgYWJvdmUgdGhlIGFuY2hvciAobWFjT1MgL3RtcCAtPiAvcHJpdmF0ZS90bXApIG5ldmVyIGZhbHNlLXBvc2l0aXZlLlxuICpcbiAqICAtIEJhc2Ugb3V0c2lkZSB0aGUgYW5jaG9yIChleHBsaWNpdCBhYnNvbHV0ZSBvciAuLi8gb3V0cHV0cyk6IHRoZSB1c2VyXG4gKiAgICBuYW1lZCB0aGF0IGRlc3RpbmF0aW9uLCBzbyB0aGVyZSBpcyBubyBib3VuZGFyeSB0byBkZWZlbmQuIEluc3RlYWQgb2ZcbiAqICAgIGd1ZXNzaW5nIGludGVudCwgdGhlIHJlYWwgZGVzdGluYXRpb24gaXMgZGlzY2xvc2VkIHZpYSBgb25EaXNjbG9zZWBcbiAqICAgIHdoZW5ldmVyIGl0IGRpZmZlcnMgZnJvbSB0aGUgbGV4aWNhbCBwYXRoLCBhbmQgdGhlIGNhbm9uaWNhbCBwYXRoIGlzXG4gKiAgICByZXR1cm5lZCBzbyB3aGF0IHdhcyB2ZXR0ZWQgaXMgd2hhdCBnZXRzIHdyaXR0ZW4uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB2ZXRPdXRwdXRCYXNlKFxuICBiYXNlRGlyOiBzdHJpbmcsXG4gIG9wdGlvbnM/OiB7IGFuY2hvcj86IHN0cmluZzsgb25EaXNjbG9zZT86IChjYW5vbmljYWxCYXNlOiBzdHJpbmcpID0+IHZvaWQgfSxcbik6IHN0cmluZyB7XG4gIGlmICghYmFzZURpciB8fCB0eXBlb2YgYmFzZURpciAhPT0gJ3N0cmluZycpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdCYXNlIGRpcmVjdG9yeSBtdXN0IGJlIGEgbm9uLWVtcHR5IHN0cmluZycpO1xuICB9XG4gIGlmIChiYXNlRGlyLmluY2x1ZGVzKCdcXDAnKSkge1xuICAgIHRocm93IG5ldyBFcnJvcignUGF0aCBzZWdtZW50IGNvbnRhaW5zIGEgbnVsbCBieXRlJyk7XG4gIH1cblxuICBjb25zdCByZXNvbHZlZEFuY2hvciA9IHBhdGgucmVzb2x2ZShvcHRpb25zPy5hbmNob3IgPz8gcHJvY2Vzcy5jd2QoKSk7XG4gIGNvbnN0IHJlc29sdmVkQmFzZSA9IHBhdGgucmVzb2x2ZShiYXNlRGlyKTtcbiAgY29uc3QgY2Fub25pY2FsQmFzZSA9IGNhbm9uaWNhbGl6ZVBhdGgocmVzb2x2ZWRCYXNlKTtcblxuICBpZiAoIWVzY2FwZXNCYXNlKHBhdGgucmVsYXRpdmUocmVzb2x2ZWRBbmNob3IsIHJlc29sdmVkQmFzZSkpKSB7XG4gICAgY29uc3QgY2Fub25pY2FsQW5jaG9yID0gY2Fub25pY2FsaXplUGF0aChyZXNvbHZlZEFuY2hvcik7XG4gICAgaWYgKGVzY2FwZXNCYXNlKHBhdGgucmVsYXRpdmUoY2Fub25pY2FsQW5jaG9yLCBjYW5vbmljYWxCYXNlKSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYE91dHB1dCBwYXRoIHJlc29sdmVzIG91dHNpZGUgdGhlIHdvcmtpbmcgZGlyZWN0b3J5IHRocm91Z2ggYSBzeW1ib2xpYyBsaW5rIChyZWFsIGRlc3RpbmF0aW9uOiAke2Nhbm9uaWNhbEJhc2V9KWAsXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gY2Fub25pY2FsQmFzZTtcbiAgfVxuXG4gIGlmIChjYW5vbmljYWxCYXNlICE9PSByZXNvbHZlZEJhc2UpIHtcbiAgICBvcHRpb25zPy5vbkRpc2Nsb3NlPy4oY2Fub25pY2FsQmFzZSk7XG4gIH1cbiAgcmV0dXJuIGNhbm9uaWNhbEJhc2U7XG59XG4iXX0=
|
|
@@ -33,6 +33,13 @@ export declare function createSetupRoutes(opts?: {
|
|
|
33
33
|
_installPermissionHook?: (client: string) => Promise<InstallPermissionHookResult>;
|
|
34
34
|
/** Override permission hook status reconciler. For testing only. */
|
|
35
35
|
_reconcilePermissionHookStatus?: (client: string) => Promise<PermissionHookStatus>;
|
|
36
|
+
/**
|
|
37
|
+
* Override the NVM launcher mitigation applied after a successful install.
|
|
38
|
+
* For testing only. Without this seam the install handler runs the real
|
|
39
|
+
* applyNvmLauncherIfNeeded against the real homedir(); on a machine WITH NVM
|
|
40
|
+
* that patches the developer's real client configs (issue #2338).
|
|
41
|
+
*/
|
|
42
|
+
_applyNvmLauncher?: (client: string) => Promise<NvmLauncherResult>;
|
|
36
43
|
/** Enable automatic hook asset repair during detect. Defaults off in tests. */
|
|
37
44
|
_autoRepairPermissionHooksOnDetect?: boolean;
|
|
38
45
|
/** Skip the sliding-window rate limiter. For testing only. */
|
|
@@ -59,18 +66,27 @@ export type NvmLauncherResult = 'applied' | 'not-applicable' | 'failed';
|
|
|
59
66
|
*/
|
|
60
67
|
export declare function applyNvmLauncherIfNeeded(client: string, home?: string): Promise<NvmLauncherResult>;
|
|
61
68
|
/**
|
|
62
|
-
* Startup repair
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
69
|
+
* Startup repair + self-heal: reconciles every known JSON-format client config
|
|
70
|
+
* on each server start. Behaviour depends on whether NVM is installed, matching
|
|
71
|
+
* the acceptance matrix in https://github.com/DollhouseMCP/mcp-server/issues/2338:
|
|
72
|
+
*
|
|
73
|
+
* • NVM present → (re)create the wrapper and point each config at it. Covers a
|
|
74
|
+
* deleted wrapper, a pre-fix install still on bare `npx`, and a config left
|
|
75
|
+
* pointing at a dead temp-dir wrapper (regenerates the real one). Healthy
|
|
76
|
+
* configs re-serialise byte-identically, so this is a no-op for them.
|
|
77
|
+
* • NVM absent → never create a wrapper, but SELF-HEAL any config whose
|
|
78
|
+
* command points at a `dollhousemcp-nvm.sh` that no longer exists (e.g. the
|
|
79
|
+
* #2338 test leak, or a user who removed ~/.dollhouse/bin) back to bare `npx`,
|
|
80
|
+
* preserving args so the client can launch DollhouseMCP again.
|
|
67
81
|
*
|
|
68
82
|
* Fire-and-forget from startWebServer. All errors are swallowed and logged.
|
|
69
83
|
*
|
|
70
84
|
* @param home - Override home directory (injectable for tests)
|
|
71
85
|
* @param configPathResolver - Override config path lookup (injectable for tests).
|
|
72
|
-
* Return null to skip a client entirely.
|
|
73
|
-
*
|
|
86
|
+
* Return null to skip a client entirely. Defaults to a
|
|
87
|
+
* home-derived resolver so `home` stays authoritative
|
|
88
|
+
* — the default must NOT reach the real homedir() when
|
|
89
|
+
* a fake home is supplied (#2338).
|
|
74
90
|
*/
|
|
75
91
|
export declare function repairNvmLauncherOnStartup(home?: string, configPathResolver?: (client: string) => string | null): Promise<void>;
|
|
76
92
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setupRoutes.d.ts","sourceRoot":"","sources":["../../../src/web/routes/setupRoutes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAajD,OAAO,EAGL,KAAK,2BAA2B,EAChC,KAAK,oBAAoB,EAC1B,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"setupRoutes.d.ts","sourceRoot":"","sources":["../../../src/web/routes/setupRoutes.ts"],"names":[],"mappings":"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"}
|