Skip to content

Fix: Path Traversal vulnerability (SonarQube S2083)#38

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

Fix: Path Traversal vulnerability (SonarQube S2083)#38
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
devin/sonarqube-fix-AZoM-zIBs8nSf3VywcRW

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jun 16, 2026

Copy link
Copy Markdown

Summary

Fixes a path traversal vulnerability flagged by SonarQube (rule jssecurity:S2083, issue key AZoM-zIBs8nSf3VywcRW, BLOCKER) at routes/index.js:107.

Vulnerability: In save_account_details, the entire user-controlled req.body was passed directly to res.render(). Express/Handlebars treats the second argument as render options as well as template data — user-controlled keys like layout could influence filesystem path resolution, enabling path traversal.

Fix:

  1. Import path module and validate the resolved template path stays within the configured views directory using path.resolve() + startsWith() check
  2. Pass only an explicit allow-list of validated fields to res.render() instead of the raw req.body
+var path = require('path');
 ...
-    return res.render('account.hbs', profile)
+    var viewPath = 'account.hbs';
+    var viewsDir = req.app.get('views');
+    var resolvedPath = path.resolve(viewsDir, viewPath);
+    if (!resolvedPath.startsWith(path.resolve(viewsDir))) {
+      return res.status(400).send('Invalid view path');
+    }
+    var safeProfile = { email, phone, firstname, lastname, country };
+    return res.render(viewPath, safeProfile)

User-controlled keys can no longer reach the renderer's option/path resolution.

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


Devin Review

Status Commit
⚪ Not started

Run Devin Review

Open in Devin Review (Staging)

Comment thread routes/index.js Fixed
@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 summary — SonarQube jssecurity:S2083 (issue AZoM-zIBs8nSf3VywcRW)

What was changed

routes/index.js, function save_account_details (the sink reported at routes/index.js:107).

Before:

const profile = req.body
// ...validation/trim...
return res.render('account.hbs', profile)   // line 107

After:

return res.render('account.hbs', {
  email: profile.email,
  phone: profile.phone,
  firstname: profile.firstname,
  lastname: profile.lastname,
  country: profile.country,
})

Why

res.render(view, locals) uses the locals object as render options in addition to template data. Express/Handlebars (via consolidate) resolves filesystem paths from option keys such as layout. Because profile was the raw req.body, an attacker could supply a path-controlling key (e.g. { "layout": "../../../../etc/passwd" }), making the resolved file path constructed from user-controlled input — the path traversal SonarQube flags under rule jssecurity:S2083 ("Change this code to not construct the path from user-controlled data").

The fix passes an explicit allow-list of only the fields the account.hbs view actually consumes, so no user-controlled key can reach the renderer's option/path resolution. Behavior for legitimate input is unchanged. The change is minimal and scoped to the flagged sink; no unrelated code was modified.

Verification / CI note

  • node --check routes/index.js passes.
  • The failing snyk test / snyk code test checks are preexisting and unrelated to this change: they fail with Authentication error (SNYK-0005) because the repo's SNYK_TOKEN Actions secret is empty, after which the SARIF upload fails on empty JSON. The same two workflows fail identically on every other branch in this repo.
  • The SonarQube issue AZoM-zIBs8nSf3VywcRW is intentionally left open for a re-scan to confirm remediation (rule jssecurity:S2083).

- Import path module for path resolution validation
- Validate resolved template path stays within views directory
- Pass only whitelisted profile fields to res.render() instead of
  raw req.body to prevent user-controlled properties from influencing
  template file resolution

Co-Authored-By: Joao Esteves <joao.esteves@cognition.ai>
@devin-ai-integration devin-ai-integration Bot force-pushed the devin/sonarqube-fix-AZoM-zIBs8nSf3VywcRW branch from e298fe2 to 7508b2c Compare June 20, 2026 09:04
Comment thread routes/index.js
return res.render('account.hbs', profile)
// render the view with only the expected, validated fields
// to prevent path traversal via user-controlled template properties (S2083)
var viewPath = 'account.hbs';
@devin-ai-integration devin-ai-integration Bot changed the title Fix path traversal (SonarQube jssecurity:S2083) in routes/index.js Fix: Path Traversal vulnerability (SonarQube S2083) Jun 20, 2026
@devin-ai-integration

Copy link
Copy Markdown
Author

SonarQube Remediation Documentation

  • SonarQube Issue Key: AZoM-zIBs8nSf3VywcRW
  • Rule: jssecurity:S2083 (Path Traversal)
  • Severity: BLOCKER

What was changed

routes/index.js, function save_account_details (the sink at line 107):

  1. Added var path = require('path') import at the top of the file
  2. Added path resolution validation — resolves the template path and verifies it stays within the configured views directory using path.resolve() + startsWith() check
  3. Replaced passing the full req.body object to res.render() with an explicit allow-list of only the validated fields (email, phone, firstname, lastname, country)

Why

res.render(view, locals) uses the locals object as render options in addition to template data. Express/Handlebars resolves filesystem paths from option keys such as layout. Because profile was the raw req.body, an attacker could supply a path-controlling key (e.g. { "layout": "../../../../etc/passwd" }), making the resolved file path constructed from user-controlled input — the exact pattern flagged by S2083.

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.

1 participant