Skip to content

Commit 522a2e2

Browse files
committed
v2
1 parent 54a4477 commit 522a2e2

File tree

2 files changed

+138
-1
lines changed

2 files changed

+138
-1
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import javascript
2+
3+
module Sqlite {
4+
// Gets an expression that constructs or returns a Sqlite database instance.
5+
API::Node dataSource() {
6+
result = API::moduleImport("typeorm").getMember("DataSource").getInstance()
7+
}
8+
9+
// Gets return value of a `createQueryBuilder`
10+
API::Node queryBuilderInstance() {
11+
result =
12+
[
13+
// Using DataSource
14+
dataSource(),
15+
// Using repository
16+
dataSource().getMember("getRepository").getReturn(),
17+
// Using entity manager
18+
dataSource().getMember("manager")
19+
].getMember("createQueryBuilder").getReturn()
20+
}
21+
22+
//API::moduleImport("typeorm").getMember("exports").getMember("DataSource").getInstance().getMember("createQueryBuilder").getReturn().getMember("where")
23+
// Gets The Brackets that are SQL Subqueries equivalent
24+
API::Node brackets() {
25+
result =
26+
API::moduleImport("typeorm")
27+
.getMember(["Brackets", "NotBrackets"])
28+
.getParameter(0)
29+
.getParameter(0)
30+
}
31+
32+
// Gets any Successor node of Brackets, NotBrackets
33+
API::Node getASuccessorOfBrackets() {
34+
result = brackets() or
35+
result = getASuccessorOfBrackets().getAMember() or
36+
result = getASuccessorOfBrackets().getAParameter() or
37+
result = getASuccessorOfBrackets().getUnknownMember() or
38+
result = getASuccessorOfBrackets().getReturn() or
39+
result = getASuccessorOfBrackets().getInstance()
40+
}
41+
42+
// Gets any Successor node of createQueryBuilder
43+
API::Node getASuccessorOfBuilderInstance() {
44+
result = queryBuilderInstance() or
45+
result = getASuccessorOfBuilderInstance().getAMember() or
46+
result = getASuccessorOfBuilderInstance().getAParameter() or
47+
result = getASuccessorOfBuilderInstance().getUnknownMember() or
48+
result = getASuccessorOfBuilderInstance().getReturn() or
49+
result = getASuccessorOfBuilderInstance().getInstance()
50+
}
51+
52+
/**
53+
* Gets functions responsible for select expressions
54+
* `orderBy` is not injectable in sqlite, if we want to write a filter we should specify a DataSource parameter string value,
55+
* which mostly is taken from config files and we will loose many sinks,
56+
* Also many application support multiple DBMSs besides sqlite,
57+
* Also Consider it that `Order By` clause is one of the most popular injectable sinks
58+
*/
59+
string selectExpression() {
60+
result =
61+
[
62+
"select", "addSelect", "from", "where", "andWhere", "orWhere", "having", "orHaving",
63+
"andHaving", "orderBy", "addOrderBy", "distinctOn", "groupBy", "addCommonTableExpression",
64+
"leftJoinAndSelect", "innerJoinAndSelect", "leftJoin", "innerJoin", "leftJoinAndMapOne",
65+
"innerJoinAndMapOne", "leftJoinAndMapMany", "innerJoinAndMapMany"
66+
]
67+
}
68+
69+
// Gets functions that return results
70+
string queryBuilderResult() {
71+
result = ["getOne", "getOneOrFail", "getMany", "getRawOne", "getRawMany", "stream"]
72+
}
73+
74+
/**
75+
* A call to a TypeORM Query Builder method and its successor nodes.
76+
*/
77+
private class QueryCall extends DatabaseAccess, API::CallNode {
78+
API::Node typeOrmNode;
79+
80+
QueryCall() {
81+
(
82+
typeOrmNode = getASuccessorOfBuilderInstance() and
83+
this = typeOrmNode.asSource()
84+
or
85+
// I'm doing following because this = typeOrmNode.asSource()s
86+
// won't let me to get a member in getAQueryArgument
87+
typeOrmNode = getASuccessorOfBrackets() and
88+
typeOrmNode.getMember(selectExpression()).getACall() = this
89+
) and
90+
this.getFile().getLocation().toString().matches("%.ts%")
91+
}
92+
93+
override DataFlow::Node getAResult() {
94+
result = typeOrmNode.getMember(queryBuilderResult()).getReturn().asSource()
95+
}
96+
97+
override DataFlow::Node getAQueryArgument() {
98+
exists(string memberName | memberName = selectExpression() |
99+
memberName = ["leftJoinAndSelect", "innerJoinAndSelect", "leftJoin", "innerJoin"] and
100+
result = typeOrmNode.getMember(memberName).getParameter(2).asSink()
101+
or
102+
memberName =
103+
["leftJoinAndMapOne", "innerJoinAndMapOne", "leftJoinAndMapMany", "innerJoinAndMapMany"] and
104+
result = typeOrmNode.getMember(memberName).getParameter(3).asSink()
105+
or
106+
memberName =
107+
[
108+
"select", "addSelect", "from", "where", "andWhere", "orWhere", "having", "orHaving",
109+
"andHaving", "orderBy", "addOrderBy", "distinctOn", "groupBy",
110+
"addCommonTableExpression"
111+
] and
112+
result = typeOrmNode.getMember(memberName).getParameter(0).asSink()
113+
)
114+
}
115+
}
116+
117+
/** An expression that is passed to the `query` method and hence interpreted as SQL. */
118+
class QueryString extends SQL::SqlString {
119+
QueryString() { this = any(QueryCall qc).getAQueryArgument() }
120+
}
121+
}
122+
123+
predicate test(API::Node n) { n = API::moduleImport("typeorm").getASuccessor*().getMember("where") }
124+
125+
predicate test2(API::Node n) {
126+
n =
127+
API::moduleImport("typeorm")
128+
.getMember("DataSource")
129+
.getInstance()
130+
.getMember("getRepository")
131+
.getReturn()
132+
.getMember("createQueryBuilder")
133+
.getReturn()
134+
.getMember("where")
135+
.getParameter(0)
136+
}

javascript/ql/lib/semmle/javascript/security/dataflow/CodeInjectionQuery.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ class Configuration extends TaintTracking::Configuration {
1818

1919
override predicate isSource(DataFlow::Node source) { source instanceof Source }
2020

21-
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
21+
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink and
22+
sink.getFile().getLocation().toString().matches("%script%") }
2223

2324
override predicate isSanitizer(DataFlow::Node node) {
2425
super.isSanitizer(node) or

0 commit comments

Comments
 (0)