shieldx/integrations/eo-global-pulse-middleware.ts
Rene Fichtmueller 1c4c034483 feat: ShieldX v0.3.0 — UnicodeScanner (L5), DNS Covert Channel rules, ATLAS v5.4 mappings
- Layer 4 EntropyScanner: Shannon entropy, Base32/Base64 detection, CVE-2025-55284
  ping/nslookup exfil, EchoLeak markdown pattern, DNS tunneling (iodine/dnscat)
- Layer 5 UnicodeScanner: ASCII Smuggling (U+E0000 Tags Block), Variant Selectors,
  Zero-Width steganography, CamoLeak image-ordering (CVE-2025-53773), homoglyphs,
  BiDi override, high-entropy URL params
- 30 DNS covert channel rules (dns-001 to dns-030)
- ATLASMapper: 29 techniques (ATLAS v5.4.0 Feb 2026), added AML.T0062 (Agent Tool
  Invocation), AML.TA0015 (C2 tactic), memory poisoning, multi-agent trust,
  CamoLeak, Unicode steganography mappings
- Rule count: 72 → 102
- Build: tsup 316ms, zero TypeScript errors
2026-03-31 16:32:16 +02:00

101 lines
3.3 KiB
TypeScript

/**
* ShieldX Drop-In Middleware for EO Global Pulse
*
* Copy this file to: eo-global-pulse/src/lib/shieldx-middleware.ts
* Then import in your AI routes:
*
* import { scanWithShieldX } from '@/lib/shieldx-middleware'
* const scanResult = await scanWithShieldX(userMessage)
* if (scanResult.blocked) return scanResult.response
*
* When the ShieldX proxy is running on :11435, configure OLLAMA_URL
* to point there instead of :11434 — all Ollama calls are then protected.
*
* This middleware provides ADDITIONAL protection for the chat route
* beyond what the proxy does, adding request-level context.
*/
import { NextResponse } from 'next/server'
const SHIELDX_PROXY = process.env.SHIELDX_PROXY_URL || 'http://localhost:11435'
const SHIELDX_ENABLED = process.env.SHIELDX_ENABLED !== 'false'
interface ShieldXScanResult {
blocked: boolean
threatLevel: string
killChainPhase: string
action: string
matchedPatterns: string[]
latencyMs: number
response?: NextResponse
}
/**
* Scan user input with ShieldX before processing.
* Falls back gracefully if ShieldX proxy is unavailable.
*/
export async function scanWithShieldX(input: string): Promise<ShieldXScanResult> {
if (!SHIELDX_ENABLED) {
return { blocked: false, threatLevel: 'none', killChainPhase: 'none', action: 'allow', matchedPatterns: [], latencyMs: 0 }
}
try {
const start = Date.now()
const res = await fetch(`${SHIELDX_PROXY}/shieldx/scan`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ input }),
signal: AbortSignal.timeout(5000),
})
if (!res.ok) {
// ShieldX unavailable — fail open (allow request)
console.warn('[ShieldX] Proxy unavailable, failing open')
return { blocked: false, threatLevel: 'unknown', killChainPhase: 'none', action: 'allow', matchedPatterns: [], latencyMs: Date.now() - start }
}
const result = await res.json()
const latencyMs = Date.now() - start
if (result.action === 'block' || result.action === 'incident') {
return {
blocked: true,
threatLevel: result.threatLevel,
killChainPhase: result.killChainPhase,
action: result.action,
matchedPatterns: result.matchedPatterns || [],
latencyMs,
response: NextResponse.json(
{ error: 'Request blocked by security filter', threatLevel: result.threatLevel },
{ status: 403 }
),
}
}
return {
blocked: false,
threatLevel: result.threatLevel || 'none',
killChainPhase: result.killChainPhase || 'none',
action: result.action || 'allow',
matchedPatterns: result.matchedPatterns || [],
latencyMs,
}
} catch {
// Network error — fail open
console.warn('[ShieldX] Scan failed, failing open')
return { blocked: false, threatLevel: 'unknown', killChainPhase: 'none', action: 'allow', matchedPatterns: [], latencyMs: 0 }
}
}
/**
* Wrapper: Change OLLAMA_URL to point through ShieldX proxy.
* Add this to your .env.local:
* OLLAMA_URL=http://localhost:11435
*
* This is the SIMPLEST integration — just change the Ollama URL.
* The proxy scans everything transparently.
*/
export function getProtectedOllamaUrl(): string {
return process.env.OLLAMA_URL || SHIELDX_PROXY
}