{"id":"191b97d8-8ddb-411c-b5ef-2f91e0123189","shortId":"vu3HGD","kind":"skill","title":"security-patterns","tagline":"Implements authentication, authorization, encryption, secrets management, and security hardening patterns. Use when designing auth flows, managing secrets, configuring CORS, implementing rate limiting, or when asked about JWT, OAuth, password hashing, API keys, RBAC, or security ","description":"# Security Patterns\n\n### When to Load\n\n- **Trigger**: Auth flows, encryption, secrets management, CORS configuration, input validation, rate limiting\n- **Skip**: No security surface involved in the current task\n\n## Security Implementation Workflow\n\nCopy this checklist and track progress:\n\n```\nSecurity Implementation Progress:\n- [ ] Step 1: Choose authentication strategy\n- [ ] Step 2: Implement authorization model\n- [ ] Step 3: Set up password hashing\n- [ ] Step 4: Configure secrets management\n- [ ] Step 5: Enable encryption (transit + rest)\n- [ ] Step 6: Configure CORS\n- [ ] Step 7: Add rate limiting\n- [ ] Step 8: Validate against anti-patterns checklist\n```\n\n## Authentication Patterns\n\n### JWT (JSON Web Tokens)\n\n```typescript\nimport jwt from \"jsonwebtoken\";\n\nfunction generateTokens(user: User) {\n  const accessToken = jwt.sign(\n    { sub: user.id, role: user.role },\n    process.env.JWT_SECRET!,\n    { expiresIn: \"15m\", algorithm: \"HS256\" },\n  );\n  const refreshToken = jwt.sign(\n    { sub: user.id, tokenVersion: user.tokenVersion },\n    process.env.JWT_REFRESH_SECRET!,\n    { expiresIn: \"7d\" },\n  );\n  return { accessToken, refreshToken };\n}\n\n// WRONG: localStorage (XSS vulnerable) | CORRECT: httpOnly cookie for refresh, memory for access\nres.cookie(\"refreshToken\", refreshToken, {\n  httpOnly: true,\n  secure: true,\n  sameSite: \"strict\",\n  maxAge: 7 * 24 * 60 * 60 * 1000,\n  path: \"/api/auth/refresh\",\n});\n```\n\n### JWT Verification Middleware\n\n```typescript\nfunction authenticate(req: Request, res: Response, next: NextFunction) {\n  const header = req.headers.authorization;\n  if (!header?.startsWith(\"Bearer \")) {\n    return res.status(401).json({ error: \"Missing token\" });\n  }\n\n  try {\n    const token = header.slice(7);\n    const payload = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload;\n    req.user = { id: payload.sub, role: payload.role };\n    next();\n  } catch (err) {\n    if (err instanceof jwt.TokenExpiredError) {\n      return res.status(401).json({ error: \"Token expired\" });\n    }\n    return res.status(401).json({ error: \"Invalid token\" });\n  }\n}\n```\n\n### Session-Based Auth\n\n```typescript\nimport session from \"express-session\";\nimport RedisStore from \"connect-redis\";\n\napp.use(\n  session({\n    store: new RedisStore({ client: redisClient }),\n    secret: process.env.SESSION_SECRET!,\n    resave: false,\n    saveUninitialized: false,\n    cookie: {\n      httpOnly: true,\n      secure: process.env.NODE_ENV === \"production\",\n      sameSite: \"strict\",\n      maxAge: 24 * 60 * 60 * 1000, // 24 hours\n    },\n  }),\n);\n```\n\n### OAuth 2.0 / OIDC Flow Summary\n\n```\nAuthorization Code Flow (web apps with backend):\n1. Redirect to provider: /authorize?response_type=code&client_id=...&redirect_uri=...&scope=openid email\n2. User authenticates, provider redirects back with ?code=AUTHORIZATION_CODE\n3. Backend exchanges code for tokens (POST /token with client_secret)\n4. Backend receives access_token + id_token, creates session/JWT\n\nPKCE Flow (SPAs, mobile): Same but with code_verifier/code_challenge instead of client_secret\nNEVER use Implicit Flow (deprecated, tokens exposed in URL)\n```\n\n### API Key Authentication\n\n```typescript\nasync function authenticateApiKey(\n  req: Request,\n  res: Response,\n  next: NextFunction,\n) {\n  const apiKey = req.headers[\"x-api-key\"] as string;\n  if (!apiKey) return res.status(401).json({ error: \"API key required\" });\n\n  // WRONG: Direct comparison (timing attack) | CORRECT: Hash-based lookup\n  const hashedKey = crypto.createHash(\"sha256\").update(apiKey).digest(\"hex\");\n  const keyRecord = await db.apiKey.findUnique({ where: { hash: hashedKey } });\n  if (!keyRecord || keyRecord.revokedAt)\n    return res.status(401).json({ error: \"Invalid API key\" });\n\n  req.apiClient = { id: keyRecord.clientId, scopes: keyRecord.scopes };\n  next();\n}\n```\n\n## Authorization Models\n\n### RBAC (Role-Based Access Control)\n\n```typescript\nconst PERMISSIONS = {\n  admin: [\n    \"users:read\",\n    \"users:write\",\n    \"users:delete\",\n    \"posts:read\",\n    \"posts:write\",\n    \"posts:delete\",\n  ],\n  editor: [\"posts:read\", \"posts:write\", \"posts:delete\", \"users:read\"],\n  viewer: [\"posts:read\", \"users:read\"],\n} as const;\n\ntype Role = keyof typeof PERMISSIONS;\n\nfunction authorize(...requiredPermissions: string[]) {\n  return (req: Request, res: Response, next: NextFunction) => {\n    const userPermissions = PERMISSIONS[req.user.role as Role] || [];\n    const hasPermission = requiredPermissions.every((p) =>\n      (userPermissions as readonly string[]).includes(p),\n    );\n    if (!hasPermission)\n      return res.status(403).json({ error: \"Insufficient permissions\" });\n    next();\n  };\n}\n\n// Usage: app.delete(\"/api/users/:id\", authenticate, authorize(\"users:delete\"), deleteUser);\n```\n\n### Resource-Level Authorization\n\n```typescript\n// WRONG: Only checking role, not ownership -- any editor can edit ANY post\n// CORRECT: Check ownership or admin role\napp.put(\n  \"/api/posts/:id\",\n  authenticate,\n  authorize(\"posts:write\"),\n  async (req, res) => {\n    const post = await db.post.findUnique({ where: { id: req.params.id } });\n    if (!post) return res.status(404).json({ error: \"Not found\" });\n    if (post.authorId !== req.user.id && req.user.role !== \"admin\") {\n      return res\n        .status(403)\n        .json({ error: \"Not authorized to edit this post\" });\n    }\n    await db.post.update({ where: { id: req.params.id }, data: req.body });\n  },\n);\n```\n\n## Password Handling\n\n```typescript\nimport bcrypt from \"bcrypt\";\n// WRONG: plaintext or MD5/SHA256 (too fast, brute-forceable)\n// CORRECT: bcrypt with appropriate cost factor\nconst SALT_ROUNDS = 12; // ~250ms on modern hardware\n\nasync function hashPassword(password: string): Promise<string> {\n  return bcrypt.hash(password, SALT_ROUNDS);\n}\nasync function verifyPassword(\n  password: string,\n  hash: string,\n): Promise<boolean> {\n  return bcrypt.compare(password, hash); // constant-time comparison built-in\n}\n\n// Registration\nawait db.user.create({\n  data: { email, password: await hashPassword(req.body.password) },\n});\n\n// Login -- WRONG: \"Invalid password\" (reveals email exists) | CORRECT: generic message\nconst user = await db.user.findUnique({ where: { email } });\nif (!user || !(await verifyPassword(req.body.password, user.password))) {\n  return res.status(401).json({ error: \"Invalid email or password\" });\n}\n```\n\n### Password Policies\n\n```typescript\nfunction validatePassword(password: string): string[] {\n  const errors: string[] = [];\n  if (password.length < 12) errors.push(\"Minimum 12 characters\");\n  if (password.length > 128) errors.push(\"Maximum 128 characters\");\n\n  // Check against breached password lists (haveibeenpwned API or local)\n  // Do NOT enforce arbitrary complexity rules (uppercase + number + symbol)\n  // NIST 800-63B recommends length over complexity\n  return errors;\n}\n```\n\n## Secrets Management\n\n```python\n# WRONG: Hardcoded values in source code\n# API_KEY = \"some-value-here\"\n\n# CORRECT: Environment variables loaded from .env\nfrom dotenv import load_dotenv\nimport os\n\nload_dotenv()\napi_key = os.getenv(\"API_KEY\")\ndb_url = os.getenv(\"DATABASE_URL\")\n\n# CORRECT: Secrets manager for production\n# AWS: Secrets Manager, Parameter Store\n# GCP: Secret Manager\n# HashiCorp Vault for self-hosted\n```\n\n### Secret Rotation\n\n```\n1. Generate new secret value\n2. Deploy code that accepts BOTH old and new values\n3. Update all consumers to use the new value\n4. Verify old value is no longer in use\n5. Revoke old value\n\nNever: Rotate in-place without a transition period\n```\n\n## Encryption Patterns\n\n### In Transit\n\n```typescript\n// Redirect HTTP to HTTPS in production\napp.use((req, res, next) => {\n  if (\n    req.headers[\"x-forwarded-proto\"] !== \"https\" &&\n    process.env.NODE_ENV === \"production\"\n  ) {\n    return res.redirect(301, `https://${req.hostname}${req.url}`);\n  }\n  next();\n});\n// HSTS header\napp.use((req, res, next) => {\n  res.setHeader(\n    \"Strict-Transport-Security\",\n    \"max-age=31536000; includeSubDomains\",\n  );\n  next();\n});\n```\n\n### At Rest\n\n```typescript\nimport crypto from \"crypto\";\nconst ALGORITHM = \"aes-256-gcm\";\n\nfunction encrypt(\n  plaintext: string,\n  key: Buffer,\n): { ciphertext: string; iv: string; tag: string } {\n  const iv = crypto.randomBytes(16);\n  const cipher = crypto.createCipheriv(ALGORITHM, key, iv);\n  let ciphertext =\n    cipher.update(plaintext, \"utf8\", \"hex\") + cipher.final(\"hex\");\n  return {\n    ciphertext,\n    iv: iv.toString(\"hex\"),\n    tag: cipher.getAuthTag().toString(\"hex\"),\n  };\n}\n\nfunction decrypt(\n  ciphertext: string,\n  key: Buffer,\n  iv: string,\n  tag: string,\n): string {\n  const decipher = crypto.createDecipheriv(\n    ALGORITHM,\n    key,\n    Buffer.from(iv, \"hex\"),\n  );\n  decipher.setAuthTag(Buffer.from(tag, \"hex\"));\n  return decipher.update(ciphertext, \"hex\", \"utf8\") + decipher.final(\"utf8\");\n}\n// Use for PII, sensitive data. Encryption key in secrets manager, NOT in code.\n```\n\n## CORS Configuration\n\n```typescript\nimport cors from \"cors\";\n\n// WRONG: Allow everything\napp.use(cors()); // origin: *, credentials: false\n\n// WRONG: Wildcard with credentials\napp.use(cors({ origin: \"*\", credentials: true })); // browsers reject this\n\n// CORRECT: Explicit allowed origins\nconst ALLOWED_ORIGINS = [\n  \"https://myapp.com\",\n  \"https://admin.myapp.com\",\n  ...(process.env.NODE_ENV !== \"production\" ? [\"http://localhost:3000\"] : []),\n];\n\napp.use(\n  cors({\n    origin: (origin, callback) => {\n      if (!origin || ALLOWED_ORIGINS.includes(origin)) {\n        callback(null, true);\n      } else {\n        callback(new Error(\"Not allowed by CORS\"));\n      }\n    },\n    credentials: true,\n    methods: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"],\n    allowedHeaders: [\"Content-Type\", \"Authorization\"],\n    maxAge: 86400, // cache preflight for 24 hours\n  }),\n);\n```\n\n## Rate Limiting\n\n```typescript\nimport rateLimit from \"express-rate-limit\";\nimport RedisStore from \"rate-limit-redis\";\n\n// Global rate limit\napp.use(\n  rateLimit({\n    windowMs: 15 * 60 * 1000, // 15 minutes\n    max: 100, // 100 requests per window\n    standardHeaders: true, // RateLimit-* headers\n    legacyHeaders: false,\n    store: new RedisStore({\n      sendCommand: (...args) => redisClient.sendCommand(args),\n    }),\n  }),\n);\n\n// Strict limit on auth endpoints\napp.use(\n  \"/api/auth/login\",\n  rateLimit({\n    windowMs: 15 * 60 * 1000,\n    max: 5, // 5 login attempts per 15 min\n    message: { error: \"Too many login attempts. Try again later.\" },\n  }),\n);\n\n// Per-API-key rate limiting for developer APIs\napp.use(\n  \"/api/v1/\",\n  rateLimit({\n    windowMs: 60 * 1000, // 1 minute\n    max: 60, // 60 requests per minute\n    keyGenerator: (req) => req.apiClient?.id || req.ip,\n  }),\n);\n```\n\n## Security Headers\n\n```typescript\nimport helmet from \"helmet\";\n\napp.use(helmet()); // Sets many secure headers at once\n\n// Key headers helmet sets:\n// X-Content-Type-Options: nosniff\n// X-Frame-Options: DENY\n// Strict-Transport-Security: max-age=15552000; includeSubDomains\n// Content-Security-Policy: default-src 'self'\n\n// Customize CSP for your app\napp.use(\n  helmet.contentSecurityPolicy({\n    directives: {\n      defaultSrc: [\"'self'\"],\n      scriptSrc: [\"'self'\"],\n      styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n      imgSrc: [\"'self'\", \"data:\", \"https://cdn.example.com\"],\n      connectSrc: [\"'self'\", \"https://api.example.com\"],\n    },\n  }),\n);\n```\n\n## Input Validation\n\n```typescript\nimport { z } from \"zod\";\n\n// WRONG: Trusting user input directly (SQL injection risk)\napp.post(\"/api/users\", (req, res) => {\n  db.query(`SELECT * FROM users WHERE email = '${req.body.email}'`);\n});\n\n// CORRECT: Validate with schema, use parameterized queries\nconst CreateUserSchema = z.object({\n  email: z.string().email().max(255),\n  name: z.string().min(1).max(100).trim(),\n  age: z.number().int().min(13).max(150).optional(),\n});\n\napp.post(\"/api/users\", async (req, res) => {\n  const result = CreateUserSchema.safeParse(req.body);\n  if (!result.success) {\n    return res.status(400).json({ errors: result.error.flatten() });\n  }\n  // Use parameterized query (ORM or prepared statement)\n  await db.user.create({ data: result.data });\n});\n```\n\n## Common Anti-Patterns Summary\n\n```\nAVOID                              DO INSTEAD\n-------------------------------------------------------------------\nJWT in localStorage                httpOnly secure cookie (refresh), memory (access)\nMD5/SHA for passwords              bcrypt or argon2 with proper cost factor\nHardcoded secrets in code          Environment variables + secrets manager\ncors({ origin: '*' })             Explicit allowed origins list\n\"Invalid password\" message         \"Invalid email or password\" (no enumeration)\nNo rate limiting on auth           Strict rate limits on login/register\nRolling your own crypto            Use established libraries (jose, bcrypt)\nTrusting user input                Validate with zod/joi, parameterized queries\nSame API key forever               Rotate keys regularly, support multiple active\nNo HTTPS redirect                  Force HTTPS + HSTS header\nSymmetric JWT for multi-service    Use RS256/ES256 (asymmetric) for distributed\nNo input length limits             Max length on all string inputs\n```","tags":["security","patterns","claude","workflow","cloudai-x","agent-skills","ai-agents","claude-code","codex","cursor","skills"],"capabilities":["skill","source-cloudai-x","skill-security-patterns","topic-agent-skills","topic-ai-agents","topic-claude-code","topic-codex","topic-cursor","topic-skills","topic-workflow"],"categories":["claude-workflow-v2"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/CloudAI-X/claude-workflow-v2/security-patterns","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add CloudAI-X/claude-workflow-v2","source_repo":"https://github.com/CloudAI-X/claude-workflow-v2","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 1352 github stars · SKILL.md body (13,142 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-03T00:52:56.205Z","embedding":null,"createdAt":"2026-04-18T21:54:59.570Z","updatedAt":"2026-05-03T00:52:56.205Z","lastSeenAt":"2026-05-03T00:52:56.205Z","tsv":"'-256':940 '-63':767 '/api/auth/login':1158 '/api/auth/refresh':192 '/api/posts':573 '/api/users':542,1296,1337 '/api/v1':1191 '/authorize':321 '/token':349 '1':78,317,836,1196,1324 '100':1134,1135,1326 '1000':190,302,1130,1163,1195 '12':647,735,738 '128':742,745 '13':1332 '15':1128,1131,1161,1170 '150':1334 '15552000':1246 '15m':146 '16':957 '2':83,332,841 '2.0':306 '24':187,299,303,1103 '250ms':648 '255':1320 '3':88,342,851 '3000':1064 '301':909 '31536000':927 '4':94,353,860 '400':1349 '401':214,246,253,410,446,715 '403':534,606 '404':593 '5':99,869,1165,1166 '6':105 '60':188,189,300,301,1129,1162,1194,1199,1200 '7':109,186,223 '7d':160 '8':114 '800':766 '86400':1099 'accept':845 'access':175,356,464,1380 'accesstoken':137,162 'activ':1450 'add':110 'admin':469,570,602 'admin.myapp.com':1059 'ae':939 'age':926,1245,1328 'algorithm':147,938,961,995 'allow':1032,1053,1056,1082,1402 'allowed_origins.includes':1072 'allowedhead':1093 'anti':118,1366 'anti-pattern':117,1365 'api':34,384,402,413,450,753,784,805,808,1183,1189,1442 'api.example.com':1279 'apikey':398,407,431 'app':314,1260 'app.delete':541 'app.post':1295,1336 'app.put':572 'app.use':275,893,915,1034,1043,1065,1125,1157,1190,1216,1261 'appropri':641 'arbitrari':759 'arg':1149,1151 'argon2':1386 'ask':28 'asymmetr':1466 'async':388,579,652,663,1338 'attack':420 'attempt':1168,1177 'auth':17,45,261,1155,1418 'authent':5,80,121,198,334,386,544,575 'authenticateapikey':390 'author':6,85,310,340,458,504,545,552,576,610,1097 'avoid':1369 'aw':820 'await':436,584,615,683,688,703,709,1360 'b':768 'back':337 'backend':316,343,354 'base':260,424,463 'bcrypt':626,628,639,1384,1432 'bcrypt.compare':672 'bcrypt.hash':659 'bearer':211 'breach':749 'browser':1048 'brute':636 'brute-forc':635 'buffer':947,986 'buffer.from':997,1001 'built':680 'built-in':679 'cach':1100 'callback':1069,1074,1078 'catch':238 'cdn.example.com':1276 'charact':739,746 'check':556,567,747 'checklist':70,120 'choos':79 'cipher':959 'cipher.final':970 'cipher.getauthtag':978 'cipher.update':966 'ciphertext':948,965,973,983,1006 'client':280,325,351,373 'code':311,324,339,341,345,369,783,843,1023,1394 'common':1364 'comparison':418,678 'complex':760,772 'configur':21,51,95,106,1025 'connect':273 'connect-redi':272 'connectsrc':1277 'const':136,149,205,220,224,397,426,434,467,497,514,520,582,644,701,730,937,954,958,992,1055,1313,1341 'constant':676 'constant-tim':675 'consum':854 'content':1095,1230,1249 'content-security-polici':1248 'content-typ':1094 'control':465 'cooki':170,289,1377 'copi':68 'cor':22,50,107,1024,1028,1030,1035,1044,1066,1084,1399 'correct':168,421,566,638,698,790,815,1051,1306 'cost':642,1389 'creat':360 'createuserschema':1314 'createuserschema.safeparse':1343 'credenti':1037,1042,1046,1085 'crypto':934,936,1427 'crypto.createcipheriv':960 'crypto.createdecipheriv':994 'crypto.createhash':428 'crypto.randombytes':956 'csp':1257 'current':63 'custom':1256 'data':620,685,1015,1275,1362 'databas':813 'db':810 'db.apikey.findunique':437 'db.post.findunique':585 'db.post.update':616 'db.query':1299 'db.user.create':684,1361 'db.user.findunique':704 'deciph':993 'decipher.final':1009 'decipher.setauthtag':1000 'decipher.update':1005 'decrypt':982 'default':1253 'default-src':1252 'defaultsrc':1264 'delet':475,481,488,547,1092 'deleteus':548 'deni':1238 'deploy':842 'deprec':379 'design':16 'develop':1188 'digest':432 'direct':417,1263,1291 'distribut':1468 'dotenv':797,800,804 'edit':563,612 'editor':482,561 'els':1077 'email':331,686,696,706,719,1304,1316,1318,1409 'enabl':100 'encrypt':7,47,101,882,943,1016 'endpoint':1156 'enforc':758 'enumer':1413 'env':294,795,905,1061 'environ':791,1395 'err':239,241 'error':216,248,255,412,448,536,595,608,717,731,774,1080,1173,1351 'errors.push':736,743 'establish':1429 'everyth':1033 'exchang':344 'exist':697 'expir':250 'expiresin':145,159 'explicit':1052,1401 'expos':381 'express':267,1112 'express-rate-limit':1111 'express-sess':266 'factor':643,1390 'fals':286,288,1038,1144 'fast':634 'flow':18,46,308,312,363,378 'forc':1454 'forceabl':637 'forev':1444 'forward':901 'found':597 'frame':1236 'function':132,197,389,503,653,664,725,942,981 'gcm':941 'gcp':825 'generat':837 'generatetoken':133 'generic':699 'get':1088 'global':1122 'handl':623 'hardcod':779,1391 'harden':12 'hardwar':651 'hash':33,92,423,439,668,674 'hash-bas':422 'hashedkey':427,440 'hashicorp':828 'hashpassword':654,689 'haspermiss':521,531 'haveibeenpwn':752 'header':206,209,914,1142,1210,1221,1225,1457 'header.slice':222 'helmet':1213,1215,1217,1226 'helmet.contentsecuritypolicy':1262 'hex':433,969,971,976,980,999,1003,1007 'host':833 'hour':304,1104 'hs256':148 'hsts':913,1456 'http':888 'httpon':169,179,290,1375 'https':890,903,1452,1455 'id':233,326,358,453,543,574,587,618,1207 'imgsrc':1273 'implement':4,23,66,75,84 'implicit':377 'import':128,263,269,625,798,801,933,1027,1108,1115,1212,1283 'in-plac':875 'includ':528 'includesubdomain':928,1247 'inject':1293 'inlin':1272 'input':52,1280,1290,1435,1470,1478 'instanceof':242 'instead':371,1371 'insuffici':537 'int':1330 'invalid':256,449,693,718,1405,1408 'involv':60 'iv':950,955,963,974,987,998 'iv.tostring':975 'jose':1431 'json':124,215,247,254,411,447,535,594,607,716,1350 'jsonwebtoken':131 'jwt':30,123,129,193,1372,1459 'jwt.sign':138,151 'jwt.tokenexpirederror':243 'jwt.verify':226 'jwtpayload':231 'key':35,385,403,414,451,785,806,809,946,962,985,996,1017,1184,1224,1443,1446 'keygener':1204 'keyof':500 'keyrecord':435,442 'keyrecord.clientid':454 'keyrecord.revokedat':443 'keyrecord.scopes':456 'later':1180 'legacyhead':1143 'length':770,1471,1474 'let':964 'level':551 'librari':1430 'limit':25,55,112,1106,1114,1120,1124,1153,1186,1416,1421,1472 'list':751,1404 'load':43,793,799,803 'local':755 'localhost':1063 'localstorag':165,1374 'login':691,1167,1176 'login/register':1423 'longer':866 'lookup':425 'manag':9,19,49,97,776,817,822,827,1020,1398 'mani':1175,1219 'max':925,1133,1164,1198,1244,1319,1325,1333,1473 'max-ag':924,1243 'maxag':185,298,1098 'maximum':744 'md5/sha':1381 'md5/sha256':632 'memori':173,1379 'messag':700,1172,1407 'method':1087 'middlewar':195 'min':1171,1323,1331 'minimum':737 'minut':1132,1197,1203 'miss':217 'mobil':365 'model':86,459 'modern':650 'multi':1462 'multi-servic':1461 'multipl':1449 'myapp.com':1058 'name':1321 'never':375,873 'new':278,838,849,858,1079,1146 'next':203,237,395,457,512,539,896,912,918,929 'nextfunct':204,396,513 'nist':765 'nosniff':1233 'null':1075 'number':763 'oauth':31,305 'oidc':307 'old':847,862,871 'openid':330 'option':1232,1237,1335 'origin':1036,1045,1054,1057,1067,1068,1071,1073,1400,1403 'orm':1356 'os':802 'os.getenv':807,812 'ownership':559,568 'p':523,529 'paramet':823 'parameter':1311,1354,1439 'password':32,91,622,655,660,666,673,687,694,721,722,727,750,1383,1406,1411 'password.length':734,741 'patch':1091 'path':191 'pattern':3,13,40,119,122,883,1367 'payload':225 'payload.role':236 'payload.sub':234 'per':1137,1169,1182,1202 'per-api-key':1181 'period':881 'permiss':468,502,516,538 'pii':1013 'pkce':362 'place':877 'plaintext':630,944,967 'polici':723,1251 'post':348,476,478,480,483,485,487,492,565,577,583,590,614,1089 'post.authorid':599 'preflight':1101 'prepar':1358 'process.env.jwt':143,156,228 'process.env.node':293,904,1060 'process.env.session':283 'product':295,819,892,906,1062 'progress':73,76 'promis':657,670 'proper':1388 'proto':902 'provid':320,335 'put':1090 'python':777 'queri':1312,1355,1440 'rate':24,54,111,1105,1113,1119,1123,1185,1415,1420 'rate-limit-redi':1118 'ratelimit':1109,1126,1141,1159,1192 'rbac':36,460 'read':471,477,484,490,493,495 'readon':526 'receiv':355 'recommend':769 'redi':274,1121 'redirect':318,327,336,887,1453 'rediscli':281 'redisclient.sendcommand':1150 'redisstor':270,279,1116,1147 'refresh':157,172,1378 'refreshtoken':150,163,177,178 'registr':682 'regular':1447 'reject':1049 'req':199,391,508,580,894,916,1205,1297,1339 'req.apiclient':452,1206 'req.body':621,1344 'req.body.email':1305 'req.body.password':690,711 'req.headers':399,898 'req.headers.authorization':207 'req.hostname':910 'req.ip':1208 'req.params.id':588,619 'req.url':911 'req.user':232 'req.user.id':600 'req.user.role':517,601 'request':200,392,509,1136,1201 'requir':415 'requiredpermiss':505 'requiredpermissions.every':522 'res':201,393,510,581,604,895,917,1298,1340 'res.cookie':176 'res.redirect':908 'res.setheader':919 'res.status':213,245,252,409,445,533,592,714,1348 'resav':285 'resourc':550 'resource-level':549 'respons':202,322,394,511 'rest':103,931 'result':1342 'result.data':1363 'result.error.flatten':1352 'result.success':1346 'return':161,212,244,251,408,444,507,532,591,603,658,671,713,773,907,972,1004,1347 'reveal':695 'revok':870 'risk':1294 'role':141,235,462,499,519,557,571 'role-bas':461 'roll':1424 'rotat':835,874,1445 'round':646,662 'rs256/es256':1465 'rule':761 'salt':645,661 'samesit':183,296 'saveuniniti':287 'schema':1309 'scope':329,455 'scriptsrc':1266 'secret':8,20,48,96,144,158,229,282,284,352,374,775,816,821,826,834,839,1019,1392,1397 'secur':2,11,38,39,58,65,74,181,292,923,1209,1220,1242,1250,1376 'security-pattern':1 'select':1300 'self':832,1255,1265,1267,1269,1274,1278 'self-host':831 'sendcommand':1148 'sensit':1014 'servic':1463 'session':259,264,268,276 'session-bas':258 'session/jwt':361 'set':89,1218,1227 'sha256':429 'skill' 'skill-security-patterns' 'skip':56 'some-value-her':786 'sourc':782 'source-cloudai-x' 'spas':364 'sql':1292 'src':1254 'standardhead':1139 'startswith':210 'statement':1359 'status':605 'step':77,82,87,93,98,104,108,113 'store':277,824,1145 'strategi':81 'strict':184,297,921,1152,1240,1419 'strict-transport-secur':920,1239 'string':405,506,527,656,667,669,728,729,732,945,949,951,953,984,988,990,991,1477 'stylesrc':1268 'sub':139,152 'summari':309,1368 'support':1448 'surfac':59 'symbol':764 'symmetr':1458 'tag':952,977,989,1002 'task':64 'time':419,677 'token':126,218,221,227,249,257,347,357,359,380 'tokenvers':154 'topic-agent-skills' 'topic-ai-agents' 'topic-claude-code' 'topic-codex' 'topic-cursor' 'topic-skills' 'topic-workflow' 'tostr':979 'track':72 'transit':102,880,885 'transport':922,1241 'tri':219,1178 'trigger':44 'trim':1327 'true':180,182,291,1047,1076,1086,1140 'trust':1288,1433 'type':323,498,1096,1231 'typeof':501 'typescript':127,196,262,387,466,553,624,724,886,932,1026,1107,1211,1282 'unsaf':1271 'unsafe-inlin':1270 'updat':430,852 'uppercas':762 'uri':328 'url':383,811,814 'usag':540 'use':14,376,856,868,1011,1310,1353,1428,1464 'user':134,135,333,470,472,474,489,494,546,702,708,1289,1302,1434 'user.id':140,153 'user.password':712 'user.role':142 'user.tokenversion':155 'userpermiss':515,524 'utf8':968,1008,1010 'valid':53,115,1281,1307,1436 'validatepassword':726 'valu':780,788,840,850,859,863,872 'variabl':792,1396 'vault':829 'verif':194 'verifi':861 'verifier/code_challenge':370 'verifypassword':665,710 'viewer':491 'vulner':167 'web':125,313 'wildcard':1040 'window':1138 'windowm':1127,1160,1193 'without':878 'workflow':67 'write':473,479,486,578 'wrong':164,416,554,629,692,778,1031,1039,1287 'x':401,900,1229,1235 'x-api-key':400 'x-content-type-opt':1228 'x-forwarded-proto':899 'x-frame-opt':1234 'xss':166 'z':1284 'z.number':1329 'z.object':1315 'z.string':1317,1322 'zod':1286 'zod/joi':1438","prices":[{"id":"d1e3e8f5-7332-4d9e-9585-01be0fab1c4f","listingId":"191b97d8-8ddb-411c-b5ef-2f91e0123189","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"CloudAI-X","category":"claude-workflow-v2","install_from":"skills.sh"},"createdAt":"2026-04-18T21:54:59.570Z"}],"sources":[{"listingId":"191b97d8-8ddb-411c-b5ef-2f91e0123189","source":"github","sourceId":"CloudAI-X/claude-workflow-v2/security-patterns","sourceUrl":"https://github.com/CloudAI-X/claude-workflow-v2/tree/main/skills/security-patterns","isPrimary":false,"firstSeenAt":"2026-04-18T21:54:59.570Z","lastSeenAt":"2026-05-03T00:52:56.205Z"}],"details":{"listingId":"191b97d8-8ddb-411c-b5ef-2f91e0123189","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"CloudAI-X","slug":"security-patterns","github":{"repo":"CloudAI-X/claude-workflow-v2","stars":1352,"topics":["agent-skills","ai","ai-agents","claude-code","codex","cursor","skills","workflow"],"license":"mit","html_url":"https://github.com/CloudAI-X/claude-workflow-v2","pushed_at":"2026-02-14T18:09:29Z","description":"Universal Claude Code workflow plugin with agents, skills, hooks, and commands","skill_md_sha":"9c53583624cf5d6a0eb2dc9ec8bc756a4f1dddbd","skill_md_path":"skills/security-patterns/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/CloudAI-X/claude-workflow-v2/tree/main/skills/security-patterns"},"layout":"multi","source":"github","category":"claude-workflow-v2","frontmatter":{"name":"security-patterns","description":"Implements authentication, authorization, encryption, secrets management, and security hardening patterns. Use when designing auth flows, managing secrets, configuring CORS, implementing rate limiting, or when asked about JWT, OAuth, password hashing, API keys, RBAC, or security best practices."},"skills_sh_url":"https://skills.sh/CloudAI-X/claude-workflow-v2/security-patterns"},"updatedAt":"2026-05-03T00:52:56.205Z"}}