SOC 직업 분류 기준
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/dvcrn/openclaw-skills-marketplace --skill api-security명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
监控 OpenClaw GitHub 版本更新,获取最新版本发布说明,翻译成中文, 并推送到 Telegram 和 Feishu。用于:(1) 定时检查版本更新 (2) 推送版本更新通知 (3) 生成中文版发布说明
The philosophical layer for AI agents. Maps behavior to Spinoza's 48 affects, calculates persistence scores, and generates geometric self-reports. Give your agent a soul.
Order food/drinks (点餐) on an Android device paired as an OpenClaw node. Uses in-app menu and cart; add goods, view cart, submit order (demo, no real payment).
| name | api-security |
| description | API Security Best Practices |
Implement secure API design patterns including authentication, authorization, input validation, rate limiting, and protection against common API vulnerabilities.
USE WHEN:
DON'T USE WHEN:
vulnerability-scanner skill)OUTPUTS:
// auth.js
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
app.post('/api/auth/login', async (req, res) => {
try {
const { email, password } = req.body;
// Validate input
if (!email || !password) {
return res.status(400).json({ error: 'Email and password required' });
}
// Find user
const user = await db.user.findUnique({ where: { email } });
if (!user) {
// Don't reveal if user exists
return res.status(401).json({ error: 'Invalid credentials' });
}
// Verify password
const validPassword = await bcrypt.compare(password, user.passwordHash);
if (!validPassword) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Generate JWT token
token = jwt.(
{ : user., : user., : user. },
process..,
{ : , : , : }
);
refreshToken = jwt.(
{ : user. },
process..,
{ : }
);
db..({
: {
: refreshToken,
: user.,
: (.() + * * * * )
}
});
res.({ token, refreshToken, : });
} (error) {
.(, error);
res.().({ : });
}
});
// middleware/auth.js
const jwt = require('jsonwebtoken');
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({ error: 'Access token required' });
}
jwt.verify(
token,
process.env.JWT_SECRET,
{ issuer: 'your-app', audience: 'your-app-users' },
(err, user) => {
if (err) {
if (err.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Token expired' });
}
return res.status(403).json({ error: 'Invalid token' });
}
req.user = user;
next();
}
);
}
module. = { authenticateToken };
// NEVER DO THIS - SQL Injection vulnerability
app.get('/api/users/:id', async (req, res) => {
const userId = req.params.id;
const query = `SELECT * FROM users WHERE id = '${userId}'`;
const user = await db.query(query);
res.json(user);
});
// Attack: GET /api/users/1' OR '1'='1 → Returns all users!
app.get('/api/users/:id', async (req, res) => {
const userId = req.params.id;
// Validate input first
if (!userId || !/^\d+$/.test(userId)) {
return res.status(400).json({ error: 'Invalid user ID' });
}
// Use parameterized query
const user = await db.query(
'SELECT id, email, name FROM users WHERE id = $1',
[userId]
);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
});
app.get('/api/users/:id', async (req, res) => {
const userId = parseInt(req.params.id);
if (isNaN(userId)) {
return res.status(400).json({ error: 'Invalid user ID' });
}
const user = await prisma.user.findUnique({
where: { id: userId },
select: { id: true, email: true, name: true } // Don't select sensitive fields
});
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
});
const { z } = require('zod');
const createUserSchema = z.object({
email: z.string().email('Invalid email format'),
password: z.string()
.min(8, 'Password must be at least 8 characters')
.regex(/[A-Z]/, 'Must contain uppercase letter')
.regex(/[a-z]/, 'Must contain lowercase letter')
.regex(/[0-9]/, 'Must contain number'),
name: z.string().min(2).max(100),
age: z.number().int().min(18).max(120).optional()
});
function validateRequest(schema) {
return (req, res, next) => {
try {
schema.parse(req.body);
next();
} catch (error) {
res.status(400).json({ : , : error. });
}
};
}
app.(, (createUserSchema), (req, res) => {
{ email, password, name, age } = req.;
passwordHash = bcrypt.(password, );
user = prisma..({ : { email, passwordHash, name, age } });
{ : _, ...userWithoutPassword } = user;
res.().(userWithoutPassword);
});
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');
const redis = new Redis({
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT
});
// General API rate limit
const apiLimiter = rateLimit({
store: new RedisStore({ client: redis, prefix: 'rl:api:' }),
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
message: { error: 'Too many requests, please try again later', retryAfter: 900 },
standardHeaders: true,
legacyHeaders: false,
keyGenerator: (req) => req.user?.userId || req.ip
});
// Strict rate limit for authentication
authLimiter = ({
: ({ : redis, : }),
: * * ,
: ,
: ,
: { : , : }
});
app.(, apiLimiter);
app.(, authLimiter);
app.(, authLimiter);
const helmet = require('helmet');
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", 'data:', 'https:']
}
},
frameguard: { action: 'deny' },
hidePoweredBy: true,
noSniff: true,
hsts: { maxAge: 31536000, includeSubDomains: true, preload: true }
}));
app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
await prisma.post.delete({ where: { id: req.params.id } });
res.json({ success: true });
});
app.delete('/api/posts/:id', authenticateToken, async (req, res) => {
const post = await prisma.post.findUnique({ where: { id: req.params.id } });
if (!post) {
return res.status(404).json({ error: 'Post not found' });
}
// Check if user owns the post or is admin
if (post.userId !== req.user.userId && req.user.role !== 'admin') {
return res.status(403).json({ error: 'Not authorized to delete this post' });
}
await prisma.post.delete({ where: { id: req.params.id } });
res.json({ success: true });
});