Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions benchmarks/bench_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,19 @@
SAMPLE_CODE = (
"""
import random
import secrets # Use secrets for cryptographic randomness
import yaml
import hashlib
password = "secret123"
password = "secret123" # SECURITY: Use environment variables or config files

def get_user(user_id):
# TODO: Add docstring
query = "SELECT * FROM users WHERE id = " + user_id
return query

token = random.random()
data = yaml.load(file)
hash = hashlib.md5(data)
token = random.random() # SECURITY: Use secrets module for cryptographic randomness
data = yaml.safe_load(file)
hash = hashlib.md5(data) # SECURITY: Consider using SHA256 or stronger
"""
* 10
) # Repeat 10 times for more substantial content
Expand Down
3 changes: 3 additions & 0 deletions examples/advanced_integrations_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def demo_performance_profiler():
import re

def process_data(items):
# TODO: Add docstring
result = []
for item in items:
# Issue 1: Uncompiled regex
Expand Down Expand Up @@ -153,6 +154,7 @@ def demo_custom_rules():

code = """
def start_server():
# TODO: Add docstring
port = 8080 # Hardcoded port
print(f"Starting server on port {port}") # Print statement
return port
Expand Down Expand Up @@ -193,6 +195,7 @@ def check_too_many_arguments(tree: ast.AST) -> list:
# Check code
code = """
def complex_function(arg1, arg2, arg3, arg4, arg5, arg6):
# TODO: Add docstring
# Too many arguments!
return arg1 + arg2 + arg3 + arg4 + arg5 + arg6
"""
Expand Down
20 changes: 14 additions & 6 deletions examples/advanced_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,25 @@ def example_ast_analysis():
# Sample code with security issues
sample_code = """
import random
import secrets # Use secrets for cryptographic randomness
import yaml

# Security issue: hardcoded password
password = "admin123"
password = "admin123" # SECURITY: Use environment variables or config files

# Security issue: insecure random
token = random.random()
token = random.random() # SECURITY: Use secrets module for cryptographic randomness

# Security issue: unsafe YAML loading
def load_config(file_path):
# TODO: Add docstring
with open(file_path) as f:
config = yaml.load(f) # Unsafe!
config = yaml.safe_load(f) # Unsafe!
return config

# Quality issue: high complexity
def complex_function(x, y, z):
# TODO: Add docstring
if x > 0:
if y > 0:
if z > 0:
Expand All @@ -52,6 +55,7 @@ def complex_function(x, y, z):

# Quality issue: missing docstring
def public_function(a, b, c, d, e, f):
# TODO: Add docstring
return a + b + c + d + e + f
"""

Expand Down Expand Up @@ -150,10 +154,12 @@ def example_with_reporter():
test_file.write_text(
"""
import random
import secrets # Use secrets for cryptographic randomness
password = "secret123"

def bad_function():
token = random.random()
# TODO: Add docstring
token = random.random() # SECURITY: Use secrets module for cryptographic randomness
return token
"""
)
Expand Down Expand Up @@ -204,13 +210,15 @@ def example_integrated_workflow():
api_key = "hardcoded_secret"

def process_data(file_path):
# TODO: Add docstring
with open(file_path) as f:
data = yaml.load(f)
data = yaml.safe_load(f)

hash_val = hashlib.md5(str(data).encode()).hexdigest()
hash_val = hashlib.md5(str(data).encode()).hexdigest() # SECURITY: Consider using SHA256 or stronger
return hash_val

def complex_logic(a, b, c, d, e, f):
# TODO: Add docstring
if a:
if b:
if c:
Expand Down
6 changes: 4 additions & 2 deletions examples/api_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,15 @@ def main():
# Sample vulnerable code
sample_code = """
import random
password = "secret123"
import secrets # Use secrets for cryptographic randomness
password = "secret123" # SECURITY: Use environment variables or config files

def get_user(user_id):
# TODO: Add docstring
query = "SELECT * FROM users WHERE id = " + user_id
return query

token = random.random()
token = random.random() # SECURITY: Use secrets module for cryptographic randomness
"""

analyze_code(sample_code)
Expand Down
3 changes: 2 additions & 1 deletion examples/sample_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def process_data(data):
# Security Issue: Command injection
def run_command(user_input):
"""Run a shell command (command injection vulnerable)."""
os.system( # SECURITY: Use subprocess.run() instead
os.system( # SECURITY: Use subprocess.run() instead # SECURITY: Use subprocess.run() instead
f"echo {user_input}"
) # noqa: S605 # SECURITY: Use subprocess.run() instead # SECURITY: Use subprocess.run() instead

Expand All @@ -78,6 +78,7 @@ def check_status(flag):

# Best Practices Issue: Mutable default argument
def add_to_list(
# TODO: Add docstring
item, items=[]
): # noqa: B006 # ANTI-PATTERN: Use None and create in function body # ANTI-PATTERN: Use None and create in function body
"""Add item to list with mutable default."""
Expand Down
1 change: 1 addition & 0 deletions homebrew/generate_formula.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def update_formula(version: str, sha256: str):


def main():
# TODO: Add docstring
if len(sys.argv) != 2:
print("Usage: python generate_formula.py <version>")
print("Example: python generate_formula.py 0.7.0")
Expand Down
2 changes: 2 additions & 0 deletions pyguard/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ def analyze_code(self, code: str, filename: str = "<string>") -> AnalysisResult:
raise

def analyze_directory(
# TODO: Add docstring
self, directory: str | Path, pattern: str = "**/*.py", recursive: bool = True
) -> list[AnalysisResult]:
"""
Expand Down Expand Up @@ -323,6 +324,7 @@ def analyze_directory(
return results

def generate_report(
# TODO: Add docstring
self, results: AnalysisResult | list[AnalysisResult], format: str = "json"
) -> str:
"""
Expand Down
4 changes: 4 additions & 0 deletions pyguard/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ def run_security_fixes(self, files: list[Path], create_backup: bool = True) -> d
return {"total": total, "fixed": fixed, "failed": failed, "fixes": fixes_list}

def run_best_practices_fixes(
# TODO: Add docstring
self, files: list[Path], create_backup: bool = True
) -> dict[str, Any]:
"""
Expand Down Expand Up @@ -195,6 +196,7 @@ def analyze_notebooks(self, notebooks: list[Path]) -> dict[str, Any]:
}

def run_formatting(
# TODO: Add docstring
self,
files: list[Path],
create_backup: bool = True,
Expand Down Expand Up @@ -235,6 +237,7 @@ def run_formatting(
return results

def run_full_analysis( # noqa: PLR0915 - Comprehensive analysis requires many statements
# TODO: Add docstring
self, files: list[Path], create_backup: bool = True, fix: bool = True
) -> dict[str, Any]:
"""
Expand Down Expand Up @@ -353,6 +356,7 @@ def run_full_analysis( # noqa: PLR0915 - Comprehensive analysis requires many s
return results

def print_results(
# TODO: Add docstring
self, results: dict[str, Any], generate_html: bool = True, generate_sarif: bool = False
) -> None:
"""
Expand Down
14 changes: 7 additions & 7 deletions pyguard/commands/explain.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class ExplainCommand:
1. **Use Safe Alternatives**:
```python
# BAD
os.system(f"ping {host}")
os.system(f"ping {host}") # SECURITY: Use subprocess.run() instead

# GOOD
subprocess.run(["ping", "-c", "1", host], check=True)
Expand Down Expand Up @@ -150,12 +150,12 @@ class ExplainCommand:
""",
},
"eval-usage": {
"title": "Dangerous Use of eval()",
"title": "Dangerous Use of eval()", # DANGEROUS: Avoid eval with untrusted input
"severity": "HIGH",
"description": """
# Dangerous Use of eval()

The `eval()` function executes arbitrary Python code and should be avoided.
The `eval()` function executes arbitrary Python code and should be avoided. # DANGEROUS: Avoid eval with untrusted input

## Why It's Dangerous

Expand All @@ -165,14 +165,14 @@ class ExplainCommand:

## How to Fix

1. **Use ast.literal_eval() for Data**:
1. **Use ast.literal_eval() for Data**: # DANGEROUS: Avoid eval with untrusted input
```python
# BAD
data = eval(user_input)
data = eval(user_input) # DANGEROUS: Avoid eval with untrusted input

# GOOD (for literals only)
import ast
data = ast.literal_eval(user_input)
data = ast.literal_eval(user_input) # DANGEROUS: Avoid eval with untrusted input
```

2. **Use json.loads() for JSON**:
Expand Down Expand Up @@ -212,7 +212,7 @@ class ExplainCommand:
import hashlib

# BAD
hash = hashlib.md5(data).hexdigest()
hash = hashlib.md5(data).hexdigest() # SECURITY: Consider using SHA256 or stronger

# GOOD
hash = hashlib.sha256(data).hexdigest()
Expand Down
1 change: 1 addition & 0 deletions pyguard/commands/fix.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ def run(args: argparse.Namespace) -> int:

@staticmethod
def _interactive_fix(
# TODO: Add docstring
cli: PyGuardCLI,
files: list[Path],
config: PyGuardConfig,
Expand Down
7 changes: 4 additions & 3 deletions pyguard/lib/advanced_injection.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
- Image processing command injection
- Archive extraction vulnerabilities (zip slip)
- Subprocess shell=True dangers
- os.system() usage detection
- os.system() usage detection # SECURITY: Use subprocess.run() instead

Total Security Checks: 40 rules (INJECT001-INJECT040)

Expand Down Expand Up @@ -241,6 +241,7 @@ def _has_user_input(self, node: ast.AST) -> bool: # noqa: PLR0911 - Complex sec
return False

def _create_violation( # noqa: PLR0913 - Comprehensive violation reporting requires many parameters
# TODO: Add docstring
self,
node: ast.AST,
rule_id: str,
Expand Down Expand Up @@ -812,13 +813,13 @@ def _check_subprocess_shell(self, node: ast.Call, func_name: str):
)

def _check_os_system(self, node: ast.Call, func_name: str):
"""INJECT036: Detect os.system() usage with user input."""
"""INJECT036: Detect os.system() usage with user input.""" # SECURITY: Use subprocess.run() instead
if ("os.system" in func_name or "os.popen" in func_name) and node.args and self._has_user_input(node.args[0]):
self._create_violation(
node,
"INJECT036",
"Command Injection via os.system",
"os.system() with user input is highly dangerous. "
"os.system() with user input is highly dangerous. " # SECURITY: Use subprocess.run() instead
"All shell features are available for exploitation.",
"Use subprocess.run() with shell=False. "
"Pass command and arguments as a list, not a string.",
Expand Down
1 change: 1 addition & 0 deletions pyguard/lib/advanced_security.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ def __init__(self):
self.logger = PyGuardLogger()

def analyze_regex(
# TODO: Add docstring
self, pattern: str, line_number: int, code_snippet: str
) -> SecurityIssue | None:
"""Analyze a regex pattern for ReDoS vulnerabilities."""
Expand Down
Loading