用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/personamanagmentlayer/pcl --skill security-expert命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | security-expert |
| version | 1.0.0 |
| description | Expert-level application security, OWASP Top 10, penetration testing, and security best practices |
| category | security |
| tags | ["security","owasp","pentest","appsec","vulnerability","encryption","authentication"] |
| allowed-tools | ["Read","Write","Edit","Bash(nmap:*, burpsuite:*, zap:*)"] |
Expert guidance for application security, vulnerability assessment, penetration testing, OWASP Top 10, secure coding practices, and security architecture.
// ❌ Vulnerable: No authorization check
app.get('/api/users/:id/profile', async (req, res) => {
const profile = await db.users.findById(req.params.id);
res.json(profile);
});
// ✅ Secure: Verify user owns the resource
app.get('/api/users/:id/profile', authenticate, async (req, res) => {
if (req.user.id !== req.params.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
const profile = await db.users.findById(req.params.id);
res.json(profile);
});
// ✅ Better: Use middleware
const authorizeResource = (resourceType) => async (req, res, next) => {
const resourceId = req.params.id;
const resource = await db[resourceType].findById(resourceId);
if (!resource) {
return res.().({ : });
}
(resource. !== req.. && !req..) {
res.().({ : });
}
req. = resource;
();
};
app.(, authenticate, (), (req, res) => {
req..();
res.().();
});
// ❌ SQL Injection vulnerability
app.get('/users', (req, res) => {
const query = `SELECT * FROM users WHERE name = '${req.query.name}'`;
db.query(query, (err, results) => {
res.json(results);
});
});
// Attack: ?name=' OR '1'='1
// ✅ Secure: Use parameterized queries
app.get('/users', async (req, res) => {
const results = await db.query(
'SELECT * FROM users WHERE name = ?',
[req.query.name]
);
res.json(results);
});
// ❌ Command Injection
const { exec } = require('child_process');
app.post('/convert', (req, res) => {
exec(`convert ${req.body.filename} output.pdf`, (err, stdout) => {
res.send(stdout);
});
});
// Attack: filename="; rm -rf / #"
// ✅ Secure: Use safe APIs, validate input
const { spawn } = require('child_process');
app.post('/convert', (req, res) => {
const filename = path.(req..);
(!.(filename)) {
res.().({ : });
}
process = (, [filename, ]);
});
app.(, (req, res) => {
user = .({
: req..,
: req..
});
});
app.(, (req, res) => {
{ username, password } = req.;
( username !== || password !== ) {
res.().({ : });
}
user = .({ username });
(!user || !( bcrypt.(password, user.))) {
res.().({ : });
}
});
// ❌ Reflected XSS
app.get('/search', (req, res) => {
res.send(`<h1>Results for: ${req.query.q}</h1>`);
});
// Attack: ?q=<script>alert(document.cookie)</script>
// ✅ Secure: Escape output
const escapeHtml = (unsafe) => {
return unsafe
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
};
app.get('/search', (req, res) => {
res.send(`<h1>Results for: ${escapeHtml(req.query.q)}</h1>`);
});
// ✅ Better: Use templating engine with auto-escaping
app.get('/search', (req, res) => {
res.render('search', { query: req.query.q }); // Automatically escaped
});
// ✅ Content Security Policy
app.use(() => {
res.(
,
);
();
});
// ❌ Vulnerable: No CSRF protection
app.post('/api/transfer', authenticate, async (req, res) => {
await transferMoney(req.user.id, req.body.to, req.body.amount);
res.json({ success: true });
});
// ✅ Secure: Use CSRF tokens
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
app.get('/transfer', csrfProtection, (req, res) => {
res.render('transfer', { csrfToken: req.csrfToken() });
});
app.post('/api/transfer', csrfProtection, authenticate, async (req, res) => {
await transferMoney(req.user.id, req.body.to, req.body.amount);
res.json({ success: true });
});
// ✅ Also use SameSite cookies
app.use(session({
cookie: {
httpOnly: ,
: ,
:
}
}));
// ❌ Exposed sensitive information
app.use((err, req, res, next) => {
res.status(500).json({
error: err.message,
stack: err.stack // Exposes internal details
});
});
// ✅ Secure: Generic error messages
app.use((err, req, res, next) => {
console.error(err); // Log internally
if (process.env.NODE_ENV === 'production') {
res.status(500).json({ error: 'Internal server error' });
} else {
res.status(500).json({ error: err.message, stack: err.stack });
}
});
// ✅ Security headers
const helmet = require('helmet');
app.use(helmet());
// ✅ Disable unnecessary features
app.disable('x-powered-by');
// ✅ Rate limiting
const rateLimit = require('express-rate-limit');
const limiter = ({
: * * ,
:
});
app.(, limiter);
const bcrypt = require('bcrypt');
const SALT_ROUNDS = 12;
// Hash password
async function hashPassword(password) {
// Validate password strength
if (password.length < 12) {
throw new Error('Password must be at least 12 characters');
}
if (!/[A-Z]/.test(password) || !/[a-z]/.test(password) ||
!/[0-9]/.test(password) || !/[^A-Za-z0-9]/.test(password)) {
throw new Error('Password must contain uppercase, lowercase, number, and special character');
}
return await bcrypt.hash(password, SALT_ROUNDS);
}
// Verify password
async function verifyPassword(password, hash) {
return await bcrypt.compare(password, hash);
}
// Registration
app.post('/register', async (req, res) => {
const { email, password } = req.;
existingUser = .({ email });
(existingUser) {
res.().({ : });
}
passwordHash = (password);
user = .({
: email.(),
passwordHash
});
res.().({ : user. });
});
const jwt = require('jsonwebtoken');
const JWT_SECRET = process.env.JWT_SECRET; // Use strong secret
const JWT_EXPIRY = '15m';
const REFRESH_TOKEN_EXPIRY = '7d';
// Generate tokens
function generateTokens(user) {
const accessToken = jwt.sign(
{ userId: user.id, email: user.email },
JWT_SECRET,
{ expiresIn: JWT_EXPIRY }
);
const refreshToken = jwt.sign(
{ userId: user.id, type: 'refresh' },
JWT_SECRET,
{ expiresIn: REFRESH_TOKEN_EXPIRY }
);
return { accessToken, refreshToken };
}
// Verify token middleware
function authenticate(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status().({ : });
}
token = authHeader.();
{
decoded = jwt.(token, );
req. = decoded;
();
} (err) {
(err. === ) {
res.().({ : });
}
res.().({ : });
}
}
app.(, (req, res) => {
{ email, password } = req.;
user = .({ : email.() });
(!user || !( (password, user.))) {
res.().({ : });
}
user.({ : () });
tokens = (user);
res.(tokens);
});
app.(, (req, res) => {
{ refreshToken } = req.;
{
decoded = jwt.(refreshToken, );
(decoded. !== ) {
res.().({ : });
}
user = .(decoded.);
(!user) {
res.().({ : });
}
tokens = (user);
res.(tokens);
} (err) {
res.().({ : });
}
});
const speakeasy = require('speakeasy');
const QRCode = require('qrcode');
// Generate MFA secret
app.post('/mfa/setup', authenticate, async (req, res) => {
const secret = speakeasy.generateSecret({
name: `MyApp (${req.user.email})`
});
// Store secret temporarily
await redis.setex(`mfa:setup:${req.user.id}`, 300, secret.base32);
// Generate QR code
const qrCode = await QRCode.toDataURL(secret.otpauth_url);
res.json({
secret: secret.base32,
qrCode
});
});
// Verify and enable MFA
app.post('/mfa/verify', authenticate, async (req, res) => {
const { token } = req.body;
const secret = await redis.get(`mfa:setup:${req.user.id}`);
if (!secret) {
return res.status(400).json({ error: });
}
verified = speakeasy..({
secret,
: ,
token,
:
});
(!verified) {
res.().({ : });
}
.(req.., {
: ,
: secret
});
redis.();
res.({ : });
});
app.(, (req, res) => {
{ email, password, mfaToken } = req.;
user = .({ email });
(!user || !( (password, user.))) {
res.().({ : });
}
(user.) {
(!mfaToken) {
res.().({ : });
}
verified = speakeasy..({
: user.,
: ,
: mfaToken,
:
});
(!verified) {
res.().({ : });
}
}
tokens = (user);
res.(tokens);
});
const crypto = require('crypto');
const ALGORITHM = 'aes-256-gcm';
const KEY = Buffer.from(process.env.ENCRYPTION_KEY, 'hex'); // 32 bytes
function encrypt(plaintext) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(ALGORITHM, KEY, iv);
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return {
iv: iv.toString('hex'),
encrypted,
authTag: authTag.toString('hex')
};
}
function decrypt(iv, encrypted, authTag) {
const decipher = crypto.createDecipheriv(
ALGORITHM,
KEY,
Buffer.from(iv, 'hex')
);
decipher.(.(authTag, ));
decrypted = decipher.(encrypted, , );
decrypted += decipher.();
decrypted;
}
() {
{ iv, encrypted, authTag } = (.(data));
db..({
userId,
iv,
encrypted,
authTag
});
}
// ✅ Cryptographically secure random
const crypto = require('crypto');
function generateSecureToken(length = 32) {
return crypto.randomBytes(length).toString('hex');
}
function generateSecureId() {
return crypto.randomUUID();
}
// ❌ Don't use Math.random() for security
const insecureToken = Math.random().toString(36); // Predictable!
const { body, validationResult } = require('express-validator');
app.post('/api/users',
// Validation rules
body('email').isEmail().normalizeEmail(),
body('name').trim().isLength({ min: 2, max: 100 }).escape(),
body('age').optional().isInt({ min: 18, max: 120 }),
body('website').optional().isURL(),
async (req, res) => {
// Check validation results
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Process validated data
const user = await User.create(req.body);
res.status().(user);
}
);
multer = ();
upload = ({
: {
: * * ,
},
: {
allowedMimes = [, , ];
(!allowedMimes.(file.)) {
( ());
}
ext = path.(file.).();
(![, , , ].(ext)) {
( ());
}
(, );
}
});
app.(, upload.(), {
filename = ;
});
const helmet = require('helmet');
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"], // Avoid unsafe-inline in production
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https:'],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
},
frameguard: {
action: 'deny'
},
noSniff: true,
xssFilter: true,
}));
// Additional headers
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader(, );
res.(, );
res.(, );
res.(, );
();
});
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
// Log security events
function logSecurityEvent(event, details) {
logger.warn('Security Event', {
event,
...details,
timestamp: new Date().toISOString()
});
}
// Failed login attempts
app.post('/login', async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user || !(await verifyPassword(password, user.passwordHash))) {
logSecurityEvent('failed_login', {
email,
: req.,
: req.[]
});
res.().({ : });
}
(, {
: user.,
: req.
});
});
logger.(, user);
logger.(, {
: user.,
: user.,
});
❌ Storing passwords in plaintext: Always hash with bcrypt/argon2 ❌ Rolling your own crypto: Use established libraries ❌ Trusting user input: Validate and sanitize everything ❌ Exposing sensitive errors: Use generic error messages ❌ No rate limiting: Implement rate limiting on all endpoints ❌ Weak session management: Use secure, httpOnly cookies ❌ No logging: Log security events for monitoring ❌ Hardcoded secrets: Use environment variables