Skip to content
Conduit

skill

opencode-security-agent

red-orbita/opencode-security-agentcommunitylistedunreviewed

Set up

Skills are installed by placing their folder where your agent loads skills.

The download comes from github.com/red-orbita/opencode-security-agent itself, not from our server, so what you get is exactly what its author published.

Install instructions come from the README

Skills do not use an MCP config. Follow the steps in the README below.

README

synced Oct 23, 2026

OpenCode Security Agent

Security agent for OpenCode. v2 blocks malicious tool calls in real time -- a plugin using tool.execute.before stops credential exfiltration, known-bad domains (giftshop.club from the Postmark MCP backdoor is hardcoded), reverse shells, curl|bash pipes, and prompt injection attempts before they execute. The v1 static scanner is still here: vulnerability database scanning, source integrity verification, and coherence analysis.

License: GPL-3.0

Latest version: 1.7.0 -- June 2026 (changelog)


Warning: always vet a skill or MCP before installing it

The whole reason this project exists is that skills and MCPs cannot be trusted by default. 36% of public skills contain security flaws (Snyk ToxicSkills 2025) and supply-chain attacks like the Postmark MCP backdoor have already exfiltrated thousands of users' emails through a single line of code added in an update.

So before you install anything -- including this project -- make it a habit to:

  • Open OpenCode and ask it to review the files first. A quick conversation explaining what you're about to install, asking what each file does, what permissions it needs, what network endpoints it touches, and whether the behaviour matches the stated purpose. If anything looks off, stop.
  • Check the official source. Compare the files you have against the canonical repo. The git commit history is your timestamped record of authorship -- a fork or a stranger's zip is not.
  • Read the diff on every update. Yesterday's clean version doesn't guarantee today's is safe. The Postmark attack landed on version v1.0.16 after fifteen clean releases.

The security agent automates the runtime side of this once it's installed -- but the install itself is on you.


Why

The AI skills ecosystem is growing fast -- but so are the attacks. Snyk's ToxicSkills study found that 36% of skills contain security flaws, including 76 with confirmed malicious payloads. And in September 2025 the Postmark MCP incident became the canonical supply-chain attack in this ecosystem: fifteen clean versions followed by a single-line update that silently BCC'd every outgoing email to phan@giftshop.club.

Static analysis of v1.0.15 would have found nothing -- it was clean. That's the gap the runtime plugin closes.

What's new in v2 -- Runtime blocking

An OpenCode plugin using the tool.execute.before event runs before every tool call. It inspects the call against a local IOC library plus your allowlist, then allows or blocks it:

  • Sensitive paths -- reads of ~/.ssh/, ~/.aws/, credentials files, /etc/shadow are blocked. Paths embedded in bash commands (e.g. cat ~/.aws/credentials) or inside strings (e.g. Python open('.ssh/id_rsa')) are also detected.
  • Known-malicious domains -- hardcoded from confirmed incidents. giftshop.club is in there by default and cannot be allowlisted.
  • Exfiltration services -- pastebin.com, transfer.sh, webhook.site, requestbin, ngrok, serveo, raw-IP URLs.
  • Dangerous commands -- curl ... | bash, base64 -d | bash, nc -e, bash -i >& /dev/tcp/..., base64 | curl chains, appends to .bashrc, fork bombs.
  • Sensitive env vars -- ANTHROPIC_API_KEY, AWS_SECRET_ACCESS_KEY, GITHUB_TOKEN, DATABASE_URL, and the generic *_API_KEY / *_SECRET / *_TOKEN / *_PASSWORD patterns.
  • Prompt injection detection -- phrases like "ignore previous instructions", "act as root", "bypass security" embedded in tool call arguments are flagged.
  • Unicode smuggling detection (v1.6.0) -- invisible characters (Tag codepoints, BIDI overrides, zero-width chars) used to hide malicious instructions in skills and tool inputs.
  • Data exfiltration detection -- upload commands targeting paste/transfer services are flagged.
  • Crypto mining detection -- xmrig, stratum+tcp, mining pool domains, and wallet address patterns.

New in v1.7.0 -- Deep Skill Scanner (pre-installation)

A comprehensive static analysis pipeline that scans skills/MCPs before installation. Inspired by NVIDIA SkillSpector, built with zero dependencies and zero LLM cost:

# Scan a skill before installing
python3 plugins/skill_scanner.py /path/to/skill/

# JSON output for automation
python3 plugins/skill_scanner.py /path/to/skill/ --json

# SARIF for GitHub Security tab
python3 plugins/skill_scanner.py /path/to/skill/ --sarif

# Quick mode (AST + YARA only, <10ms)
python3 plugins/skill_scanner.py /path/to/skill/ --quick

# Skip network (no OSV.dev lookup)
python3 plugins/skill_scanner.py /path/to/skill/ --no-network

8 analysis modules, 10 rule categories, 133+ detection patterns:

StageModuleDetects
1AST Behavioralexec, eval, subprocess, compile, dynamic import, getattr, dangerous chains
2YARA SignaturesReverse shells, RATs, cryptominers, webshells, persistence
3Taint TrackingSource→sink data flows (credentials to network, env to exec)
4MCP PrivilegeUnderdeclared permissions, wildcards, scope violations
5MCP PoisoningHidden instructions in tool metadata, Unicode deception
6Agent SignaturesAI-specific: prompt injection (8 languages), context poisoning, tool shadowing, self-modification, semantic evasion
7OSV.dev CVE LookupKnown vulnerable dependencies (live API + offline fallback)
8Risk Scoring0-100 formal score with contextual multipliers

Agent-specific detection categories (AG1-AG10):

  • AG1: Skill Behavior Manipulation (safety override, role hijacking)
  • AG2: MCP Tool Shadowing (name squatting, identity spoofing)
  • AG3: Context/Memory Poisoning (persistent injection, state tampering)
  • AG4: Agent Self-Modification (config editing, allowlist tampering)
  • AG5: Trust Boundary Violations (cross-skill access, privilege escalation)
  • AG6: Prompt Exfiltration (system prompt extraction via tools)
  • AG7: Trigger Abuse (overly broad triggers, shadow commands)
  • AG8: Agentic Supply Chain (typosquatting, malicious post-install hooks)
  • AG9: Multilingual Injection (Spanish, Russian, Chinese, Arabic, Portuguese, German, Japanese, Turkish)
  • AG10: Semantic Evasion (synonyms, passive voice, deceptive justifications, credential targeting)

Pentesting validated: 92.3% adversarial detection rate (12/13 files with 125+ bypass techniques including hex, base64, ROT13, Caesar cipher, morse code, binary encoding, Unicode/bidi tricks, homoglyphs, code obfuscation, format-level tricks, multilingual attacks, semantic evasions, supply chain, timing bombs, multi-file collusion, anti-analysis, and shell evasions). Zero false positives on benign samples.

New in v1.6.0 -- Defense in depth

Three new security layers complement the existing pre-execution check:

1. Skill Validation Gate (skill.install.before)

Skills are validated before loading. Any skill containing Unicode smuggling, prompt injection patterns, or Semgrep findings is rejected:

# Validate a skill manually
python3 plugins/skill_validator.py /path/to/skill/

# Batch validate all skills
python3 plugins/skill_validator.py --batch /path/to/skills/

2. Tool Output Inspection (tool.execute.after)

A new sentinel_postflight.py engine inspects tool/MCP outputs for indirect prompt injection -- the #1 previously-unmitigated attack vector. Detects:

  • Prompt override attempts ("ignore all previous instructions")
  • Fake system/instruction tags ([SYSTEM], <system>)
  • Credential harvesting ("include the api_key in your request")
  • Deception patterns ("do not tell the user")
  • Unicode smuggling in outputs (hidden Tag characters)

3. Sandbox Enforcement for Untrusted Skills

Skills not listed in .security/trusted-skills.json cannot auto-execute bash commands. This prevents a compromised skill from running arbitrary code without human approval:

// .security/trusted-skills.json
{
  "trusted": ["security-agent"],
  "_comment": "Only a human can edit this file"
}

Zero LLM cost. Pure local pattern matching. Only blocked calls add a short message to the conversation.

Fail-open. Missing IOCs, malformed input, plugin crash -- all default to allow. The plugin will never break OpenCode.

Whitelistable. Legitimate false positives go in .security/sentinel-allowlist.json (project) or ~/.config/opencode/sentinel-allowlist.json (global). Confirmed-malicious domains are not overrideable.

v1 -- Static scanning (still included)

0. Semgrep pre-scan (new)

Before the LLM-driven analysis, the agent runs Semgrep with two layers of rules:

Custom rules (rules/semgrep/*.yaml) -- 4 rule files designed specifically for MCP/skill threats: credential exfiltration, network exfiltration (including the Postmark BCC pattern), dangerous commands, prompt injection, crypto mining, and obfuscation patterns.

Bundled community rules (rules/semgrep/community/) -- 40 rules curated from semgrep/semgrep-rules (1.1k stars), covering:

CategoryRulesDetects
ai-mcp/11MCP command injection, tool poisoning, SSRF, credential leaks, LLM-to-exec, DNS exfil
python-exec/11os.system, subprocess, spawn, exec, eval, paramiko, reverse shells
python-deser/4pickle, jsonpickle, pyyaml, marshal (arbitrary code execution)
python-secrets/2hardcoded passwords, credential logging
javascript-exec/6child_process, eval, spawn shell, dynamic method invocation
generic-secrets/5private keys, API keys, AWS secrets, GitHub tokens in code
generic-shells/1bash reverse shell patterns
# Scan a skill before installing it
bash scripts/scan_semgrep.sh /path/to/skill/

# JSON output for CI/CD
bash scripts/scan_semgrep.sh /path/to/skill/ --json

# Skip community rules (custom only)
bash scripts/scan_semgrep.sh /path/to/skill/ --no-community

# Validate all rules
bash scripts/scan_semgrep.sh --self-test

Semgrep is optional -- if not installed, the agent falls back to LLM-only analysis. Zero LLM cost, deterministic, offline.

SARIF output for GitHub Security tab

# Generate SARIF and upload to GitHub Code Scanning
bash scripts/scan_semgrep.sh /path/to/skill/ --sarif > results.sarif

SARIF findings appear directly in the Security tab of your GitHub repository, alongside CodeQL and other SAST tools.

GitHub Action

Use the bundled action to scan any repo on push/PR:

# .github/workflows/security-scan.yml
name: Security Scan
on: [push, pull_request]

permissions:
  contents: read
  security-events: write

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: red-orbita/opencode-security-agent@v1
        with:
          target: "."
          upload-sarif: "true"
          fail-on-findings: "true"

Action inputs:

InputDefaultDescription
target.Path to scan
include-community-rulestrueInclude bundled semgrep/semgrep-rules
output-formattexttext, json, or sarif
upload-sariffalseUpload SARIF to GitHub Security tab
fail-on-findingstrueFail workflow if issues found

1. Threat intelligence scanning

Checks every installed skill and MCP server against 6 live databases: GitHub Advisory DB, vulnerablemcp.info, mcpscan.ai, Snyk, ClawHub/VirusTotal, and Reddit r/ClaudeAI.

2. Source integrity verification

When you're about to install a skill, the agent finds the official original source and compares it against your copy.

3. Coherence analysis

Analyzes whether everything a skill does matches its stated purpose.

4. Update diff detection

Stores a snapshot of every installed skill. If an update changes something, it diffs and runs coherence analysis on the new code.

5. Scheduled monitoring

Runs automatically every morning to re-scan everything.


Installation -- step by step

Requirements

  • OpenCode installed
  • Python 3.8+ (for the standalone hook; the plugin itself is TypeScript)
  • Semgrep (optional, for static analysis: pip install semgrep)

Method 1 -- Install as OpenCode plugin + skill (recommended)

# 1. Clone
git clone https://github.com/rokitoh/opencode-security-agent.git
cd opencode-security-agent

# 2. Install the plugin globally
bash scripts/install.sh --user

# 3. Or install only for the current project
bash scripts/install.sh --project

The install script:

  • Copies the plugin to ~/.config/opencode/plugins/ (or .opencode/plugins/)
  • Copies the skill to ~/.config/opencode/skills/security-agent/ (or .opencode/skills/security-agent/)
  • Copies the IOC references alongside the plugin

Method 2 -- Manual install

# Global
mkdir -p ~/.config/opencode/plugins
mkdir -p ~/.config/opencode/skills/security-agent
cp plugins/security-agent.ts ~/.config/opencode/plugins/
cp plugins/sentinel_preflight.py ~/.config/opencode/plugins/
cp references/iocs.json ~/.config/opencode/plugins/
cp skills/security-agent/SKILL.md ~/.config/opencode/skills/security-agent/

# Or project-level
mkdir -p .opencode/plugins
mkdir -p .opencode/skills/security-agent
cp plugins/security-agent.ts .opencode/plugins/
cp plugins/sentinel_preflight.py .opencode/plugins/
cp references/iocs.json .opencode/plugins/
cp skills/security-agent/SKILL.md .opencode/skills/security-agent/

Verify the install

# 1. Run the regression suite (55 tests)
python3 tests/test_hook.py -v

# 2. Start OpenCode and check the plugin loads
opencode
# Then ask: "Is the security agent plugin active?"

Uninstall

bash scripts/uninstall.sh --user      # or --project

Usage

Just talk to OpenCode:

  • "Scan my project for security issues"
  • "Is this MCP server safe to install?"
  • "Check if this skill has been tampered with"
  • "Run a security audit"

The security agent skill triggers automatically when it detects you're about to install something or when you mention security concerns.

How it works

v2 runtime plugin -- A TypeScript plugin (plugins/security-agent.ts) that hooks into OpenCode's tool.execute.before event. It calls a local Python script (plugins/sentinel_preflight.py) that pattern-matches the tool call against the IOC library (references/iocs.json). If a match is found, the plugin throws an error to block the call.

v1 static scanner -- A skill (skills/security-agent/SKILL.md) with structured instructions that tells OpenCode how to act as a security agent. Uses OpenCode's built-in tools (bash, read, write, glob, grep, webfetch) to scan files, search databases, and generate reports.

All analysis happens locally + public web searches (for v1 scanning). Your code and credentials never leave your machine.


Claude Code integration

The sentinel engine can also run as a Claude Code hook. Use hooks/claude_code_hook.py as a PreToolUse hook:

{
  "hooks": {
    "PreToolUse": [
      {
        "command": "python3 /path/to/opencode-security-agent/hooks/claude_code_hook.py",
        "timeout": 5000
      }
    ]
  }
}

The adapter translates Claude Code's hook protocol to sentinel_preflight.decide(). All the same checks apply: sensitive paths, known-malicious domains, dangerous commands, prompt injection, etc.

Note: Codex (OpenAI) does not currently expose a public hook system. For Codex environments, use the Semgrep static rules (rules/semgrep/) in CI/CD instead.


Secrets management with SOPS

API keys for threat intelligence feeds are managed with SOPS (Secrets OPerationS) using age encryption. This allows you to keep encrypted secrets alongside the project without exposing plaintext keys.

Setup (one-time)

1. Install sops and age

# Debian/Ubuntu
sudo apt install age
curl -LO https://github.com/getsops/sops/releases/download/v3.9.4/sops-v3.9.4.linux.amd64
sudo mv sops-v3.9.4.linux.amd64 /usr/local/bin/sops
sudo chmod +x /usr/local/bin/sops

# macOS
brew install sops age

# Arch Linux
sudo pacman -S sops age

2. Generate your age key

mkdir -p ~/.config/sops/age
age-keygen -o ~/.config/sops/age/keys.txt

This prints your public key (starts with age1...). Save it -- you'll need it next.

3. Configure SOPS for this project

Edit .sops.yaml in the project root and replace the placeholder with your public key:

creation_rules:
  - path_regex: secrets\.enc\.yaml$
    age: "age1your-public-key-here"

4. Create your encrypted secrets file

# Copy the example template
cp secrets.enc.yaml.example secrets.enc.yaml

# Edit with your real API keys -- SOPS encrypts on save
sops secrets.enc.yaml

Inside the editor, replace the placeholder values with your actual keys:

URLHAUS_AUTH_KEY: "your-real-key"
THREATFOX_AUTH_KEY: "your-real-key"
OTX_API_KEY: "your-real-key"
ABUSEIPDB_KEY: "your-real-key"

Save and close. SOPS encrypts the file automatically. The encrypted file is gitignored by default.

5. Verify it works

# Decrypt and print (to verify)
sops -d secrets.enc.yaml

# Run IOC update (auto-detects SOPS)
bash scripts/update_iocs.sh

How it loads secrets

update_iocs.sh tries secrets sources in this order:

  1. SOPS -- looks for secrets.enc.yaml walking up from the script directory. Requires sops in PATH and the age key at ~/.config/sops/age/keys.txt.
  2. Plain .env -- fallback. Prints a warning suggesting migration to SOPS.
  3. Environment variables -- if already exported (e.g. in CI), used directly.

Sharing secrets with a team

If multiple people need access, add all their age public keys to .sops.yaml:

creation_rules:
  - path_regex: secrets\.enc\.yaml$
    age: "age1alice...,age1bob...,age1charlie..."

Then re-encrypt:

sops updatekeys secrets.enc.yaml

API keys reference

VariableServiceFree tierGet key at
URLHAUS_AUTH_KEYURLhaus (abuse.ch)Yeshttps://auth.abuse.ch/
THREATFOX_AUTH_KEYThreatFox (abuse.ch)Yeshttps://auth.abuse.ch/
OTX_API_KEYAlienVault OTXYes (unlimited)https://otx.alienvault.com/api
ABUSEIPDB_KEYAbuseIPDBYes (1,000/day)https://www.abuseipdb.com/account/plans

Note: URLhaus and ThreatFox both use abuse.ch authentication. A single registration gives you an Auth-Key that works for both services.


Integrating External IOC / Threat Intelligence APIs

The security agent ships with a bundled static IOC library (references/iocs.json), but you can extend it by integrating external threat intelligence APIs to keep your IOC database up to date automatically.

Architecture overview

External API  -->  fetch script  -->  references/iocs.json  -->  sentinel_preflight.py
                   (cron/CI)          (merged IOCs)              (runtime checks)

The approach is simple: a script fetches IOCs from one or more APIs, merges them into iocs.json, and the runtime engine picks them up on the next tool call. No restart needed -- the Python hook reloads iocs.json on every invocation.

Running IOC updates

# Automatic (detects SOPS or .env)
bash scripts/update_iocs.sh

# Explicit SOPS invocation
sops exec-env secrets.enc.yaml 'bash scripts/update_iocs.sh'

# With environment variables directly
URLHAUS_AUTH_KEY=xxx THREATFOX_AUTH_KEY=xxx bash scripts/update_iocs.sh

Supported API integrations

1. URLhaus (abuse.ch)

Tracks malicious URLs used for malware distribution. Requires Auth-Key since 2025.

# Fetched automatically by update_iocs.sh
# Manual test:
curl -s -H "Auth-Key: $URLHAUS_AUTH_KEY" \
  "https://urlhaus-api.abuse.ch/v1/urls/recent/limit/100/" \
  | python3 scripts/import_urlhaus.py

API reference: https://urlhaus-api.abuse.ch/

2. ThreatFox (abuse.ch)

Free IOC sharing platform. Tracks C2, malware configs, and payloads. Requires Auth-Key since 2025.

# Fetched automatically by update_iocs.sh
# Manual test:
curl -s -X POST "https://threatfox-api.abuse.ch/api/v1/" \
  -H "Auth-Key: $THREATFOX_AUTH_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "get_iocs", "days": 7}' \
  | python3 scripts/import_threatfox.py

API reference: https://threatfox.abuse.ch/api/

3. AlienVault OTX (Open Threat Exchange)

Free tier available. Provides pulses with domains, IPs, URLs, and file hashes.

# Fetched automatically by update_iocs.sh
# Manual test:
curl -s -H "X-OTX-API-KEY: $OTX_API_KEY" \
  "https://otx.alienvault.com/api/v1/pulses/subscribed?limit=50" \
  | python3 scripts/import_otx.py

API reference: https://otx.alienvault.com/assets/s/v2/api_doc.html

4. AbuseIPDB

Check and report malicious IPs. Free tier: 1,000 checks/day.

# Fetched automatically by update_iocs.sh
# Manual test:
curl -s -G "https://api.abuseipdb.com/api/v2/blacklist" \
  -H "Key: $ABUSEIPDB_KEY" \
  -H "Accept: application/json" \
  -d "confidenceMinimum=90" \
  -d "limit=500" \
  | python3 scripts/import_abuseipdb.py

API reference: https://docs.abuseipdb.com/

5. MISP (Malware Information Sharing Platform)

For organizations running their own MISP instance or connecting to community instances.

export MISP_URL="https://your-misp-instance.org"
export MISP_API_KEY="your-key-here"

curl -s -X POST "$MISP_URL/events/restSearch" \
  -H "Authorization: $MISP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"tags": ["mcp", "supply-chain"], "published": true, "limit": 50}' \
  | python3 scripts/import_misp.py

API reference: https://www.misp-project.org/openapi/

Writing an import script

All import scripts follow the same pattern: read JSON from stdin, extract relevant IOCs, merge into iocs.json. See scripts/import_template.py for a full template.

Automating IOC updates

With cron (Linux/macOS)

crontab -e

# Fetch IOCs every 6 hours
0 */6 * * * /path/to/opencode-security-agent/scripts/update_iocs.sh >> /var/log/ioc-updates.log 2>&1

With GitHub Actions

# .github/workflows/update-iocs.yml
name: Update IOC database
on:
  schedule:
    - cron: '0 */6 * * *'
  workflow_dispatch:

jobs:
  update:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install sops and age
        run: |
          sudo apt-get install -y age
          curl -LO https://github.com/getsops/sops/releases/download/v3.9.4/sops-v3.9.4.linux.amd64
          sudo mv sops-v3.9.4.linux.amd64 /usr/local/bin/sops
          sudo chmod +x /usr/local/bin/sops

      - name: Decrypt secrets and update IOCs
        env:
          SOPS_AGE_KEY: ${{ secrets.SOPS_AGE_KEY }}
        run: bash scripts/update_iocs.sh

      - name: Commit updated IOCs
        run: |
          git config user.name "IOC Bot"
          git config user.email "bot@example.com"
          git add references/iocs.json
          git diff --cached --quiet || git commit -m "chore: update IOC database"
          git push

GitHub Actions with SOPS: Store your age private key as a GitHub secret named SOPS_AGE_KEY. SOPS reads it from the environment variable automatically.


Threat database

The agent maintains a local JSON database at .security/mcp-sentinel-threats.json that grows with each scan.

Benchmarks

v1.7.0 Deep Skill Scanner (69 tests in tests/test_skill_scanner.py)

CategoryCasesResult
AST Behavioral Analysis (exec, import, subprocess, chains)99/9
Taint Tracking (credential→network flows)33/3
MCP Privilege validation22/2
MCP Poisoning (HTML injection, Unicode, system override)77/7
OSV.dev CVE lookup (requirements parsing, fallback)33/3
YARA Signatures (reverse shell, cryptominer, webshell)44/4
Risk Scoring (severity weights, multipliers, clamp)88/8
Agent Signatures AG1-AG8 (tool shadowing, context poisoning, triggers)2121/21
Agent Signatures false-positive checks66/6
Integration tests (pipeline, quick mode, SARIF)66/6
Total6969/69

Performance: 4.7ms/file average. Full pipeline on 6 files: 28ms.

v1.7.0 Adversarial Pentesting (13 bypass files, 125+ techniques)

Attack CategoryTechniquesScoreResult
Encoding evasions (hex, b64, ROT13, Caesar, morse, binary, XOR)10100/100DETECTED
Multilingual injection (8 languages)990/100DETECTED
Unicode/Bidi tricks (RTL, ZWS, homoglyphs, tags)10100/100DETECTED
Code obfuscation (decorators, metaclass, lambda, generators)15100/100DETECTED
Format tricks (YAML anchors, JSON proto pollution, MD comments)12100/100DETECTED
Semantic evasions (synonyms, passive voice, social engineering)8100/100DETECTED
Supply chain (typosquatting, post-install hooks)7100/100DETECTED
Timing/conditional (time-bombs, CI-only, platform targeting)12100/100DETECTED
Multi-file collusion (Part A — utilities with hidden intent)590/100DETECTED
Multi-file collusion (Part B — pure imports, inherent limitation)20/100ACCEPTED
Comment/docstring injection8100/100DETECTED
Anti-analysis (buffer overflow, bytecode swap, GC zombie)12100/100DETECTED
Shell evasions (variable construction, DNS exfil, LD_PRELOAD)15100/100DETECTED
Detection Rate92.3%

Zero false positives on benign samples (3 clean files).

v2 runtime plugin (55 regression cases in tests/test_hook.py)

CategoryCasesResult
Benign tool calls correctly allowed66/6
Credential-harvesting attacks blocked44/4
Network exfil blocked (incl. Postmark giftshop.club IOC)44/4
Dangerous commands blocked (reverse shell, .bashrc hijack)44/4
DNS/ICMP exfiltration blocked44/4
Polyglot code execution blocked (Python, Ruby, Perl, AWK, Node)55/5
Environment/history enumeration blocked44/4
Dot-file/credential discovery blocked77/7
Network tools to raw IPs blocked22/2
Typosquatting domain detection22/2
Prompt injection detection22/2
Self-protection (allowlist/iocs write blocked)66/6
Fail-open on malformed / empty input33/3
Human guidance hints22/2
Total5555/55

Overhead: ~30-80 ms per tool call. Zero LLM tokens in normal operation.

Semgrep rules (32 regression cases in tests/test_semgrep_rules.py)

CategoryCasesResult
Rule validation (custom + community)22/2
Postmark BCC backdoor detection22/2
Credential harvesting (file read + env dump + exfil)44/4
Reverse shell + persistence + crypto mining55/5
Prompt injection + eval in MCP tools33/3
Deserialization attacks (pickle, yaml, marshal)44/4
child_process abuse + dynamic method exec33/3
Benign samples: zero false positives44/4
Overall coverage (all malicious detected, all benign clean)22/2
Semgrep availability check11/1
Total3232/32

6 malicious samples, 4 benign samples. Zero false positives on benign code.

Contributing

See CONTRIBUTING.md for guidelines on how to contribute.

Found a vulnerability? See SECURITY.md for responsible disclosure.

Legal

License

This project is licensed under the GNU General Public License v3.0.

Disclaimer

This software is provided "as is", without warranty of any kind. This is a security tool that helps detect potential threats, but it does not guarantee the detection of all vulnerabilities or malicious code.

Attribution

Based on the architecture of MCP Sentinel by Rafael Tunon Sanchez. Adapted for the OpenCode ecosystem.


Layout

opencode-security-agent/
├── README.md                                    # this file
├── CHANGELOG.md                                 # version history
├── CONTRIBUTING.md                              # contribution guidelines
├── SECURITY.md                                  # vulnerability disclosure policy
├── THREAT_MODEL.md                              # formal threat model (9 vectors, CWE/CAPEC)
├── LICENSE                                      # GPL-3.0
├── action.yml                                   # GitHub Action (reusable)
├── .sops.yaml                                   # SOPS encryption config (age backend)
├── secrets.enc.yaml.example                     # template for API keys
├── plugins/                                     # v2 runtime protection
│   ├── security-agent.ts                        # OpenCode plugin (tool.execute.before)
│   ├── sentinel_preflight.py                    # Python pattern matcher (called by plugin)
│   ├── skill_scanner.py                         # Deep Skill Scanner orchestrator (v1.7.0)
│   └── skill_validator.py                       # Skill validation gate (skill.install.before)
├── hooks/                                       # adapters for other AI agents
│   └── claude_code_hook.py                      # Claude Code PreToolUse hook adapter
├── lib/                                         # shared Python modules
│   ├── ioc_utils.py                             # load/save/merge IOCs (used by import scripts)
│   └── scanners/                                # Deep scanner analysis modules (v1.7.0)
│       ├── __init__.py                          # package init (v1.1.0)
│       ├── ast_analyzer.py                      # AST behavioral analysis (AST1-AST9)
│       ├── taint_tracker.py                     # source→sink data flow tracking (TT1-TT5)
│       ├── mcp_privilege.py                     # MCP least privilege validation (LP1-LP4)
│       ├── mcp_poisoning.py                     # tool poisoning detection (TP1-TP4, 30+ patterns)
│       ├── agent_signatures.py                  # AI agent ecosystem rules (AG1-AG10, 40+ patterns)
│       ├── osv_checker.py                       # OSV.dev CVE lookup (live + offline fallback)
│       ├── yara_patterns.py                     # YARA-like malware signatures (YR1-YR4)
│       └── risk_scorer.py                       # formal risk scoring 0-100
├── rules/                                       # Semgrep static analysis rules
│   ├── registry-metadata.yaml                   # Semgrep Registry publishing metadata
│   └── semgrep/
│       ├── credential-exfiltration.yaml         # custom: credential file reads, env harvesting
│       ├── network-exfiltration.yaml            # custom: exfil services, raw IPs, hidden BCC
│       ├── dangerous-commands.yaml              # custom: curl|bash, reverse shells, eval
│       ├── supply-chain-patterns.yaml           # custom: crypto mining, prompt injection
│       └── community/                           # bundled from semgrep/semgrep-rules
│           ├── LICENSE                          # Semgrep Rules License
│           ├── ai-mcp/                          # MCP injection, poisoning, SSRF, LLM-to-exec
│           ├── python-exec/                     # os.system, subprocess, eval, reverse shells
│           ├── python-deser/                    # pickle, jsonpickle, pyyaml, marshal
│           ├── python-secrets/                  # hardcoded passwords, credential logging
│           ├── javascript-exec/                 # child_process, eval, spawn, dynamic methods
│           ├── generic-secrets/                 # private keys, API keys, AWS/GitHub tokens
│           └── generic-shells/                  # bash reverse shell patterns
├── scripts/                                     # install/uninstall + IOC import helpers
│   ├── install.sh
│   ├── uninstall.sh
│   ├── scan_semgrep.sh                          # Semgrep static scanner wrapper
│   ├── update_iocs.sh                           # orchestrator: fetches all IOC sources
│   ├── sign_iocs.py                             # SHA-256 checksum for iocs.json integrity
│   ├── import_urlhaus.py                        # URLhaus importer
│   ├── import_threatfox.py                      # ThreatFox importer
│   ├── import_otx.py                            # AlienVault OTX importer
│   ├── import_abuseipdb.py                      # AbuseIPDB importer
│   ├── import_misp.py                           # MISP importer
│   └── import_template.py                       # template for writing new importers
├── skills/
│   └── security-agent/
│       └── SKILL.md                             # v1 static scanner skill
├── references/
│   ├── iocs.json                                # indicator library
│   ├── threat-sources.md                        # vulnerability database + API reference
│   └── threat-db-template.json                  # local threat DB schema
└── tests/
    ├── test_hook.py                             # regression tests for the Python hook (55)
    ├── test_hook_pytest.py                      # pytest version of hook tests (55)
    ├── test_semgrep_rules.py                    # regression tests for Semgrep rules (32)
    ├── test_skill_scanner.py                    # Deep Skill Scanner tests (69)
    ├── semgrep-samples/                         # test corpus for Semgrep rules
    │   ├── malicious/                           # 6 samples reproducing real attack patterns
    │   │   ├── postmark_bcc_backdoor.py         # Postmark MCP incident reproduction
    │   │   ├── credential_harvester.js          # SSH/AWS credential theft + webhook exfil
    │   │   ├── reverse_shell_persistence.py     # reverse shell + bashrc + crypto mining
    │   │   ├── prompt_injection_tool.js         # LLM prompt injection in tool description
    │   │   ├── deserialization_dns_exfil.py     # pickle/yaml/marshal + DNS exfiltration
    │   │   └── child_process_abuse.js           # child_process + eval + spawn shell
    │   └── benign/                              # 4 samples that must produce zero findings
    │       ├── normal_file_ops.py
    │       ├── normal_http_client.js
    │       ├── legitimate_mcp_server.py
    │       └── legitimate_skill.ts
    └── scanner-samples/                         # Deep Scanner test corpus (v1.7.0)
        ├── malicious/                           # intentionally malicious samples (score 100)
        ├── benign/                              # clean samples (score 0, verify no FP)
        ├── bypass/                              # earlier bypass tests (6 files)
        └── pentesting/                          # adversarial pentesting (13 files, 125+ techniques)
            ├── 01_encoding_evasions.py          # hex, b64, ROT13, Caesar, morse, binary, XOR
            ├── 02_multilingual_attacks.md       # ES, RU, ZH, AR, JA, PT, DE, TR
            ├── 03_unicode_bidi_tricks.py        # RTL, ZWS, homoglyphs, tag chars
            ├── 04_code_obfuscation.py           # type(), decorators, metaclass, generators
            ├── 05_format_tricks.py              # YAML, JSON, Markdown, TOML polyglot
            ├── 06_semantic_evasions.md          # synonyms, passive voice, social engineering
            ├── 07_supply_chain_typosquat.json   # typosquatting, hooks, identity spoof
            ├── 08_timing_conditional.py         # time-bombs, CI-only, platform targeting
            ├── 09_multifile_collude_a.py        # utilities with hidden malicious intent
            ├── 09_multifile_collude_b.py        # pure imports (accepted limitation)
            ├── 10_comment_injection.py          # docstrings, variable names, ASCII art
            ├── 11_anti_analysis.py              # bytecode swap, GC zombie, deep nesting
            └── 12_shell_evasions.sh             # variable construction, DNS exfil, hooks
↑↓ navigate openesc close