diff --git a/src/common/security/redact-sensitive.ts b/src/common/security/redact-sensitive.ts index 22b25d8..b035462 100644 --- a/src/common/security/redact-sensitive.ts +++ b/src/common/security/redact-sensitive.ts @@ -4,6 +4,9 @@ const INLINE_SECRET_PATTERN = /\b(token|secret|password|passwd|authorization|api[-_]?key|webhook|cookie|credential|private[-_]?key)=([^\s&]+)/gi; const INLINE_URL_WITH_SECRET_PATTERN = /https?:\/\/[^\s]*(?:token|secret|password|passwd|api[-_]?key|webhook|key=)[^\s]*/gi; +const INLINE_BEARER_PATTERN = /\bBearer\s+[A-Za-z0-9._~+/=-]{16,}/gi; +const INLINE_JWT_PATTERN = /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g; +const INLINE_LONG_SECRET_PATTERN = /\b[A-Za-z0-9_+=-]{48,}\b/g; export function redactSensitive(value: T): T { return redactValue(value) as T; @@ -26,7 +29,10 @@ function redactValue(value: unknown): unknown { if (typeof value === 'string') { const redacted = value .replace(INLINE_URL_WITH_SECRET_PATTERN, '[REDACTED_URL]') - .replace(INLINE_SECRET_PATTERN, '$1=[REDACTED]'); + .replace(INLINE_SECRET_PATTERN, '$1=[REDACTED]') + .replace(INLINE_BEARER_PATTERN, 'Bearer [REDACTED]') + .replace(INLINE_JWT_PATTERN, '[REDACTED]') + .replace(INLINE_LONG_SECRET_PATTERN, '[REDACTED]'); if (redacted !== value) { return redacted; diff --git a/src/deploy-runs/deploy-runs.service.ts b/src/deploy-runs/deploy-runs.service.ts index 13faff0..5fe8dfb 100644 --- a/src/deploy-runs/deploy-runs.service.ts +++ b/src/deploy-runs/deploy-runs.service.ts @@ -24,6 +24,16 @@ import { DeployRunSummary, } from './deploy-run.types'; +const escapeControl = String.fromCharCode(27); +const jenkinsConsoleNotePattern = new RegExp( + `${escapeControl}\\[8mha:/{4}[A-Za-z0-9+/=]+${escapeControl}\\[0m`, + 'g', +); +const ansiEscapePattern = new RegExp( + `${escapeControl}\\[[0-?]*[ -/]*[@-~]`, + 'g', +); + @Injectable() export class DeployRunsService { constructor( @@ -265,7 +275,9 @@ export class DeployRunsService { run.jenkinsBuildNumber, normalizedStart, ); - const text = redactSensitive({ text: log.text }).text; + const text = redactSensitive({ + text: this.cleanJenkinsLogText(log.text), + }).text; await this.auditService.record({ action: 'JENKINS_LOG_VIEWED', @@ -390,6 +402,12 @@ export class DeployRunsService { return value; } + private cleanJenkinsLogText(text: string): string { + return text + .replace(jenkinsConsoleNotePattern, '') + .replace(ansiEscapePattern, ''); + } + private async cancelJenkinsExecution( run: DeployRunSummary, project: ProjectSummary,