fix(security): validate user names on API save path — stored-XSS hardening (chain PR 8)#36356
fix(security): validate user names on API save path — stored-XSS hardening (chain PR 8)#36356mbiuki wants to merge 1 commit into
Conversation
…ed XSS (private-issues#651) The modern UserAPIImpl.save() path (used by PUT /api/v1/users/current) only checked name length, so HTML metacharacters in first/last name were persisted verbatim and rendered unescaped in the admin Users panel — enabling stored XSS. Add UserHelper.validateName(), driven by the existing UserName.regexp.pattern (the same allow-list the legacy UserLocalManagerImpl enforced), and call it from UserAPIImpl.save() so every save path is covered. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
❌ Linked Issue Needs Team LabelThis PR is linked to issue #651, but that issue has no How to fix this:Apply a This comment was automatically generated by the issue linking workflow |
|
Claude finished @mbiuki's task in 1m 51s —— View job Rollback Safety Analysis
Result: ✅ Safe To RollbackThe changes in this PR consist entirely of pure Java business-logic additions — no unsafe category is triggered:
Summary: Rolling back to the previous release removes the added Label applied: AI: Safe To Rollback |
🤖 dotBot Review (Bedrock)Reviewed 2 file(s); 3 candidate(s) → 2 confirmed, 0 uncertain (unverified, kept for review). Confirmed findings
us.deepseek.r1-v1:0 · Run: #28387045872 · tokens: in: 11573 · out: 3549 · total: 15122 · calls: 6 · est. ~$0.035 |
| * | ||
| * The allowed character set is driven by the configurable | ||
| * {@code UserName.regexp.pattern} system property; validation is skipped only when | ||
| * that property is not configured. |
There was a problem hiding this comment.
🟠 [High] Incorrect configuration property retrieval
The code uses SystemProperties.get("UserName.regexp.pattern") instead of Config.getStringProperty(...). In dotCMS, Config handles configuration from multiple sources (properties file, env vars), while SystemProperties only reads system properties. This could result in the regex pattern not being correctly loaded, leading to improper validation. Grep results show other instances in the codebase (e.g., UserLocalManagerImpl.java) use Config.getStringProperty("UserName.regexp.pattern"), indicating this is a deviation from standard practice.
| throw new DotSecurityException( | ||
| "User doesn't have permission to save the user which is trying to be saved"); | ||
| } | ||
| validateName(userToSave.getFirstName(), userToSave.getLastName()); |
There was a problem hiding this comment.
🟠 [High] Potential NullPointerException in user name validation
UserAPIImpl.java line 446 passes userToSave.getFirstName() and getLastName() directly to UserHelper.validateName(). If either returns null, String.matches() will throw NullPointerException. The validation helper does not appear to perform null checks before regex matching in the current PR changes.
Security fix — XSS→RCE chain, server-side validation layer (tracking: dotCMS/private-issues#651)
Class: Stored XSS input gap (CWE-79) · Severity: Critical (chain) · Routing: Team : Maintenance
Problem
PUT /api/v1/users/current→UserResourceHelper→UserAPIImpl.save()only ranvalidateMaximumLength. The</>-blockingUserName.regexp.patternwas enforced only in the legacyUserLocalManagerImpl.validate(), which this path never calls — sogivenName/surnamewere stored verbatim and later rendered unescaped in the admin Users panel.Fix
UserHelper.validateName(firstName, lastName), driven by the existing configurableUserName.regexp.patternallow-list.UserAPIImpl.save(user, user, respectFrontEndRoles)choke point, so all save paths (REST + legacy) are covered.Defense-in-depth note
This is the input-validation layer. The canonical fix is output encoding at render (separate PR for
view_users.jsp) — filters alone are bypassable (the original report used a field-split bypass). Both layers should land.Verification
./mvnw compile -pl :dotcms-core -DskipTests✅