Skip to content

Run a Security Scan

Time: 5 minutes | Tools: security_scan | Tier: Community

Learn how to detect common vulnerabilities in your Python code.

What You'll Learn

  • How taint analysis works
  • Common vulnerability types
  • Interpreting security results
  • Fixing detected issues

Prerequisites

  • Completed previous tutorials
  • Sample code (see below)

Sample Vulnerable Code

Create vulnerable.py:

# vulnerable.py
import sqlite3
import os

def get_user(user_id):
    """Get user from database - VULNERABLE!"""
    conn = sqlite3.connect("users.db")
    cursor = conn.cursor()
    # SQL Injection vulnerability
    query = f"SELECT * FROM users WHERE id = {user_id}"
    cursor.execute(query)
    return cursor.fetchone()

def run_command(cmd):
    """Run a system command - VULNERABLE!"""
    # Command injection vulnerability
    os.system(cmd)

def read_file(filename):
    """Read a file - VULNERABLE!"""
    # Path traversal vulnerability
    with open(f"/data/{filename}") as f:
        return f.read()

Step 1: Run the Scan

Ask your AI:

"Scan vulnerable.py for security issues"

The AI calls:

{
  "tool": "security_scan",
  "parameters": {
    "file_path": "vulnerable.py"
  }
}

Step 2: Understand the Results

{
  "data": {
    "file_path": "vulnerable.py",
    "vulnerabilities": [
      {
        "type": "SQL_INJECTION",
        "severity": "CRITICAL",
        "cwe": "CWE-89",
        "line": 10,
        "function": "get_user",
        "source": "user_id (parameter)",
        "sink": "cursor.execute(query)",
        "taint_flow": [
          "user_id (line 5) → tainted",
          "query (line 9) → tainted via f-string",
          "cursor.execute (line 10) → SINK"
        ],
        "message": "User input flows directly to SQL execution",
        "confidence": 0.95
      },
      {
        "type": "COMMAND_INJECTION",
        "severity": "CRITICAL",
        "cwe": "CWE-78",
        "line": 15,
        "function": "run_command",
        "source": "cmd (parameter)",
        "sink": "os.system(cmd)",
        "taint_flow": [
          "cmd (line 13) → tainted",
          "os.system (line 15) → SINK"
        ],
        "message": "User input flows directly to system command",
        "confidence": 0.98
      },
      {
        "type": "PATH_TRAVERSAL",
        "severity": "HIGH",
        "cwe": "CWE-22",
        "line": 20,
        "function": "read_file",
        "source": "filename (parameter)",
        "sink": "open(f\"/data/{filename}\")",
        "taint_flow": [
          "filename (line 17) → tainted",
          "open() (line 20) → SINK"
        ],
        "message": "User input used in file path without sanitization",
        "confidence": 0.90
      }
    ],
    "summary": {
      "total": 3,
      "critical": 2,
      "high": 1,
      "medium": 0,
      "low": 0
    }
  },
  "tier_applied": "community",
  "duration_ms": 45
}

Understanding Taint Analysis

What is Taint?

"Taint" means data that could be controlled by an attacker:

Taint Source Example
Function parameters def get_user(user_id)
HTTP requests request.args.get('id')
File contents open(file).read()
User input input()

What is a Sink?

A "sink" is a dangerous operation:

Sink Risk
cursor.execute() SQL injection
os.system() Command injection
open() Path traversal
eval() Code injection
render() XSS

Taint Flow

The scan shows how tainted data reaches sinks:

user_id (parameter) → tainted
query = f"...{user_id}" → tainted
cursor.execute(query) → SINK! 💥

Step 3: Fix the Vulnerabilities

Fix SQL Injection

Before:

query = f"SELECT * FROM users WHERE id = {user_id}"
cursor.execute(query)

After:

query = "SELECT * FROM users WHERE id = ?"
cursor.execute(query, (user_id,))  # Parameterized query

Fix Command Injection

Before:

os.system(cmd)

After:

import subprocess
import shlex

# Validate allowed commands
ALLOWED_COMMANDS = {"ls", "pwd", "date"}
if cmd.split()[0] not in ALLOWED_COMMANDS:
    raise ValueError("Command not allowed")

subprocess.run(shlex.split(cmd), check=True)

Fix Path Traversal

Before:

with open(f"/data/{filename}") as f:

After:

import os

# Sanitize path
safe_path = os.path.normpath(filename)
if ".." in safe_path or safe_path.startswith("/"):
    raise ValueError("Invalid filename")

full_path = os.path.join("/data", safe_path)
with open(full_path) as f:

Vulnerability Types

Type CWE Severity Example
SQL Injection CWE-89 Critical f"SELECT ... {input}"
Command Injection CWE-78 Critical os.system(input)
XSS CWE-79 High render(f"<p>{input}</p>")
Path Traversal CWE-22 High open(f"/data/{input}")
Code Injection CWE-94 Critical eval(input)

Try It Yourself

Exercise 1: Scan Your Own Code

"Scan [your_file.py] for security vulnerabilities"

Exercise 2: Check Dependencies

"Scan my requirements.txt for known vulnerabilities"

Uses scan_dependencies to check against CVE databases.

Exercise 3: Fix and Rescan

After fixing issues:

"Rescan vulnerable.py to verify the fixes"

Community Tier Limits

Feature Community
Single-file scan
Max paths 10
Cross-file scanning ❌ (Pro)
Custom sinks ❌ (Enterprise)

Key Takeaways

  1. security_scan finds vulnerabilities using taint analysis
  2. Taint = data controlled by attackers
  3. Sink = dangerous operations
  4. Results show the full flow from source to sink
  5. Always use parameterized queries and input validation

Next Tutorial

Put everything together in a complete workflow:

Putting It Together →