LeakGuard 0.4.0

dotnet add package LeakGuard --version 0.4.0
                    
NuGet\Install-Package LeakGuard -Version 0.4.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="LeakGuard" Version="0.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LeakGuard" Version="0.4.0" />
                    
Directory.Packages.props
<PackageReference Include="LeakGuard" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LeakGuard --version 0.4.0
                    
#r "nuget: LeakGuard, 0.4.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package LeakGuard@0.4.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=LeakGuard&version=0.4.0
                    
Install as a Cake Addin
#tool nuget:?package=LeakGuard&version=0.4.0
                    
Install as a Cake Tool

LeakGuard

Prevent sensitive data from leaking into your .NET logs.

LeakGuard is a developer-first Roslyn analyzer that detects when values marked as PII, secrets, or sensitive data are passed directly to common logging APIs. Diagnostics appear at compile time in your IDE and in CI/CD pipelines — before sensitive data ever reaches production logs.

v0.4 adds object logging protection (LG004) so entire objects whose types contain LeakGuard-classified members are flagged when passed to ILogger.

v0.3 adds intra-procedural data-flow detection so sensitive values tracked through local variables, common string operations, and control-flow joins are flagged before they reach logging sinks.

v0.2 adds runtime redaction helpers and IDE quick fixes so you can resolve warnings safely without hand-editing every call site.

The problem

public sealed class Customer
{
    [Pii(PiiType.Email)]
    public string Email { get; init; } = "";
}

logger.LogInformation(
    "Created customer {Email}",
    customer.Email);

LG001: PII value Customer.Email may be exposed in logging.

Without LeakGuard, this compiles cleanly. With LeakGuard, you get an immediate, actionable warning pointing at the sensitive expression.

Quick fix (Visual Studio / Rider)

Place the caret on the warning and choose the light bulb quick action:

Redact email

logger.LogInformation(
    "Created customer {Email}",
    customer.Email.RedactEmail());

At runtime:

Created customer lu***@example.com

Diagnostics and quick fixes work in Visual Studio, JetBrains Rider, dotnet build, and CI.

Why LeakGuard?

  • Explicit by design — Sensitivity is declared with attributes, not guessed from property names. Low false positives.
  • Compile-time safety — Catches leaks before they ship.
  • Actionable fixes — Redact email, phone, secrets, and generic sensitive values with one click.
  • Runtime redaction — Best-effort masking when you must include a placeholder in structured logs.
  • Incremental adoption — Mark sensitive members as you go; no logging framework changes required.
  • Small and fast — A focused Roslyn analyzer; analysis stays lightweight.
  • Ecosystem-friendly — Complements Microsoft.Extensions.Logging; not a replacement for it.

Installation

dotnet add package LeakGuard

One package includes attributes, runtime redaction helpers, Roslyn analyzers (LG001–LG004), and IDE code fixes.

Quick start

using LeakGuard;
using Microsoft.Extensions.Logging;

public sealed class Customer
{
    [Pii(PiiType.Email)]
    public string Email { get; init; } = "";
}

public class CustomerService(ILogger<CustomerService> logger)
{
    public void Create(Customer customer)
    {
        // Safe
        logger.LogInformation("Customer created {CustomerId}", customer.Id);

        // Warning LG001: PII value 'customer.Email' may be exposed in logging.
        logger.LogInformation("Created customer {Email}", customer.Email);
    }
}

Apply Redact email, rebuild, and the warning disappears.

Data-flow detection

LeakGuard tracks sensitive values through local assignments and common value-preserving operations within a single method.

[Pii(PiiType.Email)]
public string Email { get; init; } = "";

var email = customer.Email;
var normalized = email.Trim().ToLowerInvariant();

logger.LogInformation(
    "Customer {Email}",
    normalized);

LG001: PII value originating from Customer.Email may be exposed in logging.

Supported propagation includes:

  • Local variables and reassignment (email = customer.Email, then copy = email)
  • String operations such as Trim, ToLowerInvariant, Replace, and Substring
  • String interpolation and concatenation
  • Conditional expressions and null-coalescing (condition ? customer.Email : safe, customer.Email ?? fallback)
  • Sensitive parameters marked with [Pii], [Secret], or [SensitiveData]
  • Basic control-flow joins (if one branch assigns sensitive data, the merged value is treated as potentially sensitive)
  • Sanitized values via LeakGuard's own Redact* extension methods break taint

Classification merge precedence when branches differ: Secret > Pii > SensitiveData.

Data-flow limitations (v0.3)

Current analysis is intra-procedural only. LeakGuard does not yet track sensitive data through:

  • Arbitrary method calls (unless they are known taint-preserving string operations)
  • Cross-method or cross-file data flow
  • Repository/service boundaries, DTO mapping, serialization, or collection contents
  • Reflection or external libraries
  • Captured locals in complex closures, async continuations, or loops with back-edges (best-effort only)
var email = Normalize(customer.Email); // custom helper
logger.LogInformation("{Email}", email); // not tracked unless Normalize is recognized

Direct logging of marked members continues to work alongside data-flow detection.

Object logging protection

Developers often log entire objects without realizing the type contains sensitive members — or sensitive properties may be added later:

public sealed class Customer
{
    public Guid Id { get; init; }

    [Pii(PiiType.Email)]
    public string Email { get; init; } = "";

    [Pii(PiiType.Phone)]
    public string Phone { get; init; } = "";
}

logger.LogInformation(
    "Customer {@Customer}",
    customer);

LG004: Object 'Customer' contains sensitive data and may be exposed in logging.

This is separate from value-level diagnostics:

Scenario Diagnostic
logger.LogInformation("{Email}", customer.Email) LG001 on customer.Email
logger.LogInformation("{@Customer}", customer) LG004 on customer
logger.LogInformation("{@Product}", product) No diagnostic when Product has no LeakGuard attributes

LG004 inspects the compile-time type of each logging argument for instance properties and fields marked with [Pii], [Secret], or [SensitiveData], including members inherited from base types. Records and local aliases (var value = customer) are supported.

Object logging limitations (v0.4)

  • Direct and inherited members only — no recursive object-graph traversal (e.g. Order.Customer.Email through nested objects)
  • No collection element inspection (List<Customer> is not analyzed)
  • Compile-time type only — no speculative diagnostics for object or broad interface types without classified members
  • Does not replace LG001–LG003 for individual sensitive values

Runtime redaction

The LeakGuard package provides small, dependency-free extension methods:

Method Use for Example output
RedactEmail() Email addresses lu***@example.com
RedactPhone() Phone numbers 070****567
Redact() Generic sensitive data ***
RedactSecret() Secrets (API keys, passwords, tokens) ***

Nullable strings are handled naturally — null stays null; empty strings stay empty.

Email masking

Algorithm (deterministic, never throws):

  • Keep the domain after @
  • Keep up to two characters of the local part, then mask the rest with ***
  • Fall back to generic *** when no @ is found
Input Output
ludvig@example.com lu***@example.com
ab@example.com a***@example.com
a@example.com ***@example.com
invalid-email ***

Phone masking

  • Extract digits, keep a short prefix (up to 3 digits) and suffix (3 digits)
  • Mask middle digits in place, preserving separators where present
  • Not a full international phone parser — intentionally simple and predictable
Input Output
0701234567 070****567
+46701234567 +467*****567

Secret handling

RedactSecret() always returns ***. No prefix or suffix of the original value is preserved.

Prefer not logging secrets at all. Redaction is a last resort when a log line must retain a placeholder.

Code fixes

The package includes a Roslyn CodeFixProvider for:

Diagnostic Quick fix Result
LG001 + [Pii(PiiType.Email)] Redact email .RedactEmail()
LG001 + [Pii(PiiType.Phone)] Redact phone number .RedactPhone()
LG001 + other PII types Redact sensitive value .Redact()
LG002 + [Secret] Redact secret .RedactSecret()
LG003 + [SensitiveData] Redact sensitive value .Redact()

Fix All is supported via BatchFixer for multiple occurrences of the same fix.

When using LeakGuard; is missing, the fix adds it automatically (including file-scoped namespaces).

Already-redacted values do not trigger diagnostics:

logger.LogInformation("{Email}", customer.Email.RedactEmail()); // OK

Redaction calls are recognized by semantic symbol matching on LeakGuard's own extension methods — unrelated user methods named Redact() are not trusted automatically.

PII vs SensitiveData vs Secret

Attribute Purpose Typical examples
[Pii] / [Pii(PiiType.*)] Personally identifiable information Email, phone, name, address
[SensitiveData] Sensitive business data that must not appear in logs Internal notes, contract terms
[Secret] Credentials and tokens API keys, passwords, access tokens

Attributes can be applied to properties, fields, parameters, and return values.

PiiType values

Unknown, Name, Email, Phone, Address, PersonalIdentifier, Financial, Location

Specialized redactors for Name, Address, and other types may be added later. They currently use generic Redact().

Supported logging APIs

LeakGuard analyzes direct arguments passed to Microsoft.Extensions.Logging.ILogger extension methods:

  • LogTrace, LogDebug, LogInformation, LogWarning, LogError, LogCritical

Both standard and Exception-first overloads are supported. Methods are identified semantically via Roslyn symbols.

Diagnostics

ID Severity Message
LG001 Warning PII value '{member}' may be exposed in logging.
LG001 Warning PII value originating from '{source}' may be exposed in logging. (propagated)
LG002 Warning Secret value '{member}' may be exposed in logging.
LG002 Warning Secret value originating from '{source}' may be exposed in logging. (propagated)
LG003 Warning Sensitive data value '{member}' may be exposed in logging.
LG003 Warning Sensitive data value originating from '{source}' may be exposed in logging. (propagated)
LG004 Warning Object '{type}' contains sensitive data and may be exposed in logging

Diagnostics highlight the sensitive expression (e.g. customer.Email) or the logged object (e.g. customer), not the entire logging call.

Analyzer behavior

  • Attribute-based only — Property names like Email or Password are not classified automatically.
  • Direct, data-flow, and object logging — Individual sensitive values, tracked locals, and whole objects whose types contain classified members are flagged.
  • Redaction-aware — LeakGuard's own Redact* extension methods break taint and are treated as safe.
  • Generated code excluded — Source generators and generated files are not analyzed.
  • Concurrent and scoped — Methods containing logging sinks are analyzed with CFG-based intra-procedural taint tracking.

This is flagged in v0.3:

var email = customer.Email;
logger.LogInformation("{Email}", email); // LG001

Reassignment is handled conservatively:

var email = customer.Email;
email = "safe";
logger.LogInformation("{Email}", email); // OK when overwrite is proven

Security considerations

LeakGuard is a developer safety tool. It is not:

  • a complete GDPR compliance solution
  • a DLP system
  • a guarantee that sensitive data can never leak
  • a substitute for correct logging policies
  • a substitute for access controls

Runtime redaction algorithms produce best-effort safe representations for logs. They reduce accidental exposure but do not remove the underlying risk of logging sensitive data.

Secrets should ideally not be logged at all. Use redaction only when a placeholder is unavoidable, and prefer removing the secret from the log message entirely.

Performance

LeakGuard follows Roslyn analyzer best practices:

  • Concurrent execution enabled
  • Generated code excluded from analysis
  • Methods containing logging sinks are analyzed with CFG-based data-flow tracking; others are skipped early
  • No compilation-wide scans per syntax tree

The analyzer is designed to be invisible during normal IDE usage. Code fixes run only when you invoke them.

Roadmap

v0.4 (current)

  • Object logging protection (LG004) for types containing LeakGuard-classified members
  • Compilation-scoped type inspection cache for performance

v0.3

  • Intra-procedural data-flow / taint tracking for locals, string ops, and control-flow joins
  • Propagated diagnostic messages that reference the original sensitive source

v0.2

  • Runtime redaction API (Redact, RedactEmail, RedactPhone, RedactSecret)
  • IDE code fixes for LG001/LG002/LG003
  • Redaction-aware analyzer (no repeated diagnostics)

v0.4

  • Serilog support
  • OpenTelemetry support

v1.0

  • Stable analyzer API
  • Configurable policies
  • Custom sensitive attributes

Contributing

Contributions are welcome! Please open an issue to discuss significant changes before submitting a pull request.

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for analyzer and code-fix behavior
  4. Ensure dotnet build and dotnet test pass
  5. Open a pull request

License

MIT — see LICENSE.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.4.0 115 8/6/2026