[PM-42319] - Add Inject Collection - #8267
Conversation
🤖 Bitwarden Claude Code ReviewOverall Assessment: APPROVE This PR closes a cross-tenant IDOR class in Code Review Details
|
| if (collection is null || collection.OrganizationId != orgId) | ||
| { | ||
| throw new NotFoundException(); | ||
| } |
There was a problem hiding this comment.
🎨 SUGGESTED: Put re-implements by hand exactly what [InjectCollection] does — consider using the attribute here too.
Details and rationale
Put does GetByIdAsync(id) followed by a null + OrganizationId != orgId check, which is byte-for-byte the contract of CollectionModelBinder. Using the attribute would look like:
[HttpPut("{id}")]
public async Task<CollectionResponseModel> Put(Guid orgId, [InjectCollection] Collection collection,
[FromBody] UpdateCollectionRequestModel model)and PostPut would forward collection the same way PostDelete already forwards to Delete. Mixing a custom-binder parameter with [FromBody] in one action is already proven in this codebase — see OrganizationUsersController.Put([BindOrganization] Organization organization, Guid id, [FromBody] ...) at src/Api/AdminConsole/Controllers/OrganizationUsersController.cs:437.
The value is bug prevention rather than fixing a defect: every endpoint that keeps a hand-written org-scoping check is another place the check can be dropped or copied incorrectly, which is the exact class of bug this PR is closing. GetDetails genuinely cannot use the binder (it needs CollectionAdminDetails from GetByIdWithPermissionsAsync), but Put can.
Non-blocking — the current code is correct, and moving the check into the binder would mean reworking Put_CollectionBelongsToDifferentOrg_ThrowsNotFound into a binder test, so it is a fair trade to decline.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #8267 +/- ##
==========================================
- Coverage 63.36% 63.34% -0.02%
==========================================
Files 2430 2431 +1
Lines 104847 104950 +103
Branches 9505 9509 +4
==========================================
+ Hits 66433 66484 +51
- Misses 36137 36186 +49
- Partials 2277 2280 +3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
r-tome
left a comment
There was a problem hiding this comment.
Looks good, just a couple of minor tweaks needed
| [Fact] | ||
| public async Task Get_CollectionBelongsToDifferentOrg_ReturnsNotFound() | ||
| { | ||
| var otherOwnerEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com"; |
There was a problem hiding this comment.
⛏️ use the domain example.com for tests
| } | ||
|
|
||
| [Fact] | ||
| public async Task Get_CollectionBelongsToDifferentOrg_ReturnsNotFound() |
There was a problem hiding this comment.
I think we usually we only write integration tests for happy path tests. These are already covered by the unit tests
🎟️ Tracking
PM-42319
📔 Objective
Adding an InjectCollectionAttribute that will retrieve the collection and validate that the route in the url matches the organization. This is then used with the authorization logic to make sure they have access to it. Added a few other checks to validate orgid in route is used to validate against collections being retrieved/acted upon.