AgenticFirewall.Core
1.0.2
dotnet add package AgenticFirewall.Core --version 1.0.2
NuGet\Install-Package AgenticFirewall.Core -Version 1.0.2
<PackageReference Include="AgenticFirewall.Core" Version="1.0.2" />
<PackageVersion Include="AgenticFirewall.Core" Version="1.0.2" />
<PackageReference Include="AgenticFirewall.Core" />
paket add AgenticFirewall.Core --version 1.0.2
#r "nuget: AgenticFirewall.Core, 1.0.2"
#:package AgenticFirewall.Core@1.0.2
#addin nuget:?package=AgenticFirewall.Core&version=1.0.2
#tool nuget:?package=AgenticFirewall.Core&version=1.0.2
AgenticFirewall.Core
A .NET 8 library that validates and sanitizes LLM input and output before it reaches your users. Drop it into any agentic application — no LLM dependency required.
Features
| Layer | What it catches |
|---|---|
| Global blocklist | Prompt injection, jailbreak phrases, credential fishing, destructive commands |
| Role/delimiter injection | Fake conversation-turn markers ([INST], <\|im_start\|>, ### Human:, etc.) |
| Agent-specific blocklists | Domain attacks tailored to 10 built-in agent types |
| Custom banned words | Per-application word lists merged into the validation pipeline at call time |
| Normalization pipeline | Homoglyphs, leet-speak, zero-width chars, Bidi overrides, base64 encoding |
| Sensitive data redaction | SSNs, credit card numbers, API keys |
| Input size enforcement | Configurable character-count cap to block token-flooding attacks |
Installation
NuGet (recommended)
.NET CLI
dotnet add package AgenticFirewall.Core
Package Manager Console
Install-Package AgenticFirewall.Core
PackageReference (csproj)
<PackageReference Include="AgenticFirewall.Core" Version="1.0.0" />
View on NuGet: nuget.org/packages/AgenticFirewall.Core
All dependencies (AhoCorasick, Microsoft.Extensions.DependencyInjection.Abstractions) are pulled in automatically.
Alternative — Project reference (source)
<ProjectReference Include="..\AgenticFirewall.Core\AgenticFirewall.Core.csproj" />
Dependencies: AhoCorasick 2.0.279
Integration
ASP.NET Core / any DI app
Register with one line in Program.cs:
using AgenticFirewall.Core;
builder.Services.AddAgenticFirewall();
Inject IFirewallService wherever you need it:
app.MapPost("/api/chat", (ChatRequest req, IFirewallService firewall) =>
{
firewall.Validate(req.Message, AgentType.AppointmentBooker);
// ...
});
As ASP.NET Core middleware (validates every request automatically):
// Program.cs
app.UseMiddleware<FirewallMiddleware>();
// FirewallMiddleware.cs
public class FirewallMiddleware(RequestDelegate next, IFirewallService firewall, ILogger<FirewallMiddleware> logger)
{
public async Task InvokeAsync(HttpContext context)
{
if (context.Request.Path.StartsWithSegments("/api/chat"))
{
context.Request.EnableBuffering();
using var reader = new StreamReader(context.Request.Body, leaveOpen: true);
var body = await reader.ReadToEndAsync();
context.Request.Body.Position = 0;
try
{
firewall.Validate(body, AgentType.General);
}
catch (SecurityException ex)
{
logger.LogWarning("[Firewall] Blocked: {Message}", ex.Message);
context.Response.StatusCode = 403;
await context.Response.WriteAsJsonAsync(new { blocked = true, reason = ex.Message });
return;
}
}
await next(context);
}
}
Console app / no DI
Use the static Firewall façade — no setup required:
using AgenticFirewall.Core;
try
{
Firewall.Validate(userInput, AgentType.EmailSender);
string safe = Firewall.Redact(userInput);
// forward `safe` to the LLM
}
catch (SecurityException ex)
{
Console.WriteLine($"Blocked: {ex.Message}");
}
Validate LLM output too
Run the same check on the model's response before showing it to the user:
var llmResponse = await GetLlmResponseAsync(userInput);
try
{
Firewall.Validate(llmResponse); // catches data leaks, injected instructions, etc.
Display(llmResponse);
}
catch (SecurityException ex)
{
logger.LogWarning("[Firewall] LLM response blocked: {Message}", ex.Message);
Display("Sorry, that response was blocked by the safety filter.");
}
Manual DI registration (without the extension method)
builder.Services.AddSingleton<IFirewallService, FirewallService>();
API Reference
Firewall (static façade)
Firewall.Validate(string input, InputSizeLimit sizeLimit = Standard);
Firewall.Validate(string input, AgentType agentType, InputSizeLimit sizeLimit = Standard);
Firewall.Validate(string input, IEnumerable<string> customBannedWords, bool isAllCustom = false, InputSizeLimit sizeLimit = Standard);
Firewall.Validate(string input, AgentType agentType, IEnumerable<string> customBannedWords, bool isAllCustom = false, InputSizeLimit sizeLimit = Standard);
string Firewall.Redact(string input);
IFirewallService (DI interface)
void Validate(string input, InputSizeLimit sizeLimit = InputSizeLimit.Standard);
void Validate(string input, AgentType agentType, InputSizeLimit sizeLimit = InputSizeLimit.Standard);
void Validate(string input, IEnumerable<string> customBannedWords, bool isAllCustom = false, InputSizeLimit sizeLimit = InputSizeLimit.Standard);
void Validate(string input, AgentType agentType, IEnumerable<string> customBannedWords, bool isAllCustom = false, InputSizeLimit sizeLimit = InputSizeLimit.Standard);
string Redact (string input);
Both Validate overloads throw System.Security.SecurityException on any detected threat. Redact never throws.
AgentType Enum
Pass an AgentType to layer domain-specific patterns on top of the global blocklist.
| Value | Domain | Extra patterns block |
|---|---|---|
General |
(default) | Global blocklist only |
AppointmentBooker |
Calendar | Bulk export/cancel, cross-user access |
EmailSender |
Mass send, BCC exfiltration, inbox forwarding | |
PaymentProcessor |
Payments | Unauthorized transfers, fraud-check bypass |
CustomerSupport |
CRM | Data dumps, impersonation, mass account changes |
DataAnalyst |
Data / SQL | DROP/TRUNCATE, raw dumps, permission escalation |
CodeAssistant |
Code | Malware, exploit generation, offensive scripts |
ContentModerator |
Moderation | Filter bypass, force-approve harmful content |
HRAssistant |
HR | Salary/PII exfil, disciplinary record access |
MedicalAssistant |
Healthcare | Patient record dumps, unauthorized prescribing |
LegalAssistant |
Legal | Privileged communication leak, case file exfil |
Custom Banned Words
Pass any IEnumerable<string> of application-specific terms to block content that is safe in general but forbidden for your particular product — competitor names, internal project codes, legally sensitive phrases, etc.
Every term in the custom list is normalized through the same pipeline as user input, so leet-speak, homoglyph substitution, invisible characters, and base64 obfuscation are all neutralized before the match.
Global check + custom words
string[] banned = ["competitor", "lawsuit", "confidential"];
firewall.Validate(userInput, banned);
// or static façade:
Firewall.Validate(userInput, banned);
Agent-specific check + custom words
firewall.Validate(userInput, AgentType.CustomerSupport, banned);
// or static façade:
Firewall.Validate(userInput, AgentType.CustomerSupport, banned);
All three layers are evaluated in order:
- Size check — rejects oversized input immediately
- Global blocklist — prompt injection, jailbreak, credential fishing, etc.
- Agent-specific blocklist — domain patterns for the chosen
AgentType - Custom banned words — your application-defined list
A SecurityException is thrown at the first failing layer.
// Example: block mentions of competitor brands in a support bot
string[] brandPolicy = ["AcmeCorp", "RivalSaaS", "OtherVendor"];
app.MapPost("/api/chat", (ChatRequest req, IFirewallService firewall) =>
{
firewall.Validate(req.Message, AgentType.CustomerSupport, brandPolicy);
// ...
});
Obfuscation resistance —
c0mp3t1t0r(leet),соmpetitor(Cyrillic 'с'), andcompetitor(zero-width space) all normalize tocompetitorbefore the match, so users cannot bypass the list through encoding tricks.
isAllCustom — full control mode
Set isAllCustom: true to skip the built-in global and agent-specific blocklists entirely and only enforce your custom list (the size limit still applies). This is ideal for validating LLM responses, where the global blocklist contains common English words like "system", "run", "delete" that would cause false positives in natural-language output.
// Validate the LLM response with only response-specific banned terms
string[] responsePolicy = ["jailbreak", "ignore previous", "[INST]", "<|im_start|>"];
firewall.Validate(llmResponse, responsePolicy, isAllCustom: true);
isAllCustom |
Global blocklist | Agent blocklist | Custom list | Size limit |
|---|---|---|---|---|
false (default) |
Checked | Checked | Checked | Checked |
true |
Skipped | Skipped | Checked | Checked |
InputSizeLimit Enum
The enum value is the character limit — no lookup table needed.
| Value | Chars | Use when |
|---|---|---|
Simple |
250 | Tightly constrained chatbot commands |
Standard (default) |
1 024 | Chat messages, short queries |
Extended |
8 192 | Emails, support tickets, code snippets |
Large |
32 768 | Full documents, large code files |
Oversized inputs are rejected before normalization, so the check is always cheap.
Normalization Pipeline
Every input is canonicalized before hitting the blocklist, preventing evasion through encoding tricks.
Raw input
│
├─ 1. Strip zero-width, invisible, and Bidi-override characters
│ (zero-width spaces, RLO/LRO/PDF, LRM/RLM, Bidi isolates…)
│
├─ 2. Decode base64 segments (20+ consecutive base64 chars)
│ "aWdub3JlIHByZXZpb3Vz" → "ignore previous"
│
├─ 3. Homoglyph substitution + lowercase
│ Cyrillic 'а' → 'a', Greek 'ο' → 'o', accented chars, etc.
│
└─ 4. Leet-speak substitution
'0'→'o', '1'→'i', '3'→'e', '@'→'a', '$'→'s', …
The same pipeline runs on every blocklist term at startup, so 1gn0r3 pr3v10us still matches ignore previous.
Security Notes
- Role/delimiter injection — tokens like
[INST],<|im_start|>,<<SYS>>,<human>, and### Assistantare blocked because legitimate user input never contains LLM-internal boundary markers. - Bidi override characters (U+202A–U+202E, U+2066–U+2069) are stripped unconditionally — they can reverse text rendering while the raw bytes still reach the model unchanged.
- Pattern matching uses Aho-Corasick — O(n) in input length with no backtracking, immune to ReDoS.
Validatethrows rather than returning bool — a missedifcheck cannot silently allow bad input through.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. 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. |
-
net8.0
- AhoCorasick (>= 2.0.279)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.