mirror of
https://github.com/github/codeql.git
synced 2026-04-28 02:05:14 +02:00
move TypeORM library file and tests to experimental
add inline tests :) Fix TypeORM fuzzy method according to Review
This commit is contained in:
@@ -126,7 +126,6 @@ import semmle.javascript.frameworks.ShellJS
|
||||
import semmle.javascript.frameworks.Snapdragon
|
||||
import semmle.javascript.frameworks.SystemCommandExecutors
|
||||
import semmle.javascript.frameworks.SQL
|
||||
import semmle.javascript.frameworks.TypeORM
|
||||
import semmle.javascript.frameworks.SocketIO
|
||||
import semmle.javascript.frameworks.StringFormatters
|
||||
import semmle.javascript.frameworks.TorrentLibraries
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Provides SQL injection Sinks for [TypeORM](https://www.npmjs.com/package/typeorm) package
|
||||
*/
|
||||
module TypeOrm {
|
||||
/**
|
||||
* Gets a `DataSource` instance
|
||||
*/
|
||||
API::Node dataSource() {
|
||||
result = API::moduleImport("typeorm").getMember("DataSource").getInstance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `QueryRunner` nodes
|
||||
*/
|
||||
API::Node queryRunner() { result = dataSource().getMember("createQueryRunner").getReturn() }
|
||||
|
||||
/**
|
||||
* Gets a `*QueryBuilder` node of an Active record based Entity
|
||||
*/
|
||||
API::Node activeRecordQueryBuilder() {
|
||||
result =
|
||||
API::moduleImport("typeorm")
|
||||
.getMember("Entity")
|
||||
.getReturn()
|
||||
.getADecoratedClass()
|
||||
.getMember("createQueryBuilder")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `*QueryBuilder` node of a Data Mapper based Entity
|
||||
*/
|
||||
API::Node dataMapperQueryBuilder() {
|
||||
result =
|
||||
[
|
||||
// Using DataSource
|
||||
dataSource(),
|
||||
// Using repository
|
||||
dataSource().getMember("getRepository").getReturn(),
|
||||
// Using entity manager
|
||||
dataSource().getMember("manager"), queryRunner().getMember("manager")
|
||||
].getMember("createQueryBuilder").getReturn()
|
||||
or
|
||||
// in case of custom query builders
|
||||
result =
|
||||
API::moduleImport("typeorm")
|
||||
.getMember([
|
||||
"SelectQueryBuilder", "InsertQueryBuilder", "RelationQueryBuilder",
|
||||
"UpdateQueryBuilder"
|
||||
])
|
||||
.getInstance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `*QueryBuilder` node
|
||||
*/
|
||||
API::Node queryBuilderInstance() {
|
||||
result = dataMapperQueryBuilder() or
|
||||
result = activeRecordQueryBuilder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets The Brackets that are SQL Subqueries equivalent
|
||||
*/
|
||||
API::Node brackets() {
|
||||
result =
|
||||
API::moduleImport("typeorm")
|
||||
.getMember(["Brackets", "NotBrackets"])
|
||||
.getParameter(0)
|
||||
.getParameter(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any Successor node of Brackets, NotBrackets
|
||||
*/
|
||||
API::Node getASuccessorOfBrackets() {
|
||||
result = brackets() or
|
||||
result = getASuccessorOfBrackets().getAMember() or
|
||||
result = getASuccessorOfBrackets().getAParameter() or
|
||||
result = getASuccessorOfBrackets().getUnknownMember() or
|
||||
result = getASuccessorOfBrackets().getReturn() or
|
||||
result = getASuccessorOfBrackets().getInstance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets any Successor node of createQueryBuilder
|
||||
*/
|
||||
API::Node getASuccessorOfBuilderInstance() {
|
||||
result = queryBuilderInstance() or
|
||||
result = getASuccessorOfBuilderInstance().getAMember() or
|
||||
result = getASuccessorOfBuilderInstance().getAParameter() or
|
||||
result = getASuccessorOfBuilderInstance().getUnknownMember() or
|
||||
result = getASuccessorOfBuilderInstance().getReturn() or
|
||||
result = getASuccessorOfBuilderInstance().getInstance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets functions responsible for select expressions
|
||||
* `orderBy` is not injectable in TypeORM, if we want to write a filter we should specify a DataSource parameter string value,
|
||||
* which mostly is taken from config files and we will loose many sinks,
|
||||
* Also many application support multiple DBMSs besides TypeORM,
|
||||
* Also Consider it that `Order By` clause is one of the most popular injectable sinks
|
||||
*/
|
||||
string selectExpression() {
|
||||
result =
|
||||
[
|
||||
"select", "addSelect", "where", "andWhere", "orWhere", "having", "orHaving", "andHaving",
|
||||
"orderBy", "addOrderBy", "distinctOn", "groupBy", "addCommonTableExpression",
|
||||
"leftJoinAndSelect", "innerJoinAndSelect", "leftJoin", "innerJoin", "leftJoinAndMapOne",
|
||||
"innerJoinAndMapOne", "leftJoinAndMapMany", "innerJoinAndMapMany", "orUpdate", "orIgnore",
|
||||
"values", "set"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets functions that return results
|
||||
*/
|
||||
string queryBuilderResult() {
|
||||
result = ["getOne", "getOneOrFail", "getMany", "getRawOne", "getRawMany", "stream"]
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to some successor functions of TypeORM `createQueryBuilder` function which are dangerous
|
||||
*/
|
||||
private class QueryBuilderCall extends DatabaseAccess, API::CallNode {
|
||||
API::Node typeOrmNode;
|
||||
|
||||
QueryBuilderCall() {
|
||||
typeOrmNode = getASuccessorOfBuilderInstance() and
|
||||
this = typeOrmNode.asSource()
|
||||
or
|
||||
// I'm doing following because `this = TypeORMNode.asSource()`
|
||||
// don't let me to get a member in getAQueryArgument
|
||||
typeOrmNode = getASuccessorOfBrackets() and
|
||||
typeOrmNode.getMember(selectExpression()).getACall() = this
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
result = typeOrmNode.getMember(queryBuilderResult()).getReturn().asSource()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
exists(string memberName | memberName = selectExpression() |
|
||||
memberName = ["leftJoinAndSelect", "innerJoinAndSelect", "leftJoin", "innerJoin"] and
|
||||
result = typeOrmNode.getMember(memberName).getParameter(2).asSink()
|
||||
or
|
||||
memberName =
|
||||
["leftJoinAndMapOne", "innerJoinAndMapOne", "leftJoinAndMapMany", "innerJoinAndMapMany"] and
|
||||
result = typeOrmNode.getMember(memberName).getParameter(3).asSink()
|
||||
or
|
||||
memberName =
|
||||
[
|
||||
"select", "addSelect", "where", "andWhere", "orWhere", "having", "orHaving",
|
||||
"andHaving", "orderBy", "addOrderBy", "distinctOn", "groupBy",
|
||||
"addCommonTableExpression"
|
||||
] and
|
||||
result = typeOrmNode.getMember(memberName).getParameter(0).asSink()
|
||||
or
|
||||
memberName = ["orIgnore", "orUpdate"] and
|
||||
result = typeOrmNode.getMember(memberName).getParameter([0, 1]).asSink()
|
||||
or
|
||||
// following functions if use a function as their input fields,called function parameters which are vulnerable
|
||||
memberName = ["values", "set"] and
|
||||
result = typeOrmNode.getMember(memberName).getParameter(0).getAMember().getReturn().asSink()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a TypeORM `query` function of QueryRunner
|
||||
*/
|
||||
private class QueryRunner extends DatabaseAccess, API::CallNode {
|
||||
QueryRunner() { queryRunner().getMember("query").getACall() = this }
|
||||
|
||||
override DataFlow::Node getAResult() { result = this }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
/** An expression that is passed to the `query` function and hence interpreted as SQL. */
|
||||
class QueryString extends SQL::SqlString {
|
||||
QueryString() {
|
||||
this = any(QueryRunner qr).getAQueryArgument() or
|
||||
this = any(QueryBuilderCall qb).getAQueryArgument()
|
||||
}
|
||||
}
|
||||
}
|
||||
157
javascript/ql/src/experimental/semmle/javascript/SQL.qll
Normal file
157
javascript/ql/src/experimental/semmle/javascript/SQL.qll
Normal file
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Provides classes for working with SQL connectors.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
module ExperimentalSQL {
|
||||
/**
|
||||
* Provides SQL injection Sinks for the [TypeORM](https://www.npmjs.com/package/typeorm) package
|
||||
*/
|
||||
private module TypeOrm {
|
||||
/**
|
||||
* Gets a `DataSource` instance
|
||||
*
|
||||
* `DataSource` is a pre-defined connection configuration to a specific database.
|
||||
*/
|
||||
API::Node dataSource() {
|
||||
result = API::moduleImport("typeorm").getMember("DataSource").getInstance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `QueryRunner` instance
|
||||
*/
|
||||
API::Node queryRunner() { result = dataSource().getMember("createQueryRunner").getReturn() }
|
||||
|
||||
/**
|
||||
* Gets a `*QueryBuilder` instance
|
||||
*/
|
||||
API::Node queryBuilderInstance() {
|
||||
// a `*QueryBuilder` instance of a Data Mapper based Entity
|
||||
result =
|
||||
[
|
||||
// Using DataSource
|
||||
dataSource(),
|
||||
// Using repository
|
||||
dataSource().getMember("getRepository").getReturn(),
|
||||
// Using entity manager
|
||||
dataSource().getMember("manager"), queryRunner().getMember("manager")
|
||||
].getMember("createQueryBuilder").getReturn()
|
||||
or
|
||||
// A `*QueryBuilder` instance of an Active record based Entity
|
||||
result =
|
||||
API::moduleImport("typeorm")
|
||||
.getMember("Entity")
|
||||
.getReturn()
|
||||
.getADecoratedClass()
|
||||
.getMember("createQueryBuilder")
|
||||
.getReturn()
|
||||
or
|
||||
// A WhereExpressionBuilder can be used in complex WHERE expression
|
||||
result =
|
||||
API::moduleImport("typeorm")
|
||||
.getMember(["Brackets", "NotBrackets"])
|
||||
.getParameter(0)
|
||||
.getParameter(0)
|
||||
or
|
||||
// In case of custom query builders
|
||||
result =
|
||||
API::moduleImport("typeorm")
|
||||
.getMember([
|
||||
"SelectQueryBuilder", "InsertQueryBuilder", "RelationQueryBuilder",
|
||||
"UpdateQueryBuilder", "WhereExpressionBuilder"
|
||||
])
|
||||
.getInstance()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets function names which create any type of `QueryBuilder` like `WhereExpressionBuilder` or `InsertQueryBuilder`
|
||||
*/
|
||||
string queryBuilderMethods() {
|
||||
result =
|
||||
[
|
||||
"select", "addSelect", "where", "andWhere", "orWhere", "having", "orHaving", "andHaving",
|
||||
"orderBy", "addOrderBy", "distinctOn", "groupBy", "addCommonTableExpression",
|
||||
"leftJoinAndSelect", "innerJoinAndSelect", "leftJoin", "innerJoin", "leftJoinAndMapOne",
|
||||
"innerJoinAndMapOne", "leftJoinAndMapMany", "innerJoinAndMapMany", "orUpdate", "orIgnore",
|
||||
"values", "set"
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets function names that the return values of these functions can be the results of a database query run
|
||||
*/
|
||||
string queryBuilderResult() {
|
||||
result = ["getOne", "getOneOrFail", "getMany", "getRawOne", "getRawMany", "stream"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a QueryBuilder instance that has a query builder function
|
||||
*/
|
||||
API::Node getASuccessorOfBuilderInstance(string queryBuilderMethod) {
|
||||
result.getMember(queryBuilderMethod) = queryBuilderInstance().getASuccessor*()
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to some successor functions of TypeORM `createQueryBuilder` function which are dangerous
|
||||
*/
|
||||
private class QueryBuilderCall extends DatabaseAccess, DataFlow::Node {
|
||||
API::Node queryBuilder;
|
||||
|
||||
QueryBuilderCall() {
|
||||
queryBuilder = getASuccessorOfBuilderInstance(queryBuilderMethods()) and
|
||||
this = queryBuilder.asSource()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAResult() {
|
||||
result = queryBuilder.getMember(queryBuilderResult()).getReturn().asSource()
|
||||
}
|
||||
|
||||
override DataFlow::Node getAQueryArgument() {
|
||||
exists(string memberName | memberName = queryBuilderMethods() |
|
||||
memberName = ["leftJoinAndSelect", "innerJoinAndSelect", "leftJoin", "innerJoin"] and
|
||||
result = queryBuilder.getMember(memberName).getParameter(2).asSink()
|
||||
or
|
||||
memberName =
|
||||
["leftJoinAndMapOne", "innerJoinAndMapOne", "leftJoinAndMapMany", "innerJoinAndMapMany"] and
|
||||
result = queryBuilder.getMember(memberName).getParameter(3).asSink()
|
||||
or
|
||||
memberName =
|
||||
[
|
||||
"select", "addSelect", "where", "andWhere", "orWhere", "having", "orHaving",
|
||||
"andHaving", "orderBy", "addOrderBy", "distinctOn", "groupBy",
|
||||
"addCommonTableExpression"
|
||||
] and
|
||||
result = queryBuilder.getMember(memberName).getParameter(0).asSink()
|
||||
or
|
||||
memberName = ["orIgnore", "orUpdate"] and
|
||||
result = queryBuilder.getMember(memberName).getParameter([0, 1]).asSink()
|
||||
or
|
||||
// following functions if use a function as their input fields,called function parameters which are vulnerable
|
||||
memberName = ["values", "set"] and
|
||||
result =
|
||||
queryBuilder.getMember(memberName).getParameter(0).getAMember().getReturn().asSink()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the TypeORM `query` function of a `QueryRunner`
|
||||
*/
|
||||
private class QueryRunner extends DatabaseAccess, API::CallNode {
|
||||
QueryRunner() { queryRunner().getMember("query").getACall() = this }
|
||||
|
||||
override DataFlow::Node getAResult() { result = this }
|
||||
|
||||
override DataFlow::Node getAQueryArgument() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
/** An expression that is passed to the `query` function and hence interpreted as SQL. */
|
||||
class QueryString extends SQL::SqlString {
|
||||
QueryString() {
|
||||
this = any(QueryRunner qr).getAQueryArgument() or
|
||||
this = any(QueryBuilderCall qb).getAQueryArgument()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,8 @@ export class UserActiveRecord extends BaseEntity {
|
||||
|
||||
static findByName(firstName: string, lastName: string) {
|
||||
return this.createQueryBuilder("user")
|
||||
.where("user.firstName = " + firstName)
|
||||
.andWhere("user.lastName = " + lastName)
|
||||
.where("user.firstName = " + firstName) // test: SQLInjectionPoint
|
||||
.andWhere("user.lastName = " + lastName) // test: SQLInjectionPoint
|
||||
.getMany()
|
||||
}
|
||||
}
|
||||
@@ -71,19 +71,21 @@ function makePaginationQuery<T>(q: SelectQueryBuilder<T>): SelectQueryBuilder<T>
|
||||
}
|
||||
|
||||
AppDataSource.initialize().then(async () => {
|
||||
const BadInput = "A user controllable Remote Source like `' 1=1 --` "
|
||||
|
||||
// Active record
|
||||
await UserActiveRecord.findByName("FirstNameToFind", "LastNameToFind")
|
||||
await UserActiveRecord.findByName(BadInput, "Saw")
|
||||
|
||||
// data mapper
|
||||
const selectQueryBuilder = makePaginationQuery<User>(AppDataSource
|
||||
.createQueryBuilder(User, "User").select());
|
||||
selectQueryBuilder.where('id > 5').getMany().then(result => {
|
||||
selectQueryBuilder.where(BadInput).getMany().then(result => { // test: SQLInjectionPoint
|
||||
console.log(result)
|
||||
});
|
||||
|
||||
const selectQueryBuilder2 = makePaginationQuery<User>(AppDataSource
|
||||
.createQueryBuilder(User, "User"));
|
||||
selectQueryBuilder2.where('id > 5').getMany().then(result => {
|
||||
selectQueryBuilder2.where(BadInput).getMany().then(result => { // test: SQLInjectionPoint
|
||||
console.log(result)
|
||||
});
|
||||
|
||||
@@ -92,7 +94,7 @@ AppDataSource.initialize().then(async () => {
|
||||
insertQueryBuilder.into(User2)
|
||||
.values({
|
||||
firstName: "Timber",
|
||||
lastName: () => "LastNameToFind",
|
||||
lastName: () => BadInput, // test: SQLInjectionPoint
|
||||
age: 33,
|
||||
}).execute().then(result => {
|
||||
console.log(result)
|
||||
@@ -106,45 +108,45 @@ AppDataSource.initialize().then(async () => {
|
||||
.into(User2)
|
||||
.values({
|
||||
firstName: "Timber",
|
||||
lastName: () => "LastNameToFind",
|
||||
lastName: () => BadInput, // test: SQLInjectionPoint
|
||||
age: 33,
|
||||
})
|
||||
.orUpdate(
|
||||
["firstName", "lastName"],
|
||||
["externalId"],
|
||||
[BadInput, BadInput], // test: SQLInjectionPoint
|
||||
[BadInput], // test: SQLInjectionPoint
|
||||
)
|
||||
.getQueryAndParameters()
|
||||
|
||||
await AppDataSource.getRepository(User2).createQueryBuilder("user2")
|
||||
.update(User2)
|
||||
.set({ firstName: () => "firstname", lastName: "Saw2", age: 12 })
|
||||
.where('id > 5')
|
||||
.set({ firstName: () => BadInput, lastName: "Saw2", age: 12 }) // test: SQLInjectionPoint
|
||||
.where(BadInput,) // test: SQLInjectionPoint
|
||||
.execute()
|
||||
|
||||
await AppDataSource.getRepository(User2).createQueryBuilder('user2')
|
||||
.delete()
|
||||
.from(User2)
|
||||
.where('id > 5')
|
||||
.where(BadInput) // test: SQLInjectionPoint
|
||||
.execute()
|
||||
|
||||
|
||||
const queryRunner = AppDataSource.createQueryRunner()
|
||||
await queryRunner.query('SELECT name,id FROM table1 WHERE id > 5')
|
||||
await queryRunner.query(BadInput) // test: SQLInjectionPoint
|
||||
|
||||
await queryRunner.manager
|
||||
.createQueryBuilder(User2, "User")
|
||||
.select("name,id")
|
||||
.where("id > 5").execute()
|
||||
.select(BadInput) // test: SQLInjectionPoint
|
||||
.where(BadInput).execute() // test: SQLInjectionPoint
|
||||
|
||||
await AppDataSource
|
||||
.createQueryBuilder(User, "User")
|
||||
.innerJoin("User.profile", "profile", 'id > 5', {
|
||||
.innerJoin("User.profile", "profile", BadInput, { // test: SQLInjectionPoint
|
||||
id: 2,
|
||||
}).getMany().then(res => console.log(res))
|
||||
|
||||
await AppDataSource
|
||||
.createQueryBuilder(User, "User")
|
||||
.leftJoinAndMapOne("User.profile", "profile", "profile", 'id > 5', {
|
||||
.leftJoinAndMapOne("User.profile", "profile", "profile", BadInput, { // test: SQLInjectionPoint
|
||||
id: 2,
|
||||
}).getMany().then(res => console.log(res))
|
||||
|
||||
@@ -154,9 +156,9 @@ AppDataSource.initialize().then(async () => {
|
||||
.where((qb) => {
|
||||
const subQuery = qb
|
||||
.subQuery()
|
||||
.select("name,id")
|
||||
.select(BadInput) // test: SQLInjectionPoint
|
||||
.from(User2, "user2")
|
||||
.where('id > 5')
|
||||
.where(BadInput) // test: SQLInjectionPoint
|
||||
.getQuery()
|
||||
return "User2.id IN " + subQuery
|
||||
})
|
||||
@@ -165,54 +167,54 @@ AppDataSource.initialize().then(async () => {
|
||||
|
||||
|
||||
// Using repository
|
||||
let users = await AppDataSource.getRepository(User2).createQueryBuilder("User2").where("User2.id =:kind", { kind: 1 }).getMany()
|
||||
await AppDataSource.getRepository(User2).createQueryBuilder("User2").where("User2.id =:kind" + BadInput, { kind: 1 }).getMany()
|
||||
|
||||
// Using DataSource
|
||||
users = await AppDataSource
|
||||
await AppDataSource
|
||||
.createQueryBuilder()
|
||||
.select("User2")
|
||||
.select(BadInput) // test: SQLInjectionPoint
|
||||
.from(User2, "User2")
|
||||
.where('id > 5', { id: 1 })
|
||||
.where(BadInput, { id: 1 }) // test: SQLInjectionPoint
|
||||
.getMany()
|
||||
|
||||
// Using entity manager
|
||||
await AppDataSource.manager
|
||||
.createQueryBuilder(User2, "User2").where("User2.id =:kind and id > 5", { kind: '1' }).getMany()
|
||||
.createQueryBuilder(User2, "User2").where("User2.id =:kind" + BadInput, { kind: '1' }).getMany() // test: SQLInjectionPoint
|
||||
await AppDataSource
|
||||
.createQueryBuilder(User2, "User2")
|
||||
.leftJoinAndSelect("user.photos", "photo", 'id > 5').getMany()
|
||||
.leftJoinAndSelect("user.photos", "photo", BadInput).getMany() // test: SQLInjectionPoint
|
||||
await AppDataSource
|
||||
.createQueryBuilder(User2, "User2").groupBy("User2.id").having('id > 5').getMany()
|
||||
.createQueryBuilder(User2, "User2").groupBy("User2.id").having(BadInput).getMany() // test: SQLInjectionPoint
|
||||
// orderBy
|
||||
// it is a little bit restrictive, e.g. sqlite don't support it at all
|
||||
await AppDataSource
|
||||
.createQueryBuilder(User2, "User2").where('id > 5', {
|
||||
.createQueryBuilder(User2, "User2").where(BadInput, { // test: SQLInjectionPoint
|
||||
firstName: "Timber",
|
||||
})
|
||||
.where(
|
||||
new Brackets((qb) => {
|
||||
qb.where('id > 5').orWhere('id > 5');
|
||||
qb.where(BadInput).orWhere(BadInput); // test: SQLInjectionPoint
|
||||
})
|
||||
)
|
||||
.orderBy("name").orWhere('id > 5').getMany()
|
||||
.orderBy(BadInput).orWhere(BadInput).getMany() // test: SQLInjectionPoint
|
||||
|
||||
// relation
|
||||
AppDataSource.createQueryBuilder().relation(User, "name")
|
||||
.of(User)
|
||||
.select().where('id > 5').getMany().then(results => {
|
||||
.select().where(BadInput).getMany().then(results => { // test: SQLInjectionPoint
|
||||
console.log(results)
|
||||
})
|
||||
|
||||
// Brackets
|
||||
await AppDataSource.createQueryBuilder(User2, "User2")
|
||||
.where('id > 5')
|
||||
.where(BadInput) // test: SQLInjectionPoint
|
||||
.andWhere(
|
||||
new Brackets((qb) => {
|
||||
qb.where('id > 5').orWhere('id > 5');
|
||||
qb.where(BadInput).orWhere(BadInput); // test: SQLInjectionPoint
|
||||
})
|
||||
).andWhere(
|
||||
new NotBrackets((qb) => {
|
||||
qb.where('id > 5').orWhere('id > 5')
|
||||
qb.where(BadInput).orWhere(BadInput) // test: SQLInjectionPoint
|
||||
}),
|
||||
).getMany()
|
||||
}).catch(error => console.log(error))
|
||||
30
javascript/ql/test/experimental/TypeOrm/tests.expected
Normal file
30
javascript/ql/test/experimental/TypeOrm/tests.expected
Normal file
@@ -0,0 +1,30 @@
|
||||
| PASSED | SQLInjectionPoint | test.ts:19:54:19:79 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:20:55:20:80 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:82:70:82:95 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:88:70:88:95 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:97:41:97:66 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:111:41:111:66 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:115:37:115:62 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:116:27:116:52 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:122:74:122:99 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:123:29:123:54 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:129:28:129:53 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:134:41:134:66 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:138:29:138:54 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:139:38:139:63 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:143:61:143:86 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:149:80:149:105 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:159:37:159:62 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:161:36:161:61 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:175:29:175:54 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:177:39:177:64 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:182:108:182:133 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:185:74:185:99 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:187:94:187:119 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:191:65:191:90 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:196:57:196:82 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:199:58:199:83 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:204:65:204:90 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:210:28:210:53 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:213:56:213:81 | // test ... onPoint |
|
||||
| PASSED | SQLInjectionPoint | test.ts:217:56:217:81 | // test ... onPoint |
|
||||
27
javascript/ql/test/experimental/TypeOrm/tests.ql
Normal file
27
javascript/ql/test/experimental/TypeOrm/tests.ql
Normal file
@@ -0,0 +1,27 @@
|
||||
import javascript
|
||||
|
||||
class InlineTest extends LineComment {
|
||||
string tests;
|
||||
|
||||
InlineTest() { tests = this.getText().regexpCapture("\\s*test:(.*)", 1) }
|
||||
|
||||
string getPositiveTest() {
|
||||
result = tests.trim().splitAt(",").trim() and not result.matches("!%")
|
||||
}
|
||||
|
||||
predicate hasPositiveTest(string test) { test = this.getPositiveTest() }
|
||||
|
||||
predicate inNode(DataFlow::Node n) {
|
||||
this.getLocation().getFile() = n.getFile() and
|
||||
this.getLocation().getStartLine() = n.getStartLine()
|
||||
}
|
||||
}
|
||||
|
||||
import experimental.semmle.javascript.SQL
|
||||
|
||||
query predicate passingPositiveTests(string res, string expectation, InlineTest t) {
|
||||
res = "PASSED" and
|
||||
t.hasPositiveTest(expectation) and
|
||||
expectation = "SQLInjectionPoint" and
|
||||
exists(SQL::SqlString n | t.inNode(n))
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
| test.ts:19:20:19:50 | "user.f ... rstName |
|
||||
| test.ts:20:23:20:51 | "user.l ... astName |
|
||||
| test.ts:80:30:80:37 | 'id > 5' |
|
||||
| test.ts:86:31:86:38 | 'id > 5' |
|
||||
| test.ts:95:29:95:44 | "LastNameToFind" |
|
||||
| test.ts:109:29:109:44 | "LastNameToFind" |
|
||||
| test.ts:113:13:113:37 | ["first ... tName"] |
|
||||
| test.ts:114:13:114:26 | ["externalId"] |
|
||||
| test.ts:120:33:120:43 | "firstname" |
|
||||
| test.ts:121:16:121:23 | 'id > 5' |
|
||||
| test.ts:127:16:127:23 | 'id > 5' |
|
||||
| test.ts:132:29:132:69 | 'SELECT ... id > 5' |
|
||||
| test.ts:136:17:136:25 | "name,id" |
|
||||
| test.ts:137:16:137:23 | "id > 5" |
|
||||
| test.ts:141:47:141:54 | 'id > 5' |
|
||||
| test.ts:147:66:147:73 | 'id > 5' |
|
||||
| test.ts:154:16:162:9 | (qb) => ... } |
|
||||
| test.ts:157:25:157:33 | "name,id" |
|
||||
| test.ts:159:24:159:31 | 'id > 5' |
|
||||
| test.ts:168:92:168:108 | "User2.id =:kind" |
|
||||
| test.ts:173:17:173:23 | "User2" |
|
||||
| test.ts:175:16:175:23 | 'id > 5' |
|
||||
| test.ts:180:51:180:78 | "User2. ... id > 5" |
|
||||
| test.ts:183:52:183:59 | 'id > 5' |
|
||||
| test.ts:185:53:185:62 | "User2.id" |
|
||||
| test.ts:185:72:185:79 | 'id > 5' |
|
||||
| test.ts:189:51:189:58 | 'id > 5' |
|
||||
| test.ts:193:13:195:14 | new Bra ... }) |
|
||||
| test.ts:194:26:194:33 | 'id > 5' |
|
||||
| test.ts:194:44:194:51 | 'id > 5' |
|
||||
| test.ts:197:18:197:23 | "name" |
|
||||
| test.ts:197:34:197:41 | 'id > 5' |
|
||||
| test.ts:202:25:202:32 | 'id > 5' |
|
||||
| test.ts:208:16:208:23 | 'id > 5' |
|
||||
| test.ts:210:13:212:14 | new Bra ... }) |
|
||||
| test.ts:211:26:211:33 | 'id > 5' |
|
||||
| test.ts:211:44:211:51 | 'id > 5' |
|
||||
| test.ts:214:13:216:14 | new Not ... }) |
|
||||
| test.ts:215:26:215:33 | 'id > 5' |
|
||||
| test.ts:215:44:215:51 | 'id > 5' |
|
||||
@@ -1,3 +0,0 @@
|
||||
import javascript
|
||||
|
||||
select any(SQL::SqlString s)
|
||||
Reference in New Issue
Block a user