Skip to content

Conversation

@JimmyVo16
Copy link
Contributor

@JimmyVo16 JimmyVo16 commented Oct 30, 2025

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-26401

📔 Objective

Add logging to both GET /public/events and GET /organizations/{id}/events to capture organizationId, startTime and endTime query parameters, total records retrieved, and other relevant fields to validate whether there is a discrepancy between the two endpoints.

  1. Add logging
  2. Add tests

📸 Screenshots

Sanity check

image

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

@JimmyVo16 JimmyVo16 self-assigned this Oct 30, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Oct 30, 2025

Logo
Checkmarx One – Scan Summary & Details7f03e51b-f14a-4aea-95e5-1229b44c5eda

Fixed Issues (1)

Great job! The following issues were fixed in this Pull Request

Severity Issue Source File / Package
MEDIUM CSRF /src/Api/Dirt/Controllers/OrganizationReportsController.cs: 173

@codecov
Copy link

codecov bot commented Oct 30, 2025

Codecov Report

❌ Patch coverage is 68.65672% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 51.99%. Comparing base (cfe818e) to head (3021caa).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
...Utilities/DiagnosticTools/EventDiagnosticLogger.cs 85.18% 8 Missing ⚠️
...c/Api/AdminConsole/Controllers/EventsController.cs 0.00% 7 Missing ⚠️
...dminConsole/Public/Controllers/EventsController.cs 0.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6523      +/-   ##
==========================================
+ Coverage   51.86%   51.99%   +0.12%     
==========================================
  Files        1902     1906       +4     
  Lines       84058    84273     +215     
  Branches     7501     7518      +17     
==========================================
+ Hits        43598    43818     +220     
+ Misses      38766    38751      -15     
- Partials     1694     1704      +10     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@JimmyVo16 JimmyVo16 marked this pull request as ready for review October 31, 2025 00:33
@JimmyVo16 JimmyVo16 requested a review from a team as a code owner October 31, 2025 00:33
@JimmyVo16 JimmyVo16 requested a review from r-tome October 31, 2025 00:33
@claude
Copy link
Contributor

claude bot commented Oct 31, 2025

Claude finished @JimmyVo16's task —— View job


PR Review Complete

I've completed a comprehensive review of the changes in this PR. Here are my findings:


Summary of Changes

This PR adds diagnostic logging to two event endpoints (GET /public/events and GET /organizations/{id}/events) to help diagnose potential discrepancies between them. The implementation includes:

  • New EventDiagnosticLogger utility class with two overloaded methods
  • Integration into both public and internal Events controllers
  • Feature flag gating (EventDiagnosticLogging)
  • Comprehensive unit tests (221 lines)

Critical Issues

1. Potential IP Address Logging (Security)

Severity: High

The EventResponseModel contains an IpAddress field, and the logger is calling .OrderBy(e => e.Date) on the entire event collection, which means the event objects themselves (containing IP addresses) are being held in memory and could potentially be logged if there's an exception.

Location:

  • EventDiagnosticLogger.cs:23 and EventDiagnosticLogger.cs:65

Concern:
While the structured logging only explicitly logs aggregate data (count, dates, filters), the fact that IP addresses are part of the event model raises concerns given Bitwarden's strict security requirements about not logging PII.

Recommendation:

  • Add explicit documentation/comments clarifying that only aggregate data is logged (no IP addresses, no event details)
  • Consider if the logging message template should explicitly state what is NOT logged for audit purposes

2. Inconsistent Method Naming

Severity: Medium

Both overloaded methods are named LogAggregateData, making it difficult to distinguish which one is being called without examining the parameters. This was also noted by reviewer @r-tome.

Location:

  • EventDiagnosticLogger.cs:10 and EventDiagnosticLogger.cs:48

Recommendation:

// Option 1: More descriptive names
LogPublicEventsAggregateData(...) // for public API version
LogInternalEventsAggregateData(...) // for internal version

// Option 2: Indicate the model type
LogAggregateDataForPublicResponse(...)
LogAggregateDataForInternalResponse(...)

3. Double Semicolon

Severity: Low

Location:

  • EventDiagnosticLogger.cs:26: var oldestRecordDate = orderedRecords.FirstOrDefault()?.Date.ToString("o"); ;
  • EventDiagnosticLogger.cs:68: var oldestRecordDate = orderedRecords.FirstOrDefault()?.Date.ToString("o"); ;

Recommendation:
Remove the extra semicolon.


Suggested Improvements

4. Potential Performance Issue with Ordering

Severity: Medium

Location:

  • EventDiagnosticLogger.cs:23 and EventDiagnosticLogger.cs:65

The code orders the entire result set by date just to get the oldest and newest records:

var orderedRecords = data.OrderBy(e => e.Date).ToList();
var recordCount = orderedRecords.Count;
var newestRecordDate = orderedRecords.LastOrDefault()?.Date.ToString("o");
var oldestRecordDate = orderedRecords.FirstOrDefault()?.Date.ToString("o");

Issue:

  • Creates an in-memory sorted copy of potentially large event collections
  • O(n log n) complexity when O(n) would suffice
  • The .ToList() materializes the entire sorted collection

Recommendation:

var recordCount = data.Count();
DateTime? newestRecordDate = null;
DateTime? oldestRecordDate = null;

if (data.Any())
{
    newestRecordDate = data.Max(e => e.Date);
    oldestRecordDate = data.Min(e => e.Date);
}

logger.LogInformation(
    "Events query for Organization:{OrgId}. Event count:{Count} newest record:{newestRecord} oldest record:{oldestRecord}...",
    organizationId,
    recordCount,
    newestRecordDate?.ToString("o"),
    oldestRecordDate?.ToString("o"),
    // ... rest
);

This approach:

  • Uses O(n) operations instead of O(n log n)
  • Avoids materializing a sorted copy
  • More efficiently finds min/max dates
5. Inconsistent Null Handling

Severity: Low

Location:

  • EventDiagnosticLogger.cs:25-26 and EventDiagnosticLogger.cs:67-68

The code safely handles empty collections with FirstOrDefault() and LastOrDefault() with null-conditional operators, but if the collection is empty, newestRecordDate and oldestRecordDate will both be null strings in the log output.

Recommendation:
This is actually fine for diagnostic logging, but consider if you want more explicit messages like "(no records)" instead of empty values.

6. Redundant Count Calculation

Severity: Low

Location:

  • EventDiagnosticLogger.cs:24

After calling .OrderBy(...).ToList(), you get the count from the list. If you don't optimize the ordering (per suggestion #4), you could at least avoid the extra enumeration by getting the count before ordering.

Current:

var orderedRecords = data.OrderBy(e => e.Date).ToList();
var recordCount = orderedRecords.Count;

Alternative (if keeping the sort):

var recordCount = data.Count(); // Get count first (from IEnumerable)
var orderedRecords = data.OrderBy(e => e.Date).ToList();

Though if you implement suggestion #4, this becomes moot.

7. Test Coverage Gaps

Severity: Low

Location:

  • EventDiagnosticLoggerTests.cs

The tests are comprehensive but could cover a few more edge cases:

Missing test scenarios:

  • Exception handling: Verify that exceptions in logging are caught and logged as warnings
  • Null data parameter handling (currently line 80 passes null for the response but only when feature flag is disabled)
  • Very large collections (stress test)
  • Events with null dates (if that's possible)

Recommendation:
Add tests for:

[Theory, BitAutoData]
public void LogAggregateData_WithPublicResponse_ExceptionThrown_LogsWarning(...)
{
    // Arrange: Mock logger.LogInformation to throw
    // Assert: Verify LogWarning was called
}

[Theory, BitAutoData]
public void LogAggregateData_WithPublicResponse_NullDataWithFlagEnabled_HandlesGracefully(...)
{
    // Arrange: null data with feature flag enabled
    // Assert: No exception, warning is logged
}
8. Logging Format Consistency

Severity: Low

Location:

  • EventDiagnosticLogger.cs:29-40 and EventDiagnosticLogger.cs:71-80

The log messages have inconsistent spacing:

  • "Event count:{Count} newest record:{newestRecord} oldest record:{oldestRecord}" (two spaces between newest and oldest)

Recommendation:
Use consistent single spaces throughout.

9. Lack of Documentation

Severity: Low

Location:

  • EventDiagnosticLogger.cs:8

The class and methods lack XML documentation comments explaining:

  • Purpose of the diagnostic logging
  • What data is logged (and critically, what is NOT logged)
  • When this should be used
  • Feature flag dependency

Recommendation:

/// <summary>
/// Diagnostic logger for event query operations to help identify discrepancies between endpoints.
/// Logs only aggregate metadata (counts, date ranges, filters) - no PII or event content.
/// Feature-flagged by <see cref="FeatureFlagKeys.EventDiagnosticLogging"/>.
/// </summary>
public static class EventDiagnosticLogger
{
    /// <summary>
    /// Logs aggregate diagnostic data for public API event queries.
    /// </summary>
    public static void LogAggregateData(...)

Good Practices Observed

  • Feature flag gating for easy enable/disable of diagnostic logging
  • Try-catch blocks to prevent logging failures from affecting endpoint functionality
  • Structured logging with proper parameter templates
  • Comprehensive unit tests covering multiple scenarios
  • Using ISO 8601 format ("o") for dates in logs

Test Coverage Analysis

Current Coverage: 68.66% (21 lines missing)

The codecov report shows missing coverage in:

  • EventDiagnosticLogger.cs: 8 missing lines
  • EventsController.cs (AdminConsole): 7 missing lines
  • EventsController.cs (Public): 6 missing lines

The missing lines in controllers are expected (integration-level logging calls), but the 8 missing lines in EventDiagnosticLogger.cs suggest the exception handling paths aren't fully tested (see suggestion #7).


Security Assessment

Overall: Safe, with minor concerns

  • ✅ No sensitive vault data is logged
  • ✅ Only aggregate counts and metadata are logged
  • ✅ Feature-flagged for production control
  • ✅ GUIDs (organizationId, actingUserId, itemId) are appropriate to log for diagnostics
  • ⚠️ Event objects contain IP addresses, but they're not explicitly logged (see critical issue Identity Server  #1)
  • ✅ Dates are logged in ISO format (no timezone leakage concerns)

Performance Assessment

Overall: Acceptable, with optimization opportunity

  • ⚠️ O(n log n) sorting operation on potentially large result sets (see suggestion Licence #4)
  • ✅ Logging is behind feature flag, so zero impact when disabled
  • ✅ Exception handling prevents logging from blocking the request
  • ✅ Minimal allocations besides the sorting operation

Action Items for Author

Must Fix (before merge):

  1. Remove double semicolons (critical issue .NET Core Runtime #3)
  2. Fix inconsistent spacing in log message format (suggestion Add a Gitter chat badge to README.md #8)

Should Fix (before or soon after merge):
3. Rename methods to be more descriptive (critical issue #2)
4. Add XML documentation comments (suggestion #9)
5. Consider performance optimization for min/max date finding (suggestion #4)

Nice to Have:
6. Add exception handling tests (suggestion #7)
7. Add explicit comment about IP addresses not being logged (critical issue #1)


Adherence to Bitwarden Standards

  • ✅ No code regions used
  • ✅ No sensitive data in logs
  • ✅ Unit tests added
  • ✅ Feature flag used for new functionality
  • ⚠️ File uses #nullable disable (inherited from controllers, acceptable for now)
  • ✅ Follows structured logging patterns

Overall Assessment: This is a solid implementation of diagnostic logging for the stated purpose. The code is well-tested and follows good practices. The critical issues are minor and easily addressed. The main optimization opportunity is around the sorting operation, which could impact performance with large event collections.


r-tome
r-tome previously approved these changes Oct 31, 2025
Copy link
Contributor

@r-tome r-tome left a comment

Choose a reason for hiding this comment

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

Looks good to me. My only suggestion would be to change the method names on EventDiagnosticLogger to be clearer on the difference between them.

r-tome
r-tome previously approved these changes Oct 31, 2025
@JimmyVo16 JimmyVo16 merged commit 21cc0b3 into main Oct 31, 2025
44 of 45 checks passed
@JimmyVo16 JimmyVo16 deleted the ac/pm-26401/add-logging branch October 31, 2025 18:47
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