- 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
101 lines
3.3 KiB
TypeScript
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
|
|
}
|