π Secure .NET 8 Web API using GitHub Copilot, CodeQL, Dependabot, and GitHub Actions. Learn to identify and fix vulnerabilities, enforce CI security, and automate safe development practices.
Component | Description |
---|---|
App | .NET 8 Web API (UserApp ) |
Focus | Secure development with CodeQL, Copilot, and GitHub Advanced Security |
Tools | GitHub Copilot, CodeQL, Dependabot, GitHub Actions, markdownlint |
Security | Secret Scanning, Push Protection, Custom CodeQL Queries, Org Policies |
DevOps | CI/CD workflows, vulnerability detection, dependency management |
Docs | XML Comments, Markdown Docs, Security Policies, GraphQL Querying |
- Scaffold a .NET 8 Web API project (
UserApp
) - Implement clean architecture: model, service, controller
- Inject insecure logic (e.g. hardcoded secrets, raw SQL)
- Refactor with Copilot using secure coding practices
- Load secrets via
appsettings.json
and configuration - Add required NuGet packages (e.g., SqlClient)
- Document APIs with XML comments
- Create
INSTRUCTIONS.md
andCONTRIBUTING.md
- Add CI pipeline with GitHub Actions for build/test
- Enable secret scanning, push protection, and dependency graph
- Configure CodeQL static analysis for C#
- Build a custom CodeQL query to detect hardcoded secrets
- Add Dependabot for NuGet security updates
- Enforce organization-wide CodeQL policies (GHES)
- Query vulnerability alerts via GitHub Security GraphQL API
Copilot Prompt:
Provid CLI commands to create a .NET 8 Web API project calledUserApp
in the current directory.
Then create the folders:Models
,Services
, andControllers
inside the project folder.
# Create a new .NET 8 Web API project named UserApp
dotnet new webapi -n UserApp
# Create the Models, Services, and Controllers folders inside the UserApp project
mkdir -p UserApp/Models UserApp/Services UserApp/Controllers
*Copilot Prompt:
Create the filesUser
class,UserService
class, andUserController
class
Provide code forUser
modeel,UserService
andUserController
with/api/user?id=
endpoint.
public class User
{
public int Id { get; set; }
public string Email { get; set; }
public string Name { get; set; }
}
using UserApp.Models;
namespace UserApp.Services
{
public class UserService
{
private static readonly List<User> Users = new()
{
new User { Id = 1, Name = "Alice", Email = "[email protected]" },
new User { Id = 2, Name = "Bob", Email = "[email protected]" }
};
public User? GetUserById(int id)
{
return Users.FirstOrDefault(u => u.Id == id);
}
}
}
using Microsoft.AspNetCore.Mvc;
using UserApp.Models;
using UserApp.Services;
namespace UserApp.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
private readonly UserService _userService;
public UserController()
{
_userService = new UserService();
}
[HttpGet]
public IActionResult GetUser([FromQuery] int id)
{
var user = _userService.GetUserById(id);
if (user == null)
{
return NotFound();
}
return Ok(user);
}
}
}
Copilot Prompt:
Create anappsettings.json
file with a customApiKey
and a connection string namedDefault
for a local SQL Server.
{
"ApiKey": "sk_secure_configured_123",
"ConnectionStrings": {
"Default": "Server=.;Database=TestDb;Trusted_Connection=True;"
}
}
Copilot Prompt:
Add the required lines inProgram.cs
to enable MVC controller support in a .NET 8 Web API using:builder.Services.AddControllers()
andapp.MapControllers()
.
builder.Services.AddControllers();
// ...existing code...
app.MapControllers();
Copilot Prompt:
Run the app
dotnet run --project UserApp/UserApp.csproj
Copilot Prompt:
Modify the existingUserController
to simulate insecure logic:
- Add a hardcoded API key as a static string.
- Add a second GET method that retrieves a user by email using raw SQL string concatenation.
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
private static string API_KEY = "sk_test_123";
// ... existing code ...
// Insecure endpoint: GET /api/user/[email protected]
[HttpGet("email")]
public IActionResult GetUserByEmail([FromQuery] string email)
{
// Insecure: raw SQL query with user input
var query = $"SELECT * FROM Users WHERE Email = '{email}'";
// Simulate execution (do not actually run SQL)
return Ok(new { ApiKey = API_KEY, Query = query });
}
}
Copilot Prompt:
- Identify if the SQL logic is vulnerable to injection.
- Refactor the method to use
SqlCommand
with parameterized queries.- Move the hardcoded
API_KEY
intoappsettings.json
.- Inject the configuration value using
IConfiguration
and replace the static key.
{
...
// Secure endpoint: GET /api/user/[email protected]
[HttpGet("email")]
public IActionResult GetUserByEmail([FromQuery] string email)
{
// Secure: use parameterized query
var apiKey = _config["ApiKey"];
var connStr = _config.GetConnectionString("Default");
var query = "SELECT * FROM Users WHERE Email = @email";
// Simulate parameterized SQL command
return Ok(new { ApiKey = apiKey, Query = query, Parameter = email, ConnectionString = connStr });
}
}
βΉοΈ Copilot Prompt:
Which NuGet package is required to useSqlConnection
andSqlCommand
in a .NET 8 Web API?
Add the correct package using the CLI.
dotnet add package Microsoft.Data.SqlClient
- Enables use of ADO.NET classes like
SqlConnection
andSqlCommand
- Required to connect to and query SQL Server databases
- Supports secure database access with parameterized queries
- Compatible with .NET 8 and Microsoft SQL Server features
Copilot Prompt:
Add XML documentation toUserController
methods.
- Include a summary that explains what the method does.
- Describe the query parameter clearly.
- Mention the possible return values and relevant HTTP status codes.
/// <summary>
/// Retrieves a user by their unique ID.
/// </summary>
/// <param name="id">Query parameter: the unique identifier of the user.</param>
/// <returns>
/// 200 OK with user data if found, or 404 Not Found if no user exists for the given ID.
/// </returns>
Copilot Prompt:
- "Create INSTRUCTIONS.md with project setup and usage"
- "Generate CONTRIBUTING.md for new developers"
INTRUCTIONS.md
CONTRIBUTING.md
Copilot Prompt:
Create a GitHub Actions workflow.github/workflows/ci.yaml
for a .NET 8 Web API project.
- The project is located in the
UserApp
folder.- Use
dotnet restore
,dotnet build
, anddotnet test
.- Trigger the workflow on
push
andpull_request
to themain
branch.- Use
ubuntu-latest
as the runner.
name: .NET CI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 8.0.x
- name: Restore
run: dotnet restore UserApp/UserApp.csproj
- name: Build
run: dotnet build UserApp/UserApp.csproj --no-restore
- name: Test
run: dotnet test UserApp/UserApp.csproj --no-build --verbosity normal
Copilot Prompt:
Provide the commit sequence to push the changes to main
git add .
git commit -m ".Net CI"
git push origin main
Copilot Prompt:
Generate step-by-step instructions for enabling GitHub security features:
- Enable Secret Scanning.
- Enable Push Protection under Secret Scanning.
- Enable the Dependency Graph.
- Include optional steps for enabling these features organization-wide (GitHub Enterprise).
Format the steps as clearly labeled bullet points.
-
Go to your repository β Settings β Security β Advanced Security
-
Enable the following:
- β Secret scanning
- β Push protection
- β Dependency graph
- β Dependabot alerts
- β Dependabot security updates
- β Dependabot version updates
- β Code scanning (enable CodeQL analysis for C#)
- β Copilot Autofix (optional, if CodeQL is set up)
-
(Optional): Enable Private vulnerability reporting
Copilot Prompt:
Create a GitHub Actions workflow for CodeQL.github/workflows/codeql
analysis in a .NET 8 project.
- The project is located in the
UserApp
folder.- Use
dotnet build
to compileUserApp.csproj
.- Trigger the workflow on push and pull request to the
main
branch.- Enable permissions for
contents: read
andsecurity-events: write
.- Configure CodeQL to analyze C# and include a custom config file (
.github/codeql/config.yml
)- The config file should enable
security-extended
queries and support custom queries.
name: CodeQL Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
security-events: write
jobs:
analyze:
name: CodeQL Analyze C#
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
- uses: github/codeql-action/init@v3
with:
languages: csharp
config-file: .github/codeql/config.yml
- run: dotnet build UserApp/UserApp.csproj --configuration Release
- uses: github/codeql-action/analyze@v3
- Analyzes source code for security vulnerabilities (e.g., injection, secrets)
- Enables static analysis during CI to catch issues before merge
- Supports built-in and custom security queries
- Integrates with GitHub Security for inline PR feedback
Copilot Prompt:
Modify the.github/codeql/config.yml
for a .NET 8 project using CodeQL.
- Enable the
security-extended
query suite.- Use the custom queries in the
.github/codeql/queries
folder.- Set the source path to
UserApp/
.- Ignore test folders and generated code.
- Use standard YAML formatting with proper indentation.
name: "CodeQL Config"
disable-default-queries: false
queries:
- uses: security-extended
- uses: ./.github/codeql/queries
paths:
- UserApp/
paths-ignore:
- '**/test/**'
- '**/obj/**'
- '**/bin/**'
- Customizes CodeQL behavior for your repo
- Enables advanced query packs like
security-extended
- Includes your own custom queries (e.g., hardcoded secrets)
- Scopes analysis to relevant folders (e.g.,
UserApp/
) - Ignores noisy or irrelevant paths like
test/
,obj/
, andbin/
π Structure:
.github/
βββ codeql/
βββ config.yml
βββ qlpack.yml
βββ queries/
βββ FindHardcodedSecrets.ql
Copilot Prompt:
Create a custom CodeQL query namedFindHardcodedSecrets.ql
for C# to detect hardcoded secrets.
- Target fields that are initialized with StringLiteral.
- Match field names containing
apiKey
,token
,secret
,password
, orauth
(case-insensitive).- Match values that resemble secrets, such as those starting with
sk_
,token_
,apikey_
, or 32+ base64-like characters.- Use
Field
andLiteral
from thecsharp
CodeQL library.- Return the matched string literal and a message indicating a hardcoded secret.
- Include standard CodeQL metadata:
@name
,@description
,@id
,@tags
,@problem.severity
, and@security-severity
.
/**
* @name Hardcoded secrets in C# code
* @description Finds string literals that may contain hardcoded secrets.
* @kind problem
* @problem.severity warning
* @security-severity 8.0
* @id cs/hardcoded-secrets
* @tags security, external/cwe/cwe-798
*/
import csharp
from StringLiteral s
where
s.getValue().regexpMatch("(?i)(sk_[a-z0-9]{10,}|api[_-]?key|token|secret|[A-Za-z0-9+/=]{32,})")
select s, "π Possible hardcoded secret: '" + s.getValue() + "'"
- Detects hardcoded secrets (API keys, tokens, passwords) in C# source code
- Focuses on fields initialized with suspicious string literals
- Helps identify security risks before code is merged
- Complements GitHub Advanced Security with custom rules
- Can be extended or reused across multiple projects
Copilot Prompt:
Create aqlpack.yml
file for a custom CodeQL query pack targeting C#.
- Set the name to
userapp/secrets
.- Use version
0.0.1
.- Mark it as a library.
- Add a dependency on
codeql/csharp-all
.
name: userapp/secrets
version: 0.0.1
library: true
dependencies:
codeql/csharp-all: "*"
- Defines metadata for the custom query pack (name, version, type)
- Declares dependencies needed to analyze C# code (
codeql/csharp-all
) - Allows CodeQL CLI and GitHub Actions to discover and run your custom query
- Enables reuse and packaging of queries in other repositories
- Required for integrating the query into
.github/codeql/config.yml
Copilot Prompt:
Create adependabot.yml
file to enable daily NuGet dependency updates for a .NET 8 project in theUserApp
folder.
- Limit open PRs to 5.
- Add labels:
dependencies
andautomerge
.- Prefix commit messages with
π¦ deps:
.
version: 2
updates:
- package-ecosystem: "nuget"
directory: "/UserApp"
schedule:
interval: "daily"
labels:
- "dependencies"
- "automerge"
open-pull-requests-limit: 5
commit-message:
prefix: "π¦ deps:"
- Keeps NuGet dependencies in the
UserApp
project up to date - Automatically detects outdated or vulnerable packages
- Opens pull requests with recommended updates
- Reduces manual effort in dependency management
- Helps maintain security posture with regular updates
- Works seamlessly with GitHub Actions and
automerge
labels
Copilot Prompt:
- Create a CodeQL policy file at
.github/codeql/org-policy.yml
- Enforce detection of hardcoded secrets in
UserApp/**/*.cs
- Use custom query:
org/[email protected]/csharp/security/FindHardcodedSecrets.ql
- Set severity to
error
and mode toblock
- Add a message with secure development remediation steps
# .github/codeql/org-policy.yml
name: "Org-Wide CodeQL Policy"
disable-default-queries: false
queries:
- uses: org/[email protected]/csharp/security/FindHardcodedSecrets.ql
- uses: security-and-quality
languages:
- csharp
paths:
- '**/*.cs'
rules:
- id: cs/hardcoded-secrets
severity: error
paths:
- '**/*.cs'
mode: block
message: |
β Hardcoded secrets detected. Please:
1. Remove embedded credentials
2. Use environment variables or secrets config
3. Follow secure development best practices
- β Enforce security standards across all repositories in your GitHub organization
- π Detect hardcoded secrets and other vulnerabilities using custom queries
- π« Block pull requests if violations are found, preventing insecure code merges
- π‘οΈ Centralize policy enforcement using a shared
.github/codeql/org-policy.yml
file - π Improve code quality and reduce security risks at scale with automated analysis
Copilot Prompt:
Write a GitHub GraphQL query to retrieve open vulnerability alerts for a .NET 8 repository.
- Include NuGet package name, ecosystem, severity, advisory description, and GHSA ID.
- Include file path (
vulnerableManifestPath
) and first patched version.- Add timestamps:
createdAt
,updatedAt
, anddismissedAt
.- Include external advisory references (CVE links).
query VulnerabilityAlerts {
repository(owner: "YOUR_ORG", name: "UserApp") {
vulnerabilityAlerts(first: 100, states: OPEN) {
nodes {
vulnerableManifestPath
createdAt
updatedAt
dismissedAt
securityVulnerability {
package {
name
ecosystem
}
severity
advisory {
description
ghsaId
references {
url
}
identifiers {
type
value
}
}
firstPatchedVersion {
identifier
}
}
}
}
}
}
- Queries open security vulnerabilities in the repository's dependencies
- Identifies affected NuGet packages declared in
.csproj
files - Provides metadata like severity, advisory details, and remediation version
- Enables custom reporting, dashboards, or automation scripts
- Useful for organization-wide security enforcement (GitHub Enterprise only)
- Complements CodeQL and Dependabot by offering centralized vulnerability visibility
-
Open GraphQL Explorer
Navigate to https://docs.github.com/en/graphql/overview/explorer -
Sign In with GitHub
Authenticate using a GitHub account that has access to theUserApp
repository. -
Copy and Paste the GraphQL Query
Ensure you replace placeholder values likeYOUR_ORG
andUserApp
with actual values. -
Click βΆ Run
Review the security vulnerability results returned by the query.
-
Create a Query File
Save your GraphQL query in a file (e.g.,.github/security/vulnerability-query.graphql
). -
Generate a GitHub Personal Access Token (PAT)
Ensure it has these scopes:read:org
security_events
read:packages
-
Export Your Token (optional but recommended)
export GH_TOKEN=ghp_your_personal_access_token
-
Execute the curl command
curl -H "Authorization: bearer $GH_TOKEN" \
-H "Content-Type: application/json" \
-X POST \
--data-binary @<(jq -Rs '{query: .}' .github/security/vulnerability-query.graphql) \
https://api.github.com/graphql
- View the JSON Response Inspect vulnerability alerts, severity, affected packages, and patch versions.
Step | Feature | Tool / Prompt | β Outcome |
---|---|---|---|
1 | Scaffold Project | dotnet new webapi |
Ready-to-code API |
2 | Clean Architecture | Copilot | Model, Service, Controller |
3 | Config + Secrets | Copilot Chat | appsettings + IConfiguration |
4 | Vulnerable Logic | Manual | Insecure logic w/ hardcoded key |
5 | Copilot Refactor | Copilot Chat | Safer SQL + secrets |
6 | NuGet Packages | dotnet add package | Installed SqlClient |
7 | XML Docs | /// |
Method doc added |
8 | Markdown Docs | Copilot Chat | README + CONTRIB |
9 | CI Workflow | GitHub Actions | Build + Test |
10 | GitHub Security Settings | GitHub UI | Secret scanning, push protection |
11 | CodeQL Scan | GitHub Actions | Static security scan |
12 | Custom CodeQL Query | Copilot + CodeQL | Detect secrets in C# |
13 | Dependabot | GitHub Config | Auto package updates |
14 | Org Policy (GHES) | CodeQL | Enforced org-wide rule |
15 | Security API (GHES) | GraphQL | Query vulnerabilities |
This repository demonstrates secure coding practices for .NET 8 Web APIs using GitHub Copilot, CodeQL, Dependabot, and GitHub Advanced Security β with real-world workflows for secret protection, refactoring, and automated CI/CD.