Honor kind on auto-handled NIP-44 v3 rejections

The ContentProvider auto-handle path looked the v3 permission up by
(key, type, kind) and, on miss, fell back to (key, type) — whose SQL
matches any kind. A v3 reject saved with kind=A therefore leaked to a
request with kind=B, auto-rejecting it. Add a dedicated DAO query for
the explicit "all kinds" (kind IS NULL) grant and use that as the
fallback instead, mirroring how the manual bunker and intent screens
already resolve v3 permissions.
This commit is contained in:
Claude
2026-06-01 13:06:10 +00:00
parent 96c786816a
commit 5b3522c702
3 changed files with 26 additions and 2 deletions
@@ -319,8 +319,12 @@ class SignerProvider : ContentProvider() {
// SignerType, kind); fall back to a kind=null "all kinds"
// grant. V3 grants do NOT satisfy V2 requests and vice versa.
var permission = if (isV3) {
// V3 grants are kind-scoped; fall back to the explicit
// "all kinds" (kind IS NULL) grant only, never to any
// other kind — otherwise e.g. a kind-A reject would
// leak to a kind-B request.
permDao.getPermission(packageName, type.toString(), v3Kind!!)
?: permDao.getPermission(packageName, type.toString())
?: permDao.getPermissionAllKinds(packageName, type.toString())
} else {
// Classify the content to determine EncryptedDataKind-based permission type
val classifyContent = if (isEncrypt) content else (result ?: content)
@@ -78,6 +78,18 @@ interface ApplicationDao {
type: String,
): ApplicationPermissionsEntity?
/**
* Match an "all kinds" grant — the row whose `kind` column is `NULL`.
* Distinct from [getPermission] (no kind), which matches any row of the
* given type regardless of kind: V3's kind-scoped permission model needs
* the explicit `IS NULL` to avoid kind-A rejects leaking to kind-B requests.
*/
@Query("SELECT * FROM applicationPermission WHERE pkKey = :key AND type = :type AND kind IS NULL AND relay = '' LIMIT 1")
fun getPermissionAllKinds(
key: String,
type: String,
): ApplicationPermissionsEntity?
@Query("SELECT * FROM applicationPermission WHERE pkKey = :key AND type = :type AND kind = :kind AND relay = :relay LIMIT 1")
fun getPermissionForRelay(
key: String,
@@ -21,7 +21,7 @@ import androidx.paging.PagingSource
class CachingApplicationDao(
private val delegate: ApplicationDao,
) : ApplicationDao {
private enum class Method { SIGN_POLICY, PERM, PERM_KIND, PERM_RELAY, PERM_WILDCARD }
private enum class Method { SIGN_POLICY, PERM, PERM_KIND, PERM_ALL_KINDS, PERM_RELAY, PERM_WILDCARD }
private data class Key(
val method: Method,
@@ -81,6 +81,14 @@ class CachingApplicationDao(
return result
}
override fun getPermissionAllKinds(key: String, type: String): ApplicationPermissionsEntity? {
val k = Key(Method.PERM_ALL_KINDS, key, type, null, null)
lookup(k)?.let { return it.permission }
val result = delegate.getPermissionAllKinds(key, type)
store(k, Value(null, result))
return result
}
override fun getPermissionForRelay(key: String, type: String, kind: Int, relay: String): ApplicationPermissionsEntity? {
val k = Key(Method.PERM_RELAY, key, type, kind, relay)
lookup(k)?.let { return it.permission }