fix(manager-core): F-S-003 require authenticated principal for users::find_by_email

find_by_email gated on AppUsersRead via `require`, which short-circuits
when cx.principal == None. An anonymous public-HTTP script could
iterate emails to enumerate registered users, then pivot to login or
request_password_reset.

Tighten: explicitly return Forbidden when cx.principal is None — scripts
that need a "does this email exist" probe from a public route must
wrap the call in their own auth gate.

AUDIT.md anchor: F-S-003.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-07 20:39:04 +02:00
parent dc40bc7926
commit 22e77e02f0

View File

@@ -496,6 +496,14 @@ impl UsersService for UsersServiceImpl {
cx: &SdkCallCx,
email: &str,
) -> Result<Option<AppUser>, UsersError> {
// F-S-003: an anonymous public-HTTP script could iterate emails
// via find_by_email to enumerate registered users (the `require`
// helper short-circuits on principal == None). Tighten: require
// an authenticated principal — scripts that need a "does this
// email exist?" probe must wrap it in their own auth gate.
if cx.principal.is_none() {
return Err(UsersError::Forbidden);
}
self.require(cx.principal.as_ref(), Capability::AppUsersRead(cx.app_id))
.await?;
let normalized = validate_email(email)?;