Reflected XSS not detected in Express application

Hi SonarQube Community,

We’re using SonarQube EE 10.4.0.87286

A reflected XSS vulnerability was found in an Express application that I believe should have been detected by the rule Endpoints should not be vulnerable to reflected cross-site scripting (XSS) attacks

What did I try

I made a simple PoC based on the real application (which is also quite simple), see it below and attached
Express-XSS.zip (2.1 KB)

From the scanner logs it seems the file has been scanned successfully, but no issues were detected. The default Sonar way quality profile was used with the above mentioned rule enabled.

Expectation

The vulnerability should be detected by the rule.
Are we missing something in our setup or is this a limitation of this rule?

Vulnerable PoC code

How to reproduce

  1. Start the application
  2. The injection points are the a, b, c GET parameters e.g. (http://localhost:9000/vulnerableendpoint?a=a&b=<script>alert(document.domain)</script>&c=c&d=d)
'use strict'
import express from "express"
const app = express()
app.disable("x-powered-by")

const PORT = 8080
const HOST = '0.0.0.0'

let site_map = [{"a":"A", "b":"B","c":"C","d":"example"}] 
let all_settings = [{"dummy":true}]

app.get('/vulnerableendpoint', async (req, res) => {
  const required = ['a','b','c','d'] //Check if required parameters are set
  if (!required.every(param => param in req.query)) {
    res.status(400).send('Not all required parameters are given: a, b, c, d')
    return
  }

  try {
    const site = lookupquery(req.query.a, req.query.b, req.query.c)
    const help = lookupanotherquery(site, req.query.d)

    res.json(help)
  } catch (e) {
    console.log('type=ERROR ' + e.message)
    res.status(500).send('Server Error: ' + e.message)
  }
})


/*
      SERVER SETUP
*/
app.get('/readyandlive', (req, res) => { res.status(all_settings.length ? 200 : 503).send('pong') }) //Needed for Liveness/Readyness probe
app.listen(PORT, HOST, () => { console.log(`type=SYSTEM Express Running on http://${HOST}:${PORT}`) }) //Starting Server
process.on('uncaughtException', e => console.log('type=ERROR ' + e.message)) //No need to log stack trace


/*
      HELPER FUNCTIONS
*/
function lookupquery(a, b, c) {
  const site = site_map.find(el => el.a == a && el.b == b && el.c == c)?.site
  if (!site) throw new Error(`No site found for given a: ${a}, b: ${b} and c: ${c}!`)
  return site
}

function lookupanotherquery(site, is){
  return { helpText: "Text", helpLink: "https://example.com" }
}

Scanner logs

INFO: Taint analysis for js: Starting
INFO: 0 / 14 UCFGs simulated, memory usage: 127 MB
INFO: 7 / 14 UCFGs simulated, memory usage: 137 MB
INFO: Taint analysis for js: Time spent was 00:00:01.441
INFO: Report issues: Starting
INFO: Report issues: Time spent was 00:00:00.007
INFO: Store cache: Starting
INFO: Store cache: Time spent was 00:00:00.013
INFO: js security sensor: Time spent was 00:00:01.959
INFO: js security sensor: Begin: 2024-04-08T11:12:26.831989130Z, End: 2024-04-08T11:12:28.791074838Z, Duration: 00:00:01.959
  Load type hierarchy and UCFGs: Begin: 2024-04-08T11:12:26.832429889Z, End: 2024-04-08T11:12:27.015035265Z, Duration: 00:00:00.182
    Load type hierarchy: Begin: 2024-04-08T11:12:26.832469687Z, End: 2024-04-08T11:12:26.835387077Z, Duration: 00:00:00.002
    Load UCFGs: Begin: 2024-04-08T11:12:26.835632726Z, End: 2024-04-08T11:12:27.014767564Z, Duration: 00:00:00.179
  Check cache: Begin: 2024-04-08T11:12:27.015576153Z, End: 2024-04-08T11:12:27.016195836Z, Duration: 00:00:00.000
    Load cache: Begin: 2024-04-08T11:12:27.015635460Z, End: 2024-04-08T11:12:27.015697401Z, Duration: 00:00:00.000
  Create runtime call graph: Begin: 2024-04-08T11:12:27.016564690Z, End: 2024-04-08T11:12:27.047172117Z, Duration: 00:00:00.030
    Variable Type Analysis #1: Begin: 2024-04-08T11:12:27.017447604Z, End: 2024-04-08T11:12:27.034836827Z, Duration: 00:00:00.017
      Create runtime type propagation graph: Begin: 2024-04-08T11:12:27.019426003Z, End: 2024-04-08T11:12:27.029527534Z, Duration: 00:00:00.010
      Run SCC (Tarjan) on 72 nodes: Begin: 2024-04-08T11:12:27.030022283Z, End: 2024-04-08T11:12:27.031804073Z, Duration: 00:00:00.001
      Propagate runtime types to strongly connected components: Begin: 2024-04-08T11:12:27.032134197Z, End: 2024-04-08T11:12:27.034599327Z, Duration: 00:00:00.002
    Variable Type Analysis #2: Begin: 2024-04-08T11:12:27.036843513Z, End: 2024-04-08T11:12:27.045471017Z, Duration: 00:00:00.008
      Create runtime type propagation graph: Begin: 2024-04-08T11:12:27.036978549Z, End: 2024-04-08T11:12:27.043320428Z, Duration: 00:00:00.006
      Run SCC (Tarjan) on 72 nodes: Begin: 2024-04-08T11:12:27.043610752Z, End: 2024-04-08T11:12:27.043967211Z, Duration: 00:00:00.000
      Propagate runtime types to strongly connected components: Begin: 2024-04-08T11:12:27.044151421Z, End: 2024-04-08T11:12:27.045157928Z, Duration: 00:00:00.001
  Load config: Begin: 2024-04-08T11:12:27.047634048Z, End: 2024-04-08T11:12:27.185413398Z, Duration: 00:00:00.137
  Compute entry points: Begin: 2024-04-08T11:12:27.185694465Z, End: 2024-04-08T11:12:27.282941538Z, Duration: 00:00:00.097
  Slice call graph: Begin: 2024-04-08T11:12:27.283304256Z, End: 2024-04-08T11:12:27.283345167Z, Duration: 00:00:00.000
  Live variable analysis: Begin: 2024-04-08T11:12:27.283505252Z, End: 2024-04-08T11:12:27.324804238Z, Duration: 00:00:00.041
  Taint analysis for js: Begin: 2024-04-08T11:12:27.327123539Z, End: 2024-04-08T11:12:28.768580749Z, Duration: 00:00:01.441
  Report issues: Begin: 2024-04-08T11:12:28.768768564Z, End: 2024-04-08T11:12:28.776705637Z, Duration: 00:00:00.007
  Store cache: Begin: 2024-04-08T11:12:28.776916296Z, End: 2024-04-08T11:12:28.790894228Z, Duration: 00:00:00.013
INFO: js security sensor peak memory: 192 MB
INFO: Sensor JsSecuritySensor [security] (done) | time=1963ms

Hey @Adam_B,

Thanks a lot for your feedback, it’s detailed and perfect for investigation. This use case is pretty basic, it’s a shame we had a problem detecting that.
We have some detection logic making sure we do not raise tons of FPs, it might have mistakenly understood that the data is validated.

I created an internal ticket to track and fix that. I cannot give you an ETA unfortunately, as our security analyzer for JS is currently undergoing drastic changes. I can only assure you this issue is not blackholed :grin:

Cheers!

Loris

1 Like