code-review-security by hieutrtr/ai1-skills
npx skills add https://github.com/hieutrtr/ai1-skills --skill code-review-security在以下场景中激活此技能:
输出: 将审查结果写入 security-review.md 文件,包含严重性、文件:行号、描述和建议。
请勿将此技能用于:
docker-best-practices)incident-response)pre-merge-checklist)python-backend-expert 或 react-frontend-expert)广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
根据 OWASP Top 10(2021版)审查每个 PR。以下每个类别都包含针对 Python/FastAPI 和 React 代码库的特定检查项。
需要关注的内容:
Depends()Python/FastAPI 检查项:
# BAD: No authorization check -- any authenticated user can access any user
@router.get("/users/{user_id}")
async def get_user(user_id: int, db: Session = Depends(get_db)):
return await user_repo.get(user_id)
# GOOD: Verify the requesting user owns the resource or is admin
@router.get("/users/{user_id}")
async def get_user(
user_id: int,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
):
if current_user.id != user_id and current_user.role != "admin":
raise HTTPException(status_code=403, detail="Forbidden")
return await user_repo.get(user_id)
审查清单:
Depends(get_current_user))role == "admin"需要关注的内容:
Python 检查项:
# BAD: Weak password hashing
import hashlib
password_hash = hashlib.md5(password.encode()).hexdigest()
# GOOD: Use bcrypt via passlib
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
password_hash = pwd_context.hash(password)
# BAD: Secret in code
SECRET_KEY = "my-super-secret-key-123"
# GOOD: Secret from environment
SECRET_KEY = os.environ["SECRET_KEY"]
审查清单:
.env.example 仅包含占位符)需要关注的内容:
eval()、exec()、compile()shell=True 的 subprocess 调用Python 检查项:
# BAD: SQL injection via string formatting
query = f"SELECT * FROM users WHERE email = '{email}'"
db.execute(text(query))
# GOOD: Parameterized query
db.execute(text("SELECT * FROM users WHERE email = :email"), {"email": email})
# GOOD: SQLAlchemy ORM (always parameterized)
user = db.query(User).filter(User.email == email).first()
# BAD: Command injection
subprocess.run(f"convert {filename}", shell=True)
# GOOD: Pass arguments as a list
subprocess.run(["convert", filename], shell=False)
# BAD: Code execution with user input
result = eval(user_input)
# GOOD: Never eval user input. Use ast.literal_eval for safe parsing.
result = ast.literal_eval(user_input) # Only for literal structures
审查清单:
eval()、exec() 或 compile()subprocess.run(..., shell=True)pickle.loads()需要关注的内容:
审查清单:
需要关注的内容:
* 来源Python/FastAPI 检查项:
# BAD: Wide-open CORS
app.add_middleware(CORSMiddleware, allow_origins=["*"])
# GOOD: Explicit allowed origins
app.add_middleware(
CORSMiddleware,
allow_origins=["https://app.example.com"],
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
)
# BAD: Debug mode in production
app = FastAPI(debug=True)
# GOOD: Debug only in development
app = FastAPI(debug=settings.DEBUG) # DEBUG=False in production
审查清单:
审查清单:
pip-audit 或 safety check)npm audit)需要关注的内容:
Python 检查项:
# BAD: JWT without expiration
token = jwt.encode({"sub": user_id}, SECRET_KEY, algorithm="HS256")
# GOOD: JWT with expiration
token = jwt.encode(
{"sub": user_id, "exp": datetime.utcnow() + timedelta(minutes=30)},
SECRET_KEY,
algorithm="HS256",
)
审查清单:
exp 声明)审查清单:
pickle.loads)审查清单:
需要关注的内容:
Python 检查项:
# BAD: Fetch arbitrary URL from user input
url = request.query_params["url"]
response = httpx.get(url) # SSRF: can access internal services
# GOOD: Validate URL against allowlist
ALLOWED_HOSTS = {"api.example.com", "cdn.example.com"}
parsed = urlparse(url)
if parsed.hostname not in ALLOWED_HOSTS:
raise HTTPException(400, "URL not allowed")
response = httpx.get(url)
审查清单:
除了 OWASP 之外,还需审查 Python 代码中的以下模式:
| 模式 | 风险 | 修复方案 |
|---|---|---|
eval(user_input) | 远程代码执行 | 移除或使用 ast.literal_eval |
pickle.loads(data) | 任意代码执行 | 使用 JSON 或 msgpack |
subprocess.run(cmd, shell=True) | 命令注入 | 将参数作为列表传递,shell=False |
yaml.load(data) | 代码执行 | 使用 yaml.safe_load(data) |
os.system(cmd) | 命令注入 | 使用 subprocess.run([...]) |
| 原始 SQL 字符串 | SQL 注入 | 使用 ORM 或参数化查询 |
hashlib.md5(password) | 弱哈希 | 通过 passlib 使用 bcrypt |
jwt.decode(token, options={"verify_signature": False}) | 认证绕过 | 始终验证签名 |
open(user_path) | 路径遍历 | 验证路径,使用 pathlib.resolve() |
tempfile.mktemp() | 竞争条件 | 使用 tempfile.mkstemp() |
| 模式 | 风险 | 修复方案 |
|---|---|---|
dangerouslySetInnerHTML | XSS | 使用文本内容或使用 DOMPurify 进行清理 |
href 中的 javascript: | XSS | 验证 URL,仅允许 https: |
window.location = userInput | 开放重定向 | 根据允许列表进行验证 |
| 将令牌存储在 localStorage 中 | 通过 XSS 窃取令牌 | 使用 httpOnly cookie |
| 来自数据的行内事件处理器 | XSS | 使用 React 事件处理器 |
eval() 或 Function() | 代码执行 | 完全移除 |
| 渲染用户 HTML | XSS | 使用清理库 |
React 代码审查:
// BAD: XSS via dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{ __html: userBio }} />
// GOOD: Sanitize first, or use text content
import DOMPurify from "dompurify";
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userBio) }} />
// BETTER: Use text content when HTML is not needed
<p>{userBio}</p>
// BAD: javascript: URL
<a href={userLink}>Click</a> // userLink could be "javascript:alert(1)"
// GOOD: Validate protocol
const safeHref = /^https?:\/\//.test(userLink) ? userLink : "#";
<a href={safeHref}>Click</a>
根据严重性对每个发现进行分类,以便确定优先级:
| 严重性 | 描述 | 示例 | SLA |
|---|---|---|---|
| 严重 | 可远程利用,无需认证,数据泄露 | SQL 注入、RCE、认证绕过 | 阻止合并,立即修复 |
| 高 | 需要认证才能利用,权限提升 | IDOR、失效的访问控制、存储型 XSS | 阻止合并,发布前修复 |
| 中 | 需要特定条件才能利用 | CSRF、反射型 XSS、开放重定向 | 在迭代周期内修复 |
| 低 | 纵深防御,信息性 | 缺少标头、详细错误 | 方便时修复 |
| 信息 | 最佳实践建议 | 依赖项更新、代码风格 | 在待办事项中跟踪 |
报告安全发现时,为保持一致性,请使用以下格式:
## Security Finding: [Title]
**Severity:** Critical | High | Medium | Low | Info
**Category:** OWASP A01-A10 or custom category
**File:** path/to/file.py:42
**CWE:** CWE-89 (if applicable)
### Description
Brief description of the vulnerability and its impact.
### Vulnerable Code
```python
# The problematic code
vulnerable_function(user_input)
# The secure alternative
safe_function(sanitize(user_input))
What an attacker could achieve by exploiting this vulnerability.
### 自动化扫描
使用 `scripts/security-scan.py` 对 Python 代码中常见的漏洞模式执行基于 AST 的扫描。该脚本扫描以下内容:
* `eval()` / `exec()` / `compile()` 调用
* 使用 `shell=True` 的 `subprocess`
* 对可能不可信的数据使用 `pickle.loads()`
* 原始 SQL 字符串构造
* 未使用 `Loader=SafeLoader` 的 `yaml.load()`
* 硬编码密钥模式(API 密钥、密码)
* 弱哈希函数(用于密码的 MD5、SHA1)
运行:`python scripts/security-scan.py --path ./app --output-dir ./security-results`
**依赖项扫描(单独运行):**
```bash
# Python dependencies
pip-audit --requirement requirements.txt --output json > dep-audit.json
# npm dependencies
npm audit --json > npm-audit.json
SECURITY: SQL Injection (Critical, OWASP A03)
File:
app/repositories/user_repository.py:47query = f"SELECT * FROM users WHERE name LIKE '%{search_term}%'"这使用字符串插值构造了一个原始 SQL 查询,允许 SQL 注入。攻击者可以输入
'; DROP TABLE users; --来破坏数据。Fix: Use SQLAlchemy ORM filtering:
users = db.query(User).filter(User.name.ilike(f"%{search_term}%")).all()
SECURITY: Missing Rate Limiting (Medium, OWASP A04)
File:
app/routes/auth.py:12端点
/auth/login没有速率限制。攻击者可以以无限的速度执行暴力破解密码攻击。Fix: Add rate limiting middleware:
from slowapi import Limiter limiter = Limiter(key_func=get_remote_address) @router.post("/login") @limiter.limit("5/minute") async def login(request: Request, ...):
将安全发现写入 security-review.md:
# Security Review: [Feature/PR Name]
## Summary
- Critical: 0 | High: 1 | Medium: 2 | Low: 1
## Findings
### [CRITICAL] SQL Injection in user search
- **File:** app/routes/users.py:45
- **OWASP:** A03 Injection
- **Description:** Raw SQL with string interpolation
- **Recommendation:** Use SQLAlchemy ORM filtering
### [HIGH] Missing authorization check
...
## Passed Checks
- No hardcoded secrets found
- Dependencies up to date
每周安装次数
156
代码仓库
GitHub 星标数
8
首次出现
2026年2月4日
安全审计
安装于
opencode138
codex137
gemini-cli134
github-copilot132
cursor124
claude-code115
Activate this skill when:
Output: Write findings to security-review.md with severity, file:line, description, and recommendations.
Do NOT use this skill for:
docker-best-practices)incident-response)pre-merge-checklist)python-backend-expert or react-frontend-expert)Review every PR against the OWASP Top 10 (2021 edition). Each category below includes specific checks for Python/FastAPI and React codebases.
What to look for:
Depends() for auth on new routesPython/FastAPI checks:
# BAD: No authorization check -- any authenticated user can access any user
@router.get("/users/{user_id}")
async def get_user(user_id: int, db: Session = Depends(get_db)):
return await user_repo.get(user_id)
# GOOD: Verify the requesting user owns the resource or is admin
@router.get("/users/{user_id}")
async def get_user(
user_id: int,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db),
):
if current_user.id != user_id and current_user.role != "admin":
raise HTTPException(status_code=403, detail="Forbidden")
return await user_repo.get(user_id)
Review checklist:
Depends(get_current_user))role == "admin"What to look for:
Python checks:
# BAD: Weak password hashing
import hashlib
password_hash = hashlib.md5(password.encode()).hexdigest()
# GOOD: Use bcrypt via passlib
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
password_hash = pwd_context.hash(password)
# BAD: Secret in code
SECRET_KEY = "my-super-secret-key-123"
# GOOD: Secret from environment
SECRET_KEY = os.environ["SECRET_KEY"]
Review checklist:
.env.example has placeholders only)What to look for:
eval(), exec(), compile() with user inputsubprocess calls with shell=TruePython checks:
# BAD: SQL injection via string formatting
query = f"SELECT * FROM users WHERE email = '{email}'"
db.execute(text(query))
# GOOD: Parameterized query
db.execute(text("SELECT * FROM users WHERE email = :email"), {"email": email})
# GOOD: SQLAlchemy ORM (always parameterized)
user = db.query(User).filter(User.email == email).first()
# BAD: Command injection
subprocess.run(f"convert {filename}", shell=True)
# GOOD: Pass arguments as a list
subprocess.run(["convert", filename], shell=False)
# BAD: Code execution with user input
result = eval(user_input)
# GOOD: Never eval user input. Use ast.literal_eval for safe parsing.
result = ast.literal_eval(user_input) # Only for literal structures
Review checklist:
eval(), exec(), or compile() with external inputsubprocess.run(..., shell=True) with dynamic argumentspickle.loads() on untrusted dataWhat to look for:
Review checklist:
What to look for:
* originsPython/FastAPI checks:
# BAD: Wide-open CORS
app.add_middleware(CORSMiddleware, allow_origins=["*"])
# GOOD: Explicit allowed origins
app.add_middleware(
CORSMiddleware,
allow_origins=["https://app.example.com"],
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
)
# BAD: Debug mode in production
app = FastAPI(debug=True)
# GOOD: Debug only in development
app = FastAPI(debug=settings.DEBUG) # DEBUG=False in production
Review checklist:
Review checklist:
pip-audit or safety check)npm audit)What to look for:
Python checks:
# BAD: JWT without expiration
token = jwt.encode({"sub": user_id}, SECRET_KEY, algorithm="HS256")
# GOOD: JWT with expiration
token = jwt.encode(
{"sub": user_id, "exp": datetime.utcnow() + timedelta(minutes=30)},
SECRET_KEY,
algorithm="HS256",
)
Review checklist:
exp claim)Review checklist:
pickle.loads)Review checklist:
What to look for:
Python checks:
# BAD: Fetch arbitrary URL from user input
url = request.query_params["url"]
response = httpx.get(url) # SSRF: can access internal services
# GOOD: Validate URL against allowlist
ALLOWED_HOSTS = {"api.example.com", "cdn.example.com"}
parsed = urlparse(url)
if parsed.hostname not in ALLOWED_HOSTS:
raise HTTPException(400, "URL not allowed")
response = httpx.get(url)
Review checklist:
Beyond OWASP, review Python code for these patterns:
| Pattern | Risk | Fix |
|---|---|---|
eval(user_input) | Remote code execution | Remove or use ast.literal_eval |
pickle.loads(data) | Arbitrary code execution | Use JSON or msgpack |
subprocess.run(cmd, shell=True) | Command injection | Pass args as list, shell=False |
yaml.load(data) |
| Pattern | Risk | Fix |
|---|---|---|
dangerouslySetInnerHTML | XSS | Use text content or sanitize with DOMPurify |
javascript: in href | XSS | Validate URLs, allow only https: |
window.location = userInput | Open redirect | Validate against allowlist |
| Storing tokens in localStorage | Token theft via XSS | Use httpOnly cookies |
| Inline event handlers from data | XSS | Use React event handlers |
| or |
React code review:
// BAD: XSS via dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{ __html: userBio }} />
// GOOD: Sanitize first, or use text content
import DOMPurify from "dompurify";
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userBio) }} />
// BETTER: Use text content when HTML is not needed
<p>{userBio}</p>
// BAD: javascript: URL
<a href={userLink}>Click</a> // userLink could be "javascript:alert(1)"
// GOOD: Validate protocol
const safeHref = /^https?:\/\//.test(userLink) ? userLink : "#";
<a href={safeHref}>Click</a>
Classify each finding by severity for prioritization:
| Severity | Description | Examples | SLA |
|---|---|---|---|
| Critical | Exploitable remotely, no auth needed, data breach | SQL injection, RCE, auth bypass | Block merge, fix immediately |
| High | Exploitable with auth, privilege escalation | IDOR, broken access control, XSS (stored) | Block merge, fix before release |
| Medium | Requires specific conditions to exploit | CSRF, XSS (reflected), open redirect | Fix within sprint |
| Low | Defense-in-depth, informational | Missing headers, verbose errors | Fix when convenient |
| Info | Best practice recommendations | Dependency updates, code style | Track in backlog |
When reporting security findings, use this format for consistency:
## Security Finding: [Title]
**Severity:** Critical | High | Medium | Low | Info
**Category:** OWASP A01-A10 or custom category
**File:** path/to/file.py:42
**CWE:** CWE-89 (if applicable)
### Description
Brief description of the vulnerability and its impact.
### Vulnerable Code
```python
# The problematic code
vulnerable_function(user_input)
# The secure alternative
safe_function(sanitize(user_input))
What an attacker could achieve by exploiting this vulnerability.
Link to relevant OWASP page
Link to relevant CWE entry
Use scripts/security-scan.py to perform AST-based scanning for common vulnerability patterns in Python code. The script scans for:
eval() / exec() / compile() callssubprocess with shell=Truepickle.loads() on potentially untrusted datayaml.load() without Loader=SafeLoaderSECURITY: SQL Injection (Critical, OWASP A03)
File:
app/repositories/user_repository.py:47query = f"SELECT * FROM users WHERE name LIKE '%{search_term}%'"This constructs a raw SQL query with string interpolation, allowing SQL injection. An attacker could input
'; DROP TABLE users; --to destroy data.Fix: Use SQLAlchemy ORM filtering:
users = db.query(User).filter(User.name.ilike(f"%{search_term}%")).all()
SECURITY: Missing Rate Limiting (Medium, OWASP A04)
File:
app/routes/auth.py:12The
/auth/loginendpoint has no rate limiting. An attacker could perform brute-force password attacks at unlimited speed.Fix: Add rate limiting middleware:
from slowapi import Limiter limiter = Limiter(key_func=get_remote_address) @router.post("/login") @limiter.limit("5/minute") async def login(request: Request, ...):
Write security findings to security-review.md:
# Security Review: [Feature/PR Name]
## Summary
- Critical: 0 | High: 1 | Medium: 2 | Low: 1
## Findings
### [CRITICAL] SQL Injection in user search
- **File:** app/routes/users.py:45
- **OWASP:** A03 Injection
- **Description:** Raw SQL with string interpolation
- **Recommendation:** Use SQLAlchemy ORM filtering
### [HIGH] Missing authorization check
...
## Passed Checks
- No hardcoded secrets found
- Dependencies up to date
Weekly Installs
156
Repository
GitHub Stars
8
First Seen
Feb 4, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykFail
Installed on
opencode138
codex137
gemini-cli134
github-copilot132
cursor124
claude-code115
OpenClaw 安全 Linux 云部署指南:私有优先、SSH隧道、Podman容器化
33,700 周安装
Angular HTTP 数据获取教程:基于信号的 httpResource() 与 resource() 使用指南
1 周安装
Angular路由配置指南:v20+懒加载、函数式守卫与信号参数详解
1 周安装
iOS数据持久化与数据库路由指南:SwiftData、Core Data、CloudKit、迁移与审计
154 周安装
Angular CLI 工具集完整指南:v20+ 项目创建、代码生成与构建优化
1 周安装
Codex Wrapped 报告生成技能 - 获取过去30天和7天的Codex使用数据洞察
1 周安装
DOCX文档处理技能:专业格式编辑、视觉布局审阅与自动化渲染指南
1 周安装
| Code execution |
Use yaml.safe_load(data) |
os.system(cmd) | Command injection | Use subprocess.run([...]) |
| Raw SQL strings | SQL injection | Use ORM or parameterized queries |
hashlib.md5(password) | Weak hashing | Use bcrypt via passlib |
jwt.decode(token, options={"verify_signature": False}) | Auth bypass | Always verify signature |
open(user_path) | Path traversal | Validate path, use pathlib.resolve() |
tempfile.mktemp() | Race condition | Use tempfile.mkstemp() |
eval()Function()| Code execution |
| Remove entirely |
| Rendering user HTML | XSS | Use a sanitization library |
Run: python scripts/security-scan.py --path ./app --output-dir ./security-results
Dependency scanning (run separately):
# Python dependencies
pip-audit --requirement requirements.txt --output json > dep-audit.json
# npm dependencies
npm audit --json > npm-audit.json