Skip to content

dpp-resolver: unvalidated gtin enables SSRF/path traversal; tamperable qrCodeUrl enables open redirect #15

Description

@LKSNDRTMLKV

Static read-through audit of crates/dpp-resolver. HTML escaping, JWS verification, caching, and rate limiting are all sound. Two verified findings, both on the same public GTIN-resolution handler.

1. Unvalidated gtin path parameter used in a server-to-server request

Location: src/handlers/resolve_by_gtin.rs:32,147
Issue: format!("{}/public/dpp/by-gtin/{gtin}", state.vault_base_url) — unlike dpp_id, which is validated via is_valid_dpp_id in every other handler specifically to close "a latent SSRF / cache-key / XSS surface" (per src/domain/id.rs:5-8), no equivalent check exists for gtin.
Failure scenario: Axum decodes percent-encoded path segments after routing, so a request to /01/%2E%2E%2Fadmin yields gtin = "../admin", which URL dot-segment normalization collapses against by-gtin/, redirecting the outbound request to a different path on the internal vault host (e.g. {vault_base_url}/public/admin) — a path-traversal/SSRF pivot. The crate's own test suite explicitly models this exact attack shape ("../admin") for dpp_id, confirming it's a known-and-handled category simply missed for gtin.

2. Redirect host trusts a field the codebase itself documents as tamperable

Location: src/handlers/resolve_by_gtin.rs:55-61
Issue: base_url (used for both the 307 redirect Location and linkset URLs) is taken verbatim from passport.get("qrCodeUrl") with no allow-list check against the expected resolver host. qrCodeUrl is a documented mutable field (did.rs MUTABLE_FIELDS test) explicitly called out elsewhere in this crate as "content-binding-exempt and tamperable (red-team RT2-2)" — which is why the sibling resolve_qr_handler deliberately hardcodes https://passport.odal-node.io/dpp/{dpp_id} instead of trusting it.
Failure scenario: If qrCodeUrl is altered post-signature (which the crate's own docs say it can be), a GS1 Digital Link scan (/01/{gtin}) 307-redirects to an attacker-chosen host — an open redirect on exactly the path the QR-code path was specifically hardened against.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecuritySecurity-relevant issue

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions