Sandbox Escape via Host Error Prototype Chain in enclave-vm
Description
Enclave is a secure JavaScript sandbox designed for safe AI agent code execution. Prior to 2.7.0, there is a critical sandbox escape vulnerability in enclave-vm that allows untrusted, sandboxed JavaScript code to execute arbitrary code in the host Node.js runtime. When a tool invocation fails, enclave-vm exposes a host-side Error object to sandboxed code. This Error object retains its host realm prototype chain, which can be traversed to reach the host Function constructor. An attacker can intentionally trigger a host error, then climb the prototype chain. Using the host Function constructor, arbitrary JavaScript can be compiled and executed in the host context, fully bypassing the sandbox and granting access to sensitive resources such as process.env, filesystem, and network. This breaks enclave-vm’s core security guarantee of isolating untrusted code. This vulnerability is fixed in 2.7.0.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
enclave-vmnpm | < 2.7.0 | 2.7.0 |
Affected products
1- Range: ast-guard@1.1.0, ast-guard@1.1.1, ast-guard@1.1.2, …
Patches
1ed8bc438b2cdfix: enhance security by implementing safe error handling (#29)
63 files changed · +1608 −1406
assets/logo.dark.svg+4 −0 added@@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="136.802 895.168 608.48 615.071" fill="#fff"> + <path d="M467.762 1506.95c-21.99 5.26-43.47 4.21-64.45-3.15-4.2-1.48-9.92-3.66-14.17-5.81-20.09-10.13-38.68-19.75-55.76-28.88-23.8-12.71-42.14-22.39-55.01-29.04-15.879-8.21-39.739-20.88-58.129-30.33-11.14-5.73-19.4-10.17-24.78-13.33-11.28-6.62-19.307-11.88-24.08-15.79-1.38-1.13-4.58-4.38-9.6-9.74-3.047-3.26-5.8-6.91-8.26-10.94q-9.45-15.555-12.62-29.58c-3.14-13.9-4.13-26.3-4.1-42.52q.12-76.125.09-177.3a151.7 151.7 0 0 1 3.65-33.32c.98-4.4 2.706-9.67 5.18-15.83q2.24-5.58 4.78-9.48c2.76-4.24 4.17-8.23 7.35-12.13 4.82-5.92 10.083-10.92 15.79-14.98 5.03-3.59 8.76-6.81 13.37-9.54 9.946-5.9 19.396-11.3 28.35-16.221 20.02-11 37.87-20.211 55.86-29.851a3665 3665 0 0 1 54.029-28.35c.14-.074 3.39-1.784 9.74-5.13q5.775-3.04 14.04-7.81c5.38-3.1 10.1-5.624 14.15-7.57 10.3-4.96 21.41-11.5 31.44-15.23 11.15-4.154 21.09-6.984 29.81-8.49 12.75-2.2 25.53-1.88 38.36.96q16.575 3.669 30.99 10.05c7.82 3.46 19.14 9.266 33.97 17.42 5.8 3.193 9.83 5.376 12.08 6.55 5.9 3.06 10.87 6.33 16.2 8.91 4.72 2.286 12.48 6.37 23.29 12.25 3.47 1.893 9.07 4.826 16.78 8.8 6.55 3.38 13.48 7.4 22.33 11.9 4.81 2.446 11.03 5.76 18.64 9.94 4.76 2.611 9.88 4.701 14.58 7.631 1.18.73 3.11 1.77 5.78 3.12 14.51 7.341 26.36 13.861 35.53 19.571 3.89 2.41 9.36 6.48 16.42 12.19 20.02 16.22 31.38 39.22 33.91 64.94.85 8.55 1.28 17.11 1.29 25.69.05 29.39.71 57.74.7 86.68-.03 45-.13 75.85-.3 92.57-.14 13.35-1.25 28.27-5.96 41.31-2.83 7.85-4.78 12.77-5.83 14.79-1.79 3.44-4.73 7.91-8.81 13.42-10.64 14.36-24.43 22.62-41 32.76-6.68 4.09-13.57 7.96-20.66 11.64-19.73 10.21-33.27 17.03-43.55 22.57-11.75 6.33-27.09 14.35-46.01 24.05-2.39 1.22-4.77 2.49-7.12 3.83-2.77 1.55-16.76 8.9-41.99 22.05-2.14 1.12-5.19 2.48-7.79 3.94-14.98 8.39-31.39 16.72-48.5 20.81m-34.83-540.622c-10.49 1.253-20.75 4.183-30.76 8.79-10.54 4.853-25.61 12.691-45.2 23.521-7.62 4.211-16.82 9.131-27.59 14.771-18.33 9.6-36.73 19.96-54.329 28.99-8.214 4.2-17.11 8.99-26.69 14.35q-6.5 3.63-10.49 6.72c-4.94 3.82-8.124 6.5-9.55 8.01-3.04 3.22-5.807 6.36-8.3 9.43-5.4 6.66-9.61 16.45-11.36 24.35-1.68 7.57-1.89 15.37-1.9 23.27-.027 83.07-.02 131.41.02 145q.039 11.4 1.47 21.63c2.12 15.03 9.28 30.86 21.23 40.75 6.16 5.1 12.883 9.86 20.17 14.3q2 1.23 21.79 11.76c31.039 16.52 64.609 33.52 89.809 47.67 5.39 3.03 12.31 6.26 18.73 9.66 13.45 7.14 23.47 12.08 30.04 14.82 11.72 4.89 24.75 6.8 39.09 5.72 13.49-1.01 27.23-6.23 39.35-12.7 38.72-20.72 82.53-43.92 131.42-69.6 8.84-4.65 18-9.86 24.67-14.84 6.05-4.51 11.66-10.15 16.81-16.89 4.82-6.3 8.63-14.3 11.42-24 1.13-3.91 1.91-9.81 2.32-17.69.42-8.14.64-14.1.66-17.86.09-22.59.09-66.42-.01-131.48-.03-20.37-3.16-41.25-16.56-57.42-7.23-8.73-15.61-15.55-25.13-20.43-7.36-3.78-12.01-6.22-13.95-7.31-14.82-8.34-30.93-16.97-48.34-25.9-16.44-8.44-26.39-13.97-45.37-24.331-4.79-2.61-8.88-4.14-12.86-6.6-9.21-5.67-19.71-10.631-28.59-15.531-5.47-3.027-10.8-5.297-15.97-6.81-12.61-3.69-23.63-5.6-36.05-4.12" /> + <path d="M384.352 1277.93c1.21 2.35 3.63 4.65 7.25 6.89 23.13 14.3 48.21 18.41 75.23 12.34 19.03-4.28 35.4-12.96 49.12-26.05 4.48-4.27 10.54-11.54 18.17-21.81 1.07-1.44 3.66-4.1 7.75-7.98 3.15-3 6.16-5.11 9.02-6.36 7.33-3.2 15.23-4.04 23.69-2.53 10.4 1.86 21.14 10.25 23.95 20.92 1.57 5.98 1.79 11.51.64 16.59-1.11 4.86-3.27 9.75-6.49 14.66-11.07 16.87-24.59 31.26-40.56 43.19-1.45 1.08-4.42 3.17-8.9 6.27-2.9 2-5.98 3.91-9.25 5.7q-15.255 8.355-23.94 11.64c-38.86 14.68-78.56 16.43-119.09 5.26a151.4 151.4 0 0 1-22.51-8.18c-18.59-8.5-35.19-19.98-49.82-34.44-10.47-10.35-19.28-20.98-26.3-34.27-18.779-35.55-23.079-76.58-13.93-115.72 1.69-7.23 3.79-13.72 6.32-19.46.59-1.34 2.55-5.69 5.88-13.07 2.13-4.72 4.44-8.87 6.95-12.46 15.68-22.5 34.61-41.37 58.84-54.35 13.43-7.2 26.68-12.58 39.75-16.16 8.34-2.29 18.77-4.12 31.3-5.5 18.56-2.05 37.28-.69 56.15 4.09 12.49 3.15 26.53 7.5 35.8 12.39 2.98 1.56 6.46 3.46 10.45 5.68 10.49 5.84 20.12 13.35 29.12 21.06 5.58 4.77 9.81 9.03 12.69 12.77 3.25 4.23 6.46 8.52 9.61 12.86 13.61 18.73 13.55 42.96-5.88 57.88-7.57 5.81-16.01 9.66-23.54 13.81-3.28 1.81-6.49 3.46-9.64 4.98-22.4 10.75-37.42 18.09-45.05 22.02-15.31 7.88-39.85 19.65-77.72 38.23-6.39 3.13-14.33 7.19-23.83 12.18-5.74 3.01-9.77 6.19-11.45 12.8-.29 1.11-.32 2.19-.1 3.23.07.31.17.61.32.9m111.22-137.7c3.78-2.64 3.94-6.03 3.87-10.15-.08-4.71-4.92-8.88-8.97-11.2-17.07-9.78-35.09-13.94-54.06-12.51-15.19 1.15-29.83 4.75-42.64 12.08-6.47 3.7-10.99 6.58-13.57 8.66-5.29 4.27-10.44 9.52-15.47 15.76-7.79 9.68-13.32 20.51-16.6 32.46-1.88 6.83-2.42 14.92-2.56 22.19-.1 5.31 1.04 11.69 7.72 11.87 4.99.13 11.53-2.55 15.56-4.62 6.13-3.13 12.67-6.4 19.62-9.79 19.1-9.32 44.25-22.32 55.98-28.09q23.025-11.34 39.3-19.74c4.97-2.58 8.91-4.88 11.82-6.92" /> +</svg>
assets/logo.light.svg+4 −0 added@@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="136.802 895.168 608.48 615.071" fill="#0a0908"> + <path d="M467.762 1506.95c-21.99 5.26-43.47 4.21-64.45-3.15-4.2-1.48-9.92-3.66-14.17-5.81-20.09-10.13-38.68-19.75-55.76-28.88-23.8-12.71-42.14-22.39-55.01-29.04-15.879-8.21-39.739-20.88-58.129-30.33-11.14-5.73-19.4-10.17-24.78-13.33-11.28-6.62-19.307-11.88-24.08-15.79-1.38-1.13-4.58-4.38-9.6-9.74-3.047-3.26-5.8-6.91-8.26-10.94q-9.45-15.555-12.62-29.58c-3.14-13.9-4.13-26.3-4.1-42.52q.12-76.125.09-177.3a151.7 151.7 0 0 1 3.65-33.32c.98-4.4 2.706-9.67 5.18-15.83q2.24-5.58 4.78-9.48c2.76-4.24 4.17-8.23 7.35-12.13 4.82-5.92 10.083-10.92 15.79-14.98 5.03-3.59 8.76-6.81 13.37-9.54 9.946-5.9 19.396-11.3 28.35-16.221 20.02-11 37.87-20.211 55.86-29.851a3665 3665 0 0 1 54.029-28.35c.14-.074 3.39-1.784 9.74-5.13q5.775-3.04 14.04-7.81c5.38-3.1 10.1-5.624 14.15-7.57 10.3-4.96 21.41-11.5 31.44-15.23 11.15-4.154 21.09-6.984 29.81-8.49 12.75-2.2 25.53-1.88 38.36.96q16.575 3.669 30.99 10.05c7.82 3.46 19.14 9.266 33.97 17.42 5.8 3.193 9.83 5.376 12.08 6.55 5.9 3.06 10.87 6.33 16.2 8.91 4.72 2.286 12.48 6.37 23.29 12.25 3.47 1.893 9.07 4.826 16.78 8.8 6.55 3.38 13.48 7.4 22.33 11.9 4.81 2.446 11.03 5.76 18.64 9.94 4.76 2.611 9.88 4.701 14.58 7.631 1.18.73 3.11 1.77 5.78 3.12 14.51 7.341 26.36 13.861 35.53 19.571 3.89 2.41 9.36 6.48 16.42 12.19 20.02 16.22 31.38 39.22 33.91 64.94.85 8.55 1.28 17.11 1.29 25.69.05 29.39.71 57.74.7 86.68-.03 45-.13 75.85-.3 92.57-.14 13.35-1.25 28.27-5.96 41.31-2.83 7.85-4.78 12.77-5.83 14.79-1.79 3.44-4.73 7.91-8.81 13.42-10.64 14.36-24.43 22.62-41 32.76-6.68 4.09-13.57 7.96-20.66 11.64-19.73 10.21-33.27 17.03-43.55 22.57-11.75 6.33-27.09 14.35-46.01 24.05-2.39 1.22-4.77 2.49-7.12 3.83-2.77 1.55-16.76 8.9-41.99 22.05-2.14 1.12-5.19 2.48-7.79 3.94-14.98 8.39-31.39 16.72-48.5 20.81m-34.83-540.622c-10.49 1.253-20.75 4.183-30.76 8.79-10.54 4.853-25.61 12.691-45.2 23.521-7.62 4.211-16.82 9.131-27.59 14.771-18.33 9.6-36.73 19.96-54.329 28.99-8.214 4.2-17.11 8.99-26.69 14.35q-6.5 3.63-10.49 6.72c-4.94 3.82-8.124 6.5-9.55 8.01-3.04 3.22-5.807 6.36-8.3 9.43-5.4 6.66-9.61 16.45-11.36 24.35-1.68 7.57-1.89 15.37-1.9 23.27-.027 83.07-.02 131.41.02 145q.039 11.4 1.47 21.63c2.12 15.03 9.28 30.86 21.23 40.75 6.16 5.1 12.883 9.86 20.17 14.3q2 1.23 21.79 11.76c31.039 16.52 64.609 33.52 89.809 47.67 5.39 3.03 12.31 6.26 18.73 9.66 13.45 7.14 23.47 12.08 30.04 14.82 11.72 4.89 24.75 6.8 39.09 5.72 13.49-1.01 27.23-6.23 39.35-12.7 38.72-20.72 82.53-43.92 131.42-69.6 8.84-4.65 18-9.86 24.67-14.84 6.05-4.51 11.66-10.15 16.81-16.89 4.82-6.3 8.63-14.3 11.42-24 1.13-3.91 1.91-9.81 2.32-17.69.42-8.14.64-14.1.66-17.86.09-22.59.09-66.42-.01-131.48-.03-20.37-3.16-41.25-16.56-57.42-7.23-8.73-15.61-15.55-25.13-20.43-7.36-3.78-12.01-6.22-13.95-7.31-14.82-8.34-30.93-16.97-48.34-25.9-16.44-8.44-26.39-13.97-45.37-24.331-4.79-2.61-8.88-4.14-12.86-6.6-9.21-5.67-19.71-10.631-28.59-15.531-5.47-3.027-10.8-5.297-15.97-6.81-12.61-3.69-23.63-5.6-36.05-4.12" /> + <path d="M384.352 1277.93c1.21 2.35 3.63 4.65 7.25 6.89 23.13 14.3 48.21 18.41 75.23 12.34 19.03-4.28 35.4-12.96 49.12-26.05 4.48-4.27 10.54-11.54 18.17-21.81 1.07-1.44 3.66-4.1 7.75-7.98 3.15-3 6.16-5.11 9.02-6.36 7.33-3.2 15.23-4.04 23.69-2.53 10.4 1.86 21.14 10.25 23.95 20.92 1.57 5.98 1.79 11.51.64 16.59-1.11 4.86-3.27 9.75-6.49 14.66-11.07 16.87-24.59 31.26-40.56 43.19-1.45 1.08-4.42 3.17-8.9 6.27-2.9 2-5.98 3.91-9.25 5.7q-15.255 8.355-23.94 11.64c-38.86 14.68-78.56 16.43-119.09 5.26a151.4 151.4 0 0 1-22.51-8.18c-18.59-8.5-35.19-19.98-49.82-34.44-10.47-10.35-19.28-20.98-26.3-34.27-18.779-35.55-23.079-76.58-13.93-115.72 1.69-7.23 3.79-13.72 6.32-19.46.59-1.34 2.55-5.69 5.88-13.07 2.13-4.72 4.44-8.87 6.95-12.46 15.68-22.5 34.61-41.37 58.84-54.35 13.43-7.2 26.68-12.58 39.75-16.16 8.34-2.29 18.77-4.12 31.3-5.5 18.56-2.05 37.28-.69 56.15 4.09 12.49 3.15 26.53 7.5 35.8 12.39 2.98 1.56 6.46 3.46 10.45 5.68 10.49 5.84 20.12 13.35 29.12 21.06 5.58 4.77 9.81 9.03 12.69 12.77 3.25 4.23 6.46 8.52 9.61 12.86 13.61 18.73 13.55 42.96-5.88 57.88-7.57 5.81-16.01 9.66-23.54 13.81-3.28 1.81-6.49 3.46-9.64 4.98-22.4 10.75-37.42 18.09-45.05 22.02-15.31 7.88-39.85 19.65-77.72 38.23-6.39 3.13-14.33 7.19-23.83 12.18-5.74 3.01-9.77 6.19-11.45 12.8-.29 1.11-.32 2.19-.1 3.23.07.31.17.61.32.9m111.22-137.7c3.78-2.64 3.94-6.03 3.87-10.15-.08-4.71-4.92-8.88-8.97-11.2-17.07-9.78-35.09-13.94-54.06-12.51-15.19 1.15-29.83 4.75-42.64 12.08-6.47 3.7-10.99 6.58-13.57 8.66-5.29 4.27-10.44 9.52-15.47 15.76-7.79 9.68-13.32 20.51-16.6 32.46-1.88 6.83-2.42 14.92-2.56 22.19-.1 5.31 1.04 11.69 7.72 11.87 4.99.13 11.53-2.55 15.56-4.62 6.13-3.13 12.67-6.4 19.62-9.79 19.1-9.32 44.25-22.32 55.98-28.09q23.025-11.34 39.3-19.74c4.97-2.58 8.91-4.88 11.82-6.92" /> +</svg>
docs/docs.json+33 −8 renamed@@ -1,23 +1,39 @@ { "$schema": "https://mintlify.com/docs.json", "name": "Enclave", - "description": "Secure JavaScript execution and vector search libraries for AI agents", + "description": "Secure JavaScript sandbox for AI agent code execution. Protects against code injection, prototype pollution, and sandbox escapes.", "appearance": { - "default": "system", + "default": "dark", "strict": false }, - "theme": "almond", + "theme": "venus", "favicon": { "light": "/favicon.ico", "dark": "/favicon.ico" }, "colors": { - "primary": "#16A34A", - "light": "#07C983", - "dark": "#15803D" + "primary": "#e8a045", + "light": "#f0b865", + "dark": "#c78935", + "background": { + "dark": "#0a0908", + "light": "#fdfbf7" + } + }, + "metadata": { + "og:site_name": "Enclave Documentation", + "og:image": "https://enclave.agentfront.dev/og-image.png", + "og:locale": "en_US", + "twitter:card": "summary_large_image", + "twitter:site": "@agentfront", + "twitter:creator": "@agentfront" }, "navbar": { "links": [ + { + "href": "https://enclave.agentfront.dev", + "label": "Playground" + }, { "href": "https://github.com/agentfront/enclave", "icon": "github", @@ -29,9 +45,18 @@ "type": "github" } }, + "footer": { + "socials": { + "github": "https://github.com/agentfront/enclave", + "x": "https://x.com/agentfront" + } + }, "search": { "prompt": "Search Enclave docs..." }, + "seo": { + "indexHiddenPages": false + }, "navigation": { "dropdowns": [ { @@ -53,9 +78,9 @@ { "group": "Libraries", "pages": [ + "docs/libraries/enclave", "docs/libraries/ast-guard", - "docs/libraries/vectoriadb", - "docs/libraries/enclave-vm" + "docs/libraries/vectoriadb" ] } ]
docs/draft/assets/banners/codecall-plugin/code-call-example.png+0 −0 removeddocs/draft/assets/banners/codecall-plugin/code-call-options.dark.png+0 −0 removeddocs/draft/assets/banners/codecall-plugin/code-call-options.light.png+0 −0 removeddocs/draft/assets/banners/codecall-plugin/codecall-overview-dark.png+0 −0 removeddocs/draft/assets/banners/codecall-plugin/codecall-overview-light.png+0 −0 removeddocs/draft/assets/banners/codecall-plugin/code-call-plugin.png+0 −0 removeddocs/draft/assets/banners/codecall-plugin/early-day-vs-today.dark.png+0 −0 removeddocs/draft/assets/banners/codecall-plugin/early-day-vs-today.light.png+0 −0 removeddocs/draft/assets/banners/codecall-plugin/security-layers.dark.png+0 −0 removeddocs/draft/assets/banners/codecall-plugin/security-layers.light.png+0 −0 removeddocs/draft/assets/logo/banner.dark.png+0 −0 removeddocs/draft/assets/logo/banner.light.png+0 −0 removeddocs/draft/assets/logo/favicon-32x32.png+0 −0 removeddocs/draft/assets/logo/favicon.dark.ico+0 −0 removeddocs/draft/assets/logo/favicon.svg+0 −66 removed@@ -1,66 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" - height="1000"> - <style> - #light-icon { - display: inline; - } - - #dark-icon { - display: none; - } - - @media (prefers-color-scheme: dark) { - #light-icon { - display: none; - } - - #dark-icon { - display: inline; - } - } - </style> - <g id="light-icon"> - <svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" - height="1000"> - <g clip-path="url(#SvgjsClipPath1224)"> - <rect width="1000" height="1000" fill="#ffffff"></rect> - <g transform="matrix(1.0309278350515463,0,0,1.0309278350515463,5.684341886080802e-14,54.12371134020623)"> - <svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="970" - height="865"> - <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0.00 0.00 970.00 865.00"> - <path fill="#000000" - d="M 111.483 434.661 C 109.794 433.589 108.125 432.506 106.477 431.414 C 108.677 430.054 110.943 428.707 113.441 427.248 L 106.4 431.363 C 95.145 423.897 84.903 416.004 76.398 408.243 C 69.923 402.284 63.235 395.578 56.815 388.608 L 56.247 387.992 L 56.017 387.638 L 55.841 387.367 C 54.57 385.304 52.793 380.521 52.453 377.481 L 52.397 376.979 L 52.476 376.48 C 52.952 373.454 54.945 368.745 56.301 366.748 L 56.726 366.151 L 57.31 365.567 C 159.777 262.994 244.339 178.385 310.994 111.742 C 319.968 102.77 327.124 95.727 332.186 90.881 C 335.924 87.436 341.074 81.219 348.509 76.039 C 349.004 75.744 351.204 74.194 355.568 71.08 C 369.834 60.919 382.91 53.113 393.395 48.449 C 403.965 43.84 416.54 39.517 429.986 35.87 C 460.088 27.958 493.715 26.909 523.905 32.1 C 524.246 32.167 529.411 33.332 539.089 35.526 C 544.807 36.842 550.571 38.54 555.389 40.322 C 567.695 44.934 577.495 49.131 583.85 52.485 C 598.284 60.209 611.565 68.984 622.398 77.932 C 629.709 84.028 637.884 91.58 646.41 100.117 C 703.624 157.476 788.516 242.346 901.103 354.74 C 903.018 356.649 906.567 360.241 911.709 365.476 L 912.25 366.027 L 912.476 366.344 L 912.642 366.576 C 914.154 368.804 916.196 373.846 916.61 377.083 L 916.672 377.563 L 916.607 378.042 C 916.172 381.282 914.086 386.323 912.547 388.551 L 912.153 389.091 L 911.878 389.367 L 911.606 389.638 C 908.311 392.932 904.689 396.561 900.759 400.504 C 889.997 411.229 876.976 421.775 862.705 431.337 L 855.586 427.209 C 858.124 428.681 860.386 430.013 862.622 431.392 C 861.426 432.193 860.22 432.988 859.006 433.774 C 858.449 434.132 858.008 434.413 857.655 434.637 L 862.877 431.549 C 867.262 434.263 871.584 437.188 877.734 441.736 C 883.115 445.759 889.327 451.209 895.576 457.403 C 901.498 463.279 906.873 468.562 911.265 472.796 L 911.473 472.997 L 911.613 473.168 C 913.32 475.253 916.565 480.957 916.565 485.198 C 916.565 489.417 912.837 495.672 911.193 497.72 C 826.121 582.578 744.198 664.762 665.393 744.3 C 653.535 756.265 642.079 767.596 631.173 778.147 C 627.261 781.906 623.077 785.441 619.588 787.965 C 616.013 790.468 610.926 794.88 604.884 798.732 C 597.248 803.443 588.682 808.256 579.726 812.867 C 575.778 814.887 571.721 816.746 568.377 818.083 C 564.303 819.654 559.431 821.97 553.129 823.817 C 538.25 827.809 527.534 830.349 521.795 831.263 C 512.188 832.754 504.438 833.765 499.034 834.233 C 493.175 834.739 485.428 835.133 478.007 834.492 C 475.681 834.271 467.117 833.425 452.394 831.965 C 446.915 831.393 440.729 830.231 435.13 828.723 C 422.901 825.355 412.428 822.139 404.302 819.265 C 397.598 816.846 389.198 812.962 380.181 808.096 C 370.628 802.894 359.738 795.66 348.526 787.053 C 345.776 784.919 341.242 780.906 335.496 775.498 C 331.278 771.525 327.275 767.644 323.667 764.03 C 260.397 700.566 171.694 611.667 57.536 497.309 L 56.968 496.741 L 56.729 496.406 L 56.57 496.184 C 54.985 493.862 52.894 488.616 52.515 485.266 L 52.459 484.773 L 52.536 484.281 C 53.058 480.96 55.359 475.829 57.033 473.583 L 57.467 473.029 L 57.77 472.752 L 58.062 472.483 C 68.078 463.32 78.045 452.08 90.204 442.65 C 96.555 437.848 101.265 434.642 106.305 431.52 L 113.357 435.832 C 113.189 435.73 112.679 435.412 111.483 434.661 Z M 823.258 374.66 C 771.856 323.214 700.307 251.685 591.708 143.191 C 590.839 142.314 589.403 140.944 587.273 138.963 C 583.151 135.118 579.179 131.08 575.568 128.416 C 565.583 120.76 555.349 114.772 543.424 109.628 C 536.971 106.783 528.791 104.158 522.194 102.314 C 518.621 101.292 515.882 100.659 513.188 100.251 C 503.996 98.774 494.964 97.97 485.178 97.757 C 474.695 97.448 464.346 98.575 452.36 101.32 C 440.502 103.955 431.194 106.809 423.174 110.302 C 416.716 113.015 411.795 115.742 406.008 119.57 C 428.294 142.021 505.536 219.458 637.747 351.891 C 660.206 374.86 685.279 388.233 716.813 394.027 C 737.392 398.207 759.333 397.716 780.163 392.129 C 791.145 389.3 799.782 386.493 807.06 383.366 C 814.736 380.168 817.704 378.305 823.258 374.66 Z M 563.19 374.632 L 354.419 165.852 C 343.045 177.237 329.334 190.945 273.108 247.123 C 337.139 311.37 371.226 345.57 375.832 350.188 C 385.312 359.756 393.854 366.839 402.713 372.441 C 432.818 392.232 463.801 399.617 500.327 395.665 C 512.234 394.631 522.62 391.656 534.925 387.828 C 548.966 383.646 554.674 380.26 563.19 374.632 Z M 224.33 396.59 C 229.772 396.59 237.248 395.902 244.691 395.138 C 247.117 394.902 248.998 394.615 251.098 394.155 C 260.593 392.126 269.281 389.724 278.017 386.714 C 290.71 382.53 295.373 379.6 302.864 374.664 C 281.052 352.845 259.655 331.439 228.489 300.252 C 223.774 295.526 225.31 297.063 224.33 296.076 C 223.353 297.061 224.916 295.497 220.177 300.246 C 189.015 331.427 169.839 350.613 145.796 374.664 C 153.282 379.598 157.893 382.511 170.581 386.692 C 179.317 389.704 188.063 392.125 197.557 394.154 C 199.652 394.614 201.509 394.897 203.944 395.134 C 211.378 395.897 218.89 396.59 224.33 396.59 Z M 146.047 488.306 C 202.125 544.384 255.576 597.698 314.435 656.872 C 346.014 688.624 367.706 710.265 379.589 721.869 C 406.474 748.769 440.58 764.157 478.847 764.492 C 489.401 764.765 497.9 764.373 505.457 763.232 C 517.684 761.512 530.191 758.095 544.485 752.57 C 554.259 749.01 557.402 746.712 563.013 743.014 C 506.936 686.929 453.484 633.622 394.625 574.438 C 363.046 542.692 341.351 521.053 329.461 509.441 C 302.586 482.551 268.48 467.163 230.213 466.827 C 219.659 466.554 211.16 466.947 203.603 468.088 C 191.376 469.808 178.869 473.225 164.575 478.75 C 154.801 482.31 151.659 484.608 146.047 488.306 Z M 406.353 488.245 C 489.587 571.764 554.132 636.552 614.699 697.371 L 696.092 615.373 C 661.268 580.693 630.232 549.738 596.838 516.371 C 591.238 510.769 584.704 504.815 579.646 500.661 C 558.855 483.214 536.555 473.072 509.273 468.674 C 503.706 467.708 497.968 467.154 491.158 466.931 C 484.59 466.694 478.653 466.789 472.581 467.231 C 456.2 468.254 440.756 472.445 425.117 478.62 C 414.858 482.467 412.12 484.506 406.353 488.245 Z M 666.629 488.326 C 702.762 524.779 724.089 546.292 744.784 567.165 C 764.759 547.053 784.526 527.147 823 488.393 C 818.061 485.181 816.185 483.656 806.726 479.742 C 796.395 475.225 784.644 471.741 770.124 468.896 C 763.013 467.436 755.046 466.78 744.833 466.8 C 734.627 466.769 726.681 467.406 719.566 468.859 C 705.043 471.7 693.339 475.143 682.999 479.65 C 673.608 483.508 672.704 484.349 666.629 488.326 Z"></path> - </svg> - </svg> - </g> - </g> - <defs> - <clipPath id="SvgjsClipPath1224"> - <rect width="1000" height="1000" x="0" y="0" rx="500" ry="500"></rect> - </clipPath> - </defs> - </svg> - </g> - <g id="dark-icon"> - <svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" - height="1000"> - <g clip-path="url(#SvgjsClipPath1225)"> - <rect width="1000" height="1000" fill="#000000"></rect> - <g transform="matrix(1.0309278350515463,0,0,1.0309278350515463,5.684341886080802e-14,54.12371134020623)"> - <svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="970" - height="865"> - <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0.00 0.00 970.00 865.00"> - <path fill="#e8e8e8" - d="M 111.483 434.661 C 109.794 433.589 108.125 432.506 106.477 431.414 C 108.677 430.054 110.943 428.707 113.441 427.248 L 106.4 431.363 C 95.145 423.897 84.903 416.004 76.398 408.243 C 69.923 402.284 63.235 395.578 56.815 388.608 L 56.247 387.992 L 56.017 387.638 L 55.841 387.367 C 54.57 385.304 52.793 380.521 52.453 377.481 L 52.397 376.979 L 52.476 376.48 C 52.952 373.454 54.945 368.745 56.301 366.748 L 56.726 366.151 L 57.31 365.567 C 159.777 262.994 244.339 178.385 310.994 111.742 C 319.968 102.77 327.124 95.727 332.186 90.881 C 335.924 87.436 341.074 81.219 348.509 76.039 C 349.004 75.744 351.204 74.194 355.568 71.08 C 369.834 60.919 382.91 53.113 393.395 48.449 C 403.965 43.84 416.54 39.517 429.986 35.87 C 460.088 27.958 493.715 26.909 523.905 32.1 C 524.246 32.167 529.411 33.332 539.089 35.526 C 544.807 36.842 550.571 38.54 555.389 40.322 C 567.695 44.934 577.495 49.131 583.85 52.485 C 598.284 60.209 611.565 68.984 622.398 77.932 C 629.709 84.028 637.884 91.58 646.41 100.117 C 703.624 157.476 788.516 242.346 901.103 354.74 C 903.018 356.649 906.567 360.241 911.709 365.476 L 912.25 366.027 L 912.476 366.344 L 912.642 366.576 C 914.154 368.804 916.196 373.846 916.61 377.083 L 916.672 377.563 L 916.607 378.042 C 916.172 381.282 914.086 386.323 912.547 388.551 L 912.153 389.091 L 911.878 389.367 L 911.606 389.638 C 908.311 392.932 904.689 396.561 900.759 400.504 C 889.997 411.229 876.976 421.775 862.705 431.337 L 855.586 427.209 C 858.124 428.681 860.386 430.013 862.622 431.392 C 861.426 432.193 860.22 432.988 859.006 433.774 C 858.449 434.132 858.008 434.413 857.655 434.637 L 862.877 431.549 C 867.262 434.263 871.584 437.188 877.734 441.736 C 883.115 445.759 889.327 451.209 895.576 457.403 C 901.498 463.279 906.873 468.562 911.265 472.796 L 911.473 472.997 L 911.613 473.168 C 913.32 475.253 916.565 480.957 916.565 485.198 C 916.565 489.417 912.837 495.672 911.193 497.72 C 826.121 582.578 744.198 664.762 665.393 744.3 C 653.535 756.265 642.079 767.596 631.173 778.147 C 627.261 781.906 623.077 785.441 619.588 787.965 C 616.013 790.468 610.926 794.88 604.884 798.732 C 597.248 803.443 588.682 808.256 579.726 812.867 C 575.778 814.887 571.721 816.746 568.377 818.083 C 564.303 819.654 559.431 821.97 553.129 823.817 C 538.25 827.809 527.534 830.349 521.795 831.263 C 512.188 832.754 504.438 833.765 499.034 834.233 C 493.175 834.739 485.428 835.133 478.007 834.492 C 475.681 834.271 467.117 833.425 452.394 831.965 C 446.915 831.393 440.729 830.231 435.13 828.723 C 422.901 825.355 412.428 822.139 404.302 819.265 C 397.598 816.846 389.198 812.962 380.181 808.096 C 370.628 802.894 359.738 795.66 348.526 787.053 C 345.776 784.919 341.242 780.906 335.496 775.498 C 331.278 771.525 327.275 767.644 323.667 764.03 C 260.397 700.566 171.694 611.667 57.536 497.309 L 56.968 496.741 L 56.729 496.406 L 56.57 496.184 C 54.985 493.862 52.894 488.616 52.515 485.266 L 52.459 484.773 L 52.536 484.281 C 53.058 480.96 55.359 475.829 57.033 473.583 L 57.467 473.029 L 57.77 472.752 L 58.062 472.483 C 68.078 463.32 78.045 452.08 90.204 442.65 C 96.555 437.848 101.265 434.642 106.305 431.52 L 113.357 435.832 C 113.189 435.73 112.679 435.412 111.483 434.661 Z M 823.258 374.66 C 771.856 323.214 700.307 251.685 591.708 143.191 C 590.839 142.314 589.403 140.944 587.273 138.963 C 583.151 135.118 579.179 131.08 575.568 128.416 C 565.583 120.76 555.349 114.772 543.424 109.628 C 536.971 106.783 528.791 104.158 522.194 102.314 C 518.621 101.292 515.882 100.659 513.188 100.251 C 503.996 98.774 494.964 97.97 485.178 97.757 C 474.695 97.448 464.346 98.575 452.36 101.32 C 440.502 103.955 431.194 106.809 423.174 110.302 C 416.716 113.015 411.795 115.742 406.008 119.57 C 428.294 142.021 505.536 219.458 637.747 351.891 C 660.206 374.86 685.279 388.233 716.813 394.027 C 737.392 398.207 759.333 397.716 780.163 392.129 C 791.145 389.3 799.782 386.493 807.06 383.366 C 814.736 380.168 817.704 378.305 823.258 374.66 Z M 563.19 374.632 L 354.419 165.852 C 343.045 177.237 329.334 190.945 273.108 247.123 C 337.139 311.37 371.226 345.57 375.832 350.188 C 385.312 359.756 393.854 366.839 402.713 372.441 C 432.818 392.232 463.801 399.617 500.327 395.665 C 512.234 394.631 522.62 391.656 534.925 387.828 C 548.966 383.646 554.674 380.26 563.19 374.632 Z M 224.33 396.59 C 229.772 396.59 237.248 395.902 244.691 395.138 C 247.117 394.902 248.998 394.615 251.098 394.155 C 260.593 392.126 269.281 389.724 278.017 386.714 C 290.71 382.53 295.373 379.6 302.864 374.664 C 281.052 352.845 259.655 331.439 228.489 300.252 C 223.774 295.526 225.31 297.063 224.33 296.076 C 223.353 297.061 224.916 295.497 220.177 300.246 C 189.015 331.427 169.839 350.613 145.796 374.664 C 153.282 379.598 157.893 382.511 170.581 386.692 C 179.317 389.704 188.063 392.125 197.557 394.154 C 199.652 394.614 201.509 394.897 203.944 395.134 C 211.378 395.897 218.89 396.59 224.33 396.59 Z M 146.047 488.306 C 202.125 544.384 255.576 597.698 314.435 656.872 C 346.014 688.624 367.706 710.265 379.589 721.869 C 406.474 748.769 440.58 764.157 478.847 764.492 C 489.401 764.765 497.9 764.373 505.457 763.232 C 517.684 761.512 530.191 758.095 544.485 752.57 C 554.259 749.01 557.402 746.712 563.013 743.014 C 506.936 686.929 453.484 633.622 394.625 574.438 C 363.046 542.692 341.351 521.053 329.461 509.441 C 302.586 482.551 268.48 467.163 230.213 466.827 C 219.659 466.554 211.16 466.947 203.603 468.088 C 191.376 469.808 178.869 473.225 164.575 478.75 C 154.801 482.31 151.659 484.608 146.047 488.306 Z M 406.353 488.245 C 489.587 571.764 554.132 636.552 614.699 697.371 L 696.092 615.373 C 661.268 580.693 630.232 549.738 596.838 516.371 C 591.238 510.769 584.704 504.815 579.646 500.661 C 558.855 483.214 536.555 473.072 509.273 468.674 C 503.706 467.708 497.968 467.154 491.158 466.931 C 484.59 466.694 478.653 466.789 472.581 467.231 C 456.2 468.254 440.756 472.445 425.117 478.62 C 414.858 482.467 412.12 484.506 406.353 488.245 Z M 666.629 488.326 C 702.762 524.779 724.089 546.292 744.784 567.165 C 764.759 547.053 784.526 527.147 823 488.393 C 818.061 485.181 816.185 483.656 806.726 479.742 C 796.395 475.225 784.644 471.741 770.124 468.896 C 763.013 467.436 755.046 466.78 744.833 466.8 C 734.627 466.769 726.681 467.406 719.566 468.859 C 705.043 471.7 693.339 475.143 682.999 479.65 C 673.608 483.508 672.704 484.349 666.629 488.326 Z"></path> - </svg> - </svg> - </g> - </g> - <defs> - <clipPath id="SvgjsClipPath1225"> - <rect width="1000" height="1000" x="0" y="0" rx="500" ry="500"></rect> - </clipPath> - </defs> - </svg> - </g> -</svg>
docs/draft/assets/logo/frontmcp.dark.png+0 −0 removeddocs/draft/assets/logo/frontmcp.dark.svg+0 −9 removed@@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<svg xmlns="http://www.w3.org/2000/svg" viewBox="-6.245 0 1447.962 255.802"> - <path fill="#eeeeee" - d="M 26.838 130.337 C 26.315 130.004 25.797 129.669 25.287 129.33 C 25.968 128.908 26.671 128.491 27.445 128.039 L 25.263 129.314 C 21.774 127 18.602 124.553 15.962 122.148 C 13.952 120.301 11.882 118.222 9.892 116.062 L 9.712 115.871 L 9.652 115.761 L 9.592 115.677 C 9.202 115.037 8.642 113.555 8.542 112.613 L 8.522 112.457 L 8.542 112.302 C 8.692 111.364 9.312 109.905 9.732 109.286 L 9.862 109.101 L 10.042 108.92 C 41.808 77.125 68.019 50.899 88.68 30.243 C 91.462 27.462 93.68 25.277 95.249 23.777 C 96.408 22.708 98.004 20.783 100.309 19.174 C 100.462 19.084 101.144 18.604 102.497 17.64 C 106.919 14.487 110.972 12.069 114.222 10.622 C 117.498 9.196 121.396 7.854 125.564 6.726 C 134.894 4.271 145.318 3.945 154.676 5.557 C 154.781 5.576 156.382 5.938 159.382 6.617 C 161.154 7.026 162.941 7.553 164.435 8.105 C 168.249 9.534 171.287 10.836 173.257 11.874 C 177.731 14.27 181.847 16.989 185.205 19.763 C 187.471 21.651 190.005 23.994 192.648 26.638 C 210.383 44.418 236.696 70.725 271.595 105.564 C 272.188 106.155 273.288 107.269 274.882 108.891 L 275.05 109.062 L 275.12 109.16 L 275.171 109.232 C 275.64 109.923 276.273 111.486 276.401 112.489 L 276.42 112.638 L 276.4 112.786 C 276.265 113.791 275.619 115.353 275.142 116.044 L 275.02 116.211 L 274.934 116.297 L 274.85 116.381 C 273.829 117.402 272.706 118.527 271.488 119.749 C 268.152 123.073 264.116 126.342 259.692 129.306 L 257.486 128.027 C 258.272 128.483 258.974 128.896 259.667 129.323 C 259.296 129.572 258.922 129.818 258.546 130.062 C 258.373 130.173 258.236 130.26 258.127 130.329 L 259.746 129.372 C 261.105 130.213 262.445 131.12 264.351 132.53 C 266.019 133.777 267.944 135.466 269.881 137.386 C 271.717 139.207 273.383 140.845 274.744 142.157 L 274.809 142.219 L 274.852 142.272 C 275.381 142.919 276.387 144.687 276.387 146.001 C 276.387 147.309 275.232 149.248 274.722 149.883 C 248.353 176.186 222.959 201.66 198.532 226.314 C 194.857 230.023 191.306 233.535 187.925 236.806 C 186.713 237.971 185.416 239.067 184.334 239.849 C 183.226 240.625 181.649 241.993 179.776 243.187 C 177.409 244.647 174.754 246.139 171.978 247.568 C 170.754 248.194 169.497 248.77 168.46 249.185 C 167.198 249.672 165.687 250.39 163.734 250.962 C 159.122 252.199 155.8 252.987 154.022 253.27 C 151.044 253.732 148.641 254.046 146.966 254.191 C 145.15 254.348 142.749 254.47 140.449 254.271 C 139.728 254.202 137.073 253.94 132.509 253.488 C 130.811 253.31 128.894 252.95 127.158 252.483 C 123.368 251.439 120.121 250.442 117.603 249.551 C 115.525 248.801 112.921 247.597 110.126 246.089 C 107.165 244.477 103.789 242.234 100.314 239.566 C 99.461 238.905 98.056 237.661 96.275 235.985 C 94.967 234.753 93.727 233.55 92.608 232.43 C 72.997 212.758 45.502 185.203 10.112 149.755 L 9.942 149.579 L 9.872 149.475 L 9.822 149.407 C 9.322 148.687 8.682 147.061 8.562 146.022 L 8.542 145.87 L 8.562 145.717 C 8.732 144.688 9.442 143.097 9.962 142.401 L 10.102 142.229 L 10.192 142.143 L 10.282 142.06 C 13.382 139.22 16.472 135.736 20.242 132.813 C 22.211 131.324 23.671 130.331 25.233 129.363 L 27.419 130.699 C 27.367 130.668 27.209 130.569 26.838 130.337 Z M 247.465 111.738 C 231.532 95.792 209.354 73.62 175.692 39.99 C 175.423 39.719 174.978 39.294 174.318 38.68 C 173.04 37.487 171.809 36.238 170.689 35.41 C 167.594 33.039 164.422 31.182 160.726 29.586 C 158.726 28.705 156.19 27.89 154.145 27.32 C 153.038 27.004 152.189 26.805 151.354 26.681 C 148.504 26.223 145.705 25.975 142.671 25.906 C 139.422 25.81 136.214 26.161 132.499 27.013 C 128.823 27.828 125.938 28.715 123.452 29.796 C 121.45 30.637 119.925 31.483 118.131 32.67 C 125.039 39.629 148.982 63.631 189.963 104.681 C 196.924 111.8 204.696 115.945 214.471 117.741 C 220.849 119.037 227.65 118.885 234.107 117.153 C 237.511 116.276 240.188 115.406 242.444 114.437 C 244.824 113.445 245.744 112.868 247.465 111.738 Z M 166.853 111.729 L 102.14 47.015 C 98.615 50.544 94.365 54.793 76.937 72.206 C 96.784 92.12 107.35 102.721 108.778 104.153 C 111.716 107.118 114.364 109.314 117.11 111.05 C 126.442 117.185 136.045 119.474 147.367 118.249 C 151.058 117.929 154.277 117.006 158.091 115.82 C 162.444 114.524 164.213 113.474 166.853 111.729 Z M 61.817 118.536 C 63.504 118.536 65.821 118.322 68.128 118.086 C 68.88 118.013 69.463 117.924 70.114 117.781 C 73.057 117.152 75.75 116.408 78.458 115.475 C 82.393 114.178 83.838 113.269 86.16 111.739 C 79.399 104.976 72.767 98.341 63.106 88.674 C 61.645 87.209 62.121 87.686 61.817 87.38 C 61.514 87.685 61.999 87.2 60.53 88.672 C 50.871 98.337 44.927 104.284 37.474 111.739 C 39.795 113.269 41.224 114.172 45.157 115.468 C 47.865 116.401 50.576 117.152 53.518 117.781 C 54.168 117.923 54.743 118.011 55.498 118.084 C 57.802 118.321 60.131 118.536 61.817 118.536 Z M 37.552 146.965 C 54.934 164.347 71.502 180.873 89.747 199.215 C 99.535 209.057 106.259 215.765 109.942 219.362 C 118.276 227.7 128.848 232.469 140.709 232.573 C 143.98 232.658 146.615 232.536 148.957 232.183 C 152.747 231.65 156.624 230.59 161.055 228.878 C 164.084 227.774 165.059 227.062 166.798 225.916 C 149.416 208.531 132.847 192.008 114.603 173.663 C 104.815 163.823 98.09 157.115 94.404 153.516 C 86.074 145.181 75.502 140.411 63.641 140.307 C 60.369 140.222 57.735 140.344 55.392 140.698 C 51.602 141.231 47.726 142.29 43.295 144.003 C 40.265 145.106 39.292 145.818 37.552 146.965 Z M 118.238 146.946 C 144.038 172.834 164.045 192.916 182.819 211.768 L 208.048 186.351 C 197.254 175.602 187.633 166.007 177.282 155.664 C 175.547 153.927 173.521 152.082 171.953 150.794 C 165.509 145.386 158.597 142.243 150.14 140.879 C 148.415 140.58 146.636 140.408 144.525 140.339 C 142.489 140.266 140.649 140.295 138.767 140.432 C 133.689 140.749 128.902 142.048 124.055 143.962 C 120.875 145.155 120.026 145.787 118.238 146.946 Z M 198.915 146.971 C 210.115 158.27 216.726 164.938 223.141 171.408 C 229.332 165.174 235.459 159.004 247.385 146.992 C 245.854 145.996 245.273 145.523 242.341 144.31 C 239.138 142.91 235.496 141.83 230.995 140.948 C 228.791 140.496 226.322 140.292 223.156 140.299 C 219.992 140.289 217.529 140.486 215.324 140.937 C 210.822 141.817 207.195 142.885 203.989 144.282 C 201.079 145.477 200.798 145.738 198.915 146.971 Z" - style="stroke-width: 1;"/> - <path fill="#ffffff" - d="M 480.634 62.477 C 486.175 62.477 490.793 57.859 490.793 52.317 C 490.793 46.775 486.175 42.157 480.634 42.157 L 396.817 42.157 C 390.352 42.157 385.503 47.237 385.503 53.472 L 385.503 207.482 C 385.503 213.716 390.583 218.796 396.817 218.796 C 403.051 218.796 408.131 213.716 408.131 207.482 L 408.131 137.288 L 474.168 137.288 C 479.71 137.288 484.328 132.67 484.328 127.129 C 484.328 121.587 479.941 117.431 474.168 117.431 L 408.131 117.431 L 408.131 62.477 L 480.634 62.477 Z M 512.258 209.329 C 512.258 215.332 516.876 219.95 522.88 219.95 C 528.883 219.95 533.732 215.332 533.732 209.329 L 533.732 147.217 C 534.425 125.974 550.357 109.811 567.213 109.811 C 573.447 109.811 576.449 105.424 576.449 99.882 C 576.449 94.572 573.678 89.723 565.365 89.723 C 553.82 89.723 542.045 98.959 533.732 111.889 L 533.732 100.575 C 533.732 94.572 528.883 89.723 522.88 89.723 C 516.876 89.723 512.258 94.572 512.258 100.575 L 512.258 209.329 Z M 587.996 162.456 C 587.996 199.862 612.471 219.95 642.95 219.95 C 673.429 219.95 698.135 199.862 698.135 162.456 L 698.135 147.217 C 698.135 109.811 673.429 89.723 642.95 89.723 C 612.471 89.723 587.996 109.811 587.996 147.217 L 587.996 162.456 Z M 609.47 146.062 C 609.47 122.511 623.093 108.657 642.95 108.657 C 662.807 108.657 676.661 122.511 676.661 146.062 L 676.661 163.611 C 676.661 187.163 662.807 201.017 642.95 201.017 C 623.093 201.017 609.47 187.163 609.47 163.611 L 609.47 146.062 Z M 812.199 209.329 C 812.199 215.332 816.817 219.95 822.821 219.95 C 828.824 219.95 833.673 215.332 833.673 209.329 L 833.673 141.214 C 833.673 108.888 819.588 89.723 788.417 89.723 C 771.099 89.723 758.169 96.881 749.625 108.426 L 749.625 100.575 C 749.625 94.572 744.776 89.723 738.773 89.723 C 732.77 89.723 728.152 94.572 728.152 100.575 L 728.152 209.329 C 728.152 215.332 732.77 219.95 738.773 219.95 C 744.776 219.95 749.625 215.332 749.625 209.329 L 749.625 144.215 C 749.625 122.049 765.788 109.811 783.106 109.811 C 798.114 109.811 812.199 119.278 812.199 142.83 L 812.199 209.329 Z M 862.761 92.955 C 857.451 92.955 853.294 97.343 853.294 102.653 C 853.294 107.964 857.451 112.12 862.761 112.12 L 874.999 112.12 L 874.999 176.079 C 874.999 207.482 889.084 219.95 917.485 219.95 C 919.332 219.95 921.41 219.95 923.257 219.72 C 928.337 219.258 932.262 215.332 932.262 210.253 C 932.262 204.942 927.875 200.555 922.564 200.555 C 921.641 200.555 918.177 200.786 917.254 200.786 C 902.014 200.786 896.473 193.166 896.473 174.694 L 896.473 112.12 L 920.486 112.12 C 925.797 112.12 929.953 107.964 929.953 102.653 C 929.953 97.343 925.797 92.955 920.486 92.955 L 896.473 92.955 L 896.473 65.709 C 896.473 59.706 891.624 54.857 885.62 54.857 C 879.617 54.857 874.999 59.706 874.999 65.709 L 874.999 92.955 L 862.761 92.955 Z M 969.896 38.925 C 963.662 38.925 958.582 44.005 958.582 50.239 L 958.582 207.482 C 958.582 213.716 962.738 218.796 968.972 218.796 C 975.207 218.796 980.056 213.716 980.056 207.482 L 980.056 86.952 L 1021.16 163.611 C 1024.39 169.845 1026.93 174.232 1034.55 174.232 C 1042.63 174.232 1045.4 169.383 1048.86 162.918 L 1090.43 85.105 L 1090.43 207.482 C 1090.43 213.716 1094.58 218.796 1100.82 218.796 C 1107.05 218.796 1111.9 213.716 1111.9 207.482 L 1111.9 50.239 C 1111.9 44.005 1107.05 38.925 1100.82 38.925 C 1094.58 38.925 1090.43 41.465 1086.96 48.161 L 1035.01 146.062 L 983.75 48.161 C 980.286 41.465 976.13 38.925 969.896 38.925 Z M 1221.34 199.631 C 1191.33 199.631 1170.31 182.314 1170.31 142.368 L 1170.31 117.893 C 1170.31 77.947 1193.63 59.244 1221.34 59.244 C 1245.59 59.244 1250.44 67.095 1256.44 67.095 C 1263.37 67.095 1267.99 62.477 1267.99 56.935 C 1267.99 52.779 1264.75 49.315 1260.13 46.775 C 1250.21 41.234 1232.89 38.925 1221.34 38.925 C 1178.63 38.925 1146.99 67.095 1146.99 119.509 L 1146.99 140.752 C 1146.99 193.166 1178.63 219.95 1221.34 219.95 C 1234.5 219.95 1253.67 217.411 1262.9 210.945 C 1266.14 208.636 1268.22 205.866 1268.22 202.402 C 1268.22 196.86 1264.29 192.704 1258.06 192.704 C 1255.98 192.704 1253.9 193.397 1251.59 194.09 C 1246.05 196.168 1238.43 199.631 1221.34 199.631 Z M 1413.22 95.034 C 1413.22 66.402 1394.98 42.157 1355.26 42.157 L 1307.24 42.157 C 1301 42.157 1295.92 47.237 1295.92 53.472 L 1295.92 207.251 C 1295.92 213.485 1301 218.565 1307.24 218.565 C 1313.47 218.565 1318.55 213.485 1318.55 207.251 L 1318.55 151.142 L 1356.19 151.142 C 1394.52 151.142 1413.22 127.36 1413.22 95.034 Z M 1352.49 62.477 C 1377.66 62.477 1389.9 73.56 1389.9 97.112 C 1389.9 114.198 1382.74 131.285 1355.03 131.285 L 1318.55 131.285 L 1318.55 62.477 L 1352.49 62.477 Z" - style="text-wrap-mode: nowrap;"/> -</svg> \ No newline at end of file
docs/draft/assets/logo/frontmcp.light.png+0 −0 removeddocs/draft/assets/logo/frontmcp.light.svg+0 −9 removed@@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<svg xmlns="http://www.w3.org/2000/svg" viewBox="-6.245 0 1447.962 255.802"> - <path fill="#0e0e0e" - d="M 26.838 130.337 C 26.315 130.004 25.797 129.669 25.287 129.33 C 25.968 128.908 26.671 128.491 27.445 128.039 L 25.263 129.314 C 21.774 127 18.602 124.553 15.962 122.148 C 13.952 120.301 11.882 118.222 9.892 116.062 L 9.712 115.871 L 9.652 115.761 L 9.592 115.677 C 9.202 115.037 8.642 113.555 8.542 112.613 L 8.522 112.457 L 8.542 112.302 C 8.692 111.364 9.312 109.905 9.732 109.286 L 9.862 109.101 L 10.042 108.92 C 41.808 77.125 68.019 50.899 88.68 30.243 C 91.462 27.462 93.68 25.277 95.249 23.777 C 96.408 22.708 98.004 20.783 100.309 19.174 C 100.462 19.084 101.144 18.604 102.497 17.64 C 106.919 14.487 110.972 12.069 114.222 10.622 C 117.498 9.196 121.396 7.854 125.564 6.726 C 134.894 4.271 145.318 3.945 154.676 5.557 C 154.781 5.576 156.382 5.938 159.382 6.617 C 161.154 7.026 162.941 7.553 164.435 8.105 C 168.249 9.534 171.287 10.836 173.257 11.874 C 177.731 14.27 181.847 16.989 185.205 19.763 C 187.471 21.651 190.005 23.994 192.648 26.638 C 210.383 44.418 236.696 70.725 271.595 105.564 C 272.188 106.155 273.288 107.269 274.882 108.891 L 275.05 109.062 L 275.12 109.16 L 275.171 109.232 C 275.64 109.923 276.273 111.486 276.401 112.489 L 276.42 112.638 L 276.4 112.786 C 276.265 113.791 275.619 115.353 275.142 116.044 L 275.02 116.211 L 274.934 116.297 L 274.85 116.381 C 273.829 117.402 272.706 118.527 271.488 119.749 C 268.152 123.073 264.116 126.342 259.692 129.306 L 257.486 128.027 C 258.272 128.483 258.974 128.896 259.667 129.323 C 259.296 129.572 258.922 129.818 258.546 130.062 C 258.373 130.173 258.236 130.26 258.127 130.329 L 259.746 129.372 C 261.105 130.213 262.445 131.12 264.351 132.53 C 266.019 133.777 267.944 135.466 269.881 137.386 C 271.717 139.207 273.383 140.845 274.744 142.157 L 274.809 142.219 L 274.852 142.272 C 275.381 142.919 276.387 144.687 276.387 146.001 C 276.387 147.309 275.232 149.248 274.722 149.883 C 248.353 176.186 222.959 201.66 198.532 226.314 C 194.857 230.023 191.306 233.535 187.925 236.806 C 186.713 237.971 185.416 239.067 184.334 239.849 C 183.226 240.625 181.649 241.993 179.776 243.187 C 177.409 244.647 174.754 246.139 171.978 247.568 C 170.754 248.194 169.497 248.77 168.46 249.185 C 167.198 249.672 165.687 250.39 163.734 250.962 C 159.122 252.199 155.8 252.987 154.022 253.27 C 151.044 253.732 148.641 254.046 146.966 254.191 C 145.15 254.348 142.749 254.47 140.449 254.271 C 139.728 254.202 137.073 253.94 132.509 253.488 C 130.811 253.31 128.894 252.95 127.158 252.483 C 123.368 251.439 120.121 250.442 117.603 249.551 C 115.525 248.801 112.921 247.597 110.126 246.089 C 107.165 244.477 103.789 242.234 100.314 239.566 C 99.461 238.905 98.056 237.661 96.275 235.985 C 94.967 234.753 93.727 233.55 92.608 232.43 C 72.997 212.758 45.502 185.203 10.112 149.755 L 9.942 149.579 L 9.872 149.475 L 9.822 149.407 C 9.322 148.687 8.682 147.061 8.562 146.022 L 8.542 145.87 L 8.562 145.717 C 8.732 144.688 9.442 143.097 9.962 142.401 L 10.102 142.229 L 10.192 142.143 L 10.282 142.06 C 13.382 139.22 16.472 135.736 20.242 132.813 C 22.211 131.324 23.671 130.331 25.233 129.363 L 27.419 130.699 C 27.367 130.668 27.209 130.569 26.838 130.337 Z M 247.465 111.738 C 231.532 95.792 209.354 73.62 175.692 39.99 C 175.423 39.719 174.978 39.294 174.318 38.68 C 173.04 37.487 171.809 36.238 170.689 35.41 C 167.594 33.039 164.422 31.182 160.726 29.586 C 158.726 28.705 156.19 27.89 154.145 27.32 C 153.038 27.004 152.189 26.805 151.354 26.681 C 148.504 26.223 145.705 25.975 142.671 25.906 C 139.422 25.81 136.214 26.161 132.499 27.013 C 128.823 27.828 125.938 28.715 123.452 29.796 C 121.45 30.637 119.925 31.483 118.131 32.67 C 125.039 39.629 148.982 63.631 189.963 104.681 C 196.924 111.8 204.696 115.945 214.471 117.741 C 220.849 119.037 227.65 118.885 234.107 117.153 C 237.511 116.276 240.188 115.406 242.444 114.437 C 244.824 113.445 245.744 112.868 247.465 111.738 Z M 166.853 111.729 L 102.14 47.015 C 98.615 50.544 94.365 54.793 76.937 72.206 C 96.784 92.12 107.35 102.721 108.778 104.153 C 111.716 107.118 114.364 109.314 117.11 111.05 C 126.442 117.185 136.045 119.474 147.367 118.249 C 151.058 117.929 154.277 117.006 158.091 115.82 C 162.444 114.524 164.213 113.474 166.853 111.729 Z M 61.817 118.536 C 63.504 118.536 65.821 118.322 68.128 118.086 C 68.88 118.013 69.463 117.924 70.114 117.781 C 73.057 117.152 75.75 116.408 78.458 115.475 C 82.393 114.178 83.838 113.269 86.16 111.739 C 79.399 104.976 72.767 98.341 63.106 88.674 C 61.645 87.209 62.121 87.686 61.817 87.38 C 61.514 87.685 61.999 87.2 60.53 88.672 C 50.871 98.337 44.927 104.284 37.474 111.739 C 39.795 113.269 41.224 114.172 45.157 115.468 C 47.865 116.401 50.576 117.152 53.518 117.781 C 54.168 117.923 54.743 118.011 55.498 118.084 C 57.802 118.321 60.131 118.536 61.817 118.536 Z M 37.552 146.965 C 54.934 164.347 71.502 180.873 89.747 199.215 C 99.535 209.057 106.259 215.765 109.942 219.362 C 118.276 227.7 128.848 232.469 140.709 232.573 C 143.98 232.658 146.615 232.536 148.957 232.183 C 152.747 231.65 156.624 230.59 161.055 228.878 C 164.084 227.774 165.059 227.062 166.798 225.916 C 149.416 208.531 132.847 192.008 114.603 173.663 C 104.815 163.823 98.09 157.115 94.404 153.516 C 86.074 145.181 75.502 140.411 63.641 140.307 C 60.369 140.222 57.735 140.344 55.392 140.698 C 51.602 141.231 47.726 142.29 43.295 144.003 C 40.265 145.106 39.292 145.818 37.552 146.965 Z M 118.238 146.946 C 144.038 172.834 164.045 192.916 182.819 211.768 L 208.048 186.351 C 197.254 175.602 187.633 166.007 177.282 155.664 C 175.547 153.927 173.521 152.082 171.953 150.794 C 165.509 145.386 158.597 142.243 150.14 140.879 C 148.415 140.58 146.636 140.408 144.525 140.339 C 142.489 140.266 140.649 140.295 138.767 140.432 C 133.689 140.749 128.902 142.048 124.055 143.962 C 120.875 145.155 120.026 145.787 118.238 146.946 Z M 198.915 146.971 C 210.115 158.27 216.726 164.938 223.141 171.408 C 229.332 165.174 235.459 159.004 247.385 146.992 C 245.854 145.996 245.273 145.523 242.341 144.31 C 239.138 142.91 235.496 141.83 230.995 140.948 C 228.791 140.496 226.322 140.292 223.156 140.299 C 219.992 140.289 217.529 140.486 215.324 140.937 C 210.822 141.817 207.195 142.885 203.989 144.282 C 201.079 145.477 200.798 145.738 198.915 146.971 Z" - style="stroke-width: 1;"/> - <path fill="#000000" - d="M 480.634 62.477 C 486.175 62.477 490.793 57.859 490.793 52.317 C 490.793 46.775 486.175 42.157 480.634 42.157 L 396.817 42.157 C 390.352 42.157 385.503 47.237 385.503 53.472 L 385.503 207.482 C 385.503 213.716 390.583 218.796 396.817 218.796 C 403.051 218.796 408.131 213.716 408.131 207.482 L 408.131 137.288 L 474.168 137.288 C 479.71 137.288 484.328 132.67 484.328 127.129 C 484.328 121.587 479.941 117.431 474.168 117.431 L 408.131 117.431 L 408.131 62.477 L 480.634 62.477 Z M 512.258 209.329 C 512.258 215.332 516.876 219.95 522.88 219.95 C 528.883 219.95 533.732 215.332 533.732 209.329 L 533.732 147.217 C 534.425 125.974 550.357 109.811 567.213 109.811 C 573.447 109.811 576.449 105.424 576.449 99.882 C 576.449 94.572 573.678 89.723 565.365 89.723 C 553.82 89.723 542.045 98.959 533.732 111.889 L 533.732 100.575 C 533.732 94.572 528.883 89.723 522.88 89.723 C 516.876 89.723 512.258 94.572 512.258 100.575 L 512.258 209.329 Z M 587.996 162.456 C 587.996 199.862 612.471 219.95 642.95 219.95 C 673.429 219.95 698.135 199.862 698.135 162.456 L 698.135 147.217 C 698.135 109.811 673.429 89.723 642.95 89.723 C 612.471 89.723 587.996 109.811 587.996 147.217 L 587.996 162.456 Z M 609.47 146.062 C 609.47 122.511 623.093 108.657 642.95 108.657 C 662.807 108.657 676.661 122.511 676.661 146.062 L 676.661 163.611 C 676.661 187.163 662.807 201.017 642.95 201.017 C 623.093 201.017 609.47 187.163 609.47 163.611 L 609.47 146.062 Z M 812.199 209.329 C 812.199 215.332 816.817 219.95 822.821 219.95 C 828.824 219.95 833.673 215.332 833.673 209.329 L 833.673 141.214 C 833.673 108.888 819.588 89.723 788.417 89.723 C 771.099 89.723 758.169 96.881 749.625 108.426 L 749.625 100.575 C 749.625 94.572 744.776 89.723 738.773 89.723 C 732.77 89.723 728.152 94.572 728.152 100.575 L 728.152 209.329 C 728.152 215.332 732.77 219.95 738.773 219.95 C 744.776 219.95 749.625 215.332 749.625 209.329 L 749.625 144.215 C 749.625 122.049 765.788 109.811 783.106 109.811 C 798.114 109.811 812.199 119.278 812.199 142.83 L 812.199 209.329 Z M 862.761 92.955 C 857.451 92.955 853.294 97.343 853.294 102.653 C 853.294 107.964 857.451 112.12 862.761 112.12 L 874.999 112.12 L 874.999 176.079 C 874.999 207.482 889.084 219.95 917.485 219.95 C 919.332 219.95 921.41 219.95 923.257 219.72 C 928.337 219.258 932.262 215.332 932.262 210.253 C 932.262 204.942 927.875 200.555 922.564 200.555 C 921.641 200.555 918.177 200.786 917.254 200.786 C 902.014 200.786 896.473 193.166 896.473 174.694 L 896.473 112.12 L 920.486 112.12 C 925.797 112.12 929.953 107.964 929.953 102.653 C 929.953 97.343 925.797 92.955 920.486 92.955 L 896.473 92.955 L 896.473 65.709 C 896.473 59.706 891.624 54.857 885.62 54.857 C 879.617 54.857 874.999 59.706 874.999 65.709 L 874.999 92.955 L 862.761 92.955 Z M 969.896 38.925 C 963.662 38.925 958.582 44.005 958.582 50.239 L 958.582 207.482 C 958.582 213.716 962.738 218.796 968.972 218.796 C 975.207 218.796 980.056 213.716 980.056 207.482 L 980.056 86.952 L 1021.16 163.611 C 1024.39 169.845 1026.93 174.232 1034.55 174.232 C 1042.63 174.232 1045.4 169.383 1048.86 162.918 L 1090.43 85.105 L 1090.43 207.482 C 1090.43 213.716 1094.58 218.796 1100.82 218.796 C 1107.05 218.796 1111.9 213.716 1111.9 207.482 L 1111.9 50.239 C 1111.9 44.005 1107.05 38.925 1100.82 38.925 C 1094.58 38.925 1090.43 41.465 1086.96 48.161 L 1035.01 146.062 L 983.75 48.161 C 980.286 41.465 976.13 38.925 969.896 38.925 Z M 1221.34 199.631 C 1191.33 199.631 1170.31 182.314 1170.31 142.368 L 1170.31 117.893 C 1170.31 77.947 1193.63 59.244 1221.34 59.244 C 1245.59 59.244 1250.44 67.095 1256.44 67.095 C 1263.37 67.095 1267.99 62.477 1267.99 56.935 C 1267.99 52.779 1264.75 49.315 1260.13 46.775 C 1250.21 41.234 1232.89 38.925 1221.34 38.925 C 1178.63 38.925 1146.99 67.095 1146.99 119.509 L 1146.99 140.752 C 1146.99 193.166 1178.63 219.95 1221.34 219.95 C 1234.5 219.95 1253.67 217.411 1262.9 210.945 C 1266.14 208.636 1268.22 205.866 1268.22 202.402 C 1268.22 196.86 1264.29 192.704 1258.06 192.704 C 1255.98 192.704 1253.9 193.397 1251.59 194.09 C 1246.05 196.168 1238.43 199.631 1221.34 199.631 Z M 1413.22 95.034 C 1413.22 66.402 1394.98 42.157 1355.26 42.157 L 1307.24 42.157 C 1301 42.157 1295.92 47.237 1295.92 53.472 L 1295.92 207.251 C 1295.92 213.485 1301 218.565 1307.24 218.565 C 1313.47 218.565 1318.55 213.485 1318.55 207.251 L 1318.55 151.142 L 1356.19 151.142 C 1394.52 151.142 1413.22 127.36 1413.22 95.034 Z M 1352.49 62.477 C 1377.66 62.477 1389.9 73.56 1389.9 97.112 C 1389.9 114.198 1382.74 131.285 1355.03 131.285 L 1318.55 131.285 L 1318.55 62.477 L 1352.49 62.477 Z" - style="text-wrap-mode: nowrap;"/> -</svg> \ No newline at end of file
docs/draft/assets/logo/logo.dark.png+0 −0 removeddocs/draft/assets/logo/logo.dark.svg+0 −4 removed@@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0.00 0.00 970.00 865.00"> - <path fill="#e8e8e8" d="M 111.483 434.661 C 109.794 433.589 108.125 432.506 106.477 431.414 C 108.677 430.054 110.943 428.707 113.441 427.248 L 106.4 431.363 C 95.145 423.897 84.903 416.004 76.398 408.243 C 69.923 402.284 63.235 395.578 56.815 388.608 L 56.247 387.992 L 56.017 387.638 L 55.841 387.367 C 54.57 385.304 52.793 380.521 52.453 377.481 L 52.397 376.979 L 52.476 376.48 C 52.952 373.454 54.945 368.745 56.301 366.748 L 56.726 366.151 L 57.31 365.567 C 159.777 262.994 244.339 178.385 310.994 111.742 C 319.968 102.77 327.124 95.727 332.186 90.881 C 335.924 87.436 341.074 81.219 348.509 76.039 C 349.004 75.744 351.204 74.194 355.568 71.08 C 369.834 60.919 382.91 53.113 393.395 48.449 C 403.965 43.84 416.54 39.517 429.986 35.87 C 460.088 27.958 493.715 26.909 523.905 32.1 C 524.246 32.167 529.411 33.332 539.089 35.526 C 544.807 36.842 550.571 38.54 555.389 40.322 C 567.695 44.934 577.495 49.131 583.85 52.485 C 598.284 60.209 611.565 68.984 622.398 77.932 C 629.709 84.028 637.884 91.58 646.41 100.117 C 703.624 157.476 788.516 242.346 901.103 354.74 C 903.018 356.649 906.567 360.241 911.709 365.476 L 912.25 366.027 L 912.476 366.344 L 912.642 366.576 C 914.154 368.804 916.196 373.846 916.61 377.083 L 916.672 377.563 L 916.607 378.042 C 916.172 381.282 914.086 386.323 912.547 388.551 L 912.153 389.091 L 911.878 389.367 L 911.606 389.638 C 908.311 392.932 904.689 396.561 900.759 400.504 C 889.997 411.229 876.976 421.775 862.705 431.337 L 855.586 427.209 C 858.124 428.681 860.386 430.013 862.622 431.392 C 861.426 432.193 860.22 432.988 859.006 433.774 C 858.449 434.132 858.008 434.413 857.655 434.637 L 862.877 431.549 C 867.262 434.263 871.584 437.188 877.734 441.736 C 883.115 445.759 889.327 451.209 895.576 457.403 C 901.498 463.279 906.873 468.562 911.265 472.796 L 911.473 472.997 L 911.613 473.168 C 913.32 475.253 916.565 480.957 916.565 485.198 C 916.565 489.417 912.837 495.672 911.193 497.72 C 826.121 582.578 744.198 664.762 665.393 744.3 C 653.535 756.265 642.079 767.596 631.173 778.147 C 627.261 781.906 623.077 785.441 619.588 787.965 C 616.013 790.468 610.926 794.88 604.884 798.732 C 597.248 803.443 588.682 808.256 579.726 812.867 C 575.778 814.887 571.721 816.746 568.377 818.083 C 564.303 819.654 559.431 821.97 553.129 823.817 C 538.25 827.809 527.534 830.349 521.795 831.263 C 512.188 832.754 504.438 833.765 499.034 834.233 C 493.175 834.739 485.428 835.133 478.007 834.492 C 475.681 834.271 467.117 833.425 452.394 831.965 C 446.915 831.393 440.729 830.231 435.13 828.723 C 422.901 825.355 412.428 822.139 404.302 819.265 C 397.598 816.846 389.198 812.962 380.181 808.096 C 370.628 802.894 359.738 795.66 348.526 787.053 C 345.776 784.919 341.242 780.906 335.496 775.498 C 331.278 771.525 327.275 767.644 323.667 764.03 C 260.397 700.566 171.694 611.667 57.536 497.309 L 56.968 496.741 L 56.729 496.406 L 56.57 496.184 C 54.985 493.862 52.894 488.616 52.515 485.266 L 52.459 484.773 L 52.536 484.281 C 53.058 480.96 55.359 475.829 57.033 473.583 L 57.467 473.029 L 57.77 472.752 L 58.062 472.483 C 68.078 463.32 78.045 452.08 90.204 442.65 C 96.555 437.848 101.265 434.642 106.305 431.52 L 113.357 435.832 C 113.189 435.73 112.679 435.412 111.483 434.661 Z M 823.258 374.66 C 771.856 323.214 700.307 251.685 591.708 143.191 C 590.839 142.314 589.403 140.944 587.273 138.963 C 583.151 135.118 579.179 131.08 575.568 128.416 C 565.583 120.76 555.349 114.772 543.424 109.628 C 536.971 106.783 528.791 104.158 522.194 102.314 C 518.621 101.292 515.882 100.659 513.188 100.251 C 503.996 98.774 494.964 97.97 485.178 97.757 C 474.695 97.448 464.346 98.575 452.36 101.32 C 440.502 103.955 431.194 106.809 423.174 110.302 C 416.716 113.015 411.795 115.742 406.008 119.57 C 428.294 142.021 505.536 219.458 637.747 351.891 C 660.206 374.86 685.279 388.233 716.813 394.027 C 737.392 398.207 759.333 397.716 780.163 392.129 C 791.145 389.3 799.782 386.493 807.06 383.366 C 814.736 380.168 817.704 378.305 823.258 374.66 Z M 563.19 374.632 L 354.419 165.852 C 343.045 177.237 329.334 190.945 273.108 247.123 C 337.139 311.37 371.226 345.57 375.832 350.188 C 385.312 359.756 393.854 366.839 402.713 372.441 C 432.818 392.232 463.801 399.617 500.327 395.665 C 512.234 394.631 522.62 391.656 534.925 387.828 C 548.966 383.646 554.674 380.26 563.19 374.632 Z M 224.33 396.59 C 229.772 396.59 237.248 395.902 244.691 395.138 C 247.117 394.902 248.998 394.615 251.098 394.155 C 260.593 392.126 269.281 389.724 278.017 386.714 C 290.71 382.53 295.373 379.6 302.864 374.664 C 281.052 352.845 259.655 331.439 228.489 300.252 C 223.774 295.526 225.31 297.063 224.33 296.076 C 223.353 297.061 224.916 295.497 220.177 300.246 C 189.015 331.427 169.839 350.613 145.796 374.664 C 153.282 379.598 157.893 382.511 170.581 386.692 C 179.317 389.704 188.063 392.125 197.557 394.154 C 199.652 394.614 201.509 394.897 203.944 395.134 C 211.378 395.897 218.89 396.59 224.33 396.59 Z M 146.047 488.306 C 202.125 544.384 255.576 597.698 314.435 656.872 C 346.014 688.624 367.706 710.265 379.589 721.869 C 406.474 748.769 440.58 764.157 478.847 764.492 C 489.401 764.765 497.9 764.373 505.457 763.232 C 517.684 761.512 530.191 758.095 544.485 752.57 C 554.259 749.01 557.402 746.712 563.013 743.014 C 506.936 686.929 453.484 633.622 394.625 574.438 C 363.046 542.692 341.351 521.053 329.461 509.441 C 302.586 482.551 268.48 467.163 230.213 466.827 C 219.659 466.554 211.16 466.947 203.603 468.088 C 191.376 469.808 178.869 473.225 164.575 478.75 C 154.801 482.31 151.659 484.608 146.047 488.306 Z M 406.353 488.245 C 489.587 571.764 554.132 636.552 614.699 697.371 L 696.092 615.373 C 661.268 580.693 630.232 549.738 596.838 516.371 C 591.238 510.769 584.704 504.815 579.646 500.661 C 558.855 483.214 536.555 473.072 509.273 468.674 C 503.706 467.708 497.968 467.154 491.158 466.931 C 484.59 466.694 478.653 466.789 472.581 467.231 C 456.2 468.254 440.756 472.445 425.117 478.62 C 414.858 482.467 412.12 484.506 406.353 488.245 Z M 666.629 488.326 C 702.762 524.779 724.089 546.292 744.784 567.165 C 764.759 547.053 784.526 527.147 823 488.393 C 818.061 485.181 816.185 483.656 806.726 479.742 C 796.395 475.225 784.644 471.741 770.124 468.896 C 763.013 467.436 755.046 466.78 744.833 466.8 C 734.627 466.769 726.681 467.406 719.566 468.859 C 705.043 471.7 693.339 475.143 682.999 479.65 C 673.608 483.508 672.704 484.349 666.629 488.326 Z"/> -</svg> \ No newline at end of file
docs/draft/assets/logo/logo.light.png+0 −0 removeddocs/draft/assets/logo/logo.light.svg+0 −5 removed@@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0.00 0.00 970.00 865.00"> - <path fill="#000000" - d="M 111.483 434.661 C 109.794 433.589 108.125 432.506 106.477 431.414 C 108.677 430.054 110.943 428.707 113.441 427.248 L 106.4 431.363 C 95.145 423.897 84.903 416.004 76.398 408.243 C 69.923 402.284 63.235 395.578 56.815 388.608 L 56.247 387.992 L 56.017 387.638 L 55.841 387.367 C 54.57 385.304 52.793 380.521 52.453 377.481 L 52.397 376.979 L 52.476 376.48 C 52.952 373.454 54.945 368.745 56.301 366.748 L 56.726 366.151 L 57.31 365.567 C 159.777 262.994 244.339 178.385 310.994 111.742 C 319.968 102.77 327.124 95.727 332.186 90.881 C 335.924 87.436 341.074 81.219 348.509 76.039 C 349.004 75.744 351.204 74.194 355.568 71.08 C 369.834 60.919 382.91 53.113 393.395 48.449 C 403.965 43.84 416.54 39.517 429.986 35.87 C 460.088 27.958 493.715 26.909 523.905 32.1 C 524.246 32.167 529.411 33.332 539.089 35.526 C 544.807 36.842 550.571 38.54 555.389 40.322 C 567.695 44.934 577.495 49.131 583.85 52.485 C 598.284 60.209 611.565 68.984 622.398 77.932 C 629.709 84.028 637.884 91.58 646.41 100.117 C 703.624 157.476 788.516 242.346 901.103 354.74 C 903.018 356.649 906.567 360.241 911.709 365.476 L 912.25 366.027 L 912.476 366.344 L 912.642 366.576 C 914.154 368.804 916.196 373.846 916.61 377.083 L 916.672 377.563 L 916.607 378.042 C 916.172 381.282 914.086 386.323 912.547 388.551 L 912.153 389.091 L 911.878 389.367 L 911.606 389.638 C 908.311 392.932 904.689 396.561 900.759 400.504 C 889.997 411.229 876.976 421.775 862.705 431.337 L 855.586 427.209 C 858.124 428.681 860.386 430.013 862.622 431.392 C 861.426 432.193 860.22 432.988 859.006 433.774 C 858.449 434.132 858.008 434.413 857.655 434.637 L 862.877 431.549 C 867.262 434.263 871.584 437.188 877.734 441.736 C 883.115 445.759 889.327 451.209 895.576 457.403 C 901.498 463.279 906.873 468.562 911.265 472.796 L 911.473 472.997 L 911.613 473.168 C 913.32 475.253 916.565 480.957 916.565 485.198 C 916.565 489.417 912.837 495.672 911.193 497.72 C 826.121 582.578 744.198 664.762 665.393 744.3 C 653.535 756.265 642.079 767.596 631.173 778.147 C 627.261 781.906 623.077 785.441 619.588 787.965 C 616.013 790.468 610.926 794.88 604.884 798.732 C 597.248 803.443 588.682 808.256 579.726 812.867 C 575.778 814.887 571.721 816.746 568.377 818.083 C 564.303 819.654 559.431 821.97 553.129 823.817 C 538.25 827.809 527.534 830.349 521.795 831.263 C 512.188 832.754 504.438 833.765 499.034 834.233 C 493.175 834.739 485.428 835.133 478.007 834.492 C 475.681 834.271 467.117 833.425 452.394 831.965 C 446.915 831.393 440.729 830.231 435.13 828.723 C 422.901 825.355 412.428 822.139 404.302 819.265 C 397.598 816.846 389.198 812.962 380.181 808.096 C 370.628 802.894 359.738 795.66 348.526 787.053 C 345.776 784.919 341.242 780.906 335.496 775.498 C 331.278 771.525 327.275 767.644 323.667 764.03 C 260.397 700.566 171.694 611.667 57.536 497.309 L 56.968 496.741 L 56.729 496.406 L 56.57 496.184 C 54.985 493.862 52.894 488.616 52.515 485.266 L 52.459 484.773 L 52.536 484.281 C 53.058 480.96 55.359 475.829 57.033 473.583 L 57.467 473.029 L 57.77 472.752 L 58.062 472.483 C 68.078 463.32 78.045 452.08 90.204 442.65 C 96.555 437.848 101.265 434.642 106.305 431.52 L 113.357 435.832 C 113.189 435.73 112.679 435.412 111.483 434.661 Z M 823.258 374.66 C 771.856 323.214 700.307 251.685 591.708 143.191 C 590.839 142.314 589.403 140.944 587.273 138.963 C 583.151 135.118 579.179 131.08 575.568 128.416 C 565.583 120.76 555.349 114.772 543.424 109.628 C 536.971 106.783 528.791 104.158 522.194 102.314 C 518.621 101.292 515.882 100.659 513.188 100.251 C 503.996 98.774 494.964 97.97 485.178 97.757 C 474.695 97.448 464.346 98.575 452.36 101.32 C 440.502 103.955 431.194 106.809 423.174 110.302 C 416.716 113.015 411.795 115.742 406.008 119.57 C 428.294 142.021 505.536 219.458 637.747 351.891 C 660.206 374.86 685.279 388.233 716.813 394.027 C 737.392 398.207 759.333 397.716 780.163 392.129 C 791.145 389.3 799.782 386.493 807.06 383.366 C 814.736 380.168 817.704 378.305 823.258 374.66 Z M 563.19 374.632 L 354.419 165.852 C 343.045 177.237 329.334 190.945 273.108 247.123 C 337.139 311.37 371.226 345.57 375.832 350.188 C 385.312 359.756 393.854 366.839 402.713 372.441 C 432.818 392.232 463.801 399.617 500.327 395.665 C 512.234 394.631 522.62 391.656 534.925 387.828 C 548.966 383.646 554.674 380.26 563.19 374.632 Z M 224.33 396.59 C 229.772 396.59 237.248 395.902 244.691 395.138 C 247.117 394.902 248.998 394.615 251.098 394.155 C 260.593 392.126 269.281 389.724 278.017 386.714 C 290.71 382.53 295.373 379.6 302.864 374.664 C 281.052 352.845 259.655 331.439 228.489 300.252 C 223.774 295.526 225.31 297.063 224.33 296.076 C 223.353 297.061 224.916 295.497 220.177 300.246 C 189.015 331.427 169.839 350.613 145.796 374.664 C 153.282 379.598 157.893 382.511 170.581 386.692 C 179.317 389.704 188.063 392.125 197.557 394.154 C 199.652 394.614 201.509 394.897 203.944 395.134 C 211.378 395.897 218.89 396.59 224.33 396.59 Z M 146.047 488.306 C 202.125 544.384 255.576 597.698 314.435 656.872 C 346.014 688.624 367.706 710.265 379.589 721.869 C 406.474 748.769 440.58 764.157 478.847 764.492 C 489.401 764.765 497.9 764.373 505.457 763.232 C 517.684 761.512 530.191 758.095 544.485 752.57 C 554.259 749.01 557.402 746.712 563.013 743.014 C 506.936 686.929 453.484 633.622 394.625 574.438 C 363.046 542.692 341.351 521.053 329.461 509.441 C 302.586 482.551 268.48 467.163 230.213 466.827 C 219.659 466.554 211.16 466.947 203.603 468.088 C 191.376 469.808 178.869 473.225 164.575 478.75 C 154.801 482.31 151.659 484.608 146.047 488.306 Z M 406.353 488.245 C 489.587 571.764 554.132 636.552 614.699 697.371 L 696.092 615.373 C 661.268 580.693 630.232 549.738 596.838 516.371 C 591.238 510.769 584.704 504.815 579.646 500.661 C 558.855 483.214 536.555 473.072 509.273 468.674 C 503.706 467.708 497.968 467.154 491.158 466.931 C 484.59 466.694 478.653 466.789 472.581 467.231 C 456.2 468.254 440.756 472.445 425.117 478.62 C 414.858 482.467 412.12 484.506 406.353 488.245 Z M 666.629 488.326 C 702.762 524.779 724.089 546.292 744.784 567.165 C 764.759 547.053 784.526 527.147 823 488.393 C 818.061 485.181 816.185 483.656 806.726 479.742 C 796.395 475.225 784.644 471.741 770.124 468.896 C 763.013 467.436 755.046 466.78 744.833 466.8 C 734.627 466.769 726.681 467.406 719.566 468.859 C 705.043 471.7 693.339 475.143 682.999 479.65 C 673.608 483.508 672.704 484.349 666.629 488.326 Z"/> -</svg> \ No newline at end of file
docs/draft/docs/getting-started/installation.mdx+0 −52 removed@@ -1,52 +0,0 @@ ---- -title: 'Installation' -description: 'How to install Enclave libraries' ---- - -# Installation - -Each library in the Enclave monorepo can be installed independently. - -## Prerequisites - -- **Node.js**: >= 22.0.0 -- **npm**: >= 10 (or yarn/pnpm equivalent) - -## Installing Libraries - -### ast-guard - -```bash -npm install ast-guard -``` - -### vectoriadb - -```bash -npm install vectoriadb -``` - -### enclave - -```bash -npm install enclave-vm -``` - -## Development Setup - -To work on the Enclave monorepo itself: - -```bash -# Clone the repository -git clone https://github.com/agentfront/enclave.git -cd enclave - -# Install dependencies -yarn install - -# Build all libraries -yarn build - -# Run tests -yarn test -```
docs/draft/docs/getting-started/welcome.mdx+0 −43 removed@@ -1,43 +0,0 @@ ---- -title: 'Welcome to Enclave' -description: 'Secure JavaScript execution and vector search libraries for AI agents' ---- - -# Welcome to Enclave - -Enclave is a monorepo containing security-focused libraries for building safe AI agent systems. - -## Libraries - -<CardGroup cols={3}> - <Card title="ast-guard" icon="shield-check" href="/docs/libraries/ast-guard"> - AST-based JavaScript validator with 100% CVE protection - </Card> - <Card title="vectoriadb" icon="database" href="/docs/libraries/vectoriadb"> - Lightweight in-memory vector database for semantic search - </Card> - <Card title="enclave-vm" icon="lock" href="/docs/libraries/enclave-vm"> - Secure AgentScript execution environment - </Card> -</CardGroup> - -## Key Features - -- **Bank-grade Security**: 500+ security tests across all libraries -- **100% CVE Coverage**: Blocks all known vm2/isolated-vm/node-vm exploits -- **TypeScript-first**: Full type safety and excellent DX -- **Production-ready**: Used in production AI agent systems - -## Quick Start - -```bash -# Install any library -npm install ast-guard -npm install vectoriadb -npm install enclave-vm -``` - -## Links - -- [GitHub Repository](https://github.com/agentfront/enclave) -- [Installation Guide](/docs/getting-started/installation)
docs/draft/docs.json+0 −60 removed@@ -1,60 +0,0 @@ -{ - "$schema": "https://mintlify.com/docs.json", - "name": "Enclave", - "description": "Secure JavaScript execution and vector search libraries for AI agents", - "appearance": { - "default": "system", - "strict": false - }, - "theme": "almond", - "favicon": { - "light": "/favicon.ico", - "dark": "/favicon.ico" - }, - "colors": { - "primary": "#16A34A", - "light": "#07C983", - "dark": "#15803D" - }, - "navbar": { - "links": [ - { - "href": "https://github.com/agentfront/enclave", - "icon": "github", - "label": "GitHub" - } - ], - "primary": { - "href": "https://github.com/agentfront/enclave", - "type": "github" - } - }, - "search": { - "prompt": "Search Enclave docs..." - }, - "navigation": { - "dropdowns": [ - { - "dropdown": "Documentation", - "icon": "book", - "versions": [ - { - "version": "v1.0 (latest)", - "default": true, - "groups": [ - { - "tag": "latest", - "group": "Get Started", - "pages": ["docs/getting-started/welcome", "docs/getting-started/installation"] - }, - { - "group": "Libraries", - "pages": ["docs/libraries/ast-guard", "docs/libraries/vectoriadb", "docs/libraries/enclave-vm"] - } - ] - } - ] - } - ] - } -}
docs/draft/docs/libraries/ast-guard.mdx+0 −138 removed@@ -1,138 +0,0 @@ ---- -title: 'ast-guard' -description: 'Production-ready AST security guard for JavaScript validation' ---- - -# ast-guard - -Production-ready AST security guard for JavaScript validation and code safety. Blocks all known vm2/isolated-vm/node-vm CVE exploits. - -## Installation - -```bash -npm install ast-guard -``` - -## Quick Start - -```typescript -import { validate, PRESETS } from 'ast-guard'; - -const result = validate('const x = 1 + 2;', PRESETS.SECURE); -console.log(result.valid); // true -``` - -## Features - -- **100% CVE Coverage**: Blocks all known vm2, isolated-vm, and node-vm exploits -- **613+ Security Tests**: Comprehensive test suite with 95%+ code coverage -- **Security Presets**: STRICT, SECURE, STANDARD, PERMISSIVE -- **Zero Dependencies**: Only uses acorn for parsing - -## AgentScript preset hardening - -Use `createAgentScriptPreset()` when you want AST validation to match Enclave's runtime sandbox. The preset now: - -- Blocks modern browser primitives like `structuredClone`, `AbortController`/`AbortSignal`, `MessageChannel`/`MessagePort`, `BroadcastChannel`, `TextEncoder`/`TextDecoder`, and `Intl` so those capabilities never reach execution. -- Treats `queueMicrotask` the same way as timers to guard against microtask flooding attacks. -- Reports dynamic `import()` expressions through `NoEvalRule`, preventing untrusted code from lazy-loading modules. - -## Security Presets - -| Preset | Description | -|--------|-------------| -| `STRICT` | Maximum security, minimal features | -| `SECURE` | Balanced security for most use cases | -| `STANDARD` | Standard security with more features | -| `PERMISSIVE` | Minimal restrictions | - -## API - -### `validate(code, preset)` - -Validates JavaScript code against a security preset. - -```typescript -import { validate, PRESETS } from 'ast-guard'; - -const result = validate(code, PRESETS.SECURE); - -if (result.valid) { - // Code is safe to execute -} else { - console.log(result.errors); -} -``` - -### `transform(code, options)` - -Transforms code to add safety features like loop guards. - -```typescript -import { transform } from 'ast-guard'; - -const transformed = transform(code, { - maxIterations: 10000, - maxDepth: 100 -}); -``` - -## Built-in Rules - -### ResourceExhaustionRule - -Detects AST patterns that could cause CPU or memory exhaustion by exploiting native code execution paths that bypass VM timeout. - -**Detected Patterns:** -- BigInt exponentiation with large exponents: `2n ** 1000000n` -- Large array allocations: `new Array(10000000)` -- String repeat with large counts: `'x'.repeat(10000000)` -- Constructor property access chains (sandbox escape vector) -- String concatenation building `'constructor'` (obfuscation attempt) - -**Configuration Options:** - -| Option | Type | Default | Description | -|--------|------|---------|-------------| -| `maxBigIntExponent` | number | 10000 | Maximum allowed BigInt exponent | -| `maxArraySize` | number | 1000000 | Maximum allowed array size literal | -| `maxStringRepeat` | number | 100000 | Maximum allowed string repeat count | -| `blockConstructorAccess` | boolean | true | Block constructor property access patterns | -| `blockBigIntExponentiation` | boolean | false | Block all BigInt exponentiation | - -**Example Usage:** - -```typescript -import { validate, ResourceExhaustionRule } from 'ast-guard'; - -const rules = [ - new ResourceExhaustionRule({ - maxBigIntExponent: 5000, - maxArraySize: 500000, - blockConstructorAccess: true, - }), -]; - -const result = validate(code, { rules }); -``` - -**Blocked Attack Examples:** - -```javascript -// CPU exhaustion - blocked -2n ** 1000000n - -// Memory exhaustion - blocked -new Array(10000000).fill(0) -'x'.repeat(50000000) - -// Sandbox escape attempts - blocked -const c = 'con' + 'structor'; -obj[c] // Detected as constructor obfuscation -obj['con' + 'struc' + 'tor'] // Inline concatenation blocked -``` - -## Links - -- [GitHub](https://github.com/agentfront/enclave/tree/main/libs/ast-guard) -- [npm](https://www.npmjs.com/package/ast-guard)
docs/draft/docs/libraries/enclave.mdx+0 −348 removed@@ -1,348 +0,0 @@ ---- -title: 'enclave-vm' -description: 'Secure AgentScript execution environment with defense-in-depth' ---- - -# enclave-vm - -Secure AgentScript execution environment with defense-in-depth architecture for running LLM-generated JavaScript safely. - -## Installation - -```bash -npm install enclave-vm -``` - -## Quick Start - -```typescript -import { Enclave } from 'enclave-vm'; - -const enclave = new Enclave({ securityLevel: 'SECURE' }); - -const result = await enclave.execute(` - const sum = 1 + 2; - return sum; -`); - -console.log(result.value); // 3 -``` - -## Features - -- **Bank-grade Security**: 516+ security tests, 81+ blocked attack vectors -- **Defense-in-depth**: 6 security layers -- **Worker Pool Adapter**: Isolated execution in separate processes -- **Reference Sidecar**: Sandboxed environments for untrusted code - -## Security Levels - -| Level | Description | -|-------|-------------| -| `STRICT` | Maximum security, minimal features | -| `SECURE` | Balanced security for most use cases | -| `STANDARD` | Standard security with more features | -| `PERMISSIVE` | Minimal restrictions | - -## Defense Layers - -1. **AST Validation** - Static analysis of code structure -2. **Pre-scanner** - Pattern-based threat detection -3. **Runtime Proxy** - Intercepts dangerous operations -4. **Globals Validation** - Restricts global access -5. **Value Sanitization** - Cleans return values -6. **Timeout/Memory Limits** - Resource constraints -7. **Double VM Layer** - Nested VM isolation (new) - -## Double VM Layer - -The Double VM layer provides enhanced security through nested VM isolation: - -- **Parent VM**: Security barrier with operation validation -- **Inner VM**: Isolated execution environment for user code -- **Tool call flow**: Inner VM → Parent VM validation → Host handler - -```typescript -const enclave = new Enclave({ - securityLevel: 'SECURE', - doubleVm: { - enabled: true, // Default: true - parentTimeoutBuffer: 1000, // Extra timeout for parent VM (ms) - parentValidation: { - validateOperationNames: true, - allowedOperationPattern: /^[a-z]+:[a-z]+$/i, // Optional whitelist - blockedOperationPatterns: [/^admin:/i], // Blacklist patterns - maxOperationsPerSecond: 100, // Rate limiting - blockSuspiciousSequences: true, // Detect attack patterns - }, - }, -}); -``` - -### Built-in Suspicious Pattern Detection - -| Pattern | Description | -|---------|-------------| -| `EXFIL_LIST_SEND` | List/query followed by send/export | -| `RAPID_ENUMERATION` | Same operation called >30 times in 5s (configurable) | -| `CREDENTIAL_EXFIL` | Credential access + external operation | -| `BULK_OPERATION` | Bulk/batch/mass operation names | -| `DELETE_AFTER_ACCESS` | Delete operation after data access | - -### Configuring Rapid Enumeration - -The `RAPID_ENUMERATION` pattern threshold is configurable: - -```typescript -const enclave = new Enclave({ - doubleVm: { - parentValidation: { - rapidEnumerationThreshold: 50, // Default: 30 - rapidEnumerationOverrides: { - 'search': 100, // Allow 100 search calls - 'users.list': 200, // Allow 200 list calls - }, - }, - }, -}); -``` - -### Custom Patterns - -```typescript -const enclave = new Enclave({ - doubleVm: { - parentValidation: { - suspiciousPatterns: [ - { - id: 'CUSTOM_PATTERN', - description: 'Custom detection logic', - detect: (operationName, args, history) => { - // Return true if suspicious - return operationName.includes('dangerous'); - }, - }, - ], - }, - }, -}); -``` - -### `PartialDoubleVmConfig` - -The `PartialDoubleVmConfig` type is the public API for configuring the Double VM layer. All properties are optional, with sensible defaults applied automatically. - -**Import:** - -```typescript -import type { PartialDoubleVmConfig } from 'enclave-vm'; -``` - -**Properties:** - -| Property | Type | Default | Description | -|----------|------|---------|-------------| -| `enabled` | `boolean` | `true` | Enable/disable the Double VM layer | -| `parentTimeoutBuffer` | `number` | `1000` | Extra timeout (ms) for parent VM operations | -| `parentValidation` | `object` | See below | Validation settings for the parent VM | - -**`parentValidation` options:** - -| Property | Type | Default | Description | -|----------|------|---------|-------------| -| `validateOperationNames` | `boolean` | `true` | Validate operation names against patterns | -| `allowedOperationPattern` | `RegExp` | — | Whitelist pattern (only matching ops allowed) | -| `blockedOperationPatterns` | `RegExp[]` | — | Blacklist patterns (matching ops blocked) | -| `maxOperationsPerSecond` | `number` | `100` | Rate limit for operations | -| `blockSuspiciousSequences` | `boolean` | `true` | Detect suspicious operation sequences | -| `rapidEnumerationThreshold` | `number` | `30` | Threshold for rapid enumeration detection | -| `rapidEnumerationOverrides` | `Record<string, number>` | `{}` | Per-operation threshold overrides | -| `suspiciousPatterns` | `SuspiciousPattern[]` | — | Custom pattern detectors | - -**Usage Examples:** - -```typescript -// Minimal: just enable/disable -const config: PartialDoubleVmConfig = { - enabled: true, -}; - -// With validation customization -const config: PartialDoubleVmConfig = { - parentTimeoutBuffer: 2000, - parentValidation: { - maxOperationsPerSecond: 50, - blockedOperationPatterns: [/^admin:/i, /^internal\./], - }, -}; - -// Disable Double VM (not recommended for production) -const config: PartialDoubleVmConfig = { - enabled: false, -}; -``` - -**When to use `PartialDoubleVmConfig` vs full config:** - -- Use `PartialDoubleVmConfig` (the exported type) when configuring `Enclave` — it accepts partial options with defaults -- The full `DoubleVmConfig` is used internally and requires all properties to be set - -## API - -### `execute(code, options)` - -Execute JavaScript code in a secure environment. - -```typescript -const result = await enclave.execute(code, { - timeout: 5000, - maxMemory: 128 * 1024 * 1024 -}); - -if (result.success) { - console.log(result.value); -} else { - console.log(result.error); -} -``` - -### `dispose()` - -Clean up resources. - -```typescript -enclave.dispose(); -``` - -## Runtime Globals — `__maxIterations` - -Scripts executed by the enclave VM have access to a special runtime global `__maxIterations` that reflects the configured iteration limit. This global is exposed as a **non-writable, non-configurable** number for safety. - -### Reading `__maxIterations` - -```typescript -const enclave = new Enclave({ - maxIterations: 50000, // Configure iteration limit - toolHandler: async (name, args) => { /* ... */ }, -}); - -const result = await enclave.run(` - // Access the configured iteration limit - const limit = __maxIterations; - - // Use it for adaptive logic - const safeLimit = Math.min(limit / 2, 10000); - - return { configuredLimit: limit, safeLimit }; -`); - -// result.value = { configuredLimit: 50000, safeLimit: 10000 } -``` - -### Use Cases - -- **Adaptive algorithms**: Adjust iteration bounds based on the configured limit -- **Progress reporting**: Calculate percentage completion relative to the limit -- **Self-throttling**: Implement custom iteration budgets within scripts - -### Security Notes - -- The global is **read-only** — attempts to modify it will fail silently or throw depending on strict mode -- The value matches the `maxIterations` configuration passed to `Enclave` -- Default value is `10000` if not explicitly configured - -### Cross-reference - -See the `maxIterations` option in [CreateEnclaveOptions](#createenclaveoptions) for configuration details. - -## Reference Sidecar - -For handling large data efficiently without bloating the VM context: - -```typescript -import { - REF_ID_PREFIX, // '__REF_' - REF_ID_SUFFIX, // '__' - isReferenceId, - REFERENCE_CONFIGS -} from 'enclave-vm'; - -// Reference IDs follow format: __REF_[UUIDv4]__ -const isRef = isReferenceId('__REF_12345678-1234-1234-1234-123456789abc__'); // true - -// Get config for security level -const strictConfig = REFERENCE_CONFIGS['STRICT']; -// strictConfig.maxTotalSize = 16MB, maxReferenceSize = 4MB, etc. -``` - -## Secure Proxy - -Runtime property access protection with configurable blocking: - -```typescript -import { - BlockedPropertyCategory, // 'CONSTRUCTOR' | 'PROTOTYPE' | 'LEGACY_ACCESSOR' | ... - BLOCKED_PROPERTY_CATEGORIES, - createSecureProxy -} from 'enclave-vm'; - -// Available categories: CONSTRUCTOR, PROTOTYPE, LEGACY_ACCESSOR, DUNDER_PROTO, TIMING -const allBlockedProps = BLOCKED_PROPERTY_CATEGORIES.CONSTRUCTOR; -// Set { 'constructor' } -``` - -## Worker Pool Adapter - -For complete process isolation: - -```typescript -import { Enclave, WorkerPoolAdapter } from 'enclave-vm'; - -const enclave = new Enclave({ - securityLevel: 'SECURE', - adapter: new WorkerPoolAdapter({ poolSize: 4 }) -}); -``` - -## Local LLM Scoring - -Leverage the Local LLM scorer to gate executions with on-disk models. - -**Note:** `LocalLlmScorer` loads the optional `@huggingface/transformers` peer dependency on demand. Install it before enabling local scoring: - -```bash -npm install @huggingface/transformers -``` - -```typescript -import { LocalLlmScorer } from 'enclave-vm/scoring'; - -const scorer = new LocalLlmScorer({ - modelId: 'Xenova/codebert-base', - mode: 'classification', - cacheDir: '~/.enclave/models' -}); -``` - -> **Migration note:** The default Hugging Face model cache directory moved from -> `./.cache/transformers` (project-relative) to `~/.enclave/models` (home directory). -> To migrate an existing cache: -> -> ```bash -> mkdir -p ~/.enclave -> mv ./.cache/transformers ~/.enclave/models -> ``` -> -> If you prefer to keep the old location, set `cacheDir: './.cache/transformers'`. - -### `LocalLlmConfig` - -- `modelId` (required): Hugging Face identifier such as `Xenova/codebert-base` -- `mode` (optional): `'classification'` (default) or `'similarity'` -- `cacheDir` (optional): Directory for cached models, default `~/.enclave/models` -- `vectoriaConfig` (optional): VectoriaDB settings required when `mode` is `'similarity'` - -## Links - -- [GitHub](https://github.com/agentfront/enclave/tree/main/libs/enclave) -- [npm](https://www.npmjs.com/package/enclave-vm)
docs/draft/updates.mdx+0 −22 removed@@ -1,22 +0,0 @@ ---- -title: 'Updates' -slug: 'updates' -icon: 'sparkles' -mode: 'center' ---- - -<Update label="v1.0.0" description="2025-11-30" tags={["Releases"]}> - <Card - title="Enclave-VM v1.0.0: Initial Release" - href="https://github.com/agentfront/enclave/releases/tag/v1.0.0" - cta="View release" - > - 🚀 **Enclave Monorepo** – Initial release of the Enclave monorepo containing security-focused libraries for AI agents. - - 🛡️ **ast-guard v1.0.0** – Production-ready AST security guard with 100% CVE coverage for vm2/isolated-vm/node-vm exploits. - - 🔍 **vectoriadb v1.0.0** – Lightweight in-memory vector database with HNSW indexing for semantic search. - - 🔐 **enclave-vm v1.0.0** – Secure AgentScript execution environment with defense-in-depth architecture. - </Card> -</Update>
docs/favicon.ico+0 −0 renameddocs/getting-started/installation.mdx+0 −0 renameddocs/getting-started/welcome.mdx+0 −0 renameddocs/libraries/ast-guard.mdx+0 −0 renameddocs/libraries/enclave.mdx+0 −0 renameddocs/libraries/vectoriadb.mdx+0 −0 renameddocs/live/docs/libraries/vectoriadb.mdx+0 −112 removed@@ -1,112 +0,0 @@ ---- -title: 'vectoriadb' -description: 'Lightweight in-memory vector database for semantic search' ---- - -# vectoriadb - -Lightweight, production-ready in-memory vector database for semantic search with HNSW indexing. - -## Installation - -```bash -npm install vectoriadb -``` - -## Quick Start - -```typescript -import { VectoriaDB } from 'vectoriadb'; - -const db = new VectoriaDB(); -await db.initialize(); - -// Insert documents -await db.insert({ id: '1', text: 'Hello world', metadata: {} }); -await db.insert({ id: '2', text: 'Goodbye world', metadata: {} }); - -// Search -const results = await db.search('greeting', 5); -console.log(results); -``` - -## Features - -- **HNSW Indexing**: Hierarchical Navigable Small World for fast approximate nearest neighbor search -- **Multiple Embeddings**: TF-IDF and transformer-based options -- **Persistence**: File and Redis adapters for data persistence -- **Security**: Built-in input validation - -## Embedding Options - -### TF-IDF (Default) - -Fast, lightweight embeddings suitable for most use cases. - -```typescript -const db = new VectoriaDB({ - embedding: 'tfidf' -}); -``` - -### Transformer-based - -Uses Hugging Face transformers for higher quality embeddings. - -**Note:** Transformer embeddings require installing the optional dependency: - -```bash -npm install @huggingface/transformers -``` - -```typescript -const db = new VectoriaDB({ - embedding: 'transformer', - model: 'Xenova/all-MiniLM-L6-v2' -}); -``` - -## Persistence - -### File Adapter - -```typescript -import { VectoriaDB, FileAdapter } from 'vectoriadb'; - -const db = new VectoriaDB({ - adapter: new FileAdapter('./data/vectors.json') -}); -``` - -### Redis Adapter - -```typescript -import { VectoriaDB, RedisAdapter } from 'vectoriadb'; - -const db = new VectoriaDB({ - adapter: new RedisAdapter({ host: 'localhost', port: 6379 }) -}); -``` - -## API - -### `insert(document)` - -Insert a document into the database. - -### `search(query, k)` - -Search for the k nearest documents to the query. - -### `delete(id)` - -Delete a document by ID. - -### `clear()` - -Clear all documents from the database. - -## Links - -- [GitHub](https://github.com/agentfront/enclave/tree/main/libs/vectoriadb) -- [npm](https://www.npmjs.com/package/vectoriadb)
docs/live/favicon.ico+0 −0 removeddocs/live/snippets/card.jsx+0 −105 removed@@ -1,105 +0,0 @@ -// BlogCard.tsx - -export const BlogCard = ({ - author, - date, - title, - link, - img, // light image - imgDark, // optional dark image - time, - draft, - children, -}) => { - if (draft) { - return null; - } - return ( - <div className="blog-card"> - <a - href={link} - className="card group relative my-2 ring-2 ring-transparent rounded-2xl - bg-white dark:bg-background-dark - border border-gray-950/10 dark:border-white/10 - overflow-hidden w-full cursor-pointer - hover:!border-primary dark:hover:!border-primary-light - flex flex-col md:flex-row" // <- force flex here - > - {/* IMAGE (left on desktop, full-width on mobile) */} - <div data-component-part="card-image" className="shrink-0"> - {imgDark ? ( - <> - {/* light mode image */} - <img - src={img} - alt={title} - className="blog-full-image light-img w-full h-full object-cover object-center not-prose" - /> - {/* dark mode image */} - <img - src={imgDark} - alt={title} - className="blog-full-image dark-img w-full h-full object-cover object-center not-prose" - /> - </> - ) : ( - // single image if you don't pass imgDark - <img src={img} alt={title} className="blog-full-image w-full h-full object-cover object-center not-prose" /> - )} - </div> - - {/* CONTENT */} - <div className="px-6 py-5 relative flex-1" data-component-part="card-content-container"> - {/* arrow icon */} - <div - id="card-link-arrow-icon" - className="absolute text-gray-400 dark:text-gray-500 - group-hover:text-primary dark:group-hover:text-primary-light - top-5 right-5" - > - <svg - xmlns="http://www.w3.org/2000/svg" - width="24" - height="24" - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - strokeWidth="2" - strokeLinecap="round" - strokeLinejoin="round" - className="lucide lucide-arrow-up-right w-4 h-4" - > - <path d="M7 7h10v10" /> - <path d="M7 17 17 7" /> - </svg> - </div> - - <div style={{ height: 'calc(100% - 1.5rem)' }}> - <div - className="prose mt-1 font-normal text-base leading-6 - text-gray-700 dark:text-gray-300 - flex flex-col h-full" - data-component-part="card-content" - > - <h2 - data-component-part="card-title" - className="not-prose font-semibold text-base text-gray-800 dark:text-white group-hover:text-primary dark:group-hover:text-primary-light" - > - {title} - </h2> - - {children && <span data-as="p">{children}</span>} - - <div className="flex-1" /> - - <div className="blog-by text-xs font-medium text-gray-700 dark:text-gray-300 mt-4 text-right"> - <span className="date">{date}</span> · <span className="time">{time}</span> ·{' '} - <span className="author">{author}</span> - </div> - </div> - </div> - </div> - </a> - </div> - ); -};
docs/snippets/card.jsx+0 −0 renameddocs/style.css+0 −0 renameddocs/updates.mdx+0 −0 renamedlibs/ast-guard/src/presets/agentscript.preset.ts+16 −0 modified@@ -217,6 +217,20 @@ export interface AgentScriptOptions { * Default: false */ requireCallTool?: boolean; + + /** + * Allow dynamic (computed) array size for .fill() operations + * + * When true, `Array(dynamicSize).fill()` is allowed because runtime memory + * patching will enforce the limit. Only enable this when memoryLimit is + * configured at runtime. + * + * When false (default), only literal sizes are allowed for .fill() to + * prevent memory exhaustion in environments without runtime protection. + * + * Default: false + */ + allowDynamicArrayFill?: boolean; } /** @@ -551,6 +565,8 @@ export function createAgentScriptPreset(options: AgentScriptOptions = {}): Valid maxStringRepeat: 100000, // Block 'x'.repeat(100001) and larger blockConstructorAccess: true, // Block obj.constructor and obj['constructor'] blockBigIntExponentiation: false, // Only block large exponents, not all + // Allow dynamic Array.fill when runtime protection is enabled (memoryLimit set) + allowDynamicArrayFill: options.allowDynamicArrayFill ?? false, }), );
libs/ast-guard/src/rules/resource-exhaustion.rule.ts+14 −1 modified@@ -17,6 +17,17 @@ export interface ResourceExhaustionOptions { blockConstructorAccess?: boolean; /** Block BigInt exponentiation entirely (default: false, only blocks large exponents) */ blockBigIntExponentiation?: boolean; + /** + * Allow dynamic (computed) array size for .fill() operations (default: false) + * + * When true, Array(dynamicSize).fill() is allowed because runtime memory + * patching will enforce the limit. Only enable this when memoryLimit is + * configured at runtime. + * + * When false (default), only literal sizes are allowed for .fill() to + * prevent memory exhaustion in environments without runtime protection. + */ + allowDynamicArrayFill?: boolean; } /** @@ -47,6 +58,7 @@ export class ResourceExhaustionRule implements ValidationRule { maxStringRepeat = 100000, blockConstructorAccess = true, blockBigIntExponentiation = false, + allowDynamicArrayFill = false, // Allow dynamic sizes when runtime protection is enabled } = this.options; walk.simple(context.ast as any, { @@ -171,9 +183,10 @@ export class ResourceExhaustionRule implements ValidationRule { location: this.getLocation(node), }); } - } else if (arg.type !== 'Literal') { + } else if (arg.type !== 'Literal' && !allowDynamicArrayFill) { // Variable-based or computed size - block as error since .fill() immediately allocates // and we cannot verify the size statically. This prevents Vector 1110 attacks. + // When allowDynamicArrayFill is true, runtime memory patching handles protection. context.report({ code: 'RESOURCE_EXHAUSTION', message: `Array.fill with dynamic size is not allowed. Use a literal size <= ${maxArrayFillSize} to prevent CPU/memory exhaustion.`,
libs/enclave-vm/README.md+3 −3 modified@@ -8,13 +8,13 @@ The enclave-vm package provides a hardened execution environment for running LLM-generated JavaScript code (AgentScript) safely. It combines AST validation, code transformation, runtime guards, and VM sandboxing to prevent sandbox escapes and resource exhaustion. -## Bank-Grade Security +## Security | Metric | Value | | -------------- | ---------------------------------------------------------------------- | -| Security Tests | 1184+ tests, 100% pass rate | +| Security Tests | 1900+ tests, 100% pass rate | | Attack Vectors | 150+ blocked (including function gadget attacks) | -| CVE Protection | 100% (vm2, isolated-vm, node-vm exploits) | +| CVE Protection | Covers known vm2, isolated-vm, node-vm exploits | | Defense Layers | 6 (Pre-Scanner, AST, Transform, Scoring, VM/Worker Pool, Sanitization) | For the full security audit report, see [SECURITY-AUDIT.md](./SECURITY-AUDIT.md).
libs/enclave-vm/src/adapters/vm-adapter.ts+194 −7 modified@@ -11,7 +11,10 @@ import * as vm from 'vm'; import type { SandboxAdapter, ExecutionContext, ExecutionResult, SecurityLevel } from '../types'; import { createSafeRuntime } from '../safe-runtime'; import { createSafeReflect, createSecureProxy } from '../secure-proxy'; +import { createSafeError } from '../safe-error'; import { MemoryTracker, MemoryLimitError } from '../memory-tracker'; +import { createHostToolBridge } from '../tool-bridge'; +import { checkSerializedSize } from '../value-sanitizer'; /** * Sensitive patterns to redact from stack traces @@ -341,8 +344,9 @@ function createSafeConsole( // Check call count limit BEFORE doing any work stats.callCount++; if (stats.callCount > config.maxConsoleCalls) { - throw new Error( + throw createSafeError( `Console call limit exceeded (max: ${config.maxConsoleCalls}). ` + `This limit prevents I/O flood attacks.`, + 'SecurityError', ); } @@ -365,9 +369,10 @@ function createSafeConsole( // Check output size limit stats.totalBytes += output.length; if (stats.totalBytes > config.maxConsoleOutputBytes) { - throw new Error( + throw createSafeError( `Console output size limit exceeded (max: ${config.maxConsoleOutputBytes} bytes). ` + `This limit prevents I/O flood attacks.`, + 'SecurityError', ); } @@ -402,33 +407,36 @@ function createProtectedSandbox(sandbox: vm.Context): vm.Context { return new Proxy(sandbox, { set(target, prop, value) { if (isProtectedIdentifier(prop)) { - throw new Error( + throw createSafeError( `Cannot modify protected identifier "${String(prop)}". ` + `Identifiers starting with ${PROTECTED_PREFIXES.map((p) => `"${p}"`).join( ', ', )} are protected runtime functions.`, + 'SecurityError', ); } return Reflect.set(target, prop, value); }, defineProperty(target, prop, descriptor) { if (isProtectedIdentifier(prop)) { - throw new Error( + throw createSafeError( `Cannot define protected identifier "${String(prop)}". ` + `Identifiers starting with ${PROTECTED_PREFIXES.map((p) => `"${p}"`).join( ', ', )} are protected runtime functions.`, + 'SecurityError', ); } return Reflect.defineProperty(target, prop, descriptor); }, deleteProperty(target, prop) { if (isProtectedIdentifier(prop)) { - throw new Error( + throw createSafeError( `Cannot delete protected identifier "${String(prop)}". ` + `Identifiers starting with ${PROTECTED_PREFIXES.map((p) => `"${p}"`).join( ', ', )} are protected runtime functions.`, + 'SecurityError', ); } return Reflect.deleteProperty(target, prop); @@ -507,7 +515,10 @@ function createSafeObject(originalObject: ObjectConstructor): ObjectConstructor // and does NOT allow property descriptors (second argument) SafeObject.create = function (proto: object | null, propertiesObject?: PropertyDescriptorMap) { if (propertiesObject !== undefined) { - throw new Error('Object.create with property descriptors is not allowed (security restriction)'); + throw createSafeError( + 'Object.create with property descriptors is not allowed (security restriction)', + 'SecurityError', + ); } return Object.create(proto); }; @@ -518,7 +529,10 @@ function createSafeObject(originalObject: ObjectConstructor): ObjectConstructor // Add blocked methods that throw helpful errors for (const method of DANGEROUS_OBJECT_STATIC_METHODS) { SafeObject[method] = function () { - throw new Error(`Object.${method} is not allowed (security restriction: prevents property manipulation attacks)`); + throw createSafeError( + `Object.${method} is not allowed (security restriction: prevents property manipulation attacks)`, + 'SecurityError', + ); }; } @@ -815,6 +829,34 @@ export class VmAdapter implements SandboxAdapter { trackMemory(estimatedSize); return originalPadEnd.call(this, targetLength, padString); }; + + // Patch array fill - converts sparse arrays to dense (allocates memory) + // Vector 1110/1170: Array(dynamicSize).fill() can exhaust memory + // Estimate: each element uses ~8 bytes (pointer) for objects/primitives + var originalFill = arrayProto.fill; + arrayProto.fill = function(value, start, end) { + // Calculate actual fill range + var len = this.length >>> 0; + var relativeStart = start === undefined ? 0 : (start >> 0); + var relativeEnd = end === undefined ? len : (end >> 0); + var k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len); + var finalEnd = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len); + var fillCount = Math.max(0, finalEnd - k); + + // Estimate memory: 8 bytes per element (pointer size) + // For objects/arrays as fill value, each creates a reference + var estimatedSize = fillCount * 8; + + // Check single allocation limit + if (estimatedSize > memoryLimit) { + throw new RangeError('Array.fill would exceed memory limit: ' + + Math.round(estimatedSize / 1024 / 1024) + 'MB > ' + + Math.round(memoryLimit / 1024 / 1024) + 'MB'); + } + // Track cumulative memory BEFORE allocation (throws if limit exceeded) + trackMemory(estimatedSize); + return originalFill.call(this, value, start, end); + }; })(); `); patchScript.runInContext(baseSandbox); @@ -855,6 +897,127 @@ export class VmAdapter implements SandboxAdapter { // Security: Prevents escape via Iterator helpers, ShadowRealm, etc. sanitizeVmContext(baseSandbox, this.securityLevel); + // TOOL BRIDGE (string mode): define __safe_callTool inside the VM realm and + // communicate with the host tool handler via JSON string envelopes. + if (config.toolBridge?.mode === 'string') { + const maxPayloadBytes = config.toolBridge?.maxPayloadBytes ?? 5 * 1024 * 1024; + const hostToolBridge = createHostToolBridge(executionContext, { updateStats: true }); + + Object.defineProperty(baseSandbox, '__host_callToolBridge__', { + value: hostToolBridge, + writable: false, + configurable: true, // allow deletion after capture + enumerable: false, + }); + + const bridgeInitScript = new vm.Script(` + (function() { + var bridge = __host_callToolBridge__; + var stringify = JSON.stringify; + var parse = JSON.parse; + var hasOwn = Object.prototype.hasOwnProperty; + var maxBytes = ${maxPayloadBytes}; + + // Remove global handle after capture (defense-in-depth) + try { delete globalThis.__host_callToolBridge__; } catch (e) { /* ignore */ } + + function estimateBytes(str) { + // Conservative: UTF-8 can be up to 4 bytes per code unit. + return str.length * 4; + } + + function makeError(message, name) { + var err = new Error(message); + if (name) err.name = name; + return err; + } + + return async function __safe_callTool(toolName, args) { + if (typeof toolName !== 'string' || !toolName) { + throw makeError('Tool name must be a non-empty string', 'TypeError'); + } + if (typeof args !== 'object' || args === null || Array.isArray(args)) { + throw makeError('Tool arguments must be an object', 'TypeError'); + } + if (typeof bridge !== 'function') { + throw makeError('Tool bridge is not available', 'Error'); + } + + // Defense-in-depth: ensure JSON-serializable input. + var sanitizedArgs; + try { + sanitizedArgs = parse(stringify(args)); + } catch (e) { + throw makeError('Tool arguments must be JSON-serializable', 'TypeError'); + } + + var requestJson; + try { + requestJson = stringify({ v: 1, tool: toolName, args: sanitizedArgs }); + } catch (e) { + throw makeError('Tool request must be JSON-serializable', 'TypeError'); + } + + if (estimateBytes(requestJson) > maxBytes) { + throw makeError('Tool request exceeds maximum size (' + maxBytes + ' bytes)', 'RangeError'); + } + + var responseJson = await bridge(requestJson); + if (typeof responseJson !== 'string') { + throw makeError('Tool bridge returned invalid response', 'Error'); + } + if (estimateBytes(responseJson) > maxBytes) { + throw makeError('Tool response exceeds maximum size (' + maxBytes + ' bytes)', 'RangeError'); + } + + var response; + try { + response = parse(responseJson); + } catch (e) { + throw makeError('Tool bridge returned invalid JSON', 'Error'); + } + + if (!response || typeof response !== 'object' || Array.isArray(response) || response.v !== 1) { + throw makeError('Tool bridge returned invalid response', 'Error'); + } + + if (response.ok === true) { + if (hasOwn.call(response, 'value')) return response.value; + return undefined; + } + + if (response.ok === false && response.error) { + var msg = (typeof response.error.message === 'string') ? response.error.message : 'Tool call failed'; + var name = (typeof response.error.name === 'string') ? response.error.name : 'Error'; + throw makeError(msg, name); + } + + throw makeError('Tool bridge returned invalid response', 'Error'); + }; + })() + `); + + const vmSafeCallTool = bridgeInitScript.runInContext(baseSandbox) as unknown as object; + const proxiedVmSafeCallTool = createSecureProxy(vmSafeCallTool, { + levelConfig: executionContext.secureProxyConfig, + }); + + Object.defineProperty(baseSandbox, '__safe_callTool', { + value: proxiedVmSafeCallTool, + writable: false, + configurable: false, + enumerable: true, + }); + + // Best-effort cleanup if init script couldn't delete it for any reason. + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + delete (baseSandbox as any).__host_callToolBridge__; + } catch { + // ignore + } + } + // Add safe runtime functions to the isolated context as non-writable, non-configurable // Security: Prevents runtime override attacks on __safe_* functions // Note: Skip 'Object' if already added by sanitizeVmContext (with SafeObject) @@ -970,6 +1133,30 @@ export class VmAdapter implements SandboxAdapter { }; } + // SECURITY FIX (Vector 340): Check serialized size of return value BEFORE returning. + // Attacks can create structures with many references to the same large string that + // appear small in memory (strings are shared by reference) but explode during + // JSON serialization when each reference becomes a full copy. + // Example: 500 refs × 5 copies × 10KB = 25MB serialized from ~20KB in-memory + if (config.memoryLimit && config.memoryLimit > 0 && value !== undefined) { + // Use memory limit as serialization limit (or a reasonable cap) + // This prevents the serialization size from exceeding what we'd allow in memory + const maxSerializedBytes = Math.min(config.memoryLimit, 50 * 1024 * 1024); // Cap at 50MB + const sizeCheck = checkSerializedSize(value, maxSerializedBytes); + + if (!sizeCheck.ok) { + return { + success: false, + error: { + name: 'MemoryLimitError', + message: `Return value serialization would exceed memory limit: ${sizeCheck.error}`, + code: 'SERIALIZATION_LIMIT_EXCEEDED', + }, + stats, + }; + } + } + return { success: true, value: value as T,
libs/enclave-vm/src/double-vm/double-vm-wrapper.ts+42 −95 modified@@ -10,102 +10,16 @@ import * as vm from 'vm'; import type { SandboxAdapter, ExecutionContext, ExecutionResult, SecurityLevel } from '../types'; -import { sanitizeValue } from '../value-sanitizer'; +import { sanitizeValue, checkSerializedSize } from '../value-sanitizer'; import { getBlockedPropertiesForLevel, buildBlockedPropertiesFromConfig } from '../secure-proxy'; +import { createSafeError } from '../safe-error'; import { ReferenceResolver } from '../sidecar/reference-resolver'; import { MemoryTracker, MemoryLimitError } from '../memory-tracker'; +import { createHostToolBridge } from '../tool-bridge'; import type { DoubleVmConfig, SerializableParentValidationConfig } from './types'; import { generateParentVmBootstrap } from './parent-vm-bootstrap'; import { serializePatterns, DEFAULT_SUSPICIOUS_PATTERNS } from './suspicious-patterns'; -/** - * Creates a "safe" error object that cannot be used to escape the sandbox. - * - * SECURITY: This function creates error objects with a severed prototype chain - * to prevent attacks that climb the prototype chain to reach the host Function constructor. - * - * Attack vector blocked: - * - err.constructor.constructor('return process.env.SECRET')() - * - err.__proto__.constructor.constructor('malicious code')() - */ -function createSafeError(message: string, name = 'Error'): Error { - // SECURITY: Create a real Error instance but with the constructor property - // overridden to break the prototype chain escape attack. - // - // The key insight is that we need: - // 1. A real Error instance (for instanceof checks, proper stack traces) - // 2. But with .constructor pointing to a safe object that can't be used to escape - - // Create the real error - const error = new Error(message); - error.name = name; - - // Create a null-prototype object to use as a safe "constructor" - // This object has no prototype chain to climb - const SafeConstructor = Object.create(null); - Object.defineProperties(SafeConstructor, { - // Make constructor point to itself to break the chain - constructor: { - value: SafeConstructor, - writable: false, - enumerable: false, - configurable: false, - }, - // Block prototype access - prototype: { - value: null, - writable: false, - enumerable: false, - configurable: false, - }, - // Add name for debugging - name: { - value: 'SafeError', - writable: false, - enumerable: false, - configurable: false, - }, - }); - Object.freeze(SafeConstructor); - - // Override the constructor property on the error instance - // This breaks the prototype chain: err.constructor.constructor no longer reaches Function - Object.defineProperty(error, 'constructor', { - value: SafeConstructor, - writable: false, - enumerable: false, - configurable: false, - }); - - // SECURITY: Override __proto__ on the error instance to prevent prototype chain escape - // Attack vector blocked: err.__proto__.constructor.constructor('malicious code')() - // By setting __proto__ to null (non-configurable, non-writable, non-enumerable), - // we sever the link to Error.prototype, preventing attackers from climbing to Function - Object.defineProperty(error, '__proto__', { - value: null, - writable: false, - enumerable: false, - configurable: false, - }); - - // SECURITY: Remove the stack property to prevent information leakage - // Attack vector blocked: Vector 270 - Static-Literal Tool Discovery - // The stack trace can reveal internal implementation details like function names, - // file paths, and line numbers which can be used for reconnaissance attacks. - // By setting stack to undefined, we prevent attackers from extracting this information. - Object.defineProperty(error, 'stack', { - value: undefined, - writable: false, - enumerable: false, - configurable: false, - }); - - // Freeze the error to prevent modifications - Object.freeze(error); - - return error; -} - /** * Sensitive patterns to redact from stack traces * (Same as vm-adapter for consistency) @@ -224,6 +138,30 @@ export class DoubleVmWrapper implements SandboxAdapter { }; } + // SECURITY FIX (Vector 340): Check serialized size of return value BEFORE returning. + // Attacks can create structures with many references to the same large string that + // appear small in memory (strings are shared by reference) but explode during + // JSON serialization when each reference becomes a full copy. + // Example: 500 refs × 5 copies × 10KB = 25MB serialized from ~20KB in-memory + if (config.memoryLimit && config.memoryLimit > 0 && value !== undefined) { + // Use memory limit as serialization limit (or a reasonable cap) + // This prevents the serialization size from exceeding what we'd allow in memory + const maxSerializedBytes = Math.min(config.memoryLimit, 50 * 1024 * 1024); // Cap at 50MB + const sizeCheck = checkSerializedSize(value, maxSerializedBytes); + + if (!sizeCheck.ok) { + return { + success: false, + error: { + name: 'MemoryLimitError', + message: `Return value serialization would exceed memory limit: ${sizeCheck.error}`, + code: 'SERIALIZATION_LIMIT_EXCEEDED', + }, + stats, + }; + } + } + return { success: true, value: value as T, @@ -334,32 +272,36 @@ export class DoubleVmWrapper implements SandboxAdapter { }); // Inject tool call proxy to host - const hostCallTool = this.createHostCallToolProxy(executionContext); + const toolBridgeMode = config.toolBridge?.mode ?? 'string'; + const hostCallTool = + toolBridgeMode === 'string' + ? createHostToolBridge(executionContext, { updateStats: false }) + : this.createHostCallToolProxy(executionContext); Object.defineProperty(parentContext, '__host_callTool__', { value: hostCallTool, writable: false, - configurable: false, + configurable: true, // Allow deletion after capture for defense-in-depth }); // Inject mutable stats reference so parent can update counts Object.defineProperty(parentContext, '__host_stats__', { value: stats, writable: false, - configurable: false, + configurable: true, // Allow deletion after capture for defense-in-depth }); // Inject abort check function Object.defineProperty(parentContext, '__host_abort_check__', { value: () => executionContext.aborted, writable: false, - configurable: false, + configurable: true, // Allow deletion after capture for defense-in-depth }); // Inject policy-violation reporter (used for STRICT/SECURE fail-closed behavior) Object.defineProperty(parentContext, '__host_reportViolation__', { value: reportViolation, writable: false, - configurable: false, + configurable: true, // Allow deletion after capture for defense-in-depth }); // Inject config (for globals, console limits, and memory limit) @@ -371,7 +313,7 @@ export class DoubleVmWrapper implements SandboxAdapter { memoryLimit: config.memoryLimit, // Required for pre-allocation checks in inner VM }, writable: false, - configurable: false, + configurable: true, // Allow deletion after capture for defense-in-depth }); // Inject memory tracking callback (when memoryLimit is set) @@ -559,6 +501,9 @@ export class DoubleVmWrapper implements SandboxAdapter { // Whether composite reference handles are allowed const allowComposites = referenceConfig?.allowComposites ?? false; + const toolBridgeMode = config.toolBridge?.mode ?? 'string'; + const toolBridgeMaxPayloadBytes = config.toolBridge?.maxPayloadBytes ?? 5 * 1024 * 1024; + return generateParentVmBootstrap({ userCode: code, innerTimeout: config.timeout, @@ -572,6 +517,8 @@ export class DoubleVmWrapper implements SandboxAdapter { allowComposites, memoryLimit: config.memoryLimit, throwOnBlocked, + toolBridgeMode, + toolBridgeMaxPayloadBytes, }); }
libs/enclave-vm/src/double-vm/parent-vm-bootstrap.ts+136 −16 modified@@ -30,6 +30,18 @@ export interface ParentVmBootstrapOptions { /** Maximum tool calls allowed */ maxToolCalls: number; + /** + * Tool bridge mode used for host tool calls. + * @default 'string' + */ + toolBridgeMode: 'string' | 'direct'; + + /** + * Maximum size (in bytes) of a tool request/response payload. + * @default 5 * 1024 * 1024 (5MB) + */ + toolBridgeMaxPayloadBytes: number; + /** * Whether to sanitize stack traces in the sandbox. * Prevents host stack frame/path leakage via `error.stack` when user code catches errors. @@ -191,6 +203,8 @@ export function generateParentVmBootstrap(options: ParentVmBootstrapOptions): st innerTimeout, maxIterations, maxToolCalls, + toolBridgeMode = 'string', + toolBridgeMaxPayloadBytes = 5 * 1024 * 1024, sanitizeStackTraces, securityLevel, validationConfig, @@ -437,7 +451,16 @@ ${stackTraceHardeningCode} function __ag_createSafeError(message, name) { if (name === undefined) name = 'Error'; var error = new Error(message); - error.name = name; + // NOTE: Use __ag_Object.defineProperty instead of direct assignment because + // Error.prototype.name is frozen, and in strict mode direct assignment fails. + try { + __ag_Object.defineProperty(error, 'name', { + value: name, + writable: true, + enumerable: false, + configurable: true + }); + } catch (e) {} try { var SafeConstructor = __ag_Object.create(null); __ag_Object.defineProperties(SafeConstructor, { @@ -627,19 +650,30 @@ ${stackTraceHardeningCode} // Parent VM Bootstrap Script // This code runs inside the Parent VM and creates/manages the Inner VM -(async function parentVmMain() { - // Get injected references from host - const vm = __host_vm_module__; - const hostCallTool = __host_callTool__; - const hostStats = __host_stats__; - const hostAbortCheck = __host_abort_check__; - const hostReportViolation = __host_reportViolation__; - const hostConfig = __host_config__; - - // Policy violation reporter (best-effort; host decides how to handle based on security level) - function __ag_reportViolation(kind) { - try { - if (typeof hostReportViolation === 'function') hostReportViolation(kind); + (async function parentVmMain() { + // Get injected references from host + const vm = __host_vm_module__; + const hostCallTool = __host_callTool__; + const hostStats = __host_stats__; + const hostAbortCheck = __host_abort_check__; + const hostReportViolation = __host_reportViolation__; + const hostConfig = __host_config__; + const toolBridgeMode = ${JSON.stringify(toolBridgeMode)}; + const toolBridgeMaxPayloadBytes = ${toolBridgeMaxPayloadBytes}; + + // Defense-in-depth: remove direct access to host bridge references after capture. + // If the inner VM escapes to the parent global, these names should not be discoverable. + try { delete globalThis.__host_callTool__; } catch (e) { /* ignore */ } + try { delete globalThis.__host_vm_module__; } catch (e) { /* ignore */ } + try { delete globalThis.__host_stats__; } catch (e) { /* ignore */ } + try { delete globalThis.__host_abort_check__; } catch (e) { /* ignore */ } + try { delete globalThis.__host_reportViolation__; } catch (e) { /* ignore */ } + try { delete globalThis.__host_config__; } catch (e) { /* ignore */ } + + // Policy violation reporter (best-effort; host decides how to handle based on security level) + function __ag_reportViolation(kind) { + try { + if (typeof hostReportViolation === 'function') hostReportViolation(kind); } catch (e) { /* ignore */ } } @@ -673,7 +707,18 @@ ${stackTraceHardeningCode} // Create the real error var error = new Error(message); - error.name = name; + // NOTE: Use Object.defineProperty instead of direct assignment because + // Error.prototype.name is frozen, and in strict mode direct assignment fails. + Object.defineProperty(error, 'name', { + value: name, + writable: true, + enumerable: false, + configurable: true + }); + + // CRITICAL: sever the *actual* prototype chain (native getters / Object.getPrototypeOf). + // A shadowing __proto__ data property is not sufficient. + Object.setPrototypeOf(error, null); // Create a null-prototype object to use as a safe "constructor" // This object has no prototype chain to climb @@ -1031,7 +1076,63 @@ ${stackTraceHardeningCode} // Forward to host and wrap the Promise + result in secure proxies // This prevents access to Promise.constructor and result object constructors - var promise = hostCallTool(toolName, sanitizedArgs); + var promise; + if (toolBridgeMode === 'string') { + var requestJson; + try { + requestJson = JSON.stringify({ v: 1, tool: toolName, args: sanitizedArgs }); + } catch (e) { + throw createSafeError('Tool request must be JSON-serializable'); + } + + // Conservative byte estimate: UTF-8 is at most 4 bytes per code unit. + if (requestJson && (requestJson.length * 4) > toolBridgeMaxPayloadBytes) { + throw createSafeError('Tool request exceeds maximum size (' + toolBridgeMaxPayloadBytes + ' bytes)'); + } + + promise = hostCallTool(requestJson).then(function(responseJson) { + if (typeof responseJson !== 'string') { + throw createSafeError('Tool bridge returned non-string response'); + } + + if ((responseJson.length * 4) > toolBridgeMaxPayloadBytes) { + throw createSafeError('Tool response exceeds maximum size (' + toolBridgeMaxPayloadBytes + ' bytes)'); + } + + var response; + try { + response = JSON.parse(responseJson); + } catch (e) { + throw createSafeError('Tool bridge returned invalid JSON'); + } + + if (!response || typeof response !== 'object' || Array.isArray(response)) { + throw createSafeError('Tool bridge returned invalid response'); + } + + if (response.v !== 1) { + throw createSafeError('Tool bridge protocol version mismatch'); + } + + if (response.ok === true) { + if (Object.prototype.hasOwnProperty.call(response, 'value')) { + return response.value; + } + return undefined; + } + + if (response.ok === false) { + var err = response.error; + var msg = (err && typeof err.message === 'string') ? err.message : 'Tool call failed'; + var name = (err && typeof err.name === 'string') ? err.name : 'Error'; + throw createSafeError(msg, name); + } + + throw createSafeError('Tool bridge returned invalid response'); + }); + } else { + promise = hostCallTool(toolName, sanitizedArgs); + } // Create a secure promise that wraps both the promise object and its result // Note: We can't just wrap the promise because Promise.then/catch/finally must work @@ -1402,6 +1503,25 @@ ${stackTraceHardeningCode} ' trackMemory(estimatedSize);' + ' return originalPadEnd.call(this, targetLength, padString);' + '};' + + // Patch array fill - converts sparse arrays to dense (allocates memory) + // Vector 1110/1170: Array(dynamicSize).fill() can exhaust memory + 'var originalFill = arrayProto.fill;' + + 'arrayProto.fill = function(value, start, end) {' + + ' var len = this.length >>> 0;' + + ' var relativeStart = start === undefined ? 0 : (start >> 0);' + + ' var relativeEnd = end === undefined ? len : (end >> 0);' + + ' var k = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);' + + ' var finalEnd = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len);' + + ' var fillCount = Math.max(0, finalEnd - k);' + + ' var estimatedSize = fillCount * 8;' + + ' if (estimatedSize > memoryLimit) {' + + ' throw new RangeError("Array.fill would exceed memory limit: " +' + + ' Math.round(estimatedSize / 1024 / 1024) + "MB > " +' + + ' Math.round(memoryLimit / 1024 / 1024) + "MB");' + + ' }' + + ' trackMemory(estimatedSize);' + + ' return originalFill.call(this, value, start, end);' + + '};' + '})();'; var patchScript = new vm.Script(patchCode); patchScript.runInContext(innerContext);
libs/enclave-vm/src/enclave.ts+30 −0 modified@@ -52,6 +52,11 @@ import { ScoringGate, ScoringGateResult } from './scoring'; */ const DEFAULT_SECURITY_LEVEL: SecurityLevel = 'STANDARD'; +/** + * Default maximum tool bridge payload size. + */ +const DEFAULT_TOOL_BRIDGE_MAX_PAYLOAD_BYTES = 5 * 1024 * 1024; // 5MB + /** * Get merged configuration from security level and explicit options * Explicit options override security level defaults @@ -192,6 +197,27 @@ export class Enclave { }); } + // Normalize tool bridge configuration (defaults + validation) + const normalizedToolBridge = { + mode: options.toolBridge?.mode ?? 'string', + maxPayloadBytes: options.toolBridge?.maxPayloadBytes ?? DEFAULT_TOOL_BRIDGE_MAX_PAYLOAD_BYTES, + acknowledgeInsecureDirect: options.toolBridge?.acknowledgeInsecureDirect ?? false, + }; + + if (normalizedToolBridge.mode !== 'string' && normalizedToolBridge.mode !== 'direct') { + throw new TypeError(`Invalid toolBridge.mode: ${String(normalizedToolBridge.mode)}`); + } + + if (!Number.isFinite(normalizedToolBridge.maxPayloadBytes) || normalizedToolBridge.maxPayloadBytes <= 0) { + throw new TypeError('toolBridge.maxPayloadBytes must be a positive, finite number'); + } + + if (normalizedToolBridge.mode === 'direct' && normalizedToolBridge.acknowledgeInsecureDirect !== true) { + throw new Error( + `toolBridge.mode='direct' is insecure; set toolBridge.acknowledgeInsecureDirect=true to enable it explicitly.`, + ); + } + // Merge with defaults, applying security level configuration // Note: We explicitly set secureProxyConfig AFTER spreading options to ensure // the merged config from securityConfig takes precedence over partial options @@ -206,6 +232,7 @@ export class Enclave { maxConsoleOutputBytes: securityConfig.maxConsoleOutputBytes, maxConsoleCalls: securityConfig.maxConsoleCalls, ...options, + toolBridge: normalizedToolBridge, // secureProxyConfig must come AFTER options spread to use the merged config secureProxyConfig: securityConfig.secureProxyConfig, globals: { @@ -647,10 +674,13 @@ export class Enclave { switch (presetName) { case 'agentscript': // AgentScript preset with security-level-aware globals + // Enable allowDynamicArrayFill when memoryLimit is configured + // because runtime Array.prototype.fill patching provides protection return new JSAstValidator( createAgentScriptPreset({ securityLevel: this.securityLevel, allowedGlobals: allAllowedGlobals, + allowDynamicArrayFill: this.config.memoryLimit > 0, }), );
libs/enclave-vm/src/safe-error.ts+79 −0 added@@ -0,0 +1,79 @@ +/** + * Safe Error Utilities + * + * SECURITY: Any Error object created in the host realm and exposed to sandbox code + * must not allow reaching the host Function constructor via prototype-chain walks. + * + * Threat model: + * - Sandbox code can intentionally trigger host-side failures (e.g. tool calls, proxy traps) + * - If a host Error crosses the boundary, attackers can climb: + * hostError -> Error.prototype -> Error -> Function -> new Function('...')() + * and execute arbitrary host code (e.g. `process.env`, command execution). + */ + +/** + * Create an Error instance whose prototype chain is severed (actual [[Prototype]]), + * preventing prototype-chain escape to host constructors. + */ +export function createSafeError(message: string, name = 'Error'): Error { + const error = new Error(message); + error.name = name; + + // Null-prototype "constructor" object to break `err.constructor.constructor` chains. + const SafeConstructor = Object.create(null); + Object.defineProperties(SafeConstructor, { + constructor: { + value: SafeConstructor, + writable: false, + enumerable: false, + configurable: false, + }, + prototype: { + value: null, + writable: false, + enumerable: false, + configurable: false, + }, + name: { + value: 'SafeError', + writable: false, + enumerable: false, + configurable: false, + }, + }); + Object.freeze(SafeConstructor); + + // CRITICAL: sever the *actual* prototype chain (native getters / Object.getPrototypeOf). + // A shadowing `__proto__` data property is not sufficient. + Object.setPrototypeOf(error, null); + + // Provide safe shadow properties for common escape paths / ergonomics. + Object.defineProperty(error, 'constructor', { + value: SafeConstructor, + writable: false, + enumerable: false, + configurable: false, + }); + + Object.defineProperty(error, '__proto__', { + value: null, + writable: false, + enumerable: false, + configurable: false, + }); + + // Do not leak stack traces from host internals. + Object.defineProperty(error, 'stack', { + value: undefined, + writable: false, + enumerable: false, + configurable: false, + }); + + Object.freeze(error); + return error; +} + +export function createSafeTypeError(message: string): Error { + return createSafeError(message, 'TypeError'); +}
libs/enclave-vm/src/safe-runtime.ts+7 −108 modified@@ -9,120 +9,14 @@ import type { ExecutionContext, SecureProxyLevelConfig } from './types'; import { sanitizeValue } from './value-sanitizer'; +import { createSafeError, createSafeTypeError } from './safe-error'; import { ReferenceSidecar } from './sidecar/reference-sidecar'; import { ReferenceResolver, ResolutionLimitError } from './sidecar/reference-resolver'; import { ReferenceConfig, isReferenceId } from './sidecar/reference-config'; import { createSecureProxy, wrapGlobalsWithSecureProxy, SecureProxyOptions } from './secure-proxy'; import { MemoryTracker } from './memory-tracker'; import { createTrackedString, createTrackedArray } from './memory-proxy'; -/** - * Creates a "safe" error object that cannot be used to escape the sandbox. - * - * SECURITY: This function creates error objects with a severed prototype chain - * to prevent attacks that climb the prototype chain to reach the host Function constructor. - * - * Attack vector blocked: - * - err.constructor.constructor('return process.env.SECRET')() - * - err.__proto__.constructor.constructor('malicious code')() - * - * The returned error has: - * - Frozen constructor property pointing to a safe, frozen function - * - Blocked __proto__ access - * - Standard error properties (name, message, stack) preserved - * - * @param message - Error message - * @param name - Error name (default: 'Error') - * @returns A safe error object that can be thrown - */ -function createSafeError(message: string, name = 'Error'): Error { - // SECURITY: Create a real Error instance but with the constructor property - // overridden to break the prototype chain escape attack. - // - // Attack vector blocked: - // - err.constructor.constructor('return process.env.SECRET')() - // - // The key insight is that we need: - // 1. A real Error instance (for instanceof checks, proper stack traces, Jest compatibility) - // 2. But with .constructor pointing to a safe object that can't be used to escape - - // Create the real error - const error = new Error(message); - error.name = name; - - // Create a null-prototype object to use as a safe "constructor" - // This object has no prototype chain to climb - const SafeConstructor = Object.create(null); - Object.defineProperties(SafeConstructor, { - // Make constructor point to itself to break the chain - constructor: { - value: SafeConstructor, - writable: false, - enumerable: false, - configurable: false, - }, - // Block prototype access - prototype: { - value: null, - writable: false, - enumerable: false, - configurable: false, - }, - // Add name for debugging - name: { - value: 'SafeError', - writable: false, - enumerable: false, - configurable: false, - }, - }); - Object.freeze(SafeConstructor); - - // Override the constructor property on the error instance - // This breaks the prototype chain: err.constructor.constructor no longer reaches Function - Object.defineProperty(error, 'constructor', { - value: SafeConstructor, - writable: false, - enumerable: false, - configurable: false, - }); - - // SECURITY: Override __proto__ on the error instance to prevent prototype chain escape - // Attack vector blocked: err.__proto__.constructor.constructor('malicious code')() - // By setting __proto__ to null (non-configurable, non-writable, non-enumerable), - // we sever the link to Error.prototype, preventing attackers from climbing to Function - Object.defineProperty(error, '__proto__', { - value: null, - writable: false, - enumerable: false, - configurable: false, - }); - - // SECURITY: Remove the stack property to prevent information leakage - // Attack vector blocked: Vector 270 - Static-Literal Tool Discovery - // The stack trace can reveal internal implementation details like function names, - // file paths, and line numbers which can be used for reconnaissance attacks. - // By setting stack to undefined, we prevent attackers from extracting this information. - Object.defineProperty(error, 'stack', { - value: undefined, - writable: false, - enumerable: false, - configurable: false, - }); - - // Freeze the error to prevent modifications - Object.freeze(error); - - return error; -} - -/** - * Creates a safe TypeError with severed prototype chain - */ -function createSafeTypeError(message: string): Error { - return createSafeError(message, 'TypeError'); -} - /** * Options for safe runtime creation */ @@ -581,7 +475,12 @@ export function createSafeRuntime(context: ExecutionContext, options?: SafeRunti results[index] = result; } catch (error) { // SECURITY: Wrap caught errors in safe error to prevent prototype chain escape - const errMsg = error instanceof Error ? error.message : String(error); + const errMsg = + error && typeof error === 'object' + ? typeof (error as { message?: unknown }).message === 'string' + ? (error as { message: string }).message + : Object.prototype.toString.call(error) + : String(error); errors.push({ index, error: createSafeError(errMsg) as Error,
libs/enclave-vm/src/secure-proxy.ts+12 −6 modified@@ -16,6 +16,7 @@ */ import type { SecureProxyLevelConfig, SecurityLevel } from './types'; +import { createSafeError } from './safe-error'; /** * Categorized blocked properties for defense-in-depth @@ -216,7 +217,7 @@ export function createSafeReflect(securityLevel: SecurityLevel): typeof Reflect ctorTarget === GeneratorFunction || ctorTarget === AsyncGeneratorFunction ) { - throw new Error('Reflect.construct with function constructors is blocked'); + throw createSafeError('Reflect.construct with function constructors is blocked', 'SecurityError'); } return Reflect.construct( ctorTarget as new (...args: unknown[]) => unknown, @@ -499,9 +500,10 @@ export function createSecureProxy<T extends object>(target: T, options: SecurePr // For configurable properties, throw or return undefined if (throwOnBlocked) { - throw new Error( + throw createSafeError( `Security violation: Access to '${propName}' is blocked. ` + `This property can be used for sandbox escape attacks.`, + 'SecurityError', ); } return undefined; @@ -519,9 +521,10 @@ export function createSecureProxy<T extends object>(target: T, options: SecurePr } if (throwOnBlocked) { - throw new Error( + throw createSafeError( `Security violation: Access to '${propName}' is blocked. ` + `This property can be used for sandbox escape attacks.`, + 'SecurityError', ); } return undefined; @@ -562,9 +565,10 @@ export function createSecureProxy<T extends object>(target: T, options: SecurePr options.onBlocked(target, propName); } if (throwOnBlocked) { - throw new Error( + throw createSafeError( `Security violation: Setting '${propName}' is blocked. ` + `This property can be used for sandbox escape attacks.`, + 'SecurityError', ); } return false; // Silently fail @@ -582,9 +586,10 @@ export function createSecureProxy<T extends object>(target: T, options: SecurePr options.onBlocked(target, propName); } if (throwOnBlocked) { - throw new Error( + throw createSafeError( `Security violation: Defining '${propName}' is blocked. ` + `This property can be used for sandbox escape attacks.`, + 'SecurityError', ); } return false; @@ -630,9 +635,10 @@ export function createSecureProxy<T extends object>(target: T, options: SecurePr options.onBlocked(target, propName); } if (throwOnBlocked) { - throw new Error( + throw createSafeError( `Security violation: Access to property descriptor for '${propName}' is blocked. ` + `This property can be used for sandbox escape attacks.`, + 'SecurityError', ); } return undefined;
libs/enclave-vm/src/__tests__/constructor-obfuscation-attacks.spec.ts+57 −40 modified@@ -606,11 +606,16 @@ describe('ATK-COBS: Constructor Obfuscation Attack Vectors (CWE-693)', () => { const enclave = new Enclave({ securityLevel: 'SECURE', validate: false, // Disable AST validation to test runtime protection - globals: { - SECRET: 'FAKE_SECRET_FOR_SECURITY_CHALLENGE', - }, }); + // IMPORTANT: The payload must target a host-only capability to detect a real host escape. + // Using a sandbox global (like `SECRET`) is NOT sufficient, because a true host Function + // constructor escape runs in the host realm where sandbox globals are undefined. + const envKey = 'ENCLAVE_TEST_SECRET_35'; + const secretValue = 'FAKE_SECRET_FOR_SECURITY_CHALLENGE'; + const prevEnv = process.env[envKey]; + process.env[envKey] = secretValue; + const code = ` // ASCII Obfuscation Helper const s = (...args) => String.fromCharCode(...args); @@ -619,7 +624,11 @@ describe('ATK-COBS: Constructor Obfuscation Attack Vectors (CWE-693)', () => { const kCon = s(99,111,110,115,116,114,117,99,116,111,114); // "constructor" const kProto = s(95,95,112,114,111,116,111,95,95); // "__proto__" const kLookup = s(95,95,108,111,111,107,117,112,71,101,116,116,101,114,95,95); // "__lookupGetter__" - const kCode = s(114,101,116,117,114,110,32,83,69,67,82,69,84); // "return SECRET" + // "return process.env.ENCLAVE_TEST_SECRET_35" + const kCode = s( + 114,101,116,117,114,110,32,112,114,111,99,101,115,115,46,101,110,118,46, + 69,78,67,76,65,86,69,95,84,69,83,84,95,83,69,67,82,69,84,95,51,53 + ); const kPtype = s(112,114,111,116,111,116,121,112,101); // "prototype" try { @@ -663,27 +672,33 @@ describe('ATK-COBS: Constructor Obfuscation Attack Vectors (CWE-693)', () => { } `; - const result = await enclave.run(code); - - // The attack should be blocked - it should NOT return the secret - // If the attack succeeds, it returns the secret string - // If blocked, it returns a "blocked:" message, undefined, or throws an error - const secretValue = 'FAKE_SECRET_FOR_SECURITY_CHALLENGE'; - - if (result.success) { - // Attack must NOT have succeeded in retrieving the secret - expect(result.value).not.toBe(secretValue); - // Value should either be undefined (blocked) or contain "blocked" message - if (typeof result.value === 'string') { - expect(result.value).toMatch(/blocked/i); + try { + const result = await enclave.run(code); + + // The attack should be blocked - it should NOT return the secret from host process.env + // If the attack succeeds, it returns the env var string. + // If blocked, it returns a "blocked:" message, undefined, or throws an error. + if (result.success) { + // Attack must NOT have succeeded in retrieving the secret + expect(result.value).not.toBe(secretValue); + // Value should either be undefined (blocked) or contain "blocked" message + if (typeof result.value === 'string') { + expect(result.value).toMatch(/blocked/i); + } + // undefined is also acceptable - means the attack was blocked + } else { + // Execution failed with error - this is also acceptable as long as it's not the secret + expect(result.error?.message).not.toContain(secretValue); } - // undefined is also acceptable - means the attack was blocked - } else { - // Execution failed with error - this is also acceptable as long as it's not the secret - expect(result.error?.message).not.toContain(secretValue); + } finally { + // Always cleanup: restore env and dispose enclave + if (prevEnv === undefined) { + delete process.env[envKey]; + } else { + process.env[envKey] = prevEnv; + } + enclave.dispose(); } - - enclave.dispose(); }); it("ATK-COBS-36: should block Simpleton's Ladder attack via error prototype chain", async () => { @@ -755,27 +770,29 @@ describe('ATK-COBS: Constructor Obfuscation Attack Vectors (CWE-693)', () => { } `; - const result = await enclave.run(code); + try { + const result = await enclave.run(code); - // The attack should be blocked - it should NOT return the secret - // If the attack succeeds, it returns the secret string - // If blocked, it returns a "blocked:" message, undefined/null, or throws an error - const secretValue = 'FAKE_SECRET_FOR_SECURITY_CHALLENGE'; + // The attack should be blocked - it should NOT return the secret + // If the attack succeeds, it returns the secret string + // If blocked, it returns a "blocked:" message, undefined/null, or throws an error + const secretValue = 'FAKE_SECRET_FOR_SECURITY_CHALLENGE'; - if (result.success) { - // Attack must NOT have succeeded in retrieving the secret - expect(result.value).not.toBe(secretValue); - // Value should either be undefined/null (blocked) or contain "blocked" message - if (typeof result.value === 'string') { - expect(result.value).toMatch(/blocked/i); + if (result.success) { + // Attack must NOT have succeeded in retrieving the secret + expect(result.value).not.toBe(secretValue); + // Value should either be undefined/null (blocked) or contain "blocked" message + if (typeof result.value === 'string') { + expect(result.value).toMatch(/blocked/i); + } + // undefined/null is also acceptable - means the attack was blocked + } else { + // Execution failed with error - this is also acceptable as long as it's not the secret + expect(result.error?.message).not.toContain(secretValue); } - // undefined/null is also acceptable - means the attack was blocked - } else { - // Execution failed with error - this is also acceptable as long as it's not the secret - expect(result.error?.message).not.toContain(secretValue); + } finally { + enclave.dispose(); } - - enclave.dispose(); }); it('ATK-COBS-37: should block __proto__ access on safe errors to prevent prototype chain escape', async () => {
libs/enclave-vm/src/__tests__/enclave.spec.ts+2 −1 modified@@ -440,7 +440,8 @@ describe('Enclave', () => { const result = await enclave.run(code); expect(result.success).toBe(false); - expect(result.error?.message).toContain('Tool call failed'); + // The string bridge preserves the original error message from the tool handler + expect(result.error?.message).toContain('Tool execution failed'); enclave.dispose(); });
libs/enclave-vm/src/__tests__/resource-exhaustion.spec.ts+145 −0 modified@@ -269,6 +269,55 @@ describe('ATK-RSRC: Resource Exhaustion Prevention (CWE-400)', () => { enclave.dispose(); }); }); + + describe('Dynamic Array Fill with Memory Limit', () => { + it('should allow dynamic Array.fill when memory limit is set (runtime protection)', async () => { + // With memoryLimit set, allowDynamicArrayFill is automatically enabled + // Runtime patching of Array.prototype.fill provides protection + const enclave = new Enclave({ memoryLimit: 10 * 1024 * 1024 }); // 10MB + const code = ` + const size = 1000; // Dynamic size + const arr = Array(size).fill(0); + return arr.length; + `; + const result = await enclave.run(code); + expect(result.success).toBe(true); + expect(result.value).toBe(1000); + enclave.dispose(); + }); + + it('should block large dynamic Array.fill at runtime', async () => { + // Runtime protection blocks arrays that exceed memory limit + const enclave = new Enclave({ memoryLimit: 1 * 1024 * 1024 }); // 1MB + const code = ` + // Try to create a huge array - will be blocked by runtime patch + const size = 50000000; // 50 million elements × 8 bytes = 400MB + const arr = Array(size).fill(0); + return arr.length; + `; + const result = await enclave.run(code); + expect(result.success).toBe(false); + expect(result.error?.message).toMatch(/memory limit|exceed/i); + enclave.dispose(); + }); + + it('should allow Array.fill with computed size within limits', async () => { + const enclave = new Enclave({ memoryLimit: 10 * 1024 * 1024 }); // 10MB + const code = ` + // Use forEach to iterate and push to array (simulates Vector 1170 pattern) + const size = 500; + const container = []; + Array(size).fill(0).forEach(() => { + container.push('item'); + }); + return container.length; + `; + const result = await enclave.run(code); + expect(result.success).toBe(true); + expect(result.value).toBe(500); + enclave.dispose(); + }); + }); }); // ============================================================================ @@ -1606,4 +1655,100 @@ describe('ATK-RECON-01: Global Reconnaissance Defense', () => { enclave.dispose(); }); }); + + // ============================================================================ + // ATK-RSRC-340: ROPE STRING EXHAUSTION VIA REPEAT + FILL + // Creates structure with many references to same string that explodes when serialized. + // In-VM memory is small (strings shared by reference) but JSON output is huge. + // Defense: estimateSerializedSize() checks return value size before serialization. + // ============================================================================ + describe('ATK-RSRC-340: Rope String Exhaustion (repeat + fill)', () => { + it('should block return values that explode during serialization', async () => { + const enclave = new Enclave({ memoryLimit: 10 * 1024 * 1024 }); // 10MB limit + const code = ` + // Vector 340: The Rope String Exhaustion + // Creates structure with many references to same large string + // In-VM: ~20KB (one string shared by reference) + // Serialized: 500 × 5 × 10KB = 25MB+ + + const s = String.fromCharCode; + + // 1. Create a "Small" base string (~10KB) + const base = s(65).repeat(10000); + + // 2. Build array with 500 references to the same string + const container = []; + Array(500).fill(0).forEach(() => { + container.push(base); + }); + + // 3. Multi-layer return: 5 copies of container = 2500 refs total + const payload = [container, container, container, container, container]; + + return payload; + `; + const result = await enclave.run(code); + + // Should fail because serialization would exceed memory limit + expect(result.success).toBe(false); + expect(result.error?.code).toBe('SERIALIZATION_LIMIT_EXCEEDED'); + expect(result.error?.message).toMatch(/serialization|exceed|limit/i); + enclave.dispose(); + }); + + it('should allow reasonable return values with repeated references', async () => { + const enclave = new Enclave({ memoryLimit: 10 * 1024 * 1024 }); // 10MB limit + const code = ` + // Small repetition that stays within limits + const base = 'x'.repeat(100); // 100 bytes + const container = []; + Array(10).fill(0).forEach(() => { + container.push(base); + }); + // Total serialized: ~10 × 100 = 1KB - well within limit + return container; + `; + const result = await enclave.run(code); + expect(result.success).toBe(true); + expect(result.value).toHaveLength(10); + enclave.dispose(); + }); + + it('should block nested repetition attacks', async () => { + const enclave = new Enclave({ memoryLimit: 5 * 1024 * 1024 }); // 5MB limit + const code = ` + // Nested repetition: array of arrays of strings + const base = 'A'.repeat(1000); // 1KB string + const inner = []; + Array(100).fill(0).forEach(() => inner.push(base)); + const outer = []; + Array(100).fill(0).forEach(() => outer.push(inner)); + // Total: 100 × 100 × 1KB = 10MB (exceeds 5MB limit) + return outer; + `; + const result = await enclave.run(code); + expect(result.success).toBe(false); + expect(result.error?.code).toBe('SERIALIZATION_LIMIT_EXCEEDED'); + enclave.dispose(); + }); + + it('should handle object structures with repeated string values', async () => { + const enclave = new Enclave({ memoryLimit: 5 * 1024 * 1024 }); // 5MB limit + const code = ` + // Object-based repetition attack + const secret = 'S'.repeat(10000); // 10KB string + const entries = {}; + Array(1000).fill(0).forEach((_, i) => { + entries['key' + i] = secret; // 1000 refs to same 10KB string + }); + // Total serialized: 1000 × 10KB = 10MB (clearly exceeds 5MB limit) + return entries; + `; + const result = await enclave.run(code); + // Should fail due to serialization limit + expect(result.success).toBe(false); + expect(result.error?.code).toBe('SERIALIZATION_LIMIT_EXCEEDED'); + enclave.dispose(); + }); + }); });
libs/enclave-vm/src/__tests__/safe-runtime-reference.spec.ts+5 −0 modified@@ -26,6 +26,11 @@ describe('Safe Runtime Reference Integration', () => { maxSanitizeProperties: 500, maxConsoleOutputBytes: 1024 * 1024, maxConsoleCalls: 1000, + toolBridge: { + mode: 'string', + maxPayloadBytes: 5 * 1024 * 1024, + acknowledgeInsecureDirect: false, + }, }, stats: { duration: 0,
libs/enclave-vm/src/__tests__/tool-bridge.spec.ts+78 −0 added@@ -0,0 +1,78 @@ +import { Enclave } from '../enclave'; + +describe('Tool Bridge', () => { + it('should require acknowledgement for toolBridge.mode=direct', () => { + expect(() => new Enclave({ toolBridge: { mode: 'direct' } })).toThrow(/acknowledgeInsecureDirect/i); + }); + + it('should allow toolBridge.mode=direct when acknowledged', async () => { + const enclave = new Enclave({ + toolBridge: { mode: 'direct', acknowledgeInsecureDirect: true }, + toolHandler: async () => ({ ok: true }), + }); + + const result = await enclave.run(` + const out = await callTool('test', {}); + return out.ok; + `); + + expect(result.success).toBe(true); + expect(result.value).toBe(true); + + enclave.dispose(); + }); + + it('should enforce toolBridge.maxPayloadBytes on tool responses', async () => { + const enclave = new Enclave({ + toolBridge: { maxPayloadBytes: 1024 }, + toolHandler: async () => 'a'.repeat(10_000), + }); + + const result = await enclave.run(` + return await callTool('test', {}); + `); + + expect(result.success).toBe(false); + expect(result.error?.message).toMatch(/Tool response exceeds maximum size/i); + // Note: The internal error code TOOL_BRIDGE_RESPONSE_TOO_LARGE is wrapped + // by the double-vm layer, so we verify via message pattern instead + + enclave.dispose(); + }); + + it('should enforce toolBridge.maxPayloadBytes on tool requests', async () => { + const enclave = new Enclave({ + toolBridge: { maxPayloadBytes: 512 }, + toolHandler: async () => 'ok', + }); + + const result = await enclave.run(` + const big = 'x'.repeat(5000); + return await callTool('test', { big }); + `); + + expect(result.success).toBe(false); + expect(result.error?.message).toMatch(/Tool request exceeds maximum size/i); + // Note: The internal error code TOOL_BRIDGE_REQUEST_TOO_LARGE is wrapped + // by the double-vm layer, so we verify via message pattern instead + + enclave.dispose(); + }); + + it('should not expose the host bridge global in single-vm (doubleVm disabled)', async () => { + const enclave = new Enclave({ + doubleVm: { enabled: false }, + validate: false, + toolHandler: async () => 'ok', + }); + + const result = await enclave.run(` + return typeof __host_callToolBridge__; + `); + + expect(result.success).toBe(true); + expect(result.value).toBe('undefined'); + + enclave.dispose(); + }); +});
libs/enclave-vm/src/tool-bridge.ts+427 −0 added@@ -0,0 +1,427 @@ +import type { ExecutionContext } from './types'; +import { sanitizeValue } from './value-sanitizer'; +import { ReferenceResolver, ResolutionLimitError } from './sidecar/reference-resolver'; + +const TOOL_BRIDGE_PROTOCOL_VERSION = 1 as const; + +type ToolBridgeErrorPayload = { + name: string; + message: string; + code?: string; +}; + +type ToolBridgeOkResponse = { + v: typeof TOOL_BRIDGE_PROTOCOL_VERSION; + ok: true; + value?: unknown; +}; + +type ToolBridgeErrorResponse = { + v: typeof TOOL_BRIDGE_PROTOCOL_VERSION; + ok: false; + error: ToolBridgeErrorPayload; +}; + +type ToolBridgeResponse = ToolBridgeOkResponse | ToolBridgeErrorResponse; + +type ToolBridgeRequest = { + v: typeof TOOL_BRIDGE_PROTOCOL_VERSION; + tool: string; + args: Record<string, unknown>; +}; + +function utf8ByteLength(value: string): number { + return Buffer.byteLength(value, 'utf8'); +} + +function jsonReplacer(_key: string, value: unknown): unknown { + if (typeof value === 'bigint') return value.toString(); + if (typeof value === 'number' && !Number.isFinite(value)) return null; + return value; +} + +function safeJsonStringify(value: unknown): string { + return JSON.stringify(value, jsonReplacer); +} + +function truncateForErrorMessage(value: string, maxChars: number): string { + if (value.length <= maxChars) return value; + return `${value.slice(0, maxChars)}…`; +} + +function toErrorPayload( + error: unknown, + fallbackName: string, + fallbackMessage: string, + code?: string, +): ToolBridgeErrorPayload { + const safe: ToolBridgeErrorPayload = { + name: fallbackName, + message: fallbackMessage, + code, + }; + + if (error && typeof error === 'object') { + const maybeName = (error as { name?: unknown }).name; + const maybeMessage = (error as { message?: unknown }).message; + const maybeCode = (error as { code?: unknown }).code; + + if (typeof maybeName === 'string' && maybeName) safe.name = maybeName; + if (typeof maybeMessage === 'string' && maybeMessage) safe.message = maybeMessage; + if (typeof maybeCode === 'string' && maybeCode) safe.code = maybeCode; + } else if (typeof error === 'string' && error) { + safe.message = error; + } + + safe.message = truncateForErrorMessage(String(safe.message || fallbackMessage), 4096); + safe.name = truncateForErrorMessage(String(safe.name || fallbackName), 128); + + return safe; +} + +function serializeResponse(response: ToolBridgeResponse, maxPayloadBytes: number): string { + let json: string; + try { + json = safeJsonStringify(response); + } catch { + json = JSON.stringify({ + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: { + name: 'ToolBridgeError', + message: 'Tool bridge failed to serialize response', + code: 'TOOL_BRIDGE_SERIALIZE_FAILED', + }, + } satisfies ToolBridgeErrorResponse); + } + + if (utf8ByteLength(json) <= maxPayloadBytes) return json; + + // Fail-safe: if even the error payload is too large, return a minimal message. + const minimal: ToolBridgeErrorResponse = { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: { + name: 'ToolBridgeError', + message: 'Tool bridge payload exceeded maximum size', + code: 'TOOL_BRIDGE_PAYLOAD_TOO_LARGE', + }, + }; + return JSON.stringify(minimal); +} + +function parseToolBridgeRequest( + requestJson: string, +): { ok: true; request: ToolBridgeRequest } | { ok: false; error: string } { + let parsed: unknown; + try { + parsed = JSON.parse(requestJson); + } catch { + return { ok: false, error: 'Tool request must be valid JSON' }; + } + + if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) { + return { ok: false, error: 'Tool request must be a JSON object' }; + } + + const v = (parsed as { v?: unknown }).v; + const tool = (parsed as { tool?: unknown }).tool; + const args = (parsed as { args?: unknown }).args; + + if (v !== TOOL_BRIDGE_PROTOCOL_VERSION) { + return { ok: false, error: 'Unsupported tool bridge protocol version' }; + } + + if (typeof tool !== 'string' || !tool) { + return { ok: false, error: 'Tool name must be a non-empty string' }; + } + + if (!args || typeof args !== 'object' || Array.isArray(args)) { + return { ok: false, error: 'Tool arguments must be a JSON object' }; + } + + return { ok: true, request: { v: TOOL_BRIDGE_PROTOCOL_VERSION, tool, args: args as Record<string, unknown> } }; +} + +export function createHostToolBridge( + executionContext: ExecutionContext, + options: { updateStats: boolean }, +): (requestJson: string) => Promise<string> { + const { config, stats, toolHandler, sidecar, referenceConfig } = executionContext; + const maxPayloadBytes = config.toolBridge?.maxPayloadBytes ?? 5 * 1024 * 1024; + + const resolver = sidecar && referenceConfig ? new ReferenceResolver(sidecar, referenceConfig) : undefined; + + return async (requestJson: string): Promise<string> => { + try { + if (typeof requestJson !== 'string') { + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: { + name: 'ToolBridgeError', + message: 'Tool request must be a string', + code: 'TOOL_BRIDGE_BAD_REQUEST', + }, + }, + maxPayloadBytes, + ); + } + + if (utf8ByteLength(requestJson) > maxPayloadBytes) { + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: { + name: 'ToolBridgeError', + message: `Tool request exceeds maximum size (${maxPayloadBytes} bytes)`, + code: 'TOOL_BRIDGE_REQUEST_TOO_LARGE', + }, + }, + maxPayloadBytes, + ); + } + + if (executionContext.aborted) { + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: { name: 'AbortError', message: 'Execution aborted', code: 'EXECUTION_ABORTED' }, + }, + maxPayloadBytes, + ); + } + + if (options.updateStats) { + stats.toolCallCount++; + } + + if (stats.toolCallCount > config.maxToolCalls) { + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: { + name: 'ToolLimitError', + message: `Maximum tool call limit exceeded (${config.maxToolCalls}). This limit prevents runaway script execution.`, + code: 'MAX_TOOL_CALLS_EXCEEDED', + }, + }, + maxPayloadBytes, + ); + } + + if (!toolHandler) { + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: { + name: 'ToolBridgeError', + message: 'No tool handler configured. Cannot execute tool calls.', + code: 'NO_TOOL_HANDLER', + }, + }, + maxPayloadBytes, + ); + } + + const parsed = parseToolBridgeRequest(requestJson); + if (!parsed.ok) { + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: { name: 'ToolBridgeError', message: parsed.error, code: 'TOOL_BRIDGE_BAD_REQUEST' }, + }, + maxPayloadBytes, + ); + } + + const { tool, args } = parsed.request; + + // Sanitize tool args defensively (strip __proto__/constructor, enforce size limits). + const sanitizedArgs = sanitizeValue(args, { + maxDepth: config.maxSanitizeDepth, + maxProperties: config.maxSanitizeProperties, + allowDates: false, + allowErrors: false, + }); + + if (!sanitizedArgs || typeof sanitizedArgs !== 'object' || Array.isArray(sanitizedArgs)) { + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: { name: 'TypeError', message: 'Tool arguments must be an object', code: 'INVALID_TOOL_ARGS' }, + }, + maxPayloadBytes, + ); + } + + // Resolve sidecar references if present + let resolvedArgs = sanitizedArgs as Record<string, unknown>; + if (resolver && resolver.containsReferences(resolvedArgs)) { + if (resolver.wouldExceedLimit(resolvedArgs)) { + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: { + name: 'ToolBridgeError', + message: + 'Arguments would exceed maximum resolved size when references are expanded. Reduce the amount of data passed to the tool.', + code: 'REFERENCE_RESOLUTION_LIMIT', + }, + }, + maxPayloadBytes, + ); + } + + try { + resolvedArgs = resolver.resolve(resolvedArgs) as Record<string, unknown>; + } catch (error: unknown) { + if (error instanceof ResolutionLimitError) { + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: { + name: 'ToolBridgeError', + message: `Failed to resolve references in tool arguments: ${error.message}`, + code: 'REFERENCE_RESOLUTION_FAILED', + }, + }, + maxPayloadBytes, + ); + } + + const safeError = toErrorPayload( + error, + 'ToolBridgeError', + 'Reference resolution failed', + 'REFERENCE_RESOLUTION_FAILED', + ); + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: safeError, + }, + maxPayloadBytes, + ); + } + } + + // Execute tool handler and serialize the result (never throw into the sandbox realm). + let result: unknown; + try { + result = await toolHandler(tool, resolvedArgs); + } catch (error: unknown) { + const safeError = toErrorPayload(error, 'ToolError', `Tool call failed: ${tool}`, 'TOOL_CALL_FAILED'); + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: safeError, + }, + maxPayloadBytes, + ); + } + + // Sanitize tool result to prevent function/symbol/prototype attacks. + let sanitizedResult: unknown; + try { + sanitizedResult = sanitizeValue(result, { + maxDepth: config.maxSanitizeDepth, + maxProperties: config.maxSanitizeProperties, + allowDates: false, + allowErrors: true, + }); + } catch (error: unknown) { + const safeError = toErrorPayload( + error, + 'ToolBridgeError', + 'Tool returned an unsupported value', + 'TOOL_RESULT_NOT_SAFE', + ); + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: safeError, + }, + maxPayloadBytes, + ); + } + + // Lift large string results to sidecar if configured + if (sidecar && referenceConfig && typeof sanitizedResult === 'string') { + const size = utf8ByteLength(sanitizedResult); + if (size >= referenceConfig.extractionThreshold) { + try { + const refId = sidecar.store(sanitizedResult, 'tool-result', { origin: tool }); + sanitizedResult = refId; + } catch { + // If storage fails (limits), keep original value and fall through. + } + } + } + + const okResponse: ToolBridgeOkResponse = + sanitizedResult === undefined + ? { v: TOOL_BRIDGE_PROTOCOL_VERSION, ok: true } + : { v: TOOL_BRIDGE_PROTOCOL_VERSION, ok: true, value: sanitizedResult }; + + let okJson: string; + try { + okJson = safeJsonStringify(okResponse); + } catch (error: unknown) { + const safeError = toErrorPayload( + error, + 'ToolBridgeError', + 'Tool result must be JSON-serializable', + 'TOOL_RESULT_NOT_JSON', + ); + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: safeError, + }, + maxPayloadBytes, + ); + } + + if (utf8ByteLength(okJson) > maxPayloadBytes) { + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: { + name: 'ToolBridgeError', + message: `Tool response exceeds maximum size (${maxPayloadBytes} bytes)`, + code: 'TOOL_BRIDGE_RESPONSE_TOO_LARGE', + }, + }, + maxPayloadBytes, + ); + } + + return okJson; + } catch (error: unknown) { + const safeError = toErrorPayload(error, 'ToolBridgeError', 'Tool bridge failure', 'TOOL_BRIDGE_FAILURE'); + return serializeResponse( + { + v: TOOL_BRIDGE_PROTOCOL_VERSION, + ok: false, + error: safeError, + }, + maxPayloadBytes, + ); + } + }; +}
libs/enclave-vm/src/types.ts+41 −0 modified@@ -442,6 +442,40 @@ export interface ExecutionStats { /** * Configuration for Enclave execution */ +export type ToolBridgeMode = 'string' | 'direct'; + +/** + * Tool bridge configuration between the sandbox and host runtime. + * + * By default, tool calls use a JSON string request/response envelope to avoid + * leaking host-realm objects (Errors, Promises, Functions) into the sandbox. + * + * SECURITY WARNING: `mode: 'direct'` disables the string bridge and passes + * objects directly across realms. This is riskier and requires an explicit + * acknowledgement. + */ +export interface ToolBridgeConfig { + /** + * Bridge mode. + * @default 'string' + */ + mode?: ToolBridgeMode; + + /** + * Maximum size (in bytes) of a tool request or response payload. + * @default 5 * 1024 * 1024 (5MB) + */ + maxPayloadBytes?: number; + + /** + * Required acknowledgement to enable insecure direct bridging. + * + * When `mode` is set to `'direct'`, enclave construction throws unless this + * field is explicitly set to `true`. + */ + acknowledgeInsecureDirect?: boolean; +} + export interface EnclaveConfig { /** * Maximum execution time in milliseconds @@ -497,6 +531,13 @@ export interface EnclaveConfig { */ toolHandler?: ToolHandler; + /** + * Tool bridge configuration between the sandbox and host. + * + * Default: `{ mode: 'string', maxPayloadBytes: 5MB }` + */ + toolBridge?: ToolBridgeConfig; + /** * Maximum total console output in bytes * Default: Determined by securityLevel (1MB for STANDARD)
libs/enclave-vm/src/value-sanitizer.ts+242 −1 modified@@ -121,10 +121,15 @@ export function sanitizeValue( const type = typeof value; // Primitives are safe - if (type === 'string' || type === 'number' || type === 'boolean' || type === 'bigint') { + if (type === 'string' || type === 'number' || type === 'boolean') { return value; } + // BigInt: Convert to string for JSON-safety (JSON.stringify throws on raw BigInt) + if (type === 'bigint') { + return String(value); + } + // Functions are NOT safe - prevent code injection if (type === 'function') { throw new Error( @@ -276,3 +281,239 @@ export function sanitizeValueOrFallback<T>(value: unknown, fallback: T, options: return fallback; } } + +/** + * Estimate the serialized (JSON) size of a string in bytes. + * Accounts for quotes and proper JSON escaping. + * + * @param str The string to estimate + * @returns Size in bytes when serialized as JSON string + */ +function estimateStringSize(str: string): number { + let bytes = 2; // quotes + for (let i = 0; i < str.length; i++) { + const code = str.charCodeAt(i); + if (code < 128) { + // ASCII: check if needs escaping + if (code === 34 || code === 92) { + // Quote and backslash: 2-byte escape (\" or \\) + bytes += 2; + } else if (code < 32) { + // Control characters: 6-byte \uXXXX escape + bytes += 6; + } else { + bytes += 1; + } + } else if (code < 2048) { + bytes += 2; // 2-byte UTF-8 + } else if (code >= 0xd800 && code <= 0xdbff && i + 1 < str.length) { + // High surrogate - check for low surrogate pair (emoji, supplementary chars) + const nextCode = str.charCodeAt(i + 1); + if (nextCode >= 0xdc00 && nextCode <= 0xdfff) { + bytes += 4; // 4-byte UTF-8 for surrogate pair + i++; // Skip the low surrogate + } else { + bytes += 3; // Unpaired high surrogate (3-byte UTF-8) + } + } else { + bytes += 3; // 3-byte UTF-8 (BMP characters U+0800 to U+FFFF) + } + } + return bytes; +} + +/** + * Estimates the serialized (JSON) size of a value in bytes. + * + * Security feature: Prevents memory exhaustion attacks (Vector 340) where: + * - Attacker creates a structure with many references to the same large string + * - In-VM memory is small (strings are shared by reference) + * - But JSON serialization expands each reference to full string copy + * - Example: 500 refs × 5 copies × 10KB string = 25MB serialized from ~20KB in-memory + * + * This function counts EVERY string occurrence, not unique strings, + * to accurately estimate the serialized output size. + * + * IMPORTANT: Circular reference handling: + * - Uses a "current path" Set to detect circular references (ancestor chain) + * - Repeated (non-circular) references to the same object are counted fully each time + * - This matches JSON.stringify behavior where repeated refs are serialized multiple times + * + * @param value The value to estimate + * @param maxBytes Maximum allowed serialized bytes (0 = no limit) + * @param depth Current recursion depth (internal) + * @param maxDepth Maximum recursion depth (default 2000 to handle deep structures) + * @param currentPath Set tracking the current ancestor path (for circular detection only) + * @returns Estimated serialized size in bytes + * @throws Error if estimated size exceeds maxBytes or depth limit + */ +export function estimateSerializedSize( + value: unknown, + maxBytes = 0, + depth = 0, + maxDepth = 2000, + currentPath: Set<object> = new Set(), +): number { + // Depth limit to prevent stack overflow - reject deeply nested structures + if (depth > maxDepth) { + throw new Error( + `Structure exceeds maximum nesting depth (${maxDepth}). ` + + `Deeply nested structures are rejected to prevent stack overflow attacks.`, + ); + } + + // Handle null/undefined + if (value === null) return 4; // "null" + if (value === undefined) return 0; // undefined is omitted in JSON + + const type = typeof value; + + // String: count actual bytes (each occurrence, not unique!) + if (type === 'string') { + const bytes = estimateStringSize(value as string); + if (maxBytes > 0 && bytes > maxBytes) { + throw new Error(`String serialization would exceed limit: ${bytes} > ${maxBytes} bytes`); + } + return bytes; + } + + // Number: estimate digit count + if (type === 'number') { + const num = value as number; + if (!Number.isFinite(num)) return 4; // "null" for Infinity/NaN + return String(num).length; + } + + // Boolean + if (type === 'boolean') { + return value ? 4 : 5; // "true" or "false" + } + + // BigInt: Cannot be JSON.stringify'd directly, but estimate as string for size purposes + // (caller is responsible for actual serialization, e.g., converting to string first) + if (type === 'bigint') { + return String(value).length + 2; // Estimate as quoted string + } + + // Function/Symbol: shouldn't be serialized + if (type === 'function' || type === 'symbol') { + return 0; // omitted in JSON + } + + // Arrays + if (Array.isArray(value)) { + // Check for circular reference (is this object an ancestor of itself?) + if (currentPath.has(value)) { + // Circular reference detected - JSON.stringify would throw, but we estimate small + return 4; + } + + // Add to current path for descendant checks + currentPath.add(value); + + let size = 2; // brackets [] + for (let i = 0; i < value.length; i++) { + if (i > 0) size += 1; // comma + let elementSize = estimateSerializedSize(value[i], maxBytes, depth + 1, maxDepth, currentPath); + // JSON.stringify turns undefined/function/symbol array elements into null (4 bytes) + if (elementSize === 0) elementSize = 4; + size += elementSize; + + // Check limit incrementally to fail fast + if (maxBytes > 0 && size > maxBytes) { + currentPath.delete(value); // Clean up before throwing + throw new Error( + `Array serialization would exceed limit: estimated ${size}+ > ${maxBytes} bytes. ` + + `This often indicates repeated references to large strings that expand during JSON serialization.`, + ); + } + } + + // Remove from current path (we're done with this branch) + currentPath.delete(value); + return size; + } + + // Objects + if (type === 'object' && value !== null) { + // Check for circular reference (is this object an ancestor of itself?) + if (currentPath.has(value as object)) { + // Circular reference detected + return 4; + } + + // Add to current path for descendant checks + currentPath.add(value as object); + + // Handle special objects + if (value instanceof Date) { + currentPath.delete(value as object); + return 26; // ISO date string with quotes: "2024-01-01T00:00:00.000Z" + } + if (value instanceof RegExp) { + // RegExp serializes to "{}" in JSON.stringify + currentPath.delete(value as object); + return 2; // "{}" + } + + let size = 2; // braces {} + const keys = Object.keys(value as Record<string, unknown>); + let first = true; + + for (const key of keys) { + // Skip dangerous keys (they're stripped) + if (key === '__proto__' || key === 'constructor') continue; + + if (!first) size += 1; // comma + first = false; + + // Key: quoted string + colon (accounting for JSON escaping) + size += estimateStringSize(key) + 1; // "key": + + // Value + let propValue: unknown; + try { + propValue = (value as Record<string, unknown>)[key]; + } catch { + continue; // Skip throwing properties + } + + const valueSize = estimateSerializedSize(propValue, maxBytes, depth + 1, maxDepth, currentPath); + size += valueSize; + + // Check limit incrementally to fail fast + if (maxBytes > 0 && size > maxBytes) { + currentPath.delete(value as object); // Clean up before throwing + throw new Error( + `Object serialization would exceed limit: estimated ${size}+ > ${maxBytes} bytes. ` + + `This often indicates repeated references to large strings that expand during JSON serialization.`, + ); + } + } + + // Remove from current path (we're done with this branch) + currentPath.delete(value as object); + return size; + } + + return 0; +} + +/** + * Check if a value's serialized size is within limits + * + * @param value Value to check + * @param maxBytes Maximum allowed serialized bytes + * @returns Object with ok status and estimated size or error message + */ +export function checkSerializedSize( + value: unknown, + maxBytes: number, +): { ok: true; estimatedBytes: number } | { ok: false; error: string } { + try { + const estimatedBytes = estimateSerializedSize(value, maxBytes); + return { ok: true, estimatedBytes }; + } catch (err) { + return { ok: false, error: err instanceof Error ? err.message : 'Unknown error' }; + } +}
README.md+37 −147 modified@@ -1,175 +1,65 @@ +<br/> <div align="center"> +<img src="./assets/logo.dark.svg" alt="Enclave" width="80"> # Enclave -**Secure JavaScript execution and vector search libraries for AI agents** - -[](https://nodejs.org) -[](./LICENSE) - - +## Secure sandbox runtime for AI agents +[](https://www.npmjs.com/package/ast-guard) +[](https://www.npmjs.com/package/vectoriadb) +[](https://www.npmjs.com/package/enclave-vm) +<br> +[](https://nodejs.org) +[](./LICENSE) +[Documentation](https://agentfront.dev/docs/guides/enclave) | [Live Demo](https://enclave.agentfront.dev) | [FrontMCP Framework](https://github.com/agentfront/enclave) </div> --- -## Overview - -Enclave is a monorepo containing security-focused libraries for building safe AI agent systems: - -| Library | Description | Version | -| ----------------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| [**ast-guard**](./libs/ast-guard) | AST-based JavaScript validator with 100% CVE protection | [](https://www.npmjs.com/package/ast-guard) | -| [**vectoriadb**](./libs/vectoriadb) | Lightweight in-memory vector database for semantic search | [](https://www.npmjs.com/package/vectoriadb) | -| [**enclave-vm**](libs/enclave-vm) | Secure AgentScript execution environment with defense-in-depth | [](https://www.npmjs.com/package/enclave-vm) | - ---- - -## Libraries - -### ast-guard - -Production-ready AST security guard for JavaScript validation and code safety. Blocks all known vm2/isolated-vm/node-vm CVE exploits. - -```typescript -import { validate, PRESETS } from 'ast-guard'; - -const result = validate('const x = 1 + 2;', PRESETS.SECURE); -console.log(result.valid); // true -``` - -**Features:** - -- 100% CVE coverage for vm2, isolated-vm, and node-vm exploits -- 613+ security tests with 95%+ code coverage -- Four security presets: STRICT, SECURE, STANDARD, PERMISSIVE -- Zero runtime dependencies (only acorn for parsing) - -[Read more →](./libs/ast-guard) +## Install ---- - -### vectoriadb - -Lightweight, production-ready in-memory vector database for semantic search with HNSW indexing. - -```typescript -import { VectoriaDB } from 'vectoriadb'; - -const db = new VectoriaDB(); -await db.initialize(); - -await db.insert({ id: '1', text: 'Hello world', metadata: {} }); -const results = await db.search('greeting', 5); +```bash +npm install enclave-vm # Secure JS execution +npm install ast-guard # AST validation +npm install vectoriadb # Vector search ``` -**Features:** +## Why Enclave? -- HNSW (Hierarchical Navigable Small World) indexing -- Multiple embedding options (TF-IDF, transformer-based) -- Persistence adapters (File, Redis) -- Built-in security validation +- **Extensive security testing** - See [security audit](./libs/enclave-vm/SECURITY-AUDIT.md) for details +- **Defense in depth** - 6 security layers for LLM-generated code +- **Zero-config** - Works out of the box with sensible defaults +- **TypeScript-first** - Full type safety and excellent DX -[Read more →](./libs/vectoriadb) - ---- - -### enclave-vm - -Secure AgentScript execution environment with defense-in-depth architecture for running LLM-generated JavaScript safely. +## Quick Start ```typescript import { Enclave } from 'enclave-vm'; -const enclave = new Enclave({ securityLevel: 'SECURE' }); - -const result = await enclave.execute(` - const sum = 1 + 2; - return sum; +const enclave = new Enclave({ + securityLevel: 'SECURE', + toolHandler: async (name, args) => { + if (name === 'getUser') return { id: args.id, name: 'Alice' }; + throw new Error(`Unknown tool: ${name}`); + }, +}); + +const result = await enclave.run(` + const user = await callTool('getUser', { id: 123 }); + return { greeting: 'Hello, ' + user.name }; `); -console.log(result.value); // 3 -``` - -**Features:** -- Bank-grade security: 1184+ security tests, 150+ blocked attack vectors -- Defense-in-depth: 6 security layers -- Worker Pool Adapter for OS-level isolation -- AI Scoring Gate with extensible custom analyzers -- Reference Sidecar for large data handling +if (result.success) { + console.log(result.value); // { greeting: 'Hello, Alice' } +} -[Read more →](libs/enclave-vm) - ---- - -## Getting Started - -### Prerequisites - -- **Node.js**: >= 22.0.0 -- **Package Manager**: yarn - -### Installation - -```bash -# Clone the repository -git clone https://github.com/agentfront/enclave.git -cd enclave - -# Install dependencies -yarn install - -# Build all libraries -yarn build - -# Run tests -yarn test -``` - -### Using Individual Libraries - -```bash -# ast-guard -npm install ast-guard - -# vectoriadb -npm install vectoriadb - -# enclave -npm install enclave-vm +enclave.dispose(); ``` ---- - -## Development - -```bash -# Build all projects -nx run-many -t build - -# Test all projects -nx run-many -t test - -# Lint all projects -nx run-many -t lint - -# Build specific project -nx build ast-guard -nx build vectoriadb -nx build enclave -``` - ---- - -## Contributing - -PRs welcome! Please: - -1. Keep changes focused -2. Add/adjust tests for your changes -3. Run `yarn build && yarn test && yarn lint` before submitting +**[Read the full documentation →](https://agentfront.dev/docs/guides/enclave)** ---
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-7qm7-455j-5p63ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-22686ghsaADVISORY
- github.com/agentfront/enclave/commit/ed8bc438b2cd6e6f0b5f2de321e5be6f0169b5a1ghsax_refsource_MISCWEB
- github.com/agentfront/enclave/security/advisories/GHSA-7qm7-455j-5p63ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.