Ignore invalid SMTP EHLO response lines

This commit is contained in:
cketti 2022-05-22 21:52:18 +02:00
parent c78d70719b
commit 60bf78d3f0
2 changed files with 54 additions and 46 deletions

View file

@ -115,20 +115,25 @@ internal class SmtpResponseParser(
private fun parseEhloLine(ehloLine: String, keywords: MutableMap<String, List<String>>) {
val parts = ehloLine.split(" ")
val keyword = checkAndNormalizeEhloKeyword(parts[0])
val parameters = checkEhloParameters(parts)
if (keywords.containsKey(keyword)) {
parserError("Same EHLO keyword present in more than one response line")
try {
val keyword = checkAndNormalizeEhloKeyword(parts[0])
val parameters = checkEhloParameters(parts)
if (keywords.containsKey(keyword)) {
parserError("Same EHLO keyword present in more than one response line", logging = false)
}
keywords[keyword] = parameters
} catch (e: SmtpResponseParserException) {
logger.log(e, "Ignoring EHLO keyword line: $ehloLine")
}
keywords[keyword] = parameters
}
private fun checkAndNormalizeEhloKeyword(text: String): String {
val keyword = text.uppercase()
if (!keyword[0].isCapitalAlphaDigit() || keyword.any { !it.isCapitalAlphaDigit() && it != DASH }) {
parserError("EHLO keyword contains invalid character")
parserError("EHLO keyword contains invalid character", logging = false)
}
return keyword
@ -138,9 +143,9 @@ internal class SmtpResponseParser(
for (i in 1..parts.lastIndex) {
val parameter = parts[i]
if (parameter.isEmpty()) {
parserError("EHLO parameter must not be empty")
parserError("EHLO parameter must not be empty", logging = false)
} else if (parameter.any { it.code !in 33..126 }) {
parserError("EHLO parameter contains invalid character")
parserError("EHLO parameter contains invalid character", logging = false)
}
}

View file

@ -145,26 +145,36 @@ class SmtpResponseParserTest {
}
@Test
fun `read EHLO response with invalid keyword`() {
fun `read EHLO response with invalid keywords`() {
val input = """
250-smtp.domain.example
250 KEY:WORD
250-SIZE 52428800
250-8BITMIME
250-PIPELINING
250-PIPE_CONNECT
250-AUTH=PLAIN
250 HELP
""".toPeekableInputStream()
val parser = SmtpResponseParser(logger, input)
assertFailsWithMessage("EHLO keyword contains invalid character") {
parser.readHelloResponse()
val response = parser.readHelloResponse()
assertType<SmtpHelloResponse.Hello>(response) { hello ->
assertThat(hello.keywords.keys).containsExactly(
"SIZE",
"8BITMIME",
"PIPELINING",
"HELP"
)
}
assertThat(logger.logEntries).containsExactly(
LogEntry(
throwable = null,
message = """
SMTP response data on parser error:
250-smtp.domain.example
250 KEY:WORD
""".trimIndent()
)
assertThat(logger.logEntries.map { it.message }).containsExactly(
"Ignoring EHLO keyword line: PIPE_CONNECT",
"Ignoring EHLO keyword line: AUTH=PLAIN"
)
assertThat(logger.logEntries.map { it.throwable?.message }).containsExactly(
"EHLO keyword contains invalid character",
"EHLO keyword contains invalid character"
)
}
@ -176,44 +186,37 @@ class SmtpResponseParserTest {
""".toPeekableInputStream()
val parser = SmtpResponseParser(logger, input)
assertFailsWithMessage("EHLO parameter must not be empty") {
parser.readHelloResponse()
val response = parser.readHelloResponse()
assertType<SmtpHelloResponse.Hello>(response) { hello ->
assertThat(hello.keywords.keys).isEmpty()
}
assertThat(logger.logEntries).containsExactly(
LogEntry(
throwable = null,
message = """
SMTP response data on parser error:
250-smtp.domain.example
250 KEYWORD${" "}
""".trimIndent()
)
)
assertThat(logger.logEntries).hasSize(1)
assertThat(logger.logEntries.first().throwable).hasMessageThat().isEqualTo("EHLO parameter must not be empty")
assertThat(logger.logEntries.first().message).isEqualTo("Ignoring EHLO keyword line: KEYWORD ")
}
@Test
fun `read EHLO response with invalid parameter`() {
val input = """
250-smtp.domain.example
250-8BITMIME
250 KEYWORD para${"\t"}meter
""".toPeekableInputStream()
val parser = SmtpResponseParser(logger, input)
assertFailsWithMessage("EHLO parameter contains invalid character") {
parser.readHelloResponse()
val response = parser.readHelloResponse()
assertType<SmtpHelloResponse.Hello>(response) { hello ->
assertThat(hello.keywords.keys).containsExactly("8BITMIME")
}
assertThat(logger.logEntries).containsExactly(
LogEntry(
throwable = null,
message = """
SMTP response data on parser error:
250-smtp.domain.example
250 KEYWORD para${"\t"}meter
""".trimIndent()
)
)
assertThat(logger.logEntries).hasSize(1)
assertThat(logger.logEntries.first().throwable)
.hasMessageThat().isEqualTo("EHLO parameter contains invalid character")
assertThat(logger.logEntries.first().message)
.isEqualTo("Ignoring EHLO keyword line: KEYWORD para${"\t"}meter")
}
@Test