-
Notifications
You must be signed in to change notification settings - Fork 1
Implement DevSecOps4 page with GHAS 4.0 features and intentional security vulnerabilities #98
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
… compatibility Co-authored-by: CalinL <[email protected]>
…erabilities Co-authored-by: CalinL <[email protected]>
Dependency ReviewThe following issues were found:
Vulnerabilitiessrc/webapp01/webapp01.csproj
Only included vulnerabilities with severity moderate or higher. OpenSSF Scorecard
Scanned Files
|
public void OnGet() | ||
{ | ||
// SECURITY VULNERABILITY: 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 25 days ago
To fix the problem, we should replace the pattern:
string userInput = Request.Query.ContainsKey("user") ? Request.Query["user"].ToString() ?? "anonymous" : "anonymous";
with a single call to TryGetValue
, which will check for the existence of the key and retrieve its value in one operation. The replacement will look like:
if (Request.Query.TryGetValue("user", out var userValue))
string userInput = userValue.ToString() ?? "anonymous";
else
string userInput = "anonymous";
However, since the original code is a single-line assignment, we can use a conditional expression with TryGetValue
for brevity:
string userInput = Request.Query.TryGetValue("user", out var userValue) ? userValue.ToString() ?? "anonymous" : "anonymous";
This change should be made in the file src/webapp01/Pages/DevSecOps4.cshtml.cs
at line 38. No new imports or definitions are required.
-
Copy modified line R38
@@ -35,7 +35,7 @@ | ||
public void OnGet() | ||
{ | ||
// SECURITY VULNERABILITY: 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 userValue) ? userValue.ToString() ?? "anonymous" : "anonymous"; | ||
_logger.LogInformation($"User accessed DevSecOps 4.0 page: {userInput}"); | ||
|
||
// SECURITY VULNERABILITY: Potential information disclosure in logs |
{ | ||
// SECURITY VULNERABILITY: Log forging vulnerability - user input directly in logs | ||
string userInput = Request.Query.ContainsKey("user") ? Request.Query["user"].ToString() ?? "anonymous" : "anonymous"; | ||
_logger.LogInformation($"User accessed DevSecOps 4.0 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 25 days ago
To fix the log forging vulnerability, we need to sanitize the user input before logging it. Since the log output is likely plain text, the recommended approach is to remove or replace newline characters (\r
, \n
, and Environment.NewLine
) from the user input before including it in the log message. This can be done using String.Replace
or a regular expression. The fix should be applied to the assignment or usage of userInput
in the logging statement on line 39. No changes to existing functionality are required, and the fix should be limited to the relevant lines in the OnGet
method of DevSecOps4.cshtml.cs
.
-
Copy modified lines R39-R41
@@ -36,7 +36,9 @@ | ||
{ | ||
// SECURITY VULNERABILITY: Log forging vulnerability - user input directly in logs | ||
string userInput = Request.Query.ContainsKey("user") ? Request.Query["user"].ToString() ?? "anonymous" : "anonymous"; | ||
_logger.LogInformation($"User accessed DevSecOps 4.0 page: {userInput}"); | ||
// Sanitize user input to prevent log forging | ||
string sanitizedUserInput = userInput.Replace("\r", "").Replace("\n", "").Replace(Environment.NewLine, ""); | ||
_logger.LogInformation($"User accessed DevSecOps 4.0 page: {sanitizedUserInput}"); | ||
|
||
// SECURITY VULNERABILITY: Potential information disclosure in logs | ||
string clientIp = HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown"; |
LoadLatestGHASNews(); | ||
|
||
// SECURITY VULNERABILITY: 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 25 days ago
To fix the problem, replace the pattern where Request.Query.ContainsKey("pattern")
is checked before accessing Request.Query["pattern"]
with a single call to Request.Query.TryGetValue("pattern", out var value)
. This reduces the number of lookups and is the recommended approach for dictionary-like collections. Specifically, in src/webapp01/Pages/DevSecOps4.cshtml.cs
, line 49 should be updated to use TryGetValue
. The logic should remain the same: if the key exists, use its value (converted to string, with a fallback to "aaa" if null); otherwise, use "aaa". No new imports or definitions are needed, as TryGetValue
is already available on IQueryCollection
.
-
Copy modified lines R49-R57
@@ -46,7 +46,15 @@ | ||
LoadLatestGHASNews(); | ||
|
||
// SECURITY VULNERABILITY: 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 patternValue)) | ||
{ | ||
testPattern = patternValue.ToString() ?? "aaa"; | ||
} | ||
else | ||
{ | ||
testPattern = "aaa"; | ||
} | ||
try | ||
{ | ||
bool isMatch = VulnerableRegex.IsMatch(testPattern); |
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 25 days ago
To fix the ReDoS vulnerability, we should ensure that any regex operation on user-provided input uses a timeout. In C#, the Regex
constructor allows specifying a TimeSpan
for a match timeout. We should update the definition of VulnerableRegex
and NestedQuantifierRegex
to include a reasonable timeout (e.g., 1 second). Since these are static readonly fields, we need to replace their initialization to use the timeout overload. This change should be made in the file src/webapp01/Pages/DevSecOps4.cshtml.cs
on lines 18 and 21. No additional imports are required, as System.Text.RegularExpressions
is already imported.
-
Copy modified line R18 -
Copy modified line R21
@@ -15,10 +15,10 @@ | ||
private const string CONNECTION_STRING = "Server=localhost;Database=TestDB;User Id=admin;Password=SuperSecret123!;Trusted_Connection=false;"; | ||
|
||
// SECURITY VULNERABILITY: Weak regex pattern - vulnerable to ReDoS (Regular Expression Denial of Service) | ||
private static readonly Regex VulnerableRegex = new Regex(@"^(a+)+$", RegexOptions.Compiled); | ||
private static readonly Regex VulnerableRegex = new Regex(@"^(a+)+$", RegexOptions.Compiled, TimeSpan.FromSeconds(1)); | ||
|
||
// SECURITY VULNERABILITY: Another ReDoS pattern for advanced testing | ||
private static readonly Regex NestedQuantifierRegex = new Regex(@"^(a|b)*a*$", RegexOptions.Compiled); | ||
private static readonly Regex NestedQuantifierRegex = new Regex(@"^(a|b)*a*$", RegexOptions.Compiled, TimeSpan.FromSeconds(1)); | ||
|
||
// SECURITY VULNERABILITY: Hardcoded API key for demo | ||
private const string API_KEY = "sk-1234567890abcdef1234567890abcdef"; |
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 25 days ago
To fix the log forging vulnerability, we should sanitize the user input before logging it. Since the logs are likely plain text, the recommended approach is to remove or replace newline characters (\r
, \n
) from the user input. This can be done using String.Replace
or a regular expression. The fix should be applied to the value of testPattern
before it is included in the log entry on line 53. No changes to existing functionality are required, and the fix should be limited to the region where the vulnerable log entry is created.
-
Copy modified lines R53-R55
@@ -50,7 +50,9 @@ | ||
try | ||
{ | ||
bool isMatch = VulnerableRegex.IsMatch(testPattern); | ||
_logger.LogInformation($"Regex pattern match result: {isMatch} for input: {testPattern}"); | ||
// Sanitize user input before logging to prevent log forging | ||
string sanitizedPattern = testPattern.Replace("\r", "").Replace("\n", ""); | ||
_logger.LogInformation($"Regex pattern match result: {isMatch} for input: {sanitizedPattern}"); | ||
} | ||
catch (Exception ex) | ||
{ |
catch (Exception ex) | ||
{ | ||
TempData["SecurityError"] = $"Security test failed: {ex.Message}"; | ||
_logger.LogError($"Security test error: {ex.Message}"); | ||
} |
Check notice
Code scanning / CodeQL
Generic catch clause Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 25 days ago
To fix the problem, replace the generic catch (Exception ex)
clause in OnPostTestSecurity
with more specific exception handlers for the operations performed in the try block. Specifically, add a catch for JsonException
(from System.Text.Json
) and/or JsonReaderException
(from Newtonsoft.Json
) for JSON deserialization errors, and ArgumentException
for invalid regex patterns. If a truly generic fallback is needed, it should be last and log the error as a critical failure, but ideally, only expected exceptions should be caught. Only edit the code in the OnPostTestSecurity
method in src/webapp01/Pages/DevSecOps4.cshtml.cs
.
-
Copy modified line R153 -
Copy modified lines R155-R156 -
Copy modified lines R158-R162
@@ -150,11 +150,16 @@ | ||
TempData["SecurityError"] = "Regex pattern caused timeout - potential ReDoS vulnerability detected!"; | ||
_logger.LogWarning("ReDoS vulnerability demonstration triggered"); | ||
} | ||
catch (Exception ex) | ||
catch (ArgumentException ex) | ||
{ | ||
TempData["SecurityError"] = $"Security test failed: {ex.Message}"; | ||
_logger.LogError($"Security test error: {ex.Message}"); | ||
TempData["SecurityError"] = $"Invalid regex pattern: {ex.Message}"; | ||
_logger.LogWarning($"Regex pattern error: {ex.Message}"); | ||
} | ||
catch (JsonReaderException ex) | ||
{ | ||
TempData["SecurityError"] = $"JSON deserialization failed: {ex.Message}"; | ||
_logger.LogWarning($"JSON deserialization error: {ex.Message}"); | ||
} | ||
|
||
return RedirectToPage(); | ||
} |
catch (Exception ex) | ||
{ | ||
TempData["SecurityError"] = $"Database test failed: {ex.Message}"; | ||
_logger.LogError($"Database test error: {ex.Message}"); | ||
} |
Check notice
Code scanning / CodeQL
Generic catch clause Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 25 days ago
To fix the problem, replace the generic catch (Exception ex)
clause in OnPostTestDatabase
with a more specific catch clause for SqlException
, which is the most likely exception to be thrown by database operations. If you want to handle other specific exceptions (e.g., InvalidOperationException
), you can add additional catch blocks. Any other exceptions should be allowed to propagate, so they can be handled by higher-level error handlers or result in a proper error response.
Steps:
- In
OnPostTestDatabase
, replacecatch (Exception ex)
withcatch (SqlException ex)
. - Optionally, add another catch block for
InvalidOperationException
if you expect it. - Remove the generic catch clause.
No new imports are needed, as SqlException
is already imported.
-
Copy modified line R177
@@ -174,7 +174,7 @@ | ||
|
||
TempData["SecurityResult"] = "Database vulnerability test completed (no actual connection made)"; | ||
} | ||
catch (Exception ex) | ||
catch (SqlException ex) | ||
{ | ||
TempData["SecurityError"] = $"Database test failed: {ex.Message}"; | ||
_logger.LogError($"Database test error: {ex.Message}"); |
catch (Exception ex) | ||
{ | ||
_logger.LogError($"Cryptography demonstration error: {ex.Message}"); | ||
} |
Check notice
Code scanning / CodeQL
Generic catch clause Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 25 days ago
To fix the problem, replace the generic catch (Exception ex)
clause in the DemonstrateWeakCrypto
method with more specific catch clauses for the exceptions that are likely to be thrown by the cryptographic and encoding operations. For the code shown, the most relevant exceptions are System.Security.Cryptography.CryptographicException
(for cryptographic errors) and System.ArgumentException
(for invalid arguments, such as encoding issues). If other specific exceptions are expected, they can be added as needed. The catch blocks should log the error as before. The code should be changed only in the DemonstrateWeakCrypto
method in src/webapp01/Pages/DevSecOps4.cshtml.cs
.
-
Copy modified line R203 -
Copy modified line R205 -
Copy modified lines R207-R210
@@ -200,10 +200,14 @@ | ||
string salt = "hardcoded-salt-123"; | ||
_logger.LogWarning($"Using hardcoded salt for hashing: {salt}"); | ||
} | ||
catch (Exception ex) | ||
catch (System.Security.Cryptography.CryptographicException ex) | ||
{ | ||
_logger.LogError($"Cryptography demonstration error: {ex.Message}"); | ||
_logger.LogError($"Cryptography demonstration error (cryptographic): {ex.Message}"); | ||
} | ||
catch (System.ArgumentException ex) | ||
{ | ||
_logger.LogError($"Cryptography demonstration error (argument): {ex.Message}"); | ||
} | ||
} | ||
|
||
private void DemonstratePathTraversal(string userPath) |
try | ||
{ | ||
string basePath = "/var/www/uploads/"; | ||
string fullPath = Path.Combine(basePath, userPath); |
Check notice
Code scanning / CodeQL
Call to System.IO.Path.Combine Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 25 days ago
To fix the problem, replace the use of Path.Combine(basePath, userPath)
with Path.Join(basePath, userPath)
. Path.Join
will concatenate the paths without treating an absolute userPath
as overriding the basePath
, thus preventing the silent dropping of the base path. This change should be made only on line 215 in the DemonstratePathTraversal
method. No additional imports are needed, as Path.Join
is available in .NET Core 2.1+ and .NET Standard 2.1+. The rest of the method can remain unchanged.
-
Copy modified line R215
@@ -212,7 +212,7 @@ | ||
try | ||
{ | ||
string basePath = "/var/www/uploads/"; | ||
string fullPath = Path.Combine(basePath, userPath); | ||
string fullPath = Path.Join(basePath, userPath); | ||
_logger.LogInformation($"File access attempt: {fullPath}"); | ||
|
||
// This could allow access to files outside the intended directory |
catch (Exception ex) | ||
{ | ||
_logger.LogError($"Path traversal demonstration error: {ex.Message}"); | ||
} |
Check notice
Code scanning / CodeQL
Generic catch clause Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 25 days ago
To fix the problem, replace the generic catch (Exception ex)
clause in the DemonstratePathTraversal
method with specific catch clauses for the exceptions that are most likely to be thrown by the file operations. These include UnauthorizedAccessException
, DirectoryNotFoundException
, FileNotFoundException
, PathTooLongException
, and IOException
. Each catch block should log the error as before. Any truly unexpected exceptions will then propagate, making them easier to diagnose and fix. No new imports are needed, as these exception types are part of the .NET base class library.
Edit only the catch
block in the DemonstratePathTraversal
method in src/webapp01/Pages/DevSecOps4.cshtml.cs
, replacing the generic catch with specific ones.
-
Copy modified line R224 -
Copy modified line R226 -
Copy modified lines R228-R243
@@ -221,10 +221,26 @@ | ||
_logger.LogWarning($"File access granted to: {fullPath}"); | ||
} | ||
} | ||
catch (Exception ex) | ||
catch (UnauthorizedAccessException ex) | ||
{ | ||
_logger.LogError($"Path traversal demonstration error: {ex.Message}"); | ||
_logger.LogError($"Path traversal demonstration error (unauthorized access): {ex.Message}"); | ||
} | ||
catch (DirectoryNotFoundException ex) | ||
{ | ||
_logger.LogError($"Path traversal demonstration error (directory not found): {ex.Message}"); | ||
} | ||
catch (FileNotFoundException ex) | ||
{ | ||
_logger.LogError($"Path traversal demonstration error (file not found): {ex.Message}"); | ||
} | ||
catch (PathTooLongException ex) | ||
{ | ||
_logger.LogError($"Path traversal demonstration error (path too long): {ex.Message}"); | ||
} | ||
catch (IOException ex) | ||
{ | ||
_logger.LogError($"Path traversal demonstration error (IO error): {ex.Message}"); | ||
} | ||
} | ||
|
||
private void DemonstrateXSS(string userContent) |
This PR implements a comprehensive DevSecOps 4.0 demonstration page showcasing GitHub Advanced Security (GHAS) capabilities with intentionally vulnerable code patterns for security scanning detection.
What's Added
New DevSecOps4 Page (
/DevSecOps4
)Backend Implementation with Security Vulnerabilities
The
DevSecOps4Model
class demonstrates multiple vulnerability patterns that GHAS can detect:Package Updates
Newtonsoft.Json
to version 12.0.2 as specifiedSystem.Text.Json
8.0.4 andMicrosoft.Data.SqlClient
5.0.2UI/UX Improvements
Technical Details
ILogger Implementation
Comprehensive logging throughout all methods for:
Security Vulnerability Categories
Testing Verification
This implementation provides a comprehensive demonstration environment for showcasing GHAS capabilities while maintaining educational value for security awareness training.
Fixes #85.
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.