lnurlcash-conformance 0.2.2 → 0.2.3

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
@@ -4,6 +4,16 @@ Semantic versioning. While the LUD-25 draft is unmerged, `0.x` minor bumps
4
4
  may add or tighten checks that a previously-passing mint now fails; pin an
5
5
  exact version if you gate CI on the grade.
6
6
 
7
+ ## 0.2.3 - 2026-08-22
8
+
9
+ - **New check: `keeps signatures off the informational endpoint`.** LUD-25
10
+ says signatures are only ever delivered in the `withdrawSuccessResponse`
11
+ of a rotate, split or merge, and that the informational endpoint never
12
+ returns one. Nothing graded it. A mint that hands one out there lets
13
+ anyone holding a note's public URL mint an offline certificate for it,
14
+ and invites a wallet to treat an online answer as offline proof. Both
15
+ known mints pass; the check exists so a third cannot quietly not.
16
+
7
17
  ## 0.2.2 - 2026-08-22
8
18
 
9
19
  - The mock mint publishes `payLink` on a note's informational GET, as the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lnurlcash-conformance",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Language-neutral conformance vectors, an adversarial mock mint, and a grader for LNURLcash (LUD-25) implementations",
5
5
  "author": "TheCryptoDonkey",
6
6
  "license": "MIT",
package/runner/index.mjs CHANGED
@@ -848,6 +848,22 @@ export const gradeNote = async (noteUrl, report, options = {}) => {
848
848
  : `verified offline against a previous signing key (${signedBy.slice(0, 16)}...)`
849
849
  })
850
850
 
851
+ await report.check('keeps signatures off the informational endpoint', async () => {
852
+ // LUD-25: "Signatures are only ever delivered in the
853
+ // withdrawSuccessResponse of a rotate, split or merge, the
854
+ // informational endpoint never returns one." A mint that hands one out
855
+ // here lets anyone holding only a note's PUBLIC url mint a certificate
856
+ // for it, and invites a wallet to treat the informational answer as an
857
+ // offline proof when it is an online one.
858
+ const here = new URL(url)
859
+ here.searchParams.set('k1', current)
860
+ const body = await get(here)
861
+ assert(body.status !== 'ERROR', `informational GET refused: ${body.reason}`)
862
+ assert(body.sig === undefined, 'the informational GET returned a sig')
863
+ assert(body.sig2 === undefined, 'the informational GET returned a sig2')
864
+ return 'no signature where the spec forbids one'
865
+ })
866
+
851
867
  // The still-alive probe the three adversarial checks below share: a
852
868
  // compliant refusal is ATOMIC, so the note it refused to touch must
853
869
  // still be spendable afterwards.