Skip to content

Commit 0328a29

Browse files
committed
move TypeORM library file and tests to experimental
add inline tests :) Fix TypeORM fuzzy method according to Review
1 parent 999ec70 commit 0328a29

File tree

9 files changed

+249
-265
lines changed

9 files changed

+249
-265
lines changed

javascript/ql/lib/javascript.qll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ import semmle.javascript.frameworks.ShellJS
126126
import semmle.javascript.frameworks.Snapdragon
127127
import semmle.javascript.frameworks.SystemCommandExecutors
128128
import semmle.javascript.frameworks.SQL
129-
import semmle.javascript.frameworks.TypeORM
130129
import semmle.javascript.frameworks.SocketIO
131130
import semmle.javascript.frameworks.StringFormatters
132131
import semmle.javascript.frameworks.TorrentLibraries

javascript/ql/lib/semmle/javascript/frameworks/TypeORM.qll

Lines changed: 0 additions & 188 deletions
This file was deleted.
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
/**
2+
* Provides classes for working with SQL connectors.
3+
*/
4+
5+
import javascript
6+
7+
module ExperimentalSQL {
8+
/**
9+
* Provides SQL injection Sinks for the [TypeORM](https://www.npmjs.com/package/typeorm) package
10+
*/
11+
private module TypeOrm {
12+
/**
13+
* Gets a `DataSource` instance
14+
*
15+
* `DataSource` is a pre-defined connection configuration to a specific database.
16+
*/
17+
API::Node dataSource() {
18+
result = API::moduleImport("typeorm").getMember("DataSource").getInstance()
19+
}
20+
21+
/**
22+
* Gets a `QueryRunner` instance
23+
*/
24+
API::Node queryRunner() { result = dataSource().getMember("createQueryRunner").getReturn() }
25+
26+
/**
27+
* Gets a `*QueryBuilder` instance
28+
*/
29+
API::Node queryBuilderInstance() {
30+
// a `*QueryBuilder` instance of a Data Mapper based Entity
31+
result =
32+
[
33+
// Using DataSource
34+
dataSource(),
35+
// Using repository
36+
dataSource().getMember("getRepository").getReturn(),
37+
// Using entity manager
38+
dataSource().getMember("manager"), queryRunner().getMember("manager")
39+
].getMember("createQueryBuilder").getReturn()
40+
or
41+
// A `*QueryBuilder` instance of an Active record based Entity
42+
result =
43+
API::moduleImport("typeorm")
44+
.getMember("Entity")
45+
.getReturn()
46+
.getADecoratedClass()
47+
.getMember("createQueryBuilder")
48+
.getReturn()
49+
or
50+
// A WhereExpressionBuilder can be used in complex WHERE expression
51+
result =
52+
API::moduleImport("typeorm")
53+
.getMember(["Brackets", "NotBrackets"])
54+
.getParameter(0)
55+
.getParameter(0)
56+
or
57+
// In case of custom query builders
58+
result =
59+
API::moduleImport("typeorm")
60+
.getMember([
61+
"SelectQueryBuilder", "InsertQueryBuilder", "RelationQueryBuilder",
62+
"UpdateQueryBuilder", "WhereExpressionBuilder"
63+
])
64+
.getInstance()
65+
}
66+
67+
/**
68+
* Gets function names which create any type of `QueryBuilder` like `WhereExpressionBuilder` or `InsertQueryBuilder`
69+
*/
70+
string queryBuilderMethods() {
71+
result =
72+
[
73+
"select", "addSelect", "where", "andWhere", "orWhere", "having", "orHaving", "andHaving",
74+
"orderBy", "addOrderBy", "distinctOn", "groupBy", "addCommonTableExpression",
75+
"leftJoinAndSelect", "innerJoinAndSelect", "leftJoin", "innerJoin", "leftJoinAndMapOne",
76+
"innerJoinAndMapOne", "leftJoinAndMapMany", "innerJoinAndMapMany", "orUpdate", "orIgnore",
77+
"values", "set"
78+
]
79+
}
80+
81+
/**
82+
* Gets function names that the return values of these functions can be the results of a database query run
83+
*/
84+
string queryBuilderResult() {
85+
result = ["getOne", "getOneOrFail", "getMany", "getRawOne", "getRawMany", "stream"]
86+
}
87+
88+
/**
89+
* Gets a QueryBuilder instance that has a query builder function
90+
*/
91+
API::Node getASuccessorOfBuilderInstance(string queryBuilderMethod) {
92+
result.getMember(queryBuilderMethod) = queryBuilderInstance().getASuccessor*()
93+
}
94+
95+
/**
96+
* A call to some successor functions of TypeORM `createQueryBuilder` function which are dangerous
97+
*/
98+
private class QueryBuilderCall extends DatabaseAccess, DataFlow::Node {
99+
API::Node queryBuilder;
100+
101+
QueryBuilderCall() {
102+
queryBuilder = getASuccessorOfBuilderInstance(queryBuilderMethods()) and
103+
this = queryBuilder.asSource()
104+
}
105+
106+
override DataFlow::Node getAResult() {
107+
result = queryBuilder.getMember(queryBuilderResult()).getReturn().asSource()
108+
}
109+
110+
override DataFlow::Node getAQueryArgument() {
111+
exists(string memberName | memberName = queryBuilderMethods() |
112+
memberName = ["leftJoinAndSelect", "innerJoinAndSelect", "leftJoin", "innerJoin"] and
113+
result = queryBuilder.getMember(memberName).getParameter(2).asSink()
114+
or
115+
memberName =
116+
["leftJoinAndMapOne", "innerJoinAndMapOne", "leftJoinAndMapMany", "innerJoinAndMapMany"] and
117+
result = queryBuilder.getMember(memberName).getParameter(3).asSink()
118+
or
119+
memberName =
120+
[
121+
"select", "addSelect", "where", "andWhere", "orWhere", "having", "orHaving",
122+
"andHaving", "orderBy", "addOrderBy", "distinctOn", "groupBy",
123+
"addCommonTableExpression"
124+
] and
125+
result = queryBuilder.getMember(memberName).getParameter(0).asSink()
126+
or
127+
memberName = ["orIgnore", "orUpdate"] and
128+
result = queryBuilder.getMember(memberName).getParameter([0, 1]).asSink()
129+
or
130+
// following functions if use a function as their input fields,called function parameters which are vulnerable
131+
memberName = ["values", "set"] and
132+
result =
133+
queryBuilder.getMember(memberName).getParameter(0).getAMember().getReturn().asSink()
134+
)
135+
}
136+
}
137+
138+
/**
139+
* A call to the TypeORM `query` function of a `QueryRunner`
140+
*/
141+
private class QueryRunner extends DatabaseAccess, API::CallNode {
142+
QueryRunner() { queryRunner().getMember("query").getACall() = this }
143+
144+
override DataFlow::Node getAResult() { result = this }
145+
146+
override DataFlow::Node getAQueryArgument() { result = this.getArgument(0) }
147+
}
148+
149+
/** An expression that is passed to the `query` function and hence interpreted as SQL. */
150+
class QueryString extends SQL::SqlString {
151+
QueryString() {
152+
this = any(QueryRunner qr).getAQueryArgument() or
153+
this = any(QueryBuilderCall qb).getAQueryArgument()
154+
}
155+
}
156+
}
157+
}

0 commit comments

Comments
 (0)