require('../utils'); const nql = require('../../'); const knex = require('knex')({client: 'mysql'}); describe('NQL -> SQL', function () { describe('Can handle regexes safely', function () { it('can handle regex STRING with escaped quotes', function () { let query; query = nql(`name:~'John O\\'Nolan'`); query.lex().should.eql([ {token: 'PROP', matched: 'name:'}, {token: 'CONTAINS', matched: '~'}, {token: 'STRING', matched: '\'John O\\\'Nolan\''} ]); query.toJSON().should.eql({name: {$regex: /John O'Nolan/i}}); query.querySQL(knex('users')).toQuery().should.eql('select * from `users` where lower(`users`.`name`) like \'%john o\\\'nolan%\' ESCAPE \'*\''); query = nql(`name:~'John O\\"Nolan'`); query.toJSON().should.eql({name: {$regex: /John O"Nolan/i}}); query.lex().should.eql([ {token: 'PROP', matched: 'name:'}, {token: 'CONTAINS', matched: '~'}, {token: 'STRING', matched: '\'John O\\"Nolan\''} ]); query.querySQL(knex('users')).toQuery().should.eql('select * from `users` where lower(`users`.`name`) like \'%john o\\"nolan%\' ESCAPE \'*\''); query = nql(`name:~'A\\'B\\"C\\"D\\''`); query.toJSON().should.eql({name: {$regex: /A'B"C"D'/i}}); query.lex().should.eql([ {token: 'PROP', matched: 'name:'}, {token: 'CONTAINS', matched: '~'}, {token: 'STRING', matched: '\'A\\\'B\\"C\\"D\\\'\''} ]); query.querySQL(knex('users')).toQuery().should.eql('select * from `users` where lower(`users`.`name`) like \'%a\\\'b\\"c\\"d\\\'%\' ESCAPE \'*\''); }); it('errors for unescaped quotes / injection patterns', function () { (function () { nql(`name:~'bad';'`).lex(); }).should.throw(); (function () { nql(`name:~'';'`).lex(); }).should.throw(); let query; // we can't force bad quotes, it errors as above // query = nql("name:~'';select * from `settings` where `value` like ''"); // Can we trick SQL? query = nql("name:~'\\';select * from `settings` where `value` like \\''"); // eslint-disable-line quotes // The regex only has the regex chars escaped, not quotes query.toJSON().should.eql({name: {$regex: /';select \* from `settings` where `value` like '/i}}); //SQL still ends up correctly escaped. This is all handled by knex... but having a test feels right query.querySQL(knex('users')).toQuery().should.eql('select * from `users` where lower(`users`.`name`) like \'%\\\';select ** from `settings` where `value` like \\\'%\' ESCAPE \'*\''); }); }); describe('Can combine not-equals filters into NIN correctly', function () { it('can collapse two NE filters into a single NOT IN', function () { let query; query = nql('tag:-tag1+tag:-tag2'); query.toJSON().should.eql({tag: {$nin: ['tag1', 'tag2']}}); query.querySQL(knex('posts')).toQuery().should.eql('select * from `posts` where `posts`.`tag` not in (\'tag1\', \'tag2\')'); }); it('can collapse NE filters that are in nested $and statements', function () { let query; query = nql('(tag:-tag1+tag:-tag2)+type:post'); query.toJSON().should.eql({$and: [{tag: {$nin: ['tag1', 'tag2']}}, {type: 'post'}]}); query.querySQL(knex('posts')).toQuery().should.eql('select * from `posts` where (`posts`.`tag` not in (\'tag1\', \'tag2\') and `posts`.`type` = \'post\')'); }); it('can collapse multiple NE filters into a single NOT IN', function () { let query; query = nql('tag:-tag1+tag:-tag2+tag:-tag3+tag:-tag4+tag:-tag5'); query.toJSON().should.eql({tag: {$nin: ['tag1', 'tag2', 'tag3', 'tag4', 'tag5']}}); query.querySQL(knex('posts')).toQuery().should.eql('select * from `posts` where `posts`.`tag` not in (\'tag1\', \'tag2\', \'tag3\', \'tag4\', \'tag5\')'); }); it('will not collapse not equals and nequals on the same property', function () { let query; query = nql('tag:-tag1+tag:tag2'); query.toJSON().should.eql({$and: [{tag: {$ne: 'tag1'}}, {tag: 'tag2'}]}); query.querySQL(knex('posts')).toQuery().should.eql('select * from `posts` where (`posts`.`tag` != \'tag1\' and `posts`.`tag` = \'tag2\')'); }); }); });