From 12612e0e828cf391b680a1855a42eef32bc44ba5 Mon Sep 17 00:00:00 2001 From: tcchhabra Date: Wed, 11 Mar 2020 14:15:48 +0530 Subject: [PATCH 1/3] fix for issue #3740 --- src/routes/projects/list.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/routes/projects/list.js b/src/routes/projects/list.js index 8ca1279a..f31d2124 100755 --- a/src/routes/projects/list.js +++ b/src/routes/projects/list.js @@ -62,7 +62,18 @@ const SUPPORTED_FILTERS = [ const escapeEsKeyword = keyword => keyword.replace(/[+-=>|\<|\!|\(|\)|\{|\}|\[|\]|\^|"|~|\*|\?|\:|\\|\/)/g, '\\$&'); +} + const buildEsFullTextQuery = (keyword, matchType, singleFieldName) => { + keyword = escapeElasticsearchQuery(keyword); let should = [ { query_string: { From f8ecc6b0f08fe5e331eb0960f443663245914c65 Mon Sep 17 00:00:00 2001 From: tcchhabra Date: Wed, 11 Mar 2020 14:43:19 +0530 Subject: [PATCH 2/3] fix for issue #3740 --- src/routes/projects/list.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/routes/projects/list.js b/src/routes/projects/list.js index f31d2124..a40fe370 100755 --- a/src/routes/projects/list.js +++ b/src/routes/projects/list.js @@ -69,15 +69,21 @@ const escapeEsKeyword = keyword => keyword.replace(/[+-=>|\<|\!|\(|\)|\{|\}|\[|\]|\^|"|~|\*|\?|\:|\\|\/)/g, '\\$&'); + const chars = ['\\', '+', '-', '&&', '||', '!', '(', ')', '{', '}', '[', ']', + '^', '"', '~', '*', '?', ':', '/', '<', '>']; + let result = query; + _.forEach(chars, (item) => { + result = result.replace(item, `\\${item}`); + }); + return result; } const buildEsFullTextQuery = (keyword, matchType, singleFieldName) => { - keyword = escapeElasticsearchQuery(keyword); + const escapedKeyword = escapeElasticsearchQuery(keyword); let should = [ { query_string: { - query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? keyword : `*${keyword}*`, + query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? escapedKeyword : `*${escapedKeyword}*`, analyze_wildcard: (matchType === MATCH_TYPE_WILDCARD), fields: ['name^5', 'description^3', 'type^2'], }, @@ -90,7 +96,7 @@ const buildEsFullTextQuery = (keyword, matchType, singleFieldName) => { path: 'details.utm', query: { query_string: { - query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? keyword : `*${keyword}*`, + query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? escapedKeyword : `*${escapedKeyword}*`, analyze_wildcard: (matchType === MATCH_TYPE_WILDCARD || matchType === MATCH_TYPE_SINGLE_FIELD), fields: ['details.utm.code^4'], }, @@ -104,7 +110,7 @@ const buildEsFullTextQuery = (keyword, matchType, singleFieldName) => { path: 'members', query: { query_string: { - query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? keyword : `*${keyword}*`, + query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? escapedKeyword : `*${escapedKeyword}*`, analyze_wildcard: (matchType === MATCH_TYPE_WILDCARD), fields: ['members.email', 'members.handle', 'members.firstName', 'members.lastName'], }, From 049f4dc8277365c90c919f24ce18223b45128921 Mon Sep 17 00:00:00 2001 From: tcchhabra Date: Sat, 14 Mar 2020 12:01:16 +0530 Subject: [PATCH 3/3] fix for issue #3740 feedback --- src/routes/projects/list.js | 41 +++++++++++++++-------------- src/routes/projects/list.spec.js | 44 ++++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/routes/projects/list.js b/src/routes/projects/list.js index a40fe370..97402f40 100755 --- a/src/routes/projects/list.js +++ b/src/routes/projects/list.js @@ -62,28 +62,11 @@ const SUPPORTED_FILTERS = [ const escapeEsKeyword = keyword => keyword.replace(/[+-=>']; - let result = query; - _.forEach(chars, (item) => { - result = result.replace(item, `\\${item}`); - }); - return result; -} - const buildEsFullTextQuery = (keyword, matchType, singleFieldName) => { - const escapedKeyword = escapeElasticsearchQuery(keyword); let should = [ { query_string: { - query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? escapedKeyword : `*${escapedKeyword}*`, + query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? keyword : `*${keyword}*`, analyze_wildcard: (matchType === MATCH_TYPE_WILDCARD), fields: ['name^5', 'description^3', 'type^2'], }, @@ -96,7 +79,7 @@ const buildEsFullTextQuery = (keyword, matchType, singleFieldName) => { path: 'details.utm', query: { query_string: { - query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? escapedKeyword : `*${escapedKeyword}*`, + query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? keyword : `*${keyword}*`, analyze_wildcard: (matchType === MATCH_TYPE_WILDCARD || matchType === MATCH_TYPE_SINGLE_FIELD), fields: ['details.utm.code^4'], }, @@ -110,7 +93,7 @@ const buildEsFullTextQuery = (keyword, matchType, singleFieldName) => { path: 'members', query: { query_string: { - query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? escapedKeyword : `*${escapedKeyword}*`, + query: (matchType === MATCH_TYPE_EXACT_PHRASE) ? keyword : `*${keyword}*`, analyze_wildcard: (matchType === MATCH_TYPE_WILDCARD), fields: ['members.email', 'members.handle', 'members.firstName', 'members.lastName'], }, @@ -285,6 +268,22 @@ const setFilter = (value, keyword, fieldName) => { return buildEsQueryWithFilter(value, keyword, MATCH_TYPE_EXACT_PHRASE, fieldName); }; +/** + * ES need to skip special chars else it is considered as RegEx + * + * @param {String} query query being searched for + * @return {String} result after parsing + */ + function escapeElasticsearchQuery(query) { + const chars = ['\\', '+', '-', '&&', '||', '!', '(', ')', '{', '}', '[', ']', + '^', '"', '~', '*', '?', ':', '/', '<', '>']; + let result = query; + _.forEach(chars, (item) => { + result = result.replace(item, `\\${item}`); + }); + return result; + } + /** * Parse the ES search criteria and prepare search request body * @@ -443,7 +442,7 @@ const parseElasticSearchCriteria = (criteria, fields, order) => { if (!keyword) { // Not a specific field search nor an exact phrase search, do a wildcard match - keyword = criteria.filters.keyword; + keyword = escapeElasticsearchQuery(keywordCriterion); matchType = MATCH_TYPE_WILDCARD; } diff --git a/src/routes/projects/list.spec.js b/src/routes/projects/list.spec.js index 9ebb3e13..373f4dbb 100644 --- a/src/routes/projects/list.spec.js +++ b/src/routes/projects/list.spec.js @@ -21,7 +21,7 @@ const data = [ type: 'generic', billingAccountId: 1, name: 'test1', - description: 'test project1', + description: 'test project1 abc/d', status: 'active', details: { utm: { @@ -1065,7 +1065,7 @@ describe('LIST Project', () => { resJson.should.have.lengthOf(1); resJson[0].should.have.property('description'); resJson[0].should.not.have.property('cancelReason'); - resJson[0].description.should.be.eq('test project1'); + resJson[0].description.should.be.eq('test project1 abc/d'); done(); } }); @@ -1244,6 +1244,46 @@ describe('LIST Project', () => { } }); }); + + it('should find a project by quoted keyword with a special symbol in the name', (done) => { + request(server) + .get('/v5/projects/?keyword="abc/d"') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.have.lengthOf(1); + done(); + } + }); + }); + + it('should find a project by keyword with a special symbol in the name', (done) => { + request(server) + .get('/v5/projects/?keyword=abc/d') + .set({ + Authorization: `Bearer ${testUtil.jwts.admin}`, + }) + .expect('Content-Type', /json/) + .expect(200) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body; + should.exist(resJson); + resJson.should.have.lengthOf(1); + done(); + } + }); + }); }); }); });