cloudcheck optimizations#3129
Open
liquidsec wants to merge 1 commit into
Open
Conversation
- Per-host parent inheritance: child events with hosts subset of parent's inherit parent's per-host cloud metadata instead of re-running lookups. Iterates parent.host_metadata (only matched hosts, typically 0-1) rather than the child's full hosts_to_check. - YARA prefilter replaces 8-regex sequential storage-bucket loop with one combined scan; ~95% of non-IP events have no possible bucket suffix and skip the per-regex pass entirely. Also drops the per-event asyncio.Lock acquire from the previous lazy loader. - LRU cache on cloudcheck.lookup() results (100K entries, ~50MB target). Repeated hosts hit the cache in ~50ns vs ~30us for the Future-based lookup. Subdomain-enum scans repeat IPs heavily. - Event.resolved_hosts is naturally immutable (frozenset, no in-place mutation API). add_resolved_host / update_resolved_hosts removed; callers refactored to single-shot assignments via the property setter. Lets the inheritance path trust parent.resolved_hosts directly without a defensive snapshot. Bench (TestInterceptCloudcheckDNSThroughput, 1000 DNS_NAME events x 3 resolved IPs): quiet: 1485 -> 1932 events/sec (+30%) loaded: 1336 -> 1853 events/sec (+39%) inherited: 1485 -> 2241 events/sec (+51%) Bumping _module_threads on cloudcheck was tested and rejected: cloudcheck.lookup is single-threaded CPU work dispatched through asyncio (the Rust binding returns a Future but the work is sync), so more coroutine workers do not unlock parallelism. Filed upstream cloudcheck#264 asking for structured pattern metadata so the YARA prefilter would not need to string-replace (?P<name> to translate Python regex flavor to YARA's.
Contributor
🚀 Performance Benchmark Report
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## dev #3129 +/- ##
======================================
- Coverage 90% 89% -0%
======================================
Files 441 442 +1
Lines 38743 39135 +392
======================================
+ Hits 34663 34725 +62
- Misses 4080 4410 +330 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TLDR: Increases the speed of cloudcheck lookups by somewhere between 30-50%, depending on the particulars of the scan.
Summary
Three independent optimizations to cloudcheck, plus a refactor to make
Event.resolved_hostsnaturally immutable (which enables the inheritance fast path to be cheap and correct).parent.host_metadata(only matched hosts, typically 0-1 entries) rather than the child's fullhosts_to_check. Per-host attribution inhost_metadata[host]['cloud_providers']keeps tags correctly scoped: a child that doesn't have one of the parent's matching hosts does not inherit that host's tag. Regression cases for the leak invariant are intest_module_cloudcheck.py.asyncio.Lockacquire the previous lazy regex loader used.cloudcheck.lookup()— 100K-entry per-scan cache keyed by host (~50MB target). Repeated hosts hit the cache in ~50ns instead of paying the ~30µs Future-based lookup. Subdomain-enum scans repeat backing IPs heavily (CloudFront-style fan-out), so hit rate is high.Event.resolved_hostsis naturally immutable —_resolved_hostsis alwaysNoneor afrozenset.add_resolved_host/update_resolved_hostsremoved; call sites in dnsresolve / http / gowitness / portscan /event_from_jsonrefactored to single-shot assignments via the new property setter. Lets the inheritance path trustparent.resolved_hostswithout any defensive snapshot.Benchmark
bbot/test/benchmarks/test_intercept_throughput_benchmarks.py::TestInterceptCloudcheckDNSThroughput— 1000 synthetic DNS_NAME events with 3 resolved IPs each, injected into the intercept chain.The
inheritedvariant covers the common path in real scans where children walk through the intercept chain after their parent has already been tagged (URL → HTTP_RESPONSE → FINDING, DNS_NAME → IP_ADDRESS, etc.).Notes
_module_threadswas tested and rejected: cloudcheck.lookup is single-threaded CPU work dispatched through asyncio (the Rust binding returns a Future but the work is sync), so more coroutine workers do not unlock parallelism.asyncio.gatherof 2000 lookups is slightly slower than sequential.(?P<name>to translate Python regex flavor._eventloop_profiler.pyscript is a diagnostic utility (not a pytest test). Underscore-prefixed so pytest does not collect it; run directly:python bbot/test/benchmarks/_eventloop_profiler.py <bbot args>.