Skip to content

[PM-41028] feat: Restrict View Send screen for policy non-compliant Sends - #2920

Open
matt-livefront wants to merge 5 commits into
matt/PM-38792-send-controls-restrict-deletion-daysfrom
matt/PM-41028-send-controls-non-compliant
Open

[PM-41028] feat: Restrict View Send screen for policy non-compliant Sends#2920
matt-livefront wants to merge 5 commits into
matt/PM-38792-send-controls-restrict-deletion-daysfrom
matt/PM-41028-send-controls-non-compliant

Conversation

@matt-livefront

@matt-livefront matt-livefront commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

🎟️ Tracking

PM-41028

📔 Objective

  • Add SendPolicyOptions.isSendTypeRestricted(for:) to determine whether an existing Send violates the active Send Controls policy (restricted type takes priority over restricted access type/deletion date/hide-email).
  • View Send screen now hides the send-link (copy/share) section and the floating edit button, and shows an ActionCard "Organization policy restriction" banner for non-compliant Sends, with copy varying by violation: restricted text type, restricted file type, or restricted fields (with a "Make a copy" action).
  • File Sends never offer "Make a copy" (an existing attachment can't be carried into a new Send), regardless of which policy field is actually violated.
  • "Make a copy" opens a new Send pre-filled from the original Send's data, adjusted to comply with the active policy via the existing AddEditSendItemProcessor policy-enforcement logic.

📸 Screenshots

Send Text Restriction
send-text-restriction
Send File Restriction
send-file-restriction
Send Restricted Field
send-restricted-field

@matt-livefront
matt-livefront requested a review from a team as a code owner July 28, 2026 20:16
@matt-livefront matt-livefront added ai-review Request a Claude code review t:feature labels Jul 28, 2026
@github-actions github-actions Bot added app:password-manager Bitwarden Password Manager app context app:authenticator Bitwarden Authenticator app context labels Jul 28, 2026
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

Reviewed the View Send restriction banner and its state derivation (isDisabled, isSendTypeRestricted, canMakeCopy, restrictionBannerMessage), the new AddSendContentType.copy route and AddEditSendItemState(copyingFrom:), the policy application in AddEditSendItemProcessor.loadData(), and the password validation change in validateSend(). Traced the "Make a copy" navigation path (ViewSendItemProcessorSendItemCoordinator.showAddItemAddEditSendItemProcessor.loadData) and confirmed the copy is created in .add mode without id/access id/key, with policy-enforced access type, deletion date, and hide-email applied afterward while .edit mode is left untouched. Also confirmed the new "Anyone with password requires a password" rule closes the case where a copied password-protected Send would otherwise be saved with authType == .password but no password, and that the password field is always rendered when that access type is selected. The findings from the previous review pass (hide-email carried into a non-compliant copy, and the passwordless copy) are addressed by the latest commits and their tests.

Code Review Details

No new findings at or above the reporting threshold. Prior inline threads are either resolved or awaiting a team discussion on test naming, which is left to the humans on the thread.

@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.62%. Comparing base (c20ec85) to head (0bbd066).

Additional details and impacted files
@@                                  Coverage Diff                                   @@
##           matt/PM-38792-send-controls-restrict-deletion-days    #2920      +/-   ##
======================================================================================
+ Coverage                                               79.60%   79.62%   +0.01%     
======================================================================================
  Files                                                    1169     1169              
  Lines                                                   75261    75336      +75     
======================================================================================
+ Hits                                                    59913    59984      +71     
- Misses                                                  15348    15352       +4     

☔ View full report in Codecov by Harness.
📢 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.

@matt-livefront
matt-livefront force-pushed the matt/PM-41028-send-controls-non-compliant branch from 15181ef to 0603468 Compare August 21, 2026 19:56

@fedemkr fedemkr left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good, just some minor comments.

mode: .add,
name: sendView.name,
notes: sendView.notes ?? "",
recipientEmails: sendView.emails.isEmpty ? [""] : sendView.emails,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤔 Is it needed to add an empty string as one of the emails recipient when the sendView.emails is empty? If that's actually necessary, then a test is missing for that case.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It's needed to provide an empty text field for the user to add an email. I'll add a test.

Comment on lines +24 to +61
/// `isDisabled`, `restrictionBannerMessage`, and `canMakeCopy` reflect a non-disabled Send.
func test_isDisabled_notDisabled() {
let subject = ViewSendItemState(sendView: .fixture(disabled: false))
XCTAssertFalse(subject.isDisabled)
XCTAssertNil(subject.restrictionBannerMessage)
XCTAssertFalse(subject.canMakeCopy)
}

/// A disabled File Send shows the generic non-compliant message and offers no copy, regardless of
/// whether its type is restricted by policy.
func test_isDisabled_disabledFileSend() {
let subject = ViewSendItemState(sendView: .fixture(type: .file, disabled: true))

XCTAssertTrue(subject.isDisabled)
XCTAssertEqual(subject.restrictionBannerMessage, Localizations.thisSendIsNotCompliantDescriptionLong)
XCTAssertFalse(subject.canMakeCopy)
}

/// A disabled Text Send whose type is restricted by policy shows the text-restricted message and
/// offers no copy.
func test_isDisabled_disabledTextSend_typeRestricted() {
var subject = ViewSendItemState(sendView: .fixture(type: .text, disabled: true))
subject.sendPolicyOptions = SendPolicyOptions(enforcedSendType: .file)

XCTAssertTrue(subject.isDisabled)
XCTAssertEqual(subject.restrictionBannerMessage, Localizations.textSendsAreNotAllowedDescriptionLong)
XCTAssertFalse(subject.canMakeCopy)
}

/// A disabled Text Send whose type isn't restricted by policy offers the "Make a copy" action.
func test_isDisabled_disabledTextSend_typeNotRestricted() {
let subject = ViewSendItemState(sendView: .fixture(type: .text, disabled: true))

XCTAssertTrue(subject.isDisabled)
XCTAssertEqual(subject.restrictionBannerMessage, Localizations.toEditThisSendMakeACopyDescriptionLong)
XCTAssertTrue(subject.canMakeCopy)
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤔 This is a different approach as how we normally do tests. It's a bit confusing to have the test saying isDisabled when that's not the actual focus of the test and at least you would just need 2 instead of 4 test functions to test that particular property.
I understand this is to save doing a lot of tests to test the functions separately but wanted to chat a bit on whether we'd like to deviate from our standard into this kind of several-in-one tests which saves several lines of code. cc: @bitwarden/team-ios
If so, perhaps we'd need to update a bit the naming convention for these cases and/or the properties to be tested in the test functions' docs.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good point. It seemed worthwhile to combine these tests given that these properties are all tightly coupled. The common theme here is testing the properties related to disabled sends due to type restrictions. What if we rename them to test_restrictionState_xyz?

@matt-livefront
matt-livefront force-pushed the matt/PM-41028-send-controls-non-compliant branch from 0603468 to c5bb798 Compare August 24, 2026 21:20
@matt-livefront
matt-livefront force-pushed the matt/PM-41028-send-controls-non-compliant branch from 766b9c4 to 0bbd066 Compare August 31, 2026 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review app:authenticator Bitwarden Authenticator app context app:password-manager Bitwarden Password Manager app context t:feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants