fix(pairing): accept non-string NIP-46 params (Amber sends requested_perms as a JSON object)
RawRequest used a strict Vec<String>, so Amber's connect request — whose params[1] is the requested_perms grant object — failed to parse and was silently dropped. The signer saw a live session; we saw nothing. Params now coerce non-string values to their JSON text, with regression tests for both shapes.
This commit is contained in:
parent
759b5ddfc9
commit
aedde8ffb8
1 changed files with 57 additions and 4 deletions
|
|
@ -589,9 +589,8 @@ impl Nip46ClientSigner {
|
||||||
// Local-only debug capture (never leaves this machine): keep the
|
// Local-only debug capture (never leaves this machine): keep the
|
||||||
// full event so a failed handshake can be dissected offline.
|
// full event so a failed handshake can be dissected offline.
|
||||||
{
|
{
|
||||||
let path = std::path::Path::new(
|
let path =
|
||||||
"/home/avi/Tools/keynctr-debug/pairing-capture.jsonl",
|
std::path::Path::new("/home/avi/Tools/keynctr-debug/pairing-capture.jsonl");
|
||||||
);
|
|
||||||
if let Some(dir) = path.parent() {
|
if let Some(dir) = path.parent() {
|
||||||
let _ = std::fs::create_dir_all(dir);
|
let _ = std::fs::create_dir_all(dir);
|
||||||
}
|
}
|
||||||
|
|
@ -1861,10 +1860,29 @@ impl Signer for Nip46ClientSigner {
|
||||||
struct RawRequest {
|
struct RawRequest {
|
||||||
id: String,
|
id: String,
|
||||||
method: String,
|
method: String,
|
||||||
#[serde(default)]
|
#[serde(default, deserialize_with = "lenient_params")]
|
||||||
params: Vec<String>,
|
params: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// NIP-46 params are conventionally strings, but real signers embed raw JSON
|
||||||
|
/// objects (Amber sends its requested_perms grant object as `connect`
|
||||||
|
/// params[1]). A strict `Vec<String>` would reject the entire request and
|
||||||
|
/// silently drop the handshake, so non-string params are coerced to their
|
||||||
|
/// JSON text instead.
|
||||||
|
fn lenient_params<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let values: Vec<serde_json::Value> = Vec::deserialize(deserializer)?;
|
||||||
|
Ok(values
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| match v {
|
||||||
|
serde_json::Value::String(s) => s,
|
||||||
|
other => other.to_string(),
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
fn response_ok(id: &str, result: String) -> String {
|
fn response_ok(id: &str, result: String) -> String {
|
||||||
json!({ "id": id, "result": result, "error": null }).to_string()
|
json!({ "id": id, "result": result, "error": null }).to_string()
|
||||||
}
|
}
|
||||||
|
|
@ -1934,3 +1952,38 @@ fn percent_decode(raw: &str) -> Option<String> {
|
||||||
}
|
}
|
||||||
String::from_utf8(out).ok()
|
String::from_utf8(out).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod raw_request_tests {
|
||||||
|
use super::RawRequest;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_amber_style_connect_with_object_params() {
|
||||||
|
// Real signers (Amber) send requested_perms as a raw JSON object in
|
||||||
|
// params[1]; a strict Vec<String> used to reject the whole request
|
||||||
|
// and silently drop the pairing handshake.
|
||||||
|
let raw = format!(
|
||||||
|
r#"{{"id":"1","method":"connect","params":["{}",{{"sign_event":[1,7],"nip04_encrypt":true}}]}}"#,
|
||||||
|
"a".repeat(64)
|
||||||
|
);
|
||||||
|
let req: RawRequest = serde_json::from_str(&raw).expect("must parse");
|
||||||
|
assert_eq!(req.method, "connect");
|
||||||
|
assert_eq!(req.params.len(), 2);
|
||||||
|
assert_eq!(req.params[0], "a".repeat(64));
|
||||||
|
assert!(
|
||||||
|
req.params[1].starts_with('{'),
|
||||||
|
"object coerced to JSON text"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_string_params_unchanged() {
|
||||||
|
let req: RawRequest =
|
||||||
|
serde_json::from_str(r#"{"id":"2","method":"get_public_key","params":[]}"#).unwrap();
|
||||||
|
assert!(req.params.is_empty());
|
||||||
|
let req: RawRequest =
|
||||||
|
serde_json::from_str(r#"{"id":"3","method":"nip44_decrypt","params":["aa","bb"]}"#)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(req.params, vec!["aa".to_string(), "bb".to_string()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue