chore(skills): добавлены скиллы docstring и security-audit
- Зачем:
- напоминание моделям про Google-style docstring'ы и Ruff-конфиг.
- чеклист безопасности для ревью кода и зависимостей.
- Что:
- добавлен docstring (DeerHide): вычищены мёртвые ссылки, добавлен Ruff select, правило языка проекта.
- добавлен security-audit: чеклист OWASP, severity-уровни, формат отчёта.
- убраны test-generator (Node.js стек) и api-docs (избыточен).
- Проверка:
- ls agents/.agents/skills/{docstring,security-audit}/SKILL.md.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
168b40ddd7
commit
77adaad441
2 files changed
+673
No files matched your search
@@ -0,0 +1,445 @@
|
||||
---
|
||||
name: python-docstring
|
||||
description: A skill for writing consistent, well-structured Python docstrings following the Google style convention.
|
||||
metadata:
|
||||
author: Deerhide
|
||||
version: 1.0.0
|
||||
---
|
||||
|
||||
# Python Docstring Skill
|
||||
|
||||
## When to use this skill?
|
||||
|
||||
- Use this skill when writing or updating Python documentation strings (docstrings).
|
||||
- This skill is useful for ensuring consistent documentation across a Python codebase.
|
||||
- It should be applied when documenting modules, classes, functions, methods, and attributes.
|
||||
- Use it during code reviews to verify docstring quality and completeness.
|
||||
|
||||
# Language
|
||||
|
||||
Язык docstring'ов определяется по проекту: если в существующем коде комментарии и docstring'ы на русском — пиши на русском, если на английском — на английском. Не смешивай языки внутри одного проекта.
|
||||
|
||||
# Docstring Style Convention
|
||||
|
||||
We follow the **Google style** docstring convention. This style is chosen for its:
|
||||
- Readability and clean formatting
|
||||
- Wide adoption in the Python community
|
||||
- Excellent support in documentation tools (Sphinx, MkDocs)
|
||||
- Native support in linting tools (Ruff, Pylint)
|
||||
|
||||
# Module Docstrings
|
||||
|
||||
Every Python module should start with a docstring describing its purpose.
|
||||
|
||||
```python
|
||||
"""Module for handling user authentication and session management.
|
||||
|
||||
This module provides utilities for user authentication, token generation,
|
||||
and session lifecycle management. It integrates with OAuth2 providers
|
||||
and supports JWT-based authentication.
|
||||
|
||||
Example:
|
||||
Basic usage of the authentication service::
|
||||
|
||||
from auth import AuthService
|
||||
|
||||
service = AuthService(secret_key="my-secret")
|
||||
token = service.create_token(user_id=123)
|
||||
|
||||
Attributes:
|
||||
DEFAULT_TOKEN_EXPIRY (int): Default token expiration time in seconds.
|
||||
SUPPORTED_ALGORITHMS (list): List of supported JWT algorithms.
|
||||
|
||||
Todo:
|
||||
* Add support for refresh tokens
|
||||
* Implement rate limiting for failed attempts
|
||||
"""
|
||||
|
||||
DEFAULT_TOKEN_EXPIRY = 3600
|
||||
SUPPORTED_ALGORITHMS = ["HS256", "RS256"]
|
||||
```
|
||||
|
||||
# Class Docstrings
|
||||
|
||||
Class docstrings should describe the class purpose and list its attributes.
|
||||
|
||||
```python
|
||||
class UserRepository:
|
||||
"""Repository for managing user data persistence.
|
||||
|
||||
This class provides CRUD operations for user entities and handles
|
||||
database connections and transactions.
|
||||
|
||||
Attributes:
|
||||
connection: Database connection instance.
|
||||
table_name: Name of the users table.
|
||||
cache_enabled: Whether caching is enabled for queries.
|
||||
|
||||
Example:
|
||||
Creating and using a repository::
|
||||
|
||||
repo = UserRepository(connection=db_conn)
|
||||
user = repo.find_by_id(user_id=42)
|
||||
repo.save(user)
|
||||
"""
|
||||
|
||||
def __init__(self, connection: DatabaseConnection) -> None:
|
||||
"""Initialize the UserRepository.
|
||||
|
||||
Args:
|
||||
connection: Active database connection to use for operations.
|
||||
"""
|
||||
self.connection = connection
|
||||
self.table_name = "users"
|
||||
self.cache_enabled = True
|
||||
```
|
||||
|
||||
# Function and Method Docstrings
|
||||
|
||||
Functions and methods should document their purpose, arguments, return values, and exceptions.
|
||||
|
||||
## Basic Function
|
||||
|
||||
```python
|
||||
def calculate_discount(price: float, percentage: float) -> float:
|
||||
"""Calculate the discounted price.
|
||||
|
||||
Args:
|
||||
price: Original price of the item.
|
||||
percentage: Discount percentage (0-100).
|
||||
|
||||
Returns:
|
||||
The discounted price after applying the percentage reduction.
|
||||
|
||||
Raises:
|
||||
ValueError: If percentage is not between 0 and 100.
|
||||
TypeError: If price or percentage are not numeric.
|
||||
|
||||
Example:
|
||||
>>> calculate_discount(100.0, 20)
|
||||
80.0
|
||||
"""
|
||||
if not 0 <= percentage <= 100:
|
||||
raise ValueError("Percentage must be between 0 and 100")
|
||||
return price * (1 - percentage / 100)
|
||||
```
|
||||
|
||||
## Method with Complex Arguments
|
||||
|
||||
```python
|
||||
def create_user(
|
||||
self,
|
||||
username: str,
|
||||
email: str,
|
||||
*,
|
||||
role: str = "user",
|
||||
metadata: dict[str, Any] | None = None,
|
||||
) -> User:
|
||||
"""Create a new user in the system.
|
||||
|
||||
Creates a user entity with the provided information and persists
|
||||
it to the database. Sends a welcome email upon successful creation.
|
||||
|
||||
Args:
|
||||
username: Unique username for the new user. Must be alphanumeric
|
||||
and between 3-50 characters.
|
||||
email: Valid email address for the user.
|
||||
role: User role assignment. Defaults to "user".
|
||||
Valid options are "user", "admin", "moderator".
|
||||
metadata: Optional dictionary of additional user metadata.
|
||||
Keys must be strings, values can be any JSON-serializable type.
|
||||
|
||||
Returns:
|
||||
The newly created User instance with assigned ID.
|
||||
|
||||
Raises:
|
||||
DuplicateUserError: If username or email already exists.
|
||||
ValidationError: If input validation fails.
|
||||
DatabaseError: If database operation fails.
|
||||
|
||||
Note:
|
||||
This method triggers a background task for sending the welcome email.
|
||||
The email sending is non-blocking and failures are logged but not raised.
|
||||
|
||||
Example:
|
||||
Creating a basic user::
|
||||
|
||||
user = service.create_user(
|
||||
username="johndoe",
|
||||
email="john@example.com"
|
||||
)
|
||||
|
||||
Creating an admin user with metadata::
|
||||
|
||||
admin = service.create_user(
|
||||
username="admin",
|
||||
email="admin@example.com",
|
||||
role="admin",
|
||||
metadata={"department": "IT"}
|
||||
)
|
||||
"""
|
||||
```
|
||||
|
||||
## Async Functions
|
||||
|
||||
```python
|
||||
async def fetch_user_data(user_id: int, timeout: float = 30.0) -> UserData:
|
||||
"""Fetch user data from the remote service.
|
||||
|
||||
Asynchronously retrieves user information from the external API
|
||||
with automatic retry logic for transient failures.
|
||||
|
||||
Args:
|
||||
user_id: Unique identifier of the user to fetch.
|
||||
timeout: Maximum time in seconds to wait for response.
|
||||
Defaults to 30.0 seconds.
|
||||
|
||||
Returns:
|
||||
UserData object containing the user's information.
|
||||
|
||||
Raises:
|
||||
UserNotFoundError: If no user exists with the given ID.
|
||||
TimeoutError: If the request exceeds the timeout duration.
|
||||
ServiceUnavailableError: If the remote service is unreachable.
|
||||
"""
|
||||
```
|
||||
|
||||
# Generator and Iterator Docstrings
|
||||
|
||||
```python
|
||||
def paginate_results(
|
||||
query: Query,
|
||||
page_size: int = 100,
|
||||
) -> Iterator[list[Record]]:
|
||||
"""Paginate through query results.
|
||||
|
||||
Yields pages of records from the query, handling cursor-based
|
||||
pagination automatically.
|
||||
|
||||
Args:
|
||||
query: Database query to paginate.
|
||||
page_size: Number of records per page. Defaults to 100.
|
||||
|
||||
Yields:
|
||||
Lists of Record objects, each list containing up to page_size items.
|
||||
|
||||
Example:
|
||||
>>> for page in paginate_results(query, page_size=50):
|
||||
... process_records(page)
|
||||
"""
|
||||
```
|
||||
|
||||
# Property Docstrings
|
||||
|
||||
```python
|
||||
@property
|
||||
def full_name(self) -> str:
|
||||
"""Return the full name of the user.
|
||||
|
||||
Combines first_name and last_name with proper spacing.
|
||||
Returns an empty string if both names are unset.
|
||||
"""
|
||||
return f"{self.first_name} {self.last_name}".strip()
|
||||
|
||||
|
||||
@property
|
||||
def is_active(self) -> bool:
|
||||
"""Return whether the user account is currently active.
|
||||
|
||||
An account is considered active if it has been verified
|
||||
and has not been suspended or deleted.
|
||||
"""
|
||||
return self.verified and not self.suspended
|
||||
```
|
||||
|
||||
# Context Manager Docstrings
|
||||
|
||||
```python
|
||||
@contextmanager
|
||||
def database_transaction(
|
||||
connection: Connection,
|
||||
isolation_level: str = "READ_COMMITTED",
|
||||
) -> Iterator[Transaction]:
|
||||
"""Context manager for database transactions.
|
||||
|
||||
Provides automatic commit on success and rollback on failure.
|
||||
Nested transactions are supported via savepoints.
|
||||
|
||||
Args:
|
||||
connection: Active database connection.
|
||||
isolation_level: Transaction isolation level.
|
||||
Options: "READ_UNCOMMITTED", "READ_COMMITTED",
|
||||
"REPEATABLE_READ", "SERIALIZABLE".
|
||||
Defaults to "READ_COMMITTED".
|
||||
|
||||
Yields:
|
||||
Transaction object for executing queries.
|
||||
|
||||
Raises:
|
||||
TransactionError: If transaction cannot be started.
|
||||
|
||||
Example:
|
||||
>>> with database_transaction(conn) as tx:
|
||||
... tx.execute("INSERT INTO users VALUES (?)", [user])
|
||||
"""
|
||||
```
|
||||
|
||||
# Dataclass and Pydantic Model Docstrings
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class OrderItem:
|
||||
"""Represents a single item in an order.
|
||||
|
||||
Attributes:
|
||||
product_id: Unique identifier of the product.
|
||||
quantity: Number of units ordered. Must be positive.
|
||||
unit_price: Price per unit in the order currency.
|
||||
discount: Applied discount as a decimal (0.0-1.0).
|
||||
"""
|
||||
|
||||
product_id: str
|
||||
quantity: int
|
||||
unit_price: Decimal
|
||||
discount: Decimal = Decimal("0.0")
|
||||
|
||||
|
||||
class UserSettings(BaseModel):
|
||||
"""User preference settings.
|
||||
|
||||
Stores user-configurable settings for the application,
|
||||
including notification preferences and display options.
|
||||
|
||||
Attributes:
|
||||
theme: UI theme preference. Either "light" or "dark".
|
||||
notifications_enabled: Whether to send notifications.
|
||||
language: Preferred language code (ISO 639-1).
|
||||
timezone: User's timezone (IANA format).
|
||||
"""
|
||||
|
||||
theme: Literal["light", "dark"] = "light"
|
||||
notifications_enabled: bool = True
|
||||
language: str = "en"
|
||||
timezone: str = "UTC"
|
||||
```
|
||||
|
||||
# Enum Docstrings
|
||||
|
||||
```python
|
||||
class OrderStatus(Enum):
|
||||
"""Status values for order lifecycle.
|
||||
|
||||
Attributes:
|
||||
PENDING: Order created but not yet processed.
|
||||
CONFIRMED: Order confirmed and awaiting fulfillment.
|
||||
SHIPPED: Order has been shipped to customer.
|
||||
DELIVERED: Order successfully delivered.
|
||||
CANCELLED: Order was cancelled before fulfillment.
|
||||
"""
|
||||
|
||||
PENDING = "pending"
|
||||
CONFIRMED = "confirmed"
|
||||
SHIPPED = "shipped"
|
||||
DELIVERED = "delivered"
|
||||
CANCELLED = "cancelled"
|
||||
```
|
||||
|
||||
# Exception Docstrings
|
||||
|
||||
```python
|
||||
class ValidationError(Exception):
|
||||
"""Raised when input validation fails.
|
||||
|
||||
This exception is raised when user input or API request data
|
||||
fails validation rules.
|
||||
|
||||
Attributes:
|
||||
field: Name of the field that failed validation.
|
||||
message: Human-readable error message.
|
||||
code: Machine-readable error code for client handling.
|
||||
|
||||
Example:
|
||||
>>> raise ValidationError(
|
||||
... field="email",
|
||||
... message="Invalid email format",
|
||||
... code="INVALID_EMAIL"
|
||||
... )
|
||||
"""
|
||||
|
||||
def __init__(self, field: str, message: str, code: str) -> None:
|
||||
"""Initialize ValidationError.
|
||||
|
||||
Args:
|
||||
field: Name of the field that failed validation.
|
||||
message: Human-readable description of the error.
|
||||
code: Machine-readable error code.
|
||||
"""
|
||||
self.field = field
|
||||
self.message = message
|
||||
self.code = code
|
||||
super().__init__(f"{field}: {message}")
|
||||
```
|
||||
|
||||
# Docstring Sections Reference
|
||||
|
||||
The following sections are supported in Google style docstrings:
|
||||
|
||||
| Section | Purpose |
|
||||
|---------|---------|
|
||||
| `Args:` | Document function/method parameters |
|
||||
| `Returns:` | Describe the return value |
|
||||
| `Yields:` | Describe yielded values (generators) |
|
||||
| `Raises:` | List exceptions that may be raised |
|
||||
| `Attributes:` | Document class/module attributes |
|
||||
| `Example:` / `Examples:` | Provide usage examples |
|
||||
| `Note:` | Additional information or caveats |
|
||||
| `Warning:` | Important warnings for users |
|
||||
| `Todo:` | Future improvements or tasks |
|
||||
| `See Also:` | References to related functions/classes |
|
||||
|
||||
# Best Practices
|
||||
|
||||
1. **Be Concise**: First line should be a brief summary under 79 characters.
|
||||
2. **Use Imperative Mood**: Write "Calculate the sum" not "Calculates the sum".
|
||||
3. **Document All Public APIs**: Every public function, class, and method needs a docstring.
|
||||
4. **Rely on Type Hints**: Avoid duplicating type information in docstrings when type hints are present. Modern documentation tools extract types from annotations.
|
||||
5. **Provide Examples**: Complex functions benefit from usage examples.
|
||||
6. **Document Exceptions**: List all exceptions that can be raised.
|
||||
7. **Keep Updated**: Update docstrings when code changes.
|
||||
8. **Use Proper Indentation**: Continuation lines should be indented consistently.
|
||||
|
||||
# Linting Configuration
|
||||
|
||||
Recommended Ruff configuration for `pyproject.toml`:
|
||||
|
||||
```toml
|
||||
[tool.ruff]
|
||||
line-length = 120
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["D", "F", "E", "W", "I", "UP", "B", "SIM", "S", "A", "C90", "RET", "PERF"]
|
||||
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
```
|
||||
|
||||
Key rule groups: **D** (docstrings), **F** (pyflakes), **E/W** (pycodestyle),
|
||||
**I** (imports), **UP** (pyupgrade), **B** (bugbear), **SIM** (simplify),
|
||||
**S** (security/bandit), **A** (builtins shadowing), **C90** (complexity),
|
||||
**RET** (return statements), **PERF** (performance).
|
||||
|
||||
# Common Ruff Docstring Rules
|
||||
|
||||
| Rule | Description |
|
||||
|------|-------------|
|
||||
| D100 | Missing docstring in public module |
|
||||
| D101 | Missing docstring in public class |
|
||||
| D102 | Missing docstring in public method |
|
||||
| D103 | Missing docstring in public function |
|
||||
| D107 | Missing docstring in `__init__` |
|
||||
| D200 | One-line docstring should fit on one line |
|
||||
| D205 | 1 blank line required between summary and description |
|
||||
| D400 | First line should end with a period |
|
||||
| D401 | First line should be in imperative mood |
|
||||
| D417 | Missing argument descriptions in docstring |
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
---
|
||||
name: security-audit
|
||||
description: Audit code and dependencies for security vulnerabilities. Use when reviewing PRs, checking dependencies, preparing for deployment, or when user mentions security, vulnerabilities, or audit.
|
||||
---
|
||||
|
||||
# Security Audit Skill
|
||||
|
||||
Perform comprehensive security audits on codebases to identify vulnerabilities before they reach production.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- User mentions "security", "audit", "vulnerability", "CVE"
|
||||
- Before deployment commands
|
||||
- During PR reviews
|
||||
- User asks about dependencies
|
||||
- Periodic security checks
|
||||
|
||||
## Audit Checklist
|
||||
|
||||
### 1. Secrets Exposure
|
||||
|
||||
**Check for hardcoded secrets:**
|
||||
```bash
|
||||
# Search for common secret patterns
|
||||
grep -rn "API_KEY\|SECRET\|TOKEN\|PASSWORD" --include="*.{js,ts,py,go,rb,java}" .
|
||||
grep -rn "sk-\|pk_\|api_\|secret_" --include="*.{js,ts,py,go,rb,java}" .
|
||||
```
|
||||
|
||||
**Verify .gitignore:**
|
||||
```bash
|
||||
# Ensure sensitive files are ignored
|
||||
cat .gitignore | grep -E "\.env|secret|credential|\.pem|\.key"
|
||||
```
|
||||
|
||||
**Check git history for leaked secrets:**
|
||||
```bash
|
||||
# Search recent commits (requires git-secrets or truffleHog)
|
||||
git log -p --all -S "API_KEY" --since="30 days ago"
|
||||
```
|
||||
|
||||
✅ Pass criteria:
|
||||
- No hardcoded API keys, tokens, or passwords
|
||||
- `.env` files in `.gitignore`
|
||||
- No secrets in git history
|
||||
|
||||
### 2. Dependency Vulnerabilities
|
||||
|
||||
**Node.js:**
|
||||
```bash
|
||||
npm audit
|
||||
# or
|
||||
yarn audit
|
||||
# or
|
||||
pnpm audit
|
||||
```
|
||||
|
||||
**Python:**
|
||||
```bash
|
||||
pip-audit
|
||||
# or
|
||||
safety check
|
||||
```
|
||||
|
||||
**Go:**
|
||||
```bash
|
||||
govulncheck ./...
|
||||
```
|
||||
|
||||
**Rust:**
|
||||
```bash
|
||||
cargo audit
|
||||
```
|
||||
|
||||
✅ Pass criteria:
|
||||
- No critical vulnerabilities
|
||||
- No high vulnerabilities > 30 days old
|
||||
- Dependencies updated within last 90 days
|
||||
|
||||
### 3. Input Validation
|
||||
|
||||
**Check for:**
|
||||
- User inputs sanitized before use
|
||||
- SQL queries use parameterized statements
|
||||
- File paths validated and sandboxed
|
||||
- HTML content escaped before rendering
|
||||
- Command injection prevention
|
||||
|
||||
**Common vulnerable patterns:**
|
||||
```javascript
|
||||
// BAD: SQL injection
|
||||
db.query(`SELECT * FROM users WHERE id = ${userId}`)
|
||||
|
||||
// GOOD: Parameterized query
|
||||
db.query('SELECT * FROM users WHERE id = ?', [userId])
|
||||
```
|
||||
|
||||
```python
|
||||
# BAD: Command injection
|
||||
os.system(f"convert {user_file}")
|
||||
|
||||
# GOOD: Use subprocess with list
|
||||
subprocess.run(["convert", user_file], check=True)
|
||||
```
|
||||
|
||||
### 4. Authentication & Authorization
|
||||
|
||||
**Check for:**
|
||||
- Passwords hashed with bcrypt/argon2 (not MD5/SHA1)
|
||||
- Session tokens are cryptographically random
|
||||
- Sessions expire appropriately
|
||||
- CSRF protection on state-changing endpoints
|
||||
- Rate limiting on auth endpoints
|
||||
- Account lockout after failed attempts
|
||||
|
||||
**Look for:**
|
||||
```javascript
|
||||
// BAD: Weak hashing
|
||||
crypto.createHash('md5').update(password)
|
||||
|
||||
// GOOD: Bcrypt
|
||||
bcrypt.hash(password, 12)
|
||||
```
|
||||
|
||||
### 5. HTTPS & Transport Security
|
||||
|
||||
**Check for:**
|
||||
- HTTPS enforced (HSTS header)
|
||||
- Secure cookie flags (`Secure`, `HttpOnly`, `SameSite`)
|
||||
- No mixed content warnings
|
||||
- TLS 1.2+ required
|
||||
|
||||
### 6. Error Handling
|
||||
|
||||
**Check for:**
|
||||
- Stack traces not exposed in production
|
||||
- Generic error messages for users
|
||||
- Detailed errors only in logs
|
||||
- Sensitive data not in error messages
|
||||
|
||||
```javascript
|
||||
// BAD: Exposes internals
|
||||
res.status(500).send({ error: err.stack })
|
||||
|
||||
// GOOD: Generic message
|
||||
res.status(500).send({ error: 'An unexpected error occurred' })
|
||||
```
|
||||
|
||||
### 7. File Upload Security
|
||||
|
||||
If file uploads exist:
|
||||
- Validate file type server-side (not just extension)
|
||||
- Limit file size
|
||||
- Scan for malware
|
||||
- Store outside webroot
|
||||
- Rename uploaded files
|
||||
|
||||
### 8. API Security
|
||||
|
||||
- Authentication required on all sensitive endpoints
|
||||
- Authorization checks per resource
|
||||
- Rate limiting implemented
|
||||
- CORS configured restrictively
|
||||
- API versioning in place
|
||||
|
||||
## Severity Levels
|
||||
|
||||
| Level | Description | Action Required |
|
||||
|-------|-------------|-----------------|
|
||||
| 🔴 Critical | Actively exploitable | Block deployment |
|
||||
| 🟠 High | Exploitable with effort | Fix within 7 days |
|
||||
| 🟡 Medium | Requires conditions | Fix within 30 days |
|
||||
| 🟢 Low | Minimal impact | Fix when convenient |
|
||||
|
||||
## Output Format
|
||||
|
||||
```markdown
|
||||
## Security Audit Results
|
||||
|
||||
**Project:** [name]
|
||||
**Date:** [date]
|
||||
**Auditor:** Claude (automated)
|
||||
|
||||
### Summary
|
||||
|
||||
| Severity | Count |
|
||||
|----------|-------|
|
||||
| 🔴 Critical | 0 |
|
||||
| 🟠 High | 1 |
|
||||
| 🟡 Medium | 2 |
|
||||
| 🟢 Low | 3 |
|
||||
|
||||
### Findings
|
||||
|
||||
#### 1. [🟠 High] Hardcoded API Key
|
||||
|
||||
**Location:** `src/config.js:15`
|
||||
**Description:** API key for payment provider is hardcoded
|
||||
**Risk:** If source code is leaked, attackers gain API access
|
||||
**Recommendation:** Move to environment variable
|
||||
|
||||
```diff
|
||||
- const STRIPE_KEY = 'sk_live_abc123...'
|
||||
+ const STRIPE_KEY = process.env.STRIPE_SECRET_KEY
|
||||
```
|
||||
|
||||
#### 2. [🟡 Medium] Missing Rate Limiting
|
||||
|
||||
**Location:** `src/routes/auth.js`
|
||||
**Description:** Login endpoint has no rate limiting
|
||||
**Risk:** Enables brute force attacks
|
||||
**Recommendation:** Add rate limiting middleware
|
||||
|
||||
### Recommendations
|
||||
|
||||
1. [ ] Fix critical and high issues before next deployment
|
||||
2. [ ] Schedule medium issues for next sprint
|
||||
3. [ ] Add low issues to backlog
|
||||
4. [ ] Re-run audit after fixes
|
||||
```
|
||||
|
||||
## Commands to Run
|
||||
|
||||
After completing the audit, provide the user with:
|
||||
|
||||
1. Summary of findings
|
||||
2. Prioritized fix list
|
||||
3. Commands to address each issue
|
||||
4. Timeline recommendation
|
||||
Reference in new issue
Block a user