Skip to content

[security] Plugin URL Load renders OpenAPI schema content as HTML, enabling title-based XSS #6781

@areciah

Description

@areciah

Summary

The Plugin editor can execute attacker-controlled HTML/JS when loading an external OpenAPI schema URL.

loadFromUrl() fetches a remote schema and stores the raw schema string in plugin.content. The plugin title is then extracted from definition.info.title. In the edit modal, the same schema content is rendered with dangerouslySetInnerHTML, so HTML placed in fields such as info.title is parsed by the browser instead of being displayed as plain code.

Affected Code

  • app/components/plugin.tsx
    • loadFromUrl() fetches remote schema content and updates plugin.content: lines 88-117
    • plugin title itself is displayed safely via React text rendering: lines 195-202
    • vulnerable sink: dangerouslySetInnerHTML={{ __html: editingPlugin.content }}: lines 345-351
  • app/store/plugin.ts
    • parses plugin content via yaml.load(plugin.content): lines 55, 71-72
    • stores built-in plugin schema content and sets plugin.title from definition.info.title: lines 239-264

Reproduction

  1. Host the following OpenAPI schema as JSON:
{
  "openapi": "3.0.0",
  "info": {
    "title": "<img src=x onerror=alert('nextchat-plugin-title-xss')>",
    "version": "1.0.0"
  },
  "servers": [
    { "url": "https://example.com" }
  ],
  "paths": {
    "/ping": {
      "get": {
        "operationId": "ping",
        "responses": {
          "200": { "description": "ok" }
        }
      }
    }
  }
}
  1. Open the NextChat Plugin page.
  2. Create or edit a plugin.
  3. Paste the hosted schema URL into the Content URL field.
  4. Click Load.
  5. The schema content is rendered through dangerouslySetInnerHTML, and the payload inside info.title executes.

Expected Behavior

The OpenAPI schema should be displayed as plain text/code only. HTML inside schema fields such as info.title, info.version, summary, or description should never be parsed as DOM.

Actual Behavior

The schema content is inserted as HTML via dangerouslySetInnerHTML, allowing attacker-controlled schema content to execute script in the NextChat origin.

Security Impact

This is a DOM-based / stored client-side XSS issue.

An attacker who convinces a user to load a malicious plugin schema URL may execute JavaScript in the NextChat origin. Depending on deployment and stored settings, this may allow access to browser-side persisted data such as local settings, plugin auth tokens, custom endpoints, access codes, and API keys stored in local storage or IndexedDB.

  • CWE: CWE-79
  • Tentative severity: High
  • User interaction is required because the victim must load a malicious plugin schema.

Suggested Fix

Avoid dangerouslySetInnerHTML for schema display.

For example:

<code contentEditable={true} onBlur={onChangePlugin}>
  {editingPlugin.content}
</code>

A safer long-term approach would be to use a controlled textarea or code editor component and treat schema content strictly as text.

Additional hardening:

  • Validate definition.info.title and definition.info.version as plain strings.
  • Apply length limits to plugin metadata fields.
  • Ensure all schema-derived UI fields are rendered only as React text nodes.
  • Consider allowlisting or integrity checks for built-in plugin schema sources.

CVE Request

I would like to request CVE assignment for this vulnerability if the maintainers confirm it as a valid security issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions