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:
@@ -496,6 +496,14 @@ impl UsersService for UsersServiceImpl {
|
|||||||
cx: &SdkCallCx,
|
cx: &SdkCallCx,
|
||||||
email: &str,
|
email: &str,
|
||||||
) -> Result<Option<AppUser>, UsersError> {
|
) -> 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))
|
self.require(cx.principal.as_ref(), Capability::AppUsersRead(cx.app_id))
|
||||||
.await?;
|
.await?;
|
||||||
let normalized = validate_email(email)?;
|
let normalized = validate_email(email)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user