Skip to content

Commit aaa2833

Browse files
committed
Merge tag '0.3.3'
Upyo 0.3.3
2 parents 3c5cbbe + 07b8162 commit aaa2833

File tree

3 files changed

+43
-0
lines changed

3 files changed

+43
-0
lines changed

CHANGES.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ Version 0.4.0
77
To be released.
88

99

10+
Version 0.3.3
11+
-------------
12+
13+
Released on December 13, 2025.
14+
15+
### @upyo/core
16+
17+
- Fixed a potential SMTP command injection vulnerability.
18+
19+
Email addresses are now validated to prevent newline characters (`\r` or
20+
`\n`), which could be used to inject malicious SMTP commands.
21+
22+
1023
Version 0.3.2
1124
-------------
1225

@@ -95,6 +108,19 @@ Released on September 16, 2025.
95108
[#9]: https://github.com/dahlia/upyo/issues/9
96109

97110

111+
Version 0.2.4
112+
-------------
113+
114+
Released on December 13, 2025.
115+
116+
### @upyo/core
117+
118+
- Fixed a potential SMTP command injection vulnerability.
119+
120+
Email addresses are now validated to prevent newline characters (`\r` or
121+
`\n`), which could be used to inject malicious SMTP commands.
122+
123+
98124
Version 0.2.3
99125
-------------
100126

packages/core/src/address.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,4 +291,16 @@ test("parseAddress() - boundary and malformed cases", () => {
291291
assert.ok(invalidIp === undefined || typeof invalidIp === "object");
292292
});
293293

294+
test("parseAddress() - should not allow newlines", () => {
295+
// Test for SMTP command injection vulnerabilities
296+
assert.strictEqual(parseAddress("test@example.com\r\nVRFY root"), undefined);
297+
assert.strictEqual(parseAddress("test@example.com\nVRFY root"), undefined);
298+
assert.strictEqual(parseAddress("test@example.com\rVRFY root"), undefined);
299+
assert.strictEqual(
300+
parseAddress("sender@example.com>\r\nVRFY root"),
301+
undefined,
302+
);
303+
assert.strictEqual(parseAddress('"test\r\n"@example.com'), undefined);
304+
});
305+
294306
// cSpell: ignore reparsed punycode

packages/core/src/address.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ function isValidEmail(email: string): boolean {
125125
return false;
126126
}
127127

128+
// Prevent SMTP command injection by disallowing newlines
129+
if (email.includes("\r") || email.includes("\n")) {
130+
return false;
131+
}
132+
128133
// Find the @ symbol that separates local and domain parts
129134
// If the local part is quoted, we need to find the @ after the closing quote
130135
let atIndex = -1;

0 commit comments

Comments
 (0)