Static analyzer for agentic AI codebases that detects unsafe tool boundaries.
wyscan finds places in your agent code where tools can execute dangerous operations, like shell commands, file deletion, or external API calls, without authorization checks. It analyzes Python and TypeScript/JavaScript codebases to identify AFB04 (Unauthorized Action) boundaries: the execution paths where an AI agent's actions can affect systems or data.
wyscan v0.6.0 by Plarix
AFB Scanner — github.com/plarix-security/wyscan
Scanning: /path/to/agent-project
──────────────────────────────────────────────────
CRITICAL agent_tools.py:47
shutil.rmtree(agent_dir) — file operation reachable from @tool
(langchain), no policy gate in call path.
Agent can delete arbitrary directories without authorization.
WARNING api_client.py:83
requests.post(url, data=payload) — external API call reachable
from @tool (langchain), no policy gate in call path.
Agent can transmit data externally without authorization.
──────────────────────────────────────────────────
2 critical 3 warning 5 info 34 files 1.2s
wyscan scans your agent codebase and reports:
- Tool functions exposed to LLM reasoning that can execute shell commands
- File system operations (delete, write, move) reachable from agent tools
- External API calls (HTTP POST/PUT/DELETE) without policy gates
- Database write operations accessible from tool definitions
- Dynamic code execution (eval, exec) in agent-callable functions
It does NOT:
- Execute your code (static analysis only)
- Require you to change your code to run
- Use LLMs or inference (pure AST-based detection)
- Produce false positives from pattern matching (proper call graph analysis)
In agentic systems, the boundary between AI reasoning and code execution is a critical security point. A tool decorated with @tool becomes callable by the LLM. If that tool can reach subprocess.run(), shutil.rmtree(), or requests.post() without authorization checks, the agent has unrestricted access to those capabilities.
wyscan helps you find these boundaries early so you can:
- Add policy gates before deployment
- Review which capabilities agents actually need
- Audit authorization coverage across tool definitions
- Prevent agents from performing unauthorized actions
Option 1: Link locally (development)
cd wyscan
npm install
npm run build
npm linkOption 2: Global install (when published)
npm install -g @plarix/wyscanOption 3: Direct usage (no install)
npm run wyscan -- scan ./projectwyscan scan ./my-agent-projectThat's it. wyscan will scan all Python and TypeScript/JavaScript files, build a call graph from tool definitions, and report any dangerous operations reachable without policy gates.
| Command | Description |
|---|---|
wyscan scan <path> |
Scan a directory or file for AFB exposures |
wyscan check |
Verify dependencies (tree-sitter, Node.js) |
wyscan version |
Print version and exit |
wyscan help |
Show usage information |
| Flag | Description | Example |
|---|---|---|
--json |
Output structured JSON with stable schema | wyscan scan ./project --json |
--level <severity> |
Filter by severity: critical, warning, info |
wyscan scan ./project --level critical |
--output <file> |
Write report to file instead of stdout | wyscan scan ./project --output report.txt |
--summary |
One-line summary only (for CI pipelines) | wyscan scan ./project --summary |
wyscan uses exit codes for CI/CD integration:
| Code | Meaning | CI Action |
|---|---|---|
0 |
No critical or warning findings | ✓ Pass build |
1 |
Warning level findings detected | ⚠ Optional: fail build |
2 |
Critical level findings detected | ✗ Fail build |
3 |
Scanner error (parse failure, dependency missing) | ✗ Fail build |
CI/CD Example:
# Fail build on critical findings only
wyscan scan ./src --level critical
if [ $? -eq 2 ]; then
echo "Critical AFB exposures detected. Build failed."
exit 1
fiwyscan scan ./agent-projectOutput:
wyscan v0.6.0 by Plarix
AFB Scanner — github.com/plarix-security/wyscan
Scanning: /agent-project
──────────────────────────────────────────────────
CRITICAL tools/setup_agent.py:60
shutil.rmtree(agent_dir) — file operation reachable from @tool
(langchain), no policy gate in call path.
Agent can execute this without authorization basis.
WARNING tools/api_client.py:42
requests.post(url, data=payload) — external API call reachable
from @tool (crewai), no policy gate in call path.
Agent can execute this without authorization basis.
INFO tools/reader.py:28
open(file_path, 'r') — file operation reachable from @tool
(langchain), no policy gate in call path.
Agent can execute this without authorization basis.
──────────────────────────────────────────────────
1 critical 1 warning 1 info 12 files 0.3s
wyscan scan ./project --jsonOutput:
{
"version": "0.6.0",
"scanned_path": "/project",
"files_analyzed": 12,
"runtime_ms": 342,
"findings": [
{
"severity": "CRITICAL",
"file": "tools/setup_agent.py",
"line": 60,
"function": "shutil.rmtree",
"tool_registration": "setup_agent",
"description": "Tool \"setup_agent\" (langchain) can reach shutil.rmtree. No policy gate or authorization check detected in call path. Agent can execute this operation without authorization basis.",
"afb_type": "AFB04"
}
],
"summary": {
"critical": 1,
"warning": 1,
"info": 1,
"suppressed": 0
}
}# Show only critical findings
wyscan scan ./project --level critical
# Show critical and warning (hide info)
wyscan scan ./project --level warningwyscan scan ./project --summaryOutput:
1 critical 1 warning 1 info 12 files 0.3s
Perfect for CI pipelines that need a quick pass/fail status.
# Human-readable report
wyscan scan ./project --output report.txt
# Machine-readable JSON
wyscan scan ./project --json --output report.jsonwyscan checkOutput:
wyscan v0.6.0 by Plarix
AFB Scanner — github.com/plarix-security/wyscan
Checking dependencies...
✓ Node.js v18.0.0
✓ tree-sitter-python.wasm found
✓ Parser initialization ok
All checks passed. Ready to scan.
wyscan uses a four-level severity model based on blast radius and reversibility:
CRITICAL - Irreversible destruction, exfiltration, or arbitrary execution
- Shell command execution (
subprocess.run,os.system) - Dynamic code execution (
eval,exec) - File/directory deletion (
shutil.rmtree,os.remove,unlink)
WARNING - Recoverable but consequential state modification or transmission
- File writes (
write_text,open('w'),shutil.copy) - HTTP mutations (
requests.post,httpx.put,fetchwith POST) - Database writes (
session.add,executewith INSERT/UPDATE) - Email sending (
smtp.sendmail)
INFO - Read-only access to sensitive data or external systems
- File reads (
open('r'),read_text) - HTTP GET requests (
requests.get,fetch) - Database queries (
executewith SELECT,fetchall) - Environment variable access (
os.getenv)
SUPPRESSED - Low-confidence detections (logged but not reported)
wyscan detects tool registrations in:
- LangChain:
@tooldecorator,BaseToolsubclasses,Tool()constructor - CrewAI:
@taskdecorator,@agentdecorator - AutoGen:
register_function,function_map - LlamaIndex:
FunctionTool,QueryEngineTool - MCP:
@server.tooldecorator - OpenAI: Raw function calling with
tools=parameter
- Python: Full AST parsing with tree-sitter, cross-file call graph analysis
- TypeScript/JavaScript: Planned (detector exists but currently returns empty findings)
- Parse source files using tree-sitter to build an AST
- Identify tool registrations by detecting framework-specific patterns
- Build a call graph from each tool definition
- Trace execution paths to find dangerous operations
- Check for policy gates in call paths (authorization checks)
- Report findings with severity based on operation type and context
Policy Gate Detection:
wyscan looks for structural authorization checks between tools and dangerous operations:
- Functions that raise
PermissionError,AuthorizationError, or similar - Conditional branches that check parameters and can prevent execution
- Early returns when authorization fails
If no gate is detected in the call path, the finding is reported.
The --json output follows this stable schema (versioned):
{
"version": "0.6.0",
"scanned_path": "/absolute/path",
"files_analyzed": 34,
"runtime_ms": 1200,
"findings": [
{
"severity": "CRITICAL|WARNING|INFO|SUPPRESSED",
"file": "relative/path/to/file.py",
"line": 47,
"function": "shutil.rmtree",
"tool_registration": "function_name_with_@tool",
"description": "Detailed explanation of the finding",
"afb_type": "AFB04"
}
],
"summary": {
"critical": 2,
"warning": 3,
"info": 5,
"suppressed": 10
}
}Schema Stability: The JSON schema will not change between patch versions. Breaking schema changes require a minor version bump with migration documentation.
wyscan can run as a GitHub App to scan pull requests and push events automatically.
- Create a GitHub App with webhook events enabled
- Set environment variables:
GITHUB_APP_ID=your-app-id GITHUB_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n..." GITHUB_WEBHOOK_SECRET=your-webhook-secret PORT=3000 - Start the service:
npm run build npm start
GET /health- Health checkPOST /webhook- GitHub webhook events
The GitHub App mode uses the same scanner engine as the CLI. Findings are identical.
wyscan uses sensible defaults. No configuration file is required.
Default exclusions:
node_modules/.venv/,venv/__pycache__/dist/,build/.git/test/,tests/,*.test.*,*.spec.*
Default limits:
- Max files: 1000
- Confidence threshold: 0.7
- Include:
**/*.py,**/*.ts,**/*.tsx,**/*.js,**/*.jsx
# Install dependencies
npm install
# Build
npm run build
# Run tests
npm test
# Link for development
npm run link
# Run locally without install
npm run wyscan -- scan ./projectwyscan/
├── src/
│ ├── cli/ # CLI interface
│ │ ├── index.ts # Entry point and commands
│ │ ├── formatter.ts # Terminal output formatting
│ │ └── version.ts # Version constants
│ ├── analyzer/ # Scanner engine
│ │ ├── index.ts # Orchestrator
│ │ ├── python/ # Python analyzer
│ │ │ ├── detector.ts # Detection logic
│ │ │ ├── ast-parser.ts # Tree-sitter AST parser
│ │ │ └── call-graph.ts # Call graph builder
│ │ └── typescript/ # TypeScript analyzer (planned)
│ ├── github/ # GitHub App integration
│ ├── reporter/ # Output formatters
│ └── types/ # TypeScript types
├── wasm/ # Tree-sitter WASM binaries
└── test-repos/ # Test fixtures
Current scope:
- AFB04 (Unauthorized Action) detection only
- Python language support (TypeScript planned)
- Tree-sitter based AST parsing
- Single-level cross-file call resolution
Out of scope:
- AFB01 (Instruction Injection) - requires semantic analysis
- AFB02 (Context Poisoning) - requires data provenance tracking
- AFB03 (Constraint Bypass) - requires business logic understanding
- Runtime behavior analysis
- Automatic remediation or code generation
- Multi-language projects (Python + TypeScript in same scan)
- Full TypeScript/JavaScript support
- Multi-level transitive import resolution
- Custom pattern definitions
- IDE plugins (VSCode, JetBrains)
- AFB01-03 detection with semantic analysis
- Configuration file support
Contributions are welcome. See the project's GitHub repository for guidelines.
MIT License. See LICENSE file for details.
- Repository: https://github.com/plarix-security/wyscan
- Documentation: https://github.com/plarix-security/wyscan/blob/main/README.md
- Issues: https://github.com/plarix-security/wyscan/issues
- AFB Specification: https://github.com/plarix-security/wyscan/blob/main/afb-spec/
Agent Failure Boundary (AFB) is a security taxonomy for AI agent systems that defines four critical boundaries:
- AFB01: Instruction Boundary - separates system instructions from user input
- AFB02: Context Boundary - separates trustworthy from untrusted retrieved context
- AFB03: Constraint Boundary - enforces operational limits and guardrails
- AFB04: Unauthorized Action - controls what operations agents can perform
wyscan currently detects AFB04 boundaries through static analysis. Future versions will support AFB01-03 with semantic analysis capabilities.
Built by Plarix Security • Securing agentic AI systems from the ground up