Cyber Security  

GitHub Code Scanning: Using Mitigated Alerts Without Hiding Security Risk

Introduction

Security scanning is useful only when developers can understand what the results actually mean.

In a real repository, a security scanner may report hundreds of alerts over time. Some are new, some are already being fixed, and others may have been investigated and determined to be acceptable risks for a particular application.

This creates an important challenge:

How do you remove an alert from the active security queue without pretending that the underlying risk never existed?

GitHub's code-scanning experience introduces more detailed handling for mitigated alerts, including additional reasoning around why an alert was dismissed or otherwise treated as no longer requiring active remediation.

This distinction is important for security teams because:

Alert removed from active queue
        ≠
Security risk never existed

A mature security process should preserve the reasoning behind a decision instead of simply making an alert disappear.

What Is a Code-Scanning Alert?

A code-scanning alert represents a potential security problem identified during static analysis.

For example, imagine a .NET application containing:

public string BuildQuery(string customerId)
{
    return "SELECT * FROM Customers WHERE Id = '"
        + customerId
        + "'";
}

A security analyzer may identify a potential SQL injection problem.

The important information is not only:

SQL Injection Detected

It also includes:

  • Where the problem exists

  • How the data flows

  • What code is affected

  • What severity is assigned

  • Whether the issue is reachable

  • Whether remediation is required

A useful security workflow therefore looks like:

Code
 |
 v
Security Analysis
 |
 v
Alert
 |
 v
Investigation
 |
 +---- True Risk ----> Remediate
 |
 +---- Acceptable ----> Document Decision
 |
 +---- False Positive -> Dismiss With Reason

Why Alert Management Matters

Imagine a repository with:

1,000 security alerts

If every alert remains active forever, developers eventually face alert fatigue.

They may stop paying attention to the dashboard.

That creates another security problem:

Too Many Alerts
      |
      v
Less Attention
      |
      v
Important Alerts Get Missed

The answer is not to suppress everything.

The answer is to distinguish between:

Active Risk
Resolved Risk
Accepted Risk
False Positive

This allows teams to focus attention where it matters.

What Does "Mitigated" Mean?

A mitigated alert is an alert where the immediate security concern has been addressed or is no longer considered an active remediation item.

That does not necessarily mean:

"This code was always safe."

It can instead mean:

"The organization investigated this alert
and has a documented reason for its current state."

For example, suppose a scanner reports a potential vulnerability in a legacy integration.

After investigation, the team determines that:

  • The vulnerable path is unreachable

  • The input is constrained

  • A compensating control exists

  • The component is isolated

  • The system is scheduled for replacement

The team may decide that immediate remediation is not required.

That decision should be recorded.

Mitigated vs False Positive

These concepts should not be treated as identical.

False Positive

A false positive means the scanner identified something that is not actually a security problem in the application's context.

For example:

Scanner:
Potential SQL Injection

Investigation:
Input is never user-controlled and is
generated from a fixed internal enumeration.

The alert may be a false positive.

Mitigated Risk

A mitigated risk means there was a legitimate security concern or potential concern, but another control or change reduces the risk to an acceptable level.

For example:

Potentially dangerous operation
        |
        v
Additional validation
        |
        v
Risk reduced

The distinction is important for auditability.

Why Dismissal Reasons Matter

Suppose an alert is dismissed with:

Reason: Won't Fix

That tells the next developer very little.

A better record might explain:

This endpoint is accessible only from the
internal processing network. The input is
validated against the approved customer
identifier format before reaching the query.
The application is also scheduled for migration
to parameterized queries.

Now another security engineer can understand the decision.

Good security records answer:

What was found?
Why was it investigated?
What did we determine?
What control exists?
Why is remediation not happening now?

A Practical .NET Example

Consider:

public async Task<Customer?> FindCustomerAsync(
    string customerId)
{
    var sql =
        $"SELECT * FROM Customers WHERE Id = '{customerId}'";

    return await database.QuerySingleOrDefaultAsync<Customer>(
        sql);
}

A security scanner could identify this as a potential injection vulnerability.

The preferred fix is to parameterize the query:

public async Task<Customer?> FindCustomerAsync(
    string customerId)
{
    const string sql =
        "SELECT * FROM Customers WHERE Id = @CustomerId";

    return await database.QuerySingleOrDefaultAsync<Customer>(
        sql,
        new { CustomerId = customerId });
}

After the change:

Security Alert
      |
      v
Code Fixed
      |
      v
Scan Again
      |
      v
Alert Resolved

This is different from simply dismissing the alert.

Whenever practical, fixing the underlying vulnerability should remain the preferred option.

When Should an Alert Be Mitigated?

There are legitimate situations where immediate code modification may not be the best answer.

Examples include:

Legacy System

A critical legacy component may be scheduled for replacement.

Current System
      |
      v
Temporary Compensating Control
      |
      v
Migration

Unreachable Code Path

The scanner may identify a potentially dangerous path that cannot be reached under the application's actual architecture.

External Protection

A vulnerability may be mitigated by a control outside the application.

For example:

Application
    |
    v
Internal Network
    |
    v
Restricted Service

Accepted Risk

The organization may consciously accept a documented risk based on business or technical considerations.

The important part is that acceptance should be deliberate, not automatic.

Never Use Mitigation to Hide a Problem

There is a major difference between:

Investigate → Document → Mitigate

and:

Alert appears → Dismiss → Forget

The second process creates security debt.

A mature process should require enough information to justify the decision.

For example:

Alert
 |
 +--> Owner
 +--> Severity
 +--> Investigation
 +--> Decision
 +--> Reason
 +--> Compensating Control
 +--> Review Date

This creates traceability.

Security Alert Lifecycle

A useful lifecycle is:

Detected
   |
   v
Triaged
   |
   +---- False Positive
   |          |
   |          v
   |       Document
   |
   +---- Real Risk
              |
              v
         Remediation
              |
              v
           Verify
              |
              v
           Resolved

There can also be another path:

Real Risk
    |
    v
Temporary Mitigation
    |
    v
Accepted / Deferred
    |
    v
Periodic Review
    |
    v
Remediate

The second path is useful for risks that cannot be fixed immediately.

Security Debt Is Still Debt

A mitigated alert should not automatically disappear from engineering awareness.

Suppose a team has:

10 mitigated alerts

That may look good.

But if all 10 are relying on temporary workarounds, the organization still has security debt.

A better dashboard separates:

StateMeaning
OpenRequires investigation or remediation
In ProgressRemediation underway
ResolvedUnderlying issue fixed
False PositiveAlert does not represent a real issue
MitigatedRisk reduced through another control
AcceptedRisk intentionally retained

This provides much better visibility than simply counting active alerts.

Use Severity With Context

Security severity provides useful prioritization.

For example:

Critical
High
Medium
Low

But severity should not be considered in isolation.

Consider:

High Severity
+
Public Internet
+
Authentication Bypass

This deserves immediate attention.

Compare that with:

High Severity
+
Unreachable Internal Code
+
Strong Network Isolation

The technical context is different.

This is why security triage requires both automated analysis and human investigation.

Don't Ignore Business Context

Security decisions sometimes depend on how the application is actually used.

Suppose a scanner identifies:

Potential command injection

A security engineer should ask:

  • Where does the input originate?

  • Is the endpoint publicly accessible?

  • Is authentication required?

  • Is authorization enforced?

  • What permissions does the process have?

  • Is the code path reachable?

  • Is the input validated?

  • What data can the process access?

The alert is the starting point.

It is not the final security conclusion.

Code Scanning and Pull Requests

Code scanning becomes particularly useful when integrated with pull requests.

Consider:

Developer
    |
    v
Pull Request
    |
    v
CodeQL Analysis
    |
    +---- New Alert
    |        |
    |        v
    |      Review
    |
    v
No New Security Finding
    |
    v
Human Approval

A new security finding should receive attention before the change reaches the main branch.

This is especially important for security-sensitive code such as:

  • Authentication

  • Authorization

  • Payments

  • File handling

  • Database access

  • Serialization

  • External command execution

  • CI/CD workflows

Preventing Alert Fatigue

Alert fatigue is one of the biggest problems with security tooling.

Suppose developers see:

Monday    15 alerts
Tuesday   22 alerts
Wednesday 18 alerts
Thursday  30 alerts
Friday    25 alerts

If most alerts are low-value or poorly understood, developers may eventually stop investigating them.

A better strategy is:

Detect
  |
  v
Prioritize
  |
  v
Investigate
  |
  v
Remediate or Document

Quality matters more than raw alert volume.

A Practical Security Triage Process

When a new alert appears, follow a repeatable process.

Step 1: Understand the Alert

Identify:

Rule
File
Line
Severity
Data Flow

Step 2: Confirm Reachability

Ask whether an attacker can actually control the relevant input.

Step 3: Understand the Impact

Determine what could happen if the issue were exploited.

Step 4: Check Existing Controls

Look for:

Validation
Authorization
Network Restrictions
Sandboxing
Encoding
Parameterization

Step 5: Choose an Action

The result should normally be one of:

Fix
Mitigate
Accept
False Positive

Step 6: Document the Decision

Record enough information for another engineer to understand the reasoning.

Step 7: Review Later if Necessary

Temporary mitigations should have an owner and, where appropriate, a review date.

Example: File Path Vulnerability

Consider:

public async Task<byte[]> ReadFileAsync(string fileName)
{
    var path = Path.Combine(
        "/app/uploads",
        fileName);

    return await File.ReadAllBytesAsync(path);
}

A scanner may identify a potential path traversal vulnerability.

An attacker could potentially attempt:

../../sensitive-file

A better implementation validates the requested path.

For example:

public async Task<byte[]> ReadFileAsync(string fileName)
{
    var root = Path.GetFullPath("/app/uploads");

    var path = Path.GetFullPath(
        Path.Combine(root, fileName));

    if (!path.StartsWith(
            root,
            StringComparison.Ordinal))
    {
        throw new SecurityException(
            "Invalid file path.");
    }

    return await File.ReadAllBytesAsync(path);
}

The exact validation strategy should be designed carefully for the application.

The important lesson is that a real vulnerability should normally be fixed rather than dismissed.

Temporary Mitigation Example

Suppose a legacy service cannot be modified immediately.

A compensating control might be:

Internet
   |
   v
Web Application Firewall
   |
   v
Restricted Endpoint
   |
   v
Legacy Service

If the control meaningfully reduces exposure, the security team may temporarily classify the alert as mitigated.

But the mitigation should be documented:

Risk:
Path traversal in legacy endpoint.

Mitigation:
Endpoint accessible only through internal network.

Owner:
Platform Security Team.

Follow-up:
Replace legacy file handler.

The mitigation is a risk-management decision, not a declaration that the vulnerable code is safe.

Common Mistakes

Mistake 1: Dismissing Every Difficult Alert

A difficult alert is not automatically a false positive.

Mistake 2: Using "Won't Fix" Without Explanation

Future developers need to understand the decision.

Mistake 3: Treating Mitigation as Permanent

A temporary control can eventually become forgotten technical debt.

Mistake 4: Ignoring Alert Severity

Prioritize security work based on impact and exploitability.

Mistake 5: Fixing Only the Reported Line

Security vulnerabilities often involve a broader data flow.

Mistake 6: Forgetting Compensating Controls

If a mitigation depends on infrastructure, document that dependency.

Mistake 7: Never Reviewing Old Decisions

Security assumptions can change as applications evolve.

Troubleshooting

ProblemWhat to Check
Too many alertsReview rule configuration and prioritization
Alert appears to be falseTrace the input and execution path
Same alert keeps returningVerify whether the underlying code was actually changed
Developers ignore alertsReduce noise and improve triage
Mitigation is unclearDocument the exact compensating control
Risk becomes higher laterReassess the alert after architecture changes
Alert cannot be resolvedCheck whether the vulnerable code path still exists
Security dashboard looks clean but risk remainsReview mitigated and accepted alerts separately

Best Practices

Fix Real Vulnerabilities Whenever Practical

Mitigation should not become an excuse to avoid remediation.

Document Security Decisions

Every non-remediated security alert should have a meaningful explanation.

Separate False Positives From Accepted Risks

These represent different security decisions.

Track Mitigated Alerts

Do not allow them to disappear from organizational visibility.

Assign Ownership

Someone should be responsible for reviewing important security decisions.

Review Temporary Mitigations

Compensating controls can become outdated.

Use Pull Request Analysis

Detect new security problems before they reach the main branch.

Prioritize by Risk

Consider severity, exploitability, exposure, and business impact.

Combine Automated and Human Analysis

Static analysis finds patterns; engineers provide context.

Advantages

Reduced Security Noise

Proper alert management helps teams focus on meaningful risks.

Better Auditability

Documented decisions provide a history of how security issues were handled.

More Effective Prioritization

Developers can distinguish active vulnerabilities from already-managed risks.

Supports Legacy Systems

Temporary mitigation can provide a practical path when immediate remediation is not possible.

Better Security Governance

Security decisions become explicit rather than informal.

Disadvantages and Limitations

Risk of Overuse

If teams classify too many alerts as mitigated, real security debt can become hidden.

Documentation Overhead

Good security decisions require investigation and explanation.

Mitigations Can Expire

A compensating control may disappear during future architecture changes.

Human Judgment Is Required

A scanner cannot determine every organization's acceptable risk.

Security Status Can Become Stale

An alert that was safe to defer six months ago may become important after the system changes.

Building a Security Alert Review Process

A practical organization-wide process can look like:

New Alert
   |
   v
Security Triage
   |
   +---- False Positive
   |          |
   |          v
   |       Document
   |
   +---- Real Risk
              |
              +---- Fix
              |      |
              |      v
              |   Verify
              |
              +---- Mitigate
              |      |
              |      v
              |   Monitor
              |
              +---- Accept
                     |
                     v
                  Review

This creates a consistent process without requiring every alert to follow exactly the same remediation path.

A Simple Security Decision Template

For every alert that is not immediately fixed, record:

Alert:
[Security rule and description]

Risk:
[What could happen?]

Affected Component:
[Service / repository / endpoint]

Decision:
[False Positive / Mitigated / Accepted]

Reason:
[Why this decision was made]

Compensating Control:
[If applicable]

Owner:
[Responsible team]

Follow-Up:
[Future remediation or review]

This is simple enough to use consistently and detailed enough to preserve context.

Conclusion

Security alerts are useful only when teams can manage them responsibly. A clean code-scanning dashboard does not necessarily mean a completely secure application, just as a large number of alerts does not automatically mean the application is unsafe. The important part is understanding why each significant alert exists and what happened after it was discovered. Real vulnerabilities should be fixed whenever possible, while legitimate false positives, temporary mitigations, and accepted risks should be clearly distinguished and documented. Treat mitigated alerts as part of the security history rather than simply hiding them, and review important decisions again when the application or its threat model changes. This approach gives development and security teams a much clearer picture of the actual risk in their codebase.