Required Tools and Scanners

pip install bandit pip-audit semgrep
# Trivy — container/IaC scanner
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# agents/security-agent.yaml
name: security-agent
model: gpt-4o
temperature: 0.0
tools:
  - code_scanner     # wraps Bandit + Semgrep
  - dep_audit        # wraps pip-audit / npm audit
  - config_checker   # checks env files and config for secrets
  - report_writer    # structured report generation
system: |
  You are an application security analyst.
  Triage findings by CVSS severity: critical > high > medium > low > informational.
  For each finding: state the file/line, describe the risk, and suggest a fix.
  Group findings by severity. Always check for false positives before reporting.

Source Code Vulnerability Scan

import subprocess, json, pathlib
from openclaw import Agent

agent = Agent.from_config("agents/security-agent.yaml")

def scan_python_code(directory: str) -> dict:
    """Run Bandit + Semgrep and summarise with AI."""
    bandit_result = subprocess.run(
        ["bandit", "-r", directory, "-f", "json", "-q"],
        capture_output=True, text=True, timeout=120
    )
    
    semgrep_result = subprocess.run(
        ["semgrep", "--config=auto", directory, "--json", "--quiet"],
        capture_output=True, text=True, timeout=180
    )
    
    findings = {
        "bandit": json.loads(bandit_result.stdout) if bandit_result.stdout else {},
        "semgrep": json.loads(semgrep_result.stdout) if semgrep_result.stdout else {},
    }
    
    summary = agent.run(
        "Analyse these security scan results and produce a triage summary.\n"
        "Group by severity (CRITICAL/HIGH/MEDIUM/LOW).\n"
        f"Data:\n{json.dumps(findings, indent=2)[:6000]}"
    )
    return {"findings": findings, "summary": summary}

Dependency Audit

def audit_dependencies(requirements_file: str = "requirements.txt") -> str:
    result = subprocess.run(
        ["pip-audit", "--requirement", requirements_file, "--format", "json"],
        capture_output=True, text=True, timeout=120
    )
    vulns = json.loads(result.stdout) if result.stdout else []
    
    if not vulns:
        return "No known vulnerabilities found in Python dependencies."
    
    return agent.run(
        f"Found {len(vulns)} vulnerable dependencies. "
        "For each: state severity, CVE, current version, safe upgrade version, "
        "and whether it's a direct or transitive dependency.\n"
        f"Data:\n{json.dumps(vulns, indent=2)[:6000]}"
    )

Hardcoded Secrets Detection

Never commit secrets to version control. The agent scans for API keys, passwords, and tokens in source files using pattern matching and entropy analysis.
import re, pathlib

# Common secret patterns — extend as needed
SECRET_PATTERNS = [
    (r'(?i)(api[_-]?key|apikey)\s*[=:]\s*["\']?([A-Za-z0-9_\-]{20,})', "API Key"),
    (r'(?i)(password|passwd|pwd)\s*[=:]\s*["\']([^"\']{8,})', "Password"),
    (r'(?i)(secret|token)\s*[=:]\s*["\']([A-Za-z0-9_\-]{16,})', "Secret/Token"),
    (r'sk-[A-Za-z0-9]{48}', "OpenAI Key"),
    (r'ghp_[A-Za-z0-9]{36}', "GitHub Token"),
]

def scan_for_secrets(directory: str) -> list[dict]:
    findings = []
    for path in pathlib.Path(directory).rglob("*.py"):
        # Skip virtual envs and test fixtures
        if ".venv" in str(path) or "node_modules" in str(path):
            continue
        content = path.read_text(encoding="utf-8", errors="ignore")
        for pattern, label in SECRET_PATTERNS:
            for match in re.finditer(pattern, content):
                line_num = content[:match.start()].count("\n") + 1
                findings.append({
                    "file": str(path), "line": line_num,
                    "type": label, "match": match.group()[:60] + "...",
                })
    return findings

Docker Image Scanning (Trivy)

def scan_docker_image(image: str) -> str:
    result = subprocess.run(
        ["trivy", "image", "--format", "json", "--quiet", image],
        capture_output=True, text=True, timeout=300
    )
    trivy_data = json.loads(result.stdout) if result.stdout else {}
    
    return agent.run(
        f"Trivy scan of Docker image '{image}' complete. "
        "Summarise critical and high CVEs. "
        "Group by OS package vs. application library. "
        "Suggest base image upgrade if OS CVEs are present.\n"
        f"Data:\n{json.dumps(trivy_data, indent=2)[:6000]}"
    )

Scheduled Weekly Security Scan

import datetime, pathlib

def weekly_security_scan(repo_path: str, output_dir: str = "./security-reports") -> str:
    """Full security scan pipeline — run weekly via cron."""
    output = pathlib.Path(output_dir)
    output.mkdir(parents=True, exist_ok=True)
    
    code_result = scan_python_code(repo_path)
    dep_result = audit_dependencies(f"{repo_path}/requirements.txt")
    secret_findings = scan_for_secrets(repo_path)
    
    full_report = agent.run(
        "Generate a complete Security Audit Report in Markdown format.\n"
        "## Executive Summary\n## Critical Findings\n## High Findings\n"
        "## Dependency Vulnerabilities\n## Secrets Detected\n## Recommendations\n"
        f"Code scan summary: {code_result['summary']}\n"
        f"Dependency audit: {dep_result}\n"
        f"Secrets found: {len(secret_findings)} items\n"
    )
    
    date_str = datetime.date.today().isoformat()
    report_path = output / f"security-report-{date_str}.md"
    report_path.write_text(full_report, encoding="utf-8")
    return str(report_path)
# cron: weekly security scan every Monday at 02:00
0 2 * * 1 /usr/bin/python3 /opt/openclaw/security_scan.py --repo=/app >> /var/log/security-agent.log 2>&1