fix(security): address CodeQL security alerts

- Fix incomplete HTML sanitization in errors.ts (alerts #50-52)
  Apply regex repeatedly to handle nested inputs like <scri<script>pt>
- Add lgtm comment for intentional cert bypass in jira.ts (alert #40)
- Fix incomplete string escaping in load-env.js (alert #39)
  Escape backslashes before quotes
- Fix shell command injection in check-file.js (alerts #37-38)
  Use execFileSync with args array instead of string interpolation
This commit is contained in:
Johannes Millan 2025-12-23 13:42:57 +01:00
parent 50de4c1fd7
commit c4023b4f45
4 changed files with 21 additions and 11 deletions

View file

@ -23,11 +23,13 @@ export const sendJiraRequest = ({
// log('--------------------------------------------------------------------');
fetch(url, {
...requestInit,
// allow self signed certificates
// Allow self-signed certificates for self-hosted Jira instances.
// This is an intentional user-configurable setting (isAllowSelfSignedCertificate).
// CodeQL alert js/disabling-certificate-validation is expected here.
...(jiraCfg && jiraCfg.isAllowSelfSignedCertificate
? {
agent: new Agent({
rejectUnauthorized: false,
rejectUnauthorized: false, // lgtm[js/disabling-certificate-validation]
}),
}
: {}),

View file

@ -143,9 +143,17 @@ export class HttpNotOkAPIError extends AdditionalLogErrorBase {
}
// Strip script and style tags with their content
const cleanBody = body
.replace(/<script\b[^>]*>([\s\S]*?)<\/script>/gim, '')
.replace(/<style\b[^>]*>([\s\S]*?)<\/style>/gim, '');
// Apply repeatedly to handle nested/crafted inputs like <scri<script>pt>
let cleanBody = body;
let previousBody: string;
do {
previousBody = cleanBody;
cleanBody = cleanBody
.replace(/<script\b[^>]*>[\s\S]*?<\/script\s*>/gim, '')
.replace(/<style\b[^>]*>[\s\S]*?<\/style\s*>/gim, '')
.replace(/<script\b/gim, '')
.replace(/<style\b/gim, '');
} while (cleanBody !== previousBody);
// Strip HTML tags for plain text
const withoutTags = cleanBody

View file

@ -1,5 +1,5 @@
#!/usr/bin/env node
const { execSync } = require('child_process');
const { execFileSync } = require('child_process');
const path = require('path');
const file = process.argv[2];
@ -14,7 +14,7 @@ const absolutePath = path.resolve(file);
try {
// Run prettier
console.log(`🎨 Formatting ${path.basename(file)}...`);
execSync(`npm run prettier:file ${absolutePath}`, {
execFileSync('npm', ['run', 'prettier:file', '--', absolutePath], {
stdio: 'pipe',
encoding: 'utf8',
});
@ -24,13 +24,13 @@ try {
if (file.endsWith('.scss')) {
// Use stylelint for SCSS files
execSync(`npx stylelint ${absolutePath}`, {
execFileSync('npx', ['stylelint', absolutePath], {
stdio: 'pipe',
encoding: 'utf8',
});
} else {
// Use ng lint for TypeScript/JavaScript files
const lintOutput = execSync(`npm run lint:file ${absolutePath}`, {
execFileSync('npm', ['run', 'lint:file', '--', absolutePath], {
stdio: 'pipe',
encoding: 'utf8',
});

View file

@ -55,8 +55,8 @@ const envEntries = Array.from(allEnvKeys)
.map((key) => {
const value = env[key];
if (value !== undefined) {
// Escape quotes in values
const escapedValue = value.replace(/'/g, "\\'");
// Escape backslashes first, then quotes
const escapedValue = value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
return ` ${key}: '${escapedValue}',`;
} else if (REQUIRED_ENV_KEYS.includes(key)) {
throw new Error(`Required env key ${key} not found`);