@grainulation/silo 1.0.5 → 1.1.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.
Files changed (2) hide show
  1. package/lib/serve-mcp.js +34 -3
  2. package/package.json +1 -1
package/lib/serve-mcp.js CHANGED
@@ -108,7 +108,14 @@ function toolPull(dir, args) {
108
108
  };
109
109
  }
110
110
 
111
- const targetFile = into || path.join(dir, "claims.json");
111
+ const targetFile = into ? path.resolve(dir, into) : path.join(dir, "claims.json");
112
+ // Prevent path traversal — target must stay within workspace
113
+ if (targetFile !== dir && !targetFile.startsWith(dir + path.sep)) {
114
+ return {
115
+ status: "error",
116
+ message: `Target path escapes workspace directory.`,
117
+ };
118
+ }
112
119
  if (!fs.existsSync(targetFile)) {
113
120
  return {
114
121
  status: "error",
@@ -135,7 +142,14 @@ function toolStore(dir, args) {
135
142
  return { status: "error", message: "Required field: name" };
136
143
  }
137
144
 
138
- const sourceFile = from || path.join(dir, "claims.json");
145
+ const sourceFile = from ? path.resolve(dir, from) : path.join(dir, "claims.json");
146
+ // Prevent path traversal — source must stay within workspace
147
+ if (sourceFile !== dir && !sourceFile.startsWith(dir + path.sep)) {
148
+ return {
149
+ status: "error",
150
+ message: `Source path escapes workspace directory.`,
151
+ };
152
+ }
139
153
  if (!fs.existsSync(sourceFile)) {
140
154
  return { status: "error", message: `Source file not found: ${sourceFile}` };
141
155
  }
@@ -247,7 +261,14 @@ function toolConfluence(dir, args) {
247
261
  case "publish": {
248
262
  const { title, from, spaceKey, parentId, pageId } = args;
249
263
  if (!title) return { status: "error", message: "Required field: title" };
250
- const sourceFile = from || path.join(dir, "claims.json");
264
+ const sourceFile = from ? path.resolve(dir, from) : path.join(dir, "claims.json");
265
+ // Prevent path traversal — source must stay within workspace
266
+ if (sourceFile !== dir && !sourceFile.startsWith(dir + path.sep)) {
267
+ return {
268
+ status: "error",
269
+ message: `Source path escapes workspace directory.`,
270
+ };
271
+ }
251
272
  if (!fs.existsSync(sourceFile))
252
273
  return {
253
274
  status: "error",
@@ -269,6 +290,16 @@ function toolConfluence(dir, args) {
269
290
  const target = pid || searchTitle;
270
291
  if (!target)
271
292
  return { status: "error", message: "Required: pageId or title" };
293
+ // Prevent path traversal — target must stay within workspace
294
+ if (into) {
295
+ const checkPath = path.resolve(dir, into);
296
+ if (checkPath !== dir && !checkPath.startsWith(dir + path.sep)) {
297
+ return {
298
+ status: "error",
299
+ message: `Target path escapes workspace directory.`,
300
+ };
301
+ }
302
+ }
272
303
  return confluence
273
304
  .pull(target, { spaceKey })
274
305
  .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.0",
4
4
  "description": "Reusable knowledge for research sprints -- shared claim libraries, templates, and knowledge packs",
5
5
  "main": "lib/index.js",
6
6
  "exports": {