diff --git a/javascript/ql/src/experimental/semmle/javascript/SQL.qll b/javascript/ql/src/experimental/semmle/javascript/SQL.qll index 3581106e2f85..5127881085d8 100644 --- a/javascript/ql/src/experimental/semmle/javascript/SQL.qll +++ b/javascript/ql/src/experimental/semmle/javascript/SQL.qll @@ -146,11 +146,42 @@ module ExperimentalSql { override DataFlow::Node getAQueryArgument() { result = this.getArgument(0) } } + /** + * A call to a TypeORM `Repository` (https://orkhan.gitbook.io/typeorm/docs/repository-api) + */ + private class RepositoryCall extends DatabaseAccess { + API::Node repository; + + RepositoryCall() { + ( + repository = API::moduleImport("typeorm").getMember("Repository").getInstance() or + repository = dataSource().getMember("getRepository").getReturn() + ) and + this = repository.getMember(_).asSource() + } + + override DataFlow::Node getAResult() { + result = + repository + .getMember([ + "find", "findBy", "findOne", "findOneBy", "findOneOrFail", "findOneByOrFail", + "findAndCount", "findAndCountBy" + ]) + .getReturn() + .asSource() + } + + override DataFlow::Node getAQueryArgument() { + result = repository.getMember("query").getParameter(0).asSink() + } + } + /** 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() + this = any(QueryBuilderCall qb).getAQueryArgument() or + this = any(RepositoryCall rc).getAQueryArgument() } } } diff --git a/javascript/ql/test/experimental/TypeOrm/test.ts b/javascript/ql/test/experimental/TypeOrm/test.ts index 39e4dbb6ec35..3f6cd54d22d0 100644 --- a/javascript/ql/test/experimental/TypeOrm/test.ts +++ b/javascript/ql/test/experimental/TypeOrm/test.ts @@ -217,4 +217,9 @@ AppDataSource.initialize().then(async () => { qb.where(BadInput).orWhere(BadInput) // test: SQLInjectionPoint }), ).getMany() + + // Repository.query sink + await AppDataSource.getRepository(User2) + .query(BadInput) // test: SQLInjectionPoint + }).catch(error => console.log(error)) diff --git a/javascript/ql/test/experimental/TypeOrm/tests.expected b/javascript/ql/test/experimental/TypeOrm/tests.expected index a8f092c33e31..cbcf7785c787 100644 --- a/javascript/ql/test/experimental/TypeOrm/tests.expected +++ b/javascript/ql/test/experimental/TypeOrm/tests.expected @@ -29,4 +29,5 @@ passingPositiveTests | 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 | +| PASSED | SQLInjectionPoint | test.ts:223:29:223:54 | // test ... onPoint | failingPositiveTests