The same platform difference as the vault index, in shipped code, with a
worse consequence.
saveTrust wrote "fnid": null explicitly when a contact's global
address was not yet known. Android's bundled org.json reads a JSON null
back as the four-character string "null" — a non-null value that is not
an address. What that caused on a phone:
Trust.pin compares a newly offered fnid against the pinned one and
treats a difference as a different person. "null" != "fnid:…", so the
FIRST TIME a real address arrived the contact was marked CHANGED —
the "security keys changed" warning, which is supposed to mean an
attack, firing on an ordinary first sighting. The vault's own comment
about minting a fresh device warns about exactly this shape of false
alarm.
rememberIdentity returns early once fnid != null, so the real
address was never resolved and the record stayed wrong permanently.
pinnedFnid fed "null" into key-log verification, which recomputes a
leaf against the address a contact was accepted under — every check
against the wrong subject.
Two halves, because records written the old way are already on phones:
the writer now omits the key entirely (an absent key reads as "" on both
platforms, which is what the reader always expected), and the reader
rejects the literal "null" so existing records repair themselves.
The test that matters is the one feeding the reader the literal string.
On the JVM a real JSON null reads back as "" whether the key is omitted
or not, so the plain round-trip passes with the bug intact — which is why
this survived every suite. Confirmed by restoring the old reader and
watching it go red.
Found by the sweep flagged in android!54. JSONObject.NULL is written in
exactly two places in this app, and both were wrong.
The same platform difference as the vault index, in shipped code, with a
worse consequence.
`saveTrust` wrote `"fnid": null` explicitly when a contact's global
address was not yet known. Android's bundled `org.json` reads a JSON null
back as the four-character string `"null"` — a non-null value that is not
an address. What that caused on a phone:
- `Trust.pin` compares a newly offered fnid against the pinned one and
treats a difference as a different person. `"null" != "fnid:…"`, so the
FIRST TIME a real address arrived the contact was marked **CHANGED** —
the "security keys changed" warning, which is supposed to mean an
attack, firing on an ordinary first sighting. The vault's own comment
about minting a fresh device warns about exactly this shape of false
alarm.
- `rememberIdentity` returns early once `fnid != null`, so the real
address was never resolved and the record stayed wrong permanently.
- `pinnedFnid` fed `"null"` into key-log verification, which recomputes a
leaf against the address a contact was accepted under — every check
against the wrong subject.
Two halves, because records written the old way are already on phones:
the writer now omits the key entirely (an absent key reads as `""` on both
platforms, which is what the reader always expected), and the reader
rejects the literal `"null"` so existing records repair themselves.
The test that matters is the one feeding the reader the literal string.
On the JVM a real JSON null reads back as `""` whether the key is omitted
or not, so the plain round-trip passes with the bug intact — which is why
this survived every suite. Confirmed by restoring the old reader and
watching it go red.
Found by the sweep flagged in android!54. `JSONObject.NULL` is written in
exactly two places in this app, and both were wrong.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The same platform difference as the vault index, in shipped code, with a
worse consequence.
saveTrustwrote"fnid": nullexplicitly when a contact's globaladdress was not yet known. Android's bundled
org.jsonreads a JSON nullback as the four-character string
"null"— a non-null value that is notan address. What that caused on a phone:
Trust.pincompares a newly offered fnid against the pinned one andtreats a difference as a different person.
"null" != "fnid:…", so theFIRST TIME a real address arrived the contact was marked CHANGED —
the "security keys changed" warning, which is supposed to mean an
attack, firing on an ordinary first sighting. The vault's own comment
about minting a fresh device warns about exactly this shape of false
alarm.
rememberIdentityreturns early oncefnid != null, so the realaddress was never resolved and the record stayed wrong permanently.
pinnedFnidfed"null"into key-log verification, which recomputes aleaf against the address a contact was accepted under — every check
against the wrong subject.
Two halves, because records written the old way are already on phones:
the writer now omits the key entirely (an absent key reads as
""on bothplatforms, which is what the reader always expected), and the reader
rejects the literal
"null"so existing records repair themselves.The test that matters is the one feeding the reader the literal string.
On the JVM a real JSON null reads back as
""whether the key is omittedor not, so the plain round-trip passes with the bug intact — which is why
this survived every suite. Confirmed by restoring the old reader and
watching it go red.
Found by the sweep flagged in android!54.
JSONObject.NULLis written inexactly two places in this app, and both were wrong.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
assigned to @paulhitt
changed the description