-
Notifications
You must be signed in to change notification settings - Fork 1
Add DevSecOps7 demo page with intentionally vulnerable code for GHAS detection #113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: CalinL <10718943+CalinL@users.noreply.github.com>
Dependency ReviewThe following issues were found:
Snapshot WarningsEnsure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice. Vulnerabilitiessrc/webapp01/webapp01.csproj
Only included vulnerabilities with severity moderate or higher. OpenSSF Scorecard
Scanned Files
|
| { | ||
| // Log forging vulnerability - user input directly in logs | ||
| string userInput = Request.Query.ContainsKey("user") ? Request.Query["user"].ToString() ?? "anonymous" : "anonymous"; | ||
| _logger.LogInformation($"User accessed DevSecOps7 page: {userInput}"); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
To fix the vulnerability, user input that is being logged should be sanitized so that log forging via new lines or format-breaking characters is not possible. Since the log entry is written as plain text (not HTML), the recommended technique is to strip out all line breaks and similar control characters from the value before including it in the log. This can be done using string.Replace for \r and \n. Additionally, optionally wrapping or quoting the value helps clarify the log entries. The only necessary change is on the log call in line 31 within src/webapp01/Pages/DevSecOps7.cshtml.cs, where the userInput variable is sanitized in the log message.
If reused elsewhere, it may be beneficial to create a small helper for sanitization, but for now, only demonstrated where flagged.
-
Copy modified lines R31-R33
| @@ -28,7 +28,9 @@ | ||
| { | ||
| // Log forging vulnerability - user input directly in logs | ||
| string userInput = Request.Query.ContainsKey("user") ? Request.Query["user"].ToString() ?? "anonymous" : "anonymous"; | ||
| _logger.LogInformation($"User accessed DevSecOps7 page: {userInput}"); | ||
| // Sanitize user input before logging to prevent log forging | ||
| string sanitizedUserInput = userInput.Replace("\r", "").Replace("\n", ""); | ||
| _logger.LogInformation($"User accessed DevSecOps7 page: {sanitizedUserInput}"); | ||
|
|
||
| // Simulate getting latest news about GitHub Advanced Security | ||
| LoadLatestGHASNews(); |
| string testPattern = Request.Query.ContainsKey("pattern") ? Request.Query["pattern"].ToString() ?? "aaa" : "aaa"; | ||
| try | ||
| { | ||
| bool isMatch = VulnerableRegex.IsMatch(testPattern); |
Check failure
Code scanning / CodeQL
Denial of Service from comparison of user input against expensive regex High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
The best way to fix the problem is to ensure that any regex operation on attacker-controlled input either (a) uses a regular expression with guaranteed low complexity, or (b) sets a timeout on the regex evaluation, preventing ReDoS. Since the purpose of this demo is to show security practices, it's prudent to fix the dangerous usage by using the overload of the .NET Regex methods (e.g., IsMatch) or the Regex constructor that accepts a TimeSpan timeout, thus ensuring that even if the pattern or input is problematic, the evaluation will be aborted after a safe period (such as 1 second).
In this specific context, lines which call VulnerableRegex.IsMatch(testPattern) (line 40 and line 94) are the main concern. The existing VulnerableRegex is a static field constructed without a timeout. Instead of using it, construct a regex instance with a timeout for each request, or update the static regex to use a timeout. Since static initialization with TimeSpan is tricky (as there's no static constructor with timeout in use right now), the best fix is to replace the match check with a new Regex instance for each match, using the same pattern, but specifying a safe timeout.
You will need to add using System; if not already present (it isn't strictly needed in this context since TimeSpan.FromSeconds(...) is fully qualified or can be written with System.TimeSpan.FromSeconds). Otherwise, it's safe.
Specifically:
- On line 40: Replace
VulnerableRegex.IsMatch(testPattern)withnew Regex(@"^(a+)+$", RegexOptions.Compiled, TimeSpan.FromSeconds(1)).IsMatch(testPattern); - On line 94: Replace
VulnerableRegex.IsMatch(pattern)similarly. - Optionally, remove the static
VulnerableRegexif it is not needed elsewhere.
-
Copy modified line R18 -
Copy modified line R40 -
Copy modified line R94
| @@ -15,7 +15,7 @@ | ||
| private const string CONNECTION_STRING = "Server=localhost;Database=TestDB;User Id=admin;Password=SecretPassword123!;"; | ||
|
|
||
| // Weak regex pattern - vulnerable to ReDoS | ||
| private static readonly Regex VulnerableRegex = new Regex(@"^(a+)+$", RegexOptions.Compiled); | ||
| // (Removed VulnerableRegex; use safer inline construction with timeout) | ||
|
|
||
| public DevSecOps7Model(ILogger<DevSecOps7Model> logger) | ||
| { | ||
| @@ -37,7 +37,7 @@ | ||
| string testPattern = Request.Query.ContainsKey("pattern") ? Request.Query["pattern"].ToString() ?? "aaa" : "aaa"; | ||
| try | ||
| { | ||
| bool isMatch = VulnerableRegex.IsMatch(testPattern); | ||
| bool isMatch = new Regex(@"^(a+)+$", RegexOptions.Compiled, TimeSpan.FromSeconds(1)).IsMatch(testPattern); | ||
| _logger.LogInformation($"Regex pattern match result: {isMatch} for input: {testPattern}"); | ||
| } | ||
| catch (Exception ex) | ||
| @@ -91,7 +91,7 @@ | ||
| try | ||
| { | ||
| // Vulnerable regex that could cause ReDoS | ||
| bool result = VulnerableRegex.IsMatch(pattern); | ||
| bool result = new Regex(@"^(a+)+$", RegexOptions.Compiled, TimeSpan.FromSeconds(1)).IsMatch(pattern); | ||
| TempData["RegexResult"] = $"Pattern '{pattern}' match result: {result}"; | ||
| } | ||
| catch (Exception ex) |
| try | ||
| { | ||
| bool isMatch = VulnerableRegex.IsMatch(testPattern); | ||
| _logger.LogInformation($"Regex pattern match result: {isMatch} for input: {testPattern}"); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
To fix this vulnerability, sanitize the user input (testPattern) before logging it, to prevent malicious actors from injecting log-forging characters (such as newlines). For plain text logs (as in this code), the best mitigation is to strip out all newlines and possibly other control characters from the user input prior to logging. This can be done using String.Replace for \r, \n, or—for better security—Regex.Replace to remove all control characters.
You should make the minimal change necessary for correct behavior: replace the use of testPattern in the log message(s) (line 41 and any similar cases) with a sanitized version that removes newlines (e.g. testPattern.Replace("\n", "").Replace("\r", "") or testPatternSanitized). This can be either done inline or (preferably) by assigning to a new variable for clarity.
As you only have access to the code in src/webapp01/Pages/DevSecOps7.cshtml.cs, apply this change only to the scope of the specific log message(s) involving tainted user input.
-
Copy modified lines R38-R39 -
Copy modified line R43
| @@ -35,10 +35,12 @@ | ||
|
|
||
| // Demonstrate potential ReDoS vulnerability | ||
| string testPattern = Request.Query.ContainsKey("pattern") ? Request.Query["pattern"].ToString() ?? "aaa" : "aaa"; | ||
| // Sanitize user input to prevent log forging | ||
| string testPatternSanitized = testPattern.Replace("\r", "").Replace("\n", ""); | ||
| try | ||
| { | ||
| bool isMatch = VulnerableRegex.IsMatch(testPattern); | ||
| _logger.LogInformation($"Regex pattern match result: {isMatch} for input: {testPattern}"); | ||
| _logger.LogInformation($"Regex pattern match result: {isMatch} for input: {testPatternSanitized}"); | ||
| } | ||
| catch (Exception ex) | ||
| { |
| catch (Exception ex) | ||
| { | ||
| // Log forging in exception handling | ||
| _logger.LogError($"Regex evaluation failed for pattern: {testPattern}. Error: {ex.Message}"); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
To fix the problem, we should sanitize any user input before it is included in log entries. Since the logging targets text logs (not HTML), the main danger is control characters such as newlines, carriage returns, and tabs, which can be used to forge/mislead log entries. The appropriate remediation is to remove these characters from any user-supplied input before logging.
A suitable fix is to call .Replace("\r", "") and .Replace("\n", "") on the value, or use a regular expression to remove all possible problematic control characters as shown in examples (or at minimum, \r and \n). In the case of testPattern, apply this sanitization before interpolating it into the log message at line 46.
A similar change should be applied to other log entries in the file that incorporate unsanitized user input (e.g., lines 31, 41, 89, 100, and possibly 95), but the direct fix required by the error is for line 46.
Update line 46 in src/webapp01/Pages/DevSecOps7.cshtml.cs as follows:
- Before logging, sanitize
testPatternby removing newlines/carriage returns (e.g.testPatternSanitized = testPattern.Replace("\r", "").Replace("\n", "")) - Use the sanitized value in the log message.
If broader sanitization is desired, consider encapsulating the logic in a helper method within the file.
-
Copy modified lines R46-R47
| @@ -43,7 +43,8 @@ | ||
| catch (Exception ex) | ||
| { | ||
| // Log forging in exception handling | ||
| _logger.LogError($"Regex evaluation failed for pattern: {testPattern}. Error: {ex.Message}"); | ||
| var testPatternSanitized = testPattern.Replace("\r", "").Replace("\n", ""); | ||
| _logger.LogError($"Regex evaluation failed for pattern: {testPatternSanitized}. Error: {ex.Message}"); | ||
| } | ||
|
|
||
| // Simulate database connection with hardcoded credentials |
| // Simulate database connection with hardcoded credentials | ||
| try | ||
| { | ||
| using var connection = new SqlConnection(CONNECTION_STRING); |
Check failure
Code scanning / CodeQL
Insecure SQL connection High
Connection string
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
To fix the problem, edit the connection string specified within the CONNECTION_STRING constant (line 15) to include the parameter Encrypt=True;. This change enforces that the client-side connection to SQL Server will use encrypted transport (TLS), mitigating the identified risk. No additional code changes are required elsewhere, as the connection string is only used for the demonstration SQL connection instantiation.
-
Copy modified line R15
| @@ -12,7 +12,7 @@ | ||
| private readonly ILogger<DevSecOps7Model> _logger; | ||
|
|
||
| // Hardcoded credentials for demo purposes - INSECURE | ||
| private const string CONNECTION_STRING = "Server=localhost;Database=TestDB;User Id=admin;Password=SecretPassword123!;"; | ||
| private const string CONNECTION_STRING = "Server=localhost;Database=TestDB;User Id=admin;Password=SecretPassword123!;Encrypt=True;"; | ||
|
|
||
| // Weak regex pattern - vulnerable to ReDoS | ||
| private static readonly Regex VulnerableRegex = new Regex(@"^(a+)+$", RegexOptions.Compiled); |
| public void OnGet() | ||
| { | ||
| // Log forging vulnerability - user input directly in logs | ||
| string userInput = Request.Query.ContainsKey("user") ? Request.Query["user"].ToString() ?? "anonymous" : "anonymous"; |
Check notice
Code scanning / CodeQL
Inefficient use of ContainsKey Note
indexer
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
To fix the inefficient use of ContainsKey and the indexer on Request.Query, replace the two operations with a single TryGetValue call. Specifically, in the OnGet method on line 30 in file src/webapp01/Pages/DevSecOps7.cshtml.cs, update the code so that Request.Query.TryGetValue("user", out var userVals) is used. This can then safely use the value if it exists, defaulting to "anonymous" if it doesn't or if conversion fails. The change is localized to line 30, and does not require any new imports, as all necessary functionality is provided by ASP.NET Core.
-
Copy modified line R30
| @@ -27,7 +27,7 @@ | ||
| public void OnGet() | ||
| { | ||
| // Log forging vulnerability - user input directly in logs | ||
| string userInput = Request.Query.ContainsKey("user") ? Request.Query["user"].ToString() ?? "anonymous" : "anonymous"; | ||
| string userInput = Request.Query.TryGetValue("user", out var userVals) ? userVals.ToString() ?? "anonymous" : "anonymous"; | ||
| _logger.LogInformation($"User accessed DevSecOps7 page: {userInput}"); | ||
|
|
||
| // Simulate getting latest news about GitHub Advanced Security |
| LoadLatestGHASNews(); | ||
|
|
||
| // Demonstrate potential ReDoS vulnerability | ||
| string testPattern = Request.Query.ContainsKey("pattern") ? Request.Query["pattern"].ToString() ?? "aaa" : "aaa"; |
Check notice
Code scanning / CodeQL
Inefficient use of ContainsKey Note
indexer
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
To fix the inefficient use of ContainsKey followed by index access, replace the pattern:
string testPattern = Request.Query.ContainsKey("pattern") ? Request.Query["pattern"].ToString() ?? "aaa" : "aaa";with a call to TryGetValue, which combines both operations efficiently. The best way is to declare a local variable for the value retrieved (of type Microsoft.Extensions.Primitives.StringValues), then use .ToString() or fallback logic as needed. The replacement should still ensure the value is assigned as "aaa" if the query param is absent or its value is null. No new imports or methods are needed; this change is limited to the code at line 37.
-
Copy modified lines R37-R45
| @@ -34,7 +34,15 @@ | ||
| LoadLatestGHASNews(); | ||
|
|
||
| // Demonstrate potential ReDoS vulnerability | ||
| string testPattern = Request.Query.ContainsKey("pattern") ? Request.Query["pattern"].ToString() ?? "aaa" : "aaa"; | ||
| string testPattern; | ||
| if (Request.Query.TryGetValue("pattern", out var patternVal) && patternVal.ToString() != null) | ||
| { | ||
| testPattern = patternVal.ToString(); | ||
| } | ||
| else | ||
| { | ||
| testPattern = "aaa"; | ||
| } | ||
| try | ||
| { | ||
| bool isMatch = VulnerableRegex.IsMatch(testPattern); |
| catch (Exception ex) | ||
| { | ||
| // Log forging in exception handling | ||
| _logger.LogError($"Regex evaluation failed for pattern: {testPattern}. Error: {ex.Message}"); | ||
| } |
Check notice
Code scanning / CodeQL
Generic catch clause Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
To fix this error, we should update the catch (Exception ex) block on line 43 to instead catch only the specific exceptions that are reasonably expected from Regex.IsMatch. According to Microsoft's documentation, these typically are:
RegexMatchTimeoutException: Thrown when the match time-out interval is exceeded.ArgumentNullException: IftestPatternis null (but the code defaultstestPatternto "aaa", so this is unlikely, but may occur depending on refactoring).RegexParseException(added in .NET 7; in earlier versions, you'd getArgumentException): When the regex pattern itself is invalid. However, here the regex pattern is constant and safe; only the input string is dynamic, so this may not apply.
Strictly, the most likely runtime exception here is RegexMatchTimeoutException. It's safe to catch that specifically. Optionally, ArgumentNullException can be caught as a fallback.
Therefore, in the try-catch block that wraps VulnerableRegex.IsMatch(testPattern), replace
catch (Exception ex)with
catch (RegexMatchTimeoutException ex)
catch (ArgumentNullException ex)ensuring the handler's body is the same (just logging).
To implement this, we may need to add a using System; import (but that's already included by default, so this is not required) and ensure RegexMatchTimeoutException is in scope (it is, via System.Text.RegularExpressions).
Lines to change:
Only the catch block at line 43–47 in src/webapp01/Pages/DevSecOps7.cshtml.cs needs changing.
-
Copy modified line R43 -
Copy modified line R46 -
Copy modified lines R48-R51
| @@ -40,11 +40,15 @@ | ||
| bool isMatch = VulnerableRegex.IsMatch(testPattern); | ||
| _logger.LogInformation($"Regex pattern match result: {isMatch} for input: {testPattern}"); | ||
| } | ||
| catch (Exception ex) | ||
| catch (RegexMatchTimeoutException ex) | ||
| { | ||
| // Log forging in exception handling | ||
| _logger.LogError($"Regex evaluation failed for pattern: {testPattern}. Error: {ex.Message}"); | ||
| _logger.LogError($"Regex evaluation timed out for pattern: {testPattern}. Error: {ex.Message}"); | ||
| } | ||
| catch (ArgumentNullException ex) | ||
| { | ||
| _logger.LogError($"Regex evaluation failed due to null input for pattern: {testPattern}. Error: {ex.Message}"); | ||
| } | ||
|
|
||
| // Simulate database connection with hardcoded credentials | ||
| try |
| catch (Exception ex) | ||
| { | ||
| _logger.LogError($"Database connection failed: {ex.Message}"); | ||
| } |
Check notice
Code scanning / CodeQL
Generic catch clause Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
To resolve the problem, we should replace catch (Exception ex) on line 56 with a catch block for the specific exception type expected from the database connection attempt, which is SqlException. The code in question specifically attempts to connect to a database using SqlConnection, and according to Microsoft documentation, failed connection attempts will throw a SqlException. Therefore, in file src/webapp01/Pages/DevSecOps7.cshtml.cs, within the method OnGet, change the catch block as follows:
- Replace
catch (Exception ex)withcatch (SqlException ex). - No additional using directives are needed as
Microsoft.Data.SqlClientis already imported on line 4.
Other aspects of the code remain unchanged. No new dependencies or libraries are required.
-
Copy modified line R56
| @@ -53,7 +53,7 @@ | ||
| _logger.LogInformation("Attempting database connection..."); | ||
| // Don't actually open connection for demo purposes | ||
| } | ||
| catch (Exception ex) | ||
| catch (SqlException ex) | ||
| { | ||
| _logger.LogError($"Database connection failed: {ex.Message}"); | ||
| } |
| catch (Exception ex) | ||
| { | ||
| // Logging sensitive information | ||
| _logger.LogError($"Regex test failed for pattern: {pattern}. Exception: {ex}"); | ||
| TempData["RegexError"] = "Pattern evaluation failed"; | ||
| } |
Check notice
Code scanning / CodeQL
Generic catch clause Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 2 days ago
To fix the problem, replace the broad catch (catch (Exception ex)) with more precise exception handling. In the context of regex matching with Regex.IsMatch, the expected exceptions are RegexMatchTimeoutException (if a match times out) and ArgumentException (if the pattern is invalid or malformed). Therefore, change the catch block to individually handle these exceptions. Make sure to log and report the error for these specific cases, while letting other exceptions propagate up (so the framework will handle them and log appropriately).
Edit only the block in the OnPostTestRegex method in src/webapp01/Pages/DevSecOps7.cshtml.cs, lines 97-102. No new imports are needed, as these exceptions are in System.
-
Copy modified line R97 -
Copy modified lines R99-R100 -
Copy modified lines R102-R106
| @@ -94,12 +94,16 @@ | ||
| bool result = VulnerableRegex.IsMatch(pattern); | ||
| TempData["RegexResult"] = $"Pattern '{pattern}' match result: {result}"; | ||
| } | ||
| catch (Exception ex) | ||
| catch (RegexMatchTimeoutException ex) | ||
| { | ||
| // Logging sensitive information | ||
| _logger.LogError($"Regex test failed for pattern: {pattern}. Exception: {ex}"); | ||
| TempData["RegexError"] = "Pattern evaluation failed"; | ||
| _logger.LogError($"Regex test timed out for pattern: {pattern}. Exception: {ex}"); | ||
| TempData["RegexError"] = "Pattern evaluation timed out"; | ||
| } | ||
| catch (ArgumentException ex) | ||
| { | ||
| _logger.LogError($"Invalid regex pattern submitted: {pattern}. Exception: {ex}"); | ||
| TempData["RegexError"] = "Invalid regex pattern"; | ||
| } | ||
|
|
||
| return RedirectToPage(); | ||
| } |
Implements a demo page showcasing GitHub Advanced Security capabilities by introducing intentionally vulnerable code patterns that GHAS should detect.
Changes
New Pages
DevSecOps7.cshtml- Razor page with GHAS news, feature overview, and interactive regex testing formDevSecOps7.cshtml.cs- Backend with ILogger implementation and security vulnerabilities for demonstrationIntentional Vulnerabilities (for GHAS demo)
Package Versions (vulnerable for demo)
Integration
Index.cshtmlto link to DevSecOps7 pageAll vulnerabilities are intentional for demonstrating GHAS code scanning, secret scanning, and dependency review capabilities.
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.