@imenam/mcp-documentor 1.0.103 → 1.0.104

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/README.md CHANGED
@@ -152,6 +152,8 @@ Images can be embedded in any documentation entry using standard markdown syntax
152
152
  ![alt text](https://example.com/diagram.png) <!-- external URL -->
153
153
  ```
154
154
 
155
+ The GUI serves uploaded images under `/api/images/<project>/<file>`, where `<project>` and `<file>` are single path segments; any other shape is refused. Images are served with `X-Content-Type-Options: nosniff` and a `sandbox` Content-Security-Policy, so an SVG opened on its own runs no script.
156
+
155
157
  **Via the MCP tool** — use `upload_image` with the absolute path of the file on the server:
156
158
 
157
159
  ```json
@@ -1016,6 +1016,8 @@ app.post("/api/images", async (c) => {
1016
1016
  const project = c.req.query("project");
1017
1017
  if (!project)
1018
1018
  return c.json({ error: "project is required" }, 400);
1019
+ if (!isSafeSegment(project))
1020
+ return c.json({ error: "project must be a single path segment" }, 400);
1019
1021
  let body;
1020
1022
  try {
1021
1023
  body = await c.req.parseBody();
@@ -1035,6 +1037,8 @@ app.post("/api/images", async (c) => {
1035
1037
  });
1036
1038
  app.get("/api/images/:project", (c) => {
1037
1039
  const project = c.req.param("project");
1040
+ if (!isSafeSegment(project))
1041
+ return c.json({ error: "project must be a single path segment" }, 400);
1038
1042
  const assetsDir = path.join(dataDir, "projects", project, "assets");
1039
1043
  if (!fs.existsSync(assetsDir))
1040
1044
  return c.json([]);
@@ -1044,13 +1048,20 @@ app.get("/api/images/:project", (c) => {
1044
1048
  app.get("/api/images/:project/:filename", (c) => {
1045
1049
  const project = c.req.param("project");
1046
1050
  const filename = c.req.param("filename");
1051
+ if (!isSafeSegment(project) || !isSafeSegment(filename))
1052
+ return c.notFound();
1047
1053
  const filePath = path.join(dataDir, "projects", project, "assets", filename);
1048
1054
  if (!fs.existsSync(filePath))
1049
1055
  return c.notFound();
1050
1056
  const ext = path.extname(filename).toLowerCase().slice(1);
1051
1057
  const mime = IMAGE_MIME[ext] ?? "application/octet-stream";
1052
1058
  return new Response(fs.readFileSync(filePath), {
1053
- headers: { "Content-Type": mime },
1059
+ headers: {
1060
+ "Content-Type": mime,
1061
+ "X-Content-Type-Options": "nosniff",
1062
+ // An SVG opened on its own must not run scripts under the hub's origin.
1063
+ "Content-Security-Policy": "default-src 'none'; style-src 'unsafe-inline'; sandbox",
1064
+ },
1054
1065
  });
1055
1066
  });
1056
1067
  // Static files
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imenam/mcp-documentor",
3
- "version": "1.0.103",
3
+ "version": "1.0.104",
4
4
  "main": "dist/src/index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",