Verified Commit ef5736a4 authored by Aral Balkan's avatar Aral Balkan
Browse files

Improve the sanitisation sieve; fix boolean support in queries

parent c3098e75
...@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file. ...@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.1.1] - 2020-10-21
### Fixed
- Make sanitisation regular expression less greedy (the sieve now catches injection attempts it was previously missing).
- Boolean value support in queries now works properly (was previously being filtered by the sieve).
### Added
- Missing test for boolean values in queries.
## [1.1.0] - 2020-10-21 ## [1.1.0] - 2020-10-21
### Added ### Added
......
...@@ -58,7 +58,7 @@ class QueryProxy { ...@@ -58,7 +58,7 @@ class QueryProxy {
let sieve = this.query let sieve = this.query
.replace(/valueOf\..+?\.toLowerCase()\.(startsWith|endsWith|includes|startsWithCaseInsensitive|endsWithCaseInsensitive|includesCaseInsensitive)\(.+?\)/g, '') .replace(/valueOf\..+?\.toLowerCase()\.(startsWith|endsWith|includes|startsWithCaseInsensitive|endsWithCaseInsensitive|includesCaseInsensitive)\(.+?\)/g, '')
.replace(/valueOf\..+?\.(startsWith|endsWith|includes|startsWithCaseInsensitive|endsWithCaseInsensitive|includesCaseInsensitive)\(.+?\)/g, '') .replace(/valueOf\..+?\.(startsWith|endsWith|includes|startsWithCaseInsensitive|endsWithCaseInsensitive|includesCaseInsensitive)\(.+?\)/g, '')
.replace(/valueOf\..+?\s?(===|!==|<|>|<=|>=)\s?([\d_\.]+\s?|'.+?'|".+?")/g, '') .replace(/valueOf\.[^\.]+?\s?(===|\!==|<|>|<=|>=)\s?([\d_\.]+\s?|'.+?'|".+?"|false|true)/g, '')
.replace(/\|\|/g, '') .replace(/\|\|/g, '')
.replace(/\&\&/g,'') .replace(/\&\&/g,'')
.replace(/['"\(\)\s]/g, '') .replace(/['"\(\)\s]/g, '')
......
{ {
"name": "@small-tech/jsdb", "name": "@small-tech/jsdb",
"version": "1.1.0", "version": "1.1.1",
"description": "A transparent, in-memory, streaming write-on-update JavaScript database for Small Web applications that persists to a JavaScript transaction log.", "description": "A transparent, in-memory, streaming write-on-update JavaScript database for Small Web applications that persists to a JavaScript transaction log.",
"keywords": [ "keywords": [
"js", "js",
......
...@@ -375,16 +375,16 @@ test('Basic queries', t => { ...@@ -375,16 +375,16 @@ test('Basic queries', t => {
// Note: I know nothing about cars. This is randomly generated data. And I added the tags myself // Note: I know nothing about cars. This is randomly generated data. And I added the tags myself
// ===== to test the includes operator on an array property. // ===== to test the includes operator on an array property.
const cars = [ const cars = [
{ make: "Subaru", model: "Loyale", year: 1991, colour: "Fuscia", tags: ['fun', 'sporty'] }, { make: "Subaru", model: "Loyale", year: 1991, colour: "Fuscia", tags: ['fun', 'sporty'], own: true },
{ make: "Chevrolet", model: "Suburban 1500", year: 2004, colour: "Turquoise", tags: ['regal', 'expensive'] }, { make: "Chevrolet", model: "Suburban 1500", year: 2004, colour: "Turquoise", tags: ['regal', 'expensive'], own: false },
{ make: "Honda", model: "Element", year: 2004, colour: "Orange", tags: ['fun', 'affordable'] }, { make: "Honda", model: "Element", year: 2004, colour: "Orange", tags: ['fun', 'affordable'], own: false },
{ make: "Subaru", model: "Impreza", year: 2011, colour: "Crimson", tags: ['sporty', 'expensive']}, { make: "Subaru", model: "Impreza", year: 2011, colour: "Crimson", tags: ['sporty', 'expensive'], own: false},
{ make: "Hyundai", model: "Santa Fe", year: 2009, colour: "Turquoise", tags: ['sensible', 'affordable'] }, { make: "Hyundai", model: "Santa Fe", year: 2009, colour: "Turquoise", tags: ['sensible', 'affordable'], own: false },
{ make: "Toyota", model: "Avalon", year: 2005, colour: "Khaki", tags: ['fun', 'affordable']}, { make: "Toyota", model: "Avalon", year: 2005, colour: "Khaki", tags: ['fun', 'affordable'], own: false},
{ make: "Mercedes-Benz", model: "600SEL", year: 1992, colour: "Crimson", tags: ['regal', 'expensive', 'fun']}, { make: "Mercedes-Benz", model: "600SEL", year: 1992, colour: "Crimson", tags: ['regal', 'expensive', 'fun'], own: true},
{ make: "Jaguar", model: "XJ Series", year: 2004, colour: "Red", tags: ['fun', 'expensive', 'sporty']}, { make: "Jaguar", model: "XJ Series", year: 2004, colour: "Red", tags: ['fun', 'expensive', 'sporty'], own: true},
{ make: "Isuzu", model: "Hombre Space", year: 2000, colour: "Yellow", tags: ['sporty']}, { make: "Isuzu", model: "Hombre Space", year: 2000, colour: "Yellow", tags: ['sporty'], own: false},
{ make: "Lexus", model: "LX", year: 1997, colour: "Indigo", tags: ['regal', 'expensive', 'AMAZING'] } { make: "Lexus", model: "LX", year: 1997, colour: "Indigo", tags: ['regal', 'expensive', 'AMAZING'], own: false }
] ]
db.cars = cars db.cars = cars
...@@ -402,10 +402,42 @@ test('Basic queries', t => { ...@@ -402,10 +402,42 @@ test('Basic queries', t => {
const carWhereYearIsEqualTo1991 = db.cars.where('year').isEqualTo(1991).getFirst() const carWhereYearIsEqualTo1991 = db.cars.where('year').isEqualTo(1991).getFirst()
const carWhereYearEquals1991 = db.cars.where('year').equals(1991).getFirst() const carWhereYearEquals1991 = db.cars.where('year').equals(1991).getFirst()
// Test boolean values.
const carsIOwn = db.cars.where('own').is(true).get() // Note: I don’t actually own these cars ;)
t.strictEquals(JSON.stringify(carWhereYearIs1991), JSON.stringify(cars[0]), 'is returns the expected result') t.strictEquals(JSON.stringify(carWhereYearIs1991), JSON.stringify(cars[0]), 'is returns the expected result')
t.strictEquals(JSON.stringify(carWhereYearIs1991), JSON.stringify(carWhereYearIsEqualTo1991), 'is and isEqualTo are aliases') t.strictEquals(JSON.stringify(carWhereYearIs1991), JSON.stringify(carWhereYearIsEqualTo1991), 'is and isEqualTo are aliases')
t.strictEquals(JSON.stringify(carWhereYearIs1991), JSON.stringify(carWhereYearEquals1991), 'is and equals are aliases') t.strictEquals(JSON.stringify(carWhereYearIs1991), JSON.stringify(carWhereYearEquals1991), 'is and equals are aliases')
const expectedListOfCarsIOwn = JSON.stringify([
{
make: 'Subaru',
model: 'Loyale',
year: 1991,
colour: 'Fuscia',
tags: [ 'fun', 'sporty' ],
own: true
},
{
make: 'Mercedes-Benz',
model: '600SEL',
year: 1992,
colour: 'Crimson',
tags: [ 'regal', 'expensive', 'fun' ],
own: true
},
{
make: 'Jaguar',
model: 'XJ Series',
year: 2004,
colour: 'Red',
tags: [ 'fun', 'expensive', 'sporty' ],
own: true
}
])
t.strictEquals(JSON.stringify(carsIOwn), expectedListOfCarsIOwn, 'boolean values in queries are handled properly')
// //
// isNot, doesNotEqual // isNot, doesNotEqual
// //
...@@ -577,11 +609,11 @@ test('Basic queries', t => { ...@@ -577,11 +609,11 @@ test('Basic queries', t => {
t.strictEquals(complexCustomQueryResult.length, 5, 'complex custom query: returns 5 results') t.strictEquals(complexCustomQueryResult.length, 5, 'complex custom query: returns 5 results')
const expectedResult = [ const expectedResult = [
{ make: 'Chevrolet', model: 'Suburban 1500', year: 2004, colour: 'Turquoise', tags: [ 'regal', 'expensive' ] }, { make: 'Chevrolet', model: 'Suburban 1500', year: 2004, colour: 'Turquoise', tags: [ 'regal', 'expensive' ], own: false },
{ make: 'Honda', model: 'Element', year: 2004, colour: 'Orange', tags: [ 'fun', 'affordable' ] }, { make: 'Honda', model: 'Element', year: 2004, colour: 'Orange', tags: [ 'fun', 'affordable' ], own: false},
{ make: 'Toyota', model: 'Avalon', year: 2005, colour: 'Khaki', tags: [ 'fun', 'affordable' ] }, { make: 'Toyota', model: 'Avalon', year: 2005, colour: 'Khaki', tags: [ 'fun', 'affordable' ], own: false},
{ make: 'Mercedes-Benz', model: '600SEL', year: 1992, colour: 'Crimson', tags: [ 'regal', 'expensive', 'fun' ] }, { make: 'Mercedes-Benz', model: '600SEL', year: 1992, colour: 'Crimson', tags: [ 'regal', 'expensive', 'fun' ], own: true },
{ make: 'Lexus', model: 'LX', year: 1997, colour: 'Indigo', tags: [ 'regal', 'expensive', 'AMAZING' ] } { make: 'Lexus', model: 'LX', year: 1997, colour: 'Indigo', tags: [ 'regal', 'expensive', 'AMAZING' ], own: false}
] ]
t.strictEquals(JSON.stringify(complexCustomQueryResult), JSON.stringify(expectedResult), 'complex custom query result is as expected') t.strictEquals(JSON.stringify(complexCustomQueryResult), JSON.stringify(expectedResult), 'complex custom query result is as expected')
...@@ -665,7 +697,7 @@ test('Basic queries', t => { ...@@ -665,7 +697,7 @@ test('Basic queries', t => {
const queryInjectionAttackAttemptResult4 = db.cars.whereIsTrue('valueOf.make === "something"; globalThis.t.fail("Query injection payload 4 delivered"); valueOf.make === \'something\'').get() const queryInjectionAttackAttemptResult4 = db.cars.whereIsTrue('valueOf.make === "something"; globalThis.t.fail("Query injection payload 4 delivered"); valueOf.make === \'something\'').get()
const queryInjectionAttackAttemptResult5 = db.cars.whereIsTrue('valueOf.make === `something`; globalThis.t.fail("Query injection payload 5 delivered"); valueOf.make === \'something\'').get() const queryInjectionAttackAttemptResult5 = db.cars.whereIsTrue('valueOf.make === `2`; globalThis.t.fail("Query injection payload 5 delivered"); valueOf.make === \'something\'').get()
// Remove the global reference to the test instance as it’s no longer necessary. // Remove the global reference to the test instance as it’s no longer necessary.
globalThis.t = null globalThis.t = null
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment