Skip to content

Conversation

@matt-livefront
Copy link
Collaborator

@matt-livefront matt-livefront commented Nov 12, 2025

🎟️ Tracking

PM-26063

📔 Objective

Moves the flight recorder data, service, and UI into BitwardenKit so this can be shared with Authenticator. EnableFlightRecorderProcessor and FlightRecorderLogsProcessor previously used SettingsRoute to communicate navigation with their coordinators. As part of this move, I created a new FlightRecorderCoordinator with FlightRecorderRoutes to handle navigation between the flight recorder views and make this more of a component that can be shared with Authenticator.

⏰ 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

@matt-livefront matt-livefront requested a review from a team as a code owner November 12, 2025 21:17
@claude
Copy link

claude bot commented Nov 12, 2025

Claude finished @matt-livefront's task —— View job


Code Review Complete

This PR successfully moves the FlightRecorder functionality from BitwardenShared into BitwardenKit, enabling it to be shared with the Authenticator app. The architectural changes are sound and follow established patterns.


Summary of Changes Since Last Review

The author has addressed previous review feedback:

  1. Force unwrap fixed (29c2b0a5b): FileManager.flightRecorderLogURL() now returns optional URL and properly handles nil with FlightRecorderError.containerURLUnavailable
  2. FlightRecorderStateService inheritance (afad21fe6): Now properly inherits from ActiveAccountStateProvider as requested

Key Architectural Changes

Module structure (click to expand)
  • Created new FlightRecorderModule protocol with coordinator factory method
  • FlightRecorderCoordinator now manages its own routes (FlightRecorderRoute) instead of using SettingsRoute
  • SettingsCoordinator delegates to FlightRecorderCoordinator via module pattern
  • Clean separation enables Authenticator app to integrate flight recorder independently
Moved components (click to expand)

Core layer (BitwardenShared → BitwardenKit):

  • FlightRecorder protocol and DefaultFlightRecorder implementation
  • FlightRecorderStateService protocol (new)
  • FlightRecorderData and related models
  • FlightRecorderHTTPLogger
  • Extensions: FileManager.flightRecorderLogURL(), URL.setIsExcludedFromBackup(), Task.sleep(forSeconds:)

UI layer (BitwardenShared → BitwardenKit):

  • FlightRecorderCoordinator (new), FlightRecorderRoute (new), FlightRecorderModule (new)
  • EnableFlightRecorderProcessor, EnableFlightRecorderView
  • FlightRecorderLogsProcessor, FlightRecorderLogsView
  • Alert extension for flight recorder

Resources:

  • Added "secure-devices" illustration assets (light + dark mode PDFs)

Findings

Finding 1: URL.setIsExcludedFromBackup may not work as intended

BitwardenKit/Core/Platform/Extensions/URL.swift:64

The method creates a mutable copy (var url = self) and calls setResourceValues on that copy. While this likely works because setResourceValues modifies file metadata (not the URL value), the implementation is non-obvious and could benefit from clarification:

func setIsExcludedFromBackup(_ value: Bool) throws {
    // Note: Creates mutable copy to call setResourceValues, which modifies
    // the file resource attributes at this path, not the URL value itself
    var url = self
    var values = URLResourceValues()
    values.isExcludedFromBackup = value
    try url.setResourceValues(values)
}

Note: This existed in the original BitwardenShared code. The implementation appears correct based on Apple's documentation, but the comment would improve maintainability.


Finding 2: Test coverage gaps in AppModule integration

BitwardenShared/UI/Platform/Application/AppModule.swift:66-78

The new FlightRecorderModule conformance in DefaultAppModule has 0% coverage according to Codecov (7 lines missing). While the coordinator tests exist, there's no test verifying that DefaultAppModule.makeFlightRecorderCoordinator correctly instantiates the coordinator.

Suggested test:

/// `makeFlightRecorderCoordinator(stackNavigator:)` returns a properly configured coordinator.
@MainActor
func test_makeFlightRecorderCoordinator() {
    let stackNavigator = MockStackNavigator()
    let coordinator = subject.makeFlightRecorderCoordinator(stackNavigator: stackNavigator)
    XCTAssertNotNil(coordinator)
}

Finding 3: Missing test coverage in URL extension

BitwardenKit/Core/Platform/Extensions/URL.swift:64-69

The setIsExcludedFromBackup method has 70% coverage (6 lines missing according to Codecov). This is a critical method for preventing flight recorder logs from being backed up. Recommend adding tests for:

  • Success case: verify the attribute is set correctly
  • Error case: verify error handling when URL points to non-existent file

Good Practices Observed

  • Proper error handling with custom error types and detailed error codes
  • Comprehensive test coverage for core FlightRecorder logic (93.73% overall)
  • Clean separation of concerns with protocol-based architecture
  • Proper use of dependency injection throughout
  • Documentation follows DocC standards
  • Previous review feedback addressed promptly and correctly

Action Items

  1. Optional: Add clarifying comment to URL.setIsExcludedFromBackup (Finding 1)
  2. Recommended: Add test for DefaultAppModule.makeFlightRecorderCoordinator (Finding 2)
  3. Recommended: Add tests for URL.setIsExcludedFromBackup success/error cases (Finding 3)

All action items are improvements rather than blocking issues. The PR is architecturally sound and ready for merge pending team review of findings.


///
/// - Parameter value: `true` if the file should be excluded from backups, or `false` otherwise.
///
func setIsExcludedFromBackup(_ value: Bool) throws {
Copy link

Choose a reason for hiding this comment

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

⚠️ Verify: Method mutates a copy rather than self

This method creates a mutable copy of self and modifies that copy:

var url = self  // Creates a copy

The method name setIsExcludedFromBackup suggests mutation of self, but it's actually modifying a local copy. This should still work because setResourceValues affects the file resource at the URL path, not just the URL value itself.

Recommendation: Add a comment clarifying this behavior:

/// Sets whether the file should be excluded from backups.
///
/// Note: This method creates a mutable copy to call setResourceValues, which
/// modifies the file resource attributes, not the URL value itself.
///
/// - Parameter value: `true` if the file should be excluded from backups, or `false` otherwise.
///
func setIsExcludedFromBackup(_ value: Bool) throws {

Note: This pattern existed in the original code. Worth verifying it works as intended, though it's likely correct since URLResourceValues affects file metadata.

@codecov
Copy link

codecov bot commented Nov 12, 2025

Codecov Report

❌ Patch coverage is 93.80531% with 21 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.77%. Comparing base (4b34dd8) to head (afad21f).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
...rdenShared/UI/Platform/Application/AppModule.swift 0.00% 7 Missing ⚠️
BitwardenKit/Core/Platform/Extensions/URL.swift 70.00% 6 Missing ⚠️
...e/Platform/Services/Mocks/MockFlightRecorder.swift 93.47% 3 Missing ⚠️
...ngs/FlightRecorder/FlightRecorderCoordinator.swift 95.12% 2 Missing ⚠️
...Kit/Core/Platform/Extensions/Task+Extensions.swift 85.71% 1 Missing ⚠️
...denKit/Core/Platform/Services/FlightRecorder.swift 93.75% 1 Missing ⚠️
...ervices/Mocks/MockFlightRecorderStateService.swift 90.90% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2133      +/-   ##
==========================================
- Coverage   85.23%   83.77%   -1.46%     
==========================================
  Files        1709     1970     +261     
  Lines      145427   160762   +15335     
==========================================
+ Hits       123958   134686   +10728     
- Misses      21469    26076    +4607     

☔ 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.

Copy link
Member

@fedemkr fedemkr 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, just a small question

/// - Returns: The active account's unique identifier.
/// - Throws: An error if the active account cannot be determined.
///
func getActiveAccountId() async throws -> String
Copy link
Member

Choose a reason for hiding this comment

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

🤔 Should the new protocol ActiveAccountStateProvider be used instead of adding the same method here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh, yeah. Are you thinking we'd have FlightRecorderStateService inherit from ActiveAccountStateProvider?

protocol FlightRecorderStateService: ActiveAccountStateProvider { ... }

Copy link
Member

Choose a reason for hiding this comment

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

Yes I think that would be nice.

@github-actions
Copy link
Contributor

Logo
Checkmarx One – Scan Summary & Detailsefc8f237-2d50-483d-a8c8-126c8c880bd3

Great job! No new security vulnerabilities introduced in this pull request

@matt-livefront matt-livefront merged commit e8d7c67 into main Nov 13, 2025
27 checks passed
@matt-livefront matt-livefront deleted the matt/PM-26063-flight-recorder-bwk branch November 13, 2025 18:41
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