The local-use NAT64 range and the operator's blocked_networks list miss IPv4 addresses embedded in IPv6 #299

Closed
opened 2026-10-16 05:32:00 +02:00 by clawbot · 2 comments
Collaborator

Where: internal/httpfetcher/httpfetcher.go, the built-in blocked ranges and isBlockedIP, on next after #298. Found by the review of #298.

What is wrong:

  • 64:ff9b:1::/48, the local-use NAT64 range (RFC 8215), passes the check that keeps pixa from fetching internal addresses; on a network that runs NAT64 on that range, such an address reaches the IPv4 address it holds, internal ones included.
  • isBlockedIP matches the operator's blocked_networks list after converting only the ::ffff: mapped form to IPv4, so with 203.0.113.0/24 listed, ::ffff:203.0.113.5 is refused but ::203.0.113.5 is not.

Severity: medium (both weaken the check that blocks fetching addresses pixa must not reach).

Definition of done:

  1. 64:ff9b:1::/48 is blocked as 64:ff9b::/96 is.
  2. An address in ::/96 is matched against blocked_networks by the IPv4 address it holds, as the mapped form already is.
  3. Tests first, through both image routes: [64:ff9b:1::a00:1] is refused, and [::203.0.113.5] is refused when blocked_networks lists 203.0.113.0/24; make check green.

Package: internal/httpfetcher.

Model: opus-5-5

Where: `internal/httpfetcher/httpfetcher.go`, the built-in blocked ranges and `isBlockedIP`, on `next` after https://git.eeqj.de/sneak/pixa/pulls/298. Found by the review of https://git.eeqj.de/sneak/pixa/pulls/298. What is wrong: - `64:ff9b:1::/48`, the local-use NAT64 range (RFC 8215), passes the check that keeps pixa from fetching internal addresses; on a network that runs NAT64 on that range, such an address reaches the IPv4 address it holds, internal ones included. - `isBlockedIP` matches the operator's `blocked_networks` list after converting only the `::ffff:` mapped form to IPv4, so with `203.0.113.0/24` listed, `::ffff:203.0.113.5` is refused but `::203.0.113.5` is not. Severity: medium (both weaken the check that blocks fetching addresses pixa must not reach). Definition of done: 1. `64:ff9b:1::/48` is blocked as `64:ff9b::/96` is. 2. An address in `::/96` is matched against `blocked_networks` by the IPv4 address it holds, as the mapped form already is. 3. Tests first, through both image routes: `[64:ff9b:1::a00:1]` is refused, and `[::203.0.113.5]` is refused when `blocked_networks` lists `203.0.113.0/24`; `make check` green. Package: `internal/httpfetcher`. Model: opus-5-5
clawbot self-assigned this 2026-10-16 05:32:07 +02:00
Author
Collaborator

Plan:

  • Add 64:ff9b:1::/48 to the built-in blocked ranges, beside 64:ff9b::/96.
  • In isBlockedIP, read an address in ::/96 as the IPv4 address in its last four bytes before matching it against blocked_networks, next to where the ::ffff: mapped form is already unmapped. The code #298 added to isPrivateIP for this moves into one small function that both call, so there is one way of reading it.
  • Failing tests first, on /v1/image/ with a signature and on an encrypted URL, with the fetcher pixad builds from the config: [64:ff9b:1::a00:1] answers 403; [::203.0.113.5] answers 403 when blocked_networks lists 203.0.113.0/24, and is not refused when the list is empty. No existing test changes.

Model: opus-5-5

Plan: - Add `64:ff9b:1::/48` to the built-in blocked ranges, beside `64:ff9b::/96`. - In `isBlockedIP`, read an address in `::/96` as the IPv4 address in its last four bytes before matching it against `blocked_networks`, next to where the `::ffff:` mapped form is already unmapped. The code https://git.eeqj.de/sneak/pixa/pulls/298 added to `isPrivateIP` for this moves into one small function that both call, so there is one way of reading it. - Failing tests first, on `/v1/image/` with a signature and on an encrypted URL, with the fetcher pixad builds from the config: `[64:ff9b:1::a00:1]` answers 403; `[::203.0.113.5]` answers 403 when `blocked_networks` lists `203.0.113.0/24`, and is not refused when the list is empty. No existing test changes. Model: opus-5-5
Author
Collaborator

Built as planned in #300.

Judgement call: the route test with an empty blocked_networks lets the fetcher try to connect to ::203.0.113.5, since only the connection step checks that list; it checks only that the answer is not 403.

Model: opus-5-5

Built as planned in https://git.eeqj.de/sneak/pixa/pulls/300. Judgement call: the route test with an empty `blocked_networks` lets the fetcher try to connect to `::203.0.113.5`, since only the connection step checks that list; it checks only that the answer is not 403. Model: opus-5-5
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/pixa#299