codeql icon indicating copy to clipboard operation
codeql copied to clipboard

CodeQL unable to find out sources of a chosen dataflow node in Javascript

Open lllssskkk opened this issue 5 months ago • 1 comments
trafficstars

Hi, for the following snippet i'm interested in finding out what nodes flow to s + c node.

function main() {
  let s = window.location;
  let c = "";
  let input = s + c;
  eval(input);
}

By looking at the snippet, the answer clearly consists of five dataflow nodes. s + c itself, c , "" , s , window.location.

The followings are methods i tried so far.

CodeQL query for locating s + c explicitly by providing location

import javascript

class SinkNode extends  DataFlow::Node {
    SinkNode() {
      exists(DataFlow::Node node |
        node.hasLocationInfo("filepath to the snippet",
          4, 15, 4, 19) and
        this = node
      )
    }
}

class TestSucessor extends DataFlow::Node {
  TestSucessor() {
    exists(DataFlow::Node node |
      node instanceof Sink and
      exists(DataFlow::Node source |
        source.getASuccessor*() = node and
        this = source
      )
    )
  }
}

This would only mark s + c as a predecessor.

Image

class TestPredecessor extends DataFlow::Node {
  TestPredecessor() {
    exists(DataFlow::Node node |
      node instanceof Sink and
      this = node.getAPredecessor*()
    )
  }
}

This would only mark s + c as a predecessor.


class TestLocalSource extends DataFlow::Node {
  TestLocalSource() {
    exists(DataFlow::Node node |
      this = node.getALocalSource*() and
      node instanceof Sink
    )
  }
}

class TestLocalUse extends DataFlow::SourceNode {
  TestLocalUse() {
    exists(DataFlow::SourceNode src |
      exists(DataFlow::Node node |
        node instanceof Sink and
        src.getALocalUse() = node and
        this = src
      )
    )
  }
}

Neither mark anything out.

I would see that an overkill for this issue is simply using tainted analysis. Mark s+c as sink and use any() for the source. However, i want to keep it simple. Any idea how to do it simply?

Many thanks.

lllssskkk avatar Jun 10 '25 19:06 lllssskkk

Hi @lllssskkk 👋🏻

Thanks for the question! Firstly, the simplest option for finding the add expression would be:

from AddExpr e, DataFlow::Node addNode
where addNode.asExpr() = e
select e, addNode

Beyond that, you will need taint tracking for this, because the AddExpr combines s and c into a new value. So, therefore, the data flow node represented by addNode above is a different value than s and c and so data flow alone is not enough. For example, the following configuration gives me results as expected:

import javascript

module MyAnalysisConfig implements DataFlow::ConfigSig {
  predicate isSource(DataFlow::Node node) { any() }
  predicate isSink(DataFlow::Node node) { exists(AddExpr e | e = node.asExpr()) }
}

module MyAnalysisFlow = TaintTracking::Global<MyAnalysisConfig>;

from DataFlow::Node source, DataFlow::Node sink
where MyAnalysisFlow::flow(source, sink)
select source, sink

mbg avatar Jun 11 '25 17:06 mbg