Explore our comprehensive resources on behavioral AI monitoring, compliance frameworks, and policy templates.
Start your compliance journey with HAIEC. Free assessment, automated evidence, audit-ready documentation.
Explore compliance frameworks:
Developer tools & integrations:
How AI AppSec uses Semgrep to detect prompt injection sinks, tool abuse, and AI-specific vulnerabilities in source code. 122 detectors, 79 security checks, with code examples.
How @haiec/openai and @haiec/anthropic wrap the official SDKs to capture model usage, token counts, and costs automatically. The instrumentation pattern and privacy considerations.
Learn what AI vendor public security disclosures entail and how they impact AI security and compliance professionals.
A team I worked with last year ran a static security scanner on their AI application. It passed clean. No prompt injection sinks, no tool abuse, no secrets in source code. They shipped.
Two weeks later, a user in Tenant A asked the model to summarize documents from a shared knowledge base. The model returned a response that included text from Tenant B's private repository. The static scanner had checked every file in the codebase. It found no vulnerabilities. It also never ran the code, never sent a request, and never saw a tenant boundary get crossed at runtime.
The vulnerability was not in the source code. It was in the boundary logic — a Prisma query that forgot to scope by organizationId when the request came through the shared knowledge base path. Static analysis reads source files. It does not trace tenant context through async boundaries and database queries at runtime.
This is the core problem: AI applications have three distinct attack surfaces, and no single tool covers all three.
An AI application has three places where security breaks:
Source code — vulnerable patterns in the code you write. Prompt injection sinks, tool abuse, hardcoded secrets, missing input validation. This is what traditional SAST tools check.
Tenant boundaries — the logic that keeps Tenant A's data away from Tenant B. Database query scoping, cache key prefixes, file path isolation, MCP tool visibility. General-purpose SAST tools do not check this.
Runtime I/O — what the model actually sends and receives when it runs. Hallucinated PII, prompt injection in user input, sensitive data in model output. Source code analysis cannot see this because it does not run the model.
Each surface requires a different type of check. Each check catches vulnerabilities the others cannot.
AI AppSec reads your source code without running it. It uses Semgrep 1.173.0 as its execution engine with a bundled rulepack of 122 detectors and 79 security checks. It scans Python, TypeScript, JavaScript, JSX, and TSX. It recognizes LangChain, LlamaIndex, Vercel AI SDK, OpenAI SDK, and Anthropic SDK patterns.
What it catches:
What it does not catch:
organizationId is not a vulnerability pattern — it is a business logic error)AI AppSec is a prerelease package (v0.1.0). It has one tool implemented (scan_ai_security). Tenant isolation checking and LLM content verification are on the roadmap but not yet shipped.
MCP Tenant Isolation reads your source code and checks whether tenant boundaries are enforced across every code path. It has 57 deterministic rules: 42 general multi-tenant rules and 15 MCP-specific rules. It parses Prisma schemas, SQL migrations, and TypeScript/JavaScript source.
What it catches:
findMany without organizationId filterAsyncLocalStorage propagation)What it does not catch:
MCP Tenant Isolation is v2.0.0 with 203 tests and 0 npm audit vulnerabilities. It is MCP v2 SDK compatible with Zod schemas and structured output.
LLMVerify sits between your LLM and your users. It checks model inputs and outputs at runtime using deterministic, pattern-based engines. No model calls, no network on the free tier. Same input plus same rules equals same result.
What it catches:
What it does not catch:
LLMVerify is v1.6.1. PII detection is regex-based, approximately 90% for standard formats and lower for variations. It does not expose an MCP server — integration is via the npm SDK, CLI, or local HTTP API.
Here is the worked example. Consider a multi-tenant AI application with an MCP server that lets users query a shared document store.
The vulnerability: The MCP tool handler search_documents accepts a tenantId from the LLM argument instead of deriving it from the authenticated session. A user in Tenant A can pass tenantId: "tenant-b" and receive Tenant B's documents.
// Vulnerable: tenantId from LLM argument
server.tool("search_documents", { query: z.string(), tenantId: z.string() }, async ({ query, tenantId }) => {
const docs = await prisma.documents.findMany({ where: { tenantId, content: { contains: query } } })
return { content: [{ type: "text", text: JSON.stringify(docs) }] }
})
AI AppSec: Does not flag this. The code has no prompt injection sink, no tool abuse pattern, no hardcoded secret. The vulnerability is a business logic error ( trusting client-supplied tenant identity), not a security pattern that Semgrep rules detect.
MCP Tenant Isolation: Flags this. Rule TCM-001 ("Tenant ID from client input instead of session") fires because tenantId comes from the tool argument (LLM-controlled) instead of from the authenticated session context. This is exactly what the 42 general multi-tenant rules are designed to catch.
Now consider a different vulnerability in the same application.
// Vulnerable: model output contains PII from training data
const response = await openai.chat.completions.create({
model: "gpt-4",
messages: [{ role: "user", content: userQuery }],
})
// No output filtering — model can leak PII
return response.choices[0].message.content
AI AppSec: Does not flag this. There is no vulnerable pattern in the source code. The code calls the OpenAI API correctly. The vulnerability is in what the model returns, not in how the code is written.
MCP Tenant Isolation: Does not flag this. No tenant boundary is crossed. The issue is model output content, not tenant isolation.
LLMVerify: Flags this. The output verification engine detects PII patterns (SSN, phone, email, credit card) in the model response and redacts them before they reach the user.
Three vulnerabilities. Three tools. Each caught by exactly one layer and missed by the other two.
Two additional packages support the three security checks but are not scanners themselves.
AI Inventory uses @haiec/openai and @haiec/anthropic SDK wrappers to track model usage, token counts, and costs. This is instrumentation, not security scanning. You install the wrapper, it captures usage events automatically, and the data feeds into an inventory of which models your organization uses and how much they cost.
ISAF Logger generates lineage metadata and evidence artifacts for AI training pipelines. It logs the chain from input through model to output with SHA-256 hash chains for tamper-evidence. It is a Python package (pip install isaf-logger) that adds 3 lines of code to your training scripts. It does not scan code, does not verify model behavior, and does not certify compliance. It generates evidence that can support compliance documentation.
Each tool runs in seconds. Total pipeline time is under 2 minutes for a typical project.
# .github/workflows/developer-security.yml
name: Developer Security
on: [pull_request]
jobs:
source-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm install -g ai-appsec
- run: pip install semgrep==1.173.0
- run: ai-appsec scan ./src --format sarif --output ai-appsec-results.sarif
boundary-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm install -g mcp-tenant-isolation
- run: mti scan ./src --format sarif --output mti-results.sarif
runtime-verification:
runs-on: ubuntu-latest
if: false # Runtime verification runs in your application, not CI
steps:
- run: echo "LLMVerify integrates via SDK in application code"
Runtime verification (LLMVerify) does not run in CI. It runs in your application, between the LLM call and the user response. The SDK integration is 3 lines:
import { verify } from 'llmverify'
const result = await verify({
input: userMessage,
output: modelResponse,
checks: ['prompt-injection', 'pii-redaction'],
})
if (result.blocked) {
return result.redactedOutput
}
| Tool | Install size | Scan time (typical project) | Cost | |---|---|---|---| | AI AppSec | ~5 MB (plus Semgrep ~50 MB) | 10-30 seconds | Free, MIT licensed | | MCP Tenant Isolation | ~3 MB | 5-15 seconds | Free, MIT licensed | | LLMVerify | ~2 MB | Under 100ms per verification call | Free tier: 500 calls/day |
All three packages are MIT licensed and run locally. No data leaves your machine on the free tier. No account required for the open-source packages.
The three-layer approach catches more than any single tool, but it does not catch everything.
What all three layers miss together:
npm audit or Snyk for these)The three layers are a baseline, not a complete security program. They cover the three surfaces most specific to AI applications: source code patterns, tenant boundaries, and runtime model I/O. They do not replace traditional security tooling for infrastructure, dependencies, and access control.
| If you are checking for | Use | |---|---| | Prompt injection sinks in source code | AI AppSec | | Tool abuse patterns in AI agent code | AI AppSec | | Cross-tenant data leakage in multi-tenant SaaS | MCP Tenant Isolation | | MCP tool visibility scoping | MCP Tenant Isolation | | PII in model output | LLMVerify | | Prompt injection in user input at runtime | LLMVerify | | Which models your organization uses | AI Inventory | | Evidence for AI training pipeline audit | ISAF Logger |
If you ship AI applications, run all three. The cost is minutes. The cost of skipping one is the vulnerability you do not catch.