@ai-matrx/code-runtime 0.1.4

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 @@
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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA;AAAA,EACE,UAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,gBAAkB;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,eAAiB;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,iBAAmB;AAAA,IACjB;AAAA,IACA;AAAA,EACF;AAAA,EACA,oBAAsB;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,mBAAqB;AAAA,IACnB;AAAA,EACF;AAAA,EACA,sBAAwB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAgB;AAAA,IACd;AAAA,EACF;AACF;;;AD7EO,IAAM,uBAA0C,mBAAM;AAEtD,IAAM,sBAAyC,mBAAM;AAErD,IAAM,wBAA2C,mBAAM;AAEvD,IAAM,4BAA+C,mBAAM;AAE3D,IAAM,2BAA8C,mBAAM;AAE1D,IAAM,8BAAiD,mBAAM;AAE7D,IAAM,qBAAwC,mBAAM;AAGpD,IAAM,aAAa;AAE1B,IAAM,YAAY;AAClB,IAAM,iBAAiB;AACvB,IAAM,oBAAoB;AAE1B,SAAS,gBAAgB,OAAuB;AAC9C,SAAO,MAAM,QAAQ,uBAAuB,MAAM;AACpD;AAEA,SAAS,OAAO,QAAgB,OAAuB;AACrD,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,SAAS,IAAI,OAAO,QAAQ,KAAK;AACnD,QAAI,OAAO,WAAW,CAAC,MAAM,GAAI;AAAA,EACnC;AACA,SAAO;AACT;AAGO,SAAS,oBAAoB,UAAkB,WAA2B;AAC/E,QAAM,OAAO,SAAS,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE;AAC5C,aAAW,QAAQ,UAAU,MAAM,GAAG,GAAG;AACvC,QAAI,SAAS,MAAM,SAAS,IAAK;AACjC,QAAI,SAAS,KAAM,MAAK,IAAI;AAAA,QACvB,MAAK,KAAK,IAAI;AAAA,EACrB;AACA,SAAO,KAAK,KAAK,GAAG;AACtB;AAEA,IAAM,mBAAmB,CAAC,IAAI,QAAQ,OAAO,QAAQ,OAAO,SAAS,cAAc,aAAa,cAAc,WAAW;AAGlH,SAAS,oBACd,OACA,UACA,WACe;AACf,QAAM,SAAS,oBAAoB,UAAU,SAAS;AACtD,aAAW,UAAU,kBAAkB;AACrC,UAAM,YAAY,GAAG,MAAM,GAAG,MAAM;AACpC,QAAI,OAAO,UAAU,eAAe,KAAK,OAAO,SAAS,EAAG,QAAO;AAAA,EACrE;AACA,SAAO;AACT;AAGO,SAAS,YAAY,QAA0B;AACpD,QAAM,QAAQ,oBAAI,IAAY;AAC9B,aAAW,MAAM,CAAC,WAAW,cAAc,GAAG;AAC5C,OAAG,YAAY;AACf,QAAI;AACJ,YAAQ,QAAQ,GAAG,KAAK,UAAU,EAAE,OAAO,MAAM;AAC/C,UAAI,MAAM,CAAC,EAAG,OAAM,IAAI,MAAM,CAAC,CAAC;AAAA,IAClC;AAAA,EACF;AACA,SAAO,CAAC,GAAG,KAAK;AAClB;AAEA,IAAM,mBACJ;AAKF,SAAS,eAAe,OAAyC,MAAc,QAA2B;AACxG,QAAM,MAAiB,CAAC;AACxB,QAAM,UAAU,IAAI,IAAI,oBAAoB;AAC5C,aAAW,MAAM,CAAC,WAAW,cAAc,GAAG;AAC5C,OAAG,YAAY;AACf,QAAI;AACJ,YAAQ,QAAQ,GAAG,KAAK,MAAM,OAAO,MAAM;AACzC,YAAM,OAAO,MAAM,CAAC,KAAK;AACzB,YAAM,OAAO,OAAO,QAAQ,MAAM,QAAQ,MAAM,CAAC,EAAE,QAAQ,IAAI,CAAC;AAChE,UAAI,KAAK,WAAW,IAAI,KAAK,KAAK,WAAW,KAAK,GAAG;AACnD,YAAI,CAAC,oBAAoB,OAAO,MAAM,IAAI,GAAG;AAC3C,cAAI,KAAK;AAAA,YACP,MAAM;AAAA,YACN;AAAA,YACA;AAAA,YACA,UAAU;AAAA,YACV,SAAS,sBAAsB,IAAI;AAAA,UACrC,CAAC;AAAA,QACH;AACA;AAAA,MACF;AACA,UAAI,CAAC,QAAQ,IAAI,IAAI,GAAG;AACtB,YAAI,KAAK;AAAA,UACP,MAAM;AAAA,UACN;AAAA,UACA;AAAA,UACA,UAAU;AAAA,UACV,SAAS,2BAA2B,IAAI,sFAAsF,gBAAgB;AAAA,QAChJ,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACA,oBAAkB,YAAY;AAC9B,QAAM,MAAM,kBAAkB,KAAK,MAAM;AACzC,MAAI,KAAK;AACP,QAAI,KAAK;AAAA,MACP,MAAM;AAAA,MACN;AAAA,MACA,MAAM,OAAO,QAAQ,IAAI,KAAK;AAAA,MAC9B,UAAU;AAAA,MACV,SACE;AAAA,IACJ,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,MAAsB;AACjD,SACE,wBAAwB,IAAI;AAMhC;AAEA,SAAS,wBAAwB,MAAsB;AACrD,SACE,yBAAyB,IAAI;AAKjC;AAEA,SAAS,4BAA4B,MAAsB;AACzD,SACE,wBAAwB,IAAI;AAMhC;AAEA,SAAS,eAAe,MAAc,MAAyB;AAC7D,QAAM,MAAiB,CAAC;AACxB,QAAM,OAAO,CAAC,MAAc,IAAY,YAAoB;AAC1D,UAAM,IAAI,GAAG,KAAK,IAAI;AACtB,QAAI,EAAG,KAAI,KAAK,EAAE,MAAM,MAAM,MAAM,OAAO,MAAM,EAAE,KAAK,GAAG,UAAU,UAAU,QAAQ,CAAC;AAAA,EAC1F;AACA,aAAW,QAAQ,qBAAqB;AACtC,SAAK,gBAAgB,IAAI,OAAO,eAAe,gBAAgB,IAAI,CAAC,KAAK,GAAG,oBAAoB,IAAI,CAAC;AAAA,EACvG;AACA,aAAW,QAAQ,uBAAuB;AACxC,UAAM,IAAI,gBAAgB,IAAI;AAC9B,SAAK,kBAAkB,IAAI,OAAO,aAAa,CAAC,uBAAuB,CAAC,SAAS,GAAG,oBAAoB,IAAI,CAAC;AAAA,EAC/G;AACA,aAAW,QAAQ,2BAA2B;AAC5C,SAAK,sBAAsB,IAAI,OAAO,UAAU,gBAAgB,IAAI,CAAC,KAAK,GAAG,oBAAoB,IAAI,CAAC;AAAA,EACxG;AACA,aAAW,QAAQ,0BAA0B;AAC3C,SAAK,oBAAoB,IAAI,OAAO,UAAU,gBAAgB,IAAI,CAAC,SAAS,GAAG,wBAAwB,IAAI,CAAC;AAAA,EAC9G;AACA,aAAW,QAAQ,6BAA6B;AAC9C,UAAM,IAAI,gBAAgB,IAAI;AAC9B,SAAK,wBAAwB,IAAI,OAAO,eAAe,CAAC,kBAAkB,CAAC,SAAS,GAAG,4BAA4B,IAAI,CAAC;AAAA,EAC1H;AACA,SAAO;AACT;AAOO,SAAS,KAAK,OAAoD;AACvE,QAAM,MAAiB,CAAC;AACxB,aAAW,CAAC,MAAM,MAAM,KAAK,OAAO,QAAQ,KAAK,GAAG;AAClD,QAAI,0BAA0B,KAAK,IAAI,EAAG;AAC1C,UAAM,OAAO,UAAU;AACvB,QAAI,KAAK,GAAG,eAAe,OAAO,MAAM,IAAI,GAAG,GAAG,eAAe,MAAM,IAAI,CAAC;AAAA,EAC9E;AACA,SAAO;AACT;AAOO,SAAS,oBAAoB,QAAgB,SAAqD;AACvG,OAAK,SAAS,UAAU,IAAI,YAAY,MAAM,OAAQ,QAAO;AAC7D,QAAM,UAAU,KAAK,EAAE,iBAAiB,UAAU,GAAG,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,aAAa,QAAQ;AAC3F,SAAO,UAAU,QAAQ,UAAU;AACrC;","names":[]}
@@ -0,0 +1,49 @@
1
+ import { F as Finding } from './types-DL6oTcV_.cjs';
2
+
3
+ /** Every module stored code may import (the documented scope registry). */
4
+ declare const GATE_ALLOWED_IMPORTS: readonly string[];
5
+ /** Globals stored code may never name as a bare identifier. */
6
+ declare const GATE_BANNED_GLOBALS: readonly string[];
7
+ /** Evaluators banned where called or constructed (`eval(`, `new Function(`). */
8
+ declare const GATE_BANNED_CALLABLES: readonly string[];
9
+ /** Names banned as a property read (`window.fetch`, `document.cookie`). */
10
+ declare const GATE_BANNED_MEMBER_ACCESS: readonly string[];
11
+ /** Names banned as a method call on any object (`.constructor(`). */
12
+ declare const GATE_BANNED_MEMBER_CALLS: readonly string[];
13
+ /** Roots on which bracket access is refused (`window["fet"+"ch"]`). */
14
+ declare const GATE_BANNED_COMPUTED_ACCESS: readonly string[];
15
+ /** Named syntax rules every runtime implements (`dynamicImport`). */
16
+ declare const GATE_BANNED_SYNTAX: readonly string[];
17
+ /** The raw lists, for parity tests and the database trigger's generator. */
18
+ declare const GATE_LISTS: {
19
+ _comment: string[];
20
+ allowedImports: string[];
21
+ bannedGlobals: string[];
22
+ bannedCallables: string[];
23
+ bannedMemberAccess: string[];
24
+ bannedMemberCalls: string[];
25
+ bannedComputedAccess: string[];
26
+ bannedSyntax: string[];
27
+ };
28
+ /** Normalize a relative specifier against the importing file, without extension guessing. */
29
+ declare function resolveRelativePath(fromFile: string, specifier: string): string;
30
+ /** The file in `files` a relative specifier names, or null. */
31
+ declare function resolveRelativeFile(files: Readonly<Record<string, string>>, fromFile: string, specifier: string): string | null;
32
+ /** The module paths a source imports, in source order, de-duplicated. */
33
+ declare function importPaths(source: string): string[];
34
+ /**
35
+ * THE ONE GATE (CONTRACTS §7). Every finding names its file and line; any
36
+ * `refuse` finding means the code may not be stored. `.html` files and
37
+ * `.json` files are not compiled in the page and are skipped.
38
+ */
39
+ declare function gate(files: Readonly<Record<string, string>>): Finding[];
40
+ /**
41
+ * The single-body form today's writers call: the first refusing sentence, or
42
+ * null when the source may be stored. `flavor: "html"` bodies render in an
43
+ * opaque-origin iframe and are not checked.
44
+ */
45
+ declare function componentSourceGate(source: string, options?: {
46
+ flavor?: string | null;
47
+ }): string | null;
48
+
49
+ export { Finding, GATE_ALLOWED_IMPORTS, GATE_BANNED_CALLABLES, GATE_BANNED_COMPUTED_ACCESS, GATE_BANNED_GLOBALS, GATE_BANNED_MEMBER_ACCESS, GATE_BANNED_MEMBER_CALLS, GATE_BANNED_SYNTAX, GATE_LISTS, componentSourceGate, gate, importPaths, resolveRelativeFile, resolveRelativePath };
package/dist/gate.d.ts ADDED
@@ -0,0 +1,49 @@
1
+ import { F as Finding } from './types-DL6oTcV_.js';
2
+
3
+ /** Every module stored code may import (the documented scope registry). */
4
+ declare const GATE_ALLOWED_IMPORTS: readonly string[];
5
+ /** Globals stored code may never name as a bare identifier. */
6
+ declare const GATE_BANNED_GLOBALS: readonly string[];
7
+ /** Evaluators banned where called or constructed (`eval(`, `new Function(`). */
8
+ declare const GATE_BANNED_CALLABLES: readonly string[];
9
+ /** Names banned as a property read (`window.fetch`, `document.cookie`). */
10
+ declare const GATE_BANNED_MEMBER_ACCESS: readonly string[];
11
+ /** Names banned as a method call on any object (`.constructor(`). */
12
+ declare const GATE_BANNED_MEMBER_CALLS: readonly string[];
13
+ /** Roots on which bracket access is refused (`window["fet"+"ch"]`). */
14
+ declare const GATE_BANNED_COMPUTED_ACCESS: readonly string[];
15
+ /** Named syntax rules every runtime implements (`dynamicImport`). */
16
+ declare const GATE_BANNED_SYNTAX: readonly string[];
17
+ /** The raw lists, for parity tests and the database trigger's generator. */
18
+ declare const GATE_LISTS: {
19
+ _comment: string[];
20
+ allowedImports: string[];
21
+ bannedGlobals: string[];
22
+ bannedCallables: string[];
23
+ bannedMemberAccess: string[];
24
+ bannedMemberCalls: string[];
25
+ bannedComputedAccess: string[];
26
+ bannedSyntax: string[];
27
+ };
28
+ /** Normalize a relative specifier against the importing file, without extension guessing. */
29
+ declare function resolveRelativePath(fromFile: string, specifier: string): string;
30
+ /** The file in `files` a relative specifier names, or null. */
31
+ declare function resolveRelativeFile(files: Readonly<Record<string, string>>, fromFile: string, specifier: string): string | null;
32
+ /** The module paths a source imports, in source order, de-duplicated. */
33
+ declare function importPaths(source: string): string[];
34
+ /**
35
+ * THE ONE GATE (CONTRACTS §7). Every finding names its file and line; any
36
+ * `refuse` finding means the code may not be stored. `.html` files and
37
+ * `.json` files are not compiled in the page and are skipped.
38
+ */
39
+ declare function gate(files: Readonly<Record<string, string>>): Finding[];
40
+ /**
41
+ * The single-body form today's writers call: the first refusing sentence, or
42
+ * null when the source may be stored. `flavor: "html"` bodies render in an
43
+ * opaque-origin iframe and are not checked.
44
+ */
45
+ declare function componentSourceGate(source: string, options?: {
46
+ flavor?: string | null;
47
+ }): string | null;
48
+
49
+ export { Finding, GATE_ALLOWED_IMPORTS, GATE_BANNED_CALLABLES, GATE_BANNED_COMPUTED_ACCESS, GATE_BANNED_GLOBALS, GATE_BANNED_MEMBER_ACCESS, GATE_BANNED_MEMBER_CALLS, GATE_BANNED_SYNTAX, GATE_LISTS, componentSourceGate, gate, importPaths, resolveRelativeFile, resolveRelativePath };
package/dist/gate.js ADDED
@@ -0,0 +1,268 @@
1
+ // src/gate-lists.json
2
+ var gate_lists_default = {
3
+ _comment: [
4
+ "CANONICAL, MACHINE-READABLE source-gate lists for stored code compiled by @ai-matrx/code-runtime.",
5
+ "ONE list per rule, sorted, byte-comparable across runtimes. Three guards keep the copies honest:",
6
+ " - src/gate.test.ts asserts allowedImports equals listScopeEntryPaths() sorted",
7
+ " (the documented scope registry is the truth);",
8
+ " - aidream packages/matrx-ai/tests/test_component_source_gate_parity.py asserts the Python",
9
+ " twins in kind_shared.py equal these arrays;",
10
+ " - the database trigger's parity test asserts the trigger names every entry of every",
11
+ " security list below."
12
+ ],
13
+ allowedImports: [
14
+ "@/applets",
15
+ "@/components/Markdown",
16
+ "@/components/MarkdownStream",
17
+ "@/components/agent-copy/CopyButtons",
18
+ "@/components/agent-copy/CopyForAiButton",
19
+ "@/components/kind-kit/KindHeaderBar",
20
+ "@/components/kind-kit/KindPanel",
21
+ "@/components/kind-kit/KindPanelGrid",
22
+ "@/components/kind-kit/SortableList",
23
+ "@/components/kind-kit/StreamingSkeleton",
24
+ "@/components/kind-kit/TagList",
25
+ "@/components/mardown",
26
+ "@/components/markdown",
27
+ "@/components/official/icons/IconResolver",
28
+ "@/components/ui/accordion",
29
+ "@/components/ui/alert",
30
+ "@/components/ui/avatar",
31
+ "@/components/ui/badge",
32
+ "@/components/ui/button",
33
+ "@/components/ui/card",
34
+ "@/components/ui/checkbox",
35
+ "@/components/ui/collapsible",
36
+ "@/components/ui/dialog",
37
+ "@/components/ui/dropdown-menu",
38
+ "@/components/ui/input",
39
+ "@/components/ui/label",
40
+ "@/components/ui/popover",
41
+ "@/components/ui/progress",
42
+ "@/components/ui/radio-group",
43
+ "@/components/ui/scroll-area",
44
+ "@/components/ui/select",
45
+ "@/components/ui/separator",
46
+ "@/components/ui/sheet",
47
+ "@/components/ui/skeleton",
48
+ "@/components/ui/slider",
49
+ "@/components/ui/switch",
50
+ "@/components/ui/table",
51
+ "@/components/ui/tabs",
52
+ "@/components/ui/textarea",
53
+ "@/components/ui/tooltip",
54
+ "@/lib/utils",
55
+ "@ai-matrx/applets/react",
56
+ "@ai-matrx/design-system",
57
+ "@ai-matrx/design-system/controls",
58
+ "@ai-matrx/icons",
59
+ "@react-three/fiber",
60
+ "date-fns",
61
+ "lodash",
62
+ "lucide-react",
63
+ "motion/react",
64
+ "react",
65
+ "react-katex",
66
+ "react-pdf",
67
+ "recharts",
68
+ "three",
69
+ "xlsx"
70
+ ],
71
+ bannedGlobals: [
72
+ "EventSource",
73
+ "WebSocket",
74
+ "XMLHttpRequest",
75
+ "fetch",
76
+ "importScripts",
77
+ "localStorage",
78
+ "sessionStorage"
79
+ ],
80
+ bannedCallables: [
81
+ "Function",
82
+ "eval"
83
+ ],
84
+ bannedMemberAccess: [
85
+ "EventSource",
86
+ "Function",
87
+ "WebSocket",
88
+ "XMLHttpRequest",
89
+ "cookie",
90
+ "eval",
91
+ "fetch",
92
+ "importScripts",
93
+ "sendBeacon"
94
+ ],
95
+ bannedMemberCalls: [
96
+ "constructor"
97
+ ],
98
+ bannedComputedAccess: [
99
+ "globalThis",
100
+ "self",
101
+ "window"
102
+ ],
103
+ bannedSyntax: [
104
+ "dynamicImport"
105
+ ]
106
+ };
107
+
108
+ // src/gate.ts
109
+ var GATE_ALLOWED_IMPORTS = gate_lists_default.allowedImports;
110
+ var GATE_BANNED_GLOBALS = gate_lists_default.bannedGlobals;
111
+ var GATE_BANNED_CALLABLES = gate_lists_default.bannedCallables;
112
+ var GATE_BANNED_MEMBER_ACCESS = gate_lists_default.bannedMemberAccess;
113
+ var GATE_BANNED_MEMBER_CALLS = gate_lists_default.bannedMemberCalls;
114
+ var GATE_BANNED_COMPUTED_ACCESS = gate_lists_default.bannedComputedAccess;
115
+ var GATE_BANNED_SYNTAX = gate_lists_default.bannedSyntax;
116
+ var GATE_LISTS = gate_lists_default;
117
+ var IMPORT_RE = /^\s*import\s+(?:[^'"]*?\s+from\s+)?['"]([^'"]+)['"]/gm;
118
+ var EXPORT_FROM_RE = /^\s*export\s+(?:\*|\{[^}]*\})\s+from\s+['"]([^'"]+)['"]/gm;
119
+ var DYNAMIC_IMPORT_RE = /(?<![.\w$])import\s*\(/g;
120
+ function escapeForRegExp(value) {
121
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
122
+ }
123
+ function lineAt(source, index) {
124
+ let line = 1;
125
+ for (let i = 0; i < index && i < source.length; i++) {
126
+ if (source.charCodeAt(i) === 10) line++;
127
+ }
128
+ return line;
129
+ }
130
+ function resolveRelativePath(fromFile, specifier) {
131
+ const base = fromFile.split("/").slice(0, -1);
132
+ for (const part of specifier.split("/")) {
133
+ if (part === "" || part === ".") continue;
134
+ if (part === "..") base.pop();
135
+ else base.push(part);
136
+ }
137
+ return base.join("/");
138
+ }
139
+ var RESOLVE_SUFFIXES = ["", ".tsx", ".ts", ".jsx", ".js", ".json", "/index.tsx", "/index.ts", "/index.jsx", "/index.js"];
140
+ function resolveRelativeFile(files, fromFile, specifier) {
141
+ const target = resolveRelativePath(fromFile, specifier);
142
+ for (const suffix of RESOLVE_SUFFIXES) {
143
+ const candidate = `${target}${suffix}`;
144
+ if (Object.prototype.hasOwnProperty.call(files, candidate)) return candidate;
145
+ }
146
+ return null;
147
+ }
148
+ function importPaths(source) {
149
+ const found = /* @__PURE__ */ new Set();
150
+ for (const re of [IMPORT_RE, EXPORT_FROM_RE]) {
151
+ re.lastIndex = 0;
152
+ let match;
153
+ while ((match = re.exec(source ?? "")) !== null) {
154
+ if (match[1]) found.add(match[1]);
155
+ }
156
+ }
157
+ return [...found];
158
+ }
159
+ var ALLOWED_SENTENCE = "Rewrite it using only the documented scope (react, lucide-react, recharts, @ai-matrx/design-system/controls, @ai-matrx/design-system, @/lib/utils, @/components/ui/*, @/components/kind-kit/*, CopyButtons/CopyForAiButton, Markdown) or a file of this component.";
160
+ function importFindings(files, file, source) {
161
+ const out = [];
162
+ const allowed = new Set(GATE_ALLOWED_IMPORTS);
163
+ for (const re of [IMPORT_RE, EXPORT_FROM_RE]) {
164
+ re.lastIndex = 0;
165
+ let match;
166
+ while ((match = re.exec(source)) !== null) {
167
+ const path = match[1] ?? "";
168
+ const line = lineAt(source, match.index + match[0].indexOf(path));
169
+ if (path.startsWith("./") || path.startsWith("../")) {
170
+ if (!resolveRelativeFile(files, file, path)) {
171
+ out.push({
172
+ rule: "relative-import-missing",
173
+ file,
174
+ line,
175
+ severity: "refuse",
176
+ message: `This file imports "${path}", but no file of this component has that name. Add the file or fix the path.`
177
+ });
178
+ }
179
+ continue;
180
+ }
181
+ if (!allowed.has(path)) {
182
+ out.push({
183
+ rule: "import-not-allowed",
184
+ file,
185
+ line,
186
+ severity: "refuse",
187
+ message: `This component imports "${path}", which is outside the sandbox scope, so it would render as a marked placeholder. ${ALLOWED_SENTENCE}`
188
+ });
189
+ }
190
+ }
191
+ }
192
+ DYNAMIC_IMPORT_RE.lastIndex = 0;
193
+ const dyn = DYNAMIC_IMPORT_RE.exec(source);
194
+ if (dyn) {
195
+ out.push({
196
+ rule: "dynamicImport",
197
+ file,
198
+ line: lineAt(source, dyn.index),
199
+ severity: "refuse",
200
+ message: "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."
201
+ });
202
+ }
203
+ return out;
204
+ }
205
+ function bannedGlobalMessage(name) {
206
+ return `This component uses "${name}", which components stored in the database may not use. They run inside the signed-in page, so anything that reaches the network, browser storage, or the JavaScript evaluator could read or send the reader's data. Render what the Shape hands you in props.data and use a Shape action for anything the component needs from the server.`;
207
+ }
208
+ function bannedMemberCallMessage(name) {
209
+ return `This component uses ".${name}(", which components stored in the database may not use. Calling a value's constructor reaches the JavaScript evaluator, so it could run code nobody reviewed inside the signed-in page. Write the logic out directly instead.`;
210
+ }
211
+ function bannedComputedAccessMessage(name) {
212
+ return `This component uses "${name}[", which components stored in the database may not use. Looking a property up by a computed name hides which browser capability the component reaches, so no review can tell whether it is safe. Name what you need directly, or use props.data and a Shape action for anything the component needs from the server.`;
213
+ }
214
+ function globalFindings(file, code) {
215
+ const out = [];
216
+ const push = (rule, re, message) => {
217
+ const m = re.exec(code);
218
+ if (m) out.push({ rule, file, line: lineAt(code, m.index), severity: "refuse", message });
219
+ };
220
+ for (const name of GATE_BANNED_GLOBALS) {
221
+ push("bannedGlobal", new RegExp(`(?<![.\\w$])${escapeForRegExp(name)}\\b`), bannedGlobalMessage(name));
222
+ }
223
+ for (const name of GATE_BANNED_CALLABLES) {
224
+ const e = escapeForRegExp(name);
225
+ push("bannedCallable", new RegExp(`\\bnew\\s+${e}\\s*\\(|(?<![.\\w$])${e}\\s*\\(`), bannedGlobalMessage(name));
226
+ }
227
+ for (const name of GATE_BANNED_MEMBER_ACCESS) {
228
+ push("bannedMemberAccess", new RegExp(`\\.\\s*${escapeForRegExp(name)}\\b`), bannedGlobalMessage(name));
229
+ }
230
+ for (const name of GATE_BANNED_MEMBER_CALLS) {
231
+ push("bannedMemberCall", new RegExp(`\\.\\s*${escapeForRegExp(name)}\\s*\\(`), bannedMemberCallMessage(name));
232
+ }
233
+ for (const name of GATE_BANNED_COMPUTED_ACCESS) {
234
+ const e = escapeForRegExp(name);
235
+ push("bannedComputedAccess", new RegExp(`(?<![.\\w$])${e}\\s*\\[|\\.\\s*${e}\\s*\\[`), bannedComputedAccessMessage(name));
236
+ }
237
+ return out;
238
+ }
239
+ function gate(files) {
240
+ const out = [];
241
+ for (const [file, source] of Object.entries(files)) {
242
+ if (/\.(html?|json|css|md)$/i.test(file)) continue;
243
+ const code = source ?? "";
244
+ out.push(...importFindings(files, file, code), ...globalFindings(file, code));
245
+ }
246
+ return out;
247
+ }
248
+ function componentSourceGate(source, options) {
249
+ if ((options?.flavor ?? "").toLowerCase() === "html") return null;
250
+ const finding = gate({ "component.tsx": source ?? "" }).find((f) => f.severity === "refuse");
251
+ return finding ? finding.message : null;
252
+ }
253
+ export {
254
+ GATE_ALLOWED_IMPORTS,
255
+ GATE_BANNED_CALLABLES,
256
+ GATE_BANNED_COMPUTED_ACCESS,
257
+ GATE_BANNED_GLOBALS,
258
+ GATE_BANNED_MEMBER_ACCESS,
259
+ GATE_BANNED_MEMBER_CALLS,
260
+ GATE_BANNED_SYNTAX,
261
+ GATE_LISTS,
262
+ componentSourceGate,
263
+ gate,
264
+ importPaths,
265
+ resolveRelativeFile,
266
+ resolveRelativePath
267
+ };
268
+ //# sourceMappingURL=gate.js.map
@@ -0,0 +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":[]}