From 7eb03e6fad2ce01f98e2703b2b610f98a2195cc1 Mon Sep 17 00:00:00 2001 From: Jeremy Klein Date: Fri, 20 Sep 2024 18:16:23 -0700 Subject: [PATCH 1/2] Sanitize user names when parsing lnurlp urls --- .../kotlin/me/uma/protocol/LnurlpRequest.kt | 21 ++++++++++++------- .../kotlin/me/uma/protocol/PayRequest.kt | 1 + .../src/commonTest/kotlin/me/uma/UmaTests.kt | 10 +++++++++ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/uma-sdk/src/commonMain/kotlin/me/uma/protocol/LnurlpRequest.kt b/uma-sdk/src/commonMain/kotlin/me/uma/protocol/LnurlpRequest.kt index b7cd2e9..72ef927 100644 --- a/uma-sdk/src/commonMain/kotlin/me/uma/protocol/LnurlpRequest.kt +++ b/uma-sdk/src/commonMain/kotlin/me/uma/protocol/LnurlpRequest.kt @@ -47,14 +47,14 @@ data class LnurlpRequest( host = receiverAddressParts[1], pathSegments = "/.well-known/lnurlp/${receiverAddressParts[0]}".split("/"), parameters = - Parameters.build { - vaspDomain?.let { append("vaspDomain", it) } - nonce?.let { append("nonce", it) } - signature?.let { append("signature", it) } - umaVersion?.let { append("umaVersion", it) } - timestamp?.let { append("timestamp", it.toString()) } - isSubjectToTravelRule?.let { append("isSubjectToTravelRule", it.toString()) } - }, + Parameters.build { + vaspDomain?.let { append("vaspDomain", it) } + nonce?.let { append("nonce", it) } + signature?.let { append("signature", it) } + umaVersion?.let { append("umaVersion", it) } + timestamp?.let { append("timestamp", it.toString()) } + isSubjectToTravelRule?.let { append("isSubjectToTravelRule", it.toString()) } + }, ).build() return url.toString() } @@ -102,6 +102,11 @@ data class LnurlpRequest( } else { "" } + val username = urlBuilder.pathSegments[3] + val usernameRegex = "^[A-Za-z0-9._$+-]+$".toRegex() + if (!username.matches(usernameRegex)) { + throw IllegalArgumentException("Invalid username. Only alphanumeric characters and ._$+- are allowed.") + } val receiverAddress = "${urlBuilder.pathSegments[3]}@${urlBuilder.host}$port" val vaspDomain = urlBuilder.parameters["vaspDomain"] val nonce = urlBuilder.parameters["nonce"] diff --git a/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt b/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt index f1a0dae..508835a 100644 --- a/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt +++ b/uma-sdk/src/commonMain/kotlin/me/uma/protocol/PayRequest.kt @@ -82,6 +82,7 @@ sealed interface PayRequest { payerData, requestedPayeeData, comment, + invoiceUUID ) } } diff --git a/uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt b/uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt index 52d120c..7abe7d5 100644 --- a/uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt +++ b/uma-sdk/src/commonTest/kotlin/me/uma/UmaTests.kt @@ -227,6 +227,16 @@ class UmaTests { assertEquals(payreq, decodedPayReq) } + @Test + fun `test parse Lnurlp URL with invalid user`() { + val umaLnurlpQuery = + "https://example.com/.well-known/lnurlp/\$bob(?vaspDomain=example.com&nonce=123&signature=123&" + + "isSubjectToTravelRule=true×tamp=123&umaVersion=1.0" + assertThrows { + UmaProtocolHelper().parseLnurlpRequest(umaLnurlpQuery) + } + } + @Test fun `test isUmaLnurlpQuery future-proofing`() { val umaLnurlpQuery = From e4444a4624c4efd9162a675d686e5e0d3113ad32 Mon Sep 17 00:00:00 2001 From: Jeremy Klein Date: Fri, 20 Sep 2024 18:17:40 -0700 Subject: [PATCH 2/2] undo weird autoformatter decision --- .../kotlin/me/uma/protocol/LnurlpRequest.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/uma-sdk/src/commonMain/kotlin/me/uma/protocol/LnurlpRequest.kt b/uma-sdk/src/commonMain/kotlin/me/uma/protocol/LnurlpRequest.kt index 72ef927..6aec31f 100644 --- a/uma-sdk/src/commonMain/kotlin/me/uma/protocol/LnurlpRequest.kt +++ b/uma-sdk/src/commonMain/kotlin/me/uma/protocol/LnurlpRequest.kt @@ -47,14 +47,14 @@ data class LnurlpRequest( host = receiverAddressParts[1], pathSegments = "/.well-known/lnurlp/${receiverAddressParts[0]}".split("/"), parameters = - Parameters.build { - vaspDomain?.let { append("vaspDomain", it) } - nonce?.let { append("nonce", it) } - signature?.let { append("signature", it) } - umaVersion?.let { append("umaVersion", it) } - timestamp?.let { append("timestamp", it.toString()) } - isSubjectToTravelRule?.let { append("isSubjectToTravelRule", it.toString()) } - }, + Parameters.build { + vaspDomain?.let { append("vaspDomain", it) } + nonce?.let { append("nonce", it) } + signature?.let { append("signature", it) } + umaVersion?.let { append("umaVersion", it) } + timestamp?.let { append("timestamp", it.toString()) } + isSubjectToTravelRule?.let { append("isSubjectToTravelRule", it.toString()) } + }, ).build() return url.toString() }