Skip to content

Gate the authority and path components of the resource identifier at construction #29

Description

@RobertoIskandarani

The resource identifier is validated at construction on four axes — scheme, userinfo, fragment, and the query grammar (requireScheme, requireNoUserinfo, requireNoFragment, requireValidQuery in ProtectedResourceMetadata). The authority and the path are not.

Both are spliced verbatim into the same two places the query is:

  • the resource_metadata parameter of the 401 WWW-Authenticate challenge;
  • the resource member of the RFC 9728 document, which is served to unauthenticated callers.

WwwAuthenticate.escapeQuotedString strips control characters and escapes \ and ", and nothing else. So the reasoning that justified gating the query applies unchanged to these two components:

  • a raw non-ASCII octet reaches a header field RFC 9110 §5.5 confines to US-ASCII;
  • a character java.net.URI rejects (a space, ", \, |, ^, {, }, <, >, or a malformed percent-escape) clears construction and then throws out of the 401 response path — a 500 where the challenge should be.

The query gate was added first because that was the component the change adding it touched. Closing the other two is the same shape of fix: validate against the RFC 3986 grammar at the same four boundaries, reject rather than rewrite, and say which component failed.

Worth checking before implementing: whether an authority gate should also cover the internationalized-domain case, where the identifier is valid to a browser and to a servlet container but is not a URI.


Three axes, not one — do not scope this to grammar

Review feedback on the change that added the query gate pointed out that "authority and path" understates what is missing, and that the realistic failure is not an exotic character.

1. Whitespace and control characters, anywhere in the identifier. The four construction gates do not reject them outside a query, so https://api.example.com/m cp constructs — and so does a resource read from a ConfigMap or an env var with a trailing \n, which is the case an operator actually hits. URI.create then throws from prmPath(), path() and normalizeRequestUrl(), so every 401 challenge and every DPoP-bound request 500s. This is the same 500-out-of-401 shape the query gate was added to close, on a more likely input.

2. A malformed port. A separate axis from the authority's character grammar and worth its own check.

3. The authority and path character productions — the original scope above.

This is catching up, not leading

cs-sdk has already shipped axes 1 and 2, and its implementation is the reference:

  • src/Authplane/Internal/ResourceIdentifiers.csThrowIfWhitespaceOrBackslash covers whitespace, C0/DEL and backslash. Its doc notes it must run ahead of the absoluteness gate, because Uri.TryCreate trims surrounding whitespace before parsing and would otherwise accept an untrimmed identifier. That ordering detail is easy to miss and is the whole reason the gate works.
  • the same file carries ThrowIfMalformedPort as its own axis.

Read both before implementing, and match the ordering rather than the message.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions