diff --git a/CHANGELOG.md b/CHANGELOG.md index 35a8802..7988f62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ All notable changes to Podkey will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.0.11] - 2026-09-25 + +### Fixed + +- **sidestr records are read by their push length.** The vendored spec lib moves + from `373d3eb` to `@sidestr/spec` 0.0.6 (`fa86dac`), whose `recordText` + compares a push's declared length with its data again (sidestr/spec#17): a + script that pushed `tal` with `ly:x` after the push read as the record + `tally:x`. The spend window's asset view and record display now read records + exactly as validators do. Only `records.mjs` changed in Podkey's vendored set. + ## [0.0.10] - 2026-09-25 ### Changed diff --git a/README.md b/README.md index 199dcbf..5e18d08 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > Browser extension for **did:nostr** and **Solid** authentication -[![Version](https://img.shields.io/badge/version-0.0.10-blue.svg)](https://github.com/JavaScriptSolidServer/podkey/actions/workflows/ci.yml?query=branch%3Amain+is%3Asuccess) +[![Version](https://img.shields.io/badge/version-0.0.11-blue.svg)](https://github.com/JavaScriptSolidServer/podkey/actions/workflows/ci.yml?query=branch%3Amain+is%3Asuccess) [![License](https://img.shields.io/badge/license-AGPL--3.0-green.svg)](LICENSE) [![NIP-07](https://img.shields.io/badge/NIP--07-compatible-purple.svg)](https://github.com/nostr-protocol/nips/blob/master/07.md) [![Test Page](https://img.shields.io/badge/test--page-live-brightgreen)](https://javascriptsolidserver.github.io/podkey/test-page/) diff --git a/manifest.json b/manifest.json index 9429939..5238382 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "Podkey", - "version": "0.0.10", + "version": "0.0.11", "description": "Nostr (NIP-07) signer and Solid (NIP-98) login. One did:nostr key, encrypted on your device.", "content_security_policy": { "extension_pages": "script-src 'self'; object-src 'self'" diff --git a/package.json b/package.json index 6abb9f5..8e30f1e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "podkey", - "version": "0.0.10", + "version": "0.0.11", "description": "did:nostr and Solid authentication extension - NIP-07 provider for decentralized identity", "main": "src/index.js", "type": "module", diff --git a/scripts/vendor-sidestr.js b/scripts/vendor-sidestr.js index 5f57ede..8187e3c 100644 --- a/scripts/vendor-sidestr.js +++ b/scripts/vendor-sidestr.js @@ -47,7 +47,7 @@ export const PINS = [ { name: 'spec', repo: 'https://github.com/sidestr/spec', - commit: '373d3eb6accd163f418e8a813052f1516a942bb3', + commit: 'fa86dac83d47b8f70195132e91e9dc083e1d9228', // what explorer.mjs imports from `${sidestr}`, plus what the spend window uses entries: ['siding/lib/parents.mjs', 'siding/lib/overlay.mjs', 'siding/lib/overlays/index.mjs', 'siding/lib/txsign.mjs', 'siding/lib/announce.mjs', 'siding/lib/records.mjs', 'siding/lib/overlays/assets.mjs'], documents: [], diff --git a/test/sidestr-vendor.test.js b/test/sidestr-vendor.test.js index 60f65f0..cf60533 100644 --- a/test/sidestr-vendor.test.js +++ b/test/sidestr-vendor.test.js @@ -54,4 +54,12 @@ describe('vendored sidestr engine', () => { const { evmOverlay } = await import('../vendor/sidestr/spec/siding/lib/overlays/evm.mjs'); assert.throws(() => evmOverlay({ id: 'sidestr:x' }), (e) => e.code === 'unsupported'); }); + + it('the vendored records reader refuses a push whose length is not its data (sidestr/spec#17)', async () => { + const { recordText } = await import('../vendor/sidestr/spec/siding/lib/records.mjs'); + const hx = (t) => Buffer.from(t).toString('hex'); + assert.equal(recordText('6a03' + hx('tally:x')), null, 'bytes after the push'); + assert.equal(recordText('6a07' + hx('abc')), null, 'bytes missing'); + assert.equal(recordText('6a07' + hx('tally:x')), 'tally:x'); + }); }); diff --git a/vendor/sidestr/PINS.json b/vendor/sidestr/PINS.json index 4dfa000..12e6dd3 100644 --- a/vendor/sidestr/PINS.json +++ b/vendor/sidestr/PINS.json @@ -33,7 +33,7 @@ { "name": "spec", "repo": "https://github.com/sidestr/spec", - "commit": "373d3eb6accd163f418e8a813052f1516a942bb3", + "commit": "fa86dac83d47b8f70195132e91e9dc083e1d9228", "files": { "siding/lib/announce.mjs": "67a4e18d13eaa234fb4e9ddb5c3a275140fb33055e60e1d4150e4b0f23b12862", "siding/lib/block.mjs": "3f9de6866080ff788aab6e898be7744c12988636f9137ec1eeddcfa66d57ca48", @@ -44,7 +44,7 @@ "siding/lib/overlays/index.mjs": "58cccd190238b349c6f7815aefe1e2c4a337e8e770b060e06710824e8700f834", "siding/lib/overlays/pool.mjs": "c86584bac7f4ff9c6af41f76dd6abd52c0a9e49bce23c9a5df218729377a0f04", "siding/lib/parents.mjs": "a5e697a61e25c32a0df66def2809bac284247c777d5fa32647c08cfd249548ca", - "siding/lib/records.mjs": "8afa474cf154d8db6442a269deb30a83e67e8cc36e295c03c47a67c0c934ccca", + "siding/lib/records.mjs": "d314c419637122c450e3b3f1980d58c2837ed5e6b3b0e5d9b1751371ae3f2c51", "siding/lib/txsign.mjs": "5f2af06056e30e6c0997c917ba05bfcce305b947a80df1c242a6b04931039875", "LICENSE": "0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0" }, diff --git a/vendor/sidestr/spec/siding/lib/records.mjs b/vendor/sidestr/spec/siding/lib/records.mjs index 5155307..71b436e 100644 --- a/vendor/sidestr/spec/siding/lib/records.mjs +++ b/vendor/sidestr/spec/siding/lib/records.mjs @@ -6,7 +6,8 @@ export const MAX_AMOUNT = 2 ** 53 - 1; // the text of an OP_RETURN output, or null when it is not a single push of <= 255 bytes of UTF-8 export function recordText(spk) { const m = /^6a(?:4c([0-9a-f]{2})|([0-9a-f]{2}))([0-9a-f]*)$/i.exec(spk); if (!m) return null; - const len = parseInt(m[1] ?? m[2], 16); if (m[2] && len > 75) return null; if (m[1] && len <= 75) return null; // minimal push only if (m[3].length !== len * 2 || len > 255) return null; + const len = parseInt(m[1] ?? m[2], 16); if (m[2] && len > 75) return null; if (m[1] && len <= 75) return null; // minimal push only + if (m[3].length !== len * 2 || len > 255) return null; // exactly the pushed bytes: nothing after the push, nothing missing try { return dec.decode(Uint8Array.from(m[3].match(/../g) ?? [], (x) => parseInt(x, 16))); } catch { return null; } } export function recordScript(text) { const b = enc.encode(text); if (b.length > 255) throw new Error('a record is at most 255 bytes'); return '6a' + (b.length <= 75 ? b.length.toString(16).padStart(2, '0') : '4c' + b.length.toString(16).padStart(2, '0')) + toHex(b); }