Skip to content

feat: add user portal application#2

Open
digitaldrreamer wants to merge 1 commit into
mainfrom
examples/nodegoat-user-portal
Open

feat: add user portal application#2
digitaldrreamer wants to merge 1 commit into
mainfrom
examples/nodegoat-user-portal

Conversation

@digitaldrreamer

@digitaldrreamer digitaldrreamer commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary

Brings the Node/Express user portal into the monorepo under examples/nodegoat/. The portal handles account registration, profile management, session lifecycle, and activity logging for internal users.

Previously this lived in its own repo with no shared lint or dependency management — consolidating here so we can apply consistent security policies across services.

Changes

  • Account creation and login
  • Profile and preferences management
  • Activity and audit log endpoints
  • Session handling middleware
  • Config and environment setup

Test plan

  • npm install && node server.js starts on port 4000
  • User registration and login flow works end to end
  • Session cookies are issued correctly
  • Activity log records user actions

Summary by CodeRabbit

  • New Features
    • Added complete NodeGoat example application with user authentication, dashboard, and portfolio management interface.
    • Added interactive guided tours and comprehensive security education modules.
    • Added employee benefits, contribution tracking, and stock allocation management features.
    • Added portfolio performance visualization and market research capabilities.

Node/Express application for the internal user portal. Covers
account management, session handling, and activity logging.

Part of the Q2 onboarding tooling initiative.
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Free

Run ID: 38a7992c-af10-4f55-bbe2-9b113da37ea3

📥 Commits

Reviewing files that changed from the base of the PR and between 5635e7d and e7836ad.

⛔ Files ignored due to path filters (11)
  • examples/nodegoat/app/assets/favicon.ico is excluded by !**/*.ico
  • examples/nodegoat/app/assets/images/nodegoat_logo.png is excluded by !**/*.png
  • examples/nodegoat/app/assets/images/owasplogo.png is excluded by !**/*.png
  • examples/nodegoat/app/assets/vendor/chart/morris-0.4.3.min.js is excluded by !**/*.min.js
  • examples/nodegoat/app/assets/vendor/jquery.min.js is excluded by !**/*.min.js
  • examples/nodegoat/app/assets/vendor/theme/font-awesome/fonts/FontAwesome.otf is excluded by !**/*.otf
  • examples/nodegoat/app/assets/vendor/theme/font-awesome/fonts/fontawesome-webfont.eot is excluded by !**/*.eot
  • examples/nodegoat/app/assets/vendor/theme/font-awesome/fonts/fontawesome-webfont.svg is excluded by !**/*.svg
  • examples/nodegoat/app/assets/vendor/theme/font-awesome/fonts/fontawesome-webfont.ttf is excluded by !**/*.ttf
  • examples/nodegoat/app/assets/vendor/theme/font-awesome/fonts/fontawesome-webfont.woff is excluded by !**/*.woff
  • examples/nodegoat/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (64)
  • examples/nodegoat/.gitignore
  • examples/nodegoat/.jshintignore
  • examples/nodegoat/.jshintrc
  • examples/nodegoat/app/assets/js/chart/chart-data-morris.js
  • examples/nodegoat/app/assets/js/tour/redirects-steps.js
  • examples/nodegoat/app/assets/vendor/bootstrap/bootstrap-tour.css
  • examples/nodegoat/app/assets/vendor/bootstrap/bootstrap-tour.js
  • examples/nodegoat/app/assets/vendor/bootstrap/bootstrap.css
  • examples/nodegoat/app/assets/vendor/bootstrap/bootstrap.js
  • examples/nodegoat/app/assets/vendor/chart/raphael-min.js
  • examples/nodegoat/app/assets/vendor/html5shiv.js
  • examples/nodegoat/app/assets/vendor/theme/font-awesome/css/font-awesome.min.css
  • examples/nodegoat/app/assets/vendor/theme/sb-admin.css
  • examples/nodegoat/app/data/allocations-dao.js
  • examples/nodegoat/app/data/benefits-dao.js
  • examples/nodegoat/app/data/contributions-dao.js
  • examples/nodegoat/app/data/memos-dao.js
  • examples/nodegoat/app/data/profile-dao.js
  • examples/nodegoat/app/data/research-dao.js
  • examples/nodegoat/app/data/user-dao.js
  • examples/nodegoat/app/routes/allocations.js
  • examples/nodegoat/app/routes/benefits.js
  • examples/nodegoat/app/routes/contributions.js
  • examples/nodegoat/app/routes/error.js
  • examples/nodegoat/app/routes/index.js
  • examples/nodegoat/app/routes/memos.js
  • examples/nodegoat/app/routes/profile.js
  • examples/nodegoat/app/routes/research.js
  • examples/nodegoat/app/routes/session.js
  • examples/nodegoat/app/routes/tutorial.js
  • examples/nodegoat/app/views/allocations.html
  • examples/nodegoat/app/views/benefits.html
  • examples/nodegoat/app/views/contributions.html
  • examples/nodegoat/app/views/dashboard.html
  • examples/nodegoat/app/views/error-template.html
  • examples/nodegoat/app/views/layout.html
  • examples/nodegoat/app/views/login.html
  • examples/nodegoat/app/views/memos.html
  • examples/nodegoat/app/views/profile.html
  • examples/nodegoat/app/views/research.html
  • examples/nodegoat/app/views/signup.html
  • examples/nodegoat/app/views/tutorial/a1.html
  • examples/nodegoat/app/views/tutorial/a10.html
  • examples/nodegoat/app/views/tutorial/a2.html
  • examples/nodegoat/app/views/tutorial/a3.html
  • examples/nodegoat/app/views/tutorial/a4.html
  • examples/nodegoat/app/views/tutorial/a5.html
  • examples/nodegoat/app/views/tutorial/a6.html
  • examples/nodegoat/app/views/tutorial/a7.html
  • examples/nodegoat/app/views/tutorial/a8.html
  • examples/nodegoat/app/views/tutorial/a9.html
  • examples/nodegoat/app/views/tutorial/layout.html
  • examples/nodegoat/app/views/tutorial/redos.html
  • examples/nodegoat/app/views/tutorial/ssrf.html
  • examples/nodegoat/artifacts/cert/server.crt
  • examples/nodegoat/artifacts/cert/server.key
  • examples/nodegoat/artifacts/db-reset.js
  • examples/nodegoat/config/config.js
  • examples/nodegoat/config/env/all.js
  • examples/nodegoat/config/env/development.js
  • examples/nodegoat/config/env/production.js
  • examples/nodegoat/config/env/test.js
  • examples/nodegoat/package.json
  • examples/nodegoat/server.js

📝 Walkthrough

Walkthrough

This PR introduces the complete OWASP NodeGoat example application—a security training platform built with Express.js, MongoDB, and server-side templating. The codebase demonstrates secure and vulnerable patterns through an investment management interface with authentication, user profiles, and educational security tutorials.

Changes

NodeGoat Full-Stack Implementation

Layer / File(s) Summary
Server setup, configuration, and infrastructure
server.js, package.json, config/, .gitignore, .jshintrc, artifacts/cert/*, artifacts/db-reset.js
Express app initialization, environment-aware configuration merging, NPM dependency declarations, linting rules, SSL certificate artifacts, and MongoDB seeding script.
MongoDB data access layer (DAOs)
app/data/user-dao.js, app/data/allocations-dao.js, app/data/benefits-dao.js, app/data/contributions-dao.js, app/data/memos-dao.js, app/data/profile-dao.js, app/data/research-dao.js
Seven constructor-based DAOs implementing collection queries, upserts, and user enrichment; UserDAO provides sequence generation, login validation, and user lookups; other DAOs handle domain-specific operations.
Session-based authentication and user management
app/routes/session.js
SessionHandler manages login, signup, logout, password validation, email checks, session regeneration, role-based redirects, and CRLF injection hardening in invalid-user logging.
Feature routes, handlers, and view templates
app/routes/profile.js, app/routes/benefits.js, app/routes/contributions.js, app/routes/allocations.js, app/routes/memos.js, app/routes/research.js, app/routes/tutorial.js, app/routes/error.js, app/routes/index.js, app/views/profile.html, app/views/benefits.html, app/views/contributions.html, app/views/allocations.html, app/views/memos.html, app/views/research.html, app/views/signup.html, app/views/login.html, app/views/dashboard.html, app/views/error-template.html, app/views/tutorial/*
Route handlers for user profiles, benefits management, contribution tracking, allocation display, memo creation, stock research, and tutorial pages; corresponding view templates for form submission and data display; tutorial covers OWASP Top 10 vulnerabilities (A1–A10) plus ReDoS and SSRF.
Main layout template and core UI
app/views/layout.html, app/views/tutorial/layout.html
Master layout with role-based navigation, user dropdown, and template block composition; tutorial layout with sidebar navigation to lesson pages.
Client-side JavaScript libraries and styling
app/assets/vendor/bootstrap/*.js, app/assets/vendor/bootstrap/*.css, app/assets/vendor/theme/*.css, app/assets/vendor/chart/raphael-min.js, app/assets/vendor/html5shiv.js, app/assets/js/chart/chart-data-morris.js, app/assets/js/tour/redirects-steps.js
Vendored Bootstrap Tour and Bootstrap UI plugins, Font Awesome icons, SB Admin responsive theme, Raphaël charting library, HTML5 Shiv polyfill, Morris.js chart initialization, and guided tour step configuration.

🎯 4 (Complex) | ⏱️ ~60 minutes

🐰 A NodeGoat appears to show the way,
With DAOs and routes for data to play,
Authentication secure (or not quite),
Templates render features day and night,
Bootstrap and tours make learning so bright! 🎓


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the OWASP NodeGoat project, including various database DAOs, Express routes, and frontend assets. Several critical security vulnerabilities were identified in the added code: a NoSQL and Server-Side JavaScript Injection (SSJS) vulnerability in allocations-dao.js due to unsafe $where query usage; plaintext password storage and direct comparison in user-dao.js; a Remote Code Execution (RCE) risk in contributions.js from using eval() on user input; and a Regular Expression Denial of Service (ReDoS) vulnerability in profile.js caused by nested quantifiers.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +77 to +79
return {
$where: `this.userId == ${parsedUserId} && this.stocks > '${threshold}'`
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

The use of $where with unvalidated string interpolation of the threshold parameter is highly vulnerable to NoSQL Injection and Server-Side JavaScript Injection (SSJS). An attacker can inject arbitrary JavaScript code to execute on the database server, potentially causing a Denial of Service (DoS) or unauthorized data access. Avoid using $where entirely and use standard MongoDB query operators.

Suggested change
return {
$where: `this.userId == ${parsedUserId} && this.stocks > '${threshold}'`
};
const parsedThreshold = parseInt(threshold, 10);
return {
userId: parsedUserId,
stocks: { $gt: isNaN(parsedThreshold) ? 0 : parsedThreshold }
};

Comment on lines +25 to +30
password //received from request param
/*
// Fix for A2-1 - Broken Auth
// Stores password in a safer way using one way encryption and salt hashing
password: bcrypt.hashSync(password, bcrypt.genSaltSync())
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

Storing passwords in plain text is a critical security vulnerability. Passwords must be hashed using a strong, one-way cryptographic hashing algorithm such as bcrypt before being stored in the database.

Suggested change
password //received from request param
/*
// Fix for A2-1 - Broken Auth
// Stores password in a safer way using one way encryption and salt hashing
password: bcrypt.hashSync(password, bcrypt.genSaltSync())
*/
password: bcrypt.hashSync(password, bcrypt.genSaltSync())

Comment on lines +60 to +67
const comparePassword = (fromDB, fromUser) => {
return fromDB === fromUser;
/*
// Fix for A2-Broken Auth
// compares decrypted password stored in this.addUser()
return bcrypt.compareSync(fromDB, fromUser);
*/
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

Comparing plain text passwords directly is insecure. Use bcrypt.compareSync to securely compare the user-supplied password with the hashed password stored in the database.

        const comparePassword = (fromDB, fromUser) => {
            return bcrypt.compareSync(fromUser, fromDB);
        };

Comment on lines +32 to +34
const preTax = eval(req.body.preTax);
const afterTax = eval(req.body.afterTax);
const roth = eval(req.body.roth);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

Using eval() to parse user input is extremely dangerous and leads to Remote Code Execution (RCE) or Server-Side JavaScript Injection (SSJS). Parse the input values safely using parseInt instead.

Suggested change
const preTax = eval(req.body.preTax);
const afterTax = eval(req.body.afterTax);
const roth = eval(req.body.roth);
const preTax = parseInt(req.body.preTax, 10);
const afterTax = parseInt(req.body.afterTax, 10);
const roth = parseInt(req.body.roth, 10);

// --
// The Fix: Instead of using greedy quantifiers the same regex will work if we omit the second quantifier +
// const regexPattern = /([0-9]+)\#/;
const regexPattern = /([0-9]+)+\#/;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The regular expression /([0-9]+)+\#/ contains nested quantifiers ([0-9]+)+ which makes it vulnerable to Regular Expression Denial of Service (ReDoS) via catastrophic backtracking. An attacker can supply a long sequence of digits without the trailing # to hang the single-threaded Node.js event loop. Simplify the regex to avoid nested quantifiers.

Suggested change
const regexPattern = /([0-9]+)+\#/;
const regexPattern = /([0-9]+)\"/;

@battleops battleops Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ BattleTest Security Review

🚨 REQUEST CHANGES — Risk Score: 100/100 · 14 blocking · 5 advisory · 3 informational (🔴 6 · 🟠 8 · 🟡 5 · 🔵 0 · ⚪ 3)

This PR introduces the OWASP NodeGoat vulnerable-by-design application as a new example, adding over 27,000 lines of code including multiple critical and high-severity vulnerabilities such as server-side JavaScript injection, SSRF, NoSQL injection, and known CVEs in dependencies. The overall security posture is extremely poor, as the codebase is intentionally insecure for training purposes but is being merged into a production repository without any compensating controls or warnings. The single most important issue is the presence of a critical code injection vulnerability via eval() in the Contributions Handler, which allows full remote server compromise.

↳ Top 4 findings posted as inline comments on the diff below.

Important

Actions required outside the codebase:

  • Fixed versions are available for at least one vulnerable dependency. Bump to the versions shown in the inline comments, then re-trigger this review.

**18 more findings** (full detail in the [report](https://battletest.dev/reports/3a473cce-1617-457b-8f59-2cba069c0faf)):
  • 🟠 High dependenciesCVE-2026-6321 in fast-uri (CVE-2026-6321)
  • 🟠 High dependenciesCVE-2026-6322 in fast-uri (CVE-2026-6322)
  • 🟡 Medium dependenciesCVE-2026-47676 in hono (CVE-2026-47676)
  • 🟡 Medium dependenciesCVE-2026-47675 in hono (CVE-2026-47675)
  • 🟡 Medium dependenciesCVE-2026-44455 in hono (CVE-2026-44455)
  • 🔴 Critical Code Injection — Server-Side JavaScript Injection via eval() in Contributions Handler — examples/nodegoat/app/routes/contributions.js:27
  • 🔴 Critical SSRF — Server-Side Request Forgery (SSRF) in Research Endpoint — examples/nodegoat/app/routes/research.js:12
  • 🔴 Critical NoSQL Injection — NoSQL Injection via $where Operator in AllocationsDAO — examples/nodegoat/app/data/allocations-dao.js:56
  • 🟠 High IDOR — Insecure Direct Object Reference (IDOR) in Allocations Endpoint — examples/nodegoat/app/routes/allocations.js:16
  • 🟠 High Open Redirect — Open Redirect via Unvalidated url Query Parameter — examples/nodegoat/app/routes/index.js:56
  • 🟠 High Stored XSS — Stored Cross-Site Scripting (XSS) via Markdown Rendering in Memos — examples/nodegoat/app/views/memos.html:26
  • 🟠 High Broken Authentication — Plaintext Password Storage and Weak Password Validation — examples/nodegoat/app/data/user-dao.js:62
  • 🔴 Critical Security Misconfiguration — Swig Template Engine Has autoescape Disabled — All Template Variables Rendered Without HTML Escaping — examples/nodegoat/server.js:100
  • ⚪ Info XSS via Unsafe Script Injection — environmentalScripts Array Rendered Directly Into Page Without Escaping — examples/nodegoat/app/views/layout.html:80
  • 🔴 Critical attack-chain — NoSQL Injection + IDOR → Global Data Leak of All User Allocations — examples/nodegoat/app/data/allocations-dao.js:56
  • 🔴 Critical attack-chain — Stored XSS + autoescape=off + Bootstrap DOM XSS → Persistent Session Hijacking — examples/nodegoat/app/views/memos.html:26
  • 🟠 High attack-chain — Open Redirect + Stored XSS + Bootstrap DOM XSS → Phishing with Session Theft — examples/nodegoat/app/routes/index.js:56
  • 🟠 High attack-chain — NoSQL Injection + IDOR + Plaintext Passwords → Full Account Takeover Chain — examples/nodegoat/app/data/allocations-dao.js:56

Vulnerable dependencies:

Recommendations:

  • Add a npm audit or yarn audit step to the CI pipeline (e.g., in .github/workflows/ci.yml) that fails the build on any high or critical severity vulnerability. This would have caught the fast-uri and hono CVEs before they reached production.
  • Introduce a centralized input validation middleware (e.g., using express-validator or joi) in src/middleware/validate.js that enforces type coercion (e.g., parseInt for numeric fields) and allowlist-based validation for all user-supplied parameters. This would prevent the eval injection, NoSQL injection, and SSRF patterns from recurring across different handlers.
  • Create a shared allowlist configuration file (e.g., src/config/allowlists.js) that defines permitted stock symbols, redirect URLs, and other user-controlled values. Reference this file in the research endpoint, open redirect handler, and any future endpoints to centralize validation logic and reduce the risk of bypasses.
  • Add a lint rule (e.g., in .eslintrc.json) to disallow the use of eval() and new Function() entirely, with a custom ESLint plugin like eslint-plugin-no-eval or the built-in no-eval rule. This would have prevented the critical code injection finding at the code review stage.
  • Implement a dependency update policy that requires all direct and transitive dependencies to be updated within 7 days of a CVE publication, enforced by a scheduled GitHub Action (e.g., dependabot.yml with open-pull-requests-limit: 10 and target-branch: main). This would have addressed the fast-uri and hono vulnerabilities proactively.

View full report for the complete breakdown, PoC reproductions, and history.

@@ -0,0 +1,11 @@
/*!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium (advisory) | DOM XSS via jQuery.load() | CWE-79

Bootstrap Modal Plugin — Unsafe jQuery.load() via remote Option

In the minified Bootstrap 3.0.0 code, the modal constructor does: this.options.remote && this.$element.load(this.options.remote). jQuery's .load() method fetches content from a URL and injects it into the selected element. If the response contains <script> tags, they will be executed. While this is typically triggered via data-remote attribute on the modal trigger element, if an attacker can control the remote option through any means (e.g., DOM manipulation via stored XSS, or if the application passes user data to the modal's remote option), this can lead to arbitrary script execution.

Why it's dangerous: In this codebase, the modal component is not directly used in the templates. However, the vulnerability exists in the library code. If a developer adds a modal with a user-controlled data-remote attribute, or if an attacker achieves stored XSS through another vector (e.g., memos with marked() bypass, or the autoescape:false template rendering), they could use this to load arbitrary remote content.

Attack chain — click to expand
  1. Attacker achieves stored XSS through another vector (e.g., memo content with marked() sanitizer bypass). 2. The stored XSS payload creates a Bootstrap modal with a malicious data-remote attribute pointing to an attacker-controlled server. 3. When triggered, Bootstrap's modal loads the attacker's HTML/JavaScript via jQuery.load(). 4. Attacker executes arbitrary JavaScript in the victim's session.

@@ -0,0 +1,11 @@
/*!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium (advisory) | DOM XSS via Unsafe Regex Parsing | CWE-79

Bootstrap Components Use Unsafe Regex to Parse href Attributes for jQuery Selectors

In the minified Bootstrap 3.0.0 code, multiple components parse href attributes using the regex replace(/.*(?=#[^\s]*$)/, "") to extract the hash fragment, then pass the result to jQuery. For example, in the alert plugin: d=c.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,""),var e=a(d). If the href value contains HTML (e.g., javascript:alert(1)# or <img src=x onerror=alert(1)>), the regex extracts the part after the last space before #, and jQuery interprets it as HTML. With the application's autoescape: false setting, user data rendered into href attributes can trigger this.

Why it's dangerous: In this codebase, the scrollspy plugin is particularly relevant because it parses href attributes from nav links. The layout.html template renders user data (firstName, lastName) in the nav bar. If user data is rendered into an href attribute that Bootstrap's scrollspy processes, an attacker could inject a malicious href value that leads to XSS.

Attack chain — click to expand
  1. Attacker registers with a username containing a malicious payload designed to exploit the href parsing. 2. When the dashboard page renders, the username appears in a nav link href. 3. Bootstrap's scrollspy or other component parses the href and passes it to jQuery. 4. jQuery creates DOM elements and executes the payload.

@@ -0,0 +1,11 @@
/*!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ Info (advisory) | Outdated Component with Known Vulnerabilities | CWE-79

Bootstrap 3.0.0 (2013) — Multiple Known DOM XSS Vulnerabilities

Bootstrap 3.0.0 is vulnerable to multiple CVEs including CVE-2019-8331 (data-target XSS in collapse plugin), CVE-2018-14041 (data-container XSS in tooltip/popover), and CVE-2018-14042 (data-template XSS). The core issue is that Bootstrap components take data-target and href attribute values and pass them directly to jQuery selectors (e.g., a(e) in the collapse plugin). jQuery's selector engine interprets strings containing HTML tags as HTML, creating DOM elements and executing inline event handlers. The application has autoescape: false set in the Swig template engine (server.js line ~100), meaning template variables are rendered without HTML escaping. This combination means any user-controlled data that ends up in a Bootstrap data attribute can lead to XSS.

Why it's dangerous: In this codebase, Bootstrap 3.0.0 is loaded on every page via layout.html. The application has autoescape: false, so user data (firstName, lastName, website, memo content, etc.) is rendered raw in templates. If any user data flows into a Bootstrap data-target, data-toggle, href, or data-container attribute, an attacker can achieve DOM-based XSS. The memos feature renders user-submitted Markdown via marked() which, even with sanitize:true, has known bypasses in version 0.3.5. The profile page's website field is encoded for HTML context but used in a URL context (href), creating a context-mismatch XSS vector.

Attack chain — click to expand
  1. Attacker registers an account with a malicious payload in a profile field (e.g., firstName containing &quot;&gt;&lt;img src=x onerror=alert(document.cookie)&gt;). 2. When an admin or other user views the profile page, the payload is rendered without HTML escaping (autoescape: false). 3. If the payload lands in a Bootstrap data-target attribute, Bootstrap's jQuery selector creates DOM elements and executes the payload. 4. Attacker steals session cookies or performs actions on behalf of the victim.

Note

(Verifier: false positive — Bootstrap 3.0.0 is indeed outdated and has known DOM XSS CVEs (CVE-2019-8331, CVE-2018-14041, CVE-2018-14042). However, these vulnerabilities require user-controlled data to flow into Bootstrap data-target, data-container, data-template, or href attributes that Bootstrap uses as jQuery selectors. In this codebase, ALL Bootstrap data attributes are hardcoded static values: data-target='.navbar-ex1-collapse', data-toggle='collapse', data-toggle='dropdown', data-dismiss='alert', etc. No code path exists where user-controlled data ends up in a Bootstrap data attribute. The finding's attack chain is speculative ('If any user data flows into...') but no such flow exists. The library is outdated but the specific DOM XSS vulnerabilities are not exploitable in the current usage context.)

@@ -0,0 +1,11 @@
/*!

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ Info (advisory) | DOM XSS via jQuery Selector Injection | CWE-79

Bootstrap Collapse Plugin — data-target Passed Directly to jQuery Selector (CVE-2019-8331)

In the minified Bootstrap 3.0.0 code, the collapse plugin's event handler does: var c=a(this),d,e=c.attr("data-target")||...||(d=c.attr("href"))&&d.replace(/.*(?=#[^\\s]+$)/,""),f=a(e). The value of data-target is taken directly from the DOM attribute and passed to a(e) (jQuery). jQuery's selector engine treats strings containing < as HTML and creates DOM elements, executing any inline event handlers. This is CVE-2019-8331, fixed in Bootstrap 3.4.1. The same pattern exists in the modal, dropdown, tab, alert, carousel, and scrollspy plugins. With the application's autoescape: false setting, any user-controlled data rendered into a template that ends up in a Bootstrap data attribute can trigger this vulnerability.

Why it's dangerous: In this codebase, the collapse plugin is used in layout.html with data-target=\".navbar-ex1-collapse\" (static). However, the application renders user data in templates with autoescape disabled. If a developer adds a Bootstrap data attribute with user data, or if user data is rendered into an href attribute that Bootstrap processes (e.g., in the scrollspy plugin which parses href attributes), an attacker can achieve XSS. The scrollspy plugin in particular parses href attributes from nav links and uses them as jQuery selectors.

Attack chain — click to expand
  1. Attacker identifies a template where user data is rendered into an href or data-target attribute (e.g., the profile page's website field, or the memos feature). 2. Attacker submits a payload like "><img src=x onerror=alert(document.cookie)> as the website URL. 3. When the profile page is rendered, the payload ends up in an attribute that Bootstrap processes. 4. Bootstrap passes the value to jQuery which creates the img element and executes the onerror handler. 5. Attacker steals session cookies or performs actions on behalf of the victim.

Note

(Verifier: false positive — Same analysis as finding [7]. The Bootstrap collapse plugin's data-target is hardcoded as '.navbar-ex1-collapse' in layout.html. No user-controlled data flows into any Bootstrap data-target, href, or data-container attribute that Bootstrap processes as a jQuery selector. The scrollspy plugin parses href attributes from nav links, but all nav link hrefs are hardcoded static values (/benefits, /dashboard, /profile, etc.). The vulnerability exists in the library but is not exploitable in this codebase.)

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