Skip to content

Conversation

@SwenSchaeferjohann
Copy link
Contributor

@SwenSchaeferjohann SwenSchaeferjohann commented Dec 31, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Launched interactive code runner with live TypeScript execution, tabbed code viewer, and real-time console output
    • Added "Create Mint" example with embedded, runnable code
    • Created code execution API with security validation and resource limits
  • Chores

    • Added Docker containerization and Fly.io deployment configuration
    • Configured development environment with build automation scripts

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 31, 2025

Walkthrough

This PR introduces a complete code execution and interactive example platform consisting of two new services: "code-executor" (a security-hardened Node.js/Express service for TypeScript code execution) and "code-runner" (a Next.js frontend for rendering interactive code examples), along with deployment configurations, documentation, and embedding utilities.

Changes

Cohort / File(s) Summary
code-executor Deployment & Configuration
code-executor/Dockerfile, docker-compose.yml, fly.toml, package.json, tsconfig.json, .dockerignore, .gitignore
Adds multi-platform deployment setup for code-executor service: Dockerfile for Node.js 20 production build, docker-compose.yml with resource constraints and security options (read-only filesystem, tmpfs for temporary files), Fly.io configuration, package.json with Express, Helmet, rate-limiting dependencies, and TypeScript configuration.
code-executor Source Code
code-executor/src/executor.ts, src/server.ts
Introduces core execution logic: executor.ts validates code against blocked patterns, wraps user code in a sandbox, executes via tsx with timeout and output-size limits, and cleans up temporary files. server.ts provides Express API with Helmet security, API key authentication via Bearer token, health endpoint, and protected POST /typescript endpoint for code execution with validation.
code-executor Documentation
code-executor/README.md
Documents security features, environment variables, local development setup, Docker/cloud deployment options (Railway, Fly.io, Render), API endpoints with example requests/responses, and integration with code-runner.
code-runner Project Setup & Configuration
code-runner/package.json, tsconfig.json, next.config.js, tailwind.config.js, postcss.config.js, .gitignore
Establishes Next.js 14 project with React 18, UI libraries (Radix UI, Shiki for syntax highlighting), Tailwind CSS, and CORS headers enabled globally for all routes.
code-runner API & Page Components
code-runner/src/app/api/run/route.ts, src/app/page.tsx, src/app/layout.tsx, src/app/embed/create-mint/page.tsx, src/app/embed/layout.tsx
Implements POST /api/run endpoint that validates request, forwards code to external code-executor service, and returns execution results. Provides root layout with metadata and styling, home page with navigation link, embed layout wrapper, and create-mint example page with syntax-highlighted code tabs.
code-runner UI Components & Utilities
code-runner/src/components/code-runner.tsx, code-icon.tsx, copy-button.tsx, ui/resizable.tsx, ui/tabs.tsx
code-runner/src/lib/highlighter.ts, lib/utils.ts, src/app/globals.css
Provides interactive CodeRunner component with tabbed code viewer, resizable panels, live console, and run button. Includes CodeIcon for language-specific file icons, CopyButton for clipboard interaction, syntax highlighting via Shiki, utility functions (cn for class merging), resizable and tabs UI wrappers, and global dark-themed Solana CSS palette.
code-runner Documentation
code-runner/README.md
Documents architecture, required environment variables (CODE_EXECUTOR_URL, EXECUTOR_API_KEY), local development steps, guide for adding new examples, and deployment instructions for Vercel and Mintlify.
Light Token Documentation & Embed Utility
light-token/cookbook/create-mint.mdx, light-token/toolkits/for-streaming-tokens.mdx
snippets/jsx/code-runner-embed.jsx
Adds "Try It Live" CodeRunnerEmbed sections to documentation, refactors token-loading example to use instruction-based pattern with transaction building, and introduces CodeRunnerEmbed React component for embedding interactive code runners via iframe.

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Browser / Client
    participant Runner as Code Runner<br/>(Next.js)
    participant API as Code Runner API<br/>(POST /api/run)
    participant Executor as Code Executor<br/>(Express Service)
    
    rect rgb(200, 220, 255)
    Note over Browser,Executor: Code Execution Flow
    Browser->>Runner: Click "Run" button
    Runner->>API: POST /api/run<br/>{ code, language }
    API->>API: Validate request<br/>(schema, env vars)
    alt Validation failed
        API-->>Runner: 400 Bad Request
    else Valid request
        API->>Executor: POST /typescript<br/>Authorization header
        rect rgb(255, 200, 200)
        Note over Executor: Execution Phase
        Executor->>Executor: Validate code patterns<br/>(blocked keywords)
        Executor->>Executor: Wrap code in sandbox<br/>+ console.log capture
        Executor->>Executor: Execute via tsx<br/>(timeout, size limits)
        Executor->>Executor: Monitor stdout/stderr
        end
        alt Execution error
            Executor-->>API: { stderr: error message }
        else Success
            Executor-->>API: { stdout: output }
        end
        alt Has stderr only
            API-->>Runner: 500 Execution Error
        else Has stdout
            API-->>Runner: 200 { output }
        end
    end
    Runner->>Browser: Display output<br/>in console panel
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Code hops and bounds through executor's den,
Wrapped in sandbox walls, executed then,
Results flow back where examples shine,
Interactive learning—oh how divine!
The code-runner app, a browser's dream,
Makes Light Protocol sparkle and gleam! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'chore: add code runner' is overly broad and vague. It does not clearly specify what code runner is being added or which specific changes are most important among the many new files and features. Consider a more descriptive title that reflects the main scope, such as 'chore: add code executor and runner with embed support' or similar to better convey the changeset's primary purpose.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
light-token/cookbook/create-mint.mdx (1)

2-2: Fix typo in title.

The title contains a typo: "wih" should be "with".

🔎 Proposed fix
-title: Create Mint Account wih Token Metadata
+title: Create Mint Account with Token Metadata
🧹 Nitpick comments (8)
code-runner/src/app/api/run/route.ts (1)

54-59: Consider handling cases where both stdout and stderr exist.

The current logic only returns an error if stderr exists without stdout. However, it's common for code to produce both stdout (normal output) and stderr (warnings/errors). The current implementation silently discards stderr when stdout is present, which could hide important warnings or non-fatal errors from users.

Consider returning both stdout and stderr to provide complete execution feedback.

🔎 Proposed enhancement to include stderr in response
-    if (result.stderr && !result.stdout) {
+    if (result.stderr && !result.stdout) {
       return NextResponse.json(
         { error: "Execution error", details: { error: result.stderr } },
         { status: 500 }
       );
     }
 
-    return NextResponse.json({ output: result.stdout || "" }, { status: 200 });
+    return NextResponse.json(
+      { 
+        output: result.stdout || "",
+        ...(result.stderr && { stderr: result.stderr })
+      },
+      { status: 200 }
+    );
code-executor/README.md (1)

95-97: Add language specifiers to fenced code blocks.

The fenced code blocks on lines 95 and 102 are missing language specifiers, which helps with syntax highlighting and accessibility.

🔎 Proposed fix
 ### Health Check
 
-```
+```http
 GET /health
 Response: { "status": "ok" }

Execute TypeScript

- +http
POST /typescript
Headers:
Authorization: Bearer <EXECUTOR_API_KEY>
Content-Type: application/json

</details>


Also applies to: 102-113

</blockquote></details>
<details>
<summary>code-executor/docker-compose.yml (1)</summary><blockquote>

`20-21`: **Consider restricting tmpfs permissions.**

The tmpfs mount uses `mode=1777` (world-writable with sticky bit). Since the container runs as a single non-root `executor` user, a more restrictive permission like `mode=1700` would reduce the attack surface without impacting functionality.



<details>
<summary>🔎 Proposed fix</summary>

```diff
     tmpfs:
-      - /app/.temp:size=50M,mode=1777
+      - /app/.temp:size=50M,mode=1700
snippets/jsx/code-runner-embed.jsx (1)

1-17: Add input validation for the example parameter.

The example prop is directly interpolated into the iframe URL without validation. While current usage appears to use hardcoded values, adding validation would prevent potential path traversal or unexpected behavior if the component is reused with dynamic inputs.

🔎 Proposed validation
 export const CodeRunnerEmbed = ({ example, height = "450px" }) => {
+  // Validate example contains only safe characters (alphanumeric, hyphens)
+  if (!/^[a-z0-9-]+$/.test(example)) {
+    console.error(`Invalid example name: ${example}`);
+    return null;
+  }
+
   const baseUrl = process.env.CODE_RUNNER_URL || "http://localhost:3030";
   return (
     <iframe
       src={`${baseUrl}/embed/${example}`}
       style={{
         width: "100%",
         height: height,
         border: "none",
         borderRadius: "8px",
         overflow: "hidden",
       }}
       allow="clipboard-write"
+      sandbox="allow-scripts allow-same-origin"
       title={`${example} code example`}
     />
   );
 };

Note: Evaluate if sandbox attributes are compatible with your embed functionality requirements.

code-runner/src/lib/highlighter.ts (1)

5-13: Consider adding error handling for highlighter initialization.

The lazy initialization is clean, but consider adding error handling for the createHighlighter call in case Shiki fails to initialize (e.g., missing themes or language files).

🔎 Optional: Add error handling
 export async function getHighlighter(): Promise<Highlighter> {
   if (!highlighter) {
-    highlighter = await createHighlighter({
-      themes: ["github-dark"],
-      langs: ["typescript", "ts"],
-    });
+    try {
+      highlighter = await createHighlighter({
+        themes: ["github-dark"],
+        langs: ["typescript", "ts"],
+      });
+    } catch (error) {
+      console.error("Failed to initialize highlighter:", error);
+      throw new Error("Syntax highlighter initialization failed");
+    }
   }
   return highlighter;
 }
code-executor/src/server.ts (1)

27-34: Consider timing-safe comparison for API key validation.

The current string comparison on Line 29 could theoretically be vulnerable to timing attacks, though the risk is low given the network latency typically dominates any timing differences.

🔎 Optional: Use timing-safe comparison
+import crypto from "crypto";
+
 function authMiddleware(req: Request, res: Response, next: NextFunction) {
   const authHeader = req.headers.authorization;
-  if (!authHeader || authHeader !== `Bearer ${API_KEY}`) {
+  const expected = `Bearer ${API_KEY}`;
+  const valid = authHeader && authHeader.length === expected.length &&
+    crypto.timingSafeEqual(Buffer.from(authHeader), Buffer.from(expected));
+  if (!valid) {
     res.status(401).json({ error: "Unauthorized" });
     return;
   }
   next();
 }
code-executor/src/executor.ts (2)

16-26: Blocked patterns provide baseline protection but consider additional restrictions.

The current blocked patterns prevent access to child_process, fs, process.env, eval, Function, and vm. However, consider adding:

  • Network-related modules (http, https, net, dgram)
  • Additional dangerous patterns (__dirname, __filename, import.meta.url)
  • Dynamic imports (import())
🔎 Enhanced blocked patterns
 const BLOCKED_PATTERNS = [
   /require\s*\(\s*['"]child_process['"]\s*\)/,
   /require\s*\(\s*['"]fs['"]\s*\)/,
   /import\s+.*from\s+['"]child_process['"]/,
   /import\s+.*from\s+['"]fs['"]/,
   /process\.env/,
   /eval\s*\(/,
   /Function\s*\(/,
   /vm\./,
   /require\s*\(\s*['"]vm['"]\s*\)/,
+  /require\s*\(\s*['"]http['"]\s*\)/,
+  /require\s*\(\s*['"]https['"]\s*\)/,
+  /require\s*\(\s*['"]net['"]\s*\)/,
+  /import\s+.*from\s+['"]http['"]/,
+  /import\s+.*from\s+['"]https['"]/,
+  /import\s+.*from\s+['"]net['"]/,
+  /__dirname/,
+  /__filename/,
+  /import\.meta\.url/,
+  /import\s*\(/,
 ];

63-141: Recommend additional operational safeguards for production deployment.

While the code includes good baseline protections (blocked patterns, timeouts, output limits), executing arbitrary user code requires defense-in-depth:

  • Container isolation: Run in a lightweight container with no network access
  • Resource limits: Use cgroups or Docker to limit CPU, memory, and I/O
  • Filesystem isolation: Use read-only filesystem or minimal writable tmpfs
  • User isolation: Run as unprivileged user with no sudo access
  • Network isolation: Block all outbound network connections
  • Monitoring: Log and alert on suspicious patterns or resource exhaustion
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a6326e and 26eaeb4.

⛔ Files ignored due to path filters (2)
  • code-executor/package-lock.json is excluded by !**/package-lock.json
  • code-runner/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (33)
  • code-executor/.dockerignore
  • code-executor/.gitignore
  • code-executor/Dockerfile
  • code-executor/README.md
  • code-executor/docker-compose.yml
  • code-executor/fly.toml
  • code-executor/package.json
  • code-executor/src/executor.ts
  • code-executor/src/server.ts
  • code-executor/tsconfig.json
  • code-runner/.gitignore
  • code-runner/README.md
  • code-runner/next.config.js
  • code-runner/package.json
  • code-runner/postcss.config.js
  • code-runner/src/app/api/run/route.ts
  • code-runner/src/app/embed/create-mint/page.tsx
  • code-runner/src/app/embed/layout.tsx
  • code-runner/src/app/globals.css
  • code-runner/src/app/layout.tsx
  • code-runner/src/app/page.tsx
  • code-runner/src/components/code-icon.tsx
  • code-runner/src/components/code-runner.tsx
  • code-runner/src/components/copy-button.tsx
  • code-runner/src/components/ui/resizable.tsx
  • code-runner/src/components/ui/tabs.tsx
  • code-runner/src/lib/highlighter.ts
  • code-runner/src/lib/utils.ts
  • code-runner/tailwind.config.js
  • code-runner/tsconfig.json
  • light-token/cookbook/create-mint.mdx
  • light-token/toolkits/for-streaming-tokens.mdx
  • snippets/jsx/code-runner-embed.jsx
🧰 Additional context used
🧬 Code graph analysis (3)
code-runner/src/components/ui/resizable.tsx (1)
code-runner/src/lib/utils.ts (1)
  • cn (4-6)
code-executor/src/server.ts (1)
code-executor/src/executor.ts (1)
  • executeCode (63-141)
code-runner/src/components/ui/tabs.tsx (1)
code-runner/src/lib/utils.ts (1)
  • cn (4-6)
🪛 ast-grep (0.40.3)
code-runner/src/components/code-icon.tsx

[warning] 54-54: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

code-runner/src/components/code-runner.tsx

[warning] 159-159: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
code-runner/src/components/code-icon.tsx

[error] 55-55: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

code-runner/src/components/code-runner.tsx

[error] 160-160: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🪛 markdownlint-cli2 (0.18.1)
code-executor/README.md

95-95: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


102-102: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (26)
code-runner/.gitignore (1)

1-31: Standard Next.js .gitignore configuration—approved.

The .gitignore file is well-organized and covers all essential patterns for a Next.js project: dependencies, build artifacts, environment files, logs, system files, and TypeScript metadata. The categorized comments enhance readability and maintainability.

light-token/toolkits/for-streaming-tokens.mdx (1)

68-68: LGTM!

The formatting adjustments to the code block delimiters are correct and won't affect rendering.

Also applies to: 140-140

code-runner/tsconfig.json (1)

1-22: LGTM!

The TypeScript configuration is well-suited for a Next.js application with appropriate compiler options, path aliases, and Next.js plugin integration.

code-executor/.dockerignore (1)

1-8: LGTM!

The Docker ignore patterns appropriately exclude build artifacts, dependencies, temporary files, and sensitive environment configuration from the Docker build context.

code-runner/postcss.config.js (1)

1-6: LGTM!

Standard PostCSS configuration for Tailwind CSS and Autoprefixer integration.

code-executor/.gitignore (1)

1-6: LGTM!

The ignore patterns appropriately exclude build artifacts, dependencies, temporary files, environment configuration, and logs from version control.

light-token/cookbook/create-mint.mdx (1)

15-15: LGTM! Clean integration of live code example.

The CodeRunnerEmbed integration provides an interactive way for users to try the create-mint example directly in the documentation. The height specification is appropriate for the embedded content.

Also applies to: 20-22

code-executor/README.md (1)

1-124: Excellent documentation! Security measures are well-documented.

The README provides comprehensive coverage of:

  • Security features (API auth, rate limiting, timeouts, resource limits, sandboxing)
  • Deployment options across multiple platforms
  • Clear integration guidance

This will help users understand and deploy the executor service safely.

code-executor/package.json (1)

11-17: LGTM! Appropriate security-focused dependencies.

The dependency selection is appropriate for a security-hardened execution service:

  • Express for the HTTP server
  • Helmet for security headers
  • express-rate-limit for DoS protection

These align well with the security features documented in the README.

code-runner/src/app/page.tsx (1)

1-18: LGTM! Clean and minimal landing page.

The home page provides a simple entry point with proper semantic HTML and Next.js best practices (using the Link component for client-side navigation).

code-runner/src/app/embed/layout.tsx (1)

1-8: LGTM! Appropriate minimal layout for iframe embedding.

This pass-through layout is correct for embedded content, avoiding unnecessary wrapper elements that could interfere with iframe styling or dimensions.

code-runner/README.md (1)

1-87: Excellent documentation with clear architecture overview.

The README provides:

  • Clear architecture diagram showing component interaction
  • Comprehensive setup instructions for local development
  • Guidance for adding new examples
  • Deployment instructions for Vercel

This will help developers understand and extend the code runner system effectively.

code-runner/src/lib/utils.ts (1)

1-7: LGTM! Standard Tailwind utility pattern.

This is a well-established pattern for merging Tailwind CSS classes, combining classnames for conditional class handling with tailwind-merge for deduplication and conflict resolution. This pattern is widely used in component libraries like shadcn/ui.

code-executor/fly.toml (2)

10-15: Note: Auto-scaling to zero will introduce cold starts.

The configuration sets min_machines_running = 0, which will scale the service down to zero when idle. This is cost-effective but will introduce cold start latency when the first request arrives after a period of inactivity. Consider if this tradeoff is acceptable for the expected usage patterns of the documentation's live code examples.


1-21: LGTM! Deployment configuration is well-sized.

The Fly.io configuration is appropriate:

  • 512MB memory provides headroom beyond the documented 128MB heap limit
  • Shared CPU (1 vCPU) is suitable for code execution workloads
  • Port 3040 is consistent across all configurations
  • HTTPS enforcement enhances security
code-runner/src/app/globals.css (1)

1-101: LGTM! Well-structured theme configuration.

The CSS file provides a comprehensive dark theme with proper Tailwind integration, sensible global resets, and appropriate font stacks. The color variable naming is consistent and the Shiki overrides ensure proper code block rendering.

code-runner/tailwind.config.js (1)

1-19: LGTM! Standard Tailwind configuration.

The configuration properly extends the theme with CSS variable-based colors that align with the global styles. The content pattern correctly targets all relevant file types.

code-runner/src/app/embed/create-mint/page.tsx (1)

1-105: LGTM! Clean async page component.

The component properly leverages Next.js async server components to pre-highlight code samples. The tab structure is clear and the full-screen layout is appropriate for an embed context.

code-runner/next.config.js (1)

4-15: Verify wildcard CORS is appropriate for your security model.

The configuration allows requests from any origin (Access-Control-Allow-Origin: *). While this may be intentional for a public embed service, ensure this aligns with your security requirements, especially if any endpoints handle sensitive operations or data.

Consider restricting to known domains in production:

🔎 Example of domain restriction
async headers() {
  return [
    {
      source: "/embed/:path*",  // Only embed routes need CORS
      headers: [
        { 
          key: "Access-Control-Allow-Origin", 
          value: process.env.ALLOWED_ORIGINS || "https://docs.example.com" 
        },
        { key: "Access-Control-Allow-Methods", value: "GET, OPTIONS" },
        { key: "Access-Control-Allow-Headers", value: "Content-Type" },
      ],
    },
  ];
},

If wildcard CORS is required for public embeds, ensure that:

  • API endpoints validate authentication tokens (like EXECUTOR_API_KEY)
  • Rate limiting is in place
  • No sensitive data is exposed without proper authorization
code-runner/src/app/layout.tsx (1)

9-19: LGTM!

The layout implementation follows Next.js App Router best practices. The hardcoded dark theme aligns well with the code runner's technical aesthetic.

code-executor/src/server.ts (1)

40-63: LGTM on request validation and execution flow.

The validation logic is solid:

  • Type checking for code field
  • Length limit enforcement (10KB)
  • Proper error handling with try-catch
  • Appropriate HTTP status codes

The integration with executeCode looks correct based on the executor module's API.

code-runner/src/components/ui/resizable.tsx (1)

7-42: LGTM!

Clean wrapper implementation around react-resizable-panels with appropriate styling and prop forwarding. The conditional withHandle rendering and responsive Tailwind classes are well-structured.

code-runner/src/components/ui/tabs.tsx (1)

7-49: LGTM!

Standard Radix UI wrapper pattern with proper forwardRef usage and displayName preservation. The implementation follows React best practices for component composition.

code-executor/src/executor.ts (1)

103-122: LGTM on timeout and output size enforcement.

The implementation properly enforces both execution timeout (30s) and output size limits (50KB) using:

  • setTimeout with SIGKILL for hard termination
  • Stream monitoring with immediate termination on exceeding limits

This provides strong defense against resource exhaustion attacks.

code-runner/src/components/code-runner.tsx (2)

33-68: LGTM on the execution hook and error handling.

The useRunnableCode hook properly:

  • Manages loading state during execution
  • Clears previous results and errors before each run
  • Handles both HTTP errors and network failures
  • Extracts error details from the response payload

The error handling logic on Lines 50-55 correctly checks both res.ok and data.error.


165-243: Well-structured responsive layout with proper state management.

The CodeRunner component demonstrates good architectural decisions:

  • Clean separation between desktop (resizable panels) and mobile (stacked) layouts
  • Proper state synchronization between tabs and execution context
  • Reusable subcomponents (Console, CodeBlock, EmptyConsole)

Comment on lines +11 to +17
COPY package*.json ./
RUN npm ci --only=production

COPY tsconfig.json ./
COPY src ./src

RUN npm run build || npx tsc
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Build will fail: TypeScript not available.

The Dockerfile installs only production dependencies (npm ci --only=production) before building, but TypeScript is typically a dev dependency. The build step on Line 17 will fail because the TypeScript compiler won't be installed.

🔎 Recommended fix using multi-stage build
 FROM node:20-slim
 
 RUN apt-get update && apt-get install -y --no-install-recommends \
     ca-certificates \
     && rm -rf /var/lib/apt/lists/*
 
-RUN groupadd -r executor && useradd -r -g executor executor
-
 WORKDIR /app
 
 COPY package*.json ./
-RUN npm ci --only=production
+RUN npm ci
 
 COPY tsconfig.json ./
 COPY src ./src
 
-RUN npm run build || npx tsc
+RUN npm run build
+
+# Production stage
+FROM node:20-slim
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+    ca-certificates \
+    && rm -rf /var/lib/apt/lists/*
+
+RUN groupadd -r executor && useradd -r -g executor executor
+
+WORKDIR /app
+
+COPY package*.json ./
+RUN npm ci --only=production
+
+COPY --from=0 /app/dist ./dist
 
 RUN mkdir -p .temp && chown -R executor:executor /app
 
 USER executor
 
 ENV NODE_ENV=production
 ENV PORT=3040
 
 EXPOSE 3040
 
 CMD ["node", "dist/server.js"]

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In code-executor/Dockerfile around lines 11 to 17, the Dockerfile runs npm ci
--only=production before a TypeScript build so the tsc compiler (a
devDependency) is not installed and the build will fail; fix by using a
multi-stage build or installing devDependencies for the build stage: in the
first stage install all dependencies (no --only=production) or run npm ci, run
the TypeScript build, then in a final runtime stage copy only the produced dist
and production deps (or run npm prune --production) so the final image contains
only production packages while the build stage has tsc.

COPY tsconfig.json ./
COPY src ./src

RUN npm run build || npx tsc
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove the build command fallback.

The fallback pattern || npx tsc masks failures from the primary npm run build command. If the build script fails for any reason (missing dependencies, TypeScript errors, etc.), the fallback will execute and potentially hide the actual error.

🔎 Proposed fix
-RUN npm run build || npx tsc
+RUN npm run build
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
RUN npm run build || npx tsc
RUN npm run build
🤖 Prompt for AI Agents
In code-executor/Dockerfile around line 17, the RUN step uses "npm run build ||
npx tsc" which masks failures from the primary build; remove the fallback and
change the step to run only "npm run build" so the Docker build fails on real
build errors, and ensure any required dev/build dependencies are installed
earlier in the Dockerfile so the build can run successfully.

Comment on lines +12 to +13
"@lightprotocol/compressed-token": "0.22.1-alpha.7",
"@lightprotocol/stateless.js": "0.22.1-alpha.6",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

@lightprotocol/compressed-token 0.22.1-alpha.7 production ready stable

💡 Result:

Short answer: No — 0.22.1-alpha.7 is an alpha release and not considered production-stable. Use the published stable release (0.22.0) for production or wait for a non‑alpha 0.22.1. Sources: npm package page (latest stable 0.22.0) and Lightprotocol changelog referencing alpha releases. [1][2]

Sources

  • npm: @lightprotocol/compressed-token — version 0.22.0 (latest). [1]
  • Lightprotocol changelog / docs showing alpha releases (0.22.1-alpha.* referenced). [2]

🌐 Web query:

Light Protocol packages stable releases 0.22.1 npm

💡 Result:

Short answer: There is no stable 0.22.1 release on npm — the latest published stable npm versions I find are 0.22.0. The Light docs/package pages reference 0.22.1 as alpha (e.g., 0.22.1-alpha.1), not a stable npm release. [1][2][3]

Sources:

  • @lightprotocol/compressed-token (npm) — version 0.22.0. [1]
  • @lightprotocol/stateless.js docs — shows v0.22.1-alpha.1. [2]
  • Package index / metadata (socket.dev) for @lightprotocol/stateless.js showing latest stable 0.22.0. [3]

🏁 Script executed:

# Check the actual package.json file to confirm versions
cat -n code-executor/package.json | head -20

Repository: Lightprotocol/docs-v2

Length of output: 721


🏁 Script executed:

# Search for any documentation or comments about these packages
rg "lightprotocol" --type json --type markdown -A 2 -B 2

Repository: Lightprotocol/docs-v2

Length of output: 2646


🏁 Script executed:

# Check if there are version specifications elsewhere
fd package.json | xargs grep -l "lightprotocol"

Repository: Lightprotocol/docs-v2

Length of output: 91


Upgrade to stable release versions of Light Protocol packages.

The executor service currently depends on alpha versions of Light Protocol packages (@lightprotocol/compressed-token@0.22.1-alpha.7 and @lightprotocol/stateless.js@0.22.1-alpha.6). These alpha releases are not production-ready. Upgrade to the stable 0.22.0 versions available on npm, or wait for stable 0.22.1 releases before deploying to production.

🤖 Prompt for AI Agents
In code-executor/package.json around lines 12 to 13, the dependencies reference
alpha releases of Light Protocol packages; replace
"@lightprotocol/compressed-token": "0.22.1-alpha.7" and
"@lightprotocol/stateless.js": "0.22.1-alpha.6" with the stable "0.22.0"
versions (or pin to the upcoming stable 0.22.1 if/when released), then run npm
install to update package-lock.json (or yarn.lock) and run the test suite to
confirm no breaking changes; commit the updated lockfile alongside package.json.

Comment on lines +82 to +91
return new Promise((resolve) => {
const child = spawn("npx", ["tsx", filePath], {
timeout: TIMEOUT_MS,
env: {
...process.env,
NODE_NO_WARNINGS: "1",
NODE_OPTIONS: "--max-old-space-size=128",
},
cwd: join(__dirname, ".."),
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Environment variables are exposed to executed code.

Line 86 passes process.env to the spawned child process, which means executed user code can access all environment variables including potentially sensitive values like API keys, database credentials, and other secrets.

🔎 Recommended fix: Use empty environment
 const child = spawn("npx", ["tsx", filePath], {
   timeout: TIMEOUT_MS,
   env: {
-    ...process.env,
+    PATH: process.env.PATH, // Only pass essential variables
     NODE_NO_WARNINGS: "1",
     NODE_OPTIONS: "--max-old-space-size=128",
   },
   cwd: join(__dirname, ".."),
 });

Alternatively, use an explicit allowlist of safe environment variables or run with an empty environment.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return new Promise((resolve) => {
const child = spawn("npx", ["tsx", filePath], {
timeout: TIMEOUT_MS,
env: {
...process.env,
NODE_NO_WARNINGS: "1",
NODE_OPTIONS: "--max-old-space-size=128",
},
cwd: join(__dirname, ".."),
});
return new Promise((resolve) => {
const child = spawn("npx", ["tsx", filePath], {
timeout: TIMEOUT_MS,
env: {
PATH: process.env.PATH, // Only pass essential variables
NODE_NO_WARNINGS: "1",
NODE_OPTIONS: "--max-old-space-size=128",
},
cwd: join(__dirname, ".."),
});
🤖 Prompt for AI Agents
In code-executor/src/executor.ts around lines 82–91, the spawned child process
currently inherits all of process.env (exposing secrets) — replace the spread of
process.env with an explicit, minimal environment: either an empty env or an
allowlist that only includes non-sensitive keys required to run (e.g.,
NODE_NO_WARNINGS and NODE_OPTIONS), and if you need to run npx keep PATH only;
do not pass process.env wholesale. Ensure the child still receives required keys
(PATH if using npx) and remove any secrets or broad inheritance from
process.env.

Comment on lines +3 to +5
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

TypeScript moduleResolution bundler vs node for Node.js server applications

💡 Result:

Short answer

  • For a Node.js server app, prefer a Node-targeted moduleResolution (node16/node18/nodenext) so TypeScript enforces the same resolution rules Node will use at runtime. Use "bundler" only if you actually bundle the server code (and understand the trade‑offs). [1][2][3]

Why (key differences)

  • Node modes (node16/node18/nodenext) emulate Node’s resolution: they require correct file extensions for ESM, follow package.json "exports"/"imports", and check import/require differences so emitted JS will run in Node. This prevents producing import specifiers that work in bundlers but will fail under Node. [1][2]
  • bundler mode lets extensionless relative imports (./utils) and other bundler‑friendly patterns pass typechecking; it assumes a bundler will rewrite/resolve specifiers, so it can allow code that will break when run directly in Node (unless you actually bundle first). [1][2][3]

Practical effects for Node servers

  • If you use ESM in Node, node(16/18)/nodenext will make TypeScript require .js/.mjs/.cjs correctness and honor "exports" so runtime import errors are caught at compile time. [1][2]
  • Using bundler may hide missing .js extensions, incorrect package "exports" targets, or other issues that will only appear when Node runs the emitted files. [1][2]
  • If you publish a library, bundler mode is "infectious": it may produce output that only works for bundler consumers; prefer Node modes for libraries unless you bundle everything (including declarations) and guarantee consumers run in bundlers. [1][3]

Recommendations (concise)

  • Node.js server app (not bundled): set moduleResolution to node18/node16/nodenext and set module to matching Node target (e.g., "module": "node18" or "nodenext") and include "type": "module" in package.json if using ESM. This catches Node runtime issues at compile time. [1][2]
  • If you build a single bundled artifact for deployment (you run a bundler that resolves all imports and emits final JS you deploy), you can use "bundler" with "module": "esnext"/"preserve" — but ensure the bundler produces correct runtime module specifiers and bundles (or produces compatible) declaration files. [1][3]
  • When publishing a library, prefer Node resolution for maximal compatibility unless you control the consumer environment and produce bundled declarations. [3]

Minimal example tsconfig choices

  • For Node ESM server:

    • "module": "node18" (or "nodenext")
    • "moduleResolution": "node18" (or "nodenext")
    • package.json: "type": "module". [1][2]
  • For bundler-built output:

    • "module": "esnext" (or "preserve")
    • "moduleResolution": "bundler"
    • ensure bundler handles extensions/exports and that declarations are compatible. [1][3]

Sources
[1] TypeScript handbook — Choosing compiler options / guidance on bundler vs Node resolution.
[2] TypeScript tsconfig.moduleResolution reference (description of node16/node18/nodenext vs bundler).
[3] TypeScript modules theory / guidance about libraries, bundling and declaration files.


🏁 Script executed:

# Check the tsconfig.json file
cat -n code-executor/tsconfig.json

# Look for Dockerfile
fd Dockerfile

# Check for bundler configuration
fd webpack.config package.json esbuild.config

Repository: Lightprotocol/docs-v2

Length of output: 703


🏁 Script executed:

# Check Dockerfile
cat -n code-executor/Dockerfile

# Look for build configuration and package.json
ls -la code-executor/ | grep -E "webpack|esbuild|rollup|tsup|package.json|build"

# Check package.json
cat code-executor/package.json | head -50

Repository: Lightprotocol/docs-v2

Length of output: 1428


Use Node.js-compatible moduleResolution for unbundled Node.js server.

The code-executor service is a Node.js application built with only tsc (no bundler) and run directly with node dist/server.js. Using moduleResolution: "bundler" in this setup is incompatible because TypeScript will allow patterns that work in bundlers but will fail under Node.js's module resolution, such as extensionless imports and incorrect package.json "exports" handling. With "type": "module" (ESM) in package.json, change to "moduleResolution": "node18" or "nodenext" to enforce proper Node.js module resolution at compile time, preventing runtime failures.

🤖 Prompt for AI Agents
In code-executor/tsconfig.json around lines 3 to 5, moduleResolution is set to
"bundler" which is incompatible with an unbundled Node.js service; update
"moduleResolution" to a Node-compatible mode such as "node18" or "nodenext"
(depending on whether you need Node.js 18-specific behavior or full Node ESM
resolution) so TypeScript enforces Node semantics for extensionless imports and
package "exports" handling, then recompile to verify there are no now-revealed
import/extension issues.

Comment on lines +34 to +41
const response = await fetch(`${CODE_EXECUTOR_URL}/typescript`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${EXECUTOR_API_KEY}`,
},
body: JSON.stringify({ code }),
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add timeout to prevent hanging requests.

The fetch call to the code executor lacks a timeout, which could cause the API route to hang indefinitely if the executor becomes unresponsive. This can exhaust server resources and degrade user experience.

🔎 Proposed fix to add timeout using AbortSignal
+    const controller = new AbortController();
+    const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 second timeout
+
     const response = await fetch(`${CODE_EXECUTOR_URL}/typescript`, {
       method: "POST",
       headers: {
         "Content-Type": "application/json",
         Authorization: `Bearer ${EXECUTOR_API_KEY}`,
       },
       body: JSON.stringify({ code }),
+      signal: controller.signal,
     });
+
+    clearTimeout(timeoutId);
🤖 Prompt for AI Agents
In code-runner/src/app/api/run/route.ts around lines 34 to 41, the fetch to the
code executor has no timeout and can hang indefinitely; fix by creating an
AbortController, pass its signal into fetch, start a timer (e.g., setTimeout)
that calls controller.abort() after a sensible timeout (e.g., 5-10s), and clear
that timer once fetch resolves or rejects; also handle the abort case
specifically (detect AbortError or error.name === 'AbortError') to return a
suitable timeout response and avoid leaking the timer.

Comment on lines +47 to +59
const { svg, color } = getIcon(filename);
const __html = svg.replace(
/svg/,
`svg fill='${color}' height='28' style='margin-left: -6px; margin-top: -6px;'`
);
return (
<span className={className}>
<span
dangerouslySetInnerHTML={{ __html }}
style={{ display: "contents" }}
/>
</span>
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

seti-icons npm package latest version and maintenance status

💡 Result:

Summary: seti-icons — latest published npm version 0.0.4; last published ~6 years ago and appears unmaintained/low-activity (few maintainers, low weekly downloads). Sources: [1][2].

Sources

  • jsDelivr package info for seti-icons (v0.0.4). [1]
  • package overview / health (Socket) for seti-icons (latest 0.0.4, last published ~6 years ago, low weekly downloads). [2]

🏁 Script executed:

# Search for seti-icons imports and usage in the codebase
rg "seti-icons" --type ts --type tsx

# Find the getIcon function definition
rg "getIcon" --type ts --type tsx -A 5

Repository: Lightprotocol/docs-v2

Length of output: 120


🏁 Script executed:

# Search for seti-icons imports in typescript/tsx files
rg "seti-icons" --type ts

# Also try a broader search without type restriction
rg "seti-icons"

# Find the getIcon function definition
rg "getIcon" -A 5

# Check the code-icon.tsx file directly
cat code-runner/src/components/code-icon.tsx

Repository: Lightprotocol/docs-v2

Length of output: 2642


Address unmaintained seti-icons dependency.

The dangerouslySetInnerHTML usage here is a false positive: SVG content comes from the trusted seti-icons library, color values are library-generated, and no user input is directly injected. However, verification reveals that seti-icons is unmaintained—last published ~6 years ago with no active development. The codebase uses v0.0.3 of this abandoned library.

Consider migrating to an actively maintained icon library like lucide-react (already in use elsewhere in this component) or another well-maintained alternative to reduce long-term maintenance risk and potential security vulnerabilities in dependencies.

🧰 Tools
🪛 ast-grep (0.40.3)

[warning] 54-54: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)

[error] 55-55: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🤖 Prompt for AI Agents
In code-runner/src/components/code-icon.tsx around lines 47 to 59, the component
relies on the unmaintained seti-icons package and uses dangerouslySetInnerHTML
to inject SVG strings; replace this by removing the seti-icons dependency and
mapping file extensions to React SVG icon components (preferably lucide-react
which is already in the project) instead of inserting raw HTML, pass color/size
via props/styles on the React icon components, eliminate dangerouslySetInnerHTML
usage, update imports and the getIcon mapping to return ReactElements rather
than SVG strings, and run/adjust any tests or types that relied on the previous
getIcon output.

Comment on lines +155 to +163
function CodeBlock({ highlightedHtml }: { highlightedHtml: string }) {
return (
<div
className="overflow-auto px-0 py-3 m-0 rounded-none bg-ch-background font-mono text-sm max-h-full selection:bg-ch-selection [&_pre]:bg-transparent [&_pre]:p-0 [&_pre]:m-0 [&_pre]:px-4 [&_code]:bg-transparent"
style={{ direction: "ltr", textAlign: "left" }}
dangerouslySetInnerHTML={{ __html: highlightedHtml }}
/>
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find the CodeBlock component usage and where highlightedHtml is passed
rg -n "CodeBlock\|highlightedHtml" code-runner/src/components/code-runner.tsx -A 2 -B 2

Repository: Lightprotocol/docs-v2

Length of output: 47


🏁 Script executed:

# Find the highlighter.ts file and examine its implementation
find code-runner/src -name "highlighter.ts" -o -name "highlighter.js" | head -20

Repository: Lightprotocol/docs-v2

Length of output: 99


🏁 Script executed:

# Search for all usages of highlightedHtml in the codebase
rg -rn "highlightedHtml" code-runner/src --type ts --type tsx

Repository: Lightprotocol/docs-v2

Length of output: 92


🏁 Script executed:

# Check for any other potential sources of HTML rendering in the component
rg -n "dangerouslySetInnerHTML" code-runner/src --type ts --type tsx -B 3 -A 3

Repository: Lightprotocol/docs-v2

Length of output: 92


🏁 Script executed:

# List files in code-runner directory structure
find code-runner/src -type f -name "*.ts" -o -name "*.tsx" | head -30

Repository: Lightprotocol/docs-v2

Length of output: 519


🏁 Script executed:

# Check the actual content of code-runner.tsx around the CodeBlock component
cat -n code-runner/src/components/code-runner.tsx | sed -n '150,170p'

Repository: Lightprotocol/docs-v2

Length of output: 770


🏁 Script executed:

# Find CodeBlock component usage without file type restrictions
rg -n "CodeBlock" code-runner/src

Repository: Lightprotocol/docs-v2

Length of output: 293


🏁 Script executed:

# Examine the highlighter.ts file to see how HTML is generated
cat -n code-runner/src/lib/highlighter.ts

Repository: Lightprotocol/docs-v2

Length of output: 760


🏁 Script executed:

# Search for all dangerouslySetInnerHTML usage
rg -n "dangerouslySetInnerHTML" code-runner/src

Repository: Lightprotocol/docs-v2

Length of output: 260


🏁 Script executed:

# Find where current is defined and how highlightedHtml is set in CodeRunner
cat -n code-runner/src/components/code-runner.tsx | sed -n '200,230p'

Repository: Lightprotocol/docs-v2

Length of output: 1568


🏁 Script executed:

# Search for CodeTab type definition
rg -n "type CodeTab\|interface CodeTab" code-runner/src

Repository: Lightprotocol/docs-v2

Length of output: 47


🏁 Script executed:

# Find where tabs are populated/initialized with highlightedHtml
rg -n "highlightedHtml.*=" code-runner/src

Repository: Lightprotocol/docs-v2

Length of output: 175


🏁 Script executed:

# Look at the useRunnableCode hook implementation
cat -n code-runner/src/components/code-runner.tsx | sed -n '33,68p'

Repository: Lightprotocol/docs-v2

Length of output: 1364


🏁 Script executed:

# Check for CodeTab initialization in the entire component
cat -n code-runner/src/components/code-runner.tsx | sed -n '1,90p'

Repository: Lightprotocol/docs-v2

Length of output: 2933


🏁 Script executed:

# Search for where CodeRunner is imported and used
rg -n "import.*CodeRunner\|<CodeRunner" code-runner/src

Repository: Lightprotocol/docs-v2

Length of output: 47


🏁 Script executed:

# Look for where tabs are created/initialized with highlightedHtml
rg -n "highlightedHtml:" code-runner/src -B 3 -A 3

Repository: Lightprotocol/docs-v2

Length of output: 1787


🏁 Script executed:

# Search for calls to highlightCode function
rg -n "highlightCode" code-runner/src

Repository: Lightprotocol/docs-v2

Length of output: 459


🏁 Script executed:

# Look at the page.tsx to see how CodeRunner is used
cat -n code-runner/src/app/page.tsx

Repository: Lightprotocol/docs-v2

Length of output: 661


🏁 Script executed:

# Search for all usages of CodeRunner component
rg -n "CodeRunner\|CodeTab" code-runner/src --type-list | head -20

Repository: Lightprotocol/docs-v2

Length of output: 454


🏁 Script executed:

# Check if there are any other pages that use CodeRunner
find code-runner/src/app -name "page.tsx" -exec grep -l "CodeRunner\|highlightedHtml" {} \;

Repository: Lightprotocol/docs-v2

Length of output: 111


🏁 Script executed:

# Look for any direct assignment to highlightedHtml that might bypass highlightCode
rg -n "highlightedHtml\s*[:=]" code-runner/src -B 2 -A 2

Repository: Lightprotocol/docs-v2

Length of output: 1696


🏁 Script executed:

# Verify Shiki is properly imported and used
cat -n code-runner/src/lib/highlighter.ts

Repository: Lightprotocol/docs-v2

Length of output: 760


🏁 Script executed:

# Check the code-icon.tsx dangerouslySetInnerHTML usage for completeness
cat -n code-runner/src/components/code-icon.tsx | sed -n '50,60p'

Repository: Lightprotocol/docs-v2

Length of output: 398


🏁 Script executed:

# See the full context of code-icon.tsx
head -n 60 code-runner/src/components/code-icon.tsx

Repository: Lightprotocol/docs-v2

Length of output: 1374


Add a safety comment explaining that highlightedHtml is generated exclusively by Shiki.

The dangerouslySetInnerHTML usage here is safe because highlightedHtml originates exclusively from Shiki's codeToHtml() method, which properly escapes all user input. However, documenting this intent prevents future maintainers from inadvertently passing unsanitized HTML to this component.

Suggested safety comment
 function CodeBlock({ highlightedHtml }: { highlightedHtml: string }) {
   return (
+    // Safe: highlightedHtml is generated exclusively by Shiki syntax highlighter
+    // which properly escapes all user input. Never pass unsanitized HTML here.
     <div
       className="overflow-auto px-0 py-3 m-0 rounded-none bg-ch-background font-mono text-sm max-h-full selection:bg-ch-selection [&_pre]:bg-transparent [&_pre]:p-0 [&_pre]:m-0 [&_pre]:px-4 [&_code]:bg-transparent"
       style={{ direction: "ltr", textAlign: "left" }}
       dangerouslySetInnerHTML={{ __html: highlightedHtml }}
     />
   );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function CodeBlock({ highlightedHtml }: { highlightedHtml: string }) {
return (
<div
className="overflow-auto px-0 py-3 m-0 rounded-none bg-ch-background font-mono text-sm max-h-full selection:bg-ch-selection [&_pre]:bg-transparent [&_pre]:p-0 [&_pre]:m-0 [&_pre]:px-4 [&_code]:bg-transparent"
style={{ direction: "ltr", textAlign: "left" }}
dangerouslySetInnerHTML={{ __html: highlightedHtml }}
/>
);
}
function CodeBlock({ highlightedHtml }: { highlightedHtml: string }) {
return (
// Safe: highlightedHtml is generated exclusively by Shiki syntax highlighter
// which properly escapes all user input. Never pass unsanitized HTML here.
<div
className="overflow-auto px-0 py-3 m-0 rounded-none bg-ch-background font-mono text-sm max-h-full selection:bg-ch-selection [&_pre]:bg-transparent [&_pre]:p-0 [&_pre]:m-0 [&_pre]:px-4 [&_code]:bg-transparent"
style={{ direction: "ltr", textAlign: "left" }}
dangerouslySetInnerHTML={{ __html: highlightedHtml }}
/>
);
}
🧰 Tools
🪛 ast-grep (0.40.3)

[warning] 159-159: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)

[error] 160-160: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🤖 Prompt for AI Agents
In code-runner/src/components/code-runner.tsx around lines 155 to 163, add a
concise safety comment above the CodeBlock component (or directly above the
dangerouslySetInnerHTML usage) stating that highlightedHtml is generated
exclusively by Shiki's codeToHtml() and is properly escaped/escaped-by-Shiki, so
the use of dangerouslySetInnerHTML is intentional and safe; keep the comment
short, include the Shiki source and function name (codeToHtml) and a note to
avoid passing unsanitized HTML into this prop.

Comment on lines +22 to +26
onClick={() => {
navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), 1200);
}}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add error handling and cleanup for clipboard operation.

The clipboard write operation can fail (due to permissions, HTTPS requirement, or browser support) with no error handling. Additionally, the setTimeout is not cleaned up if the component unmounts during the 1.2-second window.

🔎 Proposed fix with error handling and cleanup
 export function CopyButton({
   text,
   className,
 }: {
   text: string;
   className?: string;
 }) {
   const [copied, setCopied] = useState(false);
+  const [error, setError] = useState(false);
 
   return (
     <button
       className={cn(
         "hover:text-ch-tab-active-foreground -mx-1 p-1 rounded hidden sm:block",
         className
       )}
-      onClick={() => {
-        navigator.clipboard.writeText(text);
-        setCopied(true);
-        setTimeout(() => setCopied(false), 1200);
+      onClick={async () => {
+        try {
+          await navigator.clipboard.writeText(text);
+          setCopied(true);
+          setError(false);
+          setTimeout(() => setCopied(false), 1200);
+        } catch (err) {
+          console.error("Failed to copy:", err);
+          setError(true);
+          setTimeout(() => setError(false), 1200);
+        }
       }}
       aria-label="Copy to clipboard"
     >
-      {copied ? (
+      {error ? (
+        <span className="text-red-500">✕</span>
+      ) : copied ? (
         <Check size={16} className="block" />
       ) : (
         <Copy size={16} className="block" />
       )}
     </button>
   );
 }

For the timeout cleanup, consider using useEffect:

useEffect(() => {
  if (copied || error) {
    const timer = setTimeout(() => {
      setCopied(false);
      setError(false);
    }, 1200);
    return () => clearTimeout(timer);
  }
}, [copied, error]);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In code-runner/src/components/copy-button.tsx around lines 22 to 26, the
clipboard write is called without error handling and the setTimeout has no
cleanup; wrap navigator.clipboard.writeText(text) in an async try/catch (or
Promise .then/.catch) and only call setCopied(true) on success, set an error
state (e.g., setError(true) or similar) on failure and include a user-friendly
message for unsupported clipboard API, and replace the inline setTimeout with a
cleanupable timer managed by useEffect or a ref so the timer is cleared on
unmount (start the timer when copied or error becomes true and clear it in the
effect’s cleanup).

Comment on lines +33 to +64
const rpc = createRpc();
const payer = Keypair.generate();
await rpc.requestAirdrop(payer.publicKey, 10e9);

const owner = Keypair.generate();
await rpc.requestAirdrop(owner.publicKey, 1e9);

const mintAuthority = Keypair.generate();
const mintKeypair = Keypair.generate();
const { mint } = await createMint(
rpc,
payer,
mintAuthority.publicKey,
9,
mintKeypair,
);

await mintTo(rpc, payer, mint, owner.publicKey, mintAuthority, bn(1000));

// Get c-token ATA address
const ctokenAta = getAssociatedTokenAddressInterface(mint, owner.publicKey);

// Load compressed tokens to hot balance
// Creates ATA if needed, returns null if nothing to load
const signature = await loadAta(rpc, ctokenAta, owner, mint, payer);

if (signature) {
console.log("Loaded tokens to hot balance");
console.log("Transaction:", signature);
} else {
console.log("Nothing to load");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg -n "requestAirdrop" --type ts -A 3 -B 1

Repository: Lightprotocol/docs-v2

Length of output: 871


Add confirmation waits after airdrop transactions to match codebase standards.

The sequential airdrops on lines 35 and 38 are awaited but not explicitly confirmed before subsequent operations. The established pattern in the codebase (seen in code-runner/src/app/embed/create-mint/page.tsx) confirms airdrop transactions immediately:

const sig = await rpc.requestAirdrop(payer.publicKey, 10e9);
await rpc.confirmTransaction(sig);

Add similar confirmation waits to prevent potential "insufficient funds" errors during execution. This is especially important if these examples will be executed by the new code runner infrastructure.

🤖 Prompt for AI Agents
In light-token/toolkits/for-streaming-tokens.mdx around lines 33 to 64, the two
awaited requestAirdrop calls are not followed by explicit transaction
confirmations which can lead to "insufficient funds" races; capture each airdrop
signature (e.g., sigPayer, sigOwner) and call await rpc.confirmTransaction(...)
for each signature immediately after its requestAirdrop, ensuring the
confirmations complete before continuing with mint creation and mintTo.

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.

2 participants