@umutcansu/react-native-pinvault 2.8.2 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.9.0 — 2026-11-10
4
+
5
+ ### Security
6
+
7
+ - **Release builds take `requiredSignatures`, `recoveryPublicKeys` and
8
+ `requiredRecoverySignatures` only from the native security file.** They are
9
+ part of the library's trust-anchor fingerprint, and a changed fingerprint
10
+ resets the replay watermarks; a value JS could add and drop again reset
11
+ them at will. A value the file does not declare is refused
12
+ (`E_INVALID_CONFIG`) unless it is the default (1, no recovery keys). Copy
13
+ what JS passes into `pinvault_security.json`. Debug builds are unchanged.
14
+ - **An endpoint that is absolute in another spelling is refused**: whitespace
15
+ around the value (`" //host/x"`), a backslash anywhere and a bare `scheme:`
16
+ prefix, on Android and iOS.
17
+ - **A vault file's `endpoint` follows that rule too** and may not contain a
18
+ `..` path segment (plain or `%2e`); it was checked for `://` and a leading
19
+ slash only.
20
+ - **Breaking — release builds take `serverScope` and `clientCaPins` only from
21
+ the native security file.** `serverScope` names the library's config store;
22
+ a value JS could set where the file named none moved the store to a new
23
+ namespace without replay watermarks. A value the file does not declare is
24
+ refused (`E_INVALID_CONFIG`). Copy what JS passes into
25
+ `pinvault_security.json`. Debug builds are unchanged.
26
+ - **`PinVault.fetch`: a redirect to another host or port takes only `Accept`,
27
+ `Accept-Language` and `User-Agent` along, and never a body** (a 307/308 that
28
+ would repeat one is returned as the answer). On Android the plugin now
29
+ follows redirects itself (OkHttp kept every header but `Authorization`); on
30
+ iOS the library does.
31
+ - `PinVault.fetch` on iOS: `timeoutMs` is one limit for the whole exchange,
32
+ redirects included, as on Android.
33
+ - `ios.requireUnlockedDeviceAllowFallback`: the library's
34
+ `requireUnlockedDevice()` no longer falls back silently on iOS (it fails
35
+ with `UnlockedDeviceKeyRequiredException`); this is the opt-out, the twin of
36
+ `android.requireUnlockedDeviceAllowFallback`.
37
+ - Through the library: on iOS, numbers a server sends (attestation intervals,
38
+ a certificate's version field, a vault file's version header) can no longer
39
+ overflow and end the app; a blank vault file `signaturePublicKey` no longer
40
+ turns that file's verification off.
41
+ - Depends on PinVault 2.9.0 (Swift package tag `v2.9.0`).
42
+
3
43
  ## 2.8.2 — 2026-10-21
4
44
 
5
45
  ### Changed
package/README.md CHANGED
@@ -29,9 +29,9 @@ npm install @umutcansu/react-native-pinvault
29
29
  cd ios && pod install
30
30
  ```
31
31
 
32
- The native libraries come with it, at the package's own version: npm 2.8.2
33
- uses `io.github.umutcansu:pinvault:2.8.2` from Maven Central and the Swift
34
- package at tag `v2.8.2`. The New Architecture must stay on (React Native's
32
+ The native libraries come with it, at the package's own version: npm 2.9.0
33
+ uses `io.github.umutcansu:pinvault:2.9.0` from Maven Central and the Swift
34
+ package at tag `v2.9.0`. The New Architecture must stay on (React Native's
35
35
  default since 0.76).
36
36
 
37
37
  **iOS.** The pod depends on the PinVault Swift package through React Native's
@@ -4,7 +4,7 @@ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
4
 
5
5
  # PinVault for iOS is a Swift package (Package.swift at the repository root,
6
6
  # product "PinVault"). React Native's spm_dependency adds it to the Pods project:
7
- # - consumers: the git URL at the tag of this package's version (v2.8.2);
7
+ # - consumers: the git URL at the tag of this package's version (v2.9.0);
8
8
  # - development: PINVAULT_IOS_PACKAGE_PATH = a PinVault checkout (the sample's
9
9
  # Podfile sets it to this repository), the counterpart of pinvault.localPath.
10
10
  pinvault_url = "https://github.com/umutcansu/PinVault.git"
@@ -9,7 +9,7 @@ buildscript {
9
9
  kotlinVersion: "2.2.0",
10
10
  minSdkVersion: 24,
11
11
  compileSdkVersion: 36,
12
- pinvaultVersion: "2.8.2",
12
+ pinvaultVersion: "2.9.0",
13
13
  ]
14
14
  ext.pinvaultRnExt = { prop ->
15
15
  rootProject.ext.has(prop) ? rootProject.ext.get(prop) : PinVaultRN[prop]
@@ -202,12 +202,38 @@ internal object ConfigParser {
202
202
  */
203
203
  private fun relEndpoint(f: Fields, key: String, max: Int): String? {
204
204
  val v = f.string(key, max) ?: return null
205
- if (v.contains("://") || v.startsWith("/") || v.startsWith("\\")) {
205
+ // The URL parser skips leading whitespace and reads `\` as `/`: " //host/x"
206
+ // and "https:\\host/x" passed a check that only looked at the first character.
207
+ if (v != v.trim() || v.contains('\\') || v.contains("://") || v.startsWith("/") || SCHEME_PREFIX.containsMatchIn(v)) {
206
208
  throw BridgeInputException("${f.path}.$key: must be a relative path, not an absolute URL")
207
209
  }
208
210
  return v
209
211
  }
210
212
 
213
+ /** `scheme:` at the start of a value: an absolute URL however its slashes are written. */
214
+ private val SCHEME_PREFIX = Regex("^[A-Za-z][A-Za-z0-9+.-]*:")
215
+
216
+ /**
217
+ * A vault file's endpoint: required, held to the same rule as the block's
218
+ * endpoints ([relEndpoint] — it used to be checked for `://` and a leading
219
+ * slash only, so " //host/x" or "https:\\host/x" sent the file request and
220
+ * the file's token to another host), and without a `..` segment: the
221
+ * endpoint is resolved against the block's URL, and `..` would climb out of
222
+ * the path the app's native side declared for that Config API.
223
+ */
224
+ private fun vaultEndpoint(f: Fields): String {
225
+ val endpoint = relEndpoint(f, "endpoint", 512) ?: throw BridgeInputException("${f.path}.endpoint: required")
226
+ if (hasParentSegment(endpoint)) {
227
+ throw BridgeInputException("${f.path}.endpoint: must not contain a '..' path segment")
228
+ }
229
+ return endpoint
230
+ }
231
+
232
+ /** True when the path of [endpoint] has a `..` segment, written plainly or as `%2e` (URL parsers read both). */
233
+ internal fun hasParentSegment(endpoint: String): Boolean =
234
+ endpoint.substringBefore('?').substringBefore('#').split('/')
235
+ .any { it.replace("%2e", ".", ignoreCase = true) == ".." }
236
+
211
237
  fun hostPin(p: Fields): HostPin {
212
238
  val hostname = p.requireString("hostname", 255)
213
239
  val pins = p.stringList("sha256", MAX_PINS_PER_HOST, 128) ?: throw BridgeInputException("${p.path}.sha256: required")
@@ -370,10 +396,7 @@ internal object ConfigParser {
370
396
  if (!VAULT_KEY.matches(key) || key.all { it == '.' }) {
371
397
  throw BridgeInputException("${f.path}.key: must match [A-Za-z0-9._-]{1,64} and not be only dots")
372
398
  }
373
- val endpoint = f.requireString("endpoint", 512)
374
- if (endpoint.contains("://") || endpoint.startsWith("/") || endpoint.startsWith("\\")) {
375
- throw BridgeInputException("${f.path}.endpoint: must be a relative path, not an absolute URL")
376
- }
399
+ val endpoint = vaultEndpoint(f)
377
400
  val jsSignatureKey = f.string("signaturePublicKey", KEY_LENGTH, multiline = true)
378
401
  val updateWithPins = f.bool("updateWithPins")
379
402
  val storage = f.enum("storage", StorageStrategy.values())
@@ -349,15 +349,31 @@ internal object SecurityPolicy {
349
349
  return nativeValue
350
350
  }
351
351
 
352
+ // The signing keys' threshold, the recovery keys and theirs are part of the
353
+ // library's trust-anchor fingerprint, and a changed fingerprint resets the
354
+ // replay watermarks. In a release build they are the file's alone: a value
355
+ // the bundle could add and drop again would reset them at will (a default — 1,
356
+ // no recovery keys — changes nothing and is let through).
357
+ // `serverScope` names the library's config store: another scope is another
358
+ // namespace, which starts with no watermarks at all — a bundle that sets one
359
+ // makes the device accept a replayed old config. `clientCaPins` decide which
360
+ // CA may issue the device's identity. Both follow the same rule.
361
+ fun <T : Any> anchorOnly(key: String, nativeValue: T?, jsValue: T?, isDefault: (T) -> Boolean): T? {
362
+ if (release && nativeValue == null && jsValue != null && !isDefault(jsValue)) throw BridgeInputException(
363
+ "$path.$key: a release build takes it only from the app's native security file (${native.source}); " +
364
+ "declare $key for Config API '$id' there")
365
+ return jsValue
366
+ }
367
+
352
368
  return SecurityFields(
353
369
  bootstrapPins = pins,
354
370
  oneKey = oneKey,
355
371
  keys = keys,
356
- requiredSignatures = one("requiredSignatures", block.requiredSignatures, js.requiredSignatures),
357
- recoveryKeys = list("recoveryPublicKeys", block.recoveryPublicKeys, js.recoveryKeys),
358
- requiredRecovery = one("requiredRecoverySignatures", block.requiredRecoverySignatures, js.requiredRecovery),
359
- serverScope = one("serverScope", block.serverScope, js.serverScope),
360
- clientCaPins = list("clientCaPins", block.clientCaPins, js.clientCaPins),
372
+ requiredSignatures = one("requiredSignatures", block.requiredSignatures, anchorOnly("requiredSignatures", block.requiredSignatures, js.requiredSignatures) { it == 1 }),
373
+ recoveryKeys = list("recoveryPublicKeys", block.recoveryPublicKeys, anchorOnly("recoveryPublicKeys", block.recoveryPublicKeys, js.recoveryKeys) { it.isEmpty() }),
374
+ requiredRecovery = one("requiredRecoverySignatures", block.requiredRecoverySignatures, anchorOnly("requiredRecoverySignatures", block.requiredRecoverySignatures, js.requiredRecovery) { it == 1 }),
375
+ serverScope = one("serverScope", block.serverScope, anchorOnly("serverScope", block.serverScope, js.serverScope) { false }),
376
+ clientCaPins = list("clientCaPins", block.clientCaPins, anchorOnly("clientCaPins", block.clientCaPins, js.clientCaPins) { it.isEmpty() }),
361
377
  // The file is the source of truth for the relaxations too.
362
378
  allowUnsigned = block.allowUnsigned,
363
379
  allowUnpinned = block.allowUnpinnedConfigApi,
@@ -8,9 +8,12 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull
8
8
  import okhttp3.OkHttpClient
9
9
  import okhttp3.Request
10
10
  import okhttp3.RequestBody.Companion.toRequestBody
11
+ import okhttp3.Response
11
12
  import okio.Buffer
12
13
  import okio.ByteString.Companion.decodeBase64
13
14
  import java.io.IOException
15
+ import java.io.InterruptedIOException
16
+ import java.net.ProtocolException
14
17
  import java.util.concurrent.TimeUnit
15
18
 
16
19
  /** A `fetch(url, init)` from JS, checked. */
@@ -32,7 +35,9 @@ internal class ResponseTooLargeException(limit: Long) : IOException("Response la
32
35
  * The plugin's own `fetch`: parsed strictly, sent through the pinned client
33
36
  * the library hands out (`PinVault.getClient()` or `getClient(settings)`),
34
37
  * HTTPS only, bounded in both directions. A redirect from `https` into plain
35
- * `http` is not followed (the 3xx comes back), as on iOS.
38
+ * `http` is not followed (the 3xx comes back), as on iOS. A redirect to
39
+ * another host or port takes only [CROSS_ORIGIN_HEADERS] along and never a
40
+ * body ([followUp]).
36
41
  */
37
42
  internal object PinnedFetch {
38
43
 
@@ -50,6 +55,56 @@ internal object PinnedFetch {
50
55
  "Host", "Connection", "Keep-Alive", "Proxy-Connection", "TE", "Trailer", "Transfer-Encoding", "Upgrade", "Content-Length",
51
56
  )
52
57
 
58
+ /**
59
+ * The only request headers that follow a redirect to another host or port
60
+ * (lower case): what the client accepts and what it is. Everything else
61
+ * the caller set was meant for the host it named.
62
+ */
63
+ val CROSS_ORIGIN_HEADERS = setOf("accept", "accept-language", "user-agent")
64
+
65
+ /** As OkHttp's own limit. */
66
+ const val MAX_FOLLOW_UPS = 20
67
+ private val REDIRECT_CODES = setOf(300, 301, 302, 303, 307, 308)
68
+
69
+ /** Why a caller may not set request header [name], or null when it may: the library's and the HTTP client's own. */
70
+ fun refusedHeader(name: String): String? = when {
71
+ LIBRARY_HEADERS.any { it.equals(name, ignoreCase = true) } -> "set by PinVault itself (a token host's requests carry it)"
72
+ CLIENT_HEADERS.any { it.equals(name, ignoreCase = true) } -> "set by the HTTP client (host, connection and framing headers)"
73
+ else -> null
74
+ }
75
+
76
+ /**
77
+ * [refusedHeader] for a WebSocket's upgrade request, which is an HTTP
78
+ * request like any other (a caller's `Host` would name another site on the
79
+ * pinned connection), plus the handshake's own `Sec-WebSocket-*` headers.
80
+ */
81
+ fun refusedSocketHeader(name: String): String? = refusedHeader(name)
82
+ ?: if (name.startsWith("Sec-WebSocket-", ignoreCase = true)) "set by the WebSocket handshake" else null
83
+
84
+ const val MAX_SOCKET_PROTOCOLS = 8
85
+ const val MAX_SOCKET_PROTOCOL_LENGTH = 64
86
+
87
+ /**
88
+ * A WebSocket's subprotocols as the caller listed them, checked: the
89
+ * bridge writes `Sec-WebSocket-Protocol` from this list itself (the header
90
+ * is refused from the caller, [refusedSocketHeader]). Each entry is an
91
+ * RFC 7230 token — so no comma, space or control character can add a
92
+ * second value or a second header — of bounded length, and none twice
93
+ * (RFC 6455 §4.1).
94
+ */
95
+ fun socketProtocols(raw: List<*>?): List<String> {
96
+ if (raw == null) return emptyList()
97
+ if (raw.size > MAX_SOCKET_PROTOCOLS) throw BridgeInputException("protocols: at most $MAX_SOCKET_PROTOCOLS")
98
+ val seen = HashSet<String>()
99
+ return raw.map { entry ->
100
+ if (entry !is String || entry.length > MAX_SOCKET_PROTOCOL_LENGTH || !TOKEN.matches(entry)) {
101
+ throw BridgeInputException("protocols: each must be a token (RFC 7230) of at most $MAX_SOCKET_PROTOCOL_LENGTH characters")
102
+ }
103
+ if (!seen.add(entry)) throw BridgeInputException("protocols: '$entry' is listed twice")
104
+ entry
105
+ }
106
+ }
107
+
53
108
  fun parse(json: String): FetchRequest {
54
109
  // Base64 inflates by 4/3; leave room for the other fields.
55
110
  val f = StrictJson.parseObject(json, "request", maxChars = (MAX_REQUEST_BYTES * 4 / 3 + 64 * 1024).toInt())
@@ -61,12 +116,7 @@ internal object PinnedFetch {
61
116
  val headers = Headers.Builder()
62
117
  f.stringMap("headers", 128, 256, 8192)?.forEach { (name, value) ->
63
118
  if (!TOKEN.matches(name)) throw BridgeInputException("request.headers: '${name.take(40)}' is not a header name")
64
- if (LIBRARY_HEADERS.any { it.equals(name, ignoreCase = true) }) {
65
- throw BridgeInputException("request.headers.$name: set by PinVault itself (a token host's requests carry it)")
66
- }
67
- if (CLIENT_HEADERS.any { it.equals(name, ignoreCase = true) }) {
68
- throw BridgeInputException("request.headers.$name: set by the HTTP client (host, connection and framing headers)")
69
- }
119
+ refusedHeader(name)?.let { why -> throw BridgeInputException("request.headers.$name: $why") }
70
120
  if (value.any { it == '\r' || it == '\n' || it.code == 0 }) {
71
121
  throw BridgeInputException("request.headers.$name: line breaks are not allowed")
72
122
  }
@@ -104,18 +154,77 @@ internal object PinnedFetch {
104
154
  /** Runs [request] on [client] (a pinned client from the library); blocking. */
105
155
  fun execute(client: OkHttpClient, request: FetchRequest): Map<String, Any?> {
106
156
  // newBuilder shares the pool, the pinned socket factory and the
107
- // library's interceptors; it only stops https → http redirects.
108
- val callClient = client.newBuilder().followSslRedirects(false).build()
157
+ // library's interceptors. Redirects are followed here, not by OkHttp:
158
+ // OkHttp sends every header but Authorization — and the body of a
159
+ // 307/308 — to whatever host the Location names (see followUp).
160
+ val callClient = client.newBuilder().followRedirects(false).followSslRedirects(false).build()
109
161
  val contentType = request.headers["Content-Type"]?.toMediaTypeOrNull()
110
162
  val body = when {
111
163
  request.body != null -> request.body.toRequestBody(contentType)
112
164
  request.method in setOf("POST", "PUT", "PATCH") -> ByteArray(0).toRequestBody(contentType)
113
165
  else -> null
114
166
  }
115
- val okRequest = Request.Builder().url(request.url).headers(request.headers).method(request.method, body).build()
116
- val call = callClient.newCall(okRequest)
117
- request.timeoutMs?.let { call.timeout().timeout(it, TimeUnit.MILLISECONDS) }
118
- call.execute().use { response ->
167
+ var okRequest = Request.Builder().url(request.url).headers(request.headers).method(request.method, body).build()
168
+ // One time limit for the whole exchange, redirects included, as when OkHttp followed them.
169
+ val deadline = request.timeoutMs?.let { System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(it) }
170
+ var followUps = 0
171
+ while (true) {
172
+ val call = callClient.newCall(okRequest)
173
+ if (deadline != null) {
174
+ val left = deadline - System.nanoTime()
175
+ if (left <= 0) throw InterruptedIOException("timeout")
176
+ call.timeout().timeout(left, TimeUnit.NANOSECONDS)
177
+ }
178
+ val response = call.execute()
179
+ // A client the app told not to follow redirects still does not.
180
+ val next = if (client.followRedirects) followUp(okRequest, response) else null
181
+ if (next == null) return read(response, request)
182
+ response.close()
183
+ followUps++
184
+ if (followUps > MAX_FOLLOW_UPS) throw ProtocolException("Too many follow-up requests: $followUps")
185
+ okRequest = next
186
+ }
187
+ }
188
+
189
+ /**
190
+ * The next request for a 3xx, or null when [response] is the answer.
191
+ * Built from [sent] — the request as this class made it, not as the
192
+ * library's interceptors completed it: what they add (the attestation
193
+ * token of a token host) is added again for the next hop, for its host.
194
+ *
195
+ * To the same host and port everything goes along, as OkHttp does it
196
+ * (301/302/303 turn a POST into a GET; 307/308 repeat method and body).
197
+ * To another host or port only [CROSS_ORIGIN_HEADERS] go along — a fixed
198
+ * list of credential names would miss an app's own session or API-key
199
+ * header — and a body is not sent again: a 307/308 that would repeat it
200
+ * there is the answer. A redirect into plain `http` is never followed.
201
+ */
202
+ internal fun followUp(sent: Request, response: Response): Request? {
203
+ if (response.code !in REDIRECT_CODES) return null
204
+ val location = response.header("Location") ?: return null
205
+ val target = sent.url.resolve(location) ?: return null
206
+ if (!target.isHttps) return null
207
+ val next = sent.newBuilder().url(target)
208
+ var repeatsBody = false
209
+ if (sent.method != "GET" && sent.method != "HEAD") {
210
+ if (response.code == 307 || response.code == 308) {
211
+ repeatsBody = (sent.body?.contentLength() ?: 0L) != 0L
212
+ } else {
213
+ next.method("GET", null)
214
+ listOf("Transfer-Encoding", "Content-Length", "Content-Type").forEach { next.removeHeader(it) }
215
+ }
216
+ }
217
+ val sameOrigin = target.host == sent.url.host && target.port == sent.url.port
218
+ if (!sameOrigin) {
219
+ if (repeatsBody) return null
220
+ sent.headers.names().filter { it.lowercase() !in CROSS_ORIGIN_HEADERS }.forEach { next.removeHeader(it) }
221
+ }
222
+ return next.build()
223
+ }
224
+
225
+ /** Reads [response] under the request's size limit and maps it for the bridge; closes it. */
226
+ private fun read(response: Response, request: FetchRequest): Map<String, Any?> {
227
+ response.use {
119
228
  val responseBody = response.body
120
229
  val declared = responseBody?.contentLength() ?: -1L
121
230
  if (declared > request.maxResponseBytes) throw ResponseTooLargeException(request.maxResponseBytes)
@@ -99,7 +99,7 @@ public enum ConfigParser {
99
99
  } else if let max = require.vaultFileMaxOfflineAgeMs {
100
100
  builder.vaultFileMaxOfflineAge(max, .milliseconds)
101
101
  }
102
- if try root.bool("requireUnlockedDevice") == true || require.requireUnlockedDevice { builder.requireUnlockedDevice() }
102
+ let requireUnlocked = try root.bool("requireUnlockedDevice") == true || require.requireUnlockedDevice
103
103
  if try root.bool("requireHardwareBackedKeys") == true || require.requireHardwareBackedKeys { builder.requireHardwareBackedKeys() }
104
104
  if try root.bool("managedTrustRoots") == true || require.managedTrustRoots { builder.managedTrustRoots() }
105
105
  if let v = try root.stringList("expectedSignerSha256", maxItems: 16, maxLength: 128) { builder.expectedSignerSha256(v) }
@@ -121,7 +121,11 @@ public enum ConfigParser {
121
121
  // Android-only settings: the Android side reads them; here only their shape is checked.
122
122
  _ = try root.object("android")
123
123
  var maxRN = ReactNetworking.defaultMaxResponseBytes
124
+ // `requireUnlockedDevice(allowFallback: true)`: off unless the app asks for it here
125
+ // (the Android twin is `android.requireUnlockedDeviceAllowFallback`).
126
+ var allowFallback = false
124
127
  if let ios = try root.object("ios") {
128
+ allowFallback = try ios.bool("requireUnlockedDeviceAllowFallback") == true
125
129
  if let v = try ios.int64("reactNativeMaxResponseBytes", min: 1, max: ReactNetworking.maxResponseBytesLimit) { maxRN = v }
126
130
  try applyResolve(try ios.stringMap("resolve", maxItems: 64, maxKeyLength: 255, maxValueLength: 255),
127
131
  native: native, release: release, to: builder)
@@ -147,6 +151,10 @@ public enum ConfigParser {
147
151
  if let v = require.expectedTeamIds { builder.expectedTeamId(v) }
148
152
  if let v = require.userAuthStrength { builder.userAuthStrength(v) }
149
153
  }
154
+ if allowFallback && !requireUnlocked {
155
+ throw BridgeInputError("config.ios.requireUnlockedDeviceAllowFallback: needs requireUnlockedDevice: true")
156
+ }
157
+ if requireUnlocked { builder.requireUnlockedDevice(allowFallback: allowFallback) }
150
158
 
151
159
  try root.finish()
152
160
  if let listener { builder.onConnectionEvent(listener) }
@@ -225,12 +233,38 @@ public enum ConfigParser {
225
233
  /// is refused (RN-2). Returns nil when the optional field is absent.
226
234
  static func relEndpoint(_ f: Fields, _ key: String, maxLength: Int) throws -> String? {
227
235
  guard let v = try f.string(key, maxLength: maxLength) else { return nil }
228
- if v.contains("://") || v.hasPrefix("/") || v.hasPrefix("\\") {
236
+ // Whitespace around it, a backslash anywhere (parsers read it as `/`) or a
237
+ // `scheme:` prefix: an absolute URL however its slashes are written.
238
+ if v != v.trimmingCharacters(in: .whitespacesAndNewlines) || v.contains("\\") || v.contains("://") || v.hasPrefix("/")
239
+ || v.range(of: "^[A-Za-z][A-Za-z0-9+.-]*:", options: .regularExpression) != nil {
229
240
  throw BridgeInputError("\(f.path).\(key): must be a relative path, not an absolute URL")
230
241
  }
231
242
  return v
232
243
  }
233
244
 
245
+ /// A vault file's endpoint: required, held to the same rule as the block's
246
+ /// endpoints (`relEndpoint` — it used to be checked for `://` and a leading
247
+ /// slash only, so " //host/x" or "https:\\host/x" sent the file request and
248
+ /// the file's token to another host), and without a `..` segment: the
249
+ /// endpoint is resolved against the block's URL, and `..` would climb out of
250
+ /// the path the app's native side declared for that Config API.
251
+ static func vaultEndpoint(_ f: Fields) throws -> String {
252
+ guard let endpoint = try relEndpoint(f, "endpoint", maxLength: 512) else {
253
+ throw BridgeInputError("\(f.path).endpoint: required")
254
+ }
255
+ if hasParentSegment(endpoint) {
256
+ throw BridgeInputError("\(f.path).endpoint: must not contain a '..' path segment")
257
+ }
258
+ return endpoint
259
+ }
260
+
261
+ /// True when the path of `endpoint` has a `..` segment, written plainly or as `%2e` (URL parsers read both).
262
+ static func hasParentSegment(_ endpoint: String) -> Bool {
263
+ let path = endpoint.prefix { $0 != "?" && $0 != "#" }
264
+ return path.split(separator: "/", omittingEmptySubsequences: false)
265
+ .contains { $0.replacingOccurrences(of: "%2e", with: ".", options: .caseInsensitive) == ".." }
266
+ }
267
+
234
268
  public static func hostPin(_ p: Fields) throws -> HostPin {
235
269
  let hostname = try p.requireString("hostname", maxLength: 255)
236
270
  guard let pins = try p.stringList("sha256", maxItems: maxPinsPerHost, maxLength: 128) else {
@@ -411,10 +445,7 @@ public enum ConfigParser {
411
445
 
412
446
  static func addVaultFile(_ builder: PinVaultConfig.Builder, _ f: Fields, _ tokens: VaultTokenStore, native: NativeSecurity?) throws {
413
447
  let key = try f.requireString("key", maxLength: 128)
414
- let endpoint = try f.requireString("endpoint", maxLength: 512)
415
- if endpoint.contains("://") || endpoint.hasPrefix("/") || endpoint.hasPrefix("\\") {
416
- throw BridgeInputError("\(f.path).endpoint: must be a relative path, not an absolute URL")
417
- }
448
+ let endpoint = try vaultEndpoint(f)
418
449
  let jsSignatureKey = try f.string("signaturePublicKey", maxLength: keyLength, multiline: true)
419
450
  let updateWithPins = try f.bool("updateWithPins")
420
451
  let storage = try f.enumValue("storage", StorageStrategy.self)
@@ -311,11 +311,32 @@ enum SecurityPolicy {
311
311
  if let j, !NativeSecurity.sameKeys(j, n) { throw fixed(key) }
312
312
  return n
313
313
  }
314
- out.requiredSignatures = try one("requiredSignatures", block.requiredSignatures, js.requiredSignatures)
315
- out.recoveryKeys = try list("recoveryPublicKeys", block.recoveryPublicKeys, js.recoveryKeys)
316
- out.requiredRecovery = try one("requiredRecoverySignatures", block.requiredRecoverySignatures, js.requiredRecovery)
317
- out.serverScope = try one("serverScope", block.serverScope, js.serverScope)
318
- out.clientCaPins = try list("clientCaPins", block.clientCaPins, js.clientCaPins)
314
+ // The signing keys' threshold, the recovery keys and theirs are part of the
315
+ // library's trust-anchor fingerprint, and a changed fingerprint resets the
316
+ // replay watermarks. In a release build they are the file's alone: a value
317
+ // the bundle could add and drop again would reset them at will (a default — 1,
318
+ // no recovery keys — changes nothing and is let through).
319
+ // `serverScope` names the library's config store: another scope is another
320
+ // namespace, which starts with no watermarks at all — a bundle that sets one
321
+ // makes the device accept a replayed old config. `clientCaPins` decide which
322
+ // CA may issue the device's identity. Both follow the same rule.
323
+ func anchorOnly<T>(_ key: String, _ n: T?, _ j: T?, isDefault: (T) -> Bool) throws -> T? {
324
+ if release, n == nil, let j, !isDefault(j) {
325
+ throw BridgeInputError("\(path).\(key): a release build takes it only from the app's native security file (\(native.source)); "
326
+ + "declare \(key) for Config API '\(id)' there")
327
+ }
328
+ return j
329
+ }
330
+ out.requiredSignatures = try one("requiredSignatures", block.requiredSignatures,
331
+ try anchorOnly("requiredSignatures", block.requiredSignatures, js.requiredSignatures) { $0 == 1 })
332
+ out.recoveryKeys = try list("recoveryPublicKeys", block.recoveryPublicKeys,
333
+ try anchorOnly("recoveryPublicKeys", block.recoveryPublicKeys, js.recoveryKeys) { $0.isEmpty })
334
+ out.requiredRecovery = try one("requiredRecoverySignatures", block.requiredRecoverySignatures,
335
+ try anchorOnly("requiredRecoverySignatures", block.requiredRecoverySignatures, js.requiredRecovery) { $0 == 1 })
336
+ out.serverScope = try one("serverScope", block.serverScope,
337
+ try anchorOnly("serverScope", block.serverScope, js.serverScope) { _ in false })
338
+ out.clientCaPins = try list("clientCaPins", block.clientCaPins,
339
+ try anchorOnly("clientCaPins", block.clientCaPins, js.clientCaPins) { $0.isEmpty })
319
340
  // The file is the source of truth for the relaxations too.
320
341
  out.allowUnsigned = block.allowUnsigned
321
342
  out.allowUnpinned = block.allowUnpinnedConfigApi
@@ -33,6 +33,69 @@ public enum PinnedFetch {
33
33
  static let clientHeaders = ["Host", "Connection", "Keep-Alive", "Proxy-Connection", "TE", "Trailer",
34
34
  "Transfer-Encoding", "Upgrade", "Content-Length"]
35
35
 
36
+ /// Why a caller may not set request header `name`, or nil when it may: the library's and the HTTP client's own.
37
+ public static func refusedHeader(_ name: String) -> String? {
38
+ if libraryHeaders.contains(where: { $0.caseInsensitiveCompare(name) == .orderedSame }) {
39
+ return "set by PinVault itself (a token host's requests carry it)"
40
+ }
41
+ if clientHeaders.contains(where: { $0.caseInsensitiveCompare(name) == .orderedSame }) {
42
+ return "set by the HTTP client (host, connection and framing headers)"
43
+ }
44
+ return nil
45
+ }
46
+
47
+ /// ``refusedHeader(_:)`` for a WebSocket's upgrade request, which is an HTTP
48
+ /// request like any other (a caller's `Host` would name another site on the
49
+ /// pinned connection), plus the handshake's own `Sec-WebSocket-*` headers.
50
+ public static func refusedSocketHeader(_ name: String) -> String? {
51
+ if let why = refusedHeader(name) { return why }
52
+ return name.lowercased().hasPrefix("sec-websocket-") ? "set by the WebSocket handshake" : nil
53
+ }
54
+
55
+ public static let maxSocketProtocols = 8
56
+ public static let maxSocketProtocolLength = 64
57
+
58
+ /// A WebSocket's subprotocols as the caller listed them, checked: the
59
+ /// bridge writes `Sec-WebSocket-Protocol` from this list itself (the header
60
+ /// is refused from the caller, ``refusedSocketHeader(_:)``). Each entry is
61
+ /// an RFC 7230 token — so no comma, space or control character can add a
62
+ /// second value or a second header — of bounded length, and none twice
63
+ /// (RFC 6455 §4.1).
64
+ public static func socketProtocols(_ raw: [Any]?) throws -> [String] {
65
+ guard let raw else { return [] }
66
+ if raw.count > maxSocketProtocols { throw BridgeInputError("protocols: at most \(maxSocketProtocols)") }
67
+ var seen = Set<String>()
68
+ return try raw.map { entry in
69
+ guard let name = entry as? String, !name.isEmpty, name.unicodeScalars.count <= maxSocketProtocolLength,
70
+ name.unicodeScalars.allSatisfy({ tokenChars.contains($0) }) else {
71
+ throw BridgeInputError("protocols: each must be a token (RFC 7230) of at most \(maxSocketProtocolLength) characters")
72
+ }
73
+ if !seen.insert(name).inserted { throw BridgeInputError("protocols: '\(name)' is listed twice") }
74
+ return name
75
+ }
76
+ }
77
+
78
+ /// `operation` under one time limit for the whole exchange, redirects
79
+ /// included (Android: OkHttp's call timeout). A request's own
80
+ /// `timeoutInterval` starts again at every hop and only counts idle time;
81
+ /// `timeoutMs` promises the caller an answer or a failure by then.
82
+ static func withDeadline<T>(_ timeoutMs: Int64?, _ operation: @escaping () async throws -> T) async throws -> T {
83
+ guard let timeoutMs else { return try await operation() }
84
+ return try await withThrowingTaskGroup(of: T?.self) { group in
85
+ group.addTask { try await operation() }
86
+ group.addTask {
87
+ try await Task.sleep(nanoseconds: UInt64(timeoutMs) * 1_000_000)
88
+ return nil
89
+ }
90
+ // Whichever ends first decides; the other is cancelled (a running request with it).
91
+ defer { group.cancelAll() }
92
+ guard let first = try await group.next(), let value = first else {
93
+ throw PinVaultError.io(message: "timeout", cause: URLError(.timedOut))
94
+ }
95
+ return value
96
+ }
97
+ }
98
+
36
99
  public static func parse(_ json: String) throws -> FetchRequest {
37
100
  let f = try StrictJSON.parseObject(json, path: "request", maxChars: Int(maxRequestBytes * 4 / 3) + 64 * 1024)
38
101
  let urlText = try f.requireString("url", maxLength: 8192)
@@ -47,12 +110,7 @@ public enum PinnedFetch {
47
110
  guard name.unicodeScalars.allSatisfy({ tokenChars.contains($0) }) else {
48
111
  throw BridgeInputError("request.headers: '\(name.prefix(40))' is not a header name")
49
112
  }
50
- if libraryHeaders.contains(where: { $0.caseInsensitiveCompare(name) == .orderedSame }) {
51
- throw BridgeInputError("request.headers.\(name): set by PinVault itself (a token host's requests carry it)")
52
- }
53
- if clientHeaders.contains(where: { $0.caseInsensitiveCompare(name) == .orderedSame }) {
54
- throw BridgeInputError("request.headers.\(name): set by the HTTP client (host, connection and framing headers)")
55
- }
113
+ if let why = refusedHeader(name) { throw BridgeInputError("request.headers.\(name): \(why)") }
56
114
  if value.unicodeScalars.contains(where: { $0 == "\r" || $0 == "\n" || $0.value == 0 }) {
57
115
  throw BridgeInputError("request.headers.\(name): line breaks are not allowed")
58
116
  }
@@ -105,7 +163,10 @@ public enum PinnedFetch {
105
163
  /// library stops reading once the body passes `maxResponseBytes` (a
106
164
  /// declared `Content-Length` over it is refused before the body is read).
107
165
  public static func execute(_ session: PinnedSession, _ r: FetchRequest) async throws -> [String: Any] {
108
- let (data, response) = try await session.data(for: urlRequest(r), maxResponseBytes: r.maxResponseBytes)
166
+ let request = urlRequest(r)
167
+ let (data, response) = try await withDeadline(r.timeoutMs) {
168
+ try await session.data(for: request, maxResponseBytes: r.maxResponseBytes)
169
+ }
109
170
  guard let http = response as? HTTPURLResponse else { throw BridgeInputError("not an HTTP response") }
110
171
  return try map(http, data, r)
111
172
  }
@@ -149,6 +149,12 @@ export type PinVaultConfig = {
149
149
  userAuthStrength?: 'DEVICE_OWNER' | 'BIOMETRIC_CURRENT_SET';
150
150
  /** The largest answer React Native's own https request may get (bytes, default 50 MiB, at most 256 MiB). */
151
151
  reactNativeMaxResponseBytes?: number;
152
+ /**
153
+ * `requireUnlockedDevice(allowFallback: true)`: when the Keychain refuses a key that
154
+ * works only while the device is unlocked, make it without that requirement instead
155
+ * of failing with `UnlockedDeviceKeyRequiredException` (the default since 2.9.0).
156
+ */
157
+ requireUnlockedDeviceAllowFallback?: boolean;
152
158
  };
153
159
  };
154
160
  /** A native exception: its class name (`SSLPinningException`, …) and message. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umutcansu/react-native-pinvault",
3
- "version": "2.8.2",
3
+ "version": "2.9.0",
4
4
  "description": "PinVault for React Native: dynamic certificate pinning with signed remote configs, mTLS enrollment and signed vault files, as a thin bridge over the native PinVault libraries",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
package/src/types.ts CHANGED
@@ -157,6 +157,12 @@ export type PinVaultConfig = {
157
157
  userAuthStrength?: 'DEVICE_OWNER' | 'BIOMETRIC_CURRENT_SET';
158
158
  /** The largest answer React Native's own https request may get (bytes, default 50 MiB, at most 256 MiB). */
159
159
  reactNativeMaxResponseBytes?: number;
160
+ /**
161
+ * `requireUnlockedDevice(allowFallback: true)`: when the Keychain refuses a key that
162
+ * works only while the device is unlocked, make it without that requirement instead
163
+ * of failing with `UnlockedDeviceKeyRequiredException` (the default since 2.9.0).
164
+ */
165
+ requireUnlockedDeviceAllowFallback?: boolean;
160
166
  };
161
167
  };
162
168