fix(review): harden the E2E-gap fixes after security/regression review

Addresses findings from an independent review of the gap-closing commits:

- R1 (regression): add #[serde(default)] to HttpDispatchPayload.method so
  async-HTTP outbox rows enqueued before the field existed still decode
  after upgrade instead of dead-lettering on "missing field `method`".
  Adds a regression test for the missing-key path.
- method case: uppercase ctx.request.method at the orchestrator boundary
  so it honors its documented "uppercased" contract for extension verbs.
  Route matching is already case-insensitive, so matching is unaffected.
- CLI hardening: percent-encode free-form path segments (app slug, topic
  name, ids, secret name) in the reqwest client so a value containing
  `/ ? #` can't break out of its URL segment. Adds a `seg()` helper +
  unit test and applies it uniformly across all path-interpolating
  methods (new and pre-existing).
- S1 (docs): correct the users::email_available enumeration framing — it
  adds no new vector vs. create's uniqueness error, but is cheaper and
  unthrottled; there is no built-in throttle/CAPTCHA primitive, so the
  honest mitigation is a kv-based counter. Updated SDK doc-comments,
  trait docs, and stdlib-reference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-13 09:59:24 +02:00
parent 30bad27711
commit a91b134285
9 changed files with 156 additions and 4 deletions

View File

@@ -255,6 +255,31 @@ Always include `statusCode` when you mean to set headers or a specific
body shape. Returning a bare value (`42`, `#{ ok: true }`) is fine — it
becomes a `200` body as-is.
### Security note — `users::email_available` and account enumeration
`users::email_available(email) -> bool` is the anonymous-safe way to
pre-check an email during self-serve registration (`users::find_by_email`
is forbidden for anonymous public scripts to prevent enumeration). The
probe returns only `true`/`false` — the same bit a register form already
leaks via "email already taken" (and via `create`'s own uniqueness error),
so it adds no *new* enumeration vector — but it is **cheap (no password
hash), has no side effect, and is not rate limited**, which makes bulk
probing faster than going through `create`.
There is no built-in per-route throttle or CAPTCHA primitive today (only
the global `PICLOUD_MAX_CONCURRENT_EXECUTIONS` cap). If account enumeration
matters for your app, the available mitigation is a `kv`-based counter
keyed on the client (e.g. an IP/email bucket you increment and check) —
the same pattern you'd use for any custom rate limit:
```rhai
// Pre-check, then create. Gate behind your own kv counter if enumeration
// is a concern — the platform does not rate-limit this probe for you.
if !users::email_available(email) {
return #{ statusCode: 409, body: #{ error: "email already registered" } };
}
```
## What's not here
- **Crypto** (sha256/hmac/argon2/encryption) — deferred to a focused