@ai-matrx/code-runtime 0.1.4 → 0.3.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.
- package/CHANGELOG.md +48 -0
- package/dist/execute.cjs +20 -4
- package/dist/execute.cjs.map +1 -1
- package/dist/execute.js +20 -4
- package/dist/execute.js.map +1 -1
- package/dist/gate-lists.json +1 -0
- package/dist/gate.cjs +1 -0
- package/dist/gate.cjs.map +1 -1
- package/dist/gate.js +1 -0
- package/dist/gate.js.map +1 -1
- package/dist/index.cjs +20 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +20 -4
- package/dist/index.js.map +1 -1
- package/dist/scope.cjs +13 -3
- package/dist/scope.cjs.map +1 -1
- package/dist/scope.js +13 -3
- package/dist/scope.js.map +1 -1
- package/package.json +5 -4
package/dist/gate.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/gate.ts","../src/gate-lists.json"],"sourcesContent":["/**\n * `@ai-matrx/code-runtime/gate` — THE ONE source gate for stored code.\n *\n * Every writer of stored component code (kind components, tool and emit\n * renderers, Applet files) runs `gate(files)` before the write; a database\n * trigger is the backstop and aidream's `kind_shared.py` is the Python twin.\n * The lists are DATA in `gate-lists.json` (exported as\n * `@ai-matrx/code-runtime/gate-lists.json`) so all three runtimes compare the\n * same bytes; parity tests on each side read this file.\n *\n * Moved here verbatim in behavior from matrx-frontend\n * `features/agent-apps/utils/component-source-gate.ts` (Q82 / B-17, DD-124),\n * widened to multi-file sources: a relative import must name a file in the\n * set, and every finding carries its file and line.\n *\n * WHAT THIS IS NOT: a sandbox. A string check cannot stop every escape, and\n * the runtime stubs shadow bare identifiers only. Real isolation is the\n * sandbox frame (opaque origin, `connect-src 'none'`).\n *\n * Pure: no React, no Babel — safe for servers, workers and write paths.\n */\nimport lists from \"./gate-lists.json\";\nimport type { Finding } from \"./types\";\n\nexport type { Finding } from \"./types\";\n\n/** Every module stored code may import (the documented scope registry). */\nexport const GATE_ALLOWED_IMPORTS: readonly string[] = lists.allowedImports;\n/** Globals stored code may never name as a bare identifier. */\nexport const GATE_BANNED_GLOBALS: readonly string[] = lists.bannedGlobals;\n/** Evaluators banned where called or constructed (`eval(`, `new Function(`). */\nexport const GATE_BANNED_CALLABLES: readonly string[] = lists.bannedCallables;\n/** Names banned as a property read (`window.fetch`, `document.cookie`). */\nexport const GATE_BANNED_MEMBER_ACCESS: readonly string[] = lists.bannedMemberAccess;\n/** Names banned as a method call on any object (`.constructor(`). */\nexport const GATE_BANNED_MEMBER_CALLS: readonly string[] = lists.bannedMemberCalls;\n/** Roots on which bracket access is refused (`window[\"fet\"+\"ch\"]`). */\nexport const GATE_BANNED_COMPUTED_ACCESS: readonly string[] = lists.bannedComputedAccess;\n/** Named syntax rules every runtime implements (`dynamicImport`). */\nexport const GATE_BANNED_SYNTAX: readonly string[] = lists.bannedSyntax;\n\n/** The raw lists, for parity tests and the database trigger's generator. */\nexport const GATE_LISTS = lists;\n\nconst IMPORT_RE = /^\\s*import\\s+(?:[^'\"]*?\\s+from\\s+)?['\"]([^'\"]+)['\"]/gm;\nconst EXPORT_FROM_RE = /^\\s*export\\s+(?:\\*|\\{[^}]*\\})\\s+from\\s+['\"]([^'\"]+)['\"]/gm;\nconst DYNAMIC_IMPORT_RE = /(?<![.\\w$])import\\s*\\(/g;\n\nfunction escapeForRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction lineAt(source: string, index: number): number {\n let line = 1;\n for (let i = 0; i < index && i < source.length; i++) {\n if (source.charCodeAt(i) === 10) line++;\n }\n return line;\n}\n\n/** Normalize a relative specifier against the importing file, without extension guessing. */\nexport function resolveRelativePath(fromFile: string, specifier: string): string {\n const base = fromFile.split(\"/\").slice(0, -1);\n for (const part of specifier.split(\"/\")) {\n if (part === \"\" || part === \".\") continue;\n if (part === \"..\") base.pop();\n else base.push(part);\n }\n return base.join(\"/\");\n}\n\nconst RESOLVE_SUFFIXES = [\"\", \".tsx\", \".ts\", \".jsx\", \".js\", \".json\", \"/index.tsx\", \"/index.ts\", \"/index.jsx\", \"/index.js\"];\n\n/** The file in `files` a relative specifier names, or null. */\nexport function resolveRelativeFile(\n files: Readonly<Record<string, string>>,\n fromFile: string,\n specifier: string,\n): string | null {\n const target = resolveRelativePath(fromFile, specifier);\n for (const suffix of RESOLVE_SUFFIXES) {\n const candidate = `${target}${suffix}`;\n if (Object.prototype.hasOwnProperty.call(files, candidate)) return candidate;\n }\n return null;\n}\n\n/** The module paths a source imports, in source order, de-duplicated. */\nexport function importPaths(source: string): string[] {\n const found = new Set<string>();\n for (const re of [IMPORT_RE, EXPORT_FROM_RE]) {\n re.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = re.exec(source ?? \"\")) !== null) {\n if (match[1]) found.add(match[1]);\n }\n }\n return [...found];\n}\n\nconst ALLOWED_SENTENCE =\n \"Rewrite it using only the documented scope (react, lucide-react, recharts, \" +\n \"@ai-matrx/design-system/controls, @ai-matrx/design-system, @/lib/utils, \" +\n \"@/components/ui/*, @/components/kind-kit/*, CopyButtons/CopyForAiButton, Markdown) \" +\n \"or a file of this component.\";\n\nfunction importFindings(files: Readonly<Record<string, string>>, file: string, source: string): Finding[] {\n const out: Finding[] = [];\n const allowed = new Set(GATE_ALLOWED_IMPORTS);\n for (const re of [IMPORT_RE, EXPORT_FROM_RE]) {\n re.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = re.exec(source)) !== null) {\n const path = match[1] ?? \"\";\n const line = lineAt(source, match.index + match[0].indexOf(path));\n if (path.startsWith(\"./\") || path.startsWith(\"../\")) {\n if (!resolveRelativeFile(files, file, path)) {\n out.push({\n rule: \"relative-import-missing\",\n file,\n line,\n severity: \"refuse\",\n message: `This file imports \"${path}\", but no file of this component has that name. Add the file or fix the path.`,\n });\n }\n continue;\n }\n if (!allowed.has(path)) {\n out.push({\n rule: \"import-not-allowed\",\n file,\n line,\n severity: \"refuse\",\n message: `This component imports \"${path}\", which is outside the sandbox scope, so it would render as a marked placeholder. ${ALLOWED_SENTENCE}`,\n });\n }\n }\n }\n DYNAMIC_IMPORT_RE.lastIndex = 0;\n const dyn = DYNAMIC_IMPORT_RE.exec(source);\n if (dyn) {\n out.push({\n rule: \"dynamicImport\",\n file,\n line: lineAt(source, dyn.index),\n severity: \"refuse\",\n message:\n \"This component uses a dynamic import(). The in-page compiler has no module loader, so the call would either fail at render or reach code nobody vetted. Import from the scope at the top of the file instead.\",\n });\n }\n return out;\n}\n\nfunction bannedGlobalMessage(name: string): string {\n return (\n `This component uses \"${name}\", which components stored in the database may ` +\n \"not use. They run inside the signed-in page, so anything that reaches the \" +\n \"network, browser storage, or the JavaScript evaluator could read or send \" +\n \"the reader's data. Render what the Shape hands you in props.data and use \" +\n \"a Shape action for anything the component needs from the server.\"\n );\n}\n\nfunction bannedMemberCallMessage(name: string): string {\n return (\n `This component uses \".${name}(\", which components stored in the database ` +\n \"may not use. Calling a value's constructor reaches the JavaScript \" +\n \"evaluator, so it could run code nobody reviewed inside the signed-in \" +\n \"page. Write the logic out directly instead.\"\n );\n}\n\nfunction bannedComputedAccessMessage(name: string): string {\n return (\n `This component uses \"${name}[\", which components stored in the database ` +\n \"may not use. Looking a property up by a computed name hides which \" +\n \"browser capability the component reaches, so no review can tell whether \" +\n `it is safe. Name what you need directly, or use props.data and a Shape ` +\n \"action for anything the component needs from the server.\"\n );\n}\n\nfunction globalFindings(file: string, code: string): Finding[] {\n const out: Finding[] = [];\n const push = (rule: string, re: RegExp, message: string) => {\n const m = re.exec(code);\n if (m) out.push({ rule, file, line: lineAt(code, m.index), severity: \"refuse\", message });\n };\n for (const name of GATE_BANNED_GLOBALS) {\n push(\"bannedGlobal\", new RegExp(`(?<![.\\\\w$])${escapeForRegExp(name)}\\\\b`), bannedGlobalMessage(name));\n }\n for (const name of GATE_BANNED_CALLABLES) {\n const e = escapeForRegExp(name);\n push(\"bannedCallable\", new RegExp(`\\\\bnew\\\\s+${e}\\\\s*\\\\(|(?<![.\\\\w$])${e}\\\\s*\\\\(`), bannedGlobalMessage(name));\n }\n for (const name of GATE_BANNED_MEMBER_ACCESS) {\n push(\"bannedMemberAccess\", new RegExp(`\\\\.\\\\s*${escapeForRegExp(name)}\\\\b`), bannedGlobalMessage(name));\n }\n for (const name of GATE_BANNED_MEMBER_CALLS) {\n push(\"bannedMemberCall\", new RegExp(`\\\\.\\\\s*${escapeForRegExp(name)}\\\\s*\\\\(`), bannedMemberCallMessage(name));\n }\n for (const name of GATE_BANNED_COMPUTED_ACCESS) {\n const e = escapeForRegExp(name);\n push(\"bannedComputedAccess\", new RegExp(`(?<![.\\\\w$])${e}\\\\s*\\\\[|\\\\.\\\\s*${e}\\\\s*\\\\[`), bannedComputedAccessMessage(name));\n }\n return out;\n}\n\n/**\n * THE ONE GATE (CONTRACTS §7). Every finding names its file and line; any\n * `refuse` finding means the code may not be stored. `.html` files and\n * `.json` files are not compiled in the page and are skipped.\n */\nexport function gate(files: Readonly<Record<string, string>>): Finding[] {\n const out: Finding[] = [];\n for (const [file, source] of Object.entries(files)) {\n if (/\\.(html?|json|css|md)$/i.test(file)) continue;\n const code = source ?? \"\";\n out.push(...importFindings(files, file, code), ...globalFindings(file, code));\n }\n return out;\n}\n\n/**\n * The single-body form today's writers call: the first refusing sentence, or\n * null when the source may be stored. `flavor: \"html\"` bodies render in an\n * opaque-origin iframe and are not checked.\n */\nexport function componentSourceGate(source: string, options?: { flavor?: string | null }): string | null {\n if ((options?.flavor ?? \"\").toLowerCase() === \"html\") return null;\n const finding = gate({ \"component.tsx\": source ?? \"\" }).find((f) => f.severity === \"refuse\");\n return finding ? finding.message : null;\n}\n","{\n \"_comment\": [\n \"CANONICAL, MACHINE-READABLE source-gate lists for stored code compiled by @ai-matrx/code-runtime.\",\n \"ONE list per rule, sorted, byte-comparable across runtimes. Three guards keep the copies honest:\",\n \" - src/gate.test.ts asserts allowedImports equals listScopeEntryPaths() sorted\",\n \" (the documented scope registry is the truth);\",\n \" - aidream packages/matrx-ai/tests/test_component_source_gate_parity.py asserts the Python\",\n \" twins in kind_shared.py equal these arrays;\",\n \" - the database trigger's parity test asserts the trigger names every entry of every\",\n \" security list below.\"\n ],\n \"allowedImports\": [\n \"@/applets\",\n \"@/components/Markdown\",\n \"@/components/MarkdownStream\",\n \"@/components/agent-copy/CopyButtons\",\n \"@/components/agent-copy/CopyForAiButton\",\n \"@/components/kind-kit/KindHeaderBar\",\n \"@/components/kind-kit/KindPanel\",\n \"@/components/kind-kit/KindPanelGrid\",\n \"@/components/kind-kit/SortableList\",\n \"@/components/kind-kit/StreamingSkeleton\",\n \"@/components/kind-kit/TagList\",\n \"@/components/mardown\",\n \"@/components/markdown\",\n \"@/components/official/icons/IconResolver\",\n \"@/components/ui/accordion\",\n \"@/components/ui/alert\",\n \"@/components/ui/avatar\",\n \"@/components/ui/badge\",\n \"@/components/ui/button\",\n \"@/components/ui/card\",\n \"@/components/ui/checkbox\",\n \"@/components/ui/collapsible\",\n \"@/components/ui/dialog\",\n \"@/components/ui/dropdown-menu\",\n \"@/components/ui/input\",\n \"@/components/ui/label\",\n \"@/components/ui/popover\",\n \"@/components/ui/progress\",\n \"@/components/ui/radio-group\",\n \"@/components/ui/scroll-area\",\n \"@/components/ui/select\",\n \"@/components/ui/separator\",\n \"@/components/ui/sheet\",\n \"@/components/ui/skeleton\",\n \"@/components/ui/slider\",\n \"@/components/ui/switch\",\n \"@/components/ui/table\",\n \"@/components/ui/tabs\",\n \"@/components/ui/textarea\",\n \"@/components/ui/tooltip\",\n \"@/lib/utils\",\n \"@ai-matrx/applets/react\",\n \"@ai-matrx/design-system\",\n \"@ai-matrx/design-system/controls\",\n \"@ai-matrx/icons\",\n \"@react-three/fiber\",\n \"date-fns\",\n \"lodash\",\n \"lucide-react\",\n \"motion/react\",\n \"react\",\n \"react-katex\",\n \"react-pdf\",\n \"recharts\",\n \"three\",\n \"xlsx\"\n ],\n \"bannedGlobals\": [\n \"EventSource\",\n \"WebSocket\",\n \"XMLHttpRequest\",\n \"fetch\",\n \"importScripts\",\n \"localStorage\",\n \"sessionStorage\"\n ],\n \"bannedCallables\": [\n \"Function\",\n \"eval\"\n ],\n \"bannedMemberAccess\": [\n \"EventSource\",\n \"Function\",\n \"WebSocket\",\n \"XMLHttpRequest\",\n \"cookie\",\n \"eval\",\n \"fetch\",\n \"importScripts\",\n \"sendBeacon\"\n ],\n \"bannedMemberCalls\": [\n \"constructor\"\n ],\n \"bannedComputedAccess\": [\n \"globalThis\",\n \"self\",\n \"window\"\n ],\n \"bannedSyntax\": [\n \"dynamicImport\"\n ]\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/gate.ts","../src/gate-lists.json"],"sourcesContent":["/**\n * `@ai-matrx/code-runtime/gate` — THE ONE source gate for stored code.\n *\n * Every writer of stored component code (kind components, tool and emit\n * renderers, Applet files) runs `gate(files)` before the write; a database\n * trigger is the backstop and aidream's `kind_shared.py` is the Python twin.\n * The lists are DATA in `gate-lists.json` (exported as\n * `@ai-matrx/code-runtime/gate-lists.json`) so all three runtimes compare the\n * same bytes; parity tests on each side read this file.\n *\n * Moved here verbatim in behavior from matrx-frontend\n * `features/agent-apps/utils/component-source-gate.ts` (Q82 / B-17, DD-124),\n * widened to multi-file sources: a relative import must name a file in the\n * set, and every finding carries its file and line.\n *\n * WHAT THIS IS NOT: a sandbox. A string check cannot stop every escape, and\n * the runtime stubs shadow bare identifiers only. Real isolation is the\n * sandbox frame (opaque origin, `connect-src 'none'`).\n *\n * Pure: no React, no Babel — safe for servers, workers and write paths.\n */\nimport lists from \"./gate-lists.json\";\nimport type { Finding } from \"./types\";\n\nexport type { Finding } from \"./types\";\n\n/** Every module stored code may import (the documented scope registry). */\nexport const GATE_ALLOWED_IMPORTS: readonly string[] = lists.allowedImports;\n/** Globals stored code may never name as a bare identifier. */\nexport const GATE_BANNED_GLOBALS: readonly string[] = lists.bannedGlobals;\n/** Evaluators banned where called or constructed (`eval(`, `new Function(`). */\nexport const GATE_BANNED_CALLABLES: readonly string[] = lists.bannedCallables;\n/** Names banned as a property read (`window.fetch`, `document.cookie`). */\nexport const GATE_BANNED_MEMBER_ACCESS: readonly string[] = lists.bannedMemberAccess;\n/** Names banned as a method call on any object (`.constructor(`). */\nexport const GATE_BANNED_MEMBER_CALLS: readonly string[] = lists.bannedMemberCalls;\n/** Roots on which bracket access is refused (`window[\"fet\"+\"ch\"]`). */\nexport const GATE_BANNED_COMPUTED_ACCESS: readonly string[] = lists.bannedComputedAccess;\n/** Named syntax rules every runtime implements (`dynamicImport`). */\nexport const GATE_BANNED_SYNTAX: readonly string[] = lists.bannedSyntax;\n\n/** The raw lists, for parity tests and the database trigger's generator. */\nexport const GATE_LISTS = lists;\n\nconst IMPORT_RE = /^\\s*import\\s+(?:[^'\"]*?\\s+from\\s+)?['\"]([^'\"]+)['\"]/gm;\nconst EXPORT_FROM_RE = /^\\s*export\\s+(?:\\*|\\{[^}]*\\})\\s+from\\s+['\"]([^'\"]+)['\"]/gm;\nconst DYNAMIC_IMPORT_RE = /(?<![.\\w$])import\\s*\\(/g;\n\nfunction escapeForRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction lineAt(source: string, index: number): number {\n let line = 1;\n for (let i = 0; i < index && i < source.length; i++) {\n if (source.charCodeAt(i) === 10) line++;\n }\n return line;\n}\n\n/** Normalize a relative specifier against the importing file, without extension guessing. */\nexport function resolveRelativePath(fromFile: string, specifier: string): string {\n const base = fromFile.split(\"/\").slice(0, -1);\n for (const part of specifier.split(\"/\")) {\n if (part === \"\" || part === \".\") continue;\n if (part === \"..\") base.pop();\n else base.push(part);\n }\n return base.join(\"/\");\n}\n\nconst RESOLVE_SUFFIXES = [\"\", \".tsx\", \".ts\", \".jsx\", \".js\", \".json\", \"/index.tsx\", \"/index.ts\", \"/index.jsx\", \"/index.js\"];\n\n/** The file in `files` a relative specifier names, or null. */\nexport function resolveRelativeFile(\n files: Readonly<Record<string, string>>,\n fromFile: string,\n specifier: string,\n): string | null {\n const target = resolveRelativePath(fromFile, specifier);\n for (const suffix of RESOLVE_SUFFIXES) {\n const candidate = `${target}${suffix}`;\n if (Object.prototype.hasOwnProperty.call(files, candidate)) return candidate;\n }\n return null;\n}\n\n/** The module paths a source imports, in source order, de-duplicated. */\nexport function importPaths(source: string): string[] {\n const found = new Set<string>();\n for (const re of [IMPORT_RE, EXPORT_FROM_RE]) {\n re.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = re.exec(source ?? \"\")) !== null) {\n if (match[1]) found.add(match[1]);\n }\n }\n return [...found];\n}\n\nconst ALLOWED_SENTENCE =\n \"Rewrite it using only the documented scope (react, lucide-react, recharts, \" +\n \"@ai-matrx/design-system/controls, @ai-matrx/design-system, @/lib/utils, \" +\n \"@/components/ui/*, @/components/kind-kit/*, CopyButtons/CopyForAiButton, Markdown) \" +\n \"or a file of this component.\";\n\nfunction importFindings(files: Readonly<Record<string, string>>, file: string, source: string): Finding[] {\n const out: Finding[] = [];\n const allowed = new Set(GATE_ALLOWED_IMPORTS);\n for (const re of [IMPORT_RE, EXPORT_FROM_RE]) {\n re.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = re.exec(source)) !== null) {\n const path = match[1] ?? \"\";\n const line = lineAt(source, match.index + match[0].indexOf(path));\n if (path.startsWith(\"./\") || path.startsWith(\"../\")) {\n if (!resolveRelativeFile(files, file, path)) {\n out.push({\n rule: \"relative-import-missing\",\n file,\n line,\n severity: \"refuse\",\n message: `This file imports \"${path}\", but no file of this component has that name. Add the file or fix the path.`,\n });\n }\n continue;\n }\n if (!allowed.has(path)) {\n out.push({\n rule: \"import-not-allowed\",\n file,\n line,\n severity: \"refuse\",\n message: `This component imports \"${path}\", which is outside the sandbox scope, so it would render as a marked placeholder. ${ALLOWED_SENTENCE}`,\n });\n }\n }\n }\n DYNAMIC_IMPORT_RE.lastIndex = 0;\n const dyn = DYNAMIC_IMPORT_RE.exec(source);\n if (dyn) {\n out.push({\n rule: \"dynamicImport\",\n file,\n line: lineAt(source, dyn.index),\n severity: \"refuse\",\n message:\n \"This component uses a dynamic import(). The in-page compiler has no module loader, so the call would either fail at render or reach code nobody vetted. Import from the scope at the top of the file instead.\",\n });\n }\n return out;\n}\n\nfunction bannedGlobalMessage(name: string): string {\n return (\n `This component uses \"${name}\", which components stored in the database may ` +\n \"not use. They run inside the signed-in page, so anything that reaches the \" +\n \"network, browser storage, or the JavaScript evaluator could read or send \" +\n \"the reader's data. Render what the Shape hands you in props.data and use \" +\n \"a Shape action for anything the component needs from the server.\"\n );\n}\n\nfunction bannedMemberCallMessage(name: string): string {\n return (\n `This component uses \".${name}(\", which components stored in the database ` +\n \"may not use. Calling a value's constructor reaches the JavaScript \" +\n \"evaluator, so it could run code nobody reviewed inside the signed-in \" +\n \"page. Write the logic out directly instead.\"\n );\n}\n\nfunction bannedComputedAccessMessage(name: string): string {\n return (\n `This component uses \"${name}[\", which components stored in the database ` +\n \"may not use. Looking a property up by a computed name hides which \" +\n \"browser capability the component reaches, so no review can tell whether \" +\n `it is safe. Name what you need directly, or use props.data and a Shape ` +\n \"action for anything the component needs from the server.\"\n );\n}\n\nfunction globalFindings(file: string, code: string): Finding[] {\n const out: Finding[] = [];\n const push = (rule: string, re: RegExp, message: string) => {\n const m = re.exec(code);\n if (m) out.push({ rule, file, line: lineAt(code, m.index), severity: \"refuse\", message });\n };\n for (const name of GATE_BANNED_GLOBALS) {\n push(\"bannedGlobal\", new RegExp(`(?<![.\\\\w$])${escapeForRegExp(name)}\\\\b`), bannedGlobalMessage(name));\n }\n for (const name of GATE_BANNED_CALLABLES) {\n const e = escapeForRegExp(name);\n push(\"bannedCallable\", new RegExp(`\\\\bnew\\\\s+${e}\\\\s*\\\\(|(?<![.\\\\w$])${e}\\\\s*\\\\(`), bannedGlobalMessage(name));\n }\n for (const name of GATE_BANNED_MEMBER_ACCESS) {\n push(\"bannedMemberAccess\", new RegExp(`\\\\.\\\\s*${escapeForRegExp(name)}\\\\b`), bannedGlobalMessage(name));\n }\n for (const name of GATE_BANNED_MEMBER_CALLS) {\n push(\"bannedMemberCall\", new RegExp(`\\\\.\\\\s*${escapeForRegExp(name)}\\\\s*\\\\(`), bannedMemberCallMessage(name));\n }\n for (const name of GATE_BANNED_COMPUTED_ACCESS) {\n const e = escapeForRegExp(name);\n push(\"bannedComputedAccess\", new RegExp(`(?<![.\\\\w$])${e}\\\\s*\\\\[|\\\\.\\\\s*${e}\\\\s*\\\\[`), bannedComputedAccessMessage(name));\n }\n return out;\n}\n\n/**\n * THE ONE GATE (CONTRACTS §7). Every finding names its file and line; any\n * `refuse` finding means the code may not be stored. `.html` files and\n * `.json` files are not compiled in the page and are skipped.\n */\nexport function gate(files: Readonly<Record<string, string>>): Finding[] {\n const out: Finding[] = [];\n for (const [file, source] of Object.entries(files)) {\n if (/\\.(html?|json|css|md)$/i.test(file)) continue;\n const code = source ?? \"\";\n out.push(...importFindings(files, file, code), ...globalFindings(file, code));\n }\n return out;\n}\n\n/**\n * The single-body form today's writers call: the first refusing sentence, or\n * null when the source may be stored. `flavor: \"html\"` bodies render in an\n * opaque-origin iframe and are not checked.\n */\nexport function componentSourceGate(source: string, options?: { flavor?: string | null }): string | null {\n if ((options?.flavor ?? \"\").toLowerCase() === \"html\") return null;\n const finding = gate({ \"component.tsx\": source ?? \"\" }).find((f) => f.severity === \"refuse\");\n return finding ? finding.message : null;\n}\n","{\n \"_comment\": [\n \"CANONICAL, MACHINE-READABLE source-gate lists for stored code compiled by @ai-matrx/code-runtime.\",\n \"ONE list per rule, sorted, byte-comparable across runtimes. Three guards keep the copies honest:\",\n \" - src/gate.test.ts asserts allowedImports equals listScopeEntryPaths() sorted\",\n \" (the documented scope registry is the truth);\",\n \" - aidream packages/matrx-ai/tests/test_component_source_gate_parity.py asserts the Python\",\n \" twins in kind_shared.py equal these arrays;\",\n \" - the database trigger's parity test asserts the trigger names every entry of every\",\n \" security list below.\"\n ],\n \"allowedImports\": [\n \"@/applets\",\n \"@/components/Markdown\",\n \"@/components/MarkdownStream\",\n \"@/components/agent-copy/CopyButtons\",\n \"@/components/agent-copy/CopyForAiButton\",\n \"@/components/kind-kit/KindActionButton\",\n \"@/components/kind-kit/KindHeaderBar\",\n \"@/components/kind-kit/KindPanel\",\n \"@/components/kind-kit/KindPanelGrid\",\n \"@/components/kind-kit/SortableList\",\n \"@/components/kind-kit/StreamingSkeleton\",\n \"@/components/kind-kit/TagList\",\n \"@/components/mardown\",\n \"@/components/markdown\",\n \"@/components/official/icons/IconResolver\",\n \"@/components/ui/accordion\",\n \"@/components/ui/alert\",\n \"@/components/ui/avatar\",\n \"@/components/ui/badge\",\n \"@/components/ui/button\",\n \"@/components/ui/card\",\n \"@/components/ui/checkbox\",\n \"@/components/ui/collapsible\",\n \"@/components/ui/dialog\",\n \"@/components/ui/dropdown-menu\",\n \"@/components/ui/input\",\n \"@/components/ui/label\",\n \"@/components/ui/popover\",\n \"@/components/ui/progress\",\n \"@/components/ui/radio-group\",\n \"@/components/ui/scroll-area\",\n \"@/components/ui/select\",\n \"@/components/ui/separator\",\n \"@/components/ui/sheet\",\n \"@/components/ui/skeleton\",\n \"@/components/ui/slider\",\n \"@/components/ui/switch\",\n \"@/components/ui/table\",\n \"@/components/ui/tabs\",\n \"@/components/ui/textarea\",\n \"@/components/ui/tooltip\",\n \"@/lib/utils\",\n \"@ai-matrx/applets/react\",\n \"@ai-matrx/design-system\",\n \"@ai-matrx/design-system/controls\",\n \"@ai-matrx/icons\",\n \"@react-three/fiber\",\n \"date-fns\",\n \"lodash\",\n \"lucide-react\",\n \"motion/react\",\n \"react\",\n \"react-katex\",\n \"react-pdf\",\n \"recharts\",\n \"three\",\n \"xlsx\"\n ],\n \"bannedGlobals\": [\n \"EventSource\",\n \"WebSocket\",\n \"XMLHttpRequest\",\n \"fetch\",\n \"importScripts\",\n \"localStorage\",\n \"sessionStorage\"\n ],\n \"bannedCallables\": [\n \"Function\",\n \"eval\"\n ],\n \"bannedMemberAccess\": [\n \"EventSource\",\n \"Function\",\n \"WebSocket\",\n \"XMLHttpRequest\",\n \"cookie\",\n \"eval\",\n \"fetch\",\n \"importScripts\",\n \"sendBeacon\"\n ],\n \"bannedMemberCalls\": [\n \"constructor\"\n ],\n \"bannedComputedAccess\": [\n \"globalThis\",\n \"self\",\n \"window\"\n ],\n \"bannedSyntax\": [\n \"dynamicImport\"\n ]\n}\n"],"mappings":"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","names":[]}
|
package/dist/gate.js
CHANGED
|
@@ -16,6 +16,7 @@ var gate_lists_default = {
|
|
|
16
16
|
"@/components/MarkdownStream",
|
|
17
17
|
"@/components/agent-copy/CopyButtons",
|
|
18
18
|
"@/components/agent-copy/CopyForAiButton",
|
|
19
|
+
"@/components/kind-kit/KindActionButton",
|
|
19
20
|
"@/components/kind-kit/KindHeaderBar",
|
|
20
21
|
"@/components/kind-kit/KindPanel",
|
|
21
22
|
"@/components/kind-kit/KindPanelGrid",
|
package/dist/gate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/gate-lists.json","../src/gate.ts"],"sourcesContent":["{\n \"_comment\": [\n \"CANONICAL, MACHINE-READABLE source-gate lists for stored code compiled by @ai-matrx/code-runtime.\",\n \"ONE list per rule, sorted, byte-comparable across runtimes. Three guards keep the copies honest:\",\n \" - src/gate.test.ts asserts allowedImports equals listScopeEntryPaths() sorted\",\n \" (the documented scope registry is the truth);\",\n \" - aidream packages/matrx-ai/tests/test_component_source_gate_parity.py asserts the Python\",\n \" twins in kind_shared.py equal these arrays;\",\n \" - the database trigger's parity test asserts the trigger names every entry of every\",\n \" security list below.\"\n ],\n \"allowedImports\": [\n \"@/applets\",\n \"@/components/Markdown\",\n \"@/components/MarkdownStream\",\n \"@/components/agent-copy/CopyButtons\",\n \"@/components/agent-copy/CopyForAiButton\",\n \"@/components/kind-kit/KindHeaderBar\",\n \"@/components/kind-kit/KindPanel\",\n \"@/components/kind-kit/KindPanelGrid\",\n \"@/components/kind-kit/SortableList\",\n \"@/components/kind-kit/StreamingSkeleton\",\n \"@/components/kind-kit/TagList\",\n \"@/components/mardown\",\n \"@/components/markdown\",\n \"@/components/official/icons/IconResolver\",\n \"@/components/ui/accordion\",\n \"@/components/ui/alert\",\n \"@/components/ui/avatar\",\n \"@/components/ui/badge\",\n \"@/components/ui/button\",\n \"@/components/ui/card\",\n \"@/components/ui/checkbox\",\n \"@/components/ui/collapsible\",\n \"@/components/ui/dialog\",\n \"@/components/ui/dropdown-menu\",\n \"@/components/ui/input\",\n \"@/components/ui/label\",\n \"@/components/ui/popover\",\n \"@/components/ui/progress\",\n \"@/components/ui/radio-group\",\n \"@/components/ui/scroll-area\",\n \"@/components/ui/select\",\n \"@/components/ui/separator\",\n \"@/components/ui/sheet\",\n \"@/components/ui/skeleton\",\n \"@/components/ui/slider\",\n \"@/components/ui/switch\",\n \"@/components/ui/table\",\n \"@/components/ui/tabs\",\n \"@/components/ui/textarea\",\n \"@/components/ui/tooltip\",\n \"@/lib/utils\",\n \"@ai-matrx/applets/react\",\n \"@ai-matrx/design-system\",\n \"@ai-matrx/design-system/controls\",\n \"@ai-matrx/icons\",\n \"@react-three/fiber\",\n \"date-fns\",\n \"lodash\",\n \"lucide-react\",\n \"motion/react\",\n \"react\",\n \"react-katex\",\n \"react-pdf\",\n \"recharts\",\n \"three\",\n \"xlsx\"\n ],\n \"bannedGlobals\": [\n \"EventSource\",\n \"WebSocket\",\n \"XMLHttpRequest\",\n \"fetch\",\n \"importScripts\",\n \"localStorage\",\n \"sessionStorage\"\n ],\n \"bannedCallables\": [\n \"Function\",\n \"eval\"\n ],\n \"bannedMemberAccess\": [\n \"EventSource\",\n \"Function\",\n \"WebSocket\",\n \"XMLHttpRequest\",\n \"cookie\",\n \"eval\",\n \"fetch\",\n \"importScripts\",\n \"sendBeacon\"\n ],\n \"bannedMemberCalls\": [\n \"constructor\"\n ],\n \"bannedComputedAccess\": [\n \"globalThis\",\n \"self\",\n \"window\"\n ],\n \"bannedSyntax\": [\n \"dynamicImport\"\n ]\n}\n","/**\n * `@ai-matrx/code-runtime/gate` — THE ONE source gate for stored code.\n *\n * Every writer of stored component code (kind components, tool and emit\n * renderers, Applet files) runs `gate(files)` before the write; a database\n * trigger is the backstop and aidream's `kind_shared.py` is the Python twin.\n * The lists are DATA in `gate-lists.json` (exported as\n * `@ai-matrx/code-runtime/gate-lists.json`) so all three runtimes compare the\n * same bytes; parity tests on each side read this file.\n *\n * Moved here verbatim in behavior from matrx-frontend\n * `features/agent-apps/utils/component-source-gate.ts` (Q82 / B-17, DD-124),\n * widened to multi-file sources: a relative import must name a file in the\n * set, and every finding carries its file and line.\n *\n * WHAT THIS IS NOT: a sandbox. A string check cannot stop every escape, and\n * the runtime stubs shadow bare identifiers only. Real isolation is the\n * sandbox frame (opaque origin, `connect-src 'none'`).\n *\n * Pure: no React, no Babel — safe for servers, workers and write paths.\n */\nimport lists from \"./gate-lists.json\";\nimport type { Finding } from \"./types\";\n\nexport type { Finding } from \"./types\";\n\n/** Every module stored code may import (the documented scope registry). */\nexport const GATE_ALLOWED_IMPORTS: readonly string[] = lists.allowedImports;\n/** Globals stored code may never name as a bare identifier. */\nexport const GATE_BANNED_GLOBALS: readonly string[] = lists.bannedGlobals;\n/** Evaluators banned where called or constructed (`eval(`, `new Function(`). */\nexport const GATE_BANNED_CALLABLES: readonly string[] = lists.bannedCallables;\n/** Names banned as a property read (`window.fetch`, `document.cookie`). */\nexport const GATE_BANNED_MEMBER_ACCESS: readonly string[] = lists.bannedMemberAccess;\n/** Names banned as a method call on any object (`.constructor(`). */\nexport const GATE_BANNED_MEMBER_CALLS: readonly string[] = lists.bannedMemberCalls;\n/** Roots on which bracket access is refused (`window[\"fet\"+\"ch\"]`). */\nexport const GATE_BANNED_COMPUTED_ACCESS: readonly string[] = lists.bannedComputedAccess;\n/** Named syntax rules every runtime implements (`dynamicImport`). */\nexport const GATE_BANNED_SYNTAX: readonly string[] = lists.bannedSyntax;\n\n/** The raw lists, for parity tests and the database trigger's generator. */\nexport const GATE_LISTS = lists;\n\nconst IMPORT_RE = /^\\s*import\\s+(?:[^'\"]*?\\s+from\\s+)?['\"]([^'\"]+)['\"]/gm;\nconst EXPORT_FROM_RE = /^\\s*export\\s+(?:\\*|\\{[^}]*\\})\\s+from\\s+['\"]([^'\"]+)['\"]/gm;\nconst DYNAMIC_IMPORT_RE = /(?<![.\\w$])import\\s*\\(/g;\n\nfunction escapeForRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction lineAt(source: string, index: number): number {\n let line = 1;\n for (let i = 0; i < index && i < source.length; i++) {\n if (source.charCodeAt(i) === 10) line++;\n }\n return line;\n}\n\n/** Normalize a relative specifier against the importing file, without extension guessing. */\nexport function resolveRelativePath(fromFile: string, specifier: string): string {\n const base = fromFile.split(\"/\").slice(0, -1);\n for (const part of specifier.split(\"/\")) {\n if (part === \"\" || part === \".\") continue;\n if (part === \"..\") base.pop();\n else base.push(part);\n }\n return base.join(\"/\");\n}\n\nconst RESOLVE_SUFFIXES = [\"\", \".tsx\", \".ts\", \".jsx\", \".js\", \".json\", \"/index.tsx\", \"/index.ts\", \"/index.jsx\", \"/index.js\"];\n\n/** The file in `files` a relative specifier names, or null. */\nexport function resolveRelativeFile(\n files: Readonly<Record<string, string>>,\n fromFile: string,\n specifier: string,\n): string | null {\n const target = resolveRelativePath(fromFile, specifier);\n for (const suffix of RESOLVE_SUFFIXES) {\n const candidate = `${target}${suffix}`;\n if (Object.prototype.hasOwnProperty.call(files, candidate)) return candidate;\n }\n return null;\n}\n\n/** The module paths a source imports, in source order, de-duplicated. */\nexport function importPaths(source: string): string[] {\n const found = new Set<string>();\n for (const re of [IMPORT_RE, EXPORT_FROM_RE]) {\n re.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = re.exec(source ?? \"\")) !== null) {\n if (match[1]) found.add(match[1]);\n }\n }\n return [...found];\n}\n\nconst ALLOWED_SENTENCE =\n \"Rewrite it using only the documented scope (react, lucide-react, recharts, \" +\n \"@ai-matrx/design-system/controls, @ai-matrx/design-system, @/lib/utils, \" +\n \"@/components/ui/*, @/components/kind-kit/*, CopyButtons/CopyForAiButton, Markdown) \" +\n \"or a file of this component.\";\n\nfunction importFindings(files: Readonly<Record<string, string>>, file: string, source: string): Finding[] {\n const out: Finding[] = [];\n const allowed = new Set(GATE_ALLOWED_IMPORTS);\n for (const re of [IMPORT_RE, EXPORT_FROM_RE]) {\n re.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = re.exec(source)) !== null) {\n const path = match[1] ?? \"\";\n const line = lineAt(source, match.index + match[0].indexOf(path));\n if (path.startsWith(\"./\") || path.startsWith(\"../\")) {\n if (!resolveRelativeFile(files, file, path)) {\n out.push({\n rule: \"relative-import-missing\",\n file,\n line,\n severity: \"refuse\",\n message: `This file imports \"${path}\", but no file of this component has that name. Add the file or fix the path.`,\n });\n }\n continue;\n }\n if (!allowed.has(path)) {\n out.push({\n rule: \"import-not-allowed\",\n file,\n line,\n severity: \"refuse\",\n message: `This component imports \"${path}\", which is outside the sandbox scope, so it would render as a marked placeholder. ${ALLOWED_SENTENCE}`,\n });\n }\n }\n }\n DYNAMIC_IMPORT_RE.lastIndex = 0;\n const dyn = DYNAMIC_IMPORT_RE.exec(source);\n if (dyn) {\n out.push({\n rule: \"dynamicImport\",\n file,\n line: lineAt(source, dyn.index),\n severity: \"refuse\",\n message:\n \"This component uses a dynamic import(). The in-page compiler has no module loader, so the call would either fail at render or reach code nobody vetted. Import from the scope at the top of the file instead.\",\n });\n }\n return out;\n}\n\nfunction bannedGlobalMessage(name: string): string {\n return (\n `This component uses \"${name}\", which components stored in the database may ` +\n \"not use. They run inside the signed-in page, so anything that reaches the \" +\n \"network, browser storage, or the JavaScript evaluator could read or send \" +\n \"the reader's data. Render what the Shape hands you in props.data and use \" +\n \"a Shape action for anything the component needs from the server.\"\n );\n}\n\nfunction bannedMemberCallMessage(name: string): string {\n return (\n `This component uses \".${name}(\", which components stored in the database ` +\n \"may not use. Calling a value's constructor reaches the JavaScript \" +\n \"evaluator, so it could run code nobody reviewed inside the signed-in \" +\n \"page. Write the logic out directly instead.\"\n );\n}\n\nfunction bannedComputedAccessMessage(name: string): string {\n return (\n `This component uses \"${name}[\", which components stored in the database ` +\n \"may not use. Looking a property up by a computed name hides which \" +\n \"browser capability the component reaches, so no review can tell whether \" +\n `it is safe. Name what you need directly, or use props.data and a Shape ` +\n \"action for anything the component needs from the server.\"\n );\n}\n\nfunction globalFindings(file: string, code: string): Finding[] {\n const out: Finding[] = [];\n const push = (rule: string, re: RegExp, message: string) => {\n const m = re.exec(code);\n if (m) out.push({ rule, file, line: lineAt(code, m.index), severity: \"refuse\", message });\n };\n for (const name of GATE_BANNED_GLOBALS) {\n push(\"bannedGlobal\", new RegExp(`(?<![.\\\\w$])${escapeForRegExp(name)}\\\\b`), bannedGlobalMessage(name));\n }\n for (const name of GATE_BANNED_CALLABLES) {\n const e = escapeForRegExp(name);\n push(\"bannedCallable\", new RegExp(`\\\\bnew\\\\s+${e}\\\\s*\\\\(|(?<![.\\\\w$])${e}\\\\s*\\\\(`), bannedGlobalMessage(name));\n }\n for (const name of GATE_BANNED_MEMBER_ACCESS) {\n push(\"bannedMemberAccess\", new RegExp(`\\\\.\\\\s*${escapeForRegExp(name)}\\\\b`), bannedGlobalMessage(name));\n }\n for (const name of GATE_BANNED_MEMBER_CALLS) {\n push(\"bannedMemberCall\", new RegExp(`\\\\.\\\\s*${escapeForRegExp(name)}\\\\s*\\\\(`), bannedMemberCallMessage(name));\n }\n for (const name of GATE_BANNED_COMPUTED_ACCESS) {\n const e = escapeForRegExp(name);\n push(\"bannedComputedAccess\", new RegExp(`(?<![.\\\\w$])${e}\\\\s*\\\\[|\\\\.\\\\s*${e}\\\\s*\\\\[`), bannedComputedAccessMessage(name));\n }\n return out;\n}\n\n/**\n * THE ONE GATE (CONTRACTS §7). Every finding names its file and line; any\n * `refuse` finding means the code may not be stored. `.html` files and\n * `.json` files are not compiled in the page and are skipped.\n */\nexport function gate(files: Readonly<Record<string, string>>): Finding[] {\n const out: Finding[] = [];\n for (const [file, source] of Object.entries(files)) {\n if (/\\.(html?|json|css|md)$/i.test(file)) continue;\n const code = source ?? \"\";\n out.push(...importFindings(files, file, code), ...globalFindings(file, code));\n }\n return out;\n}\n\n/**\n * The single-body form today's writers call: the first refusing sentence, or\n * null when the source may be stored. `flavor: \"html\"` bodies render in an\n * opaque-origin iframe and are not checked.\n */\nexport function componentSourceGate(source: string, options?: { flavor?: string | null }): string | null {\n if ((options?.flavor ?? \"\").toLowerCase() === \"html\") return null;\n const finding = gate({ \"component.tsx\": source ?? \"\" }).find((f) => f.severity === \"refuse\");\n return finding ? finding.message : null;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/gate-lists.json","../src/gate.ts"],"sourcesContent":["{\n \"_comment\": [\n \"CANONICAL, MACHINE-READABLE source-gate lists for stored code compiled by @ai-matrx/code-runtime.\",\n \"ONE list per rule, sorted, byte-comparable across runtimes. Three guards keep the copies honest:\",\n \" - src/gate.test.ts asserts allowedImports equals listScopeEntryPaths() sorted\",\n \" (the documented scope registry is the truth);\",\n \" - aidream packages/matrx-ai/tests/test_component_source_gate_parity.py asserts the Python\",\n \" twins in kind_shared.py equal these arrays;\",\n \" - the database trigger's parity test asserts the trigger names every entry of every\",\n \" security list below.\"\n ],\n \"allowedImports\": [\n \"@/applets\",\n \"@/components/Markdown\",\n \"@/components/MarkdownStream\",\n \"@/components/agent-copy/CopyButtons\",\n \"@/components/agent-copy/CopyForAiButton\",\n \"@/components/kind-kit/KindActionButton\",\n \"@/components/kind-kit/KindHeaderBar\",\n \"@/components/kind-kit/KindPanel\",\n \"@/components/kind-kit/KindPanelGrid\",\n \"@/components/kind-kit/SortableList\",\n \"@/components/kind-kit/StreamingSkeleton\",\n \"@/components/kind-kit/TagList\",\n \"@/components/mardown\",\n \"@/components/markdown\",\n \"@/components/official/icons/IconResolver\",\n \"@/components/ui/accordion\",\n \"@/components/ui/alert\",\n \"@/components/ui/avatar\",\n \"@/components/ui/badge\",\n \"@/components/ui/button\",\n \"@/components/ui/card\",\n \"@/components/ui/checkbox\",\n \"@/components/ui/collapsible\",\n \"@/components/ui/dialog\",\n \"@/components/ui/dropdown-menu\",\n \"@/components/ui/input\",\n \"@/components/ui/label\",\n \"@/components/ui/popover\",\n \"@/components/ui/progress\",\n \"@/components/ui/radio-group\",\n \"@/components/ui/scroll-area\",\n \"@/components/ui/select\",\n \"@/components/ui/separator\",\n \"@/components/ui/sheet\",\n \"@/components/ui/skeleton\",\n \"@/components/ui/slider\",\n \"@/components/ui/switch\",\n \"@/components/ui/table\",\n \"@/components/ui/tabs\",\n \"@/components/ui/textarea\",\n \"@/components/ui/tooltip\",\n \"@/lib/utils\",\n \"@ai-matrx/applets/react\",\n \"@ai-matrx/design-system\",\n \"@ai-matrx/design-system/controls\",\n \"@ai-matrx/icons\",\n \"@react-three/fiber\",\n \"date-fns\",\n \"lodash\",\n \"lucide-react\",\n \"motion/react\",\n \"react\",\n \"react-katex\",\n \"react-pdf\",\n \"recharts\",\n \"three\",\n \"xlsx\"\n ],\n \"bannedGlobals\": [\n \"EventSource\",\n \"WebSocket\",\n \"XMLHttpRequest\",\n \"fetch\",\n \"importScripts\",\n \"localStorage\",\n \"sessionStorage\"\n ],\n \"bannedCallables\": [\n \"Function\",\n \"eval\"\n ],\n \"bannedMemberAccess\": [\n \"EventSource\",\n \"Function\",\n \"WebSocket\",\n \"XMLHttpRequest\",\n \"cookie\",\n \"eval\",\n \"fetch\",\n \"importScripts\",\n \"sendBeacon\"\n ],\n \"bannedMemberCalls\": [\n \"constructor\"\n ],\n \"bannedComputedAccess\": [\n \"globalThis\",\n \"self\",\n \"window\"\n ],\n \"bannedSyntax\": [\n \"dynamicImport\"\n ]\n}\n","/**\n * `@ai-matrx/code-runtime/gate` — THE ONE source gate for stored code.\n *\n * Every writer of stored component code (kind components, tool and emit\n * renderers, Applet files) runs `gate(files)` before the write; a database\n * trigger is the backstop and aidream's `kind_shared.py` is the Python twin.\n * The lists are DATA in `gate-lists.json` (exported as\n * `@ai-matrx/code-runtime/gate-lists.json`) so all three runtimes compare the\n * same bytes; parity tests on each side read this file.\n *\n * Moved here verbatim in behavior from matrx-frontend\n * `features/agent-apps/utils/component-source-gate.ts` (Q82 / B-17, DD-124),\n * widened to multi-file sources: a relative import must name a file in the\n * set, and every finding carries its file and line.\n *\n * WHAT THIS IS NOT: a sandbox. A string check cannot stop every escape, and\n * the runtime stubs shadow bare identifiers only. Real isolation is the\n * sandbox frame (opaque origin, `connect-src 'none'`).\n *\n * Pure: no React, no Babel — safe for servers, workers and write paths.\n */\nimport lists from \"./gate-lists.json\";\nimport type { Finding } from \"./types\";\n\nexport type { Finding } from \"./types\";\n\n/** Every module stored code may import (the documented scope registry). */\nexport const GATE_ALLOWED_IMPORTS: readonly string[] = lists.allowedImports;\n/** Globals stored code may never name as a bare identifier. */\nexport const GATE_BANNED_GLOBALS: readonly string[] = lists.bannedGlobals;\n/** Evaluators banned where called or constructed (`eval(`, `new Function(`). */\nexport const GATE_BANNED_CALLABLES: readonly string[] = lists.bannedCallables;\n/** Names banned as a property read (`window.fetch`, `document.cookie`). */\nexport const GATE_BANNED_MEMBER_ACCESS: readonly string[] = lists.bannedMemberAccess;\n/** Names banned as a method call on any object (`.constructor(`). */\nexport const GATE_BANNED_MEMBER_CALLS: readonly string[] = lists.bannedMemberCalls;\n/** Roots on which bracket access is refused (`window[\"fet\"+\"ch\"]`). */\nexport const GATE_BANNED_COMPUTED_ACCESS: readonly string[] = lists.bannedComputedAccess;\n/** Named syntax rules every runtime implements (`dynamicImport`). */\nexport const GATE_BANNED_SYNTAX: readonly string[] = lists.bannedSyntax;\n\n/** The raw lists, for parity tests and the database trigger's generator. */\nexport const GATE_LISTS = lists;\n\nconst IMPORT_RE = /^\\s*import\\s+(?:[^'\"]*?\\s+from\\s+)?['\"]([^'\"]+)['\"]/gm;\nconst EXPORT_FROM_RE = /^\\s*export\\s+(?:\\*|\\{[^}]*\\})\\s+from\\s+['\"]([^'\"]+)['\"]/gm;\nconst DYNAMIC_IMPORT_RE = /(?<![.\\w$])import\\s*\\(/g;\n\nfunction escapeForRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nfunction lineAt(source: string, index: number): number {\n let line = 1;\n for (let i = 0; i < index && i < source.length; i++) {\n if (source.charCodeAt(i) === 10) line++;\n }\n return line;\n}\n\n/** Normalize a relative specifier against the importing file, without extension guessing. */\nexport function resolveRelativePath(fromFile: string, specifier: string): string {\n const base = fromFile.split(\"/\").slice(0, -1);\n for (const part of specifier.split(\"/\")) {\n if (part === \"\" || part === \".\") continue;\n if (part === \"..\") base.pop();\n else base.push(part);\n }\n return base.join(\"/\");\n}\n\nconst RESOLVE_SUFFIXES = [\"\", \".tsx\", \".ts\", \".jsx\", \".js\", \".json\", \"/index.tsx\", \"/index.ts\", \"/index.jsx\", \"/index.js\"];\n\n/** The file in `files` a relative specifier names, or null. */\nexport function resolveRelativeFile(\n files: Readonly<Record<string, string>>,\n fromFile: string,\n specifier: string,\n): string | null {\n const target = resolveRelativePath(fromFile, specifier);\n for (const suffix of RESOLVE_SUFFIXES) {\n const candidate = `${target}${suffix}`;\n if (Object.prototype.hasOwnProperty.call(files, candidate)) return candidate;\n }\n return null;\n}\n\n/** The module paths a source imports, in source order, de-duplicated. */\nexport function importPaths(source: string): string[] {\n const found = new Set<string>();\n for (const re of [IMPORT_RE, EXPORT_FROM_RE]) {\n re.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = re.exec(source ?? \"\")) !== null) {\n if (match[1]) found.add(match[1]);\n }\n }\n return [...found];\n}\n\nconst ALLOWED_SENTENCE =\n \"Rewrite it using only the documented scope (react, lucide-react, recharts, \" +\n \"@ai-matrx/design-system/controls, @ai-matrx/design-system, @/lib/utils, \" +\n \"@/components/ui/*, @/components/kind-kit/*, CopyButtons/CopyForAiButton, Markdown) \" +\n \"or a file of this component.\";\n\nfunction importFindings(files: Readonly<Record<string, string>>, file: string, source: string): Finding[] {\n const out: Finding[] = [];\n const allowed = new Set(GATE_ALLOWED_IMPORTS);\n for (const re of [IMPORT_RE, EXPORT_FROM_RE]) {\n re.lastIndex = 0;\n let match: RegExpExecArray | null;\n while ((match = re.exec(source)) !== null) {\n const path = match[1] ?? \"\";\n const line = lineAt(source, match.index + match[0].indexOf(path));\n if (path.startsWith(\"./\") || path.startsWith(\"../\")) {\n if (!resolveRelativeFile(files, file, path)) {\n out.push({\n rule: \"relative-import-missing\",\n file,\n line,\n severity: \"refuse\",\n message: `This file imports \"${path}\", but no file of this component has that name. Add the file or fix the path.`,\n });\n }\n continue;\n }\n if (!allowed.has(path)) {\n out.push({\n rule: \"import-not-allowed\",\n file,\n line,\n severity: \"refuse\",\n message: `This component imports \"${path}\", which is outside the sandbox scope, so it would render as a marked placeholder. ${ALLOWED_SENTENCE}`,\n });\n }\n }\n }\n DYNAMIC_IMPORT_RE.lastIndex = 0;\n const dyn = DYNAMIC_IMPORT_RE.exec(source);\n if (dyn) {\n out.push({\n rule: \"dynamicImport\",\n file,\n line: lineAt(source, dyn.index),\n severity: \"refuse\",\n message:\n \"This component uses a dynamic import(). The in-page compiler has no module loader, so the call would either fail at render or reach code nobody vetted. Import from the scope at the top of the file instead.\",\n });\n }\n return out;\n}\n\nfunction bannedGlobalMessage(name: string): string {\n return (\n `This component uses \"${name}\", which components stored in the database may ` +\n \"not use. They run inside the signed-in page, so anything that reaches the \" +\n \"network, browser storage, or the JavaScript evaluator could read or send \" +\n \"the reader's data. Render what the Shape hands you in props.data and use \" +\n \"a Shape action for anything the component needs from the server.\"\n );\n}\n\nfunction bannedMemberCallMessage(name: string): string {\n return (\n `This component uses \".${name}(\", which components stored in the database ` +\n \"may not use. Calling a value's constructor reaches the JavaScript \" +\n \"evaluator, so it could run code nobody reviewed inside the signed-in \" +\n \"page. Write the logic out directly instead.\"\n );\n}\n\nfunction bannedComputedAccessMessage(name: string): string {\n return (\n `This component uses \"${name}[\", which components stored in the database ` +\n \"may not use. Looking a property up by a computed name hides which \" +\n \"browser capability the component reaches, so no review can tell whether \" +\n `it is safe. Name what you need directly, or use props.data and a Shape ` +\n \"action for anything the component needs from the server.\"\n );\n}\n\nfunction globalFindings(file: string, code: string): Finding[] {\n const out: Finding[] = [];\n const push = (rule: string, re: RegExp, message: string) => {\n const m = re.exec(code);\n if (m) out.push({ rule, file, line: lineAt(code, m.index), severity: \"refuse\", message });\n };\n for (const name of GATE_BANNED_GLOBALS) {\n push(\"bannedGlobal\", new RegExp(`(?<![.\\\\w$])${escapeForRegExp(name)}\\\\b`), bannedGlobalMessage(name));\n }\n for (const name of GATE_BANNED_CALLABLES) {\n const e = escapeForRegExp(name);\n push(\"bannedCallable\", new RegExp(`\\\\bnew\\\\s+${e}\\\\s*\\\\(|(?<![.\\\\w$])${e}\\\\s*\\\\(`), bannedGlobalMessage(name));\n }\n for (const name of GATE_BANNED_MEMBER_ACCESS) {\n push(\"bannedMemberAccess\", new RegExp(`\\\\.\\\\s*${escapeForRegExp(name)}\\\\b`), bannedGlobalMessage(name));\n }\n for (const name of GATE_BANNED_MEMBER_CALLS) {\n push(\"bannedMemberCall\", new RegExp(`\\\\.\\\\s*${escapeForRegExp(name)}\\\\s*\\\\(`), bannedMemberCallMessage(name));\n }\n for (const name of GATE_BANNED_COMPUTED_ACCESS) {\n const e = escapeForRegExp(name);\n push(\"bannedComputedAccess\", new RegExp(`(?<![.\\\\w$])${e}\\\\s*\\\\[|\\\\.\\\\s*${e}\\\\s*\\\\[`), bannedComputedAccessMessage(name));\n }\n return out;\n}\n\n/**\n * THE ONE GATE (CONTRACTS §7). Every finding names its file and line; any\n * `refuse` finding means the code may not be stored. `.html` files and\n * `.json` files are not compiled in the page and are skipped.\n */\nexport function gate(files: Readonly<Record<string, string>>): Finding[] {\n const out: Finding[] = [];\n for (const [file, source] of Object.entries(files)) {\n if (/\\.(html?|json|css|md)$/i.test(file)) continue;\n const code = source ?? \"\";\n out.push(...importFindings(files, file, code), ...globalFindings(file, code));\n }\n return out;\n}\n\n/**\n * The single-body form today's writers call: the first refusing sentence, or\n * null when the source may be stored. `flavor: \"html\"` bodies render in an\n * opaque-origin iframe and are not checked.\n */\nexport function componentSourceGate(source: string, options?: { flavor?: string | null }): string | null {\n if ((options?.flavor ?? \"\").toLowerCase() === \"html\") return null;\n const finding = gate({ \"component.tsx\": source ?? \"\" }).find((f) => f.severity === \"refuse\");\n return finding ? finding.message : null;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -78,6 +78,7 @@ var gate_lists_default = {
|
|
|
78
78
|
"@/components/MarkdownStream",
|
|
79
79
|
"@/components/agent-copy/CopyButtons",
|
|
80
80
|
"@/components/agent-copy/CopyForAiButton",
|
|
81
|
+
"@/components/kind-kit/KindActionButton",
|
|
81
82
|
"@/components/kind-kit/KindHeaderBar",
|
|
82
83
|
"@/components/kind-kit/KindPanel",
|
|
83
84
|
"@/components/kind-kit/KindPanelGrid",
|
|
@@ -324,7 +325,7 @@ function markdownAlias(path, description) {
|
|
|
324
325
|
return { path, strategy: "named", exportMap: { default: "MarkdownStream" }, core: true, owner: "host", description };
|
|
325
326
|
}
|
|
326
327
|
function kindKit(name, exports, description) {
|
|
327
|
-
return { path: `@/components/kind-kit/${name}`, strategy: "named", exports, owner: "
|
|
328
|
+
return { path: `@/components/kind-kit/${name}`, strategy: "named", exports, owner: "package", description };
|
|
328
329
|
}
|
|
329
330
|
var SCOPE_ENTRIES = [
|
|
330
331
|
// ── Core ─────────────────────────────────────────────────────────────────
|
|
@@ -337,13 +338,15 @@ var SCOPE_ENTRIES = [
|
|
|
337
338
|
{ path: "@ai-matrx/design-system/controls", strategy: "spread", core: true, owner: "package", description: "THE controls: Button, Field, SearchField, Select, SegmentedControl, Tabs, Badge, Switch, Textarea, EmptyState\u2026" },
|
|
338
339
|
{ path: "@ai-matrx/design-system", strategy: "spread", owner: "package", description: "Design-system surfaces: Card, Dialog, Popover, Tooltip, Table, Accordion\u2026" },
|
|
339
340
|
// ── Copy actions ─────────────────────────────────────────────────────────
|
|
340
|
-
{ path: "@/components/agent-copy/CopyButtons", strategy: "named", exports: ["CopyButtons"], owner: "
|
|
341
|
+
{ path: "@/components/agent-copy/CopyButtons", strategy: "named", exports: ["CopyButtons"], owner: "package", description: "Copy / Copy-for-AI / JSON / Export bar from caller-supplied builders (the kind-kit copy-bar slot: the host's own bar when registered, a plain Copy button otherwise)" },
|
|
341
342
|
{ path: "@/components/agent-copy/CopyForAiButton", strategy: "named", exports: ["CopyForAiButton"], owner: "host", description: "Single Copy-for-AI action" },
|
|
342
|
-
// ── kind-kit
|
|
343
|
+
// ── kind-kit (@ai-matrx/content-ir-react/kind-kit; hosts register its copy
|
|
344
|
+
// bar and read notices with setKindKitHost) ───────────────────────────
|
|
343
345
|
kindKit("SortableList", ["SortableList"], "Drag-to-reorder list with arrow fallback and onRemove"),
|
|
344
346
|
kindKit("KindPanelGrid", ["KindPanelGrid"], "Content-aware auto-fit grid of equal-height panels"),
|
|
345
347
|
kindKit("KindPanel", ["KindPanel"], "Compact panel: header + overflow menu, subline, body, footer"),
|
|
346
348
|
kindKit("KindHeaderBar", ["KindHeaderBar"], "Kind title + stats + copy bar"),
|
|
349
|
+
kindKit("KindActionButton", ["KindActionButton"], "Button for the component's runAction prop: busy, optional confirm, inline error"),
|
|
347
350
|
kindKit("StreamingSkeleton", ["StreamingSkeleton", "useStreamingValue", "streamList", "streamText"], "Streaming skeletons + tolerant partial-value reads"),
|
|
348
351
|
kindKit("TagList", ["KeywordChip", "TagList"], "Wrapping chips with select, remove, inline edit, add"),
|
|
349
352
|
// ── Markdown (one renderer, four spellings stored code uses) ─────────────
|
|
@@ -432,6 +435,7 @@ var LucideModule = __toESM(require("lucide-react"), 1);
|
|
|
432
435
|
var IconsModule = __toESM(require("@ai-matrx/icons"), 1);
|
|
433
436
|
var DesignSystem = __toESM(require("@ai-matrx/design-system"), 1);
|
|
434
437
|
var Controls = __toESM(require("@ai-matrx/design-system/controls"), 1);
|
|
438
|
+
var KindKit = __toESM(require("@ai-matrx/content-ir-react/kind-kit"), 1);
|
|
435
439
|
var STORE_KEY = /* @__PURE__ */ Symbol.for("ai-matrx.code-runtime.modules");
|
|
436
440
|
function store() {
|
|
437
441
|
const g = globalThis;
|
|
@@ -456,6 +460,9 @@ var LEGACY_UI_MODULE = {
|
|
|
456
460
|
TabsListLegacy: ds.TabsList,
|
|
457
461
|
TabsTriggerLegacy: ds.TabsTrigger
|
|
458
462
|
};
|
|
463
|
+
var COPY_BUTTONS_MODULE = {
|
|
464
|
+
CopyButtons: (props) => ReactModule.createElement(KindKit.getKindKitHost().CopyButtons, props)
|
|
465
|
+
};
|
|
459
466
|
function packageLoader(path) {
|
|
460
467
|
if (path === "react") return () => ReactModule;
|
|
461
468
|
if (path === "lucide-react") return () => LucideModule;
|
|
@@ -464,6 +471,8 @@ function packageLoader(path) {
|
|
|
464
471
|
if (path === "@ai-matrx/design-system/controls") return () => Controls;
|
|
465
472
|
if (path === "@/lib/utils") return () => ({ cn: ds.cn });
|
|
466
473
|
if (path.startsWith("@/components/ui/")) return () => LEGACY_UI_MODULE;
|
|
474
|
+
if (path.startsWith("@/components/kind-kit/")) return () => KindKit;
|
|
475
|
+
if (path === "@/components/agent-copy/CopyButtons") return () => COPY_BUTTONS_MODULE;
|
|
467
476
|
if (path === "recharts") return () => import("recharts");
|
|
468
477
|
return null;
|
|
469
478
|
}
|
|
@@ -724,6 +733,7 @@ function applyToFlatScope(entry, mod, values) {
|
|
|
724
733
|
if (entry.strategy === "spread") {
|
|
725
734
|
for (const key of ownKeys(mod)) {
|
|
726
735
|
if (key === "default" || key === "__esModule") continue;
|
|
736
|
+
if (key in globalThis) continue;
|
|
727
737
|
values[key] = mod[key];
|
|
728
738
|
}
|
|
729
739
|
return;
|
|
@@ -810,7 +820,9 @@ function buildScope(spec) {
|
|
|
810
820
|
}
|
|
811
821
|
function patchMissingJsx(identifiers, state, declared, skip) {
|
|
812
822
|
for (const identifier of identifiers) {
|
|
813
|
-
if (
|
|
823
|
+
if (identifier in state.values || declared.has(identifier)) continue;
|
|
824
|
+
const providedBySpread = [...state.safeProxyPaths.values()].some(({ keys }) => keys.has(identifier));
|
|
825
|
+
if (skip.has(identifier) && !providedBySpread) continue;
|
|
814
826
|
let provided = false;
|
|
815
827
|
for (const [, { proxy, keys }] of state.safeProxyPaths) {
|
|
816
828
|
if (!keys.has(identifier)) continue;
|
|
@@ -878,6 +890,10 @@ function bindImports(mod, allFiles, state) {
|
|
|
878
890
|
} catch {
|
|
879
891
|
present = false;
|
|
880
892
|
}
|
|
893
|
+
if (!present && state.values[imported] !== void 0) {
|
|
894
|
+
view[imported] = state.values[imported];
|
|
895
|
+
present = true;
|
|
896
|
+
}
|
|
881
897
|
if (!present) {
|
|
882
898
|
if (/^[A-Z]/.test(imported)) view[imported] = createUnresolvedImportStandIn(imported, source);
|
|
883
899
|
recordUnresolved(state, { identifier: imported === local ? local : `${imported} as ${local}`, importPath: source });
|