Skip to content

fix(fp): distinguish an unreadable IX-F export from a genuine absence - #301

Merged
netravnen merged 1 commit into
dev-nextfrom
fix/fp-ixf-unreadable-export
Aug 19, 2026
Merged

netravnen merged 1 commit into
dev-nextfrom
fix/fp-ixf-unreadable-export

Conversation

@netravnen

Copy link
Copy Markdown
Contributor

The per-row "Verify IX-F" check treated an export it could not read as
proof that the network is not in the feed. extractIxfMatchForAsnIp
coerced a missing or non-array member_list to [], found nothing in it,
and returned "none" -- which the panel renders as "IX-F export has no
entry for AS at this exchange at all" directly above a "Remove
netixlan entry" button. A 200-OK CDN error page, a renamed schema, a feed
that moved, or an ixlan whose ixf_ixp_member_list_url points at a
different exchange all take that path, so an admin acting on a confident
negative could delete a netixlan row the feed never actually contradicted.

"none" is a positive claim, not a default. It is now returned only from
an export that could be read, and everything else reports as
inconclusive with no destructive control offered.

Changes:

  • extractIxfMatchForAsnIp returns matched: "unreadable" with a machine
    reason when member_list is missing, non-array or empty, and when
    there is no target ASN to look up. All returns carry reason so the
    shape stays uniform.
  • Add renderUnreadableIxfExportResult: names the check inconclusive,
    explains that this is not evidence for removal, and shows the source
    URL so the admin can inspect the feed. It deliberately offers no
    remove or resolve button, and says so in its docblock.
  • Branch to it before the "none" case in the verify flow; document on
    renderNoIxfEntryResult why it is now unreachable on a false negative.

Security:

  • Closes a path where unvalidated third-party data (an exchange's own
    export URL, fetched cross-origin) could induce an admin to delete a
    live netixlan record. The feed is not attacker-controlled in the usual
    sense, but it is third-party, unauthenticated, and frequently broken.

Testing:

  • node --test: 507 tests, 506 pass, 1 skipped (live tests are opt-in).
  • Replaced the test that asserted the old conflated behavior. New cases
    cover null body, empty object, non-IX-F JSON, non-array member_list,
    and present-but-empty member_list -- each must be "unreadable" and
    explicitly not "none".
  • Kept a case proving "none" still fires for a readable export that
    genuinely lacks the ASN, so the Remove path is not silently dead.
  • Removed the guard and confirmed 4 failures, then restored.

Backwards Compatibility:

  • extractIxfMatchForAsnIp gains a fourth matched value and a reason
    field. Its only caller is updated in the same commit; it is exported on
    the test hooks but has no other consumer. buildIxfDiff already keys off
    matched === "ip" and returns [] for the new state unchanged.

Assisted-by: Claude:claude-opus-5


Stack created with GitHub Stacks CLI • Give Feedback 💬

@netravnen
netravnen force-pushed the fix/fp-ixf-unreadable-export branch from 81e8958 to 87e2f97 Compare August 19, 2026 22:27
@netravnen
netravnen force-pushed the fix/fp-ixf-unreadable-export branch from 87e2f97 to 7b8e529 Compare August 19, 2026 22:28
@netravnen
netravnen force-pushed the fix/fp-ixf-unreadable-export branch from 7b8e529 to 0ce4430 Compare August 19, 2026 22:29
@netravnen
netravnen force-pushed the fix/fp-ixf-unreadable-export branch from 0ce4430 to 5f46b0a Compare August 19, 2026 22:31
@netravnen
netravnen force-pushed the fix/fp-ixf-unreadable-export branch from 5f46b0a to 41033df Compare August 19, 2026 23:05
Base automatically changed from fix/cp-name-normalization-guards to dev-next August 19, 2026 23:06
The per-row "Verify IX-F" check treated an export it could not read as
proof that the network is not in the feed. extractIxfMatchForAsnIp
coerced a missing or non-array member_list to [], found nothing in it,
and returned "none" -- which the panel renders as "IX-F export has no
entry for AS<n> at this exchange at all" directly above a "Remove
netixlan entry" button. A 200-OK CDN error page, a renamed schema, a feed
that moved, or an ixlan whose ixf_ixp_member_list_url points at a
different exchange all take that path, so an admin acting on a confident
negative could delete a netixlan row the feed never actually contradicted.

"none" is a positive claim, not a default. It is now returned only from
an export that could be read, and everything else reports as
inconclusive with no destructive control offered.

Changes:
- extractIxfMatchForAsnIp returns matched: "unreadable" with a machine
  `reason` when member_list is missing, non-array or empty, and when
  there is no target ASN to look up. All returns carry `reason` so the
  shape stays uniform.
- Add renderUnreadableIxfExportResult: names the check inconclusive,
  explains that this is not evidence for removal, and shows the source
  URL so the admin can inspect the feed. It deliberately offers no
  remove or resolve button, and says so in its docblock.
- Branch to it before the "none" case in the verify flow; document on
  renderNoIxfEntryResult why it is now unreachable on a false negative.

Security:
- Closes a path where unvalidated third-party data (an exchange's own
  export URL, fetched cross-origin) could induce an admin to delete a
  live netixlan record. The feed is not attacker-controlled in the usual
  sense, but it is third-party, unauthenticated, and frequently broken.

Testing:
- node --test: 507 tests, 506 pass, 1 skipped (live tests are opt-in).
- Replaced the test that asserted the old conflated behavior. New cases
  cover null body, empty object, non-IX-F JSON, non-array member_list,
  and present-but-empty member_list -- each must be "unreadable" and
  explicitly not "none".
- Kept a case proving "none" still fires for a readable export that
  genuinely lacks the ASN, so the Remove path is not silently dead.
- Removed the guard and confirmed 4 failures, then restored.

Backwards Compatibility:
- extractIxfMatchForAsnIp gains a fourth `matched` value and a `reason`
  field. Its only caller is updated in the same commit; it is exported on
  the test hooks but has no other consumer. buildIxfDiff already keys off
  matched === "ip" and returns [] for the new state unchanged.

Assisted-by: Claude:claude-opus-5
@netravnen
netravnen force-pushed the fix/fp-ixf-unreadable-export branch from 41033df to 7e736c8 Compare August 19, 2026 23:06
@netravnen
netravnen marked this pull request as ready for review August 19, 2026 23:07
@netravnen
netravnen merged commit 0a18f2a into dev-next Aug 19, 2026
2 checks passed
@netravnen
netravnen deleted the fix/fp-ixf-unreadable-export branch August 20, 2026 09:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant