Lab 05 — Rust Capability Token Parser
Language: Rust 2021 | Tools: Cargo | Difficulty: 4/5
Pairs with the Phase 01 WARMUP Chapter 11 (what Rust and Go remove — and what remains). Read it alongside Lab 04 (the C parser) to feel the contrast directly.
Why This Lab Exists (Purpose & Goal)
"Rewrite it in Rust" is a popular phrase and a dangerous half-truth. Rust's borrow checker eliminates
whole classes of bug — use-after-free, data races, out-of-bounds in safe code — but it does not
eliminate authorization flaws, parser differentials, resource exhaustion, or the hazards of unsafe
and FFI. The goal of this lab is to make that precise: you build a memory-safe parser for a capability
token and discover that all the security work that remains — bounding fields, rejecting injection,
enforcing tenant/expiry/replay invariants — is logic the language will never write for you.
The Concept, In the Weeds
You parse a compact, delimiter-separated capability token without any unsafe:
CAP1|tenant|subject|action|resource|expires_at|nonce
A capability is an unforgeable token of authority — "the bearer may perform action on
resource within tenant until expires_at." The parser must reject:
- delimiter injection — a field containing the
|separator that smuggles an extra field; - oversized fields — resource exhaustion / abuse;
- unknown actions and cross-tenant resources — authorization confusion;
- expired tokens and duplicate nonces — replay (a valid token reused outside its window);
- trailing fields — the exact-consumption rule again (Lab 01's parser differential, now in a text format).
The crucial design point: authorization is separate from parsing. The parser's job is to produce
a well-formed, validated structure; deciding whether this subject may touch this resource is a
later, explicit step. Conflating the two is how "the token parsed, so it's authorized" bugs are born.
And the Rust angle: every unwrap, allocation, integer conversion, and error boundary is a place
where a panic (a denial-of-service) or a logic error can still live — memory safety did not make them
disappear.
Why This Matters for Protecting the Company
Capability tokens, signed cookies, JWTs, and API keys are everywhere, and they are routinely
mis-validated (Phase 02). When you can write a parser that is memory-safe and enforces issuer/
tenant/expiry/replay invariants, you can protect the authorization layer of any service. The honest
framing you take from this — safe languages remove memory-corruption classes, not the need for input
validation and authorization — is exactly what keeps a team from a false sense of security after a
Rust rewrite, and what keeps you reviewing the unsafe/FFI boundaries where the guarantees stop.
How This Is Used on the Job
This is the secure-by-construction counterpart to writing token/credential parsers in production. The
unsafe-wrapper stretch task mirrors the real review you'll do at FFI boundaries (Rust↔C, the Android
JNI seam in Phase 04), where you must state and uphold the invariants the safe abstraction relies on.
Run
cargo test --features solution
cargo test # after implementing src/lab.rs
Security Work
- Compare Rust ownership and slice parsing with the C frame parser (Lab 04) — same invariants, very different failure modes.
- Add property tests or a
cargo-fuzztarget (logic bugs survive the borrow checker). - Review every
unwrap, allocation, conversion, and error boundary — each is a panic/DoS or logic risk. - Add an
unsafeFFI wrapper only as a stretch task, and document every invariant a safe caller must not violate (pointer provenance, length/capacity, initialization, alignment, ownership, thread safety).
Validation — What You Should Be Able to Do Now
- State precisely what Rust's safe subset removes and what it does not — and give an example of a fully exploitable bug (wrong tenant, replay, panic-DoS) in safe Rust.
- Keep parsing separate from authorization, and explain why merging them creates "it parsed so it's allowed" bugs.
- Name the invariants an
unsafe/FFI wrapper must uphold for a safe caller — the exact review you do at any language boundary.
The Broader Perspective
You now hold the calibrated view of memory safety that distinguishes a senior engineer from a
language partisan: safe languages are a large, real win that shifts the threat model from "memory
corruption anywhere" to "logic, resources, and the unsafe/FFI seams." That precision matters when
you advise a team on a rewrite, when you scope a review (focus the deepest scrutiny on unsafe/FFI),
and when you reason about Go's surviving hazards (slice aliasing, goroutine races, unbounded
concurrency — Phase 01 WARMUP, Chapter 11). The tool changes the shape of the risk; it never removes
your job.
Interview Angle
- "Why can a memory-safe service still be security-critical?" — Memory safety removes corruption
classes, not authorization flaws, parser differentials, resource-exhaustion DoS, secret handling, or
unsafe/FFI hazards. A Rust/Go service that authorizes the wrong tenant or panics on crafted input is fully exploitable. - "What invariants must an
unsafewrapper uphold?" — Pointer provenance, length/capacity, initialization, alignment, ownership, and thread safety, such that no safe caller can break them.
References
- Phase 01 WARMUP Chapter 11; The Rustonomicon; Rust Unsafe Code Guidelines.
cargo-fuzz/proptestdocumentation; Phase 02 (JWT/token validation as a protocol).