Skip to content

feat(local-dev): native folder picker and simplified project creation#2691

Open
vibegui wants to merge 2 commits intomainfrom
vibegui/from-folder
Open

feat(local-dev): native folder picker and simplified project creation#2691
vibegui wants to merge 2 commits intomainfrom
vibegui/from-folder

Conversation

@vibegui
Copy link
Contributor

@vibegui vibegui commented Mar 12, 2026

What is this contribution about?

Simplifies local folder project creation from complex daemon discovery to a single-click native folder picker. Users can now:

  • Click "Add Project > From Folder" in the Projects UI
  • Select a directory via native macOS Finder
  • Automatically validates folder, derives project name/slug, and creates everything needed (project, connection, plugins, virtual MCP)

Includes a complete Git Panel for branch management, commit history, diffs, and AI-powered commit messages. File watching via SSE, file serving for preview/storage plugins, and bash tool integration.

Screenshots/Demonstration

  • Native folder picker dialog (osascript on macOS)
  • Git panel with branch switcher, commit log, diff viewer, AI commit generation
  • File watch notifications (SSE) for reactive UI updates
  • Object storage file viewer in sidebar
  • Preview plugin iframe in sidebar

How to Test

  1. Start local mode: MESH_LOCAL_MODE=true bun run dev
  2. Navigate to Projects → "Create new project"
  3. Select "From Folder" card
  4. Click dashed card → macOS Finder opens
  5. Select a local directory (e.g., ~/my-project)
  6. Name/slug auto-fills from folder name
  7. Click "Create Project"
  8. Project loads with git panel, file browser, preview plugin
  9. Edit files → git panel shows changes (SSE watch)
  10. Switch branches, commit, view history
  11. Preview plugin shows dev server iframe (if configured)

Migration Notes

  • No database migrations needed (uses existing connection/project schema)
  • Local connections stored with metadata: { sourceProvider: "local-filesystem", localDevRoot: "/path" }
  • Empty connection_url: "" (not null) for local connections
  • Presigned URLs routed to /api/local-dev/files/:connectionId/*

Review Checklist

  • PR title is clear and descriptive
  • Changes are tested and working (0 TS errors, 0 lint errors)
  • No breaking changes to existing APIs
  • Git panel gracefully degrades if AI provider keys unavailable
  • SSE watch filters node_modules and .git directories
  • Path traversal protected in file serving via LocalFileStorage

🤖 Generated with Claude Code


Summary by cubic

Adds a one-click “From Folder” flow with a native macOS folder picker that auto-creates the project, connection, and in‑process local MCP for fast, zero‑config dev. Also ships a Git panel, live SSE file watching, secure local file serving, and an improved preview and file viewer experience.

  • New Features

    • “Add Project > From Folder” opens a native Finder dialog (osascript) and creates everything in one step (project, local filesystem connection, plugin binds, virtual MCP), then links the connection in project settings.
    • Local‑dev API: /api/local-dev/pick-folder, /validate-folder, /create-project, SSE watch at /api/local-dev/watch/:connectionId, and presigned file serving at /api/local-dev/files/:connectionId/* with path traversal protection.
    • In‑process local MCP (@decocms/local-dev): filesystem + object storage tools and bash, SSE file watch, secure path resolution; clients are created in‑process for zero overhead.
    • Git Panel: branch switch, history, diffs, and commit message generation; live updates via SSE; works without provider keys; topbar “Save” shows change count and toggles the panel.
    • Preview: rewritten to the MCP ext‑apps protocol (JSON‑RPC handshake), detects static sites (index.html), dev servers, or manual config; starts/stops via bash; preserves _meta so the UI can discover the preview resource URI.
    • Object Storage: new viewer route (/viewer?key=...) with Markdown preview and image rendering; file browser row click opens the viewer.
    • Public config exposes localMode to show local‑only UI like “From Folder”; login supports a localhost‑only redirectTo for CLI auth.
    • Security: dev asset signatures use timing‑safe comparison; assets default to DECOCMS_HOME/assets in local mode; project deletion cleans up unused localhost connections and virtual MCPs.
  • Dependencies

    • Adds @decocms/local-dev; includes mesh-plugin-preview.

Written for commit e825a5b. Summary will update on new commits.

@github-actions
Copy link
Contributor

🧪 Benchmark

Should we run the Virtual MCP strategy benchmark for this PR?

React with 👍 to run the benchmark.

Reaction Action
👍 Run quick benchmark (10 & 128 tools)

Benchmark will run on the next push after you react.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 12, 2026

Release Options

Should a new version be published when this PR is merged?

React with an emoji to vote on the release type:

Reaction Type Next Version
👍 Prerelease 2.165.2-alpha.1
🎉 Patch 2.165.2
❤️ Minor 2.166.0
🚀 Major 3.0.0

Current version: 2.165.1

Deployment

  • Deploy to production (triggers ArgoCD sync after Docker image is published)

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 issues found across 671 files

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/docs/client/src/content/2025-10-10/pt-br/no-code-guides/creating-agents.mdx">

<violation number="1" location="apps/docs/client/src/content/2025-10-10/pt-br/no-code-guides/creating-agents.mdx:93">
P2: The PT-BR refund limit guidance is inconsistent with the English source (R$500 vs $100), which can lead to different agent behavior depending on locale.

(Based on your team's feedback about treating English documentation as the authoritative source.) [FEEDBACK_USED]</violation>
</file>

<file name=".github/workflows/release.yaml">

<violation number="1" location=".github/workflows/release.yaml:70">
P1: The npm version check queries the wrong package (`decocms` instead of `@decocms/mesh`), which can incorrectly force publish attempts for already-released versions.</violation>
</file>

<file name=".cursor/skills/respond-to-pr-review/SKILL.md">

<violation number="1" location=".cursor/skills/respond-to-pr-review/SKILL.md:52">
P2: Filter replies by the authenticated user before considering a comment addressed; otherwise replies from other users incorrectly suppress unaddressed review items.

(Based on your team's feedback about only treating comments as addressed when the current authenticated user replied.) [FEEDBACK_USED]</violation>
</file>

<file name="apps/docs/astro.config.mjs">

<violation number="1" location="apps/docs/astro.config.mjs:16">
P2: `publicDir` is relative to the configured `root`. With `root: "client"`, setting `publicDir: "client/public"` resolves to `client/client/public` and will break static asset resolution. Use `public` (relative to root) or an absolute URL if you meant the repo root.</violation>
</file>

<file name="apps/docs/client/src/content/2026-03-10/pt-br/mcp-mesh/self-hosting/deploy/kubernetes.mdx">

<violation number="1" location="apps/docs/client/src/content/2026-03-10/pt-br/mcp-mesh/self-hosting/deploy/kubernetes.mdx:37">
P2: The architecture image URL is broken due to an invalid GitHub owner path (`deco CMS` with a space).</violation>
</file>

<file name="apps/docs/client/src/layouts/DocsLayout.astro">

<violation number="1" location="apps/docs/client/src/layouts/DocsLayout.astro:22">
P2: Avoid hardcoding the fallback docs version; use a non-date alias/source of truth so this doesn’t drift on future version bumps.</violation>
</file>

<file name=".claude/settings.json">

<violation number="1" location=".claude/settings.json:8">
P1: Validate `name` before building `DIR`/`BRANCH`; currently path segments like `../` can escape the intended `.claude/worktrees` directory.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

# Check if this specific version already exists in npm
# This works for both stable and prerelease versions (unlike checking 'latest' tag)
if npm view @decocms/mesh@$CURRENT_VERSION version >/dev/null 2>&1; then
if npm view decocms@$CURRENT_VERSION version >/dev/null 2>&1; then
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The npm version check queries the wrong package (decocms instead of @decocms/mesh), which can incorrectly force publish attempts for already-released versions.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/release.yaml, line 70:

<comment>The npm version check queries the wrong package (`decocms` instead of `@decocms/mesh`), which can incorrectly force publish attempts for already-released versions.</comment>

<file context>
@@ -66,7 +67,7 @@ jobs:
           # Check if this specific version already exists in npm
           # This works for both stable and prerelease versions (unlike checking 'latest' tag)
-          if npm view @decocms/mesh@$CURRENT_VERSION version >/dev/null 2>&1; then
+          if npm view decocms@$CURRENT_VERSION version >/dev/null 2>&1; then
             echo "version-changed=false" >> $GITHUB_OUTPUT
             echo "⏭️ Version $CURRENT_VERSION already published, skipping publish"
</file context>
Suggested change
if npm view decocms@$CURRENT_VERSION version >/dev/null 2>&1; then
if npm view @decocms/mesh@$CURRENT_VERSION version >/dev/null 2>&1; then
Fix with Cubic

"hooks": [
{
"type": "command",
"command": "sh -c 'INPUT=$(cat); NAME=$(echo \"$INPUT\" | jq -r .name); CWD=$(echo \"$INPUT\" | jq -r .cwd); DIR=\"$CWD/.claude/worktrees/$NAME\"; BRANCH=\"claude/$NAME\"; git -C \"$CWD\" worktree add -b \"$BRANCH\" \"$DIR\" HEAD >&2; cp \"$CWD/apps/mesh/.env\" \"$DIR/apps/mesh/.env\" 2>/dev/null >&2 || true; echo \"$DIR\"'"
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Validate name before building DIR/BRANCH; currently path segments like ../ can escape the intended .claude/worktrees directory.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .claude/settings.json, line 8:

<comment>Validate `name` before building `DIR`/`BRANCH`; currently path segments like `../` can escape the intended `.claude/worktrees` directory.</comment>

<file context>
@@ -0,0 +1,14 @@
+        "hooks": [
+          {
+            "type": "command",
+            "command": "sh -c 'INPUT=$(cat); NAME=$(echo \"$INPUT\" | jq -r .name); CWD=$(echo \"$INPUT\" | jq -r .cwd); DIR=\"$CWD/.claude/worktrees/$NAME\"; BRANCH=\"claude/$NAME\"; git -C \"$CWD\" worktree add -b \"$BRANCH\" \"$DIR\" HEAD >&2; cp \"$CWD/apps/mesh/.env\" \"$DIR/apps/mesh/.env\" 2>/dev/null >&2 || true; echo \"$DIR\"'"
+          }
+        ]
</file context>
Fix with Cubic

Você pode processar reembolsos de até R$500. Para valores maiores, abra um ticket
e informe o cliente que um gerente fará a revisão.
Você pode:
- Processar reembolsos abaixo de R$500 usando @PROCESS_REFUND
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The PT-BR refund limit guidance is inconsistent with the English source (R$500 vs $100), which can lead to different agent behavior depending on locale.

(Based on your team's feedback about treating English documentation as the authoritative source.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/client/src/content/2025-10-10/pt-br/no-code-guides/creating-agents.mdx, line 93:

<comment>The PT-BR refund limit guidance is inconsistent with the English source (R$500 vs $100), which can lead to different agent behavior depending on locale.

(Based on your team's feedback about treating English documentation as the authoritative source.) </comment>

<file context>
@@ -79,89 +73,114 @@ O system prompt define as características do seu agent:
-Você pode processar reembolsos de até R$500. Para valores maiores, abra um ticket 
-e informe o cliente que um gerente fará a revisão.
+Você pode:
+- Processar reembolsos abaixo de R$500 usando @PROCESS_REFUND
+- Redefinir senhas usando @RESET_PASSWORD
+- Consultar status da conta usando @GET_ACCOUNT_STATUS
</file context>
Fix with Cubic

# Get IDs that have been replied to
REPLIED_IDS=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/comments" \
--paginate \
--jq '[.[] | select(.in_reply_to_id != null) | .in_reply_to_id] | unique')
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Filter replies by the authenticated user before considering a comment addressed; otherwise replies from other users incorrectly suppress unaddressed review items.

(Based on your team's feedback about only treating comments as addressed when the current authenticated user replied.)

View Feedback

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .cursor/skills/respond-to-pr-review/SKILL.md, line 52:

<comment>Filter replies by the authenticated user before considering a comment addressed; otherwise replies from other users incorrectly suppress unaddressed review items.

(Based on your team's feedback about only treating comments as addressed when the current authenticated user replied.) </comment>

<file context>
@@ -0,0 +1,122 @@
+# Get IDs that have been replied to
+REPLIED_IDS=$(gh api "repos/${REPO}/pulls/${PR_NUMBER}/comments" \
+  --paginate \
+  --jq '[.[] | select(.in_reply_to_id != null) | .in_reply_to_id] | unique')
+```
+
</file context>
Fix with Cubic

"/": "/latest/en/introduction",
},
outDir: "dist/client/",
publicDir: "client/public",
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: publicDir is relative to the configured root. With root: "client", setting publicDir: "client/public" resolves to client/client/public and will break static asset resolution. Use public (relative to root) or an absolute URL if you meant the repo root.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/astro.config.mjs, line 16:

<comment>`publicDir` is relative to the configured `root`. With `root: "client"`, setting `publicDir: "client/public"` resolves to `client/client/public` and will break static asset resolution. Use `public` (relative to root) or an absolute URL if you meant the repo root.</comment>

<file context>
@@ -1,52 +1,21 @@
-    "/": "/latest/en/introduction",
-  },
   outDir: "dist/client/",
+  publicDir: "client/public",
   srcDir: "client/src",
-  integrations: [mdx(), react(), patchCsrRedirect()],
</file context>
Suggested change
publicDir: "client/public",
publicDir: "public",
Fix with Cubic

### Arquitetura

![Infraestrutura](https://raw.githubusercontent.com/decocms/helm-chart-deco-mcp-mesh/main/img/mcp-mesh-infra-arch.jpg)
![Infraestrutura](https://raw.githubusercontent.com/deco CMS/helm-chart-deco-mcp-mesh/main/img/mcp-mesh-infra-arch.jpg)
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The architecture image URL is broken due to an invalid GitHub owner path (deco CMS with a space).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/client/src/content/2026-03-10/pt-br/mcp-mesh/self-hosting/deploy/kubernetes.mdx, line 37:

<comment>The architecture image URL is broken due to an invalid GitHub owner path (`deco CMS` with a space).</comment>

<file context>
@@ -1,103 +1,103 @@
 ### Arquitetura
 
-![Infraestrutura](https://raw.githubusercontent.com/decocms/helm-chart-deco-mcp-mesh/main/img/mcp-mesh-infra-arch.jpg)
+![Infraestrutura](https://raw.githubusercontent.com/deco CMS/helm-chart-deco-mcp-mesh/main/img/mcp-mesh-infra-arch.jpg)
 
 ## Pré-requisitos
</file context>
Suggested change
![Infraestrutura](https://raw.githubusercontent.com/deco CMS/helm-chart-deco-mcp-mesh/main/img/mcp-mesh-infra-arch.jpg)
![Infraestrutura](https://raw.githubusercontent.com/decocms/helm-chart-deco-mcp-mesh/main/img/mcp-mesh-infra-arch.jpg)
Fix with Cubic

@vibegui vibegui force-pushed the vibegui/from-folder branch from dcc6353 to 1026dcf Compare March 12, 2026 19:50
@vibegui vibegui changed the base branch from feat/local-dev-daemon to main March 12, 2026 19:50
@vibegui vibegui force-pushed the vibegui/from-folder branch 4 times, most recently from 41a1cfe to 421ad07 Compare March 12, 2026 22:12
… flow

Simplify the local folder project creation with native macOS folder picker (osascript) instead of custom directory browser. Add git panel with branch management and commit history, file watching via SSE, and integration with object-storage and preview plugins. Local connections now use empty connection_url instead of null to satisfy DB constraints.

- Add /api/local-dev/pick-folder endpoint (native Finder dialog via osascript)
- Simplify FolderStep UI to single dashed card for folder selection
- Add GitPanel component with branch switching, commit history, and diff viewing
- Add file watch support via SSE (useConnectionWatch hook)
- Add git operation helpers (gitStatus, gitCommit, gitBranch, gitLog)
- Fix MeshContext middleware ordering (local-dev routes mounted after context injection)
- Adapt git-panel AI features to new AI provider system (useAiProviderKeyList)
- Add query keys for git operations (KEYS.gitBranch, gitStatus, etc.)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@vibegui vibegui force-pushed the vibegui/from-folder branch from 421ad07 to 7476141 Compare March 12, 2026 22:15
…d MCP App preview

- From Folder click directly opens OS picker and auto-creates project (no modal)
- Add connection to project via projectConnections.add() so it appears in settings
- Preserve _meta field when saving tools so preview UI resource URI is discoverable
- Rewrite preview tool with proper MCP ext-apps protocol (JSON-RPC handshake)
- Server-side preview detection: static sites (index.html), dev servers, manual config
- Remove FolderStep dialog component (replaced by inline pickFolderAndCreate flow)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 4 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/mesh/src/web/routes/projects-list.tsx">

<violation number="1" location="apps/mesh/src/web/routes/projects-list.tsx:52">
P2: Show the picker error before returning when `/pick-folder` fails without a path.</violation>

<violation number="2" location="apps/mesh/src/web/routes/projects-list.tsx:173">
P2: Selecting "Blank Project" from the empty-state cards now reopens the selection step instead of the blank-project form.</violation>
</file>

<file name="apps/mesh/src/web/components/create-project-dialog.tsx">

<violation number="1" location="apps/mesh/src/web/components/create-project-dialog.tsx:90">
P1: `From Folder` becomes a no-op in local mode when this dialog is opened from callers that do not pass `onPickFolder`.</violation>
</file>

<file name="apps/mesh/src/mcp-clients/local/index.ts">

<violation number="1" location="apps/mesh/src/mcp-clients/local/index.ts:394">
P2: Script injection via unescaped `JSON.stringify` inside `<script>` tag. `JSON.stringify` doesn't escape `</` sequences, so a crafted `rootPath` or `detection.command` (from `.deco/preview.json`) can close the script tag and inject arbitrary HTML/JS. Use `.replace(/</g, '\\u003c')` after stringifying to neutralize the sequence.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

};

const handleSelectFolder = () => {
if (onPickFolder) {
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: From Folder becomes a no-op in local mode when this dialog is opened from callers that do not pass onPickFolder.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/components/create-project-dialog.tsx, line 90:

<comment>`From Folder` becomes a no-op in local mode when this dialog is opened from callers that do not pass `onPickFolder`.</comment>

<file context>
@@ -109,22 +86,22 @@ export function CreateProjectDialog({
   };
 
+  const handleSelectFolder = () => {
+    if (onPickFolder) {
+      handleOpenChange(false);
+      onPickFolder();
</file context>
Fix with Cubic

});
const pick: { path?: string; cancelled?: boolean; error?: string } =
await pickRes.json();
if (!pick.path) return;
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Show the picker error before returning when /pick-folder fails without a path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/routes/projects-list.tsx, line 52:

<comment>Show the picker error before returning when `/pick-folder` fails without a path.</comment>

<file context>
@@ -27,16 +28,109 @@ export default function ProjectsListPage() {
+      });
+      const pick: { path?: string; cancelled?: boolean; error?: string } =
+        await pickRes.json();
+      if (!pick.path) return;
+
+      // 2. Validate folder (gets name/slug, checks for existing project)
</file context>
Suggested change
if (!pick.path) return;
if (!pick.path) {
if (pick.error) {
toast.error(pick.error);
}
return;
}
Fix with Cubic

</div>
<ModeSelectionCards
onSelectFolder={pickFolderAndCreate}
onSelectBlank={() => setCreateDialogOpen(true)}
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Selecting "Blank Project" from the empty-state cards now reopens the selection step instead of the blank-project form.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/web/routes/projects-list.tsx, line 173:

<comment>Selecting "Blank Project" from the empty-state cards now reopens the selection step instead of the blank-project form.</comment>

<file context>
@@ -75,24 +169,16 @@ export default function ProjectsListPage() {
-                  setCreateDialogOpen(true);
-                }}
+                onSelectFolder={pickFolderAndCreate}
+                onSelectBlank={() => setCreateDialogOpen(true)}
+                folderPicking={folderPicking}
               />
</file context>
Fix with Cubic

Comment on lines +394 to +396
var ROOT = ${JSON.stringify(rootPath)};
var BASE_FILE_URL = ${JSON.stringify(baseFileUrl)};
var detection = ${JSON.stringify(detection)};
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Script injection via unescaped JSON.stringify inside <script> tag. JSON.stringify doesn't escape </ sequences, so a crafted rootPath or detection.command (from .deco/preview.json) can close the script tag and inject arbitrary HTML/JS. Use .replace(/</g, '\\u003c') after stringifying to neutralize the sequence.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/mesh/src/mcp-clients/local/index.ts, line 394:

<comment>Script injection via unescaped `JSON.stringify` inside `<script>` tag. `JSON.stringify` doesn't escape `</` sequences, so a crafted `rootPath` or `detection.command` (from `.deco/preview.json`) can close the script tag and inject arbitrary HTML/JS. Use `.replace(/</g, '\\u003c')` after stringifying to neutralize the sequence.</comment>

<file context>
@@ -192,46 +302,117 @@ function getPreviewHtml(rootPath: string): string {
 
+// ---- Preview state ----
+
+var ROOT = ${JSON.stringify(rootPath)};
+var BASE_FILE_URL = ${JSON.stringify(baseFileUrl)};
+var detection = ${JSON.stringify(detection)};
</file context>
Suggested change
var ROOT = ${JSON.stringify(rootPath)};
var BASE_FILE_URL = ${JSON.stringify(baseFileUrl)};
var detection = ${JSON.stringify(detection)};
var ROOT = ${JSON.stringify(rootPath).replace(/</g, "\\u003c")};
var BASE_FILE_URL = ${JSON.stringify(baseFileUrl).replace(/</g, "\\u003c")};
var detection = ${JSON.stringify(detection).replace(/</g, "\\u003c")};
Fix with Cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant