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',