diff --git a/src/search/SearchRequest.ts b/src/search/SearchRequest.ts index 0dbc05c..c78982c 100644 --- a/src/search/SearchRequest.ts +++ b/src/search/SearchRequest.ts @@ -87,18 +87,23 @@ export class SearchRequest { */ static tokenizeSearchText(searchText: string): string[] { // based on code generated by gemini - const regex = /"([^"]*)"|\S+/g; // tokenizes all words between double quotes as well as every word outside of quotes - let tokens = []; - let match; - - while ((match = regex.exec(searchText)) !== null) { - if (match[1]) { - tokens.push(match[1]); // Add the content within quotes - } else { - tokens.push(match[0]); // Add the non-quoted token + let matches = SearchRequest.extractQuotedStrings(searchText); + // filter out ',' tokens + matches = matches.flatMap(item => item === ',' ? [] : item); + + // Process each match to unescape characters in quoted strings. + return matches.map(match => { + // Check if the match is a quoted string. + if (match.startsWith('"') && match.endsWith('"')) { + // Remove leading/trailing quotes and then unescape the characters. + let unescaped = match.slice(1, -1); + unescaped = unescaped.replace(/\\"/g, '"'); + unescaped = unescaped.replace(/\\\\/g, '\\'); + return unescaped; } - } - return tokens; + // For unquoted words, return as-is. + return match; + }); } @@ -403,6 +408,25 @@ export class SearchRequest { } + /** returns true iff searchText is a quoted string + */ + static isQuotedString = (searchText: string): boolean => { + return new RegExp(/"([^"]*)"/g).test(searchText); + // return new RegExp(/"(?:[^"\\]|\\.)+"|[^\s,]+/g).test(searchText); + }; + + + static extractQuotedStrings = (searchText: string): string[] => { + // based on code generated by gemini + // Regex to find tokens: quoted strings with escaped quotes, or unquoted words. + const regex = /"[^"]*"|[\S]+/g + // const regex = /"([^"]*)"/g; + const matches = searchText.match(regex); + return matches ?? []; + } + + + /** * determine the SearchReuestTypeId based on searchText * @param searchText the search text @@ -414,7 +438,12 @@ export class SearchRequest { if (searchText.includes('{')) { return 'SEARCH_STRING_CANNOT_CONTAIN_RESERVED_CHARACTERS' } - // disallow wildcards + // else if (searchText[0] == '"' && searchText[searchText.length - 1] == '"') { + // else if (new RegExp(/"([^"]*)"/g).test(searchText)) { + // else if (new RegExp(/"(?:[^"\\]|\\.)+"|[^\s,]+/g).test(searchText)) { + else if (SearchRequest.isQuotedString(searchText)) { + return 'SEARCH_PHRASE'; + } else if (searchText.includes('*')) { return 'SEARCH_AS_WILDCARD_ASTERISK' } @@ -429,11 +458,6 @@ export class SearchRequest { else if (searchText.includes('%')) { return 'WILDCARD_PERCENT_SEARCH_NOT_SUPPORTED' } - // else if (searchText[0] == '"' && searchText[searchText.length - 1] == '"') { - else if (new RegExp(/"([^"]*)"/g).test(searchText)) { - return 'SEARCH_PHRASE' - } - // process urls else if (SearchRequest.isUrl(searchText)) { // original from https://jsfiddle.net/DanielD/8S4nq/ diff --git a/src/search/test_cases/__snapshots__/search_exactPhrase.test.e2e.ts.snap b/src/search/test_cases/__snapshots__/search_exactPhrase.test.e2e.ts.snap new file mode 100644 index 0000000..f407d36 --- /dev/null +++ b/src/search/test_cases/__snapshots__/search_exactPhrase.test.e2e.ts.snap @@ -0,0 +1,874 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search("cpe:2.3:a:ivanti:connect_secure:-:*") correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2024-9420", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A use-after-free in Ivanti Connect Secure before version 22.7R2.3 and 9.1R18.9 + + and Ivanti Policy Secure before version 22.7R1.2 allows a remote authenticated attacker to achieve remote code execution", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "3c1d8aa1-5a33-4ea4-8992-aadd6440af75", + "assignerShortName": "ivanti", + "cveId": "CVE-2024-9420", + "datePublished": "2024-11-12T15:57:24.947Z", + "dateReserved": "2024-10-01T20:04:39.852Z", + "dateUpdated": "2024-11-27T20:21:28.876Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2024-9420", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search("cpe:2.3:a:ivanti:connect_secure:-:*:*:*:*:*:*:*") correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2024-9420", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A use-after-free in Ivanti Connect Secure before version 22.7R2.3 and 9.1R18.9 + + and Ivanti Policy Secure before version 22.7R1.2 allows a remote authenticated attacker to achieve remote code execution", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "3c1d8aa1-5a33-4ea4-8992-aadd6440af75", + "assignerShortName": "ivanti", + "cveId": "CVE-2024-9420", + "datePublished": "2024-11-12T15:57:24.947Z", + "dateReserved": "2024-10-01T20:04:39.852Z", + "dateUpdated": "2024-11-27T20:21:28.876Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2024-9420", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(*) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2025-37730", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "Improper certificate validation in Logstash's TCP output could lead to a man-in-the-middle (MitM) attack in “client” mode, as hostname verification in TCP output was not being performed when the ssl_verification_mode => full was set.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "271b6943-45a9-4f3a-ab4e-976f3fa05b5a", + "assignerShortName": "elastic", + "cveId": "CVE-2025-37730", + "datePublished": "2025-05-06T17:29:07.189Z", + "dateReserved": "2025-04-16T03:24:04.510Z", + "dateUpdated": "2025-05-06T17:51:59.631Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2025-37730", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(**) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2025-37730", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "Improper certificate validation in Logstash's TCP output could lead to a man-in-the-middle (MitM) attack in “client” mode, as hostname verification in TCP output was not being performed when the ssl_verification_mode => full was set.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "271b6943-45a9-4f3a-ab4e-976f3fa05b5a", + "assignerShortName": "elastic", + "cveId": "CVE-2025-37730", + "datePublished": "2025-05-06T17:29:07.189Z", + "dateReserved": "2025-04-16T03:24:04.510Z", + "dateUpdated": "2025-05-06T17:51:59.631Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2025-37730", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(*.*.*.*) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2025-32053", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A flaw was found in libsoup. A vulnerability in sniff_feed_or_html() and skip_insignificant_space() functions may lead to a heap buffer over-read.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "53f830b8-0a3f-465b-8143-3b8a9948e749", + "assignerShortName": "redhat", + "cveId": "CVE-2025-32053", + "datePublished": "2025-04-03T13:37:39.054Z", + "dateReserved": "2025-04-03T01:42:14.135Z", + "dateUpdated": "2025-05-29T06:49:29.146Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2025-32053", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(*27.*.0.1) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2022-30015", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "In Simple Food Website 1.0, a moderation can put the Cross Site Scripting Payload in any of the fields on http://127.0.0.1:1234/food/admin/all_users.php like Full Username, etc .This causes stored xss.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "8254265b-2729-46b6-b9e3-3dfca2d5bfca", + "assignerShortName": "mitre", + "cveId": "CVE-2022-30015", + "datePublished": "2022-05-23T20:50:00", + "dateReserved": "2022-05-02T00:00:00", + "dateUpdated": "2024-08-03T06:40:47.506Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2022-30015", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(*27.0.0.1) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2022-30015", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "In Simple Food Website 1.0, a moderation can put the Cross Site Scripting Payload in any of the fields on http://127.0.0.1:1234/food/admin/all_users.php like Full Username, etc .This causes stored xss.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "8254265b-2729-46b6-b9e3-3dfca2d5bfca", + "assignerShortName": "mitre", + "cveId": "CVE-2022-30015", + "datePublished": "2022-05-23T20:50:00", + "dateReserved": "2022-05-02T00:00:00", + "dateUpdated": "2024-08-03T06:40:47.506Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2022-30015", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(???.0.0.1) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2022-30015", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "In Simple Food Website 1.0, a moderation can put the Cross Site Scripting Payload in any of the fields on http://127.0.0.1:1234/food/admin/all_users.php like Full Username, etc .This causes stored xss.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "8254265b-2729-46b6-b9e3-3dfca2d5bfca", + "assignerShortName": "mitre", + "cveId": "CVE-2022-30015", + "datePublished": "2022-05-23T20:50:00", + "dateReserved": "2022-05-02T00:00:00", + "dateUpdated": "2024-08-03T06:40:47.506Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2022-30015", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(??7.0.0.1) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2022-30015", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "In Simple Food Website 1.0, a moderation can put the Cross Site Scripting Payload in any of the fields on http://127.0.0.1:1234/food/admin/all_users.php like Full Username, etc .This causes stored xss.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "8254265b-2729-46b6-b9e3-3dfca2d5bfca", + "assignerShortName": "mitre", + "cveId": "CVE-2022-30015", + "datePublished": "2022-05-23T20:50:00", + "dateReserved": "2022-05-02T00:00:00", + "dateUpdated": "2024-08-03T06:40:47.506Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2022-30015", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(?27.0.0.1) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2022-30015", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "In Simple Food Website 1.0, a moderation can put the Cross Site Scripting Payload in any of the fields on http://127.0.0.1:1234/food/admin/all_users.php like Full Username, etc .This causes stored xss.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "8254265b-2729-46b6-b9e3-3dfca2d5bfca", + "assignerShortName": "mitre", + "cveId": "CVE-2022-30015", + "datePublished": "2022-05-23T20:50:00", + "dateReserved": "2022-05-02T00:00:00", + "dateUpdated": "2024-08-03T06:40:47.506Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2022-30015", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(1??.0.0.1) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2022-30015", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "In Simple Food Website 1.0, a moderation can put the Cross Site Scripting Payload in any of the fields on http://127.0.0.1:1234/food/admin/all_users.php like Full Username, etc .This causes stored xss.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "8254265b-2729-46b6-b9e3-3dfca2d5bfca", + "assignerShortName": "mitre", + "cveId": "CVE-2022-30015", + "datePublished": "2022-05-23T20:50:00", + "dateReserved": "2022-05-02T00:00:00", + "dateUpdated": "2024-08-03T06:40:47.506Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2022-30015", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(12?.0.0.1) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2022-30015", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "In Simple Food Website 1.0, a moderation can put the Cross Site Scripting Payload in any of the fields on http://127.0.0.1:1234/food/admin/all_users.php like Full Username, etc .This causes stored xss.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "8254265b-2729-46b6-b9e3-3dfca2d5bfca", + "assignerShortName": "mitre", + "cveId": "CVE-2022-30015", + "datePublished": "2022-05-23T20:50:00", + "dateReserved": "2022-05-02T00:00:00", + "dateUpdated": "2024-08-03T06:40:47.506Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2022-30015", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(127.*.0.1) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2022-30015", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "In Simple Food Website 1.0, a moderation can put the Cross Site Scripting Payload in any of the fields on http://127.0.0.1:1234/food/admin/all_users.php like Full Username, etc .This causes stored xss.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "8254265b-2729-46b6-b9e3-3dfca2d5bfca", + "assignerShortName": "mitre", + "cveId": "CVE-2022-30015", + "datePublished": "2022-05-23T20:50:00", + "dateReserved": "2022-05-02T00:00:00", + "dateUpdated": "2024-08-03T06:40:47.506Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2022-30015", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(127.0.0.*) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2022-30015", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "In Simple Food Website 1.0, a moderation can put the Cross Site Scripting Payload in any of the fields on http://127.0.0.1:1234/food/admin/all_users.php like Full Username, etc .This causes stored xss.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "8254265b-2729-46b6-b9e3-3dfca2d5bfca", + "assignerShortName": "mitre", + "cveId": "CVE-2022-30015", + "datePublished": "2022-05-23T20:50:00", + "dateReserved": "2022-05-02T00:00:00", + "dateUpdated": "2024-08-03T06:40:47.506Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2022-30015", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(127.0.0.?) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2022-30015", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "In Simple Food Website 1.0, a moderation can put the Cross Site Scripting Payload in any of the fields on http://127.0.0.1:1234/food/admin/all_users.php like Full Username, etc .This causes stored xss.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "8254265b-2729-46b6-b9e3-3dfca2d5bfca", + "assignerShortName": "mitre", + "cveId": "CVE-2022-30015", + "datePublished": "2022-05-23T20:50:00", + "dateReserved": "2022-05-02T00:00:00", + "dateUpdated": "2024-08-03T06:40:47.506Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2022-30015", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(m*****t) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2025-23114", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A vulnerability in Veeam Updater component allows Man-in-the-Middle attackers to execute arbitrary code on the affected server. This issue occurs due to a failure to properly validate TLS certificate.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "36234546-b8fa-4601-9d6f-f4e334aa8ea1", + "assignerShortName": "hackerone", + "cveId": "CVE-2025-23114", + "datePublished": "2025-02-05T01:45:03.336Z", + "dateReserved": "2025-01-11T01:00:00.617Z", + "dateUpdated": "2025-03-13T18:23:04.462Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2025-23114", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(m**t) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2025-23114", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A vulnerability in Veeam Updater component allows Man-in-the-Middle attackers to execute arbitrary code on the affected server. This issue occurs due to a failure to properly validate TLS certificate.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "36234546-b8fa-4601-9d6f-f4e334aa8ea1", + "assignerShortName": "hackerone", + "cveId": "CVE-2025-23114", + "datePublished": "2025-02-05T01:45:03.336Z", + "dateReserved": "2025-01-11T01:00:00.617Z", + "dateUpdated": "2025-03-13T18:23:04.462Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2025-23114", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(m*cro*f*) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2025-23114", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A vulnerability in Veeam Updater component allows Man-in-the-Middle attackers to execute arbitrary code on the affected server. This issue occurs due to a failure to properly validate TLS certificate.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "36234546-b8fa-4601-9d6f-f4e334aa8ea1", + "assignerShortName": "hackerone", + "cveId": "CVE-2025-23114", + "datePublished": "2025-02-05T01:45:03.336Z", + "dateReserved": "2025-01-11T01:00:00.617Z", + "dateUpdated": "2025-03-13T18:23:04.462Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2025-23114", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(m*t) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2025-23114", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A vulnerability in Veeam Updater component allows Man-in-the-Middle attackers to execute arbitrary code on the affected server. This issue occurs due to a failure to properly validate TLS certificate.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "36234546-b8fa-4601-9d6f-f4e334aa8ea1", + "assignerShortName": "hackerone", + "cveId": "CVE-2025-23114", + "datePublished": "2025-02-05T01:45:03.336Z", + "dateReserved": "2025-01-11T01:00:00.617Z", + "dateUpdated": "2025-03-13T18:23:04.462Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2025-23114", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(micro* *office) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2022-38756", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A vulnerability has been identified in Micro Focus GroupWise Web in versions prior to 18.4.2. The GW Web component makes a request to the Post Office Agent that contains sensitive information in the query parameters that could be logged by any intervening HTTP proxies.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "f81092c5-7f14-476d-80dc-24857f90be84", + "assignerShortName": "microfocus", + "cveId": "CVE-2022-38756", + "datePublished": "2022-12-16T00:00:00", + "dateReserved": "2022-08-25T00:00:00", + "dateUpdated": "2024-08-03T11:02:14.534Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2022-38756", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(micro*) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2025-23114", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A vulnerability in Veeam Updater component allows Man-in-the-Middle attackers to execute arbitrary code on the affected server. This issue occurs due to a failure to properly validate TLS certificate.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "36234546-b8fa-4601-9d6f-f4e334aa8ea1", + "assignerShortName": "hackerone", + "cveId": "CVE-2025-23114", + "datePublished": "2025-02-05T01:45:03.336Z", + "dateReserved": "2025-01-11T01:00:00.617Z", + "dateUpdated": "2025-03-13T18:23:04.462Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2025-23114", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(micro?*) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2025-23114", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A vulnerability in Veeam Updater component allows Man-in-the-Middle attackers to execute arbitrary code on the affected server. This issue occurs due to a failure to properly validate TLS certificate.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "36234546-b8fa-4601-9d6f-f4e334aa8ea1", + "assignerShortName": "hackerone", + "cveId": "CVE-2025-23114", + "datePublished": "2025-02-05T01:45:03.336Z", + "dateReserved": "2025-01-11T01:00:00.617Z", + "dateUpdated": "2025-03-13T18:23:04.462Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2025-23114", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(micro?????) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2022-38758", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "Cross-site Scripting (XSS) vulnerability in NetIQ iManager prior to version 3.2.6 allows attacker to execute malicious scripts on the user's browser. This issue affects: Micro Focus NetIQ iManager NetIQ iManager versions prior to 3.2.6 on ALL.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "f81092c5-7f14-476d-80dc-24857f90be84", + "assignerShortName": "microfocus", + "cveId": "CVE-2022-38758", + "datePublished": "2023-01-25T00:00:00", + "dateReserved": "2022-08-25T00:00:00", + "dateUpdated": "2024-08-03T11:02:14.473Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2022-38758", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(micros???) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2025-23114", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A vulnerability in Veeam Updater component allows Man-in-the-Middle attackers to execute arbitrary code on the affected server. This issue occurs due to a failure to properly validate TLS certificate.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "36234546-b8fa-4601-9d6f-f4e334aa8ea1", + "assignerShortName": "hackerone", + "cveId": "CVE-2025-23114", + "datePublished": "2025-02-05T01:45:03.336Z", + "dateReserved": "2025-01-11T01:00:00.617Z", + "dateUpdated": "2025-03-13T18:23:04.462Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2025-23114", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(micros????) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2021-44228", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "Apache Log4j2 2.0-beta9 through 2.15.0 (excluding security releases 2.12.2, 2.12.3, and 2.3.1) JNDI features used in configuration, log messages, and parameters do not protect against attacker controlled LDAP and other JNDI related endpoints. An attacker who can control log messages or log message parameters can execute arbitrary code loaded from LDAP servers when message lookup substitution is enabled. From log4j 2.15.0, this behavior has been disabled by default. From version 2.16.0 (along with 2.12.2, 2.12.3, and 2.3.1), this functionality has been completely removed. Note that this vulnerability is specific to log4j-core and does not affect log4net, log4cxx, or other Apache Logging Services projects.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "f0158376-9dc2-43b6-827c-5f631a4d8d09", + "assignerShortName": "apache", + "cveId": "CVE-2021-44228", + "datePublished": "2021-12-10T00:00:00.000Z", + "dateReserved": "2021-11-26T00:00:00.000Z", + "dateUpdated": "2025-02-04T14:25:37.215Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2021-44228", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(micros?ft) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2025-23114", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A vulnerability in Veeam Updater component allows Man-in-the-Middle attackers to execute arbitrary code on the affected server. This issue occurs due to a failure to properly validate TLS certificate.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "36234546-b8fa-4601-9d6f-f4e334aa8ea1", + "assignerShortName": "hackerone", + "cveId": "CVE-2025-23114", + "datePublished": "2025-02-05T01:45:03.336Z", + "dateReserved": "2025-01-11T01:00:00.617Z", + "dateUpdated": "2025-03-13T18:23:04.462Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2025-23114", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(microsoft office) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2022-30190", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A remote code execution vulnerability exists when MSDT is called using the URL protocol from a calling application such as Word. An attacker who successfully exploits this vulnerability can run arbitrary code with the privileges of the calling application. The attacker can then install programs, view, change, or delete data, or create new accounts in the context allowed by the user’s rights. +Please see the MSRC Blog Entry for important information about steps you can take to protect your system from this vulnerability.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "f38d906d-7342-40ea-92c1-6c4a2c6478c8", + "assignerShortName": "microsoft", + "cveId": "CVE-2022-30190", + "datePublished": "2022-06-01T20:10:17.000Z", + "dateReserved": "2022-05-03T00:00:00.000Z", + "dateUpdated": "2025-02-04T19:04:33.929Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2022-30190", + ], +} +`; + +exports[`Wildcard Searches BasicSearchManager.search() BasicSearchManager.search(microsoft) correctly returns expected data (ok CveResult) 1`] = ` +Object { + "_id": "CVE-2025-23114", + "_index": Any, + "_score": null, + "_source": Object { + "containers": Object { + "cna": Object { + "descriptions": Array [ + Object { + "value": "A vulnerability in Veeam Updater component allows Man-in-the-Middle attackers to execute arbitrary code on the affected server. This issue occurs due to a failure to properly validate TLS certificate.", + }, + ], + }, + }, + "cveMetadata": Object { + "assignerOrgId": "36234546-b8fa-4601-9d6f-f4e334aa8ea1", + "assignerShortName": "hackerone", + "cveId": "CVE-2025-23114", + "datePublished": "2025-02-05T01:45:03.336Z", + "dateReserved": "2025-01-11T01:00:00.617Z", + "dateUpdated": "2025-03-13T18:23:04.462Z", + "state": "PUBLISHED", + }, + }, + "sort": Array [ + "CVE-2025-23114", + ], +} +`; diff --git a/src/search/test_cases/search_exactPhrase.test.e2e.ts b/src/search/test_cases/search_exactPhrase.test.e2e.ts new file mode 100644 index 0000000..d1fa810 --- /dev/null +++ b/src/search/test_cases/search_exactPhrase.test.e2e.ts @@ -0,0 +1,211 @@ +import { SearchProviderSpec } from '../../adapters/search/SearchAdapter.js'; +// import { SearchResultData } from "../SearchResultData.js"; +import { BasicSearchManager } from "../BasicSearchManager.js"; +import { SearchRequest, SearchRequestTypeId } from '../SearchRequest.js'; + +// because e2e testing is very specific to a dataset, we need to make sure we use the same opensearch dataset in cve-fixtures +// as was designed for this test. +const searchProviderSpec = SearchProviderSpec.getDefaultSearchProviderSpec(); +// const _testPipeline = `jest_test_ingest_pipeline` + + +describe('Wildcard Searches', () => { + // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- + + describe('SearchRequest.tokenizeSearchText()', () => { + const testCases: Array<{ input: string; expected: string[]; }> = [ + // phrase with 0 terms + { input: `""`, expected: [``] }, + // phrase with 1 term + { input: `"microsoft"`, expected: [`microsoft`] }, + // phrase in 2 terms + { input: `"microsoft office"`, expected: [`microsoft office`] }, + // phrase in 3 terms + { input: `"microsoft office 360"`, expected: [`microsoft office 360`] }, + { input: `"2025-09-05"`, expected: [`2025-09-05`] }, + // 2 phrases + { input: `"microsoft office" "360"`, expected: [`microsoft office`,`360`] }, + { input: `"2025-09" "05"`, expected: [`2025-09`, `05`] }, + // 3 phrases + { input: `"microsoft" "office" "360"`, expected: [`microsoft`, `office`, `360`] }, + { input: `"microsoft", "office", "360"`, expected: [`microsoft`, `office`, `360`] }, + { input: `"microsoft, office, 360"`, expected: [`microsoft, office, 360`] }, + { input: `"2025" "09" "05"`, expected: [`2025`, `09`, `05`] }, + // phrase with special characters + { input: `"microsoft off*"`, expected: [`microsoft off*`] }, + { input: `"CVE-20??-*"`, expected: [`CVE-20??-*`] }, + // phrase with unequal quotes (edge cases) + { input: `"microsoft office`, expected: [`"microsoft`, `office`] }, + { input: `"\\"abc\\"`, expected: [`\\`, `abc\\"`] }, + { input: `"""`, expected: [``, ``] }, + { input: `a"b`, expected: [`a"b`] }, + { input: `a\"b`, expected: [`a"b`] }, + // very edge cases + { input: `\"a\"b`, expected: [`a`, `b`] }, // because it's "a"b + { input: `\"a\" b`, expected: [`a`, `b`] }, // because it's "a" b + { input: `\"\"a \"b\"`, expected: [``, `a`, `b`] }, + { input: `"'"`, expected: [`'`] }, + { input: `'"'`, expected: [`'"'`] }, + { input: `'""'`, expected: [`'""'`] }, + // combinations of quoted and unquoted strings + { input: `"microsoft office" 360 1`, expected: [`microsoft office`, `360`, `1`] }, + ]; + + testCases.forEach(({ input, expected }) => { + it(`should correctly tokenize '${input}' into ${JSON.stringify(expected)}`, () => { + const result = SearchRequest.tokenizeSearchText(input); + expect(result).toEqual(expected); + }); + }); + }); + + + describe('SearchRequest.findSearchRequestType()', () => { + const testCases: Array<{ input: string; expectedType: string; }> = [ + // 1 term + { input: `"microsoft"`, expectedType: 'SEARCH_PHRASE' }, + // 2 terms + { input: `"v2.5"`, expectedType: 'SEARCH_PHRASE' }, + // 3 terms + { input: `"v1.2.5"`, expectedType: 'SEARCH_PHRASE' }, + { input: `"1.2.5"`, expectedType: 'SEARCH_PHRASE' }, + // 4 terms + { input: `"127.0.0.*"`, expectedType: 'SEARCH_PHRASE' }, + { input: `"*.*.*.*"`, expectedType: 'SEARCH_PHRASE' }, + // using special characters + { input: `"*"`, expectedType: 'SEARCH_PHRASE' }, + { input: `"????????????????"`, expectedType: 'SEARCH_PHRASE' }, + { input: `"127.0.0.?"`, expectedType: 'SEARCH_PHRASE' }, + // { input: `127.0.0.?`, expectedType: 'SEARCH_PHRASE' }, + { input: `"127.0.0.???"`, expectedType: 'SEARCH_PHRASE' }, + // using both * and ? + { input: `"127.*.0.???"`, expectedType: 'SEARCH_PHRASE' }, + { input: `"???.0.0.*"`, expectedType: 'SEARCH_PHRASE' }, + // edge cases + { input: `"""`, expectedType: 'SEARCH_PHRASE' }, + { input: `a"b`, expectedType: 'SEARCH_GENERAL_TEXT' }, + { input: `a\"b`, expectedType: 'SEARCH_GENERAL_TEXT' }, + + ]; + + testCases.forEach(({ input, expectedType }) => { + it(`findSearchRequestType('${input}') --> ${expectedType}`, () => { + const result = SearchRequest.findSearchRequestType(input); + expect(result).toBe(expectedType); + }); + }); + }); + + + describe.skip('SearchRequest.processSearchText()', () => { + const testCases: Array<{ input: string; expectedType: string; expectedProcessedText: string; }> = [ + { input: "127.0.0.*", expectedType: 'SEARCH_AS_WILDCARD_ASTERISK', expectedProcessedText: "127.0.0.*" }, + { input: "127.*.0.1", expectedType: 'SEARCH_AS_WILDCARD_ASTERISK', expectedProcessedText: "127.*.0.1" }, + { input: "12?.0.0.1", expectedType: 'SEARCH_AS_WILDCARD_QUESTION', expectedProcessedText: "12?.0.0.1" }, + { input: "1??.0.0.1", expectedType: 'SEARCH_AS_WILDCARD_QUESTION', expectedProcessedText: "1??.0.0.1" }, + { input: "127.0.0.?", expectedType: 'SEARCH_AS_WILDCARD_QUESTION', expectedProcessedText: "127.0.0.?" }, + { input: "127.0.0.??", expectedType: 'SEARCH_AS_WILDCARD_QUESTION', expectedProcessedText: "127.0.0.??" }, + { input: ".127.0.0.*", expectedType: 'SEARCH_AS_WILDCARD_ASTERISK', expectedProcessedText: ".127.0.0.*" }, + // { input: ".127.0.0.???", expectedType: 'SEARCH_AS_WILDCARD_QUESTION', expectedProcessedText: ".127.0.0.???" }, // + { input: "cpe:2.3:a:ivanti:connect_secure:-:*:*:*:*:*:*:*", expectedType: 'SEARCH_AS_WILDCARD_ASTERISK', expectedProcessedText: "cpe:2.3:a:ivanti:connect_secure:-:*:*:*:*:*:*:*" } + ]; + + testCases.forEach(({ input, expectedType, expectedProcessedText }) => { + it(`should correctly process "${input}" and return SearchRequestTypeId "${expectedType}"`, () => { + const req = new SearchRequest(input); + const result = req.processSearchText(); + + expect(result.data['searchTextType']).toBe(expectedType); + expect(result.data['processedSearchText']).toBe(expectedProcessedText); + expect(req._searchText).toBe(expectedProcessedText); + }); + }); + }); + + + describe.skip('BasicSearchManager.search()', () => { + const testCases: Array<{ input: string; succeed: boolean, expectedNum: number; }> = [ + { input: "127.0.0.*", succeed: true, expectedNum: 2 }, + { input: "127.*.0.1", succeed: true, expectedNum: 2 }, + { input: "12?.0.0.1", succeed: true, expectedNum: 2 }, + { input: "1??.0.0.1", succeed: true, expectedNum: 2 }, + { input: "127.0.0.?", succeed: true, expectedNum: 2 }, + { input: "127.0.0.??", succeed: true, expectedNum: 0 }, + { input: "127.0.0.???", succeed: true, expectedNum: 0 }, + { input: "micro*", succeed: true, expectedNum: 65 }, + { input: "micro?*", succeed: true, expectedNum: 57 }, //@todo, expected 65, same as "micro*" + { input: "micros?ft", succeed: true, expectedNum: 45 }, + { input: "microsoft", succeed: true, expectedNum: 45 }, // for comparison + { input: "micros???", succeed: true, expectedNum: 45 }, + { input: "micros????", succeed: true, expectedNum: 1 }, + { input: "micro?????", succeed: true, expectedNum: 7 }, + { input: "micro???????????", succeed: true, expectedNum: 0 }, // @todo, should return error due to repeating ? + { input: "microsoft office", succeed: true, expectedNum: 5 }, // for comparison + { input: "m*t", succeed: true, expectedNum: 237 }, + // ----- requires exact phrases ----- + { input: `"cpe:2.3:a:ivanti:connect_secure:-:*:*:*:*:*:*:*"`, succeed: true, expectedNum: 2 }, + { input: `"cpe:2.3:a:ivanti:connect_secure:-:*"`, succeed: true, expectedNum: 2 }, + // ----- costly wildcards ----- + { input: "*", succeed: true, expectedNum: 1109 }, + { input: "**", succeed: true, expectedNum: 1109 }, + // { input: "***", succeed: false, expectedNum: 1109 }, //@todo error + { input: "?27.0.0.1", succeed: true, expectedNum: 2 }, + { input: "??7.0.0.1", succeed: true, expectedNum: 2 }, + { input: "???.0.0.1", succeed: true, expectedNum: 2 }, + { input: "*27.0.0.1", succeed: true, expectedNum: 2 }, + { input: "*27.*.0.1", succeed: true, expectedNum: 2 }, + { input: "*.*.*.*", succeed: true, expectedNum: 802 }, //@todo ??? + { input: "micro* *office", succeed: true, expectedNum: 6 }, + { input: "m*cro*f*", succeed: true, expectedNum: 52 }, //@todo, expected 65, same as "micro*" + { input: "m**t", succeed: true, expectedNum: 237 }, //@todo, should return error due to repeating * + { input: "m*****t", succeed: true, expectedNum: 237 }, //@todo, should return error due to repeating * + // @todo + // [`"micro????"`, [`"micro????"`]], // @todo + // [`micro*`, [`micro*`]], + // [`*micro*`, [`*micro*`]], + // [`*micro**`, [`*micro**`]], + // [`*micro *`, [`*micro`, `*`]], + // [`micro????`, [`micro????`]], + // [`micro*????`, [`micro*????`]], + + // ----- ????? ----- + // { input: ".127.0.0.*", expectedNum: 2 }, + // { input: ".127.0.0.???", expectedType: 'SEARCH_AS_WILDCARD_QUESTION', expectedProcessedText: ".127.0.0.???" }, // + + ]; + + testCases.forEach(({ input, succeed, expectedNum }) => { + it(`BasicSearchManager.search(${input}) correctly returns expected data (ok CveResult)`, async () => { + // const req = new SearchRequest(input); + // const result = req.processSearchText(); + + // expect(result.data['searchTextType']).toBe(expectedType); + // expect(result.data['processedSearchText']).toBe(expectedProcessedText); + // expect(req._searchText).toBe(expectedProcessedText); + + const searchManager = new BasicSearchManager(SearchProviderSpec.getDefaultSearchProviderSpec()); + // testcases.forEach(async (testcase) => { + const resp = await searchManager.search(input, { + track_total_hits: true, + metadataOnly: true + }); + expect(resp.isOk()).toBe(succeed); + if (!resp.isOk()) { + console.error(`resp: ${JSON.stringify(resp, null, 2)}`); + } + const data = resp['data']['hits']; + const num = data.total.value; + expect(num).toBe(expectedNum); + if (num > 0) { + const first = data['hits'][0]; + expect(first).toMatchSnapshot({ + // _score: expect.any(Number), // this is null when a sort is set + _index: expect.any(String) + }); + } + }); + }); + }); + + +}); \ No newline at end of file