feat(corpus): lock the ShareX Rfc2898DeriveBytes leak as a regression (recall 9→10)#58
Conversation
… (recall 9→10) The re-mine of ShareX on the post-#57 extractor (modeless-Form FPs gone) left a short, mostly-real list of local-disposable findings. The cleanest real bug: ShareX.UploadersLib/FileUploaders/Vault_ooo.cs:216 (the Vault.ooo uploader's DeriveCryptoData) creates an Rfc2898DeriveBytes (PBKDF2 deriver, holds an HMAC) to derive the AES key + IV, then returns without disposing it — a real resource leak on every upload. The deriver does not escape (the returned CryptoData holds only the derived byte[]s), so it is a clean local leak the flow detector catches as OWN001. It is an oversight, not a pattern: the sibling EncryptBytes() in the same file already `using`-scopes its aes/MemoryStream/CryptoStream. Captured as corpus/real-world/sharex-rfc2898-derivebytes-leak/ in the established shape: before.cs (the leak, real System.Security.Cryptography types, no ref pack), after.cs (both crypto disposables `using`-scoped → silent), case.own (the OwnLang reduction → OWN001, checked by tests/test_corpus.py), expected-diagnostics.txt, notes.md. Ratchets the benchmark floor 9→10 so the catch is pinned. notes.md also records the honest recall gap the same method exposes: it leaks a SECOND crypto disposable, rng = RandomNumberGenerator.Create(), which the extractor misses because it is a static FACTORY (the detector knows `new` and File.Open*/Create*, not arbitrary X.Create()). after.cs disposes both, so the fix is genuinely clean; the owning-factory extension is a separate recall slice. Validated locally: case.own → OWN001; tests/test_corpus.py 8/8; the modeled before.cs facts → OWN001 'is never disposed'; after.cs (using) → silent. The before.cs→facts extractor step is validated in CI (corpus-benchmark). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds a new real-world corpus test case modeling the undisposed ChangesShareX Rfc2898DeriveBytes Corpus Case
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
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 unit tests (beta)
Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/ci.yml (1)
667-677:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate stale benchmark commentary to match the new recall floor.
Line 669 still states “Now 9/11” while Line 677 enforces
--min-recall 10. Please align/remove the hard-coded ratio so docs don’t drift on future ratchets.Suggested patch
- # improves. Now 9/11 — pooled buffers ride the path-sensitive flow engine + # improves. The pinned recall floor is 10 catches — pooled buffers ride the + # path-sensitive flow engine🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/ci.yml around lines 667 - 677, The benchmark commentary in the workflow contains a hard-coded recall ratio "Now 9/11" on line 669 that no longer matches the actual minimum recall floor specified in the python scripts/benchmark.py command which uses --min-recall 10. Remove or update the hard-coded "Now 9/11" reference from the comment block to avoid documentation drift when the recall floor is ratcheted up in the future, ensuring the comment documentation stays aligned with the actual minimum recall threshold enforced by the benchmark script invocation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In @.github/workflows/ci.yml:
- Around line 667-677: The benchmark commentary in the workflow contains a
hard-coded recall ratio "Now 9/11" on line 669 that no longer matches the actual
minimum recall floor specified in the python scripts/benchmark.py command which
uses --min-recall 10. Remove or update the hard-coded "Now 9/11" reference from
the comment block to avoid documentation drift when the recall floor is
ratcheted up in the future, ensuring the comment documentation stays aligned
with the actual minimum recall threshold enforced by the benchmark script
invocation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 69e9dbc1-b0ac-475d-a4d2-f7d3c4656a0a
📒 Files selected for processing (6)
.github/workflows/ci.ymlcorpus/real-world/sharex-rfc2898-derivebytes-leak/after.cscorpus/real-world/sharex-rfc2898-derivebytes-leak/before.cscorpus/real-world/sharex-rfc2898-derivebytes-leak/case.owncorpus/real-world/sharex-rfc2898-derivebytes-leak/expected-diagnostics.txtcorpus/real-world/sharex-rfc2898-derivebytes-leak/notes.md
… on #58) The --min-recall bump to 10 left the comment saying "Now 9/11". Point the prose at the --min-recall value below (the authoritative floor, bumped per ratchet) instead of a hard-coded ratio, so the comment cannot drift on future ratchets. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
…s (recall) (#60) Closes the recall gap documented by #58: ShareX's DeriveCryptoData leaks two crypto disposables — rfcDeriver (`new Rfc2898DeriveBytes`, already caught) and rng = RandomNumberGenerator.Create() (a static factory we missed). IsOwningFactory now recognises a second owning-factory family alongside System.IO.File.Open*/Create*: a System.Security.Cryptography static `Create*` method whose result implements IDisposable (RandomNumberGenerator.Create(), Aes.Create(), SHA256.Create(), RSA.Create(), IncrementalHash.CreateHash(), ...). A local bound to one is an acquire like `new`, so an undisposed one is OWN001. Precision-guarded: static + Create-prefixed + the RESULT implements IDisposable + the declaring type is in System.Security.Cryptography — so an instance aes.CreateEncryptor() (not static) and a non-IDisposable CryptoConfig.CreateFromName() (returns object) are never mistaken for an owned acquire. The File and crypto namespace checks are factored through a small IsInNamespace helper (the File branch stays equivalent). Pinned by two FlowLocalsSample cases in the wpf-extractor --flow-locals step: CryptoFactoryLeaks (RandomNumberGenerator.Create() undisposed -> OWN001) and CryptoFactoryDisposed (SHA256.Create() + Dispose -> silent, the control proving the factory result is an acquire, not a false positive). The sharex-rfc2898 corpus before.cs now catches both leaks; its notes/comment are updated to mark the gap closed (catch count unchanged, so --min-recall stays 10). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
The bug (found by re-mining ShareX)
Re-mining
ShareX/ShareX@ed2a864on the post-#57 extractor (modeless-Formfalse positives gone) left a short, mostly-real list of local-disposable findings. The cleanest real bug —ShareX.UploadersLib/FileUploaders/Vault_ooo.cs:216, the Vault.ooo uploader'sDeriveCryptoData:Rfc2898DeriveBytesisIDisposableand holds an internal HMAC. It derives the AES key + IV, the method returns, and it is never disposed — a real leak on every Vault.ooo upload. It does not escape (the returnedCryptoDataholds only the derivedbyte[]s), so it's a clean local leak → OWN001. An oversight, not a pattern: the siblingEncryptBytes()in the same file alreadyusing-scopes itsaes/MemoryStream/CryptoStream.What's captured
corpus/real-world/sharex-rfc2898-derivebytes-leak/, in the established shape:before.csSystem.Security.Cryptography, no ref pack) → OWN001after.csusing-scoped → silentcase.owntests/test_corpus.pyexpected-diagnostics.txtOWN001notes.mdRatchets the benchmark floor 9 → 10 so the catch is pinned (a drop is a regression).
The honest caveat (recorded in notes.md)
The same method leaks a second crypto disposable —
rng = RandomNumberGenerator.Create()— which the extractor misses: it's a static factory, and the detector only knowsnewandFile.Open*/Create*, not arbitraryX.Create().after.csdisposes both, so the fix is genuinely clean; extending the owning-factory set to the BCL crypto factories (RandomNumberGenerator.Create(),Aes.Create(), …) is a separate recall slice.Validation
Local:
case.own→ OWN001;tests/test_corpus.py8/8; modeledbefore.csfacts → OWN001 "is never disposed";after.cs(using) → silent. Thebefore.cs→facts extractor step is validated in CI (corpus-benchmark, now gated at--min-recall 10).🤖 Generated with Claude Code
https://claude.ai/code/session_01Rg8kSk1YT14x7A1vo5zgED
Generated by Claude Code
Summary by CodeRabbit
Bug Fixes
Tests
Documentation