test(storemodel): regression tests for path-traversal rejection in dropMimeData#1466
Conversation
… in dropMimeData #1464 added a canonical-path check in StoreModel::executeDropAction that refuses any drop whose source or destination resolves outside the password store. The patch shipped with unit tests on the Util::isPathInStore helper, but the integration of that helper into the drop flow was only manually verified. These three tests close that gap by crafting mime data with out-of-store source paths and asserting dropMimeData() returns false without ever reaching Pass::Move/Copy. - dropMimeDataRejectsSourceOutsideStore: mime carries /etc/passwd as the source. canDropMimeData() lets it through (it's a UI policy layer, not a fs check); executeDropAction's Util::isPathInStore call must reject. - dropMimeDataRejectsAbsoluteOutsideSource: source is a real filesystem path inside a sibling QTemporaryDir, so the check runs through canonical resolution on an existing path rather than a non-existent one. - dropMimeDataRejectsSymlinkEscape: creates a symlink physically inside the store that resolves to a sibling QTemporaryDir, then drops it onto a store-internal folder. canonicalFilePath() must follow the link and reject. Unix-only — symlink creation on Windows needs developer-mode / elevation. Validated by temporarily defanging the guard (replacing the if condition with `false && (...)`) — all three tests fail without the check, confirming they actually exercise the security boundary. With the guard in place, the qWarning output names each rejection. Build clean. 33/33 storemodel tests pass (was 30, +3 new). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI (base), Organization UI (inherited) Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThree new regression tests validate that ChangesdropMimeData security regression tests
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1466 +/- ##
==========================================
+ Coverage 33.68% 34.27% +0.59%
==========================================
Files 44 44
Lines 4379 4379
==========================================
+ Hits 1475 1501 +26
+ Misses 2904 2878 -26
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
Summary
#1464 added a canonical-path check in `StoreModel::executeDropAction` that refuses any drop whose source or destination resolves outside the password store. That patch shipped with unit tests on the `Util::isPathInStore` helper but the integration into the drop flow was only manually verified.
These three regression tests close that gap.
Tests
`canDropMimeData` is a UI-policy layer (kind / column / MIME-type) that intentionally doesn't touch the filesystem — these tests pass through `canDropMimeData` and verify the deeper `executeDropAction` check is the one doing the work.
Negative verification
To prove the tests aren't tautologically passing, I temporarily defanged the guard with `if (false && (...))` in `storemodel.cpp`. With that change all 3 tests fail:
```
FAIL! : dropMimeDataRejectsSourceOutsideStore() ... (drop with src=/etc/passwd must be refused)
FAIL! : dropMimeDataRejectsAbsoluteOutsideSource() ... (drop with src in a sibling tempdir must be refused)
FAIL! : dropMimeDataRejectsSymlinkEscape() ... (drop with src= must be refused)
```
Restoring the guard, all pass and the existing `qWarning("executeDropAction: rejecting drop that escapes the store ...")` fires for each.
Test plan
Scope
Test-only — no production-code changes. Source-traversal protection only; destination-traversal would require constructing a manipulated `QModelIndex` outside the proxy, which has no realistic attack surface in the current UI (the destination always comes from a tree-view click). Skipping happy-path `dropMimeData` tests for now: those would need a Pass-singleton stub.
🤖 Generated with Claude Code
Summary by CodeRabbit