mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-10 08:27:04 +00:00
fix(nip46): time-out the first-connect consent prompt; drop dead code
Audit fixes: - requestConnect had no timeout while requestOp did. Since authorize()/ onConnect() run inline in the signer service's single-consumer loop, an ignored first-connect dialog blocked every other client's requests forever. Both consent prompts now fail closed on the shared 120s timeout (per-op → deny-once, connect → declined) so a stuck dialog can't hold the loop hostage. - Remove the now-unused Nip46ActivityLog.forClient() (the detail screen filters the collected flow instead). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015FHr2mu5SiHwYNR7evYUuF
This commit is contained in:
-3
@@ -53,7 +53,4 @@ class Nip46ActivityLog(
|
||||
fun record(entry: Nip46ActivityEntry) {
|
||||
_entries.update { (listOf(entry) + it).take(capacity) }
|
||||
}
|
||||
|
||||
/** The most recent entries for one client (newest first). */
|
||||
fun forClient(clientPubKey: HexKey): List<Nip46ActivityEntry> = _entries.value.filter { it.clientPubKey == clientPubKey }
|
||||
}
|
||||
|
||||
+9
-6
@@ -49,8 +49,9 @@ import kotlinx.coroutines.withTimeoutOrNull
|
||||
*/
|
||||
object Nip46ConsentBridge {
|
||||
/**
|
||||
* Upper bound on how long a per-op prompt may block the signer's single-consumer loop. A user who
|
||||
* ignores the dialog eventually fails the request closed (deny-once) instead of wedging the signer.
|
||||
* Upper bound on how long a consent prompt may block the signer's single-consumer loop. A user who
|
||||
* ignores the dialog eventually fails the request closed (deny / declined) instead of wedging the
|
||||
* signer for every other client whose requests queue behind that one blocked prompt.
|
||||
*/
|
||||
private const val CONSENT_TIMEOUT_MS = 120_000L
|
||||
|
||||
@@ -64,10 +65,12 @@ object Nip46ConsentBridge {
|
||||
val meta = request.clientMetadata
|
||||
val title = meta?.name?.ifBlank { null } ?: context.getString(R.string.nip46_signer_remote_app)
|
||||
val domain = meta?.url?.ifBlank { null } ?: (clientPubKey.take(12) + "…")
|
||||
return SignerConnectCoordinator.requestConnect(
|
||||
context,
|
||||
SignerConnectInfo(appletTitle = title, coordinate = coordinate, domain = domain, iconUrl = meta?.image),
|
||||
)
|
||||
val info = SignerConnectInfo(appletTitle = title, coordinate = coordinate, domain = domain, iconUrl = meta?.image)
|
||||
// Fail closed (declined) if the prompt is never answered, so a stuck first-connect dialog can't
|
||||
// hold the single-consumer loop hostage against every other client.
|
||||
return withTimeoutOrNull(CONSENT_TIMEOUT_MS) {
|
||||
SignerConnectCoordinator.requestConnect(context, info)
|
||||
} ?: AppConnectResult.Cancelled
|
||||
}
|
||||
|
||||
/** Per-operation consent: describe the request (op + event preview) and await the user's grant. */
|
||||
|
||||
Reference in New Issue
Block a user