@matrajs/mcp 1.0.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.
@@ -0,0 +1,72 @@
1
+ # Security
2
+
3
+ ## The model
4
+
5
+ A rich text editor is a place untrusted content arrives from at least three
6
+ directions, and all three are treated as hostile:
7
+
8
+ | route | example |
9
+ |---|---|
10
+ | document JSON | a document loaded from your database, written by a user |
11
+ | pasted HTML | anything the clipboard contains |
12
+ | collaborative steps | a message from another client |
13
+
14
+ Validation that lives only in `parseDOM` or only in a command is bypassed by the
15
+ other two routes. **The last gate is the rendering path**, in
16
+ `engine/model/safe-attrs.ts`, which every route passes through.
17
+
18
+ ## What is enforced
19
+
20
+ - **Executable attributes are never set.** Anything matching `on*`, plus
21
+ `srcdoc`.
22
+ - **URL attributes are scheme-checked** — `href`, `src`, `xlink:href`,
23
+ `action`, `formaction`, `poster`, `data`. `javascript:`, `vbscript:` and
24
+ `data:` are refused, except `data:image/*` on an `<img>`, which is a
25
+ legitimate inline image. The tag matters: the same bytes on an `<iframe>` or
26
+ an `<object>` are a document, and an SVG document runs scripts.
27
+ - **The URL is normalised before its scheme is read.** A browser strips tab,
28
+ newline, carriage return and NUL from a URL before resolving it, so
29
+ `java&#9;script:` *is* `javascript:` by the time it matters. Testing the raw
30
+ string instead of the normalised one is how scheme filters get bypassed.
31
+ - **`target="_blank"` always carries `rel="noopener noreferrer"`**, whatever the
32
+ document said. Without it the opened page gets a `window.opener` handle and
33
+ can navigate this tab to a page that looks like your login screen.
34
+ - **Document depth is bounded to 100 levels**, in both document JSON and pasted
35
+ HTML. Parsing, resolving and rendering are all recursive; five thousand
36
+ nested blockquotes exhaust the stack and take down every client that opens
37
+ the document.
38
+ - **Undeclared attributes are dropped.** A node type that declares no `attrs`
39
+ gets none, so a type whose `toDOM` renders `node.attrs` cannot be fed
40
+ arbitrary keys through JSON.
41
+ - **Protocol-relative URLs are refused.** `//evil.example` inherits the page
42
+ protocol and leaves your site.
43
+ - **Content expressions are bounded** — repeat counts to 500, total automaton
44
+ states to 5000. `heading{1,1000000}` is a denial of service dressed as a
45
+ schema.
46
+ - **Commands never throw.** A command reports success as a boolean; one that
47
+ throws is caught, logged, and its half-built transaction discarded. A hostile
48
+ step cannot take the editor down.
49
+ - **Positions are validated** as finite integers inside the document. `NaN`
50
+ slips past naive range checks, because `NaN < 0` and `NaN > size` are both
51
+ false.
52
+
53
+ ## What is your responsibility
54
+
55
+ - **The document model keeps what it was given.** The gate runs on the way out,
56
+ not on the way in, so a hostile `href` survives in `getJSON()` even though it
57
+ never reaches the DOM. This is deliberate — sanitising at load silently
58
+ destroys data — but it means *your own* renderer needs the same care.
59
+ - **`getHTML()` output still needs a policy at rest.** It is safe to render in
60
+ the editor; if you store it and serve it elsewhere, apply your own sanitiser
61
+ at that boundary too. Defence in depth is the point.
62
+ - **Node views and widget decorations are your code.** The editor keeps their
63
+ DOM out of the document, but it cannot audit what you build inside them. Do
64
+ not `innerHTML` untrusted strings there.
65
+ - **A custom `toDOM` should still validate.** The gate above will refuse an
66
+ executable attribute, but it cannot know that your `data-user-id` came from
67
+ somewhere it should not have.
68
+
69
+ ## Reporting
70
+
71
+ Open an issue at https://github.com/amrelaco/matra/issues, or email
72
+ security@amrela.co for anything you would rather not post publicly.
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@matrajs/mcp",
3
+ "version": "1.0.0",
4
+ "description": "The Matra documentation as a Model Context Protocol server, so any AI tool can read it. Zero dependencies.",
5
+ "license": "MIT",
6
+ "author": "Nahim Hossain Shohan",
7
+ "homepage": "https://matrajs.com/docs/mcp",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/amrelaco/matra.git",
11
+ "directory": "packages/mcp"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/amrelaco/matra/issues"
15
+ },
16
+ "keywords": [
17
+ "mcp",
18
+ "model-context-protocol",
19
+ "docs",
20
+ "matra",
21
+ "editor",
22
+ "ai"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "type": "module",
28
+ "sideEffects": false,
29
+ "main": "./dist/index.cjs",
30
+ "module": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "bin": {
33
+ "matra-mcp": "./dist/cli.js"
34
+ },
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "import": "./dist/index.js",
39
+ "require": "./dist/index.cjs"
40
+ }
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "docs",
45
+ "README.md"
46
+ ],
47
+ "engines": {
48
+ "node": ">=18"
49
+ },
50
+ "scripts": {
51
+ "build": "node ../../scripts/docs-bundle.mjs && tsup",
52
+ "clean": "rm -rf dist"
53
+ }
54
+ }