Skip to content

Commit ad6e949

Browse files
committed
JS: Introduce RegExpCreationNode
1 parent 4354945 commit ad6e949

File tree

1 file changed

+128
-0
lines changed
  • javascript/ql/src/semmle/javascript/dataflow

1 file changed

+128
-0
lines changed

javascript/ql/src/semmle/javascript/dataflow/Nodes.qll

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,3 +1315,131 @@ module PartialInvokeNode {
13151315
* This contributes additional argument-passing flow edges that should be added to all data flow configurations.
13161316
*/
13171317
deprecated class AdditionalPartialInvokeNode = PartialInvokeNode::Range;
1318+
1319+
/**
1320+
* An invocation of the `RegExp` constructor.
1321+
*
1322+
* Example:
1323+
* ```js
1324+
* new RegExp("#[a-z]+", "g");
1325+
* RegExp("^\w*$");
1326+
* ```
1327+
*/
1328+
class RegExpConstructorInvokeNode extends DataFlow::InvokeNode {
1329+
RegExpConstructorInvokeNode() {
1330+
this = DataFlow::globalVarRef("RegExp").getAnInvocation()
1331+
}
1332+
1333+
/**
1334+
* Gets the AST of the regular expression created here, if it is constant.
1335+
*/
1336+
RegExpTerm getRegExpTerm() {
1337+
result = getArgument(0).asExpr().(StringLiteral).asRegExp()
1338+
}
1339+
1340+
/**
1341+
* Gets the flags provided in the second argument, or an empty string if no
1342+
* flags are provided.
1343+
*
1344+
* Has no result if the flags are provided but are not constant.
1345+
*/
1346+
string getFlags() {
1347+
result = getArgument(1).getStringValue()
1348+
or
1349+
not exists(getArgument(1)) and
1350+
result = ""
1351+
}
1352+
1353+
/**
1354+
* Gets the flags provided in the second argument, or an empty string if no
1355+
* flags are provided, or the string `"?"` if the provided flags are not known.
1356+
*/
1357+
string tryGetFlags() {
1358+
result = getFlags()
1359+
or
1360+
not exists(getFlags()) and
1361+
result = RegExp::unknownFlag()
1362+
}
1363+
}
1364+
1365+
/**
1366+
* A data flow node corresponding to a regular expression literal.
1367+
*
1368+
* Example:
1369+
* ```js
1370+
* /[a-z]+/i
1371+
* ```
1372+
*/
1373+
class RegExpLiteralNode extends DataFlow::SourceNode, DataFlow::ValueNode {
1374+
override RegExpLiteral astNode;
1375+
1376+
/** Gets the root term of this regular expression literal. */
1377+
RegExpTerm getRegExpTerm() {
1378+
result = astNode.getRoot()
1379+
}
1380+
1381+
/** Gets the flags of this regular expression literal. */
1382+
string getFlags() {
1383+
result = astNode.getFlags()
1384+
}
1385+
}
1386+
1387+
/**
1388+
* A data flow node corresponding to a regular expression literal or
1389+
* an invocation of the `RegExp` constructor.
1390+
*
1391+
* Examples:
1392+
* ```js
1393+
* new RegExp("#[a-z]+", "g");
1394+
* RegExp("^\w*$");
1395+
* /[a-z]+/i
1396+
* ```
1397+
*/
1398+
class RegExpCreationNode extends DataFlow::SourceNode {
1399+
RegExpCreationNode() {
1400+
this instanceof RegExpConstructorInvokeNode or
1401+
this instanceof RegExpLiteralNode
1402+
}
1403+
1404+
/**
1405+
* Gets the root term of the created regular expression, if it is known.
1406+
*
1407+
* Has no result for calls to `RegExp` with a non-constant argument.
1408+
*/
1409+
RegExpTerm getRegExpTerm() {
1410+
result = this.(RegExpConstructorInvokeNode).getRegExpTerm() or
1411+
result = this.(RegExpLiteralNode).getRegExpTerm()
1412+
}
1413+
1414+
/**
1415+
* Gets the provided regular expression flags, if they are known.
1416+
*/
1417+
string getFlags() {
1418+
result = this.(RegExpConstructorInvokeNode).getFlags() or
1419+
result = this.(RegExpLiteralNode).getFlags()
1420+
}
1421+
1422+
/**
1423+
* Gets the flags provided in the second argument, or an empty string if no
1424+
* flags are provided, or the string `"?"` if the provided flags are not known.
1425+
*/
1426+
string tryGetFlags() {
1427+
result = this.(RegExpConstructorInvokeNode).tryGetFlags() or
1428+
result = this.(RegExpLiteralNode).getFlags()
1429+
}
1430+
1431+
/** Gets a data flow node referring to this regular expression. */
1432+
private DataFlow::SourceNode getAReference(DataFlow::TypeTracker t) {
1433+
t.start() and
1434+
result = this
1435+
or
1436+
exists(DataFlow::TypeTracker t2 |
1437+
result = getAReference(t2).track(t2, t)
1438+
)
1439+
}
1440+
1441+
/** Gets a data flow node referring to this regular expression. */
1442+
DataFlow::SourceNode getAReference() {
1443+
result = getAReference(DataFlow::TypeTracker::end())
1444+
}
1445+
}

0 commit comments

Comments
 (0)