From dfe1e3a6c038eb28e1d00e4666f2df3bace5f937 Mon Sep 17 00:00:00 2001 From: Avi Shah Date: Thu, 5 Sep 2024 11:58:20 +0530 Subject: [PATCH] new logic changes support for multiline body --- .../findings/vulnerable-api/index.hbs | 20 +- app/utils/parse-vulnerable-api-finding.ts | 257 +++++++++--------- .../parse-vulnerable-api-finding-test.js | 8 +- 3 files changed, 134 insertions(+), 151 deletions(-) diff --git a/app/components/file-details/vulnerability-analysis-details/findings/vulnerable-api/index.hbs b/app/components/file-details/vulnerability-analysis-details/findings/vulnerable-api/index.hbs index 9fb1861394..f5b64d326b 100644 --- a/app/components/file-details/vulnerability-analysis-details/findings/vulnerable-api/index.hbs +++ b/app/components/file-details/vulnerability-analysis-details/findings/vulnerable-api/index.hbs @@ -26,20 +26,11 @@ {{#if @currentVulnerability.request.body}} {{#unless this.isRequestBodyEmpty}} - - {{t 'requestBody'}}: - - -
-
-            {{@currentVulnerability.request.body}}
-          
-
+ {{@currentVulnerability.request.body}} + {{/unless}} {{/if}} @@ -85,7 +76,6 @@ {{#unless this.isResponseBodyEmpty}} {{@currentVulnerability.response.text}} diff --git a/app/utils/parse-vulnerable-api-finding.ts b/app/utils/parse-vulnerable-api-finding.ts index 5cdad26bb3..d445278d07 100644 --- a/app/utils/parse-vulnerable-api-finding.ts +++ b/app/utils/parse-vulnerable-api-finding.ts @@ -23,13 +23,11 @@ export interface VulnerableApiResponse { url: string; } -type VulnerableApiFindingSection = - | 'request' - | 'response' - | 'request-headers' - | 'response-headers' - | 'params' - | 'other'; +type NestedKeyOf = { + [Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object + ? `${Key}` | `${Key}.${NestedKeyOf}` + : Key; +}[keyof ObjectType & (string | number)]; /** * Represents a finding of a vulnerability in an API. @@ -68,6 +66,46 @@ function initializeVulnerableApiFinding(): VulnerableApiFinding { }; } +function updateSection( + key: string, + currentSection: NestedKeyOf +): NestedKeyOf { + if (key === 'request') { + return 'request'; + } else if (key === 'response') { + return 'response'; + } else if (key === 'headers') { + return currentSection.startsWith('response') + ? 'response.headers' + : 'request.headers'; + } else if (key === 'params') { + return 'request.params'; + } else if (key === 'severity') { + return 'severity'; + } else if (key === 'confidence') { + return 'confidence'; + } else if (key === 'url') { + return currentSection.startsWith('response') + ? 'response.url' + : 'request.url'; + } else if (key === 'method') { + return 'request.method'; + } else if (key === 'body') { + return 'request.body'; + } else if (key === 'text') { + return 'response.text'; + } else if (key === 'status_code') { + return 'response.status_code'; + } else if (key === 'reason') { + return 'response.reason'; + } else if (key === 'key') { + return 'request.params.key'; + } else if (key === 'token') { + return 'request.params.token'; + } + return currentSection; // Default case if no section match +} + /** * Determines if a given content string contains information indicative of a vulnerable API finding. * @param content - The content string to check. @@ -142,9 +180,12 @@ function parseVulnerableApiFindingBlock(block: string): VulnerableApiFinding { const lines = block.split('\n'); const finding = initializeVulnerableApiFinding(); - let currentSection: VulnerableApiFindingSection = 'other'; + let currentSection: NestedKeyOf = 'confidence'; let currentHeaders: Record | null = null; + let currentBuffer: string | null = null; + let currentKey: string | null = null; + // Process the first line separately to handle initial URL or description processFirstLine(lines, finding); lines.forEach((line) => { @@ -153,20 +194,48 @@ function parseVulnerableApiFindingBlock(block: string): VulnerableApiFinding { if (parsedLine) { const [key, value] = parsedLine; - const { updatedSection, updatedHeaders } = updateVulnerableApiFinding( - finding, - key, - value, - currentSection, - currentHeaders - ); - - // update the current section and headers - currentSection = updatedSection; - currentHeaders = updatedHeaders; + if (currentKey && currentBuffer) { + // If a previous key exists, update it with the accumulated buffer + updateFindingField(finding, currentKey, currentBuffer, currentSection); + currentBuffer = null; // Reset buffer after updating + currentKey = null; // Reset key after updating + } + + if (key) { + if (currentKey && currentBuffer) { + // Continue accumulating the value if the same key is detected + currentBuffer += `\n${value}`; + } else { + // If a new key is detected, update section and headers + const { updatedSection, updatedHeaders } = updateVulnerableApiFinding( + finding, + key, + value, + currentSection, + currentHeaders + ); + + // Update current section and headers + if (updatedSection !== currentSection) { + currentSection = updatedSection; + } + + currentHeaders = updatedHeaders; + currentKey = key; // Track the current key + currentBuffer = value; // Start accumulating value + } + } + } else if (currentBuffer) { + // Continue accumulating the value if no new key is detected + currentBuffer += `\n${line}`; } }); + // Finalize any remaining buffer + if (currentBuffer && currentKey) { + updateFindingField(finding, currentKey, currentBuffer, currentSection); + } + return finding; } @@ -182,6 +251,14 @@ function parseLine(line: string): [string, string] | null { return null; } + // Split the line into key and value parts based on the first colon + const colonIndex = line.indexOf(':'); + + // If there's no colon, return null as it's not a valid key-value pair + if (colonIndex === -1) { + return null; + } + const value = valueParts.join(':').trim(); return [key.trim().toLowerCase(), value]; @@ -220,62 +297,6 @@ function processFirstLine( } } -/** - * Updates the severity and confidence fields of the finding based on the key-value pair. - * @param finding - The `VulnerableApiFinding` object to update. - * @param key - The key indicating the type of information (`severity` or `confidence`). - * @param value - The value to set. - */ -function updateFindingSeverityAndConfidence( - finding: VulnerableApiFinding, - key: string, - value: string -): void { - if (key === 'severity') { - finding.severity = value; - } else if (key === 'confidence') { - finding.confidence = value; - } -} - -/** - * Updates the current section of the finding based on the key. - * @param key - The key indicating the section to update. - * @param currentSection - The current section of the finding. - * @returns The updated section of the finding. - */ -function updateSection( - key: string, - currentSection: VulnerableApiFindingSection -): VulnerableApiFindingSection { - switch (key) { - case 'request': - case 'method': - return 'request'; - - case 'response': - case 'reason': - case 'text': - case 'status_code': - return 'response'; - - case 'headers': - return currentSection === 'response' - ? 'response-headers' - : 'request-headers'; - - case 'params': - return 'params'; - - case 'severity': - case 'confidence': - return 'other'; - - default: - return currentSection; - } -} - /** * Updates a specific field in the `VulnerableApiFinding` based on the current section. * @param finding - The `VulnerableApiFinding` object to update. @@ -287,18 +308,14 @@ function updateFindingField( finding: VulnerableApiFinding, key: string, value: string, - currentSection: VulnerableApiFindingSection + currentSection: NestedKeyOf ): void { - const isRequestSection = currentSection === 'request'; - const isResponseSection = currentSection === 'response'; - const isParamsSection = currentSection === 'params'; + const isRequestSection = currentSection.startsWith('request'); + const isResponseSection = currentSection.startsWith('response'); + const isParamsSection = currentSection.startsWith('request.params'); - if (key === 'body') { - if (isRequestSection) { - finding.request.body = value; - } else if (isResponseSection) { - finding.response.text = value; - } + if (key === 'body' && isRequestSection) { + finding.request.body = value; } else if (key === 'url') { if (isRequestSection || isParamsSection) { finding.request.url = value; @@ -311,15 +328,19 @@ function updateFindingField( finding.response.status_code = Number(value); } else if (key === 'reason' && isResponseSection) { finding.response.reason = value; + } else if (key === 'text' && isResponseSection) { + finding.response.text = value; + } else if (key === 'severity') { + finding.severity = value; + } else if (key === 'confidence') { + finding.confidence = value; + } else if (key === 'key' && isParamsSection) { + finding.request.params.key = value; + } else if (key === 'token' && isParamsSection) { + finding.request.params.token = value; } } -/** - * Updates the headers of the finding based on the current section. - * @param currentHeaders - The current headers to update. - * @param key - The key of the header. - * @param value - The value of the header. - */ function updateFindingHeaders( currentHeaders: Record | null, key: string, @@ -330,59 +351,31 @@ function updateFindingHeaders( } } -/** - * Updates the parameters of the finding. - * @param paramKey - The key of the parameter. - * @param paramValue - The value of the parameter. - * @param finding - The `VulnerableApiFinding` object to update. - */ -function updateFindingParams( - paramKey: string, - paramValue: string | undefined, - finding: VulnerableApiFinding -): void { - if (paramKey === 'key' || paramKey === 'token') { - finding.request.params[paramKey] = paramValue || ''; - } -} - -/** - * Updates a `VulnerableApiFinding` based on the key-value pair and current section. - * @param finding - The `VulnerableApiFinding` object to update. - * @param key - The key indicating what to update. - * @param value - The value to set. - * @param currentSection - The current section of the finding. - * @param currentHeaders - The current headers being processed. - * @returns The updated section and headers. - */ function updateVulnerableApiFinding( finding: VulnerableApiFinding, key: string, value: string, - currentSection: VulnerableApiFindingSection, + currentSection: NestedKeyOf, currentHeaders: Record | null ): { - updatedSection: VulnerableApiFindingSection; + updatedSection: NestedKeyOf; updatedHeaders: Record | null; } { - updateFindingSeverityAndConfidence(finding, key, value); - currentSection = updateSection(key, currentSection); updateFindingField(finding, key, value, currentSection); - if (key === 'headers' && currentSection === 'request-headers') { - currentHeaders = finding.request.headers; - } else if (key === 'headers' && currentSection === 'response-headers') { - currentHeaders = finding.response.headers; - } else if (key === 'params') { - currentSection = 'params'; - } else if (currentSection === 'params') { - updateFindingParams(key, value, finding); - } else if ( - currentSection === 'request-headers' || - currentSection === 'response-headers' - ) { + if (key === 'headers' && value === '') { + return { updatedSection: currentSection, updatedHeaders: currentHeaders }; + } + + // Handle headers separately based on the current section + if (currentSection.endsWith('headers')) { + if (currentSection === 'request.headers') { + currentHeaders = finding.request.headers; + } else if (currentSection === 'response.headers') { + currentHeaders = finding.response.headers; + } updateFindingHeaders(currentHeaders, key, value); } diff --git a/tests/unit/utils/parse-vulnerable-api-finding-test.js b/tests/unit/utils/parse-vulnerable-api-finding-test.js index ec85ac29df..c957cf2c41 100644 --- a/tests/unit/utils/parse-vulnerable-api-finding-test.js +++ b/tests/unit/utils/parse-vulnerable-api-finding-test.js @@ -56,7 +56,7 @@ module('Unit | Utility | parse-vulnerable-api-finding', function (hooks) { }, reason: 'Unauthorized', status_code: 401, - text: '', + text: "''", url: 'https://p157-contacts.icloud.com:443/mm/sub?token=b37163f4e3f63e20192b40e3bfe0ce293ba129f9706437f0dc0dce3e2bea9268&key=16304401481', }, severity: 'MEDIUM', @@ -85,7 +85,7 @@ module('Unit | Utility | parse-vulnerable-api-finding', function (hooks) { headers: {}, reason: '', status_code: 0, - text: '', + text: "''", url: '', }, severity: 'LOW', @@ -142,7 +142,7 @@ module('Unit | Utility | parse-vulnerable-api-finding', function (hooks) { }, reason: 'Unauthorized', status_code: 401, - text: '', + text: "''", url: 'https://p157-contacts.icloud.com:443/mm/sub?token=b37163f4e3f63e20192b40e3bfe0ce293ba129f9706437f0dc0dce3e2bea9268&key=16304401481', }, severity: 'MEDIUM', @@ -193,7 +193,7 @@ module('Unit | Utility | parse-vulnerable-api-finding', function (hooks) { }, reason: 'Unauthorized', status_code: 401, - text: '', + text: "''", url: 'https://p157-contacts.icloud.com:443/mm/sub?token=%3B+OR+%271%27%3D%271%27&key=16304401481', }, severity: 'MEDIUM',