Summary
The /api/v1/users/super endpoint enforces a restriction that only one super user (Instance Administrator) can be created during initial setup. However, due to a Time-of-Check-Time-of-Use (TOCTOU) race condition in the signupAndLoginSuper() method, concurrent requests can bypass this restriction, allowing multiple unauthorized users to obtain Instance Administrator privileges.
Severity
- CWE: CWE-367 (Time-of-Check Time-of-Use Race Condition)
- CVSS 3.1: AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H — 8.1 (HIGH)
Affected Version
- Appsmith Community Edition v1.97.0-SNAPSHOT (release branch)
- Docker image:
appsmith/appsmith-ce:release (pulled 2026-02-25)
- Commit:
55ac824f8d42f934cc7a69f8abc52880a6ad39ef
Root Cause
The signupAndLoginSuper() method in UserSignupCEImpl.java (lines 270–295) performs a non-atomic check-then-act sequence:
// Step 1: CHECK — query MongoDB for existing users
userService.isUsersEmpty()
.flatMap(isEmpty -> {
if (!Boolean.TRUE.equals(isEmpty)) {
return Mono.error(new AppsmithException(AppsmithError.UNAUTHORIZED_ACCESS));
}
// Step 2: ACT — create user and grant admin (not atomic with Step 1)
return signupAndLogin(user, exchange);
})
.flatMap(user -> userUtils.makeInstanceAdministrator(List.of(user)));
The isUsersEmpty() method (CustomUserRepositoryCEImpl.java, lines 35–44) queries MongoDB without any locking mechanism:
public Mono<Boolean> isUsersEmpty() {
return queryBuilder()
.criteria(Bridge.or(
notExists(User.Fields.isSystemGenerated),
Bridge.isFalse(User.Fields.isSystemGenerated)))
.limit(1).all(IdOnly.class).count().map(count -> count == 0);
}
There is no annotation, no distributed lock, and no MongoDB transaction wrapping the check-and-create sequence. In the reactive WebFlux environment, concurrent requests are processed in parallel, widening the race window significantly.