CheckURI refuses a CID that is not canonical and a host of two brackets

Spec v0.12, section 44.1, already asked for the CID in its canonical form
and an IPv6 literal: a CID with a character more, of zero bits, decoded to
the same bytes and passed, and https://[[2000::]/ passed because checkHost
trimmed every bracket. isCIDv1 refuses 5 or more bits left over, and
checkHost takes one pair of brackets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.13
dev 1 day ago
parent e9d4ceced3
commit e801e03d19

@ -20,6 +20,12 @@ branch `v0.13` and not approved yet. It changes no format and no verdict.
address of NAT64 written in a locator is still rejected. This module
downloads nothing: the function is for the readers that do, as the
application.
- **Two addresses that the text of v0.12 already refused**, and the
reference accepted (§44.1): a CID with a character more, of zero bits,
which decodes to the same bytes and is not its canonical form; and
`https://[[2000::]/`, whose brackets `checkHost` trimmed all at once.
`isCIDv1` refuses 5 or more bits left over, and `checkHost` takes one pair
of brackets. No normative change: `TestAddressCanonicalForms`.
- **Test data.** `vectors/resolved_ip.json`, new: 42 addresses, with the
prefix of the network or none, and the result of `CheckResolvedIP`, with
its text. The other files do not change.

@ -345,7 +345,9 @@ func checkHost(host string) error {
return errors.New("locator: an https address without a host")
}
if strings.HasPrefix(host, "[") {
a, err := netip.ParseAddr(strings.Trim(host, "[]"))
// One pair of brackets: splitAuthority ends the literal at the
// first ']', and "[[2000::]" is not an IPv6 literal.
a, err := netip.ParseAddr(strings.TrimSuffix(strings.TrimPrefix(host, "["), "]"))
if err != nil || !a.Is6() || a.Zone() != "" || !publicIP(a) {
return errors.New("locator: an https address with an IPv6 literal that is not public")
}
@ -469,7 +471,10 @@ func isCIDv1(s string) bool {
acc &= 1<<bits - 1
}
}
if acc != 0 { // the bits of the last character beyond a byte are zero
// Canonical base32 without padding: the last character carries fewer
// than 5 bits beyond the last byte, all zero. A character more, even
// one of zero bits, is another encoding of the same bytes.
if bits >= 5 || acc != 0 {
return false
}
version, out, ok := uvarint(out)

@ -169,6 +169,32 @@ func TestAddresses(t *testing.T) {
// Spec §44.1: the locator is an age file with one tlock stanza for the round
// of the DateKey: it opens with that release and with no other.
// Spec v0.12, §44.1: a CID in its canonical form, and an IPv6 literal of one
// pair of brackets. A CID with a character more, of zero bits, decodes to the
// same bytes and is another encoding of them; "[[2000::]" was read as
// 2000:: because the brackets were trimmed all at once.
func TestAddressCanonicalForms(t *testing.T) {
const cid = "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"
for _, c := range []struct {
uri string
ok bool
}{
{"ipfs://" + cid, true},
{"ipfs://" + cid + "a", false},
{"ipfs://" + cid + "aa", false},
{"ipfs://bafkreibq6d3olcwyloabkavyuz437v52i4m7ukb4ijk6p3mcuw3zbzfppu", true},
{"ipfs://bafkreibq6d3olcwyloabkavyuz437v52i4m7ukb4ijk6p3mcuw3zbzfppua", false},
{"https://[2a01:4f8::1]/x", true},
{"https://[[2a01:4f8::1]/x", false},
{"https://[[2000::]/", false},
{"https://[2a01:4f8::1]]/x", false},
} {
if err := locator.CheckURI(c.uri); (err == nil) != c.ok {
t.Errorf("CheckURI(%q) = %v, want ok %v", c.uri, err, c.ok)
}
}
}
func TestSealedLocator(t *testing.T) {
p := profile.Quicknet()
loc, _, _ := sample(t)

Loading…
Cancel
Save

Powered by TurnKey Linux.