Token导航 LogoToken导航TokenDH.com
研究检索执行命令github未标认证来源可访问clear审计异常

security-expert安全专家

Agent Skill

用于辅助安全审计、权限检查、凭据风险、认证流程和常见漏洞排查。它适合让 Agent 梳理敏感配置、检查依赖风险、分析鉴权逻辑或生成安全复核清单。使用时不能把工具输出直接当最终结论,涉及密钥、令牌、用户数据或生产系统时,应先确认最小权限、脱敏方式和操作边界。

总安装

2,856

周安装

119

GitHub Stars

19

下载量

952
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:security-expert(安全专家)
来源仓库:https://github.com/personamanagmentlayer/pcl
仓库路径:skills/security-expert
安装命令:
npx skills add https://github.com/personamanagmentlayer/pcl --skill security-expert
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/personamanagmentlayer/pcl --skill security-expert

简介

用于辅助安全审计、权限检查和常见漏洞排查。security-expert 属于研究检索类 Skill,可作为该场景下的辅助能力补充。

  • 适合梳理敏感配置、分析鉴权逻辑或生成安全复核清单。
  • 通过 GitHub 安装,支持多种 AI 编程宿主环境。
  • 不能将工具输出直接作为最终结论,需人工复核。
  • 涉及密钥、令牌或用户数据时,应先确认最小权限和操作边界。

SKILL.md

Security Expert

Expert guidance for application security, vulnerability assessment, penetration testing, OWASP Top 10, secure coding practices, and security architecture.

Core Concepts

Security Principles

  • Defense in depth
  • Least privilege
  • Secure by default
  • Fail securely
  • Complete mediation
  • Separation of duties
  • Zero trust architecture

OWASP Top 10 (2021)

  1. Broken Access Control
  2. Cryptographic Failures
  3. Injection
  4. Insecure Design
  5. Security Misconfiguration
  6. Vulnerable and Outdated Components
  7. Identification and Authentication Failures
  8. Software and Data Integrity Failures
  9. Security Logging and Monitoring Failures
  10. Server-Side Request Forgery (SSRF)

Security Domains

  • Authentication & Authorization
  • Cryptography
  • Input validation
  • Session management
  • Error handling
  • Secure communications
  • Data protection

OWASP Top 10 Vulnerabilities

1. Broken Access Control

// ❌ 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.status(404).json({ error: 'Not found' });
  }

  if (resource.userId !== req.user.id && !req.user.isAdmin) {
    return res.status(403).json({ error: 'Forbidden' });
  }

  req.resource = resource;
  next();
};

app.delete('/api/posts/:id', authenticate, authorizeResource('posts'), async (req, res) => {
  await req.resource.delete();
  res.status(204).send();
});

2. Injection (SQL, NoSQL, Command)

// ❌ 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.basename(req.body.filename); // Remove path traversal
  if (!/^[a-zA-Z0-9_-]+\.(jpg|png)$/.test(filename)) {
    return res.status(400).json({ error: 'Invalid filename' });
  }

  const process = spawn('convert', [filename, 'output.pdf']);
  // Handle process output safely
});

// ❌ NoSQL Injection (MongoDB)
app.post('/login', async (req, res) => {
  const user = await User.findOne({
    username: req.body.username,
    password: req.body.password
  });
});
// Attack: {"username": {"$ne": null}, "password": {"$ne": null}}

// ✅ Secure: Sanitize input, use proper types
app.post('/login', async (req, res) => {
  const { username, password } = req.body;

  if (typeof username !== 'string' || typeof password !== 'string') {
    return res.status(400).json({ error: 'Invalid input' });
  }

  const user = await User.findOne({ username });
  if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Create session
});

3. Cross-Site Scripting (XSS)

// ❌ 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((req, res, next) => {
  res.setHeader(
    'Content-Security-Policy',
    "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;"
  );
  next();
});

4. Cross-Site Request Forgery (CSRF)

// ❌ 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: true,
    secure: true,
    sameSite: 'strict'
  }
}));

5. Security Misconfiguration

// ❌ 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 = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});
app.use('/api/', limiter);

Authentication & Authorization

Password Security

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.body;

  // Check if user exists
  const existingUser = await User.findOne({ email });
  if (existingUser) {
    return res.status(400).json({ error: 'Email already registered' });
  }

  // Hash password
  const passwordHash = await hashPassword(password);

  // Create user
  const user = await User.create({
    email: email.toLowerCase(),
    passwordHash
  });

  res.status(201).json({ id: user.id });
});

JWT Authentication

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(401).json({ error: 'No token provided' });
  }

  const token = authHeader.substring(7);

  try {
    const decoded = jwt.verify(token, JWT_SECRET);
    req.user = decoded;
    next();
  } catch (err) {
    if (err.name === 'TokenExpiredError') {
      return res.status(401).json({ error: 'Token expired' });
    }
    return res.status(401).json({ error: 'Invalid token' });
  }
}

// Login
app.post('/login', async (req, res) => {
  const { email, password } = req.body;

  const user = await User.findOne({ email: email.toLowerCase() });
  if (!user || !(await verifyPassword(password, user.passwordHash))) {
    // Generic error to prevent username enumeration
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Update last login
  await user.update({ lastLoginAt: new Date() });

  const tokens = generateTokens(user);
  res.json(tokens);
});

// Refresh token
app.post('/refresh', async (req, res) => {
  const { refreshToken } = req.body;

  try {
    const decoded = jwt.verify(refreshToken, JWT_SECRET);

    if (decoded.type !== 'refresh') {
      return res.status(401).json({ error: 'Invalid token type' });
    }

    const user = await User.findById(decoded.userId);
    if (!user) {
      return res.status(401).json({ error: 'User not found' });
    }

    const tokens = generateTokens(user);
    res.json(tokens);
  } catch (err) {
    return res.status(401).json({ error: 'Invalid refresh token' });
  }
});

Multi-Factor Authentication (MFA)

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: 'MFA setup expired' });
  }

  const verified = speakeasy.totp.verify({
    secret,
    encoding: 'base32',
    token,
    window: 2
  });

  if (!verified) {
    return res.status(400).json({ error: 'Invalid token' });
  }

  // Enable MFA for user
  await User.update(req.user.id, {
    mfaEnabled: true,
    mfaSecret: secret
  });

  await redis.del(`mfa:setup:${req.user.id}`);

  res.json({ success: true });
});

// Login with MFA
app.post('/login/mfa', async (req, res) => {
  const { email, password, mfaToken } = req.body;

  const user = await User.findOne({ email });
  if (!user || !(await verifyPassword(password, user.passwordHash))) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  if (user.mfaEnabled) {
    if (!mfaToken) {
      return res.status(401).json({ error: 'MFA token required' });
    }

    const verified = speakeasy.totp.verify({
      secret: user.mfaSecret,
      encoding: 'base32',
      token: mfaToken,
      window: 2
    });

    if (!verified) {
      return res.status(401).json({ error: 'Invalid MFA token' });
    }
  }

  const tokens = generateTokens(user);
  res.json(tokens);
});

Cryptography

Encryption at Rest

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.setAuthTag(Buffer.from(authTag, 'hex'));

  let decrypted = decipher.update(encrypted, 'hex', 'utf8');
  decrypted += decipher.final('utf8');

  return decrypted;
}

// Store sensitive data
async function storeSensitiveData(userId, data) {
  const { iv, encrypted, authTag } = encrypt(JSON.stringify(data));

  await db.sensitiveData.create({
    userId,
    iv,
    encrypted,
    authTag
  });
}

Secure Random Generation

// ✅ 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!

Input Validation

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(201).json(user);
  }
);

// File upload validation
const multer = require('multer');

const upload = multer({
  limits: {
    fileSize: 5 * 1024 * 1024, // 5MB max
  },
  fileFilter: (req, file, cb) => {
    // Check file type
    const allowedMimes = ['image/jpeg', 'image/png', 'image/gif'];
    if (!allowedMimes.includes(file.mimetype)) {
      return cb(new Error('Invalid file type'));
    }

    // Check file extension
    const ext = path.extname(file.originalname).toLowerCase();
    if (!['.jpg', '.jpeg', '.png', '.gif'].includes(ext)) {
      return cb(new Error('Invalid file extension'));
    }

    cb(null, true);
  }
});

app.post('/upload', upload.single('image'), (req, res) => {
  // Verify file content matches extension
  // Store with random filename to prevent path traversal
  const filename = `${crypto.randomUUID()}${path.extname(req.file.originalname)}`;
  // Save file...
});

Security Headers

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('X-Frame-Options', 'DENY');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
  res.setHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
  next();
});

Secure Logging

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,
      ip: req.ip,
      userAgent: req.headers['user-agent']
    });

    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Success
  logSecurityEvent('successful_login', {
    userId: user.id,
    ip: req.ip
  });

  // Generate tokens...
});

// ❌ Don't log sensitive data
logger.info('User data', user); // May contain passwordHash, tokens

// ✅ Sanitize before logging
logger.info('User data', {
  id: user.id,
  email: user.email,
  // Omit sensitive fields
});

Best Practices

Secure Development Lifecycle

  1. Threat modeling
  2. Security requirements
  3. Secure coding standards
  4. Code review
  5. Security testing (SAST/DAST)
  6. Vulnerability scanning
  7. Penetration testing
  8. Security monitoring

Defense in Depth

  • Multiple layers of security
  • Assume breach mentality
  • Principle of least privilege
  • Input validation at every layer
  • Output encoding
  • Secure configuration

Security Testing

  • Static Application Security Testing (SAST)
  • Dynamic Application Security Testing (DAST)
  • Interactive Application Security Testing (IAST)
  • Software Composition Analysis (SCA)
  • Penetration testing
  • Bug bounty programs

Anti-Patterns to Avoid

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

Resources

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Claude Code

29.1%
按下载量换算277

OpenCode

24.04%
按下载量换算229

Codex

15.91%
按下载量换算151

Antigravity

12.65%
按下载量换算120

Gemini CLI

8.34%
按下载量换算79

windsurf

3.19%
按下载量换算30

安全审计

Gen Agent Trust Hub

未通过

Socket

通过

Snyk

通过

权限和风险

执行命令

安装流程涉及命令执行,可能通过 npx skills add https://github.com/personamanagmentlayer/pcl --skill security-expert;npx skills add personamanagmentlayer/pcl --skill "security-expert" 联网下载 Skill 或依赖。用户安装前应确认命令来源、仓库内容和执行环境。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。

来源信息

继续浏览同类 Skills