Skip to content

Conversation

@bigfluffycookie
Copy link
Contributor

@bigfluffycookie bigfluffycookie commented Nov 26, 2025

Fixes OPS-3082

Summary by CodeRabbit

  • New Features

    • Logging now automatically redacts sensitive information (passwords, auth tokens, etc.) from all log outputs — including nested objects, stringified JSON, error messages and stack traces — while preserving existing truncation and numeric rounding behavior. Public APIs and signatures remain unchanged.
  • Tests

    • Added tests validating redaction across various data formats, nesting levels, circular structures, and error contexts.

✏️ Tip: You can customize this high-level summary in your review settings.

@linear
Copy link

linear bot commented Nov 26, 2025

@coderabbitai
Copy link

coderabbitai bot commented Nov 26, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a redaction subsystem to the log cleaner: new helpers detect sensitive keys and patterns, redact nested and circular objects, and redact sensitive substrings (including stringified JSON and error stacks); integrates redaction with existing truncation and stringify flows without changing exported signatures.

Changes

Cohort / File(s) Summary
Core redaction & log cleaning
packages/server/shared/src/lib/logger/log-cleaner.ts
Introduces REDACTED marker, SENSITIVE_PATTERNS/SENSITIVE_FIELD_PATTERNS, and helpers isSensitiveField, redactSensitiveFields (recursive, circular-ref safe), and redactSensitiveDataInString (regex-based). Applies redaction to logEvent.message, event data, error stacks/messages, params, and context; retains existing exported function signatures.
Tests for redaction
packages/server/shared/test/log-cleaner.test.ts
Adds tests covering redaction of password and authorization across direct fields, nested objects, and stringified JSON bodies; updates expectations for error-related stringifications to exact JSON outputs reflecting redaction.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Caller as Caller
    participant Cleaner as cleanLogEvent()
    participant Fields as redactSensitiveFields()
    participant StringRedact as redactSensitiveDataInString()
    participant Output as Cleaned Log

    Caller->>Cleaner: submit raw logEvent
    Cleaner->>Cleaner: inspect message, event data, error, params, context
    alt key matches sensitive pattern
        Cleaner->>Output: replace value with "REDACTED"
    else value is object
        Cleaner->>Fields: recursively redact sensitive keys (handles cycles)
        Fields-->>Cleaner: return redacted object
        Cleaner->>Cleaner: stringify & truncate redacted object
    else value is string
        Cleaner->>StringRedact: redact sensitive substrings (incl. JSON/stacks)
        StringRedact-->>Cleaner: return masked string
        Cleaner->>Cleaner: truncate masked string if needed
    end
    Cleaner->>Output: emit cleaned, redacted logEvent
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay attention to:
    • Recursive redaction and circular-reference handling in redactSensitiveFields
    • Regex coverage and edge cases in redactSensitiveDataInString
    • Interaction of redaction with truncation/stringify for messages, error stacks, and nested/stringified contexts
    • Updated tests that assert exact redacted JSON outputs

Poem

🐰 I hop through logs with nimble feet,
Swapping secrets for a REDACTED beat.
Passwords hidden, tokens tucked away,
Nested and stringified — I tidy each display.
A little rabbit keeping secrets safe today.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete. While it references the issue (OPS-3082), it lacks required sections: no explanation of why changes were made, no testing checklist completion, and no information about backwards compatibility or migration scripts. Expand the description to include: context for why redaction was needed, which testing scenarios were covered, and explicit confirmation that changes are backwards compatible. Reference the issue to provide full context.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Redact sensitive information' directly aligns with the main change: implementing comprehensive redaction of sensitive data (passwords, authorization tokens, etc.) in the logging system across both message strings and object fields.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch lb/pw

Comment @coderabbitai help to get the list of available commands and usage tips.


const REDACTED = '[REDACTED]';

const SENSITIVE_FIELDS = [
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk i asked the ai to come up with a list, maybe its a bit much

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should redact any key that includes the word "password" so you don't need all the various variations

@bigfluffycookie bigfluffycookie marked this pull request as ready for review November 26, 2025 11:11
Copilot AI review requested due to automatic review settings November 26, 2025 11:11
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements sensitive data redaction in logging to prevent sensitive information like passwords, tokens, and authorization headers from being written to logs.

  • Adds redaction logic for sensitive fields in log events, error contexts, and stringified JSON
  • Introduces a list of sensitive field names that should be automatically redacted
  • Applies redaction throughout the logging pipeline including messages, error stacks, and nested objects

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
packages/server/shared/src/lib/logger/log-cleaner.ts Implements core redaction logic with sensitive field detection, object traversal, and string pattern matching
packages/server/shared/test/log-cleaner.test.ts Adds comprehensive test coverage for password, authorization, and nested object redaction scenarios

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@greptile-apps
Copy link

greptile-apps bot commented Nov 26, 2025

Greptile Overview

Greptile Summary

Added comprehensive sensitive data redaction to the log cleaner, preventing credentials and secrets from appearing in logs.

  • Implemented recursive redaction for 15 sensitive field types (passwords, tokens, API keys, etc.) across objects, strings, and error contexts
  • Added string-based redaction to catch JSON-formatted sensitive data in log messages
  • Applied redaction consistently to error stacks, messages, and ApplicationError params
  • Added test coverage for password, authorization, and nested object redaction scenarios

Confidence Score: 4/5

  • This PR is safe to merge with low risk - adds important security hardening
  • Score reflects solid implementation with comprehensive field coverage and test coverage. Minor limitation in string redaction regex that only handles double-quoted JSON patterns, but this covers the primary use case
  • No files require special attention

Important Files Changed

File Analysis

Filename Score Overview
packages/server/shared/src/lib/logger/log-cleaner.ts 4/5 Added comprehensive redaction for 15 sensitive field types across objects, strings, and error contexts with recursive traversal
packages/server/shared/test/log-cleaner.test.ts 5/5 Added 4 test cases covering password redaction in objects, nested objects, stringified JSON, and authorization headers

Sequence Diagram

sequenceDiagram
    participant App as Application Code
    participant Logger as Pino Logger
    participant Cleaner as cleanLogEvent
    participant Redact as Redaction Functions
    participant Output as Log Output

    App->>Logger: logger.info(message, event)
    Logger->>Cleaner: cleanLogEvent({message, event})
    
    alt message exists
        Cleaner->>Redact: redactSensitiveDataInString(message)
        Redact-->>Cleaner: redacted message
    end
    
    loop for each event field
        alt field is sensitive (password, token, etc)
            Cleaner->>Cleaner: Replace with [REDACTED]
        else field is Error
            Cleaner->>Redact: redactSensitiveDataInString(stack/message)
            Redact-->>Cleaner: redacted error data
        else field is object
            Cleaner->>Redact: redactSensitiveFields(object)
            Redact->>Redact: Recursively traverse object
            Redact-->>Cleaner: redacted object (stringified)
        else field is string
            Cleaner->>Redact: redactSensitiveDataInString(value)
            Redact-->>Cleaner: redacted string
        end
    end
    
    Cleaner-->>Logger: cleaned logEvent
    Logger->>Output: Write to console/Logz.io
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/server/shared/src/lib/logger/log-cleaner.ts (2)

64-69: Use replaceAll and address regex construction.

Per static analysis hints, prefer String#replaceAll() over String#replace() with a global regex. Since the regex pattern is constructed from a known constant list (not user input), the ReDoS warning is a false positive here, but using replaceAll improves clarity.

   SENSITIVE_FIELDS.forEach((field) => {
-    result = result.replace(
-      new RegExp(`"${field}"\\s*:\\s*"[^"]*"`, 'gi'),
+    result = result.replaceAll(
+      new RegExp(String.raw`"${field}"\s*:\s*"[^"]*"`, 'gi'),
       `"${field}":"${REDACTED}"`,
     );
   });

105-106: Use optional chaining for cleaner conditional.

Per static analysis, prefer optional chaining.

-    } else if (key === 'res' && value && value.raw) {
+    } else if (key === 'res' && value?.raw) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba42672 and eb9101c.

📒 Files selected for processing (2)
  • packages/server/shared/src/lib/logger/log-cleaner.ts (4 hunks)
  • packages/server/shared/test/log-cleaner.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{ts,tsx,js,jsx}: Indentation: 2 spaces for TypeScript/JavaScript
Braces required for all control blocks, even single-line
One space between keywords and parentheses: if (condition) {
Use camelCase for variables and functions
Use PascalCase for classes and types
Use UPPER_SNAKE_CASE for constants
Use lowercase with hyphens for file names (e.g., user-profile.ts)
Prefer const over let or var in TypeScript/JavaScript
Prefer arrow functions for callbacks and functional components in TypeScript/JavaScript

Files:

  • packages/server/shared/test/log-cleaner.test.ts
  • packages/server/shared/src/lib/logger/log-cleaner.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{ts,tsx}: Use types and interfaces where appropriate in TypeScript
Use explicit return types for exported functions in TypeScript

Files:

  • packages/server/shared/test/log-cleaner.test.ts
  • packages/server/shared/src/lib/logger/log-cleaner.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{tsx,ts}: Frontend tech stack must strictly use: React 18, Zustand, react-query v5, shadcn, and Axios with qs package for query strings
Follow best practices for React hooks
Prefer small, composable components and extract helper functions where possible
Use cn utility to group tailwind classnames in React components

Files:

  • packages/server/shared/test/log-cleaner.test.ts
  • packages/server/shared/src/lib/logger/log-cleaner.ts
🧬 Code graph analysis (1)
packages/server/shared/src/lib/logger/log-cleaner.ts (3)
packages/server/shared/src/lib/logger/index.ts (1)
  • truncate (112-112)
tools/scripts/publish.mjs (1)
  • name (25-25)
packages/shared/src/lib/common/application-error.ts (1)
  • ApplicationError (10-14)
🪛 ast-grep (0.40.0)
packages/server/shared/src/lib/logger/log-cleaner.ts

[warning] 65-65: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp("${field}"\\s*:\\s*"[^"]*", 'gi')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🪛 GitHub Actions: CI
packages/server/shared/src/lib/logger/log-cleaner.ts

[error] 31-31: Maximum call stack size exceeded in redactSensitiveFields (likely due to circular references) during tests.

🪛 GitHub Check: SonarCloud Code Analysis
packages/server/shared/src/lib/logger/log-cleaner.ts

[warning] 65-65: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=openops-cloud_openops&issues=AZq_2JTYzkE3kbhWKoTE&open=AZq_2JTYzkE3kbhWKoTE&pullRequest=1681


[warning] 66-66: String.raw should be used to avoid escaping \.

See more on https://sonarcloud.io/project/issues?id=openops-cloud_openops&issues=AZq_2JTYzkE3kbhWKoTF&open=AZq_2JTYzkE3kbhWKoTF&pullRequest=1681


[warning] 105-105: Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=openops-cloud_openops&issues=AZq_2JTYzkE3kbhWKoTG&open=AZq_2JTYzkE3kbhWKoTG&pullRequest=1681

🔇 Additional comments (4)
packages/server/shared/src/lib/logger/log-cleaner.ts (3)

7-25: LGTM on sensitive fields list and constant.

The REDACTED marker and SENSITIVE_FIELDS list provide good coverage for common authentication and session-related fields.


82-122: Redaction integration in cleanLogEvent looks correct.

The flow properly handles sensitive field detection at the top-level, applies recursive redaction for objects before stringify, and redacts sensitive data in strings after truncation. The order of operations (truncate → redact for strings, redact → stringify → truncate for objects) is appropriate.


141-167: Redaction in extractErrorFields is properly applied.

Error stacks, messages, params, and context are all passed through the appropriate redaction functions. This ensures sensitive data doesn't leak through error objects.

packages/server/shared/test/log-cleaner.test.ts (1)

140-199: Good test coverage for basic redaction scenarios.

The tests appropriately verify redaction of sensitive fields in various contexts: direct fields, nested objects, and stringified JSON.

Consider adding tests for:

  1. Circular references - to verify the fix prevents stack overflow:
it('should handle circular references in objects with sensitive fields', () => {
  const circularObj: any = { password: 'secret', data: {} };
  circularObj.data.self = circularObj;
  
  const logEvent = { event: { request: circularObj } };
  
  expect(() => cleanLogEvent(logEvent)).not.toThrow();
  expect(result.event.request).toContain('[REDACTED]');
});
  1. Case-insensitive field matching - e.g., PASSWORD, Authorization:
it('should redact fields case-insensitively', () => {
  const logEvent = {
    event: {
      PASSWORD: 'secret',
      Authorization: 'Bearer token',
    },
  };
  
  const result = cleanLogEvent(logEvent);
  
  expect(result.event.PASSWORD).toBe('[REDACTED]');
  expect(result.event.Authorization).toBe('[REDACTED]');
});

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
packages/server/shared/src/lib/logger/log-cleaner.ts (2)

27-29: Fix case-sensitivity bug that breaks redaction.

This is a critical bug that has been flagged multiple times: key.toLowerCase() converts the key to lowercase, but SENSITIVE_FIELDS contains camelCase entries like newPassword, accessToken, etc. The includes() check will never match, so sensitive fields won't be redacted.

Apply this diff to fix:

 const isSensitiveField = (key: string): boolean => {
-  return SENSITIVE_FIELDS.includes(key.toLowerCase());
+  return SENSITIVE_FIELDS.some(
+    (field) => field.toLowerCase() === key.toLowerCase(),
+  );
 };

70-77: Regex pattern only redacts double-quoted JSON values.

The current regex "${field}"\\s*:\\s*"[^"]*" only matches JSON-like patterns with double quotes. It will miss:

  • URL parameters: password=secret123&token=abc
  • Single-quoted values
  • Other non-JSON formats

This creates a security gap where sensitive data in these formats won't be redacted from logs.

Consider either:

  1. Adding additional patterns for URL parameters and other common formats
  2. Documenting this limitation clearly
  3. If comprehensive string redaction isn't feasible, focus on object-level redaction (which is already working)

Additionally, apply these code style improvements:

   let result = value;
   SENSITIVE_FIELDS.forEach((field) => {
-    result = result.replace(
-      new RegExp(`"${field}"\\s*:\\s*"[^"]*"`, 'gi'),
+    result = result.replaceAll(
+      new RegExp(String.raw`"${field}"\s*:\s*"[^"]*"`, 'gi'),
       `"${field}":"${REDACTED}"`,
     );
   });

Based on static analysis hints: replaceAll is more explicit for global replacements, and String.raw avoids manual escaping.

🧹 Nitpick comments (2)
packages/server/shared/src/lib/logger/log-cleaner.ts (2)

88-88: Add explicit return type to exported function.

Per coding guidelines, exported functions should have explicit return types.

Apply this diff:

-export const cleanLogEvent = (logEvent: any) => {
+export const cleanLogEvent = (logEvent: any): any => {

As per coding guidelines: "Use explicit return types for exported functions in TypeScript"


111-111: Consider using optional chain for cleaner null checking.

Apply this diff:

-    } else if (key === 'res' && value && value.raw) {
+    } else if (key === 'res' && value?.raw) {

Based on static analysis hints: Optional chain is more concise and idiomatic.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb9101c and b3df934.

📒 Files selected for processing (1)
  • packages/server/shared/src/lib/logger/log-cleaner.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{ts,tsx,js,jsx}: Indentation: 2 spaces for TypeScript/JavaScript
Braces required for all control blocks, even single-line
One space between keywords and parentheses: if (condition) {
Use camelCase for variables and functions
Use PascalCase for classes and types
Use UPPER_SNAKE_CASE for constants
Use lowercase with hyphens for file names (e.g., user-profile.ts)
Prefer const over let or var in TypeScript/JavaScript
Prefer arrow functions for callbacks and functional components in TypeScript/JavaScript

Files:

  • packages/server/shared/src/lib/logger/log-cleaner.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{ts,tsx}: Use types and interfaces where appropriate in TypeScript
Use explicit return types for exported functions in TypeScript

Files:

  • packages/server/shared/src/lib/logger/log-cleaner.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{tsx,ts}: Frontend tech stack must strictly use: React 18, Zustand, react-query v5, shadcn, and Axios with qs package for query strings
Follow best practices for React hooks
Prefer small, composable components and extract helper functions where possible
Use cn utility to group tailwind classnames in React components

Files:

  • packages/server/shared/src/lib/logger/log-cleaner.ts
🧬 Code graph analysis (1)
packages/server/shared/src/lib/logger/log-cleaner.ts (1)
packages/shared/src/lib/common/application-error.ts (1)
  • ApplicationError (10-14)
🪛 ast-grep (0.40.0)
packages/server/shared/src/lib/logger/log-cleaner.ts

[warning] 71-71: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp("${field}"\\s*:\\s*"[^"]*", 'gi')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🪛 GitHub Check: SonarCloud Code Analysis
packages/server/shared/src/lib/logger/log-cleaner.ts

[warning] 72-72: String.raw should be used to avoid escaping \.

See more on https://sonarcloud.io/project/issues?id=openops-cloud_openops&issues=AZq_2JTYzkE3kbhWKoTF&open=AZq_2JTYzkE3kbhWKoTF&pullRequest=1681


[warning] 71-71: Prefer String#replaceAll() over String#replace().

See more on https://sonarcloud.io/project/issues?id=openops-cloud_openops&issues=AZq_2JTYzkE3kbhWKoTE&open=AZq_2JTYzkE3kbhWKoTE&pullRequest=1681


[warning] 111-111: Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=openops-cloud_openops&issues=AZq_2JTYzkE3kbhWKoTG&open=AZq_2JTYzkE3kbhWKoTG&pullRequest=1681

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test Engine and Libraries
  • GitHub Check: Test Blocks Shard 3
  • GitHub Check: Test Blocks Shard 2
  • GitHub Check: Test Blocks Shard 1
  • GitHub Check: Test Server API
  • GitHub Check: Build Project
🔇 Additional comments (5)
packages/server/shared/src/lib/logger/log-cleaner.ts (5)

7-25: LGTM: Comprehensive list of sensitive fields.

The SENSITIVE_FIELDS list covers common authentication and authorization fields appropriately. While the author noted it might be "a bit much," this comprehensive approach is preferable for security.


31-61: Well done: Circular reference handling implemented correctly.

The addition of WeakSet tracking and the '[Circular]' placeholder properly prevents stack overflow on circular references. The logic correctly checks for visited objects before recursing and passes the visited set through all recursive calls.


90-90: LGTM: Message redaction applied correctly.

The chaining of truncate then redactSensitiveDataInString correctly processes the log message (subject to the limitations of the regex pattern flagged separately).


117-121: LGTM: Object and string redaction integrated correctly.

The logic correctly applies:

  • For objects: redactSensitiveFields before stringification to handle nested sensitive keys
  • For strings: truncate then redactSensitiveDataInString to handle embedded sensitive data

The integration of redaction with existing truncation logic is sound.


155-172: LGTM: Error data properly redacted across all fields.

The function correctly applies redaction to stack, message, params, and context.

Regarding Line 170: The removed null check for context is safe because destructuring with the rest operator (...context) always produces an object, so Object.keys(context) won't throw. The previous defensive check was redundant.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/server/shared/src/lib/logger/log-cleaner.ts (3)

27-29: Consider using String.raw for clearer regex escaping.

Per static analysis, using String.raw improves readability by avoiding double-escaping.

 const SENSITIVE_FIELD_PATTERNS = SENSITIVE_FIELDS.map(
-  (field) => new RegExp(`"${field}"\\s*:\\s*"[^"]*"`, 'gi'),
+  (field) => new RegExp(String.raw`"${field}"\s*:\s*"[^"]*"`, 'gi'),
 );

117-117: Use optional chaining for cleaner code.

Per static analysis, optional chaining is more concise.

-    } else if (key === 'res' && value && value.raw) {
+    } else if (key === 'res' && value?.raw) {

181-183: Consider wrapping JSON.stringify for resilience.

JSON.stringify can throw on BigInt values or if a toJSON method throws. While unlikely in typical log data, a try/catch could prevent logging failures from crashing the application.

 function stringify(value: any) {
-  return truncate(JSON.stringify(value));
+  try {
+    return truncate(JSON.stringify(value));
+  } catch {
+    return '[Unserializable]';
+  }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3df934 and 050de03.

📒 Files selected for processing (2)
  • packages/server/shared/src/lib/logger/log-cleaner.ts (4 hunks)
  • packages/server/shared/test/log-cleaner.test.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/server/shared/test/log-cleaner.test.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{ts,tsx,js,jsx}: Indentation: 2 spaces for TypeScript/JavaScript
Braces required for all control blocks, even single-line
One space between keywords and parentheses: if (condition) {
Use camelCase for variables and functions
Use PascalCase for classes and types
Use UPPER_SNAKE_CASE for constants
Use lowercase with hyphens for file names (e.g., user-profile.ts)
Prefer const over let or var in TypeScript/JavaScript
Prefer arrow functions for callbacks and functional components in TypeScript/JavaScript

Files:

  • packages/server/shared/src/lib/logger/log-cleaner.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{ts,tsx}: Use types and interfaces where appropriate in TypeScript
Use explicit return types for exported functions in TypeScript

Files:

  • packages/server/shared/src/lib/logger/log-cleaner.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{tsx,ts}: Frontend tech stack must strictly use: React 18, Zustand, react-query v5, shadcn, and Axios with qs package for query strings
Follow best practices for React hooks
Prefer small, composable components and extract helper functions where possible
Use cn utility to group tailwind classnames in React components

Files:

  • packages/server/shared/src/lib/logger/log-cleaner.ts
🪛 ast-grep (0.40.0)
packages/server/shared/src/lib/logger/log-cleaner.ts

[warning] 27-27: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp("${field}"\\s*:\\s*"[^"]*", 'gi')
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🪛 GitHub Check: SonarCloud Code Analysis
packages/server/shared/src/lib/logger/log-cleaner.ts

[warning] 28-28: String.raw should be used to avoid escaping \.

See more on https://sonarcloud.io/project/issues?id=openops-cloud_openops&issues=AZrAHmbg-hjJbXVsvE46&open=AZrAHmbg-hjJbXVsvE46&pullRequest=1681


[warning] 117-117: Prefer using an optional chain expression instead, as it's more concise and easier to read.

See more on https://sonarcloud.io/project/issues?id=openops-cloud_openops&issues=AZq_2JTYzkE3kbhWKoTG&open=AZq_2JTYzkE3kbhWKoTG&pullRequest=1681

🔇 Additional comments (3)
packages/server/shared/src/lib/logger/log-cleaner.ts (3)

31-35: Case-insensitivity fix looks correct.

The implementation now properly handles case-insensitive matching. Minor optimization opportunity: pre-compute a lowercase Set at module load for O(1) lookups, though with only 16 fields this is negligible.


37-67: Circular reference handling is correctly implemented.

The WeakSet approach properly prevents stack overflow. One consideration: string values within objects aren't passed through redactSensitiveDataInString, so stringified JSON embedded in object fields won't have in-string sensitive patterns redacted. This may be acceptable if such strings are rare in your log data.


153-179: Error field extraction with redaction looks correct.

The destructuring ...context guarantees context is always an object (possibly empty {}), so the null check isn't needed at line 176. The redaction is consistently applied to stack, message, params, and context.

);
};

const redactSensitiveFields = (obj: any, visited = new WeakSet()): any => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

never heard of weakset, apparently its more memory efficient? maybe ai comments arent so bad after all

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no idea but can you make sure this function can never throw an error? we simply can't allow the logger to fail. If there's an error then the field should just show the error message.


const REDACTED = '[REDACTED]';

const SENSITIVE_FIELDS = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should redact any key that includes the word "password" so you don't need all the various variations

Comment on lines +125 to 129
} else if (typeof value === 'string') {
eventData[key] = redactSensitiveDataInString(truncate(value));
} else {
eventData[key] = truncate(value);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

string is pretty much the only type that is left anyway

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey you never know

);
};

const redactSensitiveFields = (obj: any, visited = new WeakSet()): any => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no idea but can you make sure this function can never throw an error? we simply can't allow the logger to fail. If there's an error then the field should just show the error message.

@sonarqubecloud
Copy link

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/server/shared/src/lib/logger/log-cleaner.ts (1)

67-82: Consider combining patterns into a single regex for efficiency.

The current implementation iterates and creates intermediate strings for each pattern. While functionally correct, a single combined regex would be more efficient for strings with many patterns to check.

 const redactSensitiveDataInString = (
   value: string | undefined,
 ): string | undefined => {
   if (!value) {
     return value;
   }
-  let result = value;
-  SENSITIVE_FIELD_PATTERNS.forEach((pattern) => {
-    result = result.replace(pattern, (match) => {
-      const colonIndex = match.indexOf(':');
-      const keyPart = match.substring(0, colonIndex);
-      return `${keyPart}:"${REDACTED}"`;
-    });
-  });
-  return result;
+  const combinedPattern = new RegExp(
+    `"[^"]*(${SENSITIVE_PATTERNS.join('|')})[^"]*"\\s*:\\s*"[^"]*"`,
+    'gi',
+  );
+  return value.replace(combinedPattern, (match) => {
+    const colonIndex = match.indexOf(':');
+    const keyPart = match.substring(0, colonIndex);
+    return `${keyPart}:"${REDACTED}"`;
+  });
 };

Alternatively, move the combined regex to module scope to avoid recompilation on each call.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3891758 and 21772ca.

📒 Files selected for processing (1)
  • packages/server/shared/src/lib/logger/log-cleaner.ts (4 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{ts,tsx,js,jsx}: Indentation: 2 spaces for TypeScript/JavaScript
Braces required for all control blocks, even single-line
One space between keywords and parentheses: if (condition) {
Use camelCase for variables and functions
Use PascalCase for classes and types
Use UPPER_SNAKE_CASE for constants
Use lowercase with hyphens for file names (e.g., user-profile.ts)
Prefer const over let or var in TypeScript/JavaScript
Prefer arrow functions for callbacks and functional components in TypeScript/JavaScript

Files:

  • packages/server/shared/src/lib/logger/log-cleaner.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{ts,tsx}: Use types and interfaces where appropriate in TypeScript
Use explicit return types for exported functions in TypeScript

Files:

  • packages/server/shared/src/lib/logger/log-cleaner.ts
**/*.{tsx,ts}

📄 CodeRabbit inference engine (.cursor/rules/coding-guidelines.mdc)

**/*.{tsx,ts}: Frontend tech stack must strictly use: React 18, Zustand, react-query v5, shadcn, and Axios with qs package for query strings
Follow best practices for React hooks
Prefer small, composable components and extract helper functions where possible
Use cn utility to group tailwind classnames in React components

Files:

  • packages/server/shared/src/lib/logger/log-cleaner.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test Server API
  • GitHub Check: Test Engine and Libraries
  • GitHub Check: Test Blocks Shard 2
  • GitHub Check: Build Project
  • GitHub Check: Test Blocks Shard 1
  • GitHub Check: Test Blocks Shard 3
🔇 Additional comments (7)
packages/server/shared/src/lib/logger/log-cleaner.ts (7)

7-19: LGTM!

Good foundational patterns. Using lowercase patterns with substring matching (via includes) provides flexible case-insensitive detection while keeping the list concise.


21-24: Regex targets JSON string values only.

The pattern correctly matches JSON-style "key": "value" pairs. Note it won't redact URL query parameters or numeric/boolean values with sensitive keys. This is acceptable for typical log contexts where sensitive data appears as string values.


26-29: LGTM!

The substring matching approach correctly handles variations like newPassword, API_TOKEN, and x-authorization-header. Past case-sensitivity concerns are resolved.


31-65: LGTM - robust implementation.

Correctly handles circular references with WeakSet, includes defensive try-catch to prevent logger failures, and processes nested structures recursively. The error fallback provides useful debugging info without compromising the logging flow.


114-128: LGTM - good integration of redaction into the processing pipeline.

The placement of isSensitiveField check before type-specific handling ensures sensitive keys are fully redacted regardless of value type. The order (truncate → redact for strings, redact → stringify for objects) is correct.


160-177: LGTM - comprehensive error redaction.

Stack traces and error messages can inadvertently contain sensitive data. Applying redactSensitiveDataInString to these fields and redactSensitiveFields to structured params/context ensures thorough sanitization.


180-186: LGTM!

The try-catch provides a safety net for edge cases. Since redactSensitiveFields now handles circular references upstream, JSON.stringify should rarely fail, but the defensive error handling is appropriate for logger code.

@bigfluffycookie bigfluffycookie merged commit bf5b658 into main Nov 28, 2025
24 checks passed
@bigfluffycookie bigfluffycookie deleted the lb/pw branch November 28, 2025 12:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants