| Version | Supported |
|---|---|
| 0.x (latest minor) | Yes |
| 0.x (previous minor) | Yes |
| older | No |
Private disclosure preferred. Contact the maintainers via the channel listed at the repository's contact page on GitHub (Insights → Community Standards), or open a GitHub Security Advisory via the repository's "Security" tab → "Report a vulnerability".
Please do NOT open a public issue for suspected vulnerabilities.
- Acknowledgement within 5 business days.
- Assessment + scoped fix plan within 15 business days.
- Coordinated disclosure within 90 days of initial report, or sooner if a public exploit exists.
- CVE assignment where applicable; credit to the reporter on request.
In scope: the solid-pod-rs crate, its default features, its documented public API, its CI/CD configuration.
Out of scope: downstream consumers' integrations — report those directly to those projects.
The following properties are enforced by the library core and exercised by the workspace test suite. They are load-bearing invariants; a regression in any of them should be reported as a vulnerability.
- Raw 32-byte message verification (BIP-340 compliant).
auth::nip98::verify_schnorr_signatureusesVerifyingKey::verify_raw()which verifies against the raw 32-byte event-id hash directly, with no additional pre-hashing. This matches the BIP-340 specification. The k256signature::Verifiertrait'sverify()method must not be used -- it applies an extra SHA-256 tagged hash internally, which produces a double-hash mismatch against any standards-compliant Nostr client signature. - Test fixture signing convention. All test fixtures use
sk.sign_raw(&id_bytes, &[0u8; 32])(raw signing with zero auxiliary randomness), notsk.sign(), to match the BIP-340 raw signing path. Usingsk.sign()in tests would produce signatures that pass the (incorrect) tagged-hash verification path but fail against real Nostr clients.
- Signature verified against the proof's embedded
header.jwk.oidc::verify_dpop_proof_coredispatches onheader.algagainst a hard-coded allowlist and rejects the proof if the signature does not validate. This is the Sprint 9 P0 fix (parity row 62b); the pre-fix code path decoded the body without signature verification and is considered CVE-class. - Algorithm allowlist.
ES256,ES384,RS256,RS384,RS512,PS256,PS384,PS512,EdDSA.alg=noneand the entire HMAC family (HS256,HS384,HS512) are unconditionally rejected. athaccess-token hash binding (RFC 9449 §4.3). Theathclaim must be present and equal tobase64url(SHA-256(bearer_token)); comparison is constant-time.htm/htubinding. The proof'shtmclaim must match the request method;htumust match the request's target URI after normalisation.jtireplay cache. Underdpop-replay-cache, a bounded LRU of seenjtivalues closes the replay window (Solid-OIDC §5.2, RFC 9449 §11.1). Cache is clock-aware and safe under concurrent access.iatskew gate. Proof-JWTiatmust lie within the configured tolerance window (default ±60 s). The Sprint 6 fix corrected a short-circuit bug that made this gate unreachable.
Algorithm::Nonerejected at every boundary — DPoP proofs, access tokens, webhook signatures, ID tokens.- RFC 7638 canonical JWK thumbprints.
Jwk::thumbprintusesBTreeMap-backed canonical JSON serialisation; verified byte-for- byte against the spec's appendix-A test vector. - Access-token verification dispatches on
header.algagainst aJwkSet.verify_access_tokenacceptsTokenVerifyKey::Symmetric(test/dev) orTokenVerifyKey::Asymmetric(JwkSet)(production).
Applied to every outbound HTTP fetch in the library:
- Rejected address classes. RFC 1918 private ranges
(
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16), loopback (127.0.0.0/8,::1), link-local (169.254.0.0/16,fe80::/10), and the cloud-metadata endpoint (169.254.169.254). - DNS-rebinding defence. After the SSRF policy approves a
hostname, the per-call reqwest client is constructed with
.resolve()pinning the TCP connect to the approved IP. A rebind between the SSRF check and the connect cannot redirect the request to an unapproved address. - JWKS discovery re-checks.
fetch_jwksruns the SSRF policy once on the issuer host and a second time on the discoveredjwks_urihost; the issuer approval does not transfer to the JWKS host.
Only the following dotfiles are served; all other dotfile requests return 404 regardless of storage-layer presence:
.acl— WAC access-control documents..meta— RDF metadata sidecars..well-known— the standard discovery tree..quota.json— per-pod quota sidecar (whenquotais enabled).
The allowlist is enforced at the storage boundary (security::dotfile)
and again by the server's DotfileGuard middleware.
- 1 MiB Turtle ACL input cap (configurable via
JSS_MAX_ACL_BYTES). Defends against O(n²) splitter blowup on multi-MB inputs. - 32-level JSON-LD depth cap enforced via a pre-parse depth- counted JSON skim. Defends against stack-overflow recursion bombs (200-level crafted inputs are rejected within ~5 ms).
Under webhook-signing, every outbound webhook delivery is signed
with Ed25519 over @method, @target-uri, content-type,
content-digest (RFC 9530), date, x-solid-notification-id. The
verifier is symmetric so receivers can reuse it to validate inbound
signatures.
FsQuotaStore serialises quota mutations to a temp file under the
pod root and fs::renames into place. Concurrent writers cannot
observe a torn .quota.json.