ci(security): integrate Bright CI pipeline for security tests and remediation#871
ci(security): integrate Bright CI pipeline for security tests and remediation#871bright-security-golf[bot] wants to merge 18 commits intostablefrom
Conversation
skip-checks:true
skip-checks:true
skip-checks:true
skip-checks:true
skip-checks:true
| const text = raw.toString().trim(); | ||
| const res = dotT.compile(text)(); | ||
| // Escape user input to prevent Server Side Template Injection | ||
| const escapedText = text.replace(/\{\{.*?\}\}/g, ''); |
Check failure
Code scanning / CodeQL
Polynomial regular expression used on uncontrolled data High
| const res = dotT.compile(text)(); | ||
| // Escape user input to prevent Server Side Template Injection | ||
| const escapedText = text.replace(/\{\{.*?\}\}/g, ''); | ||
| const res = dotT.compile(escapedText)(); |
Check failure
Code scanning / CodeQL
Code injection Critical
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 15 days ago
In general, to fix this type of issue you must not treat arbitrary user input as a template to be compiled or evaluated. Instead, templates should be static (or at least come from trusted configuration), and user input should only be used as data passed into those templates, where the template engine can safely escape it.
The minimal fix here, without changing the public API shape more than necessary, is:
- Stop compiling user‑provided text as a doT template.
- If all you need is to echo or transform text, just return it (or safely escape it) without using
dotT.compile. - If you really need templating, use a static template string and interpolate user text as data through the template context.
Given the current method name renderTemplate and swagger docs describing “Write your text here / Rendered result”, the least intrusive change is to remove the call to dotT.compile and simply return the text (or a basic transformation), keeping logging behavior. Specifically, in src/app.controller.ts:
- Replace lines 72–80’s body so that, after trimming, you no longer call
dotT.compile(escapedText)(). Instead, just return the (possibly sanitized) text, and ensure the function returns a string in all cases. - Optionally, you can also simplify by removing the ineffective
escapedTextlogic; but to minimize functional change, you can keep it and just not treat it as a template.
No additional imports or methods are needed for this minimal secure fix.
| @@ -74,10 +74,10 @@ | ||
| const text = raw.toString().trim(); | ||
| // Escape user input to prevent Server Side Template Injection | ||
| const escapedText = text.replace(/\{\{.*?\}\}/g, ''); | ||
| const res = dotT.compile(escapedText)(); | ||
| this.logger.debug(`Rendered template: ${res}`); | ||
| return res; | ||
| this.logger.debug(`Rendered template: ${escapedText}`); | ||
| return escapedText; | ||
| } | ||
| return ''; | ||
| } | ||
|
|
||
| @Get('goto') |
| private isValidXPath(xpath: string): boolean { | ||
| // Implement a basic validation logic or use a library | ||
| // For demonstration, we assume a simple check | ||
| return !xpath.includes("//") && !xpath.includes("'"); |
Check failure
Code scanning / CodeQL
Type confusion through parameter tampering Critical
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 15 days ago
In general, to fix type confusion from HTTP parameters, ensure that any value coming from the request is checked to be of the expected runtime type (here, a string) before applying string methods or using it in security-sensitive logic. Reject or normalize non-string values explicitly.
For this code, the best minimal fix is:
- Add a helper method to normalize the query parameter to a string, or reject non-string inputs. Since the code already uses a validation method, we can enhance
isValidXPathto first verify the type. - Specifically, change
isValidXPathso that it:- Returns
falseifxpathis not a string (e.g., is an array or any other type). - Only applies
.includeswhen the type is confirmed as string.
- Returns
- Optionally, you can add a similar type guard in
queryPartnersRawbefore logging and before callingescapeXPathValue/includes, but becausequeryPartnersRawalready relies onisValidXPathfor rejection, updatingisValidXPathis sufficient without changing observable behavior (invalid inputs already result in an error).
Concretely:
- In
src/partners/partners.controller.ts, modify theisValidXPathimplementation (lines 163–167) to:
private isValidXPath(xpath: string): boolean {
if (typeof xpath !== 'string') {
return false;
}
return !xpath.includes('//') && !xpath.includes("'");
}This ensures that when CodeQL follows tainted data into includes, the value is guaranteed to be a string, eliminating the type confusion risk while preserving existing behavior (non-string values simply cause the same error path as other invalid XPath expressions).
| @@ -163,6 +163,9 @@ | ||
| private isValidXPath(xpath: string): boolean { | ||
| // Implement a basic validation logic or use a library | ||
| // For demonstration, we assume a simple check | ||
| return !xpath.includes("//") && !xpath.includes("'"); | ||
| if (typeof xpath !== 'string') { | ||
| return false; | ||
| } | ||
| return !xpath.includes('//') && !xpath.includes("'"); | ||
| } | ||
| } |
| private isValidXPath(xpath: string): boolean { | ||
| // Implement a basic validation logic or use a library | ||
| // For demonstration, we assume a simple check | ||
| return !xpath.includes("//") && !xpath.includes("'"); |
Check failure
Code scanning / CodeQL
Type confusion through parameter tampering Critical
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 15 days ago
In general terms, fix the problem by ensuring that any user-controlled HTTP parameter that is assumed to be a string is either (a) checked at runtime to confirm it is a string (and rejected otherwise), or (b) safely coerced into a string in a well-defined way before using string methods on it. This prevents arrays (or other types) from being treated as strings in a way that could bypass validation or sanitization.
For this specific code, the best fix with minimal functional change is:
- Add a small helper that takes an arbitrary value (potentially string, string[] or undefined) and either:
- returns it as a string if it is a string,
- rejects it (throws an error) if it is not a string.
- Use this helper at the beginning of
queryPartnersRawandsearchPartnersto normalize/validate the query parameters before they are used. - Keep the existing XPath validation and escaping logic, but only run it on validated strings.
Concretely, within src/partners/partners.controller.ts:
- Add a private method like
private ensureStringParam(param: unknown, name: string): stringat the bottom of the controller (or near the other helpers). It should:- Check
typeof param === 'string'. - If not, throw an
HttpExceptionwithHttpStatus.BAD_REQUEST, indicating an invalid parameter type.
- Check
- In
queryPartnersRaw, immediately convert thexpathparameter using this helper, e.g.const xpathStr = this.ensureStringParam(xpath, 'xpath');and then usexpathStrinstead ofxpathinisValidXPathand in the call topartnersService.getPartnersProperties. - In
searchPartners, similarly normalizekeywordwithensureStringParamand use the result forescapeXPathValueand logging.
This keeps behavior unchanged for valid string inputs, but safely rejects array or other non-string values instead of letting them slip through type confusion.
| @@ -42,15 +42,16 @@ | ||
| @ApiOkResponse({ | ||
| type: String | ||
| }) | ||
| async queryPartnersRaw(@Query('xpath') xpath: string): Promise<string> { | ||
| this.logger.debug(`Getting partners with xpath expression "${xpath}"`); | ||
| async queryPartnersRaw(@Query('xpath') xpath: unknown): Promise<string> { | ||
| const xpathStr = this.ensureStringParam(xpath, 'xpath'); | ||
| this.logger.debug(`Getting partners with xpath expression "${xpathStr}"`); | ||
|
|
||
| try { | ||
| // Validate the xpath input to prevent injection | ||
| if (!this.isValidXPath(xpath)) { | ||
| if (!this.isValidXPath(xpathStr)) { | ||
| throw new Error('Invalid XPath expression'); | ||
| } | ||
| return this.partnersService.getPartnersProperties(xpath); | ||
| return this.partnersService.getPartnersProperties(xpathStr); | ||
| } catch (err) { | ||
| throw new HttpException( | ||
| `Failed to load XML using XPATH. Details: ${err}`, | ||
| @@ -131,12 +127,15 @@ | ||
| @ApiOkResponse({ | ||
| type: String | ||
| }) | ||
| async searchPartners(@Query('keyword') keyword: string): Promise<string> { | ||
| this.logger.debug(`Searching partner names by the keyword "${keyword}"`); | ||
| async searchPartners(@Query('keyword') keyword: unknown): Promise<string> { | ||
| const keywordStr = this.ensureStringParam(keyword, 'keyword'); | ||
| this.logger.debug( | ||
| `Searching partner names by the keyword "${keywordStr}"` | ||
| ); | ||
|
|
||
| try { | ||
| // Escape user input to prevent XPath injection | ||
| const safeKeyword = this.escapeXPathValue(keyword); | ||
| const safeKeyword = this.escapeXPathValue(keywordStr); | ||
| const xpath = `//partners/partner/name[contains(., '${safeKeyword}')]`; | ||
| return this.partnersService.getPartnersProperties(xpath); | ||
| } catch (err) { | ||
| @@ -163,6 +159,17 @@ | ||
| private isValidXPath(xpath: string): boolean { | ||
| // Implement a basic validation logic or use a library | ||
| // For demonstration, we assume a simple check | ||
| return !xpath.includes("//") && !xpath.includes("'"); | ||
| return !xpath.includes('//') && !xpath.includes("'"); | ||
| } | ||
|
|
||
| // Ensure that query parameters expected to be strings are actually strings | ||
| private ensureStringParam(param: unknown, name: string): string { | ||
| if (typeof param !== 'string') { | ||
| throw new HttpException( | ||
| `Invalid "${name}" parameter. Expected a single string value.`, | ||
| HttpStatus.BAD_REQUEST | ||
| ); | ||
| } | ||
| return param; | ||
| } | ||
| } |
| private isValidXPath(xpath: string): boolean { | ||
| // Implement a basic validation logic or use a library | ||
| // For demonstration, we assume a simple check | ||
| return !xpath.includes("//") && !xpath.includes("'"); |
Check failure
Code scanning / CodeQL
Type confusion through parameter tampering Critical
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 15 days ago
In general, to fix this kind of issue you must ensure that any value derived from an HTTP parameter is validated or normalized to the expected primitive type before using methods like includes, indexOf, or concatenation. For query parameters, that means checking typeof value === 'string' (and possibly Array.isArray) and either rejecting non-string input or converting it to a safe canonical string.
For this codebase, the minimal, non‑breaking fix is to harden the isValidXPath function in src/partners/partners.service.ts so that it only applies .includes when the argument is actually a string. If the value is not a string (e.g., an array), we should treat it as invalid and return false. This protects both the service’s internal validation and the controller’s this.isValidXPath(xpath) call (the controller snippet references such a method, and given the shared pattern, it's reasonable to use the same runtime checks there, but per your constraints I will only modify the shown code in partners.service.ts). Concretely, we will:
- Update
isValidXPath(xpath: string): booleaninPartnersServiceto:- First check
if (typeof xpath !== 'string') return false;. - Then perform the existing substring checks.
- First check
- This ensures that if the client sends
?xpath=a&xpath=b(so Nest gives an array),isValidXPathwill safely returnfalseinstead of callingArray.prototype.includeswith different semantics.
No additional methods or external libraries are required; we just add a simple runtime type guard in the existing function.
| @@ -91,8 +91,15 @@ | ||
|
|
||
| // Basic validation for XPath expressions | ||
| private isValidXPath(xpath: string): boolean { | ||
| // Ensure the value is a string at runtime to avoid type confusion | ||
| if (typeof xpath !== 'string') { | ||
| this.logger.debug( | ||
| `Invalid XPath type received. Expected 'string', got '${typeof xpath}'.` | ||
| ); | ||
| return false; | ||
| } | ||
| // Implement a basic validation logic or use a library | ||
| // For demonstration, we assume a simple check | ||
| return !xpath.includes("//") && !xpath.includes("'"); | ||
| return !xpath.includes('//') && !xpath.includes("'"); | ||
| } | ||
| } |
| private isValidXPath(xpath: string): boolean { | ||
| // Implement a basic validation logic or use a library | ||
| // For demonstration, we assume a simple check | ||
| return !xpath.includes("//") && !xpath.includes("'"); |
Check failure
Code scanning / CodeQL
Type confusion through parameter tampering Critical
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 15 days ago
General fix: enforce that all sanitizer logic (here, isValidXPath) and any downstream uses treat the user-controlled xpath parameter as a string at runtime, rejecting or normalizing array values. That means adding explicit runtime type checks (or coercions) before calling string methods such as .includes, and ensuring the controller does not pass anything but a proper string into the service.
Best concrete fix with minimal behavior change:
- In
PartnersController.queryPartnersRaw, add a type check on thexpathquery parameter to ensure it is a string; if it is an array or anything else, fail fast with a400 Bad Request. This prevents array values from ever reaching the service. - In
PartnersService.isValidXPath, add atypeofcheck to only operate on strings and explicitly reject non-string inputs. This directly addresses the CodeQL warning on line 96, and hardens the service even if other callers are added later. - Optionally (but still minimal), refine the parameter type of
isValidXPathtostring | string[]in TypeScript if desired; however, the safer and simpler approach here is to keep it asstringand add a runtime guard. Since CodeQL’s complaint is about actual runtime behavior, the essential part is the runtime guard.
Concretely:
- In
src/partners/partners.controller.ts, insidequeryPartnersRaw, before logging and validation, checktypeof xpath !== 'string'and throw anHttpExceptionwithHttpStatus.BAD_REQUESTif that’s the case. - In
src/partners/partners.service.ts, inisValidXPath, first checkif (typeof xpath !== 'string') { return false; }(or throw), then perform the.includeschecks. This ensures.includesis never called on an array or other non-string value.
No new methods or imports are required beyond the existing ones.
| @@ -91,8 +91,12 @@ | ||
|
|
||
| // Basic validation for XPath expressions | ||
| private isValidXPath(xpath: string): boolean { | ||
| // Ensure we are only operating on string inputs to avoid type confusion | ||
| if (typeof xpath !== 'string') { | ||
| return false; | ||
| } | ||
| // Implement a basic validation logic or use a library | ||
| // For demonstration, we assume a simple check | ||
| return !xpath.includes("//") && !xpath.includes("'"); | ||
| return !xpath.includes('//') && !xpath.includes("'"); | ||
| } | ||
| } |
| @@ -43,6 +43,13 @@ | ||
| type: String | ||
| }) | ||
| async queryPartnersRaw(@Query('xpath') xpath: string): Promise<string> { | ||
| // Ensure the xpath parameter is a single string value, not an array or other type | ||
| if (typeof xpath !== 'string') { | ||
| throw new HttpException( | ||
| 'Invalid XPath parameter type', | ||
| HttpStatus.BAD_REQUEST | ||
| ); | ||
| } | ||
| this.logger.debug(`Getting partners with xpath expression "${xpath}"`); | ||
|
|
||
| try { |
Note
Fixed 13 of 15 vulnerabilities.
Please review the fixes before merging.
POST /graphqlGET /api/partners/partnerLoginGET /api/partners/searchPartnersPOST /api/renderGET /api/file/azureGET /api/file/googleGET /api/file/digital_oceanGET /api/file/awsGET /api/users/id/1GET /api/secretsPOST /graphqlGET /graphqlPOST /graphqlGET /api/products/latestWorkflow execution details