Skip to content

Commit d091258

Browse files
authored
Merge pull request #55 from robster7674/fix/nsf-regex-crash
fix(nsf): replace regex with isSha1Hex to eliminate ICU JNI crash
2 parents 6ed33d3 + fd5772c commit d091258

2 files changed

Lines changed: 44 additions & 4 deletions

File tree

Common/src/main/java/tk/glucodata/drivers/nightscout/NightscoutFollowerRegistry.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,19 @@ object NightscoutFollowerRegistry {
146146
}
147147
connection.setRequestProperty(
148148
"api-secret",
149-
if (trimmed.matches(Regex("^[0-9a-fA-F]{40}$"))) trimmed else sha1(trimmed)
149+
if (isSha1Hex(trimmed)) trimmed else sha1(trimmed)
150150
)
151151
}
152152

153+
// Replaces Regex("^[0-9a-fA-F]{40}$") to avoid repeated ICU JNI allocation on
154+
// the NightscoutFollower HandlerThread. On Samsung Android 15 with Scudo+MTE the
155+
// ReleaseIntArrayElements call inside MatcherNative_matchesImpl corrupts the chunk
156+
// header after many poll cycles, resulting in a fatal SIGABRT.
157+
private fun isSha1Hex(s: String): Boolean {
158+
if (s.length != 40) return false
159+
return s.all { it in '0'..'9' || it in 'a'..'f' || it in 'A'..'F' }
160+
}
161+
153162
private fun sha1(value: String): String =
154163
MessageDigest.getInstance("SHA-1")
155164
.digest(value.toByteArray(Charsets.UTF_8))

Common/src/test/java/tk/glucodata/drivers/nightscout/NightscoutFollowerRegistryTests.kt

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,45 @@ class NightscoutFollowerRegistryTests {
4949
}
5050

5151
@Test
52-
fun applyAuth_rawSha1Hex_sentAsApiSecret() {
53-
// 40-char hex = raw SHA1 (Nightscout stores api-secret this way)
54-
val sha1hex = "a".repeat(40)
52+
fun applyAuth_rawSha1Hex_lowercase_sentAsApiSecret() {
53+
val sha1hex = "a0b1c2d3e4f5a0b1c2d3e4f5a0b1c2d3e4f5a0b1"
5554
val headers = applyAuthToMock(sha1hex)
5655
assertEquals(sha1hex, headers["api-secret"])
5756
assertNull(headers["Authorization"])
5857
}
5958

59+
@Test
60+
fun applyAuth_rawSha1Hex_uppercase_sentAsApiSecret() {
61+
val sha1hex = "A0B1C2D3E4F5A0B1C2D3E4F5A0B1C2D3E4F5A0B1"
62+
val headers = applyAuthToMock(sha1hex)
63+
assertEquals(sha1hex, headers["api-secret"])
64+
assertNull(headers["Authorization"])
65+
}
66+
67+
@Test
68+
fun applyAuth_rawSha1Hex_mixedCase_sentAsApiSecret() {
69+
val sha1hex = "a0B1C2d3E4f5A0b1C2D3e4F5a0B1C2d3E4f5A0b1"
70+
val headers = applyAuthToMock(sha1hex)
71+
assertEquals(sha1hex, headers["api-secret"])
72+
assertNull(headers["Authorization"])
73+
}
74+
75+
@Test
76+
fun applyAuth_39charHex_hashed() {
77+
val notSha1 = "a".repeat(39)
78+
val headers = applyAuthToMock(notSha1)
79+
assertNotEquals(notSha1, headers["api-secret"])
80+
assertNotNull(headers["api-secret"])
81+
}
82+
83+
@Test
84+
fun applyAuth_40charNonHex_hashed() {
85+
val notHex = "a".repeat(39) + "g"
86+
val headers = applyAuthToMock(notHex)
87+
assertNotEquals(notHex, headers["api-secret"])
88+
assertNotNull(headers["api-secret"])
89+
}
90+
6091
@Test
6192
fun applyAuth_plainText_hashed() {
6293
val plain = "my-super-secret"

0 commit comments

Comments
 (0)