@grampro/kit 2.7.0 → 2.7.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +7 -1
  3. package/package.json +2 -2
  4. package/source/components/accordion/passport.json +1 -1
  5. package/source/components/alert/passport.json +1 -1
  6. package/source/components/avatar/passport.json +1 -1
  7. package/source/components/badge/passport.json +1 -1
  8. package/source/components/breadcrumb/passport.json +1 -1
  9. package/source/components/button/passport.json +1 -1
  10. package/source/components/card/passport.json +1 -1
  11. package/source/components/checkbox/passport.json +1 -1
  12. package/source/components/combobox/passport.json +1 -1
  13. package/source/components/data-grid/passport.json +1 -1
  14. package/source/components/date-picker/passport.json +1 -1
  15. package/source/components/dialog/passport.json +1 -1
  16. package/source/components/file-uploader/passport.json +1 -1
  17. package/source/components/input/passport.json +1 -1
  18. package/source/components/menu/passport.json +1 -1
  19. package/source/components/modal/passport.json +1 -1
  20. package/source/components/number-input/passport.json +1 -1
  21. package/source/components/popover/passport.json +1 -1
  22. package/source/components/progress/passport.json +1 -1
  23. package/source/components/radio-group/passport.json +1 -1
  24. package/source/components/skeleton/passport.json +1 -1
  25. package/source/components/spinner/passport.json +1 -1
  26. package/source/components/switch/passport.json +1 -1
  27. package/source/components/tabs/passport.json +1 -1
  28. package/source/components/textarea/passport.json +1 -1
  29. package/source/components/toaster/passport.json +1 -1
  30. package/source/components/tooltip/passport.json +1 -1
  31. package/tools/mcp/README.md +23 -0
  32. package/tools/mcp/server.cjs +16 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.7.1
4
+
5
+ ### Security
6
+
7
+ **get_guidance could read any .md file on disk.** It appended `.md` to a
8
+ caller-supplied `topic` and joined it onto the references folder with no
9
+ traversal guard, so `topic = "../../../../CHANGELOG"` returned the repo
10
+ CHANGELOG and the same trick reached any `.md` file the server process could
11
+ open. Found by a VAPT pass driving the tool with traversal payloads. The
12
+ server runs with your own authority, so it is a confused-deputy hole rather
13
+ than privilege escalation — but a tool that advertises nine fixed topics
14
+ should not become a file-exfiltration primitive for a prompt-injected agent.
15
+ It now refuses any `topic` that is not one of the shipped guidance names, with
16
+ a resolved-prefix check behind the allowlist. A regression test drives the
17
+ same payloads.
18
+
19
+ **Affected: 2.4.4 through 2.7.0** — every release that has shipped the MCP
20
+ server. Each was checked by running the same payload against its published
21
+ tarball. 2.4.3 and earlier have no MCP server and are not affected. A project
22
+ that installed the package locally needs `npm i -D @grampro/kit@latest`. One
23
+ configured with `npx -y @grampro/kit mcp` may keep running a cached copy; check
24
+ with `npx -y @grampro/kit --version`, and if it is not 2.7.1, run
25
+ `npx -y @grampro/kit@2.7.1 mcp` once to replace it.
26
+
3
27
  ## 2.7.0
4
28
 
5
29
  ### Added
package/README.md CHANGED
@@ -135,7 +135,13 @@ anything first.
135
135
  No dependencies, and it starts in about the time an empty Node process
136
136
  takes. Full walkthrough, including a prompt to paste:
137
137
  [kit.gramproindia.com/mcp-server](https://kit.gramproindia.com/mcp-server).
138
- Details in [`tools/mcp/README.md`](tools/mcp/README.md).
138
+ Details in [`tools/mcp/README.md`](tools/mcp/README.md), including what a tool
139
+ call can and cannot reach.
140
+
141
+ **Use 2.7.1 or later.** From 2.4.4 through 2.7.0, a crafted `get_guidance`
142
+ call could read any `.md` file the server could open. The
143
+ [changelog](CHANGELOG.md#271) explains how to check which version your agent
144
+ is running.
139
145
 
140
146
  ## Figma plugin
141
147
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@grampro/kit",
3
- "version": "2.7.0",
4
- "description": "GramproKit \u2014 an agent-native UI runtime for React. Copy-in components you own, each publishing a contract that software can read.",
3
+ "version": "2.7.1",
4
+ "description": "GramproKit — an agent-native UI runtime for React. Copy-in components you own, each publishing a contract that software can read.",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "index.cjs",
@@ -10,7 +10,7 @@
10
10
  ]
11
11
  },
12
12
  "source": {
13
- "libraryVersion": "2.7.0",
13
+ "libraryVersion": "2.7.1",
14
14
  "files": [
15
15
  "core/index.ts",
16
16
  "core/open.ts",
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.7.0",
11
+ "libraryVersion": "2.7.1",
12
12
  "files": [
13
13
  "core/index.ts",
14
14
  "core/live.ts",
@@ -9,7 +9,7 @@
9
9
  ]
10
10
  },
11
11
  "source": {
12
- "libraryVersion": "2.7.0",
12
+ "libraryVersion": "2.7.1",
13
13
  "files": [
14
14
  "core/identity.ts",
15
15
  "core/index.ts",
@@ -9,7 +9,7 @@
9
9
  ]
10
10
  },
11
11
  "source": {
12
- "libraryVersion": "2.7.0",
12
+ "libraryVersion": "2.7.1",
13
13
  "files": [
14
14
  "core/count.ts",
15
15
  "core/index.ts",
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.7.0",
11
+ "libraryVersion": "2.7.1",
12
12
  "files": [
13
13
  "core/index.ts",
14
14
  "core/trail.ts",
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.7.0",
11
+ "libraryVersion": "2.7.1",
12
12
  "files": [
13
13
  "core/index.ts",
14
14
  "core/state.ts",
@@ -12,7 +12,7 @@
12
12
  ]
13
13
  },
14
14
  "source": {
15
- "libraryVersion": "2.7.0",
15
+ "libraryVersion": "2.7.1",
16
16
  "files": [
17
17
  "core/index.ts",
18
18
  "core/trend.ts",
@@ -10,7 +10,7 @@
10
10
  ]
11
11
  },
12
12
  "source": {
13
- "libraryVersion": "2.7.0",
13
+ "libraryVersion": "2.7.1",
14
14
  "files": [
15
15
  "core/agent.ts",
16
16
  "core/group.ts",
@@ -9,7 +9,7 @@
9
9
  ]
10
10
  },
11
11
  "source": {
12
- "libraryVersion": "2.7.0",
12
+ "libraryVersion": "2.7.1",
13
13
  "files": [
14
14
  "core/agent.ts",
15
15
  "core/filter.ts",
@@ -14,7 +14,7 @@
14
14
  ]
15
15
  },
16
16
  "source": {
17
- "libraryVersion": "2.7.0",
17
+ "libraryVersion": "2.7.1",
18
18
  "files": [
19
19
  "agent/coerce.ts",
20
20
  "agent/contract.ts",
@@ -10,7 +10,7 @@
10
10
  ]
11
11
  },
12
12
  "source": {
13
- "libraryVersion": "2.7.0",
13
+ "libraryVersion": "2.7.1",
14
14
  "files": [
15
15
  "core/agent.ts",
16
16
  "core/calendar.ts",
@@ -9,7 +9,7 @@
9
9
  ]
10
10
  },
11
11
  "source": {
12
- "libraryVersion": "2.7.0",
12
+ "libraryVersion": "2.7.1",
13
13
  "files": [
14
14
  "core/api.ts",
15
15
  "core/dialog.ts",
@@ -13,7 +13,7 @@
13
13
  ]
14
14
  },
15
15
  "source": {
16
- "libraryVersion": "2.7.0",
16
+ "libraryVersion": "2.7.1",
17
17
  "files": [
18
18
  "core/format.ts",
19
19
  "core/index.ts",
@@ -9,7 +9,7 @@
9
9
  ]
10
10
  },
11
11
  "source": {
12
- "libraryVersion": "2.7.0",
12
+ "libraryVersion": "2.7.1",
13
13
  "files": [
14
14
  "core/agent.ts",
15
15
  "core/count.ts",
@@ -16,7 +16,7 @@
16
16
  ]
17
17
  },
18
18
  "source": {
19
- "libraryVersion": "2.7.0",
19
+ "libraryVersion": "2.7.1",
20
20
  "files": [
21
21
  "core/index.ts",
22
22
  "core/navigation.ts",
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.7.0",
11
+ "libraryVersion": "2.7.1",
12
12
  "files": [
13
13
  "core/dismiss.ts",
14
14
  "core/index.ts",
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.7.0",
11
+ "libraryVersion": "2.7.1",
12
12
  "files": [
13
13
  "core/agent.ts",
14
14
  "core/format.ts",
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.7.0",
11
+ "libraryVersion": "2.7.1",
12
12
  "files": [
13
13
  "core/index.ts",
14
14
  "core/types.ts",
@@ -9,7 +9,7 @@
9
9
  ]
10
10
  },
11
11
  "source": {
12
- "libraryVersion": "2.7.0",
12
+ "libraryVersion": "2.7.1",
13
13
  "files": [
14
14
  "core/index.ts",
15
15
  "core/progress.ts",
@@ -10,7 +10,7 @@
10
10
  ]
11
11
  },
12
12
  "source": {
13
- "libraryVersion": "2.7.0",
13
+ "libraryVersion": "2.7.1",
14
14
  "files": [
15
15
  "core/agent.ts",
16
16
  "core/group.ts",
@@ -9,7 +9,7 @@
9
9
  ]
10
10
  },
11
11
  "source": {
12
- "libraryVersion": "2.7.0",
12
+ "libraryVersion": "2.7.1",
13
13
  "files": [
14
14
  "core/index.ts",
15
15
  "core/lines.ts",
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.7.0",
11
+ "libraryVersion": "2.7.1",
12
12
  "files": [
13
13
  "core/index.ts",
14
14
  "core/types.ts",
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.7.0",
11
+ "libraryVersion": "2.7.1",
12
12
  "files": [
13
13
  "core/agent.ts",
14
14
  "core/index.ts",
@@ -12,7 +12,7 @@
12
12
  ]
13
13
  },
14
14
  "source": {
15
- "libraryVersion": "2.7.0",
15
+ "libraryVersion": "2.7.1",
16
16
  "files": [
17
17
  "core/index.ts",
18
18
  "core/navigation.ts",
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.7.0",
11
+ "libraryVersion": "2.7.1",
12
12
  "files": [
13
13
  "core/agent.ts",
14
14
  "core/count.ts",
@@ -11,7 +11,7 @@
11
11
  ]
12
12
  },
13
13
  "source": {
14
- "libraryVersion": "2.7.0",
14
+ "libraryVersion": "2.7.1",
15
15
  "files": [
16
16
  "core/api.ts",
17
17
  "core/index.ts",
@@ -8,7 +8,7 @@
8
8
  ]
9
9
  },
10
10
  "source": {
11
- "libraryVersion": "2.7.0",
11
+ "libraryVersion": "2.7.1",
12
12
  "files": [
13
13
  "core/index.ts",
14
14
  "core/types.ts",
@@ -266,3 +266,26 @@ CLI's own dependencies, which are no use to a JSON-RPC loop and cost around
266
266
  The catalogue it serves is the one in this package, not the components
267
267
  copied into your project — so an agent can see a component before you
268
268
  install it, and `install_component` puts it in place.
269
+
270
+ ### What a caller can reach
271
+
272
+ Every tool argument is treated as untrusted. An agent that has read a hostile
273
+ web page or design file can be steered into calling these tools with
274
+ anything, so the boundary has to hold no matter who wrote the arguments.
275
+
276
+ - **`get_guidance`** serves only the guidance topics that ship in this
277
+ package. Any other topic is refused, including one containing `../`. Before
278
+ 2.7.1 it was not refused: a crafted topic could read any `.md` file the
279
+ server could open. See the changelog.
280
+ - **`get_component`, `get_example` and `install_component`** accept only names
281
+ in the catalogue. A name is reduced to letters and digits before anything
282
+ is looked up, and `install_component` runs the CLI with an argument list,
283
+ never through a shell.
284
+ - **`figma_frame` with `designFile`** reads the path it is given, with your
285
+ user's permissions. It only returns content from a file shaped like the
286
+ plugin's export, and refuses anything else without quoting it. A file that
287
+ *is* shaped like an export gets read as one, which is the tool's purpose.
288
+
289
+ The server runs as you, so none of this stops an agent that can already read
290
+ your files directly. What it prevents is a tool that advertises one narrow
291
+ job being turned into a general file reader.
@@ -197,7 +197,23 @@ function getGuidance(topic) {
197
197
  ? fs.readdirSync(REFERENCES).filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, ""))
198
198
  : [];
199
199
  if (!topic) return { topics: available };
200
+
201
+ /*
202
+ * `topic` is caller-controlled, and `path.join(REFERENCES, topic + ".md")`
203
+ * followed a `../` out of the references folder: a prompt-injected agent
204
+ * could turn this into "read any .md file on disk". The list is already an
205
+ * allowlist of what actually ships, so a topic that is not one of those
206
+ * names is refused before any path is built. The resolved-prefix check is
207
+ * the belt to that braces — it also stops a name that readdir would never
208
+ * produce, such as one reaching through a symlink.
209
+ */
210
+ if (!available.includes(topic)) {
211
+ return { error: `No guidance for "${topic}".`, topics: available };
212
+ }
200
213
  const file = path.join(REFERENCES, `${topic}.md`);
214
+ if (!path.resolve(file).startsWith(path.resolve(REFERENCES) + path.sep)) {
215
+ return { error: `No guidance for "${topic}".`, topics: available };
216
+ }
201
217
  if (!fs.existsSync(file)) return { error: `No guidance for "${topic}".`, topics: available };
202
218
  return { topic, text: fs.readFileSync(file, "utf8") };
203
219
  }