feat: Import 35+ skills, merge duplicates, add openclaw installer
Major updates: - Added 35+ new skills from awesome-opencode-skills and antigravity repos - Merged SEO skills into seo-master - Merged architecture skills into architecture - Merged security skills into security-auditor and security-coder - Merged testing skills into testing-master and testing-patterns - Merged pentesting skills into pentesting - Renamed website-creator to thai-frontend-dev - Replaced skill-creator with github version - Removed Chutes references (use MiniMax API instead) - Added install-openclaw-skills.sh for cross-platform installation - Updated .env.example with MiniMax API credentials
This commit is contained in:
324
skills/security-coder/SKILL.md
Normal file
324
skills/security-coder/SKILL.md
Normal file
@@ -0,0 +1,324 @@
|
||||
---
|
||||
name: security-coder
|
||||
description: |
|
||||
Master secure coding skill combining frontend, backend, and mobile security coding.
|
||||
XSS prevention, injection prevention, authentication, API security.
|
||||
Use when writing secure code or fixing security vulnerabilities.
|
||||
---
|
||||
|
||||
# Security Coder
|
||||
|
||||
Comprehensive secure coding skill combining: frontend security, backend security, and mobile security coding practices.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Task | Use Section |
|
||||
|------|-------------|
|
||||
| Prevent XSS | **Frontend Security** |
|
||||
| Secure APIs | **Backend Security** |
|
||||
| Mobile app security | **Mobile Security** |
|
||||
| Fix vulnerabilities | **Common Fixes** |
|
||||
| Input validation | **Input Security** |
|
||||
| Auth implementation | **Authentication** |
|
||||
|
||||
---
|
||||
|
||||
## Frontend Security (XSS Prevention)
|
||||
|
||||
### Output Handling
|
||||
| Unsafe | Safe |
|
||||
|--------|------|
|
||||
| `element.innerHTML = userInput` | `element.textContent = userInput` |
|
||||
| `document.write(userInput)` | `element.appendChild(safeEl)` |
|
||||
| `eval(userInput)` | Never eval user input |
|
||||
| `href=userInput` (javascript:) | Validate protocol, use `#` for unknown |
|
||||
|
||||
### Sanitization
|
||||
```javascript
|
||||
// Use DOMPurify for HTML content
|
||||
import DOMPurify from 'dompurify';
|
||||
const clean = DOMPurify.sanitize(dirty, { ALLOWED_TAGS: ['b', 'i'] });
|
||||
|
||||
// Always use textContent for user data
|
||||
element.textContent = userData;
|
||||
|
||||
// Encode for context
|
||||
// HTML: < > & "
|
||||
// JS: \x3C \x3E \u2028 \u2029
|
||||
// URL: encodeURIComponent()
|
||||
// CSS: escape or use allowlist
|
||||
```
|
||||
|
||||
### Content Security Policy (CSP)
|
||||
```http
|
||||
Content-Security-Policy:
|
||||
default-src 'self';
|
||||
script-src 'self' 'nonce-{random}' https://trusted-cdn.com;
|
||||
style-src 'self' 'nonce-{random}';
|
||||
img-src 'self' https: data:;
|
||||
font-src 'self';
|
||||
connect-src 'self' https://api.example.com;
|
||||
frame-ancestors 'none';
|
||||
base-uri 'self';
|
||||
form-action 'self';
|
||||
```
|
||||
|
||||
### Safe DOM Manipulation
|
||||
- Always prefer `textContent` over `innerHTML`
|
||||
- Use `document.createElement()` over `document.write()`
|
||||
- Validate URLs before setting `href`, `src`, `action`
|
||||
- Use `srcdoc` only with sanitized content
|
||||
|
||||
---
|
||||
|
||||
## Backend Security (Injection Prevention)
|
||||
|
||||
### SQL Injection Prevention
|
||||
```python
|
||||
# BAD - vulnerable
|
||||
query = f"SELECT * FROM users WHERE id = {user_id}"
|
||||
|
||||
# GOOD - parameterized
|
||||
query = "SELECT * FROM users WHERE id = %s"
|
||||
cursor.execute(query, (user_id,))
|
||||
|
||||
# GOOD - ORM
|
||||
user = User.query.filter_by(id=user_id).first()
|
||||
```
|
||||
|
||||
### NoSQL Injection Prevention
|
||||
```python
|
||||
# Validate input types
|
||||
if not isinstance(user_id, int):
|
||||
raise ValueError("Invalid ID type")
|
||||
|
||||
# Use parameterized queries
|
||||
result = db.users.find_one({"_id": ObjectId(user_id)})
|
||||
```
|
||||
|
||||
### Command Injection Prevention
|
||||
```python
|
||||
# NEVER use user input in shell commands
|
||||
# BAD
|
||||
os.system(f"grep {user_input} file")
|
||||
|
||||
# GOOD - subprocess with list
|
||||
subprocess.run(["grep", user_input, "file"], shell=False)
|
||||
|
||||
# GOOD - shlex.quote
|
||||
os.system(f"grep {shlex.quote(user_input)} file")
|
||||
```
|
||||
|
||||
### Input Validation
|
||||
```python
|
||||
# Allowlist validation
|
||||
import re
|
||||
def validate_email(email):
|
||||
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
||||
return re.match(pattern, email) is not None
|
||||
|
||||
# Type validation
|
||||
def get_user_id(user_id):
|
||||
if isinstance(user_id, str) and user_id.isdigit():
|
||||
return int(user_id)
|
||||
raise ValueError("Invalid user ID")
|
||||
|
||||
# Range validation
|
||||
def get_page(page, max_page=100):
|
||||
page = int(page)
|
||||
if page < 1:
|
||||
page = 1
|
||||
return min(page, max_page)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Security
|
||||
|
||||
### Authentication Headers
|
||||
```http
|
||||
Authorization: Bearer <jwt_token>
|
||||
X-API-Key: <api_key>
|
||||
```
|
||||
|
||||
### Rate Limiting
|
||||
```python
|
||||
# Implement rate limiting
|
||||
from functools import wraps
|
||||
import time
|
||||
|
||||
def rate_limit(max_calls, period):
|
||||
def decorator(func):
|
||||
calls = {}
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
now = time.time()
|
||||
key = func.__name__
|
||||
if key not in calls:
|
||||
calls[key] = []
|
||||
calls[key] = [t for t in calls[key] if now - t < period]
|
||||
if len(calls[key]) >= max_calls:
|
||||
raise TooManyRequestsError()
|
||||
calls[key].append(now)
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
return decorator
|
||||
```
|
||||
|
||||
### Security Headers
|
||||
```python
|
||||
response.headers.update({
|
||||
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
|
||||
'X-Content-Type-Options': 'nosniff',
|
||||
'X-Frame-Options': 'DENY',
|
||||
'X-XSS-Protection': '1; mode=block',
|
||||
'Referrer-Policy': 'strict-origin-when-cross-origin',
|
||||
'Permissions-Policy': 'geolocation=(), microphone=(), camera=()',
|
||||
'Content-Security-Policy': "default-src 'self'"
|
||||
})
|
||||
```
|
||||
|
||||
### CORS Configuration
|
||||
```python
|
||||
# Strict CORS
|
||||
ALLOWED_ORIGINS = ['https://app.example.com']
|
||||
|
||||
@app.after_request
|
||||
def add_cors(response):
|
||||
origin = request.headers.get('Origin')
|
||||
if origin in ALLOWED_ORIGINS:
|
||||
response.headers['Access-Control-Allow-Origin'] = origin
|
||||
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE'
|
||||
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
|
||||
response.headers['Access-Control-Allow-Credentials'] = 'true'
|
||||
return response
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Authentication Security
|
||||
|
||||
### Password Hashing
|
||||
```python
|
||||
# Use bcrypt or Argon2
|
||||
import bcrypt
|
||||
|
||||
def hash_password(password):
|
||||
return bcrypt.hashpw(password.encode(), bcrypt.gensalt())
|
||||
|
||||
def verify_password(password, hashed):
|
||||
return bcrypt.checkpw(password.encode(), hashed)
|
||||
|
||||
# Or Argon2
|
||||
from argon2 import PasswordHasher
|
||||
ph = PasswordHasher()
|
||||
hash = ph.hash("secret")
|
||||
ph.verify(hash, "secret")
|
||||
```
|
||||
|
||||
### JWT Security
|
||||
```python
|
||||
# Sign with RS256 (asymmetric)
|
||||
# Verify with public key
|
||||
import jwt
|
||||
|
||||
# Token structure
|
||||
payload = {
|
||||
'sub': user_id,
|
||||
'exp': datetime.utcnow() + timedelta(hours=1),
|
||||
'iat': datetime.utcnow(),
|
||||
'scope': ['read', 'write'] # Minimal scopes
|
||||
}
|
||||
|
||||
# Short expiration, no sensitive data
|
||||
token = jwt.encode(payload, private_key, algorithm='RS256')
|
||||
```
|
||||
|
||||
### Session Management
|
||||
```python
|
||||
# Secure session config
|
||||
SESSION_COOKIE = {
|
||||
'httponly': True, # No JS access
|
||||
'secure': True, # HTTPS only
|
||||
'samesite': 'Lax', # CSRF protection
|
||||
'max-age': 3600, # 1 hour
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Mobile Security
|
||||
|
||||
### iOS Security
|
||||
- Keychain for sensitive data
|
||||
- Certificate pinning
|
||||
- jailbreak detection
|
||||
- Biometric auth (Face ID, Touch ID)
|
||||
- Obfuscate API keys
|
||||
|
||||
### Android Security
|
||||
- Keystore for keys
|
||||
- Network security config
|
||||
- ProGuard/R8 obfuscation
|
||||
- Safe browsing API
|
||||
- Biometric auth
|
||||
|
||||
### React Native
|
||||
```javascript
|
||||
// Secure storage
|
||||
import * as Keychain from 'react-native-keychain';
|
||||
|
||||
// Certificate pinning
|
||||
const sslPinning = {
|
||||
certs: ['cert1', 'cert2']
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Security Fixes
|
||||
|
||||
### IDOR Prevention
|
||||
```python
|
||||
# Always verify ownership
|
||||
def get_document(doc_id, user_id):
|
||||
doc = Document.query.get(doc_id)
|
||||
if doc.owner_id != user_id:
|
||||
abort(403) # Forbidden
|
||||
return doc
|
||||
```
|
||||
|
||||
### CSRF Prevention
|
||||
```python
|
||||
# Use CSRF tokens
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import SubmitField
|
||||
|
||||
class MyForm(FlaskForm):
|
||||
csrf_token = HiddenField()
|
||||
submit = SubmitField()
|
||||
```
|
||||
|
||||
### XXE Prevention
|
||||
```python
|
||||
# Disable XML external entities
|
||||
import defusedxml
|
||||
ET = defusedxml.ElementSafeTypes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Checklist
|
||||
|
||||
- [ ] Input validation on all user data
|
||||
- [ ] Parameterized queries (no string interpolation)
|
||||
- [ ] Output encoding for context
|
||||
- [ ] CSRF tokens on forms
|
||||
- [ ] Secure session configuration
|
||||
- [ ] HTTPS only
|
||||
- [ ] Security headers
|
||||
- [ ] Rate limiting
|
||||
- [ ] Logging without sensitive data
|
||||
- [ ] Error messages without stack traces
|
||||
Reference in New Issue
Block a user