Skip to content

Commit

Permalink
KTOR-917 KTOR-993 Prevent URI Encoding for Safe Chars (#3179)
Browse files Browse the repository at this point in the history
* KTOR-993 Add test to verify URI encoding

* KTOR-917 Keep some characters in cookie URI encoding according to RFC 3986

* Fix codestyle warnings
  • Loading branch information
e5l committed Oct 12, 2022
1 parent e1bfa58 commit 5ec968c
Show file tree
Hide file tree
Showing 12 changed files with 391 additions and 208 deletions.
4 changes: 2 additions & 2 deletions ktor-client/ktor-client-core/common/test/CookiesTest.kt
Expand Up @@ -44,11 +44,11 @@ class CookiesTest {

@Test
fun testCookiesAreRenderedWithSpaceInBetween() = testSuspend {
var storage = AcceptAllCookiesStorage()
val storage = AcceptAllCookiesStorage()
storage.addCookie("http://localhost/", Cookie("name1", "value1"))
storage.addCookie("http://localhost/", Cookie("name2", "value2"))
val feature = HttpCookies(storage, emptyList())
var builder = HttpRequestBuilder()
val builder = HttpRequestBuilder()

feature.sendCookiesWith(builder)

Expand Down
Expand Up @@ -194,7 +194,7 @@ class CookiesTest : ClientLoader() {
val response = httpResponse.bodyAsText()
val cookieStrings = response.split("; ").filter { it.isNotBlank() }
assertEquals(4, cookieStrings.size)
assertEquals("uri=first%2C+cookie", cookieStrings[0])
assertEquals("uri=first,+cookie", cookieStrings[0])
assertEquals("raw=first%2C+cookie", cookieStrings[1])
assertEquals("base64=Zmlyc3QsIGNvb2tpZQ==", cookieStrings[2])
assertEquals("dquotes=\"first, cookie\"", cookieStrings[3])
Expand Down
2 changes: 1 addition & 1 deletion ktor-http/common/src/io/ktor/http/Cookie.kt
Expand Up @@ -183,7 +183,7 @@ public fun encodeCookieValue(value: String, encoding: CookieEncoding): String =
else -> value
}
CookieEncoding.BASE64_ENCODING -> value.encodeBase64()
CookieEncoding.URI_ENCODING -> value.encodeURLQueryComponent(encodeFull = true, spaceToPlus = true)
CookieEncoding.URI_ENCODING -> value.encodeURLQueryComponent(encodeFull = false, spaceToPlus = true)
}

/**
Expand Down
2 changes: 1 addition & 1 deletion ktor-http/common/test/io/ktor/tests/http/CodecTest.kt
Expand Up @@ -141,7 +141,7 @@ class CodecTest {
mapOf(
"a" to listOf("b", "c", "d"),
"1" to listOf("2"),
"x" to listOf("y", "z"),
"x" to listOf("y", "z")
).entries.formUrlEncodeTo(result)

assertEquals("a=b&a=c&a=d&1=2&x=y&x=z", result.toString())
Expand Down
110 changes: 110 additions & 0 deletions ktor-http/common/test/io/ktor/tests/http/UrlEncodeTest.kt
@@ -0,0 +1,110 @@
/*
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package io.ktor.tests.http

import io.ktor.http.*
import kotlin.test.*

class UrlEncodeTest {

@Test
fun testUrlEncodePathKeepDigits() {
assertEquals("0123456789", "0123456789".encodeURLPath())
}

@Test
fun testUrlEncodeQueryComponentKeepDigits() {
assertEquals("0123456789", "0123456789".encodeURLQueryComponent())
}

@Test
fun testUrlKeepDigitsInPath() {
assertEquals("/0123456789/", Url("http://x.com/0123456789/").encodedPath)
}

@Test
fun testUrlEncodePathKeepLetters() {
assertEquals(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".encodeURLPath()
)
}

@Test
fun testUrlEncodeQueryComponentKeepLetters() {
assertEquals(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".encodeURLQueryComponent()
)
}

@Test
fun testUrlKeepLettersInPath() {
assertEquals(
"/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/",
Url("http://x.com/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/").encodedPath
)
}

@Test
fun testUrlEncodePathKeepHyphen() {
assertEquals("-", "-".encodeURLPath())
}

@Test
fun testUrlEncodeQueryComponentKeepHyphen() {
assertEquals("-", "-".encodeURLQueryComponent())
}

@Test
fun testUrlKeepHyphenInPath() {
assertEquals("/-/", Url("http://x.com/-/").encodedPath)
}

@Test
fun testUrlEncodePathKeepPeriod() {
assertEquals(".", ".".encodeURLPath())
}

@Test
fun testUrlEncodeQueryComponentKeepPeriod() {
assertEquals(".", ".".encodeURLQueryComponent())
}

@Test
fun testUrlKeepPeriodInPath() {
assertEquals("/./", Url("http://x.com/./").encodedPath)
}

@Test
fun testUrlEncodePathKeepUnderscore() {
assertEquals("_", "_".encodeURLPath())
}

@Test
fun testUrlEncodeQueryComponentKeepUnderscore() {
assertEquals("_", "_".encodeURLQueryComponent())
}

@Test
fun testUrlKeepUnderscoreInPath() {
assertEquals("/_/", Url("http://x.com/_/").encodedPath)
}

@Test
fun testUrlEncodePathKeepTilde() {
assertEquals("~", "~".encodeURLPath())
}

@Test
fun testUrlEncodeQueryComponentKeepTilde() {
assertEquals("~", "~".encodeURLQueryComponent())
}

@Test
fun testUrlKeepTildeInPath() {
assertEquals("/~/", Url("http://x.com/~/").encodedPath)
}
}
Expand Up @@ -242,7 +242,6 @@ class MyCalculatorPlugin {
): MyCalculatorPlugin {
return MyCalculatorPlugin()
}

}

fun add(x: Int, y: Int): Int {
Expand Down
@@ -0,0 +1,32 @@
/*
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package io.ktor.tests.server.plugins

import io.ktor.http.*
import kotlin.test.*

class Base64EncodingCookiesTest {
@Test
fun `no bad characters`() {
testEncode("YWJj", "abc")
}

@Test
fun `space inside`() {
testEncode("YWJjIDEyMw==", "abc 123")
}

@Test
fun `equals inside`() {
testEncode("YWJjPTEyMw==", "abc=123")
}

private fun testEncode(expected: String, value: String): String {
val encoded = encodeCookieValue(value, CookieEncoding.BASE64_ENCODING)
assertEquals(expected, encoded, "Encode failed")
assertEquals(value, decodeCookieValue(encoded, CookieEncoding.BASE64_ENCODING), "Decode failed")
return encoded
}
}

0 comments on commit 5ec968c

Please sign in to comment.