-
Notifications
You must be signed in to change notification settings - Fork 2
Add DevSecOps-2649 demo page with intentional vulnerabilities for GHAS showcase #117
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
…rabilities Co-authored-by: CalinL <10718943+CalinL@users.noreply.github.com>
Co-authored-by: CalinL <10718943+CalinL@users.noreply.github.com>
| try | ||
| { | ||
| string jsonData = JsonConvert.SerializeObject(LatestSecurityNews); | ||
| var deserializedNews = JsonConvert.DeserializeObject<List<SecurityNewsItem>>(jsonData); |
| catch (Exception ex) | ||
| { | ||
| // SECURITY VULNERABILITY: Logging full exception details including stack trace | ||
| _logger.LogError($"Failed to process security news: {ex.ToString()}"); |
| catch (Exception ex) | ||
| { | ||
| // SECURITY VULNERABILITY: Logging full exception details | ||
| _logger.LogError($"Regex evaluation failed: {ex.ToString()}"); |
| // User can inject newlines and fake log entries | ||
| string userAgent = Request.Headers["User-Agent"].ToString(); | ||
| string ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown"; | ||
| string userId = Request.Query.ContainsKey("userId") ? Request.Query["userId"].ToString() ?? "anonymous" : "anonymous"; |
| catch (Exception ex) | ||
| { | ||
| // SECURITY VULNERABILITY: Logging full exception details including stack trace | ||
| _logger.LogError($"Failed to process security news: {ex.ToString()}"); | ||
| } |
| catch (Exception ex) | ||
| { | ||
| // SECURITY VULNERABILITY: Logging exception with potentially sensitive information | ||
| _logger.LogError($"Database connection failed: {ex.Message} - Connection string: {DB_CONNECTION}"); | ||
| } |
| catch (Exception ex) | ||
| { | ||
| // SECURITY VULNERABILITY: Logging full exception details | ||
| _logger.LogError($"Regex evaluation failed: {ex.ToString()}"); | ||
| TempData["RegexTestResult"] = $"Error during regex evaluation: {ex.Message}"; | ||
| } |
| catch (Exception ex) | ||
| { | ||
| _logger.LogError($"Database query failed: {ex}"); | ||
| } |
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
|
| string userId = Request.Query.ContainsKey("userId") ? Request.Query["userId"].ToString() ?? "anonymous" : "anonymous"; | ||
|
|
||
| // Log forging vulnerability - user input directly concatenated into log message | ||
| _logger.LogInformation($"Page accessed by user: {userId} from IP: {ipAddress} with User-Agent: {userAgent}"); |
Check failure
Code scanning / CodeQL
Log entries created from user input High
user-provided value
This log entry depends on a
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
General fix: Before including user-controlled data in log messages, normalize it so it cannot introduce new log entries or confuse log parsers. For plain-text logs, the usual step is to remove or replace newline characters (\r, \n) and optionally other control characters. The sanitized values are then used in the logged message.
Best concrete fix here: Sanitize userId and userAgent right after they are obtained, by removing any carriage-return and newline characters. Then use these sanitized variables in the LogInformation call. ipAddress is derived from server-side connection info (not directly user-controlled in this snippet), so it can remain unchanged. This preserves existing functionality (still logs the same information) while mitigating log forging. No new external dependencies are needed; we can use string.Replace which is already available.
Specific changes in src/webapp01/Pages/DevSecOps-2649.cshtml.cs:
- In
OnGet, immediately after settinguserAgentanduserId, add sanitized versions, e.g.sanitizedUserAgentandsanitizedUserId, in which\rand\nare removed. - Update the
_logger.LogInformationcall on line 52 to use the sanitized variables instead of the raw ones.
-
Copy modified lines R50-R53 -
Copy modified lines R55-R56
| @@ -47,9 +47,13 @@ | ||
| string userAgent = Request.Headers["User-Agent"].ToString(); | ||
| string ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown"; | ||
| string userId = Request.Query.ContainsKey("userId") ? Request.Query["userId"].ToString() ?? "anonymous" : "anonymous"; | ||
|
|
||
| // Sanitize user-controlled values to prevent log forging by removing newline characters | ||
| string sanitizedUserAgent = userAgent.Replace("\r", string.Empty).Replace("\n", string.Empty); | ||
| string sanitizedUserId = userId.Replace("\r", string.Empty).Replace("\n", string.Empty); | ||
|
|
||
| // Log forging vulnerability - user input directly concatenated into log message | ||
| _logger.LogInformation($"Page accessed by user: {userId} from IP: {ipAddress} with User-Agent: {userAgent}"); | ||
| // Log access information using sanitized values | ||
| _logger.LogInformation($"Page accessed by user: {sanitizedUserId} from IP: {ipAddress} with User-Agent: {sanitizedUserAgent}"); | ||
|
|
||
| // Load GHAS news and statistics | ||
| LoadLatestSecurityNews(); |
| // SECURITY VULNERABILITY: Using hardcoded connection string with credentials | ||
| try | ||
| { | ||
| using var connection = new SqlConnection(DB_CONNECTION); |
Check failure
Code scanning / CodeQL
Insecure SQL connection High
Connection string
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
In general, to fix this class of issue you must ensure every SQL Server connection string explicitly sets Encrypt=true (or Encrypt=True) so that the client requires TLS for the TDS connection, instead of relying on defaults. This should be done wherever connection strings are defined, especially for constants or configuration values that are passed to SqlConnection or similar APIs.
For this specific file, the best fix without changing functionality is to update the DB_CONNECTION constant at line 24 in src/webapp01/Pages/DevSecOps-2649.cshtml.cs to include Encrypt=true; in the connection string. Since the string already ends with TrustServerCertificate=true;, we can safely append Encrypt=true; at the end. No other code changes are needed because SqlConnection will simply use the more secure setting when the connection is opened (even though in this demo it is commented out). All code paths using DB_CONNECTION will thereby enforce encryption in transit.
Concretely:
- Edit the
DB_CONNECTIONdeclaration (line 24) to:
private const string DB_CONNECTION = "Server=prod-db.example.com;Database=ProductionDB;User Id=dbadmin;Password=P@ssw0rd123!Secure;TrustServerCertificate=true;Encrypt=true;"; - No new methods, definitions, or imports are required, and no other lines in this file need modification.
-
Copy modified line R24
| @@ -21,7 +21,7 @@ | ||
| private readonly ILogger<DevSecOps2649Model> _logger; | ||
|
|
||
| // SECURITY VULNERABILITY: Hardcoded database credentials - should be detected by GHAS | ||
| private const string DB_CONNECTION = "Server=prod-db.example.com;Database=ProductionDB;User Id=dbadmin;Password=P@ssw0rd123!Secure;TrustServerCertificate=true;"; | ||
| private const string DB_CONNECTION = "Server=prod-db.example.com;Database=ProductionDB;User Id=dbadmin;Password=P@ssw0rd123!Secure;TrustServerCertificate=true;Encrypt=true;"; | ||
| private const string API_KEY = "demo_api_key_51ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop1234567890_FOR_TESTING_ONLY"; | ||
|
|
||
| // SECURITY VULNERABILITY: Vulnerable regex pattern susceptible to ReDoS (Regular Expression Denial of Service) |
|
|
||
| try | ||
| { | ||
| using var connection = new SqlConnection(DB_CONNECTION); |
Check failure
Code scanning / CodeQL
Insecure SQL connection High
Connection string
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
In general, the fix is to ensure that every SQL Server connection string explicitly enables encryption by adding Encrypt=true; (or equivalent) so the client enforces encryption in transit. When TrustServerCertificate=true is used, you should also ensure this is really desired; otherwise, remove it and use a properly trusted certificate.
For this specific file, the minimal, targeted fix is to update the DB_CONNECTION constant on line 24 in src/webapp01/Pages/DevSecOps-2649.cshtml.cs to include Encrypt=true; in the connection string. This preserves existing behavior (same server, database, credentials, and TrustServerCertificate setting) while adding enforced encryption. No other code needs to change, and no new methods or imports are necessary, because the connection string is simply a literal constant already used by new SqlConnection(DB_CONNECTION).
Concretely:
- Edit line 24 so the string ends with
...;TrustServerCertificate=true;Encrypt=true;(or insertEncrypt=true;anywhere in the connection string). - Leave all other lines as they are.
-
Copy modified line R24
| @@ -21,7 +21,7 @@ | ||
| private readonly ILogger<DevSecOps2649Model> _logger; | ||
|
|
||
| // SECURITY VULNERABILITY: Hardcoded database credentials - should be detected by GHAS | ||
| private const string DB_CONNECTION = "Server=prod-db.example.com;Database=ProductionDB;User Id=dbadmin;Password=P@ssw0rd123!Secure;TrustServerCertificate=true;"; | ||
| private const string DB_CONNECTION = "Server=prod-db.example.com;Database=ProductionDB;User Id=dbadmin;Password=P@ssw0rd123!Secure;TrustServerCertificate=true;Encrypt=true;"; | ||
| private const string API_KEY = "demo_api_key_51ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop1234567890_FOR_TESTING_ONLY"; | ||
|
|
||
| // SECURITY VULNERABILITY: Vulnerable regex pattern susceptible to ReDoS (Regular Expression Denial of Service) |
| try | ||
| { | ||
| string jsonData = JsonConvert.SerializeObject(LatestSecurityNews); | ||
| var deserializedNews = JsonConvert.DeserializeObject<List<SecurityNewsItem>>(jsonData); |
Check warning
Code scanning / CodeQL
Useless assignment to local variable Warning
deserializedNews
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
In general, to fix a “useless assignment to local variable” you either remove the unused variable and assignment or make use of the value. Here, the deserialized value is not needed anywhere, and the intent is just to demonstrate insecure deserialization. The best fix is to remove the deserializedNews local variable and call JsonConvert.DeserializeObject<List<SecurityNewsItem>>(jsonData); as a standalone statement whose result is ignored. This preserves the vulnerable behavior (deserialization without validation) while eliminating the dead assignment.
Concretely, in src/webapp01/Pages/DevSecOps-2649.cshtml.cs, within the try block where jsonData is serialized, replace line 119:
var deserializedNews = JsonConvert.DeserializeObject<List<SecurityNewsItem>>(jsonData);with:
JsonConvert.DeserializeObject<List<SecurityNewsItem>>(jsonData);No new methods, imports, or definitions are required; we only modify that one line.
-
Copy modified line R115 -
Copy modified line R119
| @@ -112,11 +112,11 @@ | ||
|
|
||
| // SECURITY VULNERABILITY: Potential insecure deserialization | ||
| // Serializing and deserializing without type validation | ||
| // Note: deserializedNews is intentionally unused - this code exists purely for GHAS to detect the vulnerability pattern | ||
| // Note: this code exists purely for GHAS to detect the vulnerability pattern | ||
| try | ||
| { | ||
| string jsonData = JsonConvert.SerializeObject(LatestSecurityNews); | ||
| var deserializedNews = JsonConvert.DeserializeObject<List<SecurityNewsItem>>(jsonData); | ||
| JsonConvert.DeserializeObject<List<SecurityNewsItem>>(jsonData); | ||
|
|
||
| // Log forging in the count | ||
| _logger.LogInformation($"Loaded {LatestSecurityNews.Count} security news items for display"); |
| // User can inject newlines and fake log entries | ||
| string userAgent = Request.Headers["User-Agent"].ToString(); | ||
| string ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown"; | ||
| string userId = Request.Query.ContainsKey("userId") ? Request.Query["userId"].ToString() ?? "anonymous" : "anonymous"; |
Check notice
Code scanning / CodeQL
Inefficient use of ContainsKey Note
indexer
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
In general, the fix is to avoid doing one operation to check for a key and a second operation to fetch the value from the same collection. When TryGetValue exists, you replace if (dict.ContainsKey(k)) x = dict[k]; with if (dict.TryGetValue(k, out x)) { ... }. In this case, Request.Query is an IQueryCollection, which exposes an indexer and ContainsKey but not TryGetValue, so we instead remove the ContainsKey call and perform a single retrieval, then check whether the retrieved value is present.
Specifically, in src/webapp01/Pages/DevSecOps-2649.cshtml.cs at line 49, replace the ternary that first calls Request.Query.ContainsKey("userId") and then uses Request.Query["userId"] with logic that directly obtains the value from Request.Query["userId"] once, converts it to string, and falls back to "anonymous" if it is null or empty. This can be done with either a temporary variable or in a compact expression; the key point is to avoid calling ContainsKey and indexing again. No new imports or helper methods are required.
-
Copy modified line R49
| @@ -46,7 +46,7 @@ | ||
| // User can inject newlines and fake log entries | ||
| string userAgent = Request.Headers["User-Agent"].ToString(); | ||
| string ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown"; | ||
| string userId = Request.Query.ContainsKey("userId") ? Request.Query["userId"].ToString() ?? "anonymous" : "anonymous"; | ||
| string userId = string.IsNullOrEmpty(Request.Query["userId"].ToString()) ? "anonymous" : Request.Query["userId"].ToString(); | ||
|
|
||
| // Log forging vulnerability - user input directly concatenated into log message | ||
| _logger.LogInformation($"Page accessed by user: {userId} from IP: {ipAddress} with User-Agent: {userAgent}"); |
| catch (Exception ex) | ||
| { | ||
| // SECURITY VULNERABILITY: Logging full exception details including stack trace | ||
| _logger.LogError($"Failed to process security news: {ex.ToString()}"); |
Check notice
Code scanning / CodeQL
Redundant ToString() call Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
In general, to fix redundant ToString() calls in interpolated strings or string concatenations, you remove the explicit .ToString() and let C#’s implicit conversion handle it. This preserves behavior because interpolation automatically calls ToString() on non-string expressions when building the final string.
Specifically here, within src/webapp01/Pages/DevSecOps-2649.cshtml.cs in the catch (Exception ex) block, change the logging line from using ex.ToString() to just ex inside the interpolated string. The updated line will still produce the same text because interpolation will invoke Exception.ToString() implicitly. No additional imports, methods, or definitions are needed; only this single line needs to be updated.
-
Copy modified line R127
| @@ -124,7 +124,7 @@ | ||
| catch (Exception ex) | ||
| { | ||
| // SECURITY VULNERABILITY: Logging full exception details including stack trace | ||
| _logger.LogError($"Failed to process security news: {ex.ToString()}"); | ||
| _logger.LogError($"Failed to process security news: {ex}"); | ||
| } | ||
| } | ||
|
|
| catch (Exception ex) | ||
| { | ||
| // SECURITY VULNERABILITY: Logging exception with potentially sensitive information | ||
| _logger.LogError($"Database connection failed: {ex.Message} - Connection string: {DB_CONNECTION}"); | ||
| } |
Check notice
Code scanning / CodeQL
Generic catch clause Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
In general, to fix a “generic catch clause” issue, replace catch (Exception) with one or more catch blocks for specific exception types that you reasonably expect and know how to handle. This avoids unintentionally catching programming errors or system‑level exceptions that should typically propagate. If you still need a final safety net, you can use a bare rethrow or at least avoid leaking sensitive information when logging.
For this specific code, AttemptDatabaseConnection is working with SqlConnection, so the most appropriate specific exception is SqlException (from Microsoft.Data.SqlClient). Creating or opening a SQL connection commonly throws SqlException. Other exceptions like InvalidOperationException or ArgumentException are possible but less clearly “expected” here; to avoid changing behavior too much, we can add a second, more generic catch that logs without sensitive data but then rethrows to avoid swallowing unexpected exceptions. However, the main CodeQL complaint is about catch (Exception ex) as the only handler. The minimal, behavior‑preserving fix that keeps the demo semantics is to narrow this to catch (SqlException ex) since that covers the likely failure here and stops the overly generic catch, without changing any other logic.
Concretely:
- In
src/webapp01/Pages/DevSecOps-2649.cshtml.cs, locateAttemptDatabaseConnection. - Replace
catch (Exception ex)withcatch (SqlException ex), reusing the existing logging statement unchanged. - No additional imports are required because
Microsoft.Data.SqlClientis already imported at the top of the file.
-
Copy modified line R155
| @@ -152,7 +152,7 @@ | ||
|
|
||
| _logger.LogInformation("Database connection string configured (not opened for demo safety)"); | ||
| } | ||
| catch (Exception ex) | ||
| catch (SqlException ex) | ||
| { | ||
| // SECURITY VULNERABILITY: Logging exception with potentially sensitive information | ||
| _logger.LogError($"Database connection failed: {ex.Message} - Connection string: {DB_CONNECTION}"); |
| catch (Exception ex) | ||
| { | ||
| // SECURITY VULNERABILITY: Logging full exception details | ||
| _logger.LogError($"Regex evaluation failed: {ex.ToString()}"); | ||
| TempData["RegexTestResult"] = $"Error during regex evaluation: {ex.Message}"; | ||
| } |
Check notice
Code scanning / CodeQL
Generic catch clause Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
To fix the problem, we should avoid catching the base Exception type and instead either (a) catch more specific, expected exception types or (b) remove the generic catch altogether and allow unexpected failures to propagate to the framework’s global exception handler. Since we must not change existing normal functionality, the best compromise is to narrow the catch blocks to likely operational exceptions and log them at an appropriate level while still returning the same type of response.
For OnPostTestRegexVulnerability (around lines 230–235), the key expected runtime problems beyond RegexMatchTimeoutException are argument-related issues with the regex or input, e.g., ArgumentException. We can replace catch (Exception ex) with catch (ArgumentException ex) so we still log and show an error when the regex API throws an argument-related exception, but we no longer swallow all Exception types. This preserves the current TempData message path for typical regex failures while allowing truly unexpected failures to bubble.
For GetUserDataUnsafe (around lines 248–266), the natural expected failures are database-related: connection problems or SQL client errors. We can replace the generic catch (Exception ex) with catch (SqlException ex) from Microsoft.Data.SqlClient, which is already imported. This retains the existing “Database query failed” log behavior for actual SQL failures, but avoids eating unrelated exceptions. No new imports or methods are required; only the catch type needs to change in each place.
-
Copy modified line R230 -
Copy modified line R263
| @@ -227,7 +227,7 @@ | ||
| _logger.LogError($"Regex timeout exception: {ex.Message} for input: {regexInput}"); | ||
| TempData["RegexTestResult"] = "Regex evaluation timed out - ReDoS vulnerability demonstrated!"; | ||
| } | ||
| catch (Exception ex) | ||
| catch (ArgumentException ex) | ||
| { | ||
| // SECURITY VULNERABILITY: Logging full exception details | ||
| _logger.LogError($"Regex evaluation failed: {ex.ToString()}"); | ||
| @@ -260,7 +260,7 @@ | ||
|
|
||
| _logger.LogWarning("SQL query constructed with string concatenation - VULNERABLE TO SQL INJECTION"); | ||
| } | ||
| catch (Exception ex) | ||
| catch (SqlException ex) | ||
| { | ||
| _logger.LogError($"Database query failed: {ex}"); | ||
| } |
| catch (Exception ex) | ||
| { | ||
| // SECURITY VULNERABILITY: Logging full exception details | ||
| _logger.LogError($"Regex evaluation failed: {ex.ToString()}"); |
Check notice
Code scanning / CodeQL
Redundant ToString() call Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
In general, to fix redundant ToString() calls in string interpolation or formatting contexts, remove the explicit .ToString() and pass the object directly; the language/runtime will call ToString() implicitly when needed.
Specifically for this file, on line 233 in src/webapp01/Pages/DevSecOps-2649.cshtml.cs, we should change the logging statement from _logger.LogError($"Regex evaluation failed: {ex.ToString()}"); to _logger.LogError($"Regex evaluation failed: {ex}");. This preserves the exact behavior because string interpolation will still invoke Exception.ToString(), but without the redundant explicit call. No additional methods, imports, or definitions are required; only this one-line change is needed.
-
Copy modified line R233
| @@ -230,7 +230,7 @@ | ||
| catch (Exception ex) | ||
| { | ||
| // SECURITY VULNERABILITY: Logging full exception details | ||
| _logger.LogError($"Regex evaluation failed: {ex.ToString()}"); | ||
| _logger.LogError($"Regex evaluation failed: {ex}"); | ||
| TempData["RegexTestResult"] = $"Error during regex evaluation: {ex.Message}"; | ||
| } | ||
|
|
| catch (Exception ex) | ||
| { | ||
| _logger.LogError($"Database query failed: {ex}"); | ||
| } |
Check notice
Code scanning / CodeQL
Generic catch clause Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
In general, the fix is to avoid a broad catch (Exception) and instead catch the most specific exception type(s) that the try block is expected to throw and that you actually intend to handle locally. Unexpected exceptions should typically be allowed to propagate so they can be handled by higher-level middleware or global exception handling.
For this specific case in GetUserDataUnsafe (lines 242–269), the code in the try block works with SqlConnection and SQL query construction, so the main anticipated, handleable failure is a database-related SqlException. The best minimal change is to replace catch (Exception ex) with catch (SqlException ex). This preserves the existing behavior for database errors (log them and return an empty results list) while no longer swallowing unrelated exceptions. No additional methods or imports are required because Microsoft.Data.SqlClient is already imported at the top of the file.
Concretely:
- In
src/webapp01/Pages/DevSecOps-2649.cshtml.cs, withinGetUserDataUnsafe, change the catch clause at line 263 fromcatch (Exception ex)tocatch (SqlException ex). - Keep the logging body unchanged so that functionality remains the same for SQL-related failures.
-
Copy modified line R263
| @@ -260,7 +260,7 @@ | ||
|
|
||
| _logger.LogWarning("SQL query constructed with string concatenation - VULNERABLE TO SQL INJECTION"); | ||
| } | ||
| catch (Exception ex) | ||
| catch (SqlException ex) | ||
| { | ||
| _logger.LogError($"Database query failed: {ex}"); | ||
| } |
Implements new demo page to demonstrate GitHub Advanced Security detection capabilities with intentional vulnerability patterns.
Changes
DevSecOps-2649.cshtmlwith latest GHAS features (2024-2026), interactive demos, and security statistics dashboardDevSecOps2649Modelwith ILogger integration and intentional vulnerability patterns:^(a+)+$with exponential backtrackingExample Vulnerability Pattern
All vulnerabilities are explicitly commented for educational purposes and should trigger GHAS code scanning alerts.
Warning
GitHub's Secret Scanning detected potential secrets in the added content. We attempted to remediate the issue by updating the code or redacting.
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.