0
0
mirror of https://github.com/thunderbird/thunderbird-android.git synced 2024-09-20 12:12:15 +02:00

Merge pull request #6878 from thundernest/check_UriParser_startPos_argument

Explicitly check `startPos` parameter in `UriParser.parseUri()` implementations
This commit is contained in:
cketti 2023-05-08 11:37:39 +02:00 committed by GitHub
commit d407f18b11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 351 additions and 312 deletions

View File

@ -9,6 +9,8 @@ import java.util.regex.Pattern
*/
class GenericUriParser : UriParser {
override fun parseUri(text: CharSequence, startPos: Int): UriMatch? {
require(startPos in text.indices) { "Invalid 'startPos' value" }
val matcher = PATTERN.matcher(text)
if (!matcher.find(startPos) || matcher.start() != startPos) return null

View File

@ -11,6 +11,8 @@ import kotlin.math.min
*/
internal class HttpUriParser : UriParser {
override fun parseUri(text: CharSequence, startPos: Int): UriMatch? {
require(startPos in text.indices) { "Invalid 'startPos' value" }
val matchResult = SCHEME_REGEX.find(text, startPos) ?: return null
if (matchResult.range.first != startPos) return null

View File

@ -1,7 +1,10 @@
package com.fsck.k9.message.html
import assertk.assertThat
import assertk.assertions.hasMessage
import assertk.assertions.isEqualTo
import assertk.assertions.isFailure
import assertk.assertions.isInstanceOf
import kotlin.test.assertNotNull
import org.junit.Test
@ -62,6 +65,24 @@ class GenericUriParserTest {
assertUriValid("matrix:roomid/rid:example.org/event/lol823y4bcp3qo4?via=example2.org")
}
@Test
fun `negative 'startPos' value`() {
assertThat {
parser.parseUri("test", -1)
}.isFailure()
.isInstanceOf(IllegalArgumentException::class)
.hasMessage("Invalid 'startPos' value")
}
@Test
fun `out of bounds 'startPos' value`() {
assertThat {
parser.parseUri("test", 4)
}.isFailure()
.isInstanceOf(IllegalArgumentException::class)
.hasMessage("Invalid 'startPos' value")
}
private fun assertUriValid(input: String) {
val result = parser.parseUri(input, 0)

View File

@ -1,312 +0,0 @@
package com.fsck.k9.message.html;
import org.junit.Assert;
import org.junit.Test;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
public class HttpUriParserTest {
private final HttpUriParser parser = new HttpUriParser();
@Test
public void emptyUriIgnored() {
assertInvalidUri("http://");
}
@Test
public void emptyAuthorityIgnored() {
assertInvalidUri("http:///");
}
@Test
public void simpleDomain() {
assertValidUri("http://www.google.com");
}
@Test
public void simpleDomainWithHttps() {
assertValidUri("https://www.google.com");
}
@Test
public void simpleRtspUri() {
assertValidUri("rtsp://example.com/media.mp4");
}
@Test
public void invalidDomainIgnored() {
assertInvalidUri("http://-www.google.com");
}
@Test
public void domainWithTrailingSlash() {
assertValidUri("http://www.google.com/");
}
@Test
public void domainWithUserInfo() {
assertValidUri("http://test@google.com/");
}
@Test
public void domainWithFullUserInfo() {
assertValidUri("http://test:secret@google.com/");
}
@Test
public void domainWithoutWww() {
assertValidUri("http://google.com/");
}
@Test
public void query() {
assertValidUri("http://google.com/give/me/?q=mode&c=information");
}
@Test
public void fragment() {
assertValidUri("http://google.com/give/me#only-the-best");
}
@Test
public void queryAndFragment() {
assertValidUri("http://google.com/give/me/?q=mode&c=information#only-the-best");
}
@Test
public void ipv4Address() {
assertValidUri("http://127.0.0.1");
}
@Test
public void ipv4AddressWithTrailingSlash() {
assertValidUri("http://127.0.0.1/");
}
@Test
public void ipv4AddressWithEmptyPort() {
assertValidUri("http://127.0.0.1:");
}
@Test
public void ipv4AddressWithPort() {
assertValidUri("http://127.0.0.1:524/");
}
@Test
public void ipv6Address() {
assertValidUri("http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]");
}
@Test
public void ipv6AddressWithPort() {
assertValidUri("http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80");
}
@Test
public void ipv6AddressWithTrailingSlash() {
assertValidUri("http://[1080:0:0:0:8:800:200C:417A]/");
}
@Test
public void ipv6AddressWithEndCompression() {
assertValidUri("http://[3ffe:2a00:100:7031::1]");
}
@Test
public void ipv6AddressWithBeginCompression() {
assertValidUri("http://[1080::8:800:200C:417A]/");
}
@Test
public void ipv6AddressWithCompressionPort() {
assertValidUri("http://[::FFFF:129.144.52.38]:80/");
}
@Test
public void ipv6AddressWithPrependedCompression() {
assertValidUri("http://[::192.9.5.5]/");
}
@Test
public void ipv6AddressWithTrailingIp4AndPort() {
assertValidUri("http://[::192.9.5.5]:80/");
}
@Test
public void ipv6WithoutClosingSquareBracketIgnored() {
assertInvalidUri("http://[1080:0:0:0:8:80:200C:417A/");
}
@Test
public void ipv6InvalidClosingSquareBracketIgnored() {
assertInvalidUri("http://[1080:0:0:0:8:800:270C:417A/]");
}
@Test
public void domainWithTrailingSpace() {
String text = "http://google.com/ ";
UriMatch uriMatch = parser.parseUri(text, 0);
assertUriMatch("http://google.com/", uriMatch);
}
@Test
public void domainWithTrailingNewline() {
String text = "http://google.com/\n";
UriMatch uriMatch = parser.parseUri(text, 0);
assertUriMatch("http://google.com/", uriMatch);
}
@Test
public void domainWithTrailingAngleBracket() {
String text = "<http://google.com/>";
UriMatch uriMatch = parser.parseUri(text, 1);
assertUriMatch("http://google.com/", uriMatch, 1);
}
@Test
public void uriInMiddleAfterInput() {
String prefix = "prefix ";
String uri = "http://google.com/";
String text = prefix + uri;
UriMatch uriMatch = parser.parseUri(text, prefix.length());
assertUriMatch("http://google.com/", uriMatch, prefix.length());
}
@Test
public void uriInMiddleOfInput() {
String prefix = "prefix ";
String uri = "http://google.com/";
String postfix = " postfix";
String text = prefix + uri + postfix;
UriMatch uriMatch = parser.parseUri(text, prefix.length());
assertUriMatch("http://google.com/", uriMatch, prefix.length());
}
@Test
public void uriWrappedInParentheses() {
String input = "(https://domain.example/)";
UriMatch uriMatch = parser.parseUri(input, 1);
assertUriMatch("https://domain.example/", uriMatch, 1);
}
@Test
public void uriContainingParentheses() {
String input = "https://domain.example/(parentheses)";
UriMatch uriMatch = parser.parseUri(input, 0);
assertUriMatch("https://domain.example/(parentheses)", uriMatch, 0);
}
@Test
public void uriContainingParenthesesWrappedInParentheses() {
String input = "(https://domain.example/(parentheses))";
UriMatch uriMatch = parser.parseUri(input, 1);
assertUriMatch("https://domain.example/(parentheses)", uriMatch, 1);
}
@Test
public void uriEndingInDotAtEndOfText() {
String input = "URL: https://domain.example/path.";
UriMatch uriMatch = parser.parseUri(input, 5);
assertUriMatch("https://domain.example/path", uriMatch, 5);
}
@Test
public void uriEndingInDotWithAdditionalText() {
String input = "URL: https://domain.example/path. Some other text";
UriMatch uriMatch = parser.parseUri(input, 5);
assertUriMatch("https://domain.example/path", uriMatch, 5);
}
@Test
public void uriWrappedInAngleBracketsEndingInDot() {
String input = "URL: <https://domain.example/path.>";
UriMatch uriMatch = parser.parseUri(input, 6);
assertUriMatch("https://domain.example/path.", uriMatch, 6);
}
@Test
public void uriWrappedInParenthesesEndingInDot() {
String input = "URL: (https://domain.example/path.)";
UriMatch uriMatch = parser.parseUri(input, 6);
assertUriMatch("https://domain.example/path.", uriMatch, 6);
}
@Test
public void uriWrappedInParenthesesFollowedByADot() {
String input = "URL: (https://domain.example/path).";
UriMatch uriMatch = parser.parseUri(input, 6);
assertUriMatch("https://domain.example/path", uriMatch, 6);
}
@Test
public void uriWrappedInParenthesesFollowedByADotAndSomeOtherText() {
String input = "URL: (https://domain.example/path). Some other text";
UriMatch uriMatch = parser.parseUri(input, 6);
assertUriMatch("https://domain.example/path", uriMatch, 6);
}
@Test
public void uriWrappedInParenthesesFollowedByAQuestionMarkAndSomeOtherText() {
String input = "URL: (https://domain.example/path)? Some other text";
UriMatch uriMatch = parser.parseUri(input, 6);
assertUriMatch("https://domain.example/path", uriMatch, 6);
}
private void assertValidUri(String uri) {
UriMatch uriMatch = parser.parseUri(uri, 0);
assertUriMatch(uri, uriMatch);
}
private void assertUriMatch(String uri, UriMatch uriMatch) {
assertUriMatch(uri, uriMatch, 0);
}
private void assertUriMatch(String uri, UriMatch uriMatch, int offset) {
assertNotNull(uriMatch);
Assert.assertEquals(offset, uriMatch.getStartIndex());
Assert.assertEquals(uri.length() + offset, uriMatch.getEndIndex());
Assert.assertEquals(uri, uriMatch.getUri().toString());
}
private void assertInvalidUri(String uri) {
UriMatch uriMatch = parser.parseUri(uri, 0);
assertNull(uriMatch);
}
}

View File

@ -0,0 +1,326 @@
package com.fsck.k9.message.html
import assertk.assertThat
import assertk.assertions.hasMessage
import assertk.assertions.isEqualTo
import assertk.assertions.isFailure
import assertk.assertions.isInstanceOf
import assertk.assertions.isNotNull
import assertk.assertions.isNull
import org.junit.Test
class HttpUriParserTest {
private val parser = HttpUriParser()
@Test
fun `missing domain`() {
assertInvalidUri("http://")
}
@Test
fun `missing domain followed by slash`() {
assertInvalidUri("http:///")
}
@Test
fun `simple domain`() {
assertValidUri("http://www.google.com")
}
@Test
fun `simple domain with https`() {
assertValidUri("https://www.google.com")
}
@Test
fun `simple RTSP URI`() {
assertValidUri("rtsp://example.com/media.mp4")
}
@Test
fun `subdomain starting with invalid character`() {
assertInvalidUri("http://-www.google.com")
}
@Test
fun `domain with trailing slash`() {
assertValidUri("http://www.google.com/")
}
@Test
fun `domain with user info`() {
assertValidUri("http://test@google.com/")
}
@Test
fun `domain with full user info`() {
assertValidUri("http://test:secret@google.com/")
}
@Test
fun `domain without www`() {
assertValidUri("http://google.com/")
}
@Test
fun query() {
assertValidUri("http://google.com/give/me/?q=mode&c=information")
}
@Test
fun fragment() {
assertValidUri("http://google.com/give/me#only-the-best")
}
@Test
fun `query and fragment`() {
assertValidUri("http://google.com/give/me/?q=mode&c=information#only-the-best")
}
@Test
fun `IPv4 address`() {
assertValidUri("http://127.0.0.1")
}
@Test
fun `IPv4 address with trailing slash`() {
assertValidUri("http://127.0.0.1/")
}
@Test
fun `IPv4 address with empty port`() {
assertValidUri("http://127.0.0.1:")
}
@Test
fun `IPv4 address with port`() {
assertValidUri("http://127.0.0.1:524/")
}
@Test
fun `IPv6 address`() {
assertValidUri("http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]")
}
@Test
fun `IPv6 address with port`() {
assertValidUri("http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80")
}
@Test
fun `IPv6 address with trailing slash`() {
assertValidUri("http://[1080:0:0:0:8:800:200C:417A]/")
}
@Test
fun `IPv6 address with end compression`() {
assertValidUri("http://[3ffe:2a00:100:7031::1]")
}
@Test
fun `IPv6 address with begin compression`() {
assertValidUri("http://[1080::8:800:200C:417A]/")
}
@Test
fun `IPv6 address with compression port`() {
assertValidUri("http://[::FFFF:129.144.52.38]:80/")
}
@Test
fun `IPv6 address with prepended compression`() {
assertValidUri("http://[::192.9.5.5]/")
}
@Test
fun `IPv6 address with trailing IP4 and port`() {
assertValidUri("http://[::192.9.5.5]:80/")
}
@Test
fun `IPv6 without closing square bracket`() {
assertInvalidUri("http://[1080:0:0:0:8:80:200C:417A/")
}
@Test
fun `IPv6 invalid closing square bracket`() {
assertInvalidUri("http://[1080:0:0:0:8:800:270C:417A/]")
}
@Test
fun `domain with trailing space`() {
val text = "http://google.com/ "
val uriMatch = parser.parseUri(text, 0)
assertUriMatch("http://google.com/", uriMatch)
}
@Test
fun `domain with trailing newline`() {
val text = "http://google.com/\n"
val uriMatch = parser.parseUri(text, 0)
assertUriMatch("http://google.com/", uriMatch)
}
@Test
fun `domain with trailing angle bracket`() {
val text = "<http://google.com/>"
val uriMatch = parser.parseUri(text, 1)
assertUriMatch("http://google.com/", uriMatch, 1)
}
@Test
fun `URI at the end of input`() {
val prefix = "prefix "
val uri = "http://google.com/"
val text = prefix + uri
val uriMatch = parser.parseUri(text, prefix.length)
assertUriMatch("http://google.com/", uriMatch, prefix.length)
}
@Test
fun `URI in middle of input`() {
val prefix = "prefix "
val uri = "http://google.com/"
val postfix = " postfix"
val text = prefix + uri + postfix
val uriMatch = parser.parseUri(text, prefix.length)
assertUriMatch("http://google.com/", uriMatch, prefix.length)
}
@Test
fun `URI wrapped in parentheses`() {
val input = "(https://domain.example/)"
val uriMatch = parser.parseUri(input, 1)
assertUriMatch("https://domain.example/", uriMatch, 1)
}
@Test
fun `URI containing parentheses`() {
val input = "https://domain.example/(parentheses)"
val uriMatch = parser.parseUri(input, 0)
assertUriMatch("https://domain.example/(parentheses)", uriMatch, 0)
}
@Test
fun `URI containing parentheses wrapped in parentheses`() {
val input = "(https://domain.example/(parentheses))"
val uriMatch = parser.parseUri(input, 1)
assertUriMatch("https://domain.example/(parentheses)", uriMatch, 1)
}
@Test
fun `URI ending in dot at end of text`() {
val input = "URL: https://domain.example/path."
val uriMatch = parser.parseUri(input, 5)
assertUriMatch("https://domain.example/path", uriMatch, 5)
}
@Test
fun `URI ending in dot with additional text`() {
val input = "URL: https://domain.example/path. Some other text"
val uriMatch = parser.parseUri(input, 5)
assertUriMatch("https://domain.example/path", uriMatch, 5)
}
@Test
fun `URI wrapped in angle brackets ending in dot`() {
val input = "URL: <https://domain.example/path.>"
val uriMatch = parser.parseUri(input, 6)
assertUriMatch("https://domain.example/path.", uriMatch, 6)
}
@Test
fun `URI wrapped in parentheses ending in dot`() {
val input = "URL: (https://domain.example/path.)"
val uriMatch = parser.parseUri(input, 6)
assertUriMatch("https://domain.example/path.", uriMatch, 6)
}
@Test
fun `URI wrapped in parentheses followed by a dot`() {
val input = "URL: (https://domain.example/path)."
val uriMatch = parser.parseUri(input, 6)
assertUriMatch("https://domain.example/path", uriMatch, 6)
}
@Test
fun `URI wrapped in parentheses followed by a dot and some other text`() {
val input = "URL: (https://domain.example/path). Some other text"
val uriMatch = parser.parseUri(input, 6)
assertUriMatch("https://domain.example/path", uriMatch, 6)
}
@Test
fun `URI wrapped in parentheses followed by a question mark and some other text`() {
val input = "URL: (https://domain.example/path)? Some other text"
val uriMatch = parser.parseUri(input, 6)
assertUriMatch("https://domain.example/path", uriMatch, 6)
}
@Test
fun `negative 'startPos' value`() {
assertThat {
parser.parseUri("test", -1)
}.isFailure()
.isInstanceOf(IllegalArgumentException::class)
.hasMessage("Invalid 'startPos' value")
}
@Test
fun `out of bounds 'startPos' value`() {
assertThat {
parser.parseUri("test", 4)
}.isFailure()
.isInstanceOf(IllegalArgumentException::class)
.hasMessage("Invalid 'startPos' value")
}
private fun assertValidUri(uri: String) {
val uriMatch = parser.parseUri(uri, 0)
assertUriMatch(uri, uriMatch)
}
private fun assertUriMatch(uri: String, uriMatch: UriMatch?, offset: Int = 0) {
assertThat(uriMatch).isNotNull().isEqualTo(
UriMatch(
startIndex = offset,
endIndex = offset + uri.length,
uri = uri,
),
)
}
private fun assertInvalidUri(uri: String) {
val uriMatch = parser.parseUri(uri, 0)
assertThat(uriMatch).isNull()
}
}