Conversation
# Conflicts: # tests/ByteSync.Client.Tests/ByteSync.Client.Tests.csproj # tests/ByteSync.Common.Tests/ByteSync.Common.Tests.csproj
|
There was a problem hiding this comment.
Pull Request Overview
This PR hardens security by mitigating regex-related CPU hotspots (ReDoS), hardening process invocations, and improving thread safety of random number generation.
- Adds explicit timeouts (200-500ms) to all Regex usage to prevent ReDoS attacks
- Replaces direct process invocations with trusted absolute paths for system binaries
- Migrates from static Random to cryptographically secure RandomNumberGenerator for thread safety
Reviewed Changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ByteSync.Common/Helpers/RandomUtils.cs | Replaces static Random with RandomNumberGenerator for cryptographic security |
| src/ByteSync.Common/Helpers/DebugUtils.cs | Removes static Random, uses RandomNumberGenerator with improved floating-point comparison |
| src/ByteSync.Client/Services/Updates/*.cs | Uses trusted absolute paths for system binaries and adds regex timeouts |
| src/ByteSync.Client/Services/Communications/*.cs | Hardens process invocation with trusted paths and adds regex timeouts |
| src/ByteSync.Client/Business/Filtering/**/*.cs | Adds comprehensive regex timeouts and error handling to all filtering logic |
| src/ByteSync.Client/Business/Configurations/ApplicationSettings.cs | Adds timeout to regex pattern used in client ID generation |
| tests/**/*.cs | Adds comprehensive test coverage for new security features |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| double milliseconds; | ||
| if (minSeconds == maxSeconds) | ||
| double epsilon = 1e-10; | ||
| if (double.Abs(minSeconds - maxSeconds) < epsilon) |
There was a problem hiding this comment.
This should use Math.Abs instead of double.Abs. The double type does not have a static Abs method.
| if (double.Abs(minSeconds - maxSeconds) < epsilon) | |
| if (Math.Abs(minSeconds - maxSeconds) < epsilon) |
| else | ||
| { | ||
| return (char) _random.Next('a', 'z'); | ||
| return (char) RandomNumberGenerator.GetInt32('a', 'z'); |
There was a problem hiding this comment.
The upper bound should be 'Z' + 1 since RandomNumberGenerator.GetInt32 uses exclusive upper bound, but the current code will never return 'Z'.
| else | ||
| { | ||
| return (char) _random.Next('a', 'z'); | ||
| return (char) RandomNumberGenerator.GetInt32('a', 'z'); |
There was a problem hiding this comment.
The upper bound should be 'z' + 1 since RandomNumberGenerator.GetInt32 uses exclusive upper bound, but the current code will never return 'z'.
| else | ||
| { | ||
| return (char) _random.Next('a', 'z'); | ||
| return (char) RandomNumberGenerator.GetInt32('a', 'z'); |
There was a problem hiding this comment.
The upper bound should be 'Z' + 1 since RandomNumberGenerator.GetInt32 uses exclusive upper bound, but the current code will never return 'Z'.
| else | ||
| { | ||
| return (char) _random.Next('a', 'z'); | ||
| return (char) RandomNumberGenerator.GetInt32('a', 'z'); |
There was a problem hiding this comment.
The upper bound should be 'z' + 1 since RandomNumberGenerator.GetInt32 uses exclusive upper bound, but the current code will never return 'z'.
| var top = (int) Math.Pow(10, decimals); | ||
|
|
||
| var r = _random.Next(top) + 1; | ||
| var r = RandomNumberGenerator.GetInt32(top + 1) + 1; |
There was a problem hiding this comment.
This logic is incorrect. RandomNumberGenerator.GetInt32(top + 1) returns 0 to top inclusive, so adding 1 gives 1 to top+1. This should be RandomNumberGenerator.GetInt32(top) to get 0 to top-1, then add 1 to get 1 to top.
| var r = RandomNumberGenerator.GetInt32(top + 1) + 1; | |
| var r = RandomNumberGenerator.GetInt32(top) + 1; |
paul-fresquet
left a comment
There was a problem hiding this comment.
Good Job, only one question 👍
There was a problem hiding this comment.
question: why did you change the way processes are started? Is it security-related ?
There was a problem hiding this comment.
Yes, it was a security flaw. By launching only through the environment variable, there’s a risk that it could point to a malicious program. That’s why I replaced each variable with the full path.



PR Title
Harden regex usage, process invocation, and randomness; reduce potential hotspots and improve safety
Overview
This PR mitigates potential regex-related CPU hotspots (ReDoS), improves robustness against invalid patterns, hardens process invocations by using trusted binary paths, and makes random number generation thread-safe.
Key Changes
1. Regex timeouts and safety
Regex.IsMatch/Regex.Replacecalls with pre-constructed Regex instances where appropriate.ArgumentException(invalid patterns) and treat as non-match instead of throwing in user-facing filter paths.2. Process invocation hardening
Path.Combine(Environment.SystemDirectory, "cmd.exe")and escape&./usr/bin/xdg-open/usr/bin/open3. Thread-safe randomness
RandomwithThreadLocal<Random>to avoid contention and non-thread-safe usage.Files:
ByteSync.Common/Helpers/DebugUtils.csByteSync.Common/Helpers/RandomUtils.csBehavior Changes
Risks and Mitigations
Risk: Timeouts could be too strict for very large inputs.
Mitigation: Chosen conservative values (200–500 ms). We can tune if we observe false negatives.
Risk: Absolute paths may differ on atypical distributions.
Mitigation: Paths chosen are standard; if needed, we can add existence checks and fallbacks in a follow-up.
Checklist
Summary:
Implemented a comprehensive regex safety pass with timeouts and error handling; hardened system process invocation paths; switched to
ThreadLocal<Random>to improve thread safety.