@grainulation/silo 1.0.5 → 1.1.1
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/bin/silo.js +0 -1
- package/lib/confluence.js +4 -8
- package/lib/serve-mcp.js +40 -4
- package/package.json +1 -1
package/bin/silo.js
CHANGED
|
@@ -423,7 +423,6 @@ try {
|
|
|
423
423
|
const serverPath = path.join(__dirname, "..", "lib", "server.js");
|
|
424
424
|
const child = fork(serverPath, serverArgs, {
|
|
425
425
|
stdio: "inherit",
|
|
426
|
-
env: process.env,
|
|
427
426
|
});
|
|
428
427
|
child.on("error", (err) => {
|
|
429
428
|
process.stderr.write(`silo: error starting server: ${err.message}\n`);
|
package/lib/confluence.js
CHANGED
|
@@ -25,14 +25,10 @@ class Confluence {
|
|
|
25
25
|
* @param {string} opts.spaceKey — Default space key
|
|
26
26
|
*/
|
|
27
27
|
constructor(opts = {}) {
|
|
28
|
-
this.baseUrl = (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
).replace(/\/+$/, "");
|
|
33
|
-
this.token = opts.token || process.env.CONFLUENCE_TOKEN || "";
|
|
34
|
-
this.email = opts.email || process.env.CONFLUENCE_EMAIL || "";
|
|
35
|
-
this.spaceKey = opts.spaceKey || process.env.CONFLUENCE_SPACE_KEY || "";
|
|
28
|
+
this.baseUrl = (opts.baseUrl || "").replace(/\/+$/, "");
|
|
29
|
+
this.token = opts.token || "";
|
|
30
|
+
this.email = opts.email || "";
|
|
31
|
+
this.spaceKey = opts.spaceKey || "";
|
|
36
32
|
}
|
|
37
33
|
|
|
38
34
|
/** Check if the adapter is configured with required credentials. */
|
package/lib/serve-mcp.js
CHANGED
|
@@ -52,7 +52,12 @@ const search = new Search(store);
|
|
|
52
52
|
const io = new ImportExport(store);
|
|
53
53
|
const packs = new Packs(store);
|
|
54
54
|
const graph = new Graph(store);
|
|
55
|
-
const confluence = new Confluence(
|
|
55
|
+
const confluence = new Confluence({
|
|
56
|
+
baseUrl: process.env.CONFLUENCE_BASE_URL,
|
|
57
|
+
token: process.env.CONFLUENCE_TOKEN,
|
|
58
|
+
email: process.env.CONFLUENCE_EMAIL,
|
|
59
|
+
spaceKey: process.env.CONFLUENCE_SPACE_KEY,
|
|
60
|
+
});
|
|
56
61
|
|
|
57
62
|
// ─── Tool implementations ───────────────────────────────────────────────────
|
|
58
63
|
|
|
@@ -108,7 +113,14 @@ function toolPull(dir, args) {
|
|
|
108
113
|
};
|
|
109
114
|
}
|
|
110
115
|
|
|
111
|
-
const targetFile = into
|
|
116
|
+
const targetFile = into ? path.resolve(dir, into) : path.join(dir, "claims.json");
|
|
117
|
+
// Prevent path traversal — target must stay within workspace
|
|
118
|
+
if (targetFile !== dir && !targetFile.startsWith(dir + path.sep)) {
|
|
119
|
+
return {
|
|
120
|
+
status: "error",
|
|
121
|
+
message: `Target path escapes workspace directory.`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
112
124
|
if (!fs.existsSync(targetFile)) {
|
|
113
125
|
return {
|
|
114
126
|
status: "error",
|
|
@@ -135,7 +147,14 @@ function toolStore(dir, args) {
|
|
|
135
147
|
return { status: "error", message: "Required field: name" };
|
|
136
148
|
}
|
|
137
149
|
|
|
138
|
-
const sourceFile = from
|
|
150
|
+
const sourceFile = from ? path.resolve(dir, from) : path.join(dir, "claims.json");
|
|
151
|
+
// Prevent path traversal — source must stay within workspace
|
|
152
|
+
if (sourceFile !== dir && !sourceFile.startsWith(dir + path.sep)) {
|
|
153
|
+
return {
|
|
154
|
+
status: "error",
|
|
155
|
+
message: `Source path escapes workspace directory.`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
139
158
|
if (!fs.existsSync(sourceFile)) {
|
|
140
159
|
return { status: "error", message: `Source file not found: ${sourceFile}` };
|
|
141
160
|
}
|
|
@@ -247,7 +266,14 @@ function toolConfluence(dir, args) {
|
|
|
247
266
|
case "publish": {
|
|
248
267
|
const { title, from, spaceKey, parentId, pageId } = args;
|
|
249
268
|
if (!title) return { status: "error", message: "Required field: title" };
|
|
250
|
-
const sourceFile = from
|
|
269
|
+
const sourceFile = from ? path.resolve(dir, from) : path.join(dir, "claims.json");
|
|
270
|
+
// Prevent path traversal — source must stay within workspace
|
|
271
|
+
if (sourceFile !== dir && !sourceFile.startsWith(dir + path.sep)) {
|
|
272
|
+
return {
|
|
273
|
+
status: "error",
|
|
274
|
+
message: `Source path escapes workspace directory.`,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
251
277
|
if (!fs.existsSync(sourceFile))
|
|
252
278
|
return {
|
|
253
279
|
status: "error",
|
|
@@ -269,6 +295,16 @@ function toolConfluence(dir, args) {
|
|
|
269
295
|
const target = pid || searchTitle;
|
|
270
296
|
if (!target)
|
|
271
297
|
return { status: "error", message: "Required: pageId or title" };
|
|
298
|
+
// Prevent path traversal — target must stay within workspace
|
|
299
|
+
if (into) {
|
|
300
|
+
const checkPath = path.resolve(dir, into);
|
|
301
|
+
if (checkPath !== dir && !checkPath.startsWith(dir + path.sep)) {
|
|
302
|
+
return {
|
|
303
|
+
status: "error",
|
|
304
|
+
message: `Target path escapes workspace directory.`,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
272
308
|
return confluence
|
|
273
309
|
.pull(target, { spaceKey })
|
|
274
310
|
.then((result) => {
|