Skip to content

fix(dig-node): add bounded non-injectable shape hint for rejected peer ids (#107) - #176

Merged
MichaelTaylor3d merged 2 commits into
mainfrom
harden/107-peer-id-shape-hint
Aug 3, 2026
Merged

fix(dig-node): add bounded non-injectable shape hint for rejected peer ids (#107)#176
MichaelTaylor3d merged 2 commits into
mainfrom
harden/107-peer-id-shape-hint

Conversation

@MichaelTaylor3d

Copy link
Copy Markdown
Contributor

Closes dig-node#107. A peer-supplied identifier that fails is_canonical_hex_id currently logs only the bare <non-canonical> sentinel — correct for security (an attacker-writable log is not evidence, #1603) but it loses debug signal for a nearly-right id (63 hex chars, an 0x prefix, a typo). This adds a bounded, non-injectable shape hint alongside the sentinel, computed from the raw value but echoing none of its bytes.

The change (single DRY helper, both paths)

  • New Charset closed enum — Hex / HexPrefix / NonHex — with of(raw) classifying by alphabet and tag() → fixed source-literal strings.
  • New IdShapeHint { len, charset } whose Display renders exactlylen=<N> charset=<tag> and nothing else can appear in its output.
  • SafeId::Display's non-canonical arm now renders <non-canonical> len=<N> charset=<tag>. Because both serve-log reject paths render ids through SafeIddig.fetchRange via ServeTarget's SafeId fields and dig.getAvailability via SafeId::new(...) — this one edit covers both (and every other SafeId log site). The ABSENT and canonical-64-hex arms are unchanged.

Vocabulary: len=<integer char count> + charset ∈ {hex, hex+prefix, non-hex}. A rejected id now renders e.g. <non-canonical> len=63 charset=hex.

Security property (the point of the ticket)

The hint is fixed-vocabulary — only a decimal length + one of three enum tags. No byte, substring, or transformed form of the input can appear, preserving the forgery/amplification properties (#1603) the sentinel was chosen for.

How verified (TDD)

  • shape_hint_echoes_no_byte_of_a_hostile_id — feeds a hostile id (control chars, a peer_id=… newline-forgery payload, an RTL override, a path, a NUL) and asserts the emitted hint equals exactlylen=<count> charset=non-hex — pinning the whole output, so any regression folding a raw substring in fails. Non-vacuous.
  • shape_hint_reports_length_and_charset_from_the_alphabet63-hex → len=63 charset=hex, 0xABCDEF → hex+prefix, not-a-root → non-hex, bare 0x → non-hex.
  • both_serve_log_reject_paths_emit_the_shape_hint — asserts the hint on the fetchRange ServeTarget fields AND the getAvailability SafeId render.
  • cargo test -p dig-node-core --lib667 passed, 0 failed (664 baseline + 3). fmt + clippy -D warnings clean; build OK.

Blast radius

SafeId::Display is consumed only at logging call sites; the change is additive log-output on the already-rejected branch — no control-flow, signature, or caller-logic impact (risk LOW). Note: the DRY win means the hint also enriches the module_serve/module_reshare/module_anchor/module_transportSafeId log sites — a strict improvement, but any log-scraper keying on a bare <non-canonical> should switch to a prefix match. No SPEC.md/SYSTEM.md/docs change (internal diagnostic log format, not a wire/contract).

Version

root [workspace.package].version 0.93.7 → 0.93.8 (patch, fix:); Cargo.lock regenerated.


Generated by Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
…r ids (#107)
A peer-supplied id that fails is_canonical_hex_id logs the fixed
NON_CANONICAL sentinel (correct: an attacker-writable log is not
evidence, #1603) but that erased the difference between a nearly-right
id (a 63-hex typo, a 0x-prefixed key) and pure garbage.
Emit a bounded, non-injectable shape hint alongside the sentinel:
len=<N> plus charset=<hex|hex+prefix|non-hex>, derived from the raw
value but echoing none of its bytes. A closed Charset enum + an integer
length keep the amplification/forgery guarantees of the bare sentinel.
The classify logic is one pure helper (IdShapeHint) reused by both
serve-log reject paths (dig.fetchRange via ServeTarget's SafeId fields,
dig.getAvailability via SafeId::new), so both gain the hint DRY.
Co-Authored-By: Claude <noreply@anthropic.com>
@MichaelTaylor3d
MichaelTaylor3d merged commit 1e9dd52 into mainAug 3, 2026
15 checks passed
@MichaelTaylor3d
MichaelTaylor3d deleted the harden/107-peer-id-shape-hint branch August 3, 2026 20:24
Sign up for freeto 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.

2 participants

@MichaelTaylor3d@claude