@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 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
- opts.baseUrl ||
30
- process.env.CONFLUENCE_BASE_URL ||
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 || path.join(dir, "claims.json");
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 || path.join(dir, "claims.json");
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 || path.join(dir, "claims.json");
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) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grainulation/silo",
3
- "version": "1.0.5",
3
+ "version": "1.1.1",
4
4
  "description": "Reusable knowledge for research sprints -- shared claim libraries, templates, and knowledge packs",
5
5
  "main": "lib/index.js",
6
6
  "exports": {