Repository navigation
Security fixes for 1.8.15: reset/invite tokens and attachment authorization - #152
Merged
Merged
Conversation
…ization Three reported issues (GHSA-3vm2-w3rc-mcgg, GHSA-c6h7-9644-p3pw, GHSA-gx88-52rh-qhcr): - The users API bound the invitation token to the calling admin instead of the new user, so an invitee could set the admin's password. Tokens are now issued by PasswordResets.issue() after the target user is persisted, in the API, the admin Users page and sendReset. - Reset and invitation links were built from the request's Host header and never expired. FSUtils.publicBaseUrl() builds them from FACTION_BASE_URL, then FACTION_OAUTH_CALLBACK, falling back to the request only when neither is configured. Tokens carry an expiry (1h reset, 72h invite), a new token supersedes the user's earlier ones, and Register redeems through PasswordResets.redeem(), which rejects and deletes expired tokens. Rows written before this release are treated as expiring an hour after creation. - /service/fileUpload checked only for a session. FileAccess now resolves a stored file to its assessment or verification and applies the same rules as opening the assessment (assessor, team, access level, or verification assessor/assigned remediation) to download, delete and attach, returning 403 otherwise and 404 for unknown ids. Uploads record the creator. Tests: PasswordResetsTest, PublicBaseUrlTest, FileAccessTest.
…ged out Since 1.8.8 the AccessControlInterceptor only admits unauthenticated requests to the "/" and "/sso" namespaces, but Register stayed in /portal, so every password reset and invitation link redirected to the login page. Register now lives in "/" with login, reset and sendReset, the emailed links point at /Register, and RegisterReachabilityTest pins the mapping.
summitt
force-pushed
the
security-fixes-1.8.15
branch
from
October 3, 2026 06:20
776c0c4 to
56ae172
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the three reports from @Ishbarna (GHSA-3vm2-w3rc-mcgg, GHSA-c6h7-9644-p3pw, GHSA-gx88-52rh-qhcr) and bumps the version to 1.8.15-SNAPSHOT.
Misbound invitation token (users API).
POST /api/users/addUserwithverify=truebound the token to the calling admin; redeeming it set the admin's password. Tokens are now issued byPasswordResets.issue()after the new user is persisted, in the API, the admin Users page andsendReset.Host-header reset links and non-expiring tokens. Links are built by
FSUtils.publicBaseUrl()fromFACTION_BASE_URL, thenFACTION_OAUTH_CALLBACK, and only fall back to the request when neither is set. Tokens expire (1h for resets, 72h for invitations), a new token supersedes the user's earlier ones, andRegisterredeems viaPasswordResets.redeem(), which rejects and deletes expired tokens. Pre-existing rows without an expiry are treated as expiring an hour after creation.IDOR in
/service/fileUpload.FileAccessresolves a stored file to its assessment or verification and applies the same rules as opening the assessment (assessor, team, access level; verification assessor or assigned remediation) to download, delete-by-id, delete-by-name and attach. Unauthorized requests get 403, unknown ids 404. Uploads recordcreatorId.Reset and invitation links never worked when logged out (since 1.8.8). The
AccessControlInterceptoronly admits unauthenticated requests to the/and/ssonamespaces, butRegistersat in/portal, so every emailed link redirected to the login page.Registernow lives in/, the links point at/Register?uid=..., andRegisterReachabilityTestpins the mapping. This also closes the reachability caveat in GHSA-c6h7.Verification
PasswordResetsTest(binding, supersession, expiry, legacy rows),PublicBaseUrlTest(configured URL beats Host header),FileAccessTest(two teams, five users, assessment and verification files). Each watched failing first.EntityUnitTest.testVulnerabilitySectionfailure.Hostheader,sendResetissued a 1h token for the stranger; the emailed link now opens the registration page without a session, a new password set through it logs in, and the consumed, expired, legacy (no expiry, 2h old) and unknown tokens all show "Link is no longer valid".verify=trueproduced a token bound to the invitee (user 4), not the admin (user 2), with a 72h expiry;sendResetproduced a 1h token.Deployment note
Set
FACTION_BASE_URL(or keepFACTION_OAUTH_CALLBACKset) in any deployment that sends email, so links never derive from the request.