Release v2.11.4
Summary
Hardens what a deployment can declare as an anonymous path, fixes four security defects found while doing so, and adds a security spec suite that gates every pull request.
The anonymous path rules moved from a deny-list of route-template characters to an allow-list of RFC 3986 unreserved characters. A deny-list has to anticipate every character that means something to one of the two matchers — the middlewares matching a literal and the router matching a route template — and the cost of missing one is a prefix that means different things to each. Refusals stay fail-closed, leaving the path authenticated, and are now reported at startup instead of being silent.
Added
- Security spec suite covering broken access control, injection, open redirect, request smuggling and security misconfiguration. Specs run a real AuthProxy in front of a real origin that records every forwarded request, so assertions can be made about what reached the backend rather than only what the client saw (#42)
- A Security workflow that runs the security specs and a known-vulnerable dependency check on every pull request, with no path filter (#42)
- Startup warnings naming every declared anonymous path that was refused, and why
Changed
- Anonymous path entries are validated against an allow-list of letters, digits,
-,.,_and~. Percent-encoding, backslashes, semicolons, authority syntax, control characters and non-ASCII are refused, as is any.or..segment - Anonymous path entries may no longer target the prefixes AuthProxy answers itself (
/.cratis,/_pages,/invite,/register,/signin-*). Declaring one never made an endpoint more public — it took the endpoint away from AuthProxy and handed it to a backend - CI now builds Debug as well as Release and runs the specs. Previously no tests ran in CI at all
Security
- Fixed an open redirect on the login, link, tenant-selection and logout endpoints. Each carried its own version of a same-site check and they disagreed: two accepted
//evil.testoutright, and the two that rejected it still accepted/\evil.test, which every major browser treats identically. The login endpoint is the most exposed of the four — itsreturnUrlis accepted anonymously and becomes the post-authentication redirect, so a victim completes a genuine sign-in before landing on the attacker's page - Fixed authorization being granted by the presence of the script-readable
.cratis-identitycookie, whose value was never read. Sending any value for it alongside a valid session skipped every service's/.cratis/meauthorization call, so a user whose access had been revoked stayed authorized for as long as they kept sending it. The decision now lives in a separate HTTP-only cookie sealed with data protection and bound to the principal and tenant it was issued for - Fixed an unauthenticated HTTP 500 reachable by sending both a
Service-IDheader and a?service=parameter. The two route variants shared a template and an order, and endpoint selection runs ahead of authentication