Skip to content

Commit cffac90

Browse files
committed
fix(crud-typeorm): prevent sql injection in order by
1 parent 4bc9673 commit cffac90

File tree

4 files changed

+45
-3
lines changed

4 files changed

+45
-3
lines changed

integration/crud-typeorm/orm.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { join } from 'path';
22
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
3+
import { isNil } from '@nestjsx/util';
34

45
export const withCache: TypeOrmModuleOptions = {
56
type: 'postgres',
@@ -9,7 +10,9 @@ export const withCache: TypeOrmModuleOptions = {
910
password: 'root',
1011
database: 'nestjsx_crud',
1112
synchronize: false,
12-
logging: true,
13+
logging: !isNil(process.env.TYPEORM_LOGGING)
14+
? !!parseInt(process.env.TYPEORM_LOGGING, 10)
15+
: true,
1316
cache: {
1417
type: 'redis',
1518
options: {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"test": "yarn s test",
1919
"pretest:coveralls": "yarn pretest",
2020
"test:coveralls": "yarn s test.coveralls",
21-
"start:typeorm": "npx nodemon -w ./integration/crud-typeorm -e ts node_modules/.bin/ts-node integration/crud-typeorm/main.ts",
21+
"start:typeorm": "npx nodemon -w ./integration/crud-typeorm -e ts node_modules/ts-node/dist/bin.js integration/crud-typeorm/main.ts",
2222
"db:sync:typeorm": "cd ./integration/crud-typeorm && npx ts-node -r tsconfig-paths/register ../../node_modules/typeorm/cli.js schema:sync -f=orm",
2323
"db:drop:typeorm": "cd ./integration/crud-typeorm && npx ts-node -r tsconfig-paths/register ../../node_modules/typeorm/cli.js schema:drop -f=orm",
2424
"db:seeds:typeorm": "cd ./integration/crud-typeorm && npx ts-node -r tsconfig-paths/register ../../node_modules/typeorm/cli.js migration:run -f=orm",

packages/crud-typeorm/src/typeorm-crud.service.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ export class TypeOrmCrudService<T> extends CrudService<T> {
4545
protected entityPrimaryColumns: string[];
4646
protected entityColumnsHash: ObjectLiteral = {};
4747
protected entityRelationsHash: ObjectLiteral = {};
48+
protected sqlInjectionRegEx: RegExp[] = [
49+
/(%27)|(\')|(--)|(%23)|(#)/gi,
50+
/((%3D)|(=))[^\n]*((%27)|(\')|(--)|(%3B)|(;))/gi,
51+
/w*((%27)|(\'))((%6F)|o|(%4F))((%72)|r|(%52))/gi,
52+
/((%27)|(\'))union/gi,
53+
];
4854

4955
constructor(protected repo: Repository<T>) {
5056
super();
@@ -791,7 +797,9 @@ export class TypeOrmCrudService<T> extends CrudService<T> {
791797
const params: ObjectLiteral = {};
792798

793799
for (let i = 0; i < sort.length; i++) {
794-
params[this.getFieldWithAlias(sort[i].field)] = sort[i].order;
800+
const field = this.getFieldWithAlias(sort[i].field);
801+
const checkedFiled = this.checkSqlInjection(field);
802+
params[checkedFiled] = sort[i].order;
795803
}
796804

797805
return params;
@@ -947,4 +955,18 @@ export class TypeOrmCrudService<T> extends CrudService<T> {
947955
this.throwBadRequestException(`Invalid column '${cond.field}' value`);
948956
}
949957
}
958+
959+
private checkSqlInjection(field: string): string {
960+
/* istanbul ignore else */
961+
if (this.sqlInjectionRegEx.length) {
962+
for (let i = 0; i < this.sqlInjectionRegEx.length; i++) {
963+
/* istanbul ignore else */
964+
if (this.sqlInjectionRegEx[0].test(field)) {
965+
this.throwBadRequestException(`SQL injection detected: "${field}"`);
966+
}
967+
}
968+
}
969+
970+
return field;
971+
}
950972
}

packages/crud-typeorm/test/b.query-params.spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,23 @@ describe('#crud-typeorm', () => {
540540
res.body[0].company.projects[0].id,
541541
);
542542
});
543+
544+
it('should throw 400 if SQL injection has been detected', (done) => {
545+
const query = qb
546+
.sortBy({
547+
field: ' ASC; SELECT CAST( version() AS INTEGER); --',
548+
order: 'DESC',
549+
})
550+
.query();
551+
552+
return request(server)
553+
.get('/companies')
554+
.query(query)
555+
.end((_, res) => {
556+
expect(res.status).toBe(400);
557+
done();
558+
});
559+
});
543560
});
544561

545562
describe('#search', () => {

0 commit comments

Comments
 (0)