Skip to content

fix(security): prevent open redirect from user-controlled data (SonarQube jssecurity:S5146) - #222

Open
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/sonarqube-fix-AZhSVLrd4wErqc9Ey1Y4-1787475874
Open

fix(security): prevent open redirect from user-controlled data (SonarQube jssecurity:S5146)#222
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/sonarqube-fix-AZhSVLrd4wErqc9Ey1Y4-1787475874

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Aug 23, 2026

Copy link
Copy Markdown

Summary

adminLoginSuccess in routes/index.js:61 passed the user-controlled redirectPage (from POST /login body, seeded by ?redirectPage= on the login page) directly into res.redirect() — an unvalidated open redirect (SonarQube jssecurity:S5146, issue AZhSVLrd4wErqc9Ey1Y4).

Attack: POST /login with redirectPage=https://evil.[REDACTED SECRET]/login (or protocol-relative //evil.[REDACTED SECRET], or /\evil.[REDACTED SECRET]) sends a freshly authenticated admin to an attacker-controlled page from a trusted-looking link — phishing/credential harvesting, plus Referer leakage.

Fix — allow-list of known in-app paths, everything else falls back to /admin:

const ALLOWED_REDIRECT_PATHS = ['/', '/admin', '/account_details', '/login']

function safeRedirectPath(redirectPage) {
  if (typeof redirectPage !== 'string') return '/admin'
  return ALLOWED_REDIRECT_PATHS.indexOf(redirectPage) !== -1 ? redirectPage : '/admin'
}

- if (redirectPage) { return res.redirect(redirectPage) } else { return res.redirect('/admin') }
+ return res.redirect(safeRedirectPath(redirectPage))

An exact-match allow-list (rather than string sanitization) removes the whole bypass class: absolute URLs, protocol-relative //host, backslash/CRLF variants and javascript: all fail to match. Paths are the routes registered in app.js. No other code touched; other SonarQube findings in this file are out of scope.

Devin-Org: engineering

Link to Devin session: https://app.devin.ai/sessions/1159045a948445109741d10e3c4524b7
Requested by: @joao-cognition


Note

Devin errored when opening this Pull Request as joao-cognition.
As a fallback, Devin opened this PR as itself.


Devin Review

Status Commit
⚪ Not started

Run Devin Review

Devin Review (Staging)

…be jssecurity:S5146)

Co-Authored-By: Joao Esteves <joao.esteves@cognition.ai>
@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration

Copy link
Copy Markdown
Author

Remediation of SonarQube issue AZhSVLrd4wErqc9Ey1Y4 (rule jssecurity:S5146, BLOCKER, project COG-GTM_nodejs-goof, routes/index.js:61): "Change this code to not perform redirects based on user-controlled data."

What was wrong: adminLoginSuccess() took redirectPage, which originates from the POST /login form body (pre-populated from the ?redirectPage= query string on the login page), and passed it unvalidated to res.redirect(). Any absolute URL (https://evil.[REDACTED SECRET]), protocol-relative URL (//evil.[REDACTED SECRET]) or backslash variant (/\evil.[REDACTED SECRET]) would send a just-authenticated admin to an attacker-controlled site — a phishing / credential-harvesting vector, and it leaks the app origin via Referer.

What changed: added ALLOWED_REDIRECT_PATHS (/, /admin, /account_details, /login — the in-app routes registered in app.js) plus a safeRedirectPath() helper; the redirect target must match the allow-list exactly, otherwise it falls back to /admin. Exact matching, rather than string sanitization, eliminates the entire bypass class (absolute, protocol-relative, backslash/CRLF and javascript: variants all simply fail to match).

Scope: routes/index.js only, 1 file, +10/-5. No other SonarQube findings in this file were touched, and the issue was not marked resolved/false-positive in SonarQube.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants