{"id":"9bd188b6-6c05-4a1e-94e4-576ad5e2efcc","shortId":"eHVqJM","kind":"skill","title":"Security And Hardening","tagline":"Agent Skills skill by Addyosmani","description":"# Security and Hardening\n\n## Overview\n\nSecurity-first development practices for web applications. Treat every external input as hostile, every secret as sacred, and every authorization check as mandatory. Security isn't a phase — it's a constraint on every line of code that touches user data, authentication, or external systems.\n\n## When to Use\n\n- Building anything that accepts user input\n- Implementing authentication or authorization\n- Storing or transmitting sensitive data\n- Integrating with external APIs or services\n- Adding file uploads, webhooks, or callbacks\n- Handling payment or PII data\n\n## The Three-Tier Boundary System\n\n### Always Do (No Exceptions)\n\n- **Validate all external input** at the system boundary (API routes, form handlers)\n- **Parameterize all database queries** — never concatenate user input into SQL\n- **Encode output** to prevent XSS (use framework auto-escaping, don't bypass it)\n- **Use HTTPS** for all external communication\n- **Hash passwords** with bcrypt/scrypt/argon2 (never store plaintext)\n- **Set security headers** (CSP, HSTS, X-Frame-Options, X-Content-Type-Options)\n- **Use httpOnly, secure, sameSite cookies** for sessions\n- **Run `npm audit`** (or equivalent) before every release\n\n### Ask First (Requires Human Approval)\n\n- Adding new authentication flows or changing auth logic\n- Storing new categories of sensitive data (PII, payment info)\n- Adding new external service integrations\n- Changing CORS configuration\n- Adding file upload handlers\n- Modifying rate limiting or throttling\n- Granting elevated permissions or roles\n\n### Never Do\n\n- **Never commit secrets** to version control (API keys, passwords, tokens)\n- **Never log sensitive data** (passwords, tokens, full credit card numbers)\n- **Never trust client-side validation** as a security boundary\n- **Never disable security headers** for convenience\n- **Never use `eval()` or `innerHTML`** with user-provided data\n- **Never store sessions in client-accessible storage** (localStorage for auth tokens)\n- **Never expose stack traces** or internal error details to users\n\n## OWASP Top 10 Prevention\n\n### 1. Injection (SQL, NoSQL, OS Command)\n\n```typescript\n// BAD: SQL injection via string concatenation\nconst query = `SELECT * FROM users WHERE id = '${userId}'`;\n\n// GOOD: Parameterized query\nconst user = await db.query('SELECT * FROM users WHERE id = $1', [userId]);\n\n// GOOD: ORM with parameterized input\nconst user = await prisma.user.findUnique({ where: { id: userId } });\n```\n\n### 2. Broken Authentication\n\n```typescript\n// Password hashing\nimport { hash, compare } from 'bcrypt';\n\nconst SALT_ROUNDS = 12;\nconst hashedPassword = await hash(plaintext, SALT_ROUNDS);\nconst isValid = await compare(plaintext, hashedPassword);\n\n// Session management\napp.use(session({\n  secret: process.env.SESSION_SECRET,  // From environment, not code\n  resave: false,\n  saveUninitialized: false,\n  cookie: {\n    httpOnly: true,     // Not accessible via JavaScript\n    secure: true,       // HTTPS only\n    sameSite: 'lax',    // CSRF protection\n    maxAge: 24 * 60 * 60 * 1000,  // 24 hours\n  },\n}));\n```\n\n### 3. Cross-Site Scripting (XSS)\n\n```typescript\n// BAD: Rendering user input as HTML\nelement.innerHTML = userInput;\n\n// GOOD: Use framework auto-escaping (React does this by default)\nreturn <div>{userInput}</div>;\n\n// If you MUST render HTML, sanitize first\nimport DOMPurify from 'dompurify';\nconst clean = DOMPurify.sanitize(userInput);\n```\n\n### 4. Broken Access Control\n\n```typescript\n// Always check authorization, not just authentication\napp.patch('/api/tasks/:id', authenticate, async (req, res) => {\n  const task = await taskService.findById(req.params.id);\n\n  // Check that the authenticated user owns this resource\n  if (task.ownerId !== req.user.id) {\n    return res.status(403).json({\n      error: { code: 'FORBIDDEN', message: 'Not authorized to modify this task' }\n    });\n  }\n\n  // Proceed with update\n  const updated = await taskService.update(req.params.id, req.body);\n  return res.json(updated);\n});\n```\n\n### 5. Security Misconfiguration\n\n```typescript\n// Security headers (use helmet for Express)\nimport helmet from 'helmet';\napp.use(helmet());\n\n// Content Security Policy\napp.use(helmet.contentSecurityPolicy({\n  directives: {\n    defaultSrc: [\"'self'\"],\n    scriptSrc: [\"'self'\"],\n    styleSrc: [\"'self'\", \"'unsafe-inline'\"],  // Tighten if possible\n    imgSrc: [\"'self'\", 'data:', 'https:'],\n    connectSrc: [\"'self'\"],\n  },\n}));\n\n// CORS — restrict to known origins\napp.use(cors({\n  origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',\n  credentials: true,\n}));\n```\n\n### 6. Sensitive Data Exposure\n\n```typescript\n// Never return sensitive fields in API responses\nfunction sanitizeUser(user: UserRecord): PublicUser {\n  const { passwordHash, resetToken, ...publicFields } = user;\n  return publicFields;\n}\n\n// Use environment variables for secrets\nconst API_KEY = process.env.STRIPE_API_KEY;\nif (!API_KEY) throw new Error('STRIPE_API_KEY not configured');\n```\n\n## Input Validation Patterns\n\n### Schema Validation at Boundaries\n\n```typescript\nimport { z } from 'zod';\n\nconst CreateTaskSchema = z.object({\n  title: z.string().min(1).max(200).trim(),\n  description: z.string().max(2000).optional(),\n  priority: z.enum(['low', 'medium', 'high']).default('medium'),\n  dueDate: z.string().datetime().optional(),\n});\n\n// Validate at the route handler\napp.post('/api/tasks', async (req, res) => {\n  const result = CreateTaskSchema.safeParse(req.body);\n  if (!result.success) {\n    return res.status(422).json({\n      error: {\n        code: 'VALIDATION_ERROR',\n        message: 'Invalid input',\n        details: result.error.flatten(),\n      },\n    });\n  }\n  // result.data is now typed and validated\n  const task = await taskService.create(result.data);\n  return res.status(201).json(task);\n});\n```\n\n### File Upload Safety\n\n```typescript\n// Restrict file types and sizes\nconst ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];\nconst MAX_SIZE = 5 * 1024 * 1024; // 5MB\n\nfunction validateUpload(file: UploadedFile) {\n  if (!ALLOWED_TYPES.includes(file.mimetype)) {\n    throw new ValidationError('File type not allowed');\n  }\n  if (file.size > MAX_SIZE) {\n    throw new ValidationError('File too large (max 5MB)');\n  }\n  // Don't trust the file extension — check magic bytes if critical\n}\n```\n\n## Triaging npm audit Results\n\nNot all audit findings require immediate action. Use this decision tree:\n\n```\nnpm audit reports a vulnerability\n├── Severity: critical or high\n│   ├── Is the vulnerable code reachable in your app?\n│   │   ├── YES --> Fix immediately (update, patch, or replace the dependency)\n│   │   └── NO (dev-only dep, unused code path) --> Fix soon, but not a blocker\n│   └── Is a fix available?\n│       ├── YES --> Update to the patched version\n│       └── NO --> Check for workarounds, consider replacing the dependency, or add to allowlist with a review date\n├── Severity: moderate\n│   ├── Reachable in production? --> Fix in the next release cycle\n│   └── Dev-only? --> Fix when convenient, track in backlog\n└── Severity: low\n    └── Track and fix during regular dependency updates\n```\n\n**Key questions:**\n- Is the vulnerable function actually called in your code path?\n- Is the dependency a runtime dependency or dev-only?\n- Is the vulnerability exploitable given your deployment context (e.g., a server-side vulnerability in a client-only app)?\n\nWhen you defer a fix, document the reason and set a review date.\n\n## Rate Limiting\n\n```typescript\nimport rateLimit from 'express-rate-limit';\n\n// General API rate limit\napp.use('/api/', rateLimit({\n  windowMs: 15 * 60 * 1000, // 15 minutes\n  max: 100,                   // 100 requests per window\n  standardHeaders: true,\n  legacyHeaders: false,\n}));\n\n// Stricter limit for auth endpoints\napp.use('/api/auth/', rateLimit({\n  windowMs: 15 * 60 * 1000,\n  max: 10,  // 10 attempts per 15 minutes\n}));\n```\n\n## Secrets Management\n\n```\n.env files:\n  ├── .env.example  → Committed (template with placeholder values)\n  ├── .env          → NOT committed (contains real secrets)\n  └── .env.local    → NOT committed (local overrides)\n\n.gitignore must include:\n  .env\n  .env.local\n  .env.*.local\n  *.pem\n  *.key\n```\n\n**Always check before committing:**\n```bash\n# Check for accidentally staged secrets\ngit diff --cached | grep -i \"password\\|secret\\|api_key\\|token\"\n```\n\n## Security Review Checklist\n\n```markdown\n### Authentication\n- [ ] Passwords hashed with bcrypt/scrypt/argon2 (salt rounds ≥ 12)\n- [ ] Session tokens are httpOnly, secure, sameSite\n- [ ] Login has rate limiting\n- [ ] Password reset tokens expire\n\n### Authorization\n- [ ] Every endpoint checks user permissions\n- [ ] Users can only access their own resources\n- [ ] Admin actions require admin role verification\n\n### Input\n- [ ] All user input validated at the boundary\n- [ ] SQL queries are parameterized\n- [ ] HTML output is encoded/escaped\n\n### Data\n- [ ] No secrets in code or version control\n- [ ] Sensitive fields excluded from API responses\n- [ ] PII encrypted at rest (if applicable)\n\n### Infrastructure\n- [ ] Security headers configured (CSP, HSTS, etc.)\n- [ ] CORS restricted to known origins\n- [ ] Dependencies audited for vulnerabilities\n- [ ] Error messages don't expose internals\n```\n## See Also\n\nFor detailed security checklists and pre-commit verification steps, see `references/security-checklist.md`.\n\n## Common Rationalizations\n\n| Rationalization | Reality |\n|---|---|\n| \"This is an internal tool, security doesn't matter\" | Internal tools get compromised. Attackers target the weakest link. |\n| \"We'll add security later\" | Security retrofitting is 10x harder than building it in. Add it now. |\n| \"No one would try to exploit this\" | Automated scanners will find it. Security by obscurity is not security. |\n| \"The framework handles security\" | Frameworks provide tools, not guarantees. You still need to use them correctly. |\n| \"It's just a prototype\" | Prototypes become production. Security habits from day one. |\n\n## Red Flags\n\n- User input passed directly to database queries, shell commands, or HTML rendering\n- Secrets in source code or commit history\n- API endpoints without authentication or authorization checks\n- Missing CORS configuration or wildcard (`*`) origins\n- No rate limiting on authentication endpoints\n- Stack traces or internal errors exposed to users\n- Dependencies with known critical vulnerabilities\n\n## Verification\n\nAfter implementing security-relevant code:\n\n- [ ] `npm audit` shows no critical or high vulnerabilities\n- [ ] No secrets in source code or git history\n- [ ] All user input validated at system boundaries\n- [ ] Authentication and authorization checked on every protected endpoint\n- [ ] Security headers present in response (check with browser DevTools)\n- [ ] Error responses don't expose internal details\n- [ ] Rate limiting active on auth endpoints","tags":["security","and","hardening","agent","skills","addyosmani"],"capabilities":["skill","source-addyosmani","category-agent-skills"],"categories":["agent-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/addyosmani/agent-skills/security-and-hardening","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"install_from":"skills.sh"}},"qualityScore":"0.300","qualityRationale":"deterministic score 0.30 from registry signals: · indexed on skills.sh · published under addyosmani/agent-skills","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:v1","enrichmentVersion":1,"enrichedAt":"2026-04-22T11:40:30.629Z","embedding":null,"createdAt":"2026-04-18T20:31:40.210Z","updatedAt":"2026-04-22T11:40:30.629Z","lastSeenAt":"2026-04-22T11:40:30.629Z","tsv":"'/api':938 '/api/auth':962 '/api/tasks':467,660 '1':300,333,634 '10':298,969,970 '100':947,948 '1000':409,943,967 '1024':718,719 '10x':1172 '12':361,1036 '15':941,944,965,973 '2':347 '200':636 '2000':641 '201':696 '24':406,410 '3':412 '3000':567 '4':455 '403':491 '422':672 '5':515,717 '5mb':720,746 '6':570 '60':407,408,942,966 'accept':65 'access':280,394,457,1060 'accident':1012 'action':768,1065 'activ':1337 'actual':874 'ad':83,187,204,212 'add':832,1166,1178 'addyosmani':8 'admin':1064,1067 'agent':4 'allow':709,734 'allowed_types.includes':726 'allowlist':834 'also':1129 'alway':100,460,1005 'anyth':63 'api':80,112,234,580,600,603,606,612,934,1022,1098,1249 'app':789,909 'app.patch':466 'app.post':659 'app.use':377,529,534,560,937,961 'applic':20,1105 'approv':186 'ask':182 'async':470,661 'attack':1159 'attempt':971 'audit':176,760,764,774,1119,1289 'auth':193,284,959,1339 'authent':55,69,189,349,465,469,481,1029,1252,1266,1311 'author':33,71,462,498,1051,1254,1313 'auto':134,431 'auto-escap':133,430 'autom':1188 'avail':816 'await':326,342,364,371,475,508,691 'backlog':858 'bad':307,419 'bash':1009 'bcrypt':357 'bcrypt/scrypt/argon2':149,1033 'becom':1221 'blocker':812 'boundari':98,111,257,622,1077,1310 'broken':348,456 'browser':1326 'build':62,1175 'bypass':138 'byte':755 'cach':1017 'call':875 'callback':88 'card':246 'categori':197 'category-agent-skills' 'chang':192,209 'check':34,461,478,753,824,1006,1010,1054,1255,1314,1324 'checklist':1027,1133 'clean':452 'client':251,279,907 'client-access':278 'client-on':906 'client-sid':250 'code':50,385,494,675,785,805,878,1090,1245,1287,1300 'command':305,1238 'commit':229,980,987,993,1008,1137,1247 'common':1142 'communic':145 'compar':355,372 'compromis':1158 'concaten':121,312 'configur':211,615,1109,1258 'connectsrc':553 'consid':827 'const':313,324,340,358,362,369,451,473,506,587,599,628,664,689,708,714 'constraint':45 'contain':988 'content':164,531 'context':897 'control':233,458,1093 'conveni':263,855 'cooki':171,390 'cor':210,555,561,1113,1257 'correct':1214 'createtaskschema':629 'createtaskschema.safeparse':666 'credenti':568 'credit':245 'critic':757,779,1279,1292 'cross':414 'cross-sit':413 'csp':156,1110 'csrf':403 'cycl':849 'data':54,76,93,200,241,273,551,572,1086 'databas':118,1235 'date':838,922 'datetim':652 'day':1226 'db.query':327 'decis':771 'default':437,648 'defaultsrc':537 'defer':912 'dep':803 'depend':798,830,866,882,885,1118,1276 'deploy':896 'descript':638 'detail':293,681,1131,1334 'dev':801,851,888 'dev-on':800,850,887 'develop':16 'devtool':1327 'diff':1016 'direct':536,1233 'disabl':259 'document':915 'doesn':1152 'dompurifi':448,450 'dompurify.sanitize':453 'duedat':650 'e.g':898 'element.innerhtml':425 'elev':222 'encod':126 'encoded/escaped':1085 'encrypt':1101 'endpoint':960,1053,1250,1267,1318,1340 'env':977,985,999,1001 'env.example':979 'env.local':991,1000 'environ':383,595 'equival':178 'error':292,493,610,674,677,1122,1272,1328 'escap':135,432 'etc':1112 'eval':266 'everi':22,27,32,47,180,1052,1316 'except':103 'exclud':1096 'expir':1050 'exploit':893,1186 'expos':287,1126,1273,1332 'exposur':573 'express':524,930 'express-rate-limit':929 'extens':752 'extern':23,57,79,106,144,206 'fals':387,389,955 'field':578,1095 'file':84,213,699,704,723,731,742,751,978 'file.mimetype':727 'file.size':736 'find':765,1191 'first':15,183,446 'fix':791,807,815,844,853,863,914 'flag':1229 'flow':190 'forbidden':495 'form':114 'frame':160 'framework':132,429,1200,1203 'full':244 'function':582,721,873 'general':933 'get':1157 'git':1015,1302 'gitignor':996 'given':894 'good':321,335,427 'grant':221 'grep':1018 'guarante':1207 'habit':1224 'handl':89,1201 'handler':115,215,658 'harden':3,11 'harder':1173 'hash':146,352,354,365,1031 'hashedpassword':363,374 'header':155,261,520,1108,1320 'helmet':522,526,528,530 'helmet.contentsecuritypolicy':535 'high':647,781,1294 'histori':1248,1303 'hostil':26 'hour':411 'hsts':157,1111 'html':424,444,1082,1240 'httpon':168,391,1040 'https':141,399,552 'human':185 'id':319,332,345,468 'image/jpeg':711 'image/png':712 'image/webp':713 'imgsrc':549 'immedi':767,792 'implement':68,1283 'import':353,447,525,624,926 'includ':998 'info':203 'infrastructur':1106 'inject':301,309 'inlin':545 'innerhtml':268 'input':24,67,107,123,339,422,616,680,1070,1073,1231,1306 'integr':77,208 'intern':291,1127,1149,1155,1271,1333 'invalid':679 'isn':38 'isvalid':370 'javascript':396 'json':492,673,697 'key':235,601,604,607,613,868,1004,1023 'known':558,1116,1278 'larg':744 'later':1168 'lax':402 'legacyhead':954 'limit':218,924,932,936,957,1046,1264,1336 'line':48 'link':1163 'll':1165 'local':994,1002 'localhost':566 'localstorag':282 'log':239 'logic':194 'login':1043 'low':645,860 'magic':754 'manag':376,976 'mandatori':36 'markdown':1028 'matter':1154 'max':635,640,715,737,745,946,968 'maxag':405 'medium':646,649 'messag':496,678,1123 'min':633 'minut':945,974 'misconfigur':517 'miss':1256 'moder':840 'modifi':216,500 'must':442,997 'need':1210 'never':120,150,226,228,238,248,258,264,274,286,575 'new':188,196,205,609,729,740 'next':847 'nosql':303 'npm':175,759,773,1288 'number':247 'obscur':1195 'one':1182,1227 'option':161,166,642,653 'origin':559,562,564,1117,1261 'orm':336 'os':304 'output':127,1083 'overrid':995 'overview':12 'owasp':296 'own':483 'parameter':116,322,338,1081 'pass':1232 'password':147,236,242,351,1020,1030,1047 'passwordhash':588 'patch':794,821 'path':806,879 'pattern':618 'payment':90,202 'pem':1003 'per':950,972 'permiss':223,1056 'phase':41 'pii':92,201,1100 'placehold':983 'plaintext':152,366,373 'polici':533 'possibl':548 'practic':17 'pre':1136 'pre-commit':1135 'present':1321 'prevent':129,299 'prioriti':643 'prisma.user.findunique':343 'proceed':503 'process.env.allowed':563 'process.env.session':380 'process.env.stripe':602 'product':843,1222 'protect':404,1317 'prototyp':1219,1220 'provid':272,1204 'publicfield':590,593 'publicus':586 'queri':119,314,323,1079,1236 'question':869 'rate':217,923,931,935,1045,1263,1335 'ratelimit':927,939,963 'ration':1143,1144 'reachabl':786,841 'react':433 'real':989 'realiti':1145 'reason':917 'red':1228 'references/security-checklist.md':1141 'regular':865 'releas':181,848 'relev':1286 'render':420,443,1241 'replac':796,828 'report':775 'req':471,662 'req.body':511,667 'req.params.id':477,510 'req.user.id':488 'request':949 'requir':184,766,1066 'res':472,663 'res.json':513 'res.status':490,671,695 'resav':386 'reset':1048 'resettoken':589 'resourc':485,1063 'respons':581,1099,1323,1329 'rest':1103 'restrict':556,703,1114 'result':665,761 'result.data':683,693 'result.error.flatten':682 'result.success':669 'retrofit':1170 'return':438,489,512,576,592,670,694 'review':837,921,1026 'role':225,1068 'round':360,368,1035 'rout':113,657 'run':174 'runtim':884 'sacr':30 'safeti':701 'salt':359,367,1034 'samesit':170,401,1042 'sanit':445 'sanitizeus':583 'saveuniniti':388 'scanner':1189 'schema':619 'script':416 'scriptsrc':539 'secret':28,230,379,381,598,975,990,1014,1021,1088,1242,1297 'secur':1,9,14,37,154,169,256,260,397,516,519,532,1025,1041,1107,1132,1151,1167,1169,1193,1198,1202,1223,1285,1319 'security-first':13 'security-relev':1284 'see':1128,1140 'select':315,328 'self':538,540,542,550,554 'sensit':75,199,240,571,577,1094 'server':901 'server-sid':900 'servic':82,207 'session':173,276,375,378,1037 'set':153,919 'sever':778,839,859 'shell':1237 'show':1290 'side':252,902 'site':415 'size':707,716,738 'skill':5,6 'soon':808 'sourc':1244,1299 'source-addyosmani' 'split':565 'sql':125,302,308,1078 'stack':288,1268 'stage':1013 'standardhead':952 'step':1139 'still':1209 'storag':281 'store':72,151,195,275 'stricter':956 'string':311 'stripe':611 'stylesrc':541 'system':58,99,110,1309 'target':1160 'task':474,502,690,698 'task.ownerid':487 'taskservice.create':692 'taskservice.findbyid':476 'taskservice.update':509 'templat':981 'three':96 'three-tier':95 'throttl':220 'throw':608,728,739 'tier':97 'tighten':546 'titl':631 'token':237,243,285,1024,1038,1049 'tool':1150,1156,1205 'top':297 'touch':52 'trace':289,1269 'track':856,861 'transmit':74 'treat':21 'tree':772 'tri':1184 'triag':758 'trim':637 'true':392,398,569,953 'trust':249,749 'type':165,686,705,710,732 'typescript':306,350,418,459,518,574,623,702,925 'unsaf':544 'unsafe-inlin':543 'unus':804 'updat':505,507,514,793,818,867 'upload':85,214,700 'uploadedfil':724 'use':61,131,140,167,265,428,521,594,769,1212 'user':53,66,122,271,295,317,325,330,341,421,482,584,591,1055,1057,1072,1230,1275,1305 'user-provid':270 'userid':320,334,346 'userinput':426,439,454 'userrecord':585 'valid':104,253,617,620,654,676,688,1074,1307 'validateupload':722 'validationerror':730,741 'valu':984 'variabl':596 'verif':1069,1138,1281 'version':232,822,1092 'via':310,395 'vulner':777,784,872,892,903,1121,1280,1295 'weakest':1162 'web':19 'webhook':86 'wildcard':1260 'window':951 'windowm':940,964 'without':1251 'workaround':826 'would':1183 'x':159,163 'x-content-type-opt':162 'x-frame-opt':158 'xss':130,417 'yes':790,817 'z':625 'z.enum':644 'z.object':630 'z.string':632,639,651 'zod':627","prices":[{"id":"f88c8c58-732e-44ca-bce1-37c5a4341ac4","listingId":"9bd188b6-6c05-4a1e-94e4-576ad5e2efcc","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"addyosmani","category":"agent-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T20:31:40.210Z"}],"sources":[{"listingId":"9bd188b6-6c05-4a1e-94e4-576ad5e2efcc","source":"github","sourceId":"addyosmani/agent-skills/security-and-hardening","sourceUrl":"https://github.com/addyosmani/agent-skills/tree/main/skills/security-and-hardening","isPrimary":false,"firstSeenAt":"2026-04-18T21:53:03.865Z","lastSeenAt":"2026-04-22T06:52:42.623Z"},{"listingId":"9bd188b6-6c05-4a1e-94e4-576ad5e2efcc","source":"skills_sh","sourceId":"addyosmani/agent-skills/security-and-hardening","sourceUrl":"https://skills.sh/addyosmani/agent-skills/security-and-hardening","isPrimary":true,"firstSeenAt":"2026-04-18T20:31:40.210Z","lastSeenAt":"2026-04-22T11:40:30.629Z"}],"details":{"listingId":"9bd188b6-6c05-4a1e-94e4-576ad5e2efcc","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"addyosmani","slug":"security-and-hardening","source":"skills_sh","category":"agent-skills","skills_sh_url":"https://skills.sh/addyosmani/agent-skills/security-and-hardening"},"updatedAt":"2026-04-22T11:40:30.629Z"}}