feat: add unified code quality dashboard with multiple validators
- Add validator_type field to scans and violations (architecture, security, performance) - Create security validator with SEC-xxx rules - Create performance validator with PERF-xxx rules - Add base validator class for shared functionality - Add validate_all.py script to run all validators - Update code quality service with validator type filtering - Add validator type tabs to dashboard UI - Add validator type filter to violations list - Update stats response with per-validator breakdown - Add security and performance rules documentation - Add chat-bubble icons to icon library 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
570
docs/development/performance-rules.md
Normal file
570
docs/development/performance-rules.md
Normal file
@@ -0,0 +1,570 @@
|
||||
# Performance Rules Reference
|
||||
|
||||
This document provides a comprehensive reference for all performance rules enforced by the `scripts/validate_performance.py` validator.
|
||||
|
||||
## Overview
|
||||
|
||||
The performance validator identifies potential performance issues and enforces best practices for efficient code across the codebase. Rules are organized by category and severity level.
|
||||
|
||||
**Version:** 1.0
|
||||
**Total Rules:** 70
|
||||
**Configuration Directory:** `.performance-rules/`
|
||||
|
||||
## Running the Validator
|
||||
|
||||
### Using Python Directly
|
||||
|
||||
```bash
|
||||
# Check all files
|
||||
python scripts/validate_performance.py
|
||||
|
||||
# Verbose output
|
||||
python scripts/validate_performance.py -v
|
||||
|
||||
# Errors only
|
||||
python scripts/validate_performance.py --errors-only
|
||||
|
||||
# JSON output (for CI/CD)
|
||||
python scripts/validate_performance.py --json
|
||||
```
|
||||
|
||||
### Using the Unified Validator
|
||||
|
||||
```bash
|
||||
# Run performance checks only
|
||||
python scripts/validate_all.py --performance
|
||||
|
||||
# Run all validators
|
||||
python scripts/validate_all.py
|
||||
```
|
||||
|
||||
## Severity Levels
|
||||
|
||||
| Severity | Description | Exit Code | Action Required |
|
||||
|----------|-------------|-----------|-----------------|
|
||||
| **Error** | Critical performance issue | 1 | Must fix |
|
||||
| **Warning** | Performance concern | 0 | Should fix |
|
||||
| **Info** | Performance suggestion | 0 | Consider implementing |
|
||||
|
||||
---
|
||||
|
||||
## Database Performance Rules (PERF-001 to PERF-015)
|
||||
|
||||
### PERF-001: N+1 Query Detection
|
||||
**Severity:** Warning
|
||||
|
||||
Accessing relationships in loops causes N+1 queries. For each item in a list, a separate query is executed.
|
||||
|
||||
```python
|
||||
# Bad - N+1 queries
|
||||
orders = db.query(Order).all()
|
||||
for order in orders:
|
||||
customer_name = order.customer.name # N+1 query!
|
||||
|
||||
# Good - Eager loading
|
||||
orders = db.query(Order).options(
|
||||
joinedload(Order.customer)
|
||||
).all()
|
||||
for order in orders:
|
||||
customer_name = order.customer.name # Already loaded
|
||||
```
|
||||
|
||||
### PERF-002: Eager Loading for Known Relationships
|
||||
**Severity:** Info
|
||||
|
||||
When you always need related data, use eager loading to reduce database round trips.
|
||||
|
||||
### PERF-003: Query Result Limiting
|
||||
**Severity:** Warning
|
||||
|
||||
All list queries should have pagination or limits. Unbounded queries can cause memory issues and slow responses.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
all_products = db.query(Product).all()
|
||||
|
||||
# Good
|
||||
products = db.query(Product).limit(100).all()
|
||||
# Or with pagination
|
||||
products = db.query(Product).offset(skip).limit(limit).all()
|
||||
```
|
||||
|
||||
### PERF-004: Index Usage for Filtered Columns
|
||||
**Severity:** Info
|
||||
|
||||
Columns frequently used in WHERE clauses should have indexes:
|
||||
- Foreign keys (vendor_id, customer_id)
|
||||
- Status fields
|
||||
- Date fields used for filtering
|
||||
- Boolean flags used for filtering
|
||||
|
||||
### PERF-005: Select Only Needed Columns
|
||||
**Severity:** Info
|
||||
|
||||
For large tables, select only the columns you need. Use `.with_entities()` or `load_only()` to reduce data transfer.
|
||||
|
||||
```python
|
||||
# Good - Only load id and name columns
|
||||
products = db.query(Product).options(
|
||||
load_only(Product.id, Product.name)
|
||||
).all()
|
||||
```
|
||||
|
||||
### PERF-006: Bulk Operations for Multiple Records
|
||||
**Severity:** Warning
|
||||
|
||||
Use bulk operations instead of individual operations in loops.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
for item in items:
|
||||
product = Product(**item)
|
||||
db.add(product)
|
||||
|
||||
# Good
|
||||
products = [Product(**item) for item in items]
|
||||
db.add_all(products)
|
||||
```
|
||||
|
||||
### PERF-007: Connection Pool Configuration
|
||||
**Severity:** Info
|
||||
|
||||
Configure database connection pool for optimal performance:
|
||||
- `pool_size`: Number of persistent connections
|
||||
- `max_overflow`: Additional connections allowed
|
||||
- `pool_pre_ping`: Check connection health
|
||||
- `pool_recycle`: Recycle connections periodically
|
||||
|
||||
### PERF-008: Use EXISTS for Existence Checks
|
||||
**Severity:** Info
|
||||
|
||||
Use EXISTS or `.first() is not None` instead of `count() > 0`. EXISTS stops at first match, count() scans all matches.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
if db.query(Order).filter_by(customer_id=id).count() > 0:
|
||||
...
|
||||
|
||||
# Good
|
||||
exists_query = db.query(exists().where(Order.customer_id == id))
|
||||
if db.scalar(exists_query):
|
||||
...
|
||||
```
|
||||
|
||||
### PERF-009: Batch Updates Instead of Loops
|
||||
**Severity:** Warning
|
||||
|
||||
Use `.update()` with filters instead of updating in a loop. One UPDATE statement is faster than N individual updates.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
for product in products:
|
||||
product.is_active = False
|
||||
db.add(product)
|
||||
|
||||
# Good
|
||||
db.query(Product).filter(
|
||||
Product.id.in_(product_ids)
|
||||
).update({"is_active": False}, synchronize_session=False)
|
||||
```
|
||||
|
||||
### PERF-010: Avoid SELECT * Patterns
|
||||
**Severity:** Info
|
||||
|
||||
When you only need specific columns, don't load entire rows. This reduces memory usage and network transfer.
|
||||
|
||||
### PERF-011: Use Appropriate Join Strategies
|
||||
**Severity:** Info
|
||||
|
||||
Choose the right join strategy:
|
||||
- `joinedload`: Few related items, always needed
|
||||
- `selectinload`: Many related items, always needed
|
||||
- `subqueryload`: Complex queries, many related items
|
||||
- `lazyload`: Rarely accessed relationships
|
||||
|
||||
### PERF-012: Transaction Scope Optimization
|
||||
**Severity:** Warning
|
||||
|
||||
Keep transactions short and focused:
|
||||
- Don't hold transactions during I/O
|
||||
- Commit after bulk operations
|
||||
- Use read-only transactions when possible
|
||||
|
||||
### PERF-013: Query Result Caching
|
||||
**Severity:** Info
|
||||
|
||||
Consider caching for frequently accessed, rarely changed data like configuration tables and reference data.
|
||||
|
||||
### PERF-014: Composite Indexes for Multi-Column Filters
|
||||
**Severity:** Info
|
||||
|
||||
Queries filtering on multiple columns benefit from composite indexes. Order columns by selectivity (most selective first).
|
||||
|
||||
### PERF-015: Avoid Correlated Subqueries
|
||||
**Severity:** Info
|
||||
|
||||
Correlated subqueries execute once per row. Use JOINs or window functions instead when possible.
|
||||
|
||||
---
|
||||
|
||||
## Caching Performance Rules (PERF-016 to PERF-025)
|
||||
|
||||
### PERF-016: Cache Expensive Computations
|
||||
**Severity:** Info
|
||||
|
||||
Computationally expensive operations should be cached: complex aggregations, external API results, template rendering, data transformations.
|
||||
|
||||
### PERF-017: Cache Key Includes Tenant Context
|
||||
**Severity:** Warning
|
||||
|
||||
Multi-tenant cache keys must include vendor_id. Otherwise, cached data may leak between tenants.
|
||||
|
||||
```python
|
||||
# Bad - Cache key missing tenant context
|
||||
@cache.memoize()
|
||||
def get_products():
|
||||
return db.query(Product).all()
|
||||
|
||||
# Good - Cache key includes vendor_id
|
||||
@cache.memoize()
|
||||
def get_products(vendor_id: int):
|
||||
return db.query(Product).filter_by(vendor_id=vendor_id).all()
|
||||
```
|
||||
|
||||
### PERF-018: Cache TTL Configuration
|
||||
**Severity:** Info
|
||||
|
||||
Cache entries should have appropriate TTL:
|
||||
- Short TTL (1-5 min): Frequently changing data
|
||||
- Medium TTL (5-60 min): Semi-static data
|
||||
- Long TTL (1+ hour): Reference data
|
||||
|
||||
### PERF-019: Cache Invalidation Strategy
|
||||
**Severity:** Warning
|
||||
|
||||
Define cache invalidation strategy: time-based (TTL), event-based (on data change), or manual (admin action).
|
||||
|
||||
### PERF-020: Response Caching Headers
|
||||
**Severity:** Info
|
||||
|
||||
API responses can use HTTP caching headers: Cache-Control for browser/CDN caching, ETag for conditional requests.
|
||||
|
||||
### PERF-021 to PERF-025: Additional Caching Rules
|
||||
**Severity:** Info
|
||||
|
||||
Query result caching, session-level caching, distributed cache for scalability, cache warming strategy, and cache hit rate monitoring.
|
||||
|
||||
---
|
||||
|
||||
## API Performance Rules (PERF-026 to PERF-035)
|
||||
|
||||
### PERF-026: Pagination Required for List Endpoints
|
||||
**Severity:** Error
|
||||
|
||||
All list endpoints must support pagination. Unbounded lists cause memory exhaustion and slow response times.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
@router.get("/products")
|
||||
def list_products(db: Session):
|
||||
return db.query(Product).all()
|
||||
|
||||
# Good
|
||||
@router.get("/products")
|
||||
def list_products(
|
||||
skip: int = 0,
|
||||
limit: int = Query(default=20, le=100),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
return db.query(Product).offset(skip).limit(limit).all()
|
||||
```
|
||||
|
||||
### PERF-027: Reasonable Default Page Sizes
|
||||
**Severity:** Warning
|
||||
|
||||
Default page sizes should be reasonable:
|
||||
- Default: 20-50 items
|
||||
- Maximum: 100-200 items
|
||||
|
||||
```python
|
||||
# Bad
|
||||
limit: int = Query(default=500, le=10000)
|
||||
|
||||
# Good
|
||||
limit: int = Query(default=20, ge=1, le=100)
|
||||
```
|
||||
|
||||
### PERF-028: Response Compression
|
||||
**Severity:** Info
|
||||
|
||||
Enable response compression for large responses with GZip or Brotli.
|
||||
|
||||
### PERF-029: Efficient Serialization
|
||||
**Severity:** Info
|
||||
|
||||
Use Pydantic's `response_model` for efficient serialization. Avoid manual dict conversion.
|
||||
|
||||
### PERF-030: Avoid Redundant Queries in Response
|
||||
**Severity:** Warning
|
||||
|
||||
Don't trigger lazy-loaded relationships during serialization. Use eager loading or carefully control serialization.
|
||||
|
||||
### PERF-031: Streaming for Large Responses
|
||||
**Severity:** Info
|
||||
|
||||
Use streaming responses for large data: file downloads, large exports (CSV, JSON), real-time data feeds.
|
||||
|
||||
### PERF-032 to PERF-035: Additional API Rules
|
||||
**Severity:** Info/Warning
|
||||
|
||||
Conditional requests support, field selection support, avoid deep nesting in responses, and endpoint response time monitoring.
|
||||
|
||||
---
|
||||
|
||||
## Async & Concurrency Rules (PERF-036 to PERF-045)
|
||||
|
||||
### PERF-036: Async for I/O Operations
|
||||
**Severity:** Info
|
||||
|
||||
Use async for I/O-bound operations: database queries, external API calls, file operations.
|
||||
|
||||
### PERF-037: Parallel Independent Operations
|
||||
**Severity:** Warning
|
||||
|
||||
Multiple independent async operations should run in parallel. Use `asyncio.gather()` instead of sequential awaits.
|
||||
|
||||
```python
|
||||
# Bad - Sequential awaits
|
||||
user = await get_user(user_id)
|
||||
orders = await get_orders(user_id)
|
||||
preferences = await get_preferences(user_id)
|
||||
|
||||
# Good - Parallel execution
|
||||
user, orders, preferences = await asyncio.gather(
|
||||
get_user(user_id),
|
||||
get_orders(user_id),
|
||||
get_preferences(user_id)
|
||||
)
|
||||
```
|
||||
|
||||
### PERF-038: Background Tasks for Slow Operations
|
||||
**Severity:** Warning
|
||||
|
||||
Operations taking > 500ms should run in background: email sending, report generation, external API syncs, file processing.
|
||||
|
||||
### PERF-039: Connection Pooling for HTTP Clients
|
||||
**Severity:** Warning
|
||||
|
||||
HTTP clients should reuse connections. Create client once, not per request.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
def fetch_data(url):
|
||||
response = requests.get(url) # New connection each time
|
||||
|
||||
# Good
|
||||
async with httpx.AsyncClient() as client:
|
||||
response = await client.get(url)
|
||||
```
|
||||
|
||||
### PERF-040: Timeout Configuration
|
||||
**Severity:** Error
|
||||
|
||||
All external calls must have timeouts. Without timeouts, requests can hang indefinitely.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
response = requests.get(url)
|
||||
|
||||
# Good
|
||||
response = requests.get(url, timeout=30)
|
||||
```
|
||||
|
||||
### PERF-041 to PERF-045: Additional Async Rules
|
||||
**Severity:** Info
|
||||
|
||||
Connection pool limits, retry with backoff, circuit breaker pattern, task queues for heavy processing, and worker pool sizing.
|
||||
|
||||
---
|
||||
|
||||
## Memory Management Rules (PERF-046 to PERF-055)
|
||||
|
||||
### PERF-046: Generators for Large Datasets
|
||||
**Severity:** Warning
|
||||
|
||||
Use generators/iterators for processing large datasets. Avoids loading everything into memory at once.
|
||||
|
||||
```python
|
||||
# Bad - Loads all into memory
|
||||
products = db.query(Product).all()
|
||||
for product in products:
|
||||
process(product)
|
||||
|
||||
# Good - Stream processing
|
||||
for product in db.query(Product).yield_per(100):
|
||||
process(product)
|
||||
```
|
||||
|
||||
### PERF-047: Stream Large File Uploads
|
||||
**Severity:** Warning
|
||||
|
||||
Large files should be streamed to disk, not held in memory.
|
||||
|
||||
```python
|
||||
# Bad - Entire file in memory
|
||||
content = await file.read()
|
||||
with open(path, 'wb') as f:
|
||||
f.write(content)
|
||||
|
||||
# Good - Streaming
|
||||
with open(path, 'wb') as f:
|
||||
while chunk := await file.read(8192):
|
||||
f.write(chunk)
|
||||
```
|
||||
|
||||
### PERF-048: Chunked Processing for Imports
|
||||
**Severity:** Warning
|
||||
|
||||
Bulk imports should process in chunks: read in batches, commit in batches, report progress periodically.
|
||||
|
||||
### PERF-049: Context Managers for Resources
|
||||
**Severity:** Error
|
||||
|
||||
Use context managers for file operations. Ensures resources are properly released.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
f = open('file.txt')
|
||||
content = f.read()
|
||||
f.close() # May not run if exception
|
||||
|
||||
# Good
|
||||
with open('file.txt') as f:
|
||||
content = f.read()
|
||||
```
|
||||
|
||||
### PERF-050 to PERF-055: Additional Memory Rules
|
||||
**Severity:** Info
|
||||
|
||||
Limit in-memory collections, string concatenation efficiency, efficient data structures, object pooling, weak references for caches, and slots for frequently instantiated classes.
|
||||
|
||||
---
|
||||
|
||||
## Frontend Performance Rules (PERF-056 to PERF-070)
|
||||
|
||||
### PERF-056: Debounce Search Inputs
|
||||
**Severity:** Warning
|
||||
|
||||
Search inputs should debounce API calls. Recommended: 300-500ms delay.
|
||||
|
||||
```javascript
|
||||
// Bad
|
||||
<input @input="searchProducts($event.target.value)">
|
||||
|
||||
// Good
|
||||
<input @input="debouncedSearch($event.target.value)">
|
||||
// With: debouncedSearch = debounce(searchProducts, 300)
|
||||
```
|
||||
|
||||
### PERF-057: Lazy Load Off-Screen Content
|
||||
**Severity:** Info
|
||||
|
||||
Defer loading of off-screen content: modals, inactive tabs, below-the-fold content, images.
|
||||
|
||||
### PERF-058: Image Optimization
|
||||
**Severity:** Warning
|
||||
|
||||
Images should be optimized:
|
||||
- Use appropriate formats (WebP, AVIF)
|
||||
- Serve responsive sizes
|
||||
- Lazy load off-screen images
|
||||
- Use CDN for static assets
|
||||
|
||||
```html
|
||||
<img src="image.webp" loading="lazy" alt="Product">
|
||||
```
|
||||
|
||||
### PERF-059: Minimize Alpine.js Watchers
|
||||
**Severity:** Info
|
||||
|
||||
Excessive `$watch` calls impact performance. Use computed properties or event handlers instead.
|
||||
|
||||
### PERF-060: Virtual Scrolling for Long Lists
|
||||
**Severity:** Info
|
||||
|
||||
Lists with 100+ items should use virtual scrolling. Only render visible items in the viewport.
|
||||
|
||||
### PERF-061: Minimize Bundle Size
|
||||
**Severity:** Info
|
||||
|
||||
Reduce JavaScript bundle size: import only needed modules, use tree-shaking, split code by route.
|
||||
|
||||
### PERF-062: Reasonable Polling Intervals
|
||||
**Severity:** Warning
|
||||
|
||||
Polling should be >= 10 seconds for non-critical updates.
|
||||
|
||||
```javascript
|
||||
// Bad
|
||||
setInterval(fetchUpdates, 1000); // Every second
|
||||
|
||||
// Good
|
||||
setInterval(fetchUpdates, 30000); // Every 30 seconds
|
||||
```
|
||||
|
||||
### PERF-063 to PERF-070: Additional Frontend Rules
|
||||
**Severity:** Info/Warning
|
||||
|
||||
CSS containment, avoid layout thrashing, CSS animations over JavaScript, preload critical resources, defer non-critical JavaScript, minimize DOM nodes, efficient event handlers, and cache DOM queries.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
All rules are defined in `.performance-rules/` directory:
|
||||
|
||||
```
|
||||
.performance-rules/
|
||||
├── _main.yaml # Main configuration
|
||||
├── database.yaml # PERF-001 to PERF-015
|
||||
├── caching.yaml # PERF-016 to PERF-025
|
||||
├── api.yaml # PERF-026 to PERF-035
|
||||
├── async.yaml # PERF-036 to PERF-045
|
||||
├── memory.yaml # PERF-046 to PERF-055
|
||||
└── frontend.yaml # PERF-056 to PERF-070
|
||||
```
|
||||
|
||||
## Suppressing Rules
|
||||
|
||||
Use noqa comments to suppress specific rules:
|
||||
|
||||
```python
|
||||
# noqa: PERF-003 - This is intentionally unbounded for admin export
|
||||
products = db.query(Product).all()
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Architecture Rules](architecture-rules.md)
|
||||
- [Security Rules](security-rules.md)
|
||||
- [Code Quality Guide](code-quality.md)
|
||||
- [Contributing Guide](contributing.md)
|
||||
|
||||
---
|
||||
|
||||
## Summary Statistics
|
||||
|
||||
| Category | Rules | Errors | Warnings | Info |
|
||||
|----------|-------|--------|----------|------|
|
||||
| Database | 15 | 0 | 5 | 10 |
|
||||
| Caching | 10 | 0 | 2 | 8 |
|
||||
| API | 10 | 1 | 3 | 6 |
|
||||
| Async & Concurrency | 10 | 1 | 4 | 5 |
|
||||
| Memory Management | 10 | 1 | 4 | 5 |
|
||||
| Frontend | 15 | 0 | 4 | 11 |
|
||||
| **Total** | **70** | **3** | **22** | **45** |
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-12-21
|
||||
**Version:** 1.0
|
||||
560
docs/development/security-rules.md
Normal file
560
docs/development/security-rules.md
Normal file
@@ -0,0 +1,560 @@
|
||||
# Security Rules Reference
|
||||
|
||||
This document provides a comprehensive reference for all security rules enforced by the `scripts/validate_security.py` validator.
|
||||
|
||||
## Overview
|
||||
|
||||
The security validator identifies potential security vulnerabilities and enforces security best practices across the codebase. Rules are organized by category and severity level.
|
||||
|
||||
**Version:** 1.0
|
||||
**Total Rules:** 60
|
||||
**Configuration Directory:** `.security-rules/`
|
||||
|
||||
## Running the Validator
|
||||
|
||||
### Using Python Directly
|
||||
|
||||
```bash
|
||||
# Check all files
|
||||
python scripts/validate_security.py
|
||||
|
||||
# Verbose output
|
||||
python scripts/validate_security.py -v
|
||||
|
||||
# Errors only
|
||||
python scripts/validate_security.py --errors-only
|
||||
|
||||
# JSON output (for CI/CD)
|
||||
python scripts/validate_security.py --json
|
||||
```
|
||||
|
||||
### Using the Unified Validator
|
||||
|
||||
```bash
|
||||
# Run security checks only
|
||||
python scripts/validate_all.py --security
|
||||
|
||||
# Run all validators
|
||||
python scripts/validate_all.py
|
||||
```
|
||||
|
||||
## Severity Levels
|
||||
|
||||
| Severity | Description | Exit Code | Action Required |
|
||||
|----------|-------------|-----------|-----------------|
|
||||
| **Error** | Critical security vulnerability | 1 | Must fix immediately |
|
||||
| **Warning** | Security concern | 0 | Should fix |
|
||||
| **Info** | Security suggestion | 0 | Consider implementing |
|
||||
|
||||
---
|
||||
|
||||
## Authentication Rules (SEC-001 to SEC-010)
|
||||
|
||||
### SEC-001: No Hardcoded Credentials
|
||||
**Severity:** Error
|
||||
|
||||
Credentials must never be hardcoded in source code. Use environment variables or secret management.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
api_key = "sk-1234567890abcdef"
|
||||
password = "admin123"
|
||||
|
||||
# Good
|
||||
api_key = os.environ.get("API_KEY")
|
||||
password = settings.admin_password
|
||||
```
|
||||
|
||||
### SEC-002: JWT Expiry Enforcement
|
||||
**Severity:** Error
|
||||
|
||||
All JWT tokens must have expiration claims. Access tokens should expire in 15-60 minutes.
|
||||
|
||||
### SEC-003: Password Hashing Required
|
||||
**Severity:** Error
|
||||
|
||||
Passwords must be hashed using bcrypt, argon2, or scrypt. Never store or compare passwords in plain text.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
if user.password == password:
|
||||
...
|
||||
|
||||
# Good
|
||||
if bcrypt.checkpw(password.encode(), user.hashed_password):
|
||||
...
|
||||
```
|
||||
|
||||
### SEC-004: Session Regeneration After Auth
|
||||
**Severity:** Warning
|
||||
|
||||
Session IDs should be regenerated after authentication to prevent session fixation attacks.
|
||||
|
||||
### SEC-005: Brute Force Protection
|
||||
**Severity:** Warning
|
||||
|
||||
Login endpoints should implement rate limiting or account lockout after failed attempts.
|
||||
|
||||
### SEC-006: Secure Password Reset
|
||||
**Severity:** Warning
|
||||
|
||||
Password reset tokens must be cryptographically random, expire within 1 hour, and be single-use.
|
||||
|
||||
### SEC-007: Authentication on Sensitive Endpoints
|
||||
**Severity:** Error
|
||||
|
||||
All endpoints except public ones must require authentication.
|
||||
|
||||
### SEC-008: Token in Authorization Header
|
||||
**Severity:** Warning
|
||||
|
||||
JWT tokens should be sent in Authorization header, not in URL parameters.
|
||||
|
||||
### SEC-009: Logout Invalidates Tokens
|
||||
**Severity:** Warning
|
||||
|
||||
Logout should invalidate or blacklist tokens.
|
||||
|
||||
### SEC-010: Multi-Factor Authentication Support
|
||||
**Severity:** Info
|
||||
|
||||
Consider implementing MFA for sensitive operations.
|
||||
|
||||
---
|
||||
|
||||
## Injection Prevention Rules (SEC-011 to SEC-020)
|
||||
|
||||
### SEC-011: No Raw SQL Queries
|
||||
**Severity:** Error
|
||||
|
||||
Use SQLAlchemy ORM or parameterized queries only. Never concatenate user input into SQL strings.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
db.execute(f"SELECT * FROM users WHERE name = '{name}'")
|
||||
|
||||
# Good
|
||||
db.query(User).filter(User.name == name)
|
||||
```
|
||||
|
||||
### SEC-012: No Shell Command Injection
|
||||
**Severity:** Error
|
||||
|
||||
Never use `shell=True` with subprocess. Use subprocess with list arguments.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
subprocess.run(f"convert {filename}", shell=True)
|
||||
os.system(f"rm {filename}")
|
||||
|
||||
# Good
|
||||
subprocess.run(["convert", filename])
|
||||
```
|
||||
|
||||
### SEC-013: No Code Execution
|
||||
**Severity:** Error
|
||||
|
||||
Never use `eval()` or `exec()` with user input.
|
||||
|
||||
### SEC-014: Path Traversal Prevention
|
||||
**Severity:** Error
|
||||
|
||||
Validate file paths to prevent directory traversal. Use `secure_filename()` for uploads.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
path = f"/uploads/{filename}"
|
||||
|
||||
# Good
|
||||
from werkzeug.utils import secure_filename
|
||||
path = f"/uploads/{secure_filename(filename)}"
|
||||
```
|
||||
|
||||
### SEC-015: XSS Prevention in Templates
|
||||
**Severity:** Error
|
||||
|
||||
Use safe output methods in templates. Prefer `x-text` over `x-html` in Alpine.js.
|
||||
|
||||
```html
|
||||
<!-- Bad - vulnerable to XSS -->
|
||||
<div x-html="userInput"></div>
|
||||
|
||||
<!-- Good - safe from XSS -->
|
||||
<div x-text="userInput"></div>
|
||||
```
|
||||
|
||||
### SEC-016: LDAP Injection Prevention
|
||||
**Severity:** Error
|
||||
|
||||
Escape special characters in LDAP queries.
|
||||
|
||||
### SEC-017: XML External Entity Prevention
|
||||
**Severity:** Error
|
||||
|
||||
Disable external entities when parsing XML. Use `defusedxml`.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
import xml.etree.ElementTree as ET
|
||||
tree = ET.parse(user_file)
|
||||
|
||||
# Good
|
||||
import defusedxml.ElementTree as ET
|
||||
tree = ET.parse(user_file)
|
||||
```
|
||||
|
||||
### SEC-018: Template Injection Prevention
|
||||
**Severity:** Error
|
||||
|
||||
Never render user input as template code.
|
||||
|
||||
### SEC-019: SSRF Prevention
|
||||
**Severity:** Warning
|
||||
|
||||
Validate URLs before making external requests. Whitelist allowed domains.
|
||||
|
||||
### SEC-020: Deserialization Safety
|
||||
**Severity:** Error
|
||||
|
||||
Never deserialize untrusted data with pickle. Use `yaml.safe_load()` instead of `yaml.load()`.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
data = pickle.loads(user_data)
|
||||
config = yaml.load(user_config)
|
||||
|
||||
# Good
|
||||
config = yaml.safe_load(user_config)
|
||||
data = json.loads(user_data)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Protection Rules (SEC-021 to SEC-030)
|
||||
|
||||
### SEC-021: PII Logging Prevention
|
||||
**Severity:** Error
|
||||
|
||||
Never log passwords, tokens, credit cards, or sensitive PII.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
logger.info(f"User login: {username}, password: {password}")
|
||||
|
||||
# Good
|
||||
logger.info(f"User login: {username}")
|
||||
```
|
||||
|
||||
### SEC-022: Sensitive Data in URLs
|
||||
**Severity:** Error
|
||||
|
||||
Sensitive data should not appear in URL query parameters. Use POST body or headers instead.
|
||||
|
||||
### SEC-023: Mass Assignment Prevention
|
||||
**Severity:** Warning
|
||||
|
||||
Use explicit field assignment, not `**kwargs` from user input.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
user = User(**request.json)
|
||||
|
||||
# Good
|
||||
user = User(
|
||||
name=request.json.get("name"),
|
||||
email=request.json.get("email")
|
||||
)
|
||||
```
|
||||
|
||||
### SEC-024: Error Message Information Leakage
|
||||
**Severity:** Error
|
||||
|
||||
Error messages should not reveal internal details. No stack traces to users.
|
||||
|
||||
### SEC-025: Secure Cookie Settings
|
||||
**Severity:** Error
|
||||
|
||||
Cookies must have Secure, HttpOnly, SameSite attributes.
|
||||
|
||||
### SEC-026: Encryption for Sensitive Data at Rest
|
||||
**Severity:** Info
|
||||
|
||||
Consider encrypting sensitive data stored in the database.
|
||||
|
||||
### SEC-027: Data Retention Limits
|
||||
**Severity:** Info
|
||||
|
||||
Implement data retention policies.
|
||||
|
||||
### SEC-028: Response Data Filtering
|
||||
**Severity:** Warning
|
||||
|
||||
API responses should not include sensitive internal fields. Use Pydantic response models.
|
||||
|
||||
### SEC-029: File Upload Validation
|
||||
**Severity:** Error
|
||||
|
||||
Validate uploaded files by extension AND content type. Limit file size.
|
||||
|
||||
### SEC-030: Backup Encryption
|
||||
**Severity:** Info
|
||||
|
||||
Database backups should be encrypted.
|
||||
|
||||
---
|
||||
|
||||
## API Security Rules (SEC-031 to SEC-040)
|
||||
|
||||
### SEC-031: CORS Origin Validation
|
||||
**Severity:** Error
|
||||
|
||||
CORS must not allow all origins in production. Specify allowed origins explicitly.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
allow_origins=["*"]
|
||||
|
||||
# Good
|
||||
allow_origins=["https://example.com", "https://api.example.com"]
|
||||
```
|
||||
|
||||
### SEC-032: Rate Limiting on Sensitive Endpoints
|
||||
**Severity:** Warning
|
||||
|
||||
Auth, password reset, and payment endpoints need rate limiting.
|
||||
|
||||
### SEC-033: Security Headers
|
||||
**Severity:** Warning
|
||||
|
||||
Configure security headers like X-Content-Type-Options, X-Frame-Options, Content-Security-Policy.
|
||||
|
||||
### SEC-034: HTTPS Enforcement
|
||||
**Severity:** Error
|
||||
|
||||
External URLs must use HTTPS. HTTP is only acceptable for localhost.
|
||||
|
||||
### SEC-035: Request Size Limits
|
||||
**Severity:** Warning
|
||||
|
||||
Limit request body size to prevent DoS attacks.
|
||||
|
||||
### SEC-036: Input Validation with Pydantic
|
||||
**Severity:** Warning
|
||||
|
||||
All API inputs should be validated using Pydantic models.
|
||||
|
||||
### SEC-037: API Versioning
|
||||
**Severity:** Info
|
||||
|
||||
APIs should be versioned for security update isolation.
|
||||
|
||||
### SEC-038: Method Restrictions
|
||||
**Severity:** Warning
|
||||
|
||||
Endpoints should only allow necessary HTTP methods.
|
||||
|
||||
### SEC-039: Authentication Bypass Prevention
|
||||
**Severity:** Error
|
||||
|
||||
Ensure authentication cannot be bypassed.
|
||||
|
||||
### SEC-040: Timeout Configuration
|
||||
**Severity:** Warning
|
||||
|
||||
All external calls must have timeouts configured.
|
||||
|
||||
---
|
||||
|
||||
## Cryptography Rules (SEC-041 to SEC-050)
|
||||
|
||||
### SEC-041: Strong Hashing Algorithms
|
||||
**Severity:** Error
|
||||
|
||||
Use bcrypt, argon2, scrypt for passwords. Use SHA-256 or stronger for general hashing. Never use MD5 or SHA1.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
import hashlib
|
||||
password_hash = hashlib.md5(password.encode()).hexdigest()
|
||||
|
||||
# Good
|
||||
import bcrypt
|
||||
password_hash = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
|
||||
```
|
||||
|
||||
### SEC-042: Secure Random Generation
|
||||
**Severity:** Error
|
||||
|
||||
Use the `secrets` module for security-sensitive randomness. Never use `random` module for tokens or keys.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
import random
|
||||
token = ''.join(random.choices(string.ascii_letters, k=32))
|
||||
|
||||
# Good
|
||||
import secrets
|
||||
token = secrets.token_urlsafe(32)
|
||||
```
|
||||
|
||||
### SEC-043: No Hardcoded Encryption Keys
|
||||
**Severity:** Error
|
||||
|
||||
Encryption keys must come from environment variables or secret management services.
|
||||
|
||||
### SEC-044: Strong Encryption Algorithms
|
||||
**Severity:** Error
|
||||
|
||||
Use AES-256 or ChaCha20. Never use DES, 3DES, or RC4.
|
||||
|
||||
### SEC-045: Proper IV/Nonce Usage
|
||||
**Severity:** Error
|
||||
|
||||
Encryption IVs and nonces must be randomly generated and unique per encryption.
|
||||
|
||||
### SEC-046: TLS Version Requirements
|
||||
**Severity:** Warning
|
||||
|
||||
Enforce TLS 1.2 or higher. Disable SSLv2, SSLv3, TLS 1.0, TLS 1.1.
|
||||
|
||||
### SEC-047: Certificate Verification
|
||||
**Severity:** Error
|
||||
|
||||
Always verify SSL certificates. Never disable verification in production.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
requests.get(url, verify=False)
|
||||
|
||||
# Good
|
||||
requests.get(url, verify=True)
|
||||
```
|
||||
|
||||
### SEC-048: Key Derivation for Passwords
|
||||
**Severity:** Warning
|
||||
|
||||
When deriving encryption keys from passwords, use PBKDF2 with 100K+ iterations, Argon2, or scrypt.
|
||||
|
||||
### SEC-049: Secure Key Storage
|
||||
**Severity:** Info
|
||||
|
||||
Encryption keys should be stored in environment variables, secret management, or HSMs.
|
||||
|
||||
### SEC-050: Key Rotation Support
|
||||
**Severity:** Info
|
||||
|
||||
Implement key rotation with multiple key versions.
|
||||
|
||||
---
|
||||
|
||||
## Audit & Logging Rules (SEC-051 to SEC-060)
|
||||
|
||||
### SEC-051: Authentication Event Logging
|
||||
**Severity:** Warning
|
||||
|
||||
Log authentication events including successful logins, failed attempts, logouts, and password changes.
|
||||
|
||||
### SEC-052: Admin Action Audit Trail
|
||||
**Severity:** Warning
|
||||
|
||||
All admin operations should be logged with admin user ID, action performed, target resource, timestamp, and IP address.
|
||||
|
||||
### SEC-053: Data Modification Logging
|
||||
**Severity:** Info
|
||||
|
||||
Log create/update/delete on sensitive data like user accounts, roles, financial transactions, and configuration changes.
|
||||
|
||||
### SEC-054: Security Event Logging
|
||||
**Severity:** Warning
|
||||
|
||||
Log security-relevant events like authorization failures, input validation failures, rate limit triggers, and suspicious activity.
|
||||
|
||||
### SEC-055: Log Injection Prevention
|
||||
**Severity:** Warning
|
||||
|
||||
Sanitize user input before logging. Newlines and control characters can corrupt logs.
|
||||
|
||||
```python
|
||||
# Bad
|
||||
logger.info(f"User search: {request.query}")
|
||||
|
||||
# Good
|
||||
logger.info(f"User search: {request.query!r}") # repr escapes
|
||||
```
|
||||
|
||||
### SEC-056: Centralized Logging
|
||||
**Severity:** Info
|
||||
|
||||
Use centralized logging for correlation across services, tamper-evident storage, and alerting.
|
||||
|
||||
### SEC-057: Log Level Appropriateness
|
||||
**Severity:** Info
|
||||
|
||||
Use appropriate log levels: ERROR for security failures, WARNING for suspicious activity, INFO for successful events.
|
||||
|
||||
### SEC-058: Structured Logging Format
|
||||
**Severity:** Info
|
||||
|
||||
Use structured logging (JSON) for easy parsing and searchability.
|
||||
|
||||
### SEC-059: Audit Log Integrity
|
||||
**Severity:** Info
|
||||
|
||||
Protect audit logs from tampering with append-only storage and cryptographic chaining.
|
||||
|
||||
### SEC-060: Privacy-Aware Logging
|
||||
**Severity:** Warning
|
||||
|
||||
Comply with data protection regulations. No PII in logs without consent.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
All rules are defined in `.security-rules/` directory:
|
||||
|
||||
```
|
||||
.security-rules/
|
||||
├── _main.yaml # Main configuration
|
||||
├── authentication.yaml # SEC-001 to SEC-010
|
||||
├── injection.yaml # SEC-011 to SEC-020
|
||||
├── data_protection.yaml # SEC-021 to SEC-030
|
||||
├── api_security.yaml # SEC-031 to SEC-040
|
||||
├── cryptography.yaml # SEC-041 to SEC-050
|
||||
└── audit.yaml # SEC-051 to SEC-060
|
||||
```
|
||||
|
||||
## Suppressing Rules
|
||||
|
||||
Use noqa comments to suppress specific rules:
|
||||
|
||||
```python
|
||||
# noqa: SEC-001 - This is a test file with intentional test credentials
|
||||
test_password = "test123"
|
||||
```
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Architecture Rules](architecture-rules.md)
|
||||
- [Performance Rules](performance-rules.md)
|
||||
- [Code Quality Guide](code-quality.md)
|
||||
- [Contributing Guide](contributing.md)
|
||||
|
||||
---
|
||||
|
||||
## Summary Statistics
|
||||
|
||||
| Category | Rules | Errors | Warnings | Info |
|
||||
|----------|-------|--------|----------|------|
|
||||
| Authentication | 10 | 4 | 5 | 1 |
|
||||
| Injection Prevention | 10 | 9 | 1 | 0 |
|
||||
| Data Protection | 10 | 4 | 3 | 3 |
|
||||
| API Security | 10 | 4 | 5 | 1 |
|
||||
| Cryptography | 10 | 6 | 2 | 2 |
|
||||
| Audit & Logging | 10 | 0 | 5 | 5 |
|
||||
| **Total** | **60** | **27** | **21** | **12** |
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-12-21
|
||||
**Version:** 1.0
|
||||
Reference in New Issue
Block a user