Skip to content

fix: add adaptive timeouts to missing portions of enumeration - #261

Merged
rvazarkar merged 4 commits into
v4from
BED-2061
Nov 17, 2025
Merged

rvazarkar merged 4 commits into
v4from
BED-2061

Conversation

@rvazarkar

@rvazarkar rvazarkar commented Nov 14, 2025 •

Copy link
Copy Markdown
Contributor

Description

The check for IsWebClientRunning was not wrapped in a timeout. Additionally, our registry strategy executor was not wrapped in timeouts either. Both of these are used during SHE local group collection and could potentially lead to major hangs.

Motivation and Context

https://specterops.atlassian.net/browse/BED-6829

How Has This Been Tested?

Unable to reproduce, so adding these as a precaution.

Screenshots (if appropriate):

Types of changes

  • Chore (a change that does not modify the application functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • Documentation updates are needed, and have been made accordingly.
  • I have added and/or updated tests to cover my changes.
  • All new and existing tests passed.
  • My changes include a database migration.

Summary by CodeRabbit

  • Refactor

    • Simplified internal control flow to reduce branching and improve readability.
  • Bug Fixes

    • Added adaptive timeout protection (2-minute max) to registry and web-client operations to prevent hangs and return clear timeout failures.

@coderabbitai

coderabbitai Bot commented Nov 14, 2025 •

Copy link
Copy Markdown

Walkthrough

Refactors IRegistryKey.Connect to remove an explicit else branch and adds AdaptiveTimeout usage (2-minute max) to RegistryProcessor and WebClientServiceProcessor, replacing direct calls with ExecuteWithTimeout and handling timeout/failure results.

Changes

Cohort / File(s) Summary
Control flow refactor
src/CommonLib/IRegistryKey.cs
Removes the explicit else branch from Connect, consolidating the throw path after the if while preserving behavior.
Adaptive timeout integration
src/CommonLib/Processors/RegistryProcessor.cs, src/CommonLib/Processors/WebClientServiceProcessor.cs
Adds private AdaptiveTimeout fields (max 2 minutes); replaces direct operation calls with _*.ExecuteWithTimeout(...); checks APIResult success/failure, returns/throws timeout-specific failures, and uses result.Value on success.

Sequence Diagram(s)

sequenceDiagram
    participant Proc as Processor
    participant AT as AdaptiveTimeout
    participant Op as Operation

    Proc->>AT: ExecuteWithTimeout(Op, max=2m)
    AT->>Op: Run operation (bounded)
    alt completes within 2 minutes
        Op-->>AT: APIResult.Success(value)
        AT-->>Proc: APIResult.Success(value)
        Proc->>Proc: Use result.Value
    else times out or fails
        Op--xAT: Timeout / failure
        AT-->>Proc: APIResult.Failure (timeout or error)
        Proc->>Proc: Return Failure / throw TimeoutException
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Check AdaptiveTimeout initialization and intended lifetime.
  • Verify correct usage of result.Value and null-safety.
  • Confirm timeout-to-exception/message translation in WebClientServiceProcessor.

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • MikeX777

Poem

🐰 I hopped through code with careful paws,
I trimmed an else, avoided flaws.
Two minutes to fetch what data yields,
Or timeout bells across the fields.
Hooray — the rabbit guards the fields! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding adaptive timeouts to previously unguarded code portions in the enumeration process.
Description check ✅ Passed The description covers all required template sections with substantive content: clear problem statement, motivation link, testing approach, and appropriate change type selection with relevant checklist items checked.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BED-2061

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 965a281 and 9448ff1.

📒 Files selected for processing (1)
  • src/CommonLib/Processors/WebClientServiceProcessor.cs (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: definitelynotagoblin
Repo: SpecterOps/SharpHoundCommon PR: 222
File: src/CommonLib/Processors/LocalGroupProcessor.cs:19-27
Timestamp: 2025-07-15T17:45:25.688Z
Learning: In SharpHoundCommon, the team prefers to keep code simple rather than implement perfect resource management when the resources being managed are non-critical. Specifically, they accept not implementing IDisposable for AdaptiveTimeout instances when the Dispose method is primarily for flushing analytics logs from ExecutionTimeSampler, viewing it as a courtesy rather than a safety requirement.
📚 Learning: 2025-06-26T16:38:49.677Z
Learnt from: definitelynotagoblin
Repo: SpecterOps/SharpHoundCommon PR: 217
File: src/CommonLib/Timeout.cs:17-36
Timestamp: 2025-06-26T16:38:49.677Z
Learning: In SharpHoundCommonLib's Timeout.cs, the team initially had concerns about disposing CancellationTokenSource instances before tasks could check cancellation tokens, but they understand that orphaned tasks (those that exceed timeout) won't be impacted by disposed tokens since their results are already ignored by the timeout handler. They prefer proper resource management with using statements for CancellationTokenSource disposal.

Applied to files:

  • src/CommonLib/Processors/WebClientServiceProcessor.cs
🧬 Code graph analysis (1)
src/CommonLib/Processors/WebClientServiceProcessor.cs (2)
src/CommonLib/Logging/Logging.cs (2)
  • Logging (7-20)
  • LogProvider (22-35)
src/CommonLib/OutputTypes/APIResult.cs (4)
  • APIResult (2-5)
  • APIResult (7-28)
  • APIResult (10-15)
  • APIResult (17-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
src/CommonLib/Processors/WebClientServiceProcessor.cs (2)

19-19: LGTM: Adaptive timeout field properly configured.

The 2-minute max timeout is appropriate for network pipe checks and aligns with the timeout strategy used in RegistryProcessor.


82-96: LGTM: Timeout wrapping correctly implemented.

The ExecuteWithTimeout integration properly handles all execution paths:

  • Success: returns the inner APIResult with the pipe check result
  • Exception: returns APIResult.Failure with exception details
  • Timeout: returns APIResult.Failure with a clear timeout message

This implementation is now consistent with RegistryProcessor's timeout handling pattern (both return APIResult.Failure on timeout rather than throwing exceptions), resolving the inconsistency flagged in the past review comment.

Tip

📝 Customizable high-level summaries are now available!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  - Provide custom instructions to shape the summary (bullet lists, tables, contributor stats, etc.).
  - Use `high_level_summary_in_walkthrough` to move the summary from the description to the walkthrough section.

  Example:

  > "Create a concise high-level summary as a bullet-point list. Then include a Markdown table showing lines added and removed by each contributing author."

  Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/CommonLib/Processors/WebClientServiceProcessor.cs (1)

82-96: Timeout handling in IsWebClientRunning is correct; consider surfacing error details

The timeout wrapper around TestPathExists correctly bounds the pipe check and returns the original APIResult<bool> on success, throwing on timeout. To aid diagnosis and align with SHRegistryKey.Connect, you could include the underlying timeout error in the exception message:

-            throw new TimeoutException($"Failed to check pipe on {computerName}: {pipePath}");
+            throw new TimeoutException(
+                $"Failed to check pipe on {computerName}: {pipePath}. Error: {result.Error}");

This keeps behavior the same while exposing more context when a timeout occurs.

src/CommonLib/Processors/RegistryProcessor.cs (1)

58-67: Registry collection timeout behavior is good; optionally include underlying error

Wrapping StrategyExecutor.CollectAsync in _registryAdaptiveTimeout.ExecuteWithTimeout and returning a failed APIResult<RegistryData> on !result.IsSuccess correctly prevents hangs and surfaces a timeout condition to callers.

If ExecuteWithTimeout provides a descriptive Error, you might optionally add it to the failure message for easier troubleshooting:

-            if (!result.IsSuccess) {
-                return APIResult<RegistryData>.Failure($"Timeout when grabbing registry data from {targetMachine}");
-            }
+            if (!result.IsSuccess) {
+                return APIResult<RegistryData>.Failure(
+                    $"Timeout when grabbing registry data from {targetMachine}: {result.Error}");
+            }

This keeps the contract intact while exposing more detail about what went wrong.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dec61f6 and e140b6d.

📒 Files selected for processing (3)
  • src/CommonLib/IRegistryKey.cs (1 hunks)
  • src/CommonLib/Processors/RegistryProcessor.cs (2 hunks)
  • src/CommonLib/Processors/WebClientServiceProcessor.cs (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: definitelynotagoblin
Repo: SpecterOps/SharpHoundCommon PR: 222
File: src/CommonLib/Processors/LocalGroupProcessor.cs:19-27
Timestamp: 2025-07-15T17:45:25.688Z
Learning: In SharpHoundCommon, the team prefers to keep code simple rather than implement perfect resource management when the resources being managed are non-critical. Specifically, they accept not implementing IDisposable for AdaptiveTimeout instances when the Dispose method is primarily for flushing analytics logs from ExecutionTimeSampler, viewing it as a courtesy rather than a safety requirement.
🧬 Code graph analysis (2)
src/CommonLib/Processors/RegistryProcessor.cs (1)
src/CommonLib/Logging/Logging.cs (2)
  • Logging (7-20)
  • LogProvider (22-35)
src/CommonLib/Processors/WebClientServiceProcessor.cs (2)
src/CommonLib/Logging/Logging.cs (2)
  • Logging (7-20)
  • LogProvider (22-35)
src/CommonLib/OutputTypes/APIResult.cs (4)
  • APIResult (2-5)
  • APIResult (7-28)
  • APIResult (10-15)
  • APIResult (17-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
src/CommonLib/IRegistryKey.cs (1)

39-43: Connect control flow simplification is fine

Switching to an early return plus a trailing TimeoutException keeps behavior unchanged and improves readability. No issues with the timeout handling here.

src/CommonLib/Processors/WebClientServiceProcessor.cs (1)

18-20: AdaptiveTimeout wiring for CreateFile looks consistent

Using a per-instance AdaptiveTimeout with a 2‑minute cap and a dedicated logger for IsWebClientRunning matches the pattern elsewhere in the codebase and is fine as a long‑lived field. Based on learnings.

src/CommonLib/Processors/RegistryProcessor.cs (1)

18-18: Registry AdaptiveTimeout initialization matches established pattern

Introducing _registryAdaptiveTimeout with a 2‑minute max timeout and a dedicated logger for ReadRegistrySettings aligns with the new timeout strategy and looks good. No extra disposal complexity needed here. Based on learnings.

Comment thread src/CommonLib/Processors/WebClientServiceProcessor.cs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e140b6d and 965a281.

📒 Files selected for processing (1)
  • src/CommonLib/Processors/WebClientServiceProcessor.cs (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: definitelynotagoblin
Repo: SpecterOps/SharpHoundCommon PR: 222
File: src/CommonLib/Processors/LocalGroupProcessor.cs:19-27
Timestamp: 2025-07-15T17:45:25.688Z
Learning: In SharpHoundCommon, the team prefers to keep code simple rather than implement perfect resource management when the resources being managed are non-critical. Specifically, they accept not implementing IDisposable for AdaptiveTimeout instances when the Dispose method is primarily for flushing analytics logs from ExecutionTimeSampler, viewing it as a courtesy rather than a safety requirement.
🧬 Code graph analysis (1)
src/CommonLib/Processors/WebClientServiceProcessor.cs (1)
src/CommonLib/OutputTypes/APIResult.cs (4)
  • APIResult (2-5)
  • APIResult (7-28)
  • APIResult (10-15)
  • APIResult (17-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (1)
src/CommonLib/Processors/WebClientServiceProcessor.cs (1)

19-19: LGTM! Field initialization is appropriate.

The 2-minute timeout is reasonable for network-based operations. Based on learnings, the team accepts not implementing disposal for AdaptiveTimeout instances.

Comment thread src/CommonLib/Processors/WebClientServiceProcessor.cs Outdated
@rvazarkar
rvazarkar merged commit bbf2f26 into v4 Nov 17, 2025
4 checks passed
@rvazarkar
rvazarkar deleted the BED-2061 branch November 17, 2025 16:20
@github-actions github-actions Bot locked and limited conversation to collaborators Nov 17, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants