From 4354945c26e089289c8da70cd785a30a8d10943e Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 20 Nov 2019 14:41:12 +0000 Subject: [PATCH] JS: Factor out recognition of RegExp flags --- javascript/ql/src/semmle/javascript/Expr.qll | 8 +-- .../ql/src/semmle/javascript/Regexp.qll | 55 ++++++++++++++----- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/javascript/ql/src/semmle/javascript/Expr.qll b/javascript/ql/src/semmle/javascript/Expr.qll index 258a85904d2..b43a38306ce 100644 --- a/javascript/ql/src/semmle/javascript/Expr.qll +++ b/javascript/ql/src/semmle/javascript/Expr.qll @@ -454,16 +454,16 @@ class RegExpLiteral extends @regexpliteral, Literal, RegExpParent { string getFlags() { result = getValue().regexpCapture(".*/(\\w*)$", 1) } /** Holds if this regular expression has an `m` flag. */ - predicate isMultiline() { getFlags().matches("%m%") } + predicate isMultiline() { RegExp::isMultiline(getFlags()) } /** Holds if this regular expression has a `g` flag. */ - predicate isGlobal() { getFlags().matches("%g%") } + predicate isGlobal() { RegExp::isGlobal(getFlags()) } /** Holds if this regular expression has an `i` flag. */ - predicate isIgnoreCase() { getFlags().matches("%i%") } + predicate isIgnoreCase() { RegExp::isIgnoreCase(getFlags()) } /** Holds if this regular expression has an `s` flag. */ - predicate isDotAll() { getFlags().matches("%s%") } + predicate isDotAll() { RegExp::isDotAll(getFlags()) } } /** diff --git a/javascript/ql/src/semmle/javascript/Regexp.qll b/javascript/ql/src/semmle/javascript/Regexp.qll index c79548e04c5..9d1a4b9cf61 100644 --- a/javascript/ql/src/semmle/javascript/Regexp.qll +++ b/javascript/ql/src/semmle/javascript/Regexp.qll @@ -113,9 +113,7 @@ class RegExpTerm extends Locatable, @regexpterm { /** * Holds if this is the root term of a regular expression. */ - predicate isRootTerm() { - not getParent() instanceof RegExpTerm - } + predicate isRootTerm() { not getParent() instanceof RegExpTerm } /** * Gets the outermost term of this regular expression. @@ -130,9 +128,7 @@ class RegExpTerm extends Locatable, @regexpterm { /** * Holds if this term occurs as part of a regular expression literal. */ - predicate isPartOfRegExpLiteral() { - exists(getLiteral()) - } + predicate isPartOfRegExpLiteral() { exists(getLiteral()) } /** * Holds if this term occurs as part of a string literal. @@ -140,9 +136,7 @@ class RegExpTerm extends Locatable, @regexpterm { * This predicate holds regardless of whether the string literal is actually * used as a regular expression. See `isUsedAsRegExp`. */ - predicate isPartOfStringLiteral() { - getRootTerm().getParent() instanceof StringLiteral - } + predicate isPartOfStringLiteral() { getRootTerm().getParent() instanceof StringLiteral } /** * Holds if this term is part of a regular expression literal, or a string literal @@ -344,8 +338,7 @@ class RegExpAnchor extends RegExpTerm, @regexp_anchor { * ^ * ``` */ -class RegExpCaret extends RegExpAnchor, @regexp_caret { -} +class RegExpCaret extends RegExpAnchor, @regexp_caret { } /** * A dollar assertion `$` matching the end of a line. @@ -356,8 +349,7 @@ class RegExpCaret extends RegExpAnchor, @regexp_caret { * $ * ``` */ -class RegExpDollar extends RegExpAnchor, @regexp_dollar { -} +class RegExpDollar extends RegExpAnchor, @regexp_dollar { } /** * A word boundary assertion. @@ -940,3 +932,40 @@ private class StringRegExpPatternSource extends RegExpPatternSource { override RegExpTerm getRegExpTerm() { result = asExpr().(StringLiteral).asRegExp() } } + +module RegExp { + /** Gets the string `"?"` used to represent a regular expression whose flags are unknown. */ + string unknownFlag() { result = "?" } + + /** Holds `flags` includes the `m` flag. */ + bindingset[flags] + predicate isMultiline(string flags) { flags.matches("%m%") } + + /** Holds `flags` includes the `g` flag. */ + bindingset[flags] + predicate isGlobal(string flags) { flags.matches("%g%") } + + /** Holds `flags` includes the `i` flag. */ + bindingset[flags] + predicate isIgnoreCase(string flags) { flags.matches("%i%") } + + /** Holds `flags` includes the `s` flag. */ + bindingset[flags] + predicate isDotAll(string flags) { flags.matches("%s%") } + + /** Holds `flags` includes the `m` flag or is the unknown flag `?`. */ + bindingset[flags] + predicate maybeMultiline(string flags) { flags = unknownFlag() or isMultiline(flags) } + + /** Holds `flags` includes the `g` flag or is the unknown flag `?`. */ + bindingset[flags] + predicate maybeGlobal(string flags) { flags = unknownFlag() or isGlobal(flags) } + + /** Holds `flags` includes the `i` flag or is the unknown flag `?`. */ + bindingset[flags] + predicate maybeIgnoreCase(string flags) { flags = unknownFlag() or isIgnoreCase(flags) } + + /** Holds `flags` includes the `s` flag or is the unknown flag `?`. */ + bindingset[flags] + predicate maybeDotAll(string flags) { flags = unknownFlag() or isDotAll(flags) } +}