Merge pull request #1694 from asger-semmle/concatenation-operand

Approved by xiemaisi
This commit is contained in:
semmle-qlci
2019-08-08 12:41:30 +01:00
committed by GitHub
9 changed files with 796 additions and 41 deletions

View File

@@ -13,6 +13,7 @@ module StringConcatenation {
}
/** Gets the `n`th operand to the string concatenation defining `node`. */
pragma[nomagic]
DataFlow::Node getOperand(DataFlow::Node node, int n) {
exists(AddExpr add | node = add.flow() |
n = 0 and result = add.getLeftOperand().flow()

View File

@@ -438,30 +438,72 @@ module StringOps {
}
/**
* A data flow node that concatenates strings and returns the result.
* Holds if `first` and `second` are adjacent leaves in a concatenation tree.
*/
class Concatenation extends DataFlow::Node {
Concatenation() {
pragma[nomagic]
private predicate adjacentLeaves(ConcatenationLeaf first, ConcatenationLeaf second) {
exists(Concatenation parent, int i |
first = parent.getOperand(i).getLastLeaf() and
second = parent.getOperand(i + 1).getFirstLeaf()
)
}
/**
* A data flow node that performs a string concatenation or occurs as an operand
* in a string concatenation.
*
* For example, the expression `x + y + z` contains the following concatenation
* nodes:
* - The leaf nodes `x`, `y`, and `z`
* - The intermediate node `x + y`, which is both a concatenation and an operand
* - The root node `x + y + z`
*
*
* Note that the following are not recognized a string concatenations:
* - Array `join()` calls with a non-empty separator
* - Tagged template literals
*
*
* Also note that all `+` operators are seen as string concatenations,
* even in cases where it is used for arithmetic.
*
* Examples of string concatenations:
* ```
* x + y
* x += y
* [x, y].join('')
* Array(x, y).join('')
* `Hello, ${message}`
* ```
*/
class ConcatenationNode extends DataFlow::Node {
pragma[inline]
ConcatenationNode() {
exists(StringConcatenation::getAnOperand(this))
or
this = StringConcatenation::getAnOperand(_)
}
/**
* Gets the `n`th operand of this string concatenation.
*/
DataFlow::Node getOperand(int n) {
pragma[inline]
ConcatenationOperand getOperand(int n) {
result = StringConcatenation::getOperand(this, n)
}
/**
* Gets an operand of this string concatenation.
*/
DataFlow::Node getAnOperand() {
pragma[inline]
ConcatenationOperand getAnOperand() {
result = StringConcatenation::getAnOperand(this)
}
/**
* Gets the number of operands of this string concatenation.
*/
pragma[inline]
int getNumOperand() {
result = StringConcatenation::getNumOperand(this)
}
@@ -469,20 +511,23 @@ module StringOps {
/**
* Gets the first operand of this string concatenation.
*/
DataFlow::Node getFirstOperand() {
pragma[inline]
ConcatenationOperand getFirstOperand() {
result = StringConcatenation::getFirstOperand(this)
}
/**
* Gets the last operand of this string concatenation
*/
DataFlow::Node getLastOperand() {
pragma[inline]
ConcatenationOperand getLastOperand() {
result = StringConcatenation::getLastOperand(this)
}
/**
* Holds if this only acts as a string coercion, such as `"" + x`.
*/
pragma[inline]
predicate isCoercion() {
StringConcatenation::isCoercion(this)
}
@@ -492,15 +537,220 @@ module StringOps {
* it is a concatenation operator that is not itself the immediate operand to
* another concatenation operator.
*/
pragma[inline]
predicate isRoot() {
StringConcatenation::isRoot(this)
}
/**
* Holds if this is a leaf in the concatenation tree, that is, it is not
* itself a concatenation.
*/
pragma[inline]
predicate isLeaf() {
not exists(StringConcatenation::getAnOperand(this))
}
/**
* Gets the root of the concatenation tree in which this is an operator.
*/
Concatenation getRoot() {
pragma[inline]
ConcatenationRoot getRoot() {
result = StringConcatenation::getRoot(this)
}
/**
* Gets the enclosing concatenation in which this is an operand, if any.
*/
pragma[inline]
Concatenation getParentConcatenation() {
this = StringConcatenation::getAnOperand(result)
}
/**
* Gets the last leaf in this concatenation tree.
*
* For example, `z` is the last leaf in `x + y + z`.
*/
pragma[inline]
ConcatenationLeaf getLastLeaf() {
result = StringConcatenation::getLastOperand*(this)
}
/**
* Gets the first leaf in this concatenation tree.
*
* For example, `x` is the first leaf in `x + y + z`.
*/
pragma[inline]
ConcatenationLeaf getFirstLeaf() {
result = StringConcatenation::getFirstOperand*(this)
}
/**
* Gets the leaf that is occurs immediately before this leaf in the
* concatenation tree, if any.
*
* For example, `y` is the previous leaf from `z` in `x + y + z`.
*/
pragma[inline]
ConcatenationLeaf getPreviousLeaf() {
adjacentLeaves(result, this)
}
/**
* Gets the leaf that is occurs immediately after this leaf in the
* concatenation tree, if any.
*
* For example, `y` is the next leaf from `x` in `x + y + z`.
*/
pragma[inline]
ConcatenationLeaf getNextLeaf() {
adjacentLeaves(this, result)
}
}
/**
* A data flow node that performs a string concatenation and returns the result.
*
* Examples:
* ```
* x + y
* x += y
* [x, y].join('')
* Array(x, y).join('')
* `Hello ${message}`
* ```
*
* See `ConcatenationNode` for more information.
*/
class Concatenation extends ConcatenationNode {
pragma[inline]
Concatenation() {
exists(StringConcatenation::getAnOperand(this))
}
}
/**
* One of the operands in a string concatenation.
*
* Examples:
* ```
* x + y // x and y are operands
* [x, y].join('') // x and y are operands
* `Hello ${message}` // `Hello ` and message are operands
* ```
*
* See `ConcatenationNode` for more information.
*/
class ConcatenationOperand extends ConcatenationNode {
pragma[inline]
ConcatenationOperand() {
this = StringConcatenation::getAnOperand(_)
}
}
/**
* A data flow node that performs a string concatenation, and is not an
* immediate operand in a larger string concatenation.
*
* Examples:
* ```
* // x + y + z is a root, but the inner x + y is not
* return x + y + z;
* ```
*
* See `ConcatenationNode` for more information.
*/
class ConcatenationRoot extends Concatenation {
pragma[inline]
ConcatenationRoot() {
isRoot()
}
/**
* Gets a leaf in this concatenation tree that this node is the root of.
*/
pragma[inline]
ConcatenationLeaf getALeaf() {
this = StringConcatenation::getRoot(result)
}
/**
* Returns the concatenation of all constant operands in this concatenation,
* ignoring the non-constant parts entirely.
*
* For example, for the following concatenation
* ```
* `Hello ${person}, how are you?`
* ```
* the result is `"Hello , how are you?"`
*/
string getConstantStringParts() {
result = getStringValue()
or
not exists(getStringValue()) and
result = strictconcat(StringLiteralLike leaf |
leaf = getALeaf().asExpr()
|
leaf.getStringValue()
order by leaf.getFirstToken().getIndex() asc
)
}
}
/** A string literal or template literal without any substitutions. */
private class StringLiteralLike extends Expr {
StringLiteralLike() {
this instanceof StringLiteral or
this instanceof TemplateElement
}
}
/**
* An operand to a concatenation that is not itself a concatenation.
*
* Example:
* ```
* x + y + z // x, y, and z are leaves
* [x, y + z].join('') // x, y, and z are leaves
* ```
*
* See `ConcatenationNode` for more information.
*/
class ConcatenationLeaf extends ConcatenationOperand {
pragma[inline]
ConcatenationLeaf() {
isLeaf()
}
}
/**
* The root node in a concatenation of one or more strings containing HTML fragments.
*/
class HtmlConcatenationRoot extends ConcatenationRoot {
pragma[noinline]
HtmlConcatenationRoot() {
getConstantStringParts().regexpMatch("(?s).*</?[a-zA-Z][^\\r\\n<>/]*/?>.*")
}
}
/**
* A data flow node that is part of an HTML string concatenation.
*/
class HtmlConcatenationNode extends ConcatenationNode {
HtmlConcatenationNode() {
getRoot() instanceof HtmlConcatenationRoot
}
}
/**
* A data flow node that is part of an HTML string concatenation,
* and is not itself a concatenation operator.
*/
class HtmlConcatenationLeaf extends ConcatenationLeaf {
HtmlConcatenationLeaf() {
getRoot() instanceof HtmlConcatenationRoot
}
}
}

View File

@@ -42,6 +42,11 @@ class TemplateLiteral extends Expr, @templateliteral {
*/
Expr getAnElement() { result = getElement(_) }
/**
* Gets the number of elements of this template literal.
*/
int getNumElement() { result = count(getAnElement()) }
override predicate isImpure() { getAnElement().isImpure() }
override string getStringValue() {

View File

@@ -34,11 +34,11 @@ private class HeuristicCodeInjectionSink extends HeuristicSink, CodeInjection::S
srcPattern = "(?s).*function\\s*\\(.*\\).*" or
srcPattern = "(?s).*(\\(.*\\)|[A-Za-z_]+)\\s?=>.*"
|
isContatenatedWithString(this, srcPattern)
isConcatenatedWithString(this, srcPattern)
)
or
// dynamic property name
isContatenatedWithStrings("(?is)[a-z]+\\[", this, "(?s)\\].*")
isConcatenatedWithStrings("(?is)[a-z]+\\[", this, "(?s)\\].*")
}
}
@@ -53,8 +53,8 @@ private class HeuristicDomBasedXssSink extends HeuristicSink, DomBasedXss::DomBa
HeuristicDomBasedXssSink() {
isAssignedToOrConcatenatedWith(this, "(?i)(html|innerhtml)") or
isArgTo(this, "(?i)(html|render)") or
isContatenatedWithString(this, "(?is).*<.*>.*") or
isContatenatedWithStrings("(?is).*<[a-z ]+.*", this, "(?s).*>.*")
this instanceof StringOps::HtmlConcatenationLeaf or
isConcatenatedWithStrings("(?is).*<[a-z ]+.*", this, "(?s).*>.*")
}
}
@@ -62,8 +62,8 @@ private class HeuristicReflectedXssSink extends HeuristicSink, ReflectedXss::Ref
HeuristicReflectedXssSink() {
isAssignedToOrConcatenatedWith(this, "(?i)(html|innerhtml)") or
isArgTo(this, "(?i)(html|render)") or
isContatenatedWithString(this, "(?is).*<.*>.*") or
isContatenatedWithStrings("(?is).*<[a-z ]+.*", this, "(?s).*>.*")
this instanceof StringOps::HtmlConcatenationLeaf or
isConcatenatedWithStrings("(?is).*<[a-z ]+.*", this, "(?s).*>.*")
}
}
@@ -71,7 +71,7 @@ private class HeuristicSqlInjectionSink extends HeuristicSink, SqlInjection::Sin
HeuristicSqlInjectionSink() {
isAssignedToOrConcatenatedWith(this, "(?i)(sql|query)") or
isArgTo(this, "(?i)(query)") or
isContatenatedWithString(this,
isConcatenatedWithString(this,
"(?s).*(ALTER|COUNT|CREATE|DATABASE|DELETE|DISTINCT|DROP|FROM|GROUP|INSERT|INTO|LIMIT|ORDER|SELECT|TABLE|UPDATE|WHERE).*")
}
}
@@ -94,10 +94,10 @@ private class HeuristicTaintedPathSink extends HeuristicSink, TaintedPath::Sink
pathPattern = "(?i)([a-z0-9_.-]+/){2,}" or
pathPattern = "(?i)(/[a-z0-9_.-]+){2,}"
|
isContatenatedWithString(this, pathPattern)
isConcatenatedWithString(this, pathPattern)
)
or
isContatenatedWithStrings(".*/", this, "/.*")
isConcatenatedWithStrings(".*/", this, "/.*")
}
}

View File

@@ -66,42 +66,28 @@ predicate isArgTo(DataFlow::Node arg, string regexp) {
}
/**
* Holds if `n` is concatenated with something with a name that matches `regexp`.
* Holds if `n` is a concatenation containing something with a name that matches `regexp`.
*/
bindingset[regexp]
predicate isConcatenatedWith(DataFlow::Node n, string regexp) {
exists(Expr other |
other = n.asExpr().(AddExpr).getAnOperand() or
other = n.asExpr().(AssignAddExpr).getRhs()
|
isReadFrom(DataFlow::valueNode(other), regexp)
)
predicate isConcatenatedWith(StringOps::Concatenation n, string regexp) {
isReadFrom(n.getAnOperand(), regexp)
}
/**
* Holds if `n` is concatenated with a string constant that matches `regexp`.
* Holds if `n` is a concatenation containing something with a name that matches `regexp`.
*/
bindingset[regexp]
predicate isContatenatedWithString(DataFlow::Node n, string regexp) {
exists(Expr other |
other = n.asExpr().(AddExpr).getAnOperand() or
other = n.asExpr().(AssignAddExpr).getRhs()
|
other.getStringValue().regexpMatch(regexp)
)
predicate isConcatenatedWithString(StringOps::Concatenation n, string regexp) {
n.getAnOperand().getStringValue().regexpMatch(regexp)
}
/**
* Holds if `n` is concatenated between two string constants that match `lRegexp` and `rRegexp` respectively.
*/
bindingset[lRegexp, rRegexp]
predicate isContatenatedWithStrings(string lRegexp, DataFlow::Node n, string rRegexp) {
exists(AddExpr concat1, AddExpr concat2 |
concat1.getLeftOperand().getStringValue().regexpMatch(lRegexp) and
concat1.getRightOperand() = n.asExpr() and
concat2.getLeftOperand() = concat1 and
concat2.getRightOperand().getStringValue().regexpMatch(rRegexp)
)
predicate isConcatenatedWithStrings(string lRegexp, StringOps::ConcatenationLeaf n, string rRegexp) {
n.getPreviousLeaf().getStringValue().regexpMatch(lRegexp) and
n.getNextLeaf().getStringValue().regexpMatch(rRegexp)
}
/**

View File

@@ -3,6 +3,7 @@
| sinks.js:4:9:4:12 | sink |
| sinks.js:6:16:6:19 | sink |
| sinks.js:7:5:7:22 | getScript() + sink |
| sinks.js:8:5:8:18 | script += sink |
| sinks.js:8:15:8:18 | sink |
| sinks.js:9:5:9:18 | sink += script |
| sinks.js:10:11:10:14 | sink |
@@ -25,8 +26,11 @@
| sinks.js:41:5:41:26 | sink + ... ion(){" |
| sinks.js:42:5:42:18 | "x => " + sink |
| sinks.js:43:14:43:17 | sink |
| sinks.js:45:5:45:18 | "<div>" + sink |
| sinks.js:45:5:45:11 | "<div>" |
| sinks.js:45:15:45:18 | sink |
| sinks.js:46:5:46:20 | '<div foo="foo"' |
| sinks.js:46:24:46:27 | sink |
| sinks.js:46:31:46:42 | 'bar="bar">' |
| sinks.js:48:5:48:20 | "SELECT " + sink |
| sinks.js:50:5:50:21 | "/foo/bar" + sink |
| sinks.js:51:5:51:21 | "foo/bar/" + sink |

View File

@@ -0,0 +1,474 @@
concatenation
| closure.js:5:1:5:37 | build(' ... 'four') |
| closure.js:5:1:5:46 | build(' ... 'five' |
| closure.js:5:14:5:28 | 'two' + 'three' |
| html-concat.js:2:14:2:26 | `<b>${x}</b>` |
| html-concat.js:3:14:3:26 | `<B>${x}</B>` |
| html-concat.js:5:21:5:47 | `Hey <s ... trong>` |
| html-concat.js:7:18:10:24 | `\\n H ... m!</i>` |
| html-concat.js:13:3:13:18 | buffer |
| html-concat.js:13:3:13:18 | buffer += '<li>' |
| html-concat.js:14:3:14:13 | buffer |
| html-concat.js:14:3:14:13 | buffer += x |
| html-concat.js:15:3:15:15 | buffer |
| html-concat.js:15:3:15:15 | buffer += '!' |
| tst.js:3:3:3:12 | x |
| tst.js:3:3:3:12 | x += "two" |
| tst.js:4:3:4:14 | x |
| tst.js:4:3:4:14 | x += "three" |
| tst.js:5:3:5:13 | x |
| tst.js:5:3:5:13 | x += "four" |
| tst.js:12:5:12:26 | x |
| tst.js:12:5:12:26 | x += "o ... + "two" |
| tst.js:12:10:12:18 | "one" + y |
| tst.js:12:10:12:26 | "one" + y + "two" |
| tst.js:14:3:14:13 | x |
| tst.js:14:3:14:13 | x += "last" |
| tst.js:19:11:19:23 | "one" + "two" |
| tst.js:20:3:20:25 | x |
| tst.js:20:3:20:25 | x += (" ... "four") |
| tst.js:20:9:20:24 | "three" + "four" |
| tst.js:21:10:21:19 | x + "five" |
| tst.js:25:10:25:32 | ["one", ... three"] |
| tst.js:25:10:25:41 | ["one", ... oin("") |
| tst.js:29:10:29:37 | Array(" ... three") |
| tst.js:29:10:29:46 | Array(" ... oin("") |
| tst.js:33:10:33:41 | new Arr ... three") |
| tst.js:33:10:33:50 | new Arr ... oin("") |
| tst.js:37:12:37:18 | ["one"] |
| tst.js:40:10:40:20 | xs.join("") |
| tst.js:44:12:44:20 | ["first"] |
| tst.js:49:10:49:20 | xs.join("") |
| tst.js:53:10:53:34 | `one ${ ... three` |
| tst.js:61:10:61:34 | `first ... } last` |
| tst.js:87:5:87:14 | x |
| tst.js:87:5:87:14 | x += 'two' |
| tst.js:89:3:89:14 | x |
| tst.js:89:3:89:14 | x += 'three' |
concatenationOperand
| closure.js:5:1:5:37 | build(' ... 'four') |
| closure.js:5:7:5:11 | 'one' |
| closure.js:5:14:5:18 | 'two' |
| closure.js:5:14:5:28 | 'two' + 'three' |
| closure.js:5:22:5:28 | 'three' |
| closure.js:5:31:5:36 | 'four' |
| closure.js:5:41:5:46 | 'five' |
| html-concat.js:2:15:2:17 | <b> |
| html-concat.js:2:20:2:20 | x |
| html-concat.js:2:22:2:25 | </b> |
| html-concat.js:3:15:3:17 | <B> |
| html-concat.js:3:20:3:20 | x |
| html-concat.js:3:22:3:25 | </B> |
| html-concat.js:5:22:5:33 | Hey <strong> |
| html-concat.js:5:36:5:36 | x |
| html-concat.js:5:38:5:46 | </strong> |
| html-concat.js:7:19:8:10 | \\n Hello |
| html-concat.js:8:13:8:13 | x |
| html-concat.js:8:15:10:23 | .\\n \\n ... um!</i> |
| html-concat.js:13:3:13:8 | buffer |
| html-concat.js:13:13:13:18 | '<li>' |
| html-concat.js:14:3:14:8 | buffer |
| html-concat.js:14:13:14:13 | x |
| html-concat.js:15:3:15:8 | buffer |
| html-concat.js:15:13:15:15 | '!' |
| tst.js:3:3:3:3 | x |
| tst.js:3:8:3:12 | "two" |
| tst.js:4:3:4:3 | x |
| tst.js:4:8:4:14 | "three" |
| tst.js:5:3:5:3 | x |
| tst.js:5:8:5:13 | "four" |
| tst.js:12:5:12:5 | x |
| tst.js:12:10:12:14 | "one" |
| tst.js:12:10:12:18 | "one" + y |
| tst.js:12:10:12:26 | "one" + y + "two" |
| tst.js:12:18:12:18 | y |
| tst.js:12:22:12:26 | "two" |
| tst.js:14:3:14:3 | x |
| tst.js:14:8:14:13 | "last" |
| tst.js:19:11:19:15 | "one" |
| tst.js:19:19:19:23 | "two" |
| tst.js:20:3:20:3 | x |
| tst.js:20:8:20:25 | ("three" + "four") |
| tst.js:20:9:20:15 | "three" |
| tst.js:20:19:20:24 | "four" |
| tst.js:21:10:21:10 | x |
| tst.js:21:14:21:19 | "five" |
| tst.js:25:10:25:32 | ["one", ... three"] |
| tst.js:25:11:25:15 | "one" |
| tst.js:25:18:25:22 | "two" |
| tst.js:25:25:25:31 | "three" |
| tst.js:29:10:29:37 | Array(" ... three") |
| tst.js:29:16:29:20 | "one" |
| tst.js:29:23:29:27 | "two" |
| tst.js:29:30:29:36 | "three" |
| tst.js:33:10:33:41 | new Arr ... three") |
| tst.js:33:20:33:24 | "one" |
| tst.js:33:27:33:31 | "two" |
| tst.js:33:34:33:40 | "three" |
| tst.js:37:12:37:18 | ["one"] |
| tst.js:37:13:37:17 | "one" |
| tst.js:44:12:44:20 | ["first"] |
| tst.js:44:13:44:19 | "first" |
| tst.js:53:11:53:14 | one |
| tst.js:53:17:53:17 | x |
| tst.js:53:19:53:23 | two |
| tst.js:53:26:53:26 | x |
| tst.js:53:28:53:33 | three |
| tst.js:61:11:61:16 | first |
| tst.js:61:19:61:19 | x |
| tst.js:61:23:61:23 | x |
| tst.js:61:27:61:27 | x |
| tst.js:61:29:61:33 | last |
| tst.js:87:5:87:5 | x |
| tst.js:87:10:87:14 | 'two' |
| tst.js:89:3:89:3 | x |
| tst.js:89:8:89:14 | 'three' |
concatenationLeaf
| closure.js:5:7:5:11 | 'one' |
| closure.js:5:14:5:18 | 'two' |
| closure.js:5:22:5:28 | 'three' |
| closure.js:5:31:5:36 | 'four' |
| closure.js:5:41:5:46 | 'five' |
| html-concat.js:2:15:2:17 | <b> |
| html-concat.js:2:20:2:20 | x |
| html-concat.js:2:22:2:25 | </b> |
| html-concat.js:3:15:3:17 | <B> |
| html-concat.js:3:20:3:20 | x |
| html-concat.js:3:22:3:25 | </B> |
| html-concat.js:5:22:5:33 | Hey <strong> |
| html-concat.js:5:36:5:36 | x |
| html-concat.js:5:38:5:46 | </strong> |
| html-concat.js:7:19:8:10 | \\n Hello |
| html-concat.js:8:13:8:13 | x |
| html-concat.js:8:15:10:23 | .\\n \\n ... um!</i> |
| html-concat.js:13:3:13:8 | buffer |
| html-concat.js:13:13:13:18 | '<li>' |
| html-concat.js:14:3:14:8 | buffer |
| html-concat.js:14:13:14:13 | x |
| html-concat.js:15:3:15:8 | buffer |
| html-concat.js:15:13:15:15 | '!' |
| tst.js:3:3:3:3 | x |
| tst.js:3:8:3:12 | "two" |
| tst.js:4:3:4:3 | x |
| tst.js:4:8:4:14 | "three" |
| tst.js:5:3:5:3 | x |
| tst.js:5:8:5:13 | "four" |
| tst.js:12:5:12:5 | x |
| tst.js:12:10:12:14 | "one" |
| tst.js:12:18:12:18 | y |
| tst.js:12:22:12:26 | "two" |
| tst.js:14:3:14:3 | x |
| tst.js:14:8:14:13 | "last" |
| tst.js:19:11:19:15 | "one" |
| tst.js:19:19:19:23 | "two" |
| tst.js:20:3:20:3 | x |
| tst.js:20:8:20:25 | ("three" + "four") |
| tst.js:20:9:20:15 | "three" |
| tst.js:20:19:20:24 | "four" |
| tst.js:21:10:21:10 | x |
| tst.js:21:14:21:19 | "five" |
| tst.js:25:11:25:15 | "one" |
| tst.js:25:18:25:22 | "two" |
| tst.js:25:25:25:31 | "three" |
| tst.js:29:16:29:20 | "one" |
| tst.js:29:23:29:27 | "two" |
| tst.js:29:30:29:36 | "three" |
| tst.js:33:20:33:24 | "one" |
| tst.js:33:27:33:31 | "two" |
| tst.js:33:34:33:40 | "three" |
| tst.js:37:13:37:17 | "one" |
| tst.js:44:13:44:19 | "first" |
| tst.js:53:11:53:14 | one |
| tst.js:53:17:53:17 | x |
| tst.js:53:19:53:23 | two |
| tst.js:53:26:53:26 | x |
| tst.js:53:28:53:33 | three |
| tst.js:61:11:61:16 | first |
| tst.js:61:19:61:19 | x |
| tst.js:61:23:61:23 | x |
| tst.js:61:27:61:27 | x |
| tst.js:61:29:61:33 | last |
| tst.js:87:5:87:5 | x |
| tst.js:87:10:87:14 | 'two' |
| tst.js:89:3:89:3 | x |
| tst.js:89:8:89:14 | 'three' |
concatenationNode
| closure.js:5:1:5:37 | build(' ... 'four') |
| closure.js:5:1:5:46 | build(' ... 'five' |
| closure.js:5:7:5:11 | 'one' |
| closure.js:5:14:5:18 | 'two' |
| closure.js:5:14:5:28 | 'two' + 'three' |
| closure.js:5:22:5:28 | 'three' |
| closure.js:5:31:5:36 | 'four' |
| closure.js:5:41:5:46 | 'five' |
| html-concat.js:2:14:2:26 | `<b>${x}</b>` |
| html-concat.js:2:15:2:17 | <b> |
| html-concat.js:2:20:2:20 | x |
| html-concat.js:2:22:2:25 | </b> |
| html-concat.js:3:14:3:26 | `<B>${x}</B>` |
| html-concat.js:3:15:3:17 | <B> |
| html-concat.js:3:20:3:20 | x |
| html-concat.js:3:22:3:25 | </B> |
| html-concat.js:5:21:5:47 | `Hey <s ... trong>` |
| html-concat.js:5:22:5:33 | Hey <strong> |
| html-concat.js:5:36:5:36 | x |
| html-concat.js:5:38:5:46 | </strong> |
| html-concat.js:7:18:10:24 | `\\n H ... m!</i>` |
| html-concat.js:7:19:8:10 | \\n Hello |
| html-concat.js:8:13:8:13 | x |
| html-concat.js:8:15:10:23 | .\\n \\n ... um!</i> |
| html-concat.js:13:3:13:8 | buffer |
| html-concat.js:13:3:13:18 | buffer |
| html-concat.js:13:3:13:18 | buffer += '<li>' |
| html-concat.js:13:13:13:18 | '<li>' |
| html-concat.js:14:3:14:8 | buffer |
| html-concat.js:14:3:14:13 | buffer |
| html-concat.js:14:3:14:13 | buffer += x |
| html-concat.js:14:13:14:13 | x |
| html-concat.js:15:3:15:8 | buffer |
| html-concat.js:15:3:15:15 | buffer |
| html-concat.js:15:3:15:15 | buffer += '!' |
| html-concat.js:15:13:15:15 | '!' |
| tst.js:3:3:3:3 | x |
| tst.js:3:3:3:12 | x |
| tst.js:3:3:3:12 | x += "two" |
| tst.js:3:8:3:12 | "two" |
| tst.js:4:3:4:3 | x |
| tst.js:4:3:4:14 | x |
| tst.js:4:3:4:14 | x += "three" |
| tst.js:4:8:4:14 | "three" |
| tst.js:5:3:5:3 | x |
| tst.js:5:3:5:13 | x |
| tst.js:5:3:5:13 | x += "four" |
| tst.js:5:8:5:13 | "four" |
| tst.js:12:5:12:5 | x |
| tst.js:12:5:12:26 | x |
| tst.js:12:5:12:26 | x += "o ... + "two" |
| tst.js:12:10:12:14 | "one" |
| tst.js:12:10:12:18 | "one" + y |
| tst.js:12:10:12:26 | "one" + y + "two" |
| tst.js:12:18:12:18 | y |
| tst.js:12:22:12:26 | "two" |
| tst.js:14:3:14:3 | x |
| tst.js:14:3:14:13 | x |
| tst.js:14:3:14:13 | x += "last" |
| tst.js:14:8:14:13 | "last" |
| tst.js:19:11:19:15 | "one" |
| tst.js:19:11:19:23 | "one" + "two" |
| tst.js:19:19:19:23 | "two" |
| tst.js:20:3:20:3 | x |
| tst.js:20:3:20:25 | x |
| tst.js:20:3:20:25 | x += (" ... "four") |
| tst.js:20:8:20:25 | ("three" + "four") |
| tst.js:20:9:20:15 | "three" |
| tst.js:20:9:20:24 | "three" + "four" |
| tst.js:20:19:20:24 | "four" |
| tst.js:21:10:21:10 | x |
| tst.js:21:10:21:19 | x + "five" |
| tst.js:21:14:21:19 | "five" |
| tst.js:25:10:25:32 | ["one", ... three"] |
| tst.js:25:10:25:41 | ["one", ... oin("") |
| tst.js:25:11:25:15 | "one" |
| tst.js:25:18:25:22 | "two" |
| tst.js:25:25:25:31 | "three" |
| tst.js:29:10:29:37 | Array(" ... three") |
| tst.js:29:10:29:46 | Array(" ... oin("") |
| tst.js:29:16:29:20 | "one" |
| tst.js:29:23:29:27 | "two" |
| tst.js:29:30:29:36 | "three" |
| tst.js:33:10:33:41 | new Arr ... three") |
| tst.js:33:10:33:50 | new Arr ... oin("") |
| tst.js:33:20:33:24 | "one" |
| tst.js:33:27:33:31 | "two" |
| tst.js:33:34:33:40 | "three" |
| tst.js:37:12:37:18 | ["one"] |
| tst.js:37:13:37:17 | "one" |
| tst.js:40:10:40:20 | xs.join("") |
| tst.js:44:12:44:20 | ["first"] |
| tst.js:44:13:44:19 | "first" |
| tst.js:49:10:49:20 | xs.join("") |
| tst.js:53:10:53:34 | `one ${ ... three` |
| tst.js:53:11:53:14 | one |
| tst.js:53:17:53:17 | x |
| tst.js:53:19:53:23 | two |
| tst.js:53:26:53:26 | x |
| tst.js:53:28:53:33 | three |
| tst.js:61:10:61:34 | `first ... } last` |
| tst.js:61:11:61:16 | first |
| tst.js:61:19:61:19 | x |
| tst.js:61:23:61:23 | x |
| tst.js:61:27:61:27 | x |
| tst.js:61:29:61:33 | last |
| tst.js:87:5:87:5 | x |
| tst.js:87:5:87:14 | x |
| tst.js:87:5:87:14 | x += 'two' |
| tst.js:87:10:87:14 | 'two' |
| tst.js:89:3:89:3 | x |
| tst.js:89:3:89:14 | x |
| tst.js:89:3:89:14 | x += 'three' |
| tst.js:89:8:89:14 | 'three' |
operand
| closure.js:5:1:5:37 | build(' ... 'four') | 0 | closure.js:5:7:5:11 | 'one' |
| closure.js:5:1:5:37 | build(' ... 'four') | 1 | closure.js:5:14:5:28 | 'two' + 'three' |
| closure.js:5:1:5:37 | build(' ... 'four') | 2 | closure.js:5:31:5:36 | 'four' |
| closure.js:5:1:5:46 | build(' ... 'five' | 0 | closure.js:5:1:5:37 | build(' ... 'four') |
| closure.js:5:1:5:46 | build(' ... 'five' | 1 | closure.js:5:41:5:46 | 'five' |
| closure.js:5:14:5:28 | 'two' + 'three' | 0 | closure.js:5:14:5:18 | 'two' |
| closure.js:5:14:5:28 | 'two' + 'three' | 1 | closure.js:5:22:5:28 | 'three' |
| html-concat.js:2:14:2:26 | `<b>${x}</b>` | 0 | html-concat.js:2:15:2:17 | <b> |
| html-concat.js:2:14:2:26 | `<b>${x}</b>` | 1 | html-concat.js:2:20:2:20 | x |
| html-concat.js:2:14:2:26 | `<b>${x}</b>` | 2 | html-concat.js:2:22:2:25 | </b> |
| html-concat.js:3:14:3:26 | `<B>${x}</B>` | 0 | html-concat.js:3:15:3:17 | <B> |
| html-concat.js:3:14:3:26 | `<B>${x}</B>` | 1 | html-concat.js:3:20:3:20 | x |
| html-concat.js:3:14:3:26 | `<B>${x}</B>` | 2 | html-concat.js:3:22:3:25 | </B> |
| html-concat.js:5:21:5:47 | `Hey <s ... trong>` | 0 | html-concat.js:5:22:5:33 | Hey <strong> |
| html-concat.js:5:21:5:47 | `Hey <s ... trong>` | 1 | html-concat.js:5:36:5:36 | x |
| html-concat.js:5:21:5:47 | `Hey <s ... trong>` | 2 | html-concat.js:5:38:5:46 | </strong> |
| html-concat.js:7:18:10:24 | `\\n H ... m!</i>` | 0 | html-concat.js:7:19:8:10 | \\n Hello |
| html-concat.js:7:18:10:24 | `\\n H ... m!</i>` | 1 | html-concat.js:8:13:8:13 | x |
| html-concat.js:7:18:10:24 | `\\n H ... m!</i>` | 2 | html-concat.js:8:15:10:23 | .\\n \\n ... um!</i> |
| html-concat.js:13:3:13:18 | buffer | 0 | html-concat.js:13:3:13:8 | buffer |
| html-concat.js:13:3:13:18 | buffer | 1 | html-concat.js:13:13:13:18 | '<li>' |
| html-concat.js:13:3:13:18 | buffer += '<li>' | 0 | html-concat.js:13:3:13:8 | buffer |
| html-concat.js:13:3:13:18 | buffer += '<li>' | 1 | html-concat.js:13:13:13:18 | '<li>' |
| html-concat.js:14:3:14:13 | buffer | 0 | html-concat.js:14:3:14:8 | buffer |
| html-concat.js:14:3:14:13 | buffer | 1 | html-concat.js:14:13:14:13 | x |
| html-concat.js:14:3:14:13 | buffer += x | 0 | html-concat.js:14:3:14:8 | buffer |
| html-concat.js:14:3:14:13 | buffer += x | 1 | html-concat.js:14:13:14:13 | x |
| html-concat.js:15:3:15:15 | buffer | 0 | html-concat.js:15:3:15:8 | buffer |
| html-concat.js:15:3:15:15 | buffer | 1 | html-concat.js:15:13:15:15 | '!' |
| html-concat.js:15:3:15:15 | buffer += '!' | 0 | html-concat.js:15:3:15:8 | buffer |
| html-concat.js:15:3:15:15 | buffer += '!' | 1 | html-concat.js:15:13:15:15 | '!' |
| tst.js:3:3:3:12 | x | 0 | tst.js:3:3:3:3 | x |
| tst.js:3:3:3:12 | x | 1 | tst.js:3:8:3:12 | "two" |
| tst.js:3:3:3:12 | x += "two" | 0 | tst.js:3:3:3:3 | x |
| tst.js:3:3:3:12 | x += "two" | 1 | tst.js:3:8:3:12 | "two" |
| tst.js:4:3:4:14 | x | 0 | tst.js:4:3:4:3 | x |
| tst.js:4:3:4:14 | x | 1 | tst.js:4:8:4:14 | "three" |
| tst.js:4:3:4:14 | x += "three" | 0 | tst.js:4:3:4:3 | x |
| tst.js:4:3:4:14 | x += "three" | 1 | tst.js:4:8:4:14 | "three" |
| tst.js:5:3:5:13 | x | 0 | tst.js:5:3:5:3 | x |
| tst.js:5:3:5:13 | x | 1 | tst.js:5:8:5:13 | "four" |
| tst.js:5:3:5:13 | x += "four" | 0 | tst.js:5:3:5:3 | x |
| tst.js:5:3:5:13 | x += "four" | 1 | tst.js:5:8:5:13 | "four" |
| tst.js:12:5:12:26 | x | 0 | tst.js:12:5:12:5 | x |
| tst.js:12:5:12:26 | x | 1 | tst.js:12:10:12:26 | "one" + y + "two" |
| tst.js:12:5:12:26 | x += "o ... + "two" | 0 | tst.js:12:5:12:5 | x |
| tst.js:12:5:12:26 | x += "o ... + "two" | 1 | tst.js:12:10:12:26 | "one" + y + "two" |
| tst.js:12:10:12:18 | "one" + y | 0 | tst.js:12:10:12:14 | "one" |
| tst.js:12:10:12:18 | "one" + y | 1 | tst.js:12:18:12:18 | y |
| tst.js:12:10:12:26 | "one" + y + "two" | 0 | tst.js:12:10:12:18 | "one" + y |
| tst.js:12:10:12:26 | "one" + y + "two" | 1 | tst.js:12:22:12:26 | "two" |
| tst.js:14:3:14:13 | x | 0 | tst.js:14:3:14:3 | x |
| tst.js:14:3:14:13 | x | 1 | tst.js:14:8:14:13 | "last" |
| tst.js:14:3:14:13 | x += "last" | 0 | tst.js:14:3:14:3 | x |
| tst.js:14:3:14:13 | x += "last" | 1 | tst.js:14:8:14:13 | "last" |
| tst.js:19:11:19:23 | "one" + "two" | 0 | tst.js:19:11:19:15 | "one" |
| tst.js:19:11:19:23 | "one" + "two" | 1 | tst.js:19:19:19:23 | "two" |
| tst.js:20:3:20:25 | x | 0 | tst.js:20:3:20:3 | x |
| tst.js:20:3:20:25 | x | 1 | tst.js:20:8:20:25 | ("three" + "four") |
| tst.js:20:3:20:25 | x += (" ... "four") | 0 | tst.js:20:3:20:3 | x |
| tst.js:20:3:20:25 | x += (" ... "four") | 1 | tst.js:20:8:20:25 | ("three" + "four") |
| tst.js:20:9:20:24 | "three" + "four" | 0 | tst.js:20:9:20:15 | "three" |
| tst.js:20:9:20:24 | "three" + "four" | 1 | tst.js:20:19:20:24 | "four" |
| tst.js:21:10:21:19 | x + "five" | 0 | tst.js:21:10:21:10 | x |
| tst.js:21:10:21:19 | x + "five" | 1 | tst.js:21:14:21:19 | "five" |
| tst.js:25:10:25:32 | ["one", ... three"] | 0 | tst.js:25:11:25:15 | "one" |
| tst.js:25:10:25:32 | ["one", ... three"] | 1 | tst.js:25:18:25:22 | "two" |
| tst.js:25:10:25:32 | ["one", ... three"] | 2 | tst.js:25:25:25:31 | "three" |
| tst.js:25:10:25:41 | ["one", ... oin("") | 0 | tst.js:25:10:25:32 | ["one", ... three"] |
| tst.js:29:10:29:37 | Array(" ... three") | 0 | tst.js:29:16:29:20 | "one" |
| tst.js:29:10:29:37 | Array(" ... three") | 1 | tst.js:29:23:29:27 | "two" |
| tst.js:29:10:29:37 | Array(" ... three") | 2 | tst.js:29:30:29:36 | "three" |
| tst.js:29:10:29:46 | Array(" ... oin("") | 0 | tst.js:29:10:29:37 | Array(" ... three") |
| tst.js:33:10:33:41 | new Arr ... three") | 0 | tst.js:33:20:33:24 | "one" |
| tst.js:33:10:33:41 | new Arr ... three") | 1 | tst.js:33:27:33:31 | "two" |
| tst.js:33:10:33:41 | new Arr ... three") | 2 | tst.js:33:34:33:40 | "three" |
| tst.js:33:10:33:50 | new Arr ... oin("") | 0 | tst.js:33:10:33:41 | new Arr ... three") |
| tst.js:37:12:37:18 | ["one"] | 0 | tst.js:37:13:37:17 | "one" |
| tst.js:40:10:40:20 | xs.join("") | 0 | tst.js:37:12:37:18 | ["one"] |
| tst.js:44:12:44:20 | ["first"] | 0 | tst.js:44:13:44:19 | "first" |
| tst.js:49:10:49:20 | xs.join("") | 0 | tst.js:44:12:44:20 | ["first"] |
| tst.js:53:10:53:34 | `one ${ ... three` | 0 | tst.js:53:11:53:14 | one |
| tst.js:53:10:53:34 | `one ${ ... three` | 1 | tst.js:53:17:53:17 | x |
| tst.js:53:10:53:34 | `one ${ ... three` | 2 | tst.js:53:19:53:23 | two |
| tst.js:53:10:53:34 | `one ${ ... three` | 3 | tst.js:53:26:53:26 | x |
| tst.js:53:10:53:34 | `one ${ ... three` | 4 | tst.js:53:28:53:33 | three |
| tst.js:61:10:61:34 | `first ... } last` | 0 | tst.js:61:11:61:16 | first |
| tst.js:61:10:61:34 | `first ... } last` | 1 | tst.js:61:19:61:19 | x |
| tst.js:61:10:61:34 | `first ... } last` | 2 | tst.js:61:23:61:23 | x |
| tst.js:61:10:61:34 | `first ... } last` | 3 | tst.js:61:27:61:27 | x |
| tst.js:61:10:61:34 | `first ... } last` | 4 | tst.js:61:29:61:33 | last |
| tst.js:87:5:87:14 | x | 0 | tst.js:87:5:87:5 | x |
| tst.js:87:5:87:14 | x | 1 | tst.js:87:10:87:14 | 'two' |
| tst.js:87:5:87:14 | x += 'two' | 0 | tst.js:87:5:87:5 | x |
| tst.js:87:5:87:14 | x += 'two' | 1 | tst.js:87:10:87:14 | 'two' |
| tst.js:89:3:89:14 | x | 0 | tst.js:89:3:89:3 | x |
| tst.js:89:3:89:14 | x | 1 | tst.js:89:8:89:14 | 'three' |
| tst.js:89:3:89:14 | x += 'three' | 0 | tst.js:89:3:89:3 | x |
| tst.js:89:3:89:14 | x += 'three' | 1 | tst.js:89:8:89:14 | 'three' |
nextLeaf
| closure.js:5:7:5:11 | 'one' | closure.js:5:14:5:18 | 'two' |
| closure.js:5:14:5:18 | 'two' | closure.js:5:22:5:28 | 'three' |
| closure.js:5:22:5:28 | 'three' | closure.js:5:31:5:36 | 'four' |
| closure.js:5:31:5:36 | 'four' | closure.js:5:41:5:46 | 'five' |
| html-concat.js:2:15:2:17 | <b> | html-concat.js:2:20:2:20 | x |
| html-concat.js:2:20:2:20 | x | html-concat.js:2:22:2:25 | </b> |
| html-concat.js:3:15:3:17 | <B> | html-concat.js:3:20:3:20 | x |
| html-concat.js:3:20:3:20 | x | html-concat.js:3:22:3:25 | </B> |
| html-concat.js:5:22:5:33 | Hey <strong> | html-concat.js:5:36:5:36 | x |
| html-concat.js:5:36:5:36 | x | html-concat.js:5:38:5:46 | </strong> |
| html-concat.js:7:19:8:10 | \\n Hello | html-concat.js:8:13:8:13 | x |
| html-concat.js:8:13:8:13 | x | html-concat.js:8:15:10:23 | .\\n \\n ... um!</i> |
| html-concat.js:13:3:13:8 | buffer | html-concat.js:13:13:13:18 | '<li>' |
| html-concat.js:14:3:14:8 | buffer | html-concat.js:14:13:14:13 | x |
| html-concat.js:15:3:15:8 | buffer | html-concat.js:15:13:15:15 | '!' |
| tst.js:3:3:3:3 | x | tst.js:3:8:3:12 | "two" |
| tst.js:4:3:4:3 | x | tst.js:4:8:4:14 | "three" |
| tst.js:5:3:5:3 | x | tst.js:5:8:5:13 | "four" |
| tst.js:12:5:12:5 | x | tst.js:12:10:12:14 | "one" |
| tst.js:12:10:12:14 | "one" | tst.js:12:18:12:18 | y |
| tst.js:12:18:12:18 | y | tst.js:12:22:12:26 | "two" |
| tst.js:14:3:14:3 | x | tst.js:14:8:14:13 | "last" |
| tst.js:19:11:19:15 | "one" | tst.js:19:19:19:23 | "two" |
| tst.js:20:3:20:3 | x | tst.js:20:8:20:25 | ("three" + "four") |
| tst.js:20:9:20:15 | "three" | tst.js:20:19:20:24 | "four" |
| tst.js:21:10:21:10 | x | tst.js:21:14:21:19 | "five" |
| tst.js:25:11:25:15 | "one" | tst.js:25:18:25:22 | "two" |
| tst.js:25:18:25:22 | "two" | tst.js:25:25:25:31 | "three" |
| tst.js:29:16:29:20 | "one" | tst.js:29:23:29:27 | "two" |
| tst.js:29:23:29:27 | "two" | tst.js:29:30:29:36 | "three" |
| tst.js:33:20:33:24 | "one" | tst.js:33:27:33:31 | "two" |
| tst.js:33:27:33:31 | "two" | tst.js:33:34:33:40 | "three" |
| tst.js:53:11:53:14 | one | tst.js:53:17:53:17 | x |
| tst.js:53:17:53:17 | x | tst.js:53:19:53:23 | two |
| tst.js:53:19:53:23 | two | tst.js:53:26:53:26 | x |
| tst.js:53:26:53:26 | x | tst.js:53:28:53:33 | three |
| tst.js:61:11:61:16 | first | tst.js:61:19:61:19 | x |
| tst.js:61:19:61:19 | x | tst.js:61:23:61:23 | x |
| tst.js:61:23:61:23 | x | tst.js:61:27:61:27 | x |
| tst.js:61:27:61:27 | x | tst.js:61:29:61:33 | last |
| tst.js:87:5:87:5 | x | tst.js:87:10:87:14 | 'two' |
| tst.js:89:3:89:3 | x | tst.js:89:8:89:14 | 'three' |
htmlRoot
| html-concat.js:2:14:2:26 | `<b>${x}</b>` |
| html-concat.js:3:14:3:26 | `<B>${x}</B>` |
| html-concat.js:5:21:5:47 | `Hey <s ... trong>` |
| html-concat.js:7:18:10:24 | `\\n H ... m!</i>` |
| html-concat.js:13:3:13:18 | buffer |
| html-concat.js:13:3:13:18 | buffer += '<li>' |
htmlLeaf
| html-concat.js:2:15:2:17 | <b> |
| html-concat.js:2:20:2:20 | x |
| html-concat.js:2:22:2:25 | </b> |
| html-concat.js:3:15:3:17 | <B> |
| html-concat.js:3:20:3:20 | x |
| html-concat.js:3:22:3:25 | </B> |
| html-concat.js:5:22:5:33 | Hey <strong> |
| html-concat.js:5:36:5:36 | x |
| html-concat.js:5:38:5:46 | </strong> |
| html-concat.js:7:19:8:10 | \\n Hello |
| html-concat.js:8:13:8:13 | x |
| html-concat.js:8:15:10:23 | .\\n \\n ... um!</i> |
| html-concat.js:13:3:13:8 | buffer |
| html-concat.js:13:13:13:18 | '<li>' |

View File

@@ -0,0 +1,17 @@
import javascript
query StringOps::Concatenation concatenation() { any() }
query StringOps::ConcatenationOperand concatenationOperand() { any() }
query StringOps::ConcatenationLeaf concatenationLeaf() { any() }
query StringOps::ConcatenationNode concatenationNode() { any() }
query predicate operand(StringOps::ConcatenationNode node, int i, DataFlow::Node child) {
child = node.getOperand(i)
}
query predicate nextLeaf(StringOps::ConcatenationNode node, DataFlow::Node next) {
next = node.getNextLeaf()
}
query StringOps::HtmlConcatenationRoot htmlRoot() { any() }
query StringOps::HtmlConcatenationLeaf htmlLeaf() { any() }

View File

@@ -0,0 +1,18 @@
function f(x, y) {
let bold = `<b>${x}</b>`;
let Bold = `<B>${x}</B>`;
let longTagName = `Hey <strong>${x}</strong>`;
let longText = `
Hello ${x}.
<i>Lorem ipsum!</i>`;
let buffer = '';
buffer += '<li>';
buffer += x;
buffer += '!';
return buffer;
}