Skip to content

Commit cdf4d37

Browse files
authored
fix: prevent $search queries from throwing (#772)
1 parent ac58f9a commit cdf4d37

File tree

2 files changed

+36
-8
lines changed

2 files changed

+36
-8
lines changed

db-service/lib/cql-functions.js

+12-6
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,16 @@ const StandardFunctions = {
2323
search: function (ref, arg) {
2424
if (!('val' in arg)) throw new Error(`Only single value arguments are allowed for $search`)
2525
// only apply first search term, rest is ignored
26-
const sub= /("")|("(?:[^"]|\\")*(?:[^\\]|\\\\)")|(\S*)/.exec(arg.val)
27-
arg.val = arg.__proto__.val = (sub[2] ? JSON.parse(sub[2]) : sub[3]) || ''
28-
const refs = ref.list || [ref],
29-
{ toString } = ref
26+
const sub = /("")|("(?:[^"]|\\")*(?:[^\\]|\\\\)")|(\S*)/.exec(arg.val)
27+
let val
28+
try {
29+
val = (sub[2] ? JSON.parse(sub[2]) : sub[3]) || ''
30+
} catch {
31+
val = sub[2] || sub[3] || ''
32+
}
33+
arg.val = arg.__proto__.val = val
34+
const refs = ref.list || [ref]
35+
const { toString } = ref
3036
return '(' + refs.map(ref2 => this.contains(this.tolower(toString(ref2)), this.tolower(arg))).join(' or ') + ')'
3137
},
3238
/**
@@ -159,8 +165,8 @@ const StandardFunctions = {
159165
* Generates SQL statement that produces current point in time (date and time with time zone)
160166
* @returns {string}
161167
*/
162-
now: function() {
163-
return this.session_context({val: '$now'})
168+
now: function () {
169+
return this.session_context({ val: '$now' })
164170
},
165171
/**
166172
* Generates SQL statement that produces the year of a given timestamp

test/scenarios/bookshop/search.test.js

+24-2
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ describe.skip('Bookshop - Search', () => {
7979
// ad-hoc search expression
8080
Books['@cds.search.authorsAddress'] = true
8181

82-
let res = await SELECT.from(Books).columns('author.name as author', 'title').search('1 Main Street, Bradford')
82+
let res = await SELECT.from(Books).columns('author.name as author', 'title').search('"1 Main Street, Bradford"')
8383
// author name in res[0] must match "Emily Brontë"
8484
expect(res.length).to.be.eq(1)
8585
expect(res[0].author).to.be.eq('Emily Brontë')
@@ -91,12 +91,34 @@ describe.skip('Bookshop - Search', () => {
9191
Books['@cds.search.author'] = true
9292
Authors['@cds.search.address'] = true // address is a calculated element
9393

94-
let res = await SELECT.from(Books).columns('author.name as author', 'title').search('1 Main Street, Bradford')
94+
let res = await SELECT.from(Books).columns('author.name as author', 'title').search('"1 Main Street, Bradford"')
9595
// author name in res[0] must match "Emily Brontë"
9696
expect(res.length).to.be.eq(1)
9797
expect(res[0].author).to.be.eq('Emily Brontë')
9898
})
9999

100+
test('Search escaped character in search literal', async () => {
101+
const { Books } = cds.entities
102+
const { Authors } = cds.entities
103+
// ad-hoc search expression
104+
Books['@cds.search.author'] = true
105+
Authors['@cds.search.address'] = true // address is a calculated element
106+
107+
let res = await SELECT.from(Books).columns('author.name as author', 'title').search('"\\"\\\\"')
108+
expect(res.length).to.be.eq(0)
109+
})
110+
111+
test('Search improperly escaped character in search literal', async () => {
112+
const { Books } = cds.entities
113+
const { Authors } = cds.entities
114+
// ad-hoc search expression
115+
Books['@cds.search.author'] = true
116+
Authors['@cds.search.address'] = true // address is a calculated element
117+
118+
let res = await SELECT.from(Books).columns('author.name as author', 'title').search('"\\q"')
119+
expect(res.length).to.be.eq(0)
120+
})
121+
100122
test('search on result of subselect', async () => {
101123
const res = await cds.run(
102124
SELECT.from(SELECT.from({ ref: ['sap.capire.bookshop.Books'] }).columns('title'))

0 commit comments

Comments
 (0)