{"id":"85b060e4-5b12-4bd3-9d8b-2e348a381caf","shortId":"hPPP9p","kind":"skill","title":"harden","tagline":"Harden code proactively against vulnerabilities at the boundary where untrusted input enters the system. Use when implementing auth, handling user input, storing or transmitting sensitive data, integrating external APIs, adding file uploads, or any code that crosses a trust bound","description":"# Harden\n\nSecurity as a constraint on every line that touches user data, auth, or external systems. This skill is **proactive**: applied during implementation, not after. For reactive scans, use `safe-repo` (sensitive data) or `deps-audit` (CVEs).\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 webhooks\n- Adding file uploads, payments, or PII handling\n\n## Three-Tier Boundary System\n\n### Always do (no exceptions)\n\n- Validate every external input at the system boundary (route handler, API entry)\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- HTTPS for all external communication\n- Hash passwords with bcrypt/scrypt/argon2 (salt rounds ≥ 12); never plaintext\n- Set security headers: CSP, HSTS, X-Frame-Options, X-Content-Type-Options\n- Session cookies: `httpOnly`, `secure`, `sameSite`\n- Use environment variables for secrets; reference, never inline\n\n### Ask first (human approval required)\n\n- New authentication flow or auth logic changes\n- Storing new categories of sensitive data (PII, payment)\n- New external service integrations\n- CORS configuration changes\n- File upload handlers\n- Rate-limit / throttling changes\n- Granting elevated permissions or roles\n\n### Never do\n\n- Commit secrets to version control\n- Log sensitive data (passwords, tokens, full PAN)\n- Trust client-side validation as a security boundary\n- Disable security headers for convenience\n- Use `eval()` or `innerHTML` with user-provided data\n- Store sessions in client-accessible storage (e.g., localStorage for auth tokens)\n- Expose stack traces or internal errors to users\n\n## OWASP Top 10 — Quick Reference\n\n| Risk | Mitigation |\n|------|------------|\n| Injection (SQL/NoSQL/OS) | Parameterized queries; ORM with bound params; never concat user input |\n| Broken authentication | bcrypt/argon2 hashing; httpOnly+secure+sameSite cookies; rate-limit auth endpoints |\n| XSS | Framework auto-escape; sanitize with DOMPurify if HTML is unavoidable |\n| Broken access control | Check authorization on EVERY endpoint; verify ownership, not just authentication |\n| Misconfiguration | helmet for headers; CORS restricted to known origins via env var |\n| Sensitive data exposure | Strip sensitive fields before API response; env vars for secrets |\n| Insufficient logging | Log auth failures, access denials, input rejections |\n| SSRF | Allowlist outbound destinations; never fetch user-provided URLs without validation |\n\n## Input Validation at Boundaries\n\nAlways validate at the **system boundary** (route handler, message consumer), not in business logic:\n\n- Schema validator (Zod, Joi, Yup, pydantic) defines the contract\n- Reject with 422 + structured error before any business logic touches the data\n- Trust internal code; validate only at the edges\n\n## File Upload Safety\n\n- Allowlist MIME types; deny unknown\n- Enforce max size before processing\n- Check magic bytes if file type is security-critical (don't trust extension or `mimetype`)\n- Store outside webroot or behind authenticated access\n\n## Triaging `npm audit` / CVE findings\n\n```\nCritical or High severity?\n├── Vulnerable code reachable in your app?\n│   ├── YES → Fix immediately\n│   └── NO (dev-only, unused path) → Fix soon, not a blocker\n└── Fix available?\n    ├── YES → Update to patched version\n    └── NO → Workaround / replace dep / allowlist with review date\n```\n\nDefer with documented reason and review date. Never silent allowlist.\n\n## Secrets Management\n\n```\n.env.example   → committed (template, placeholder values)\n.env           → NOT committed (real secrets)\n.env.local     → NOT committed (local overrides)\n*.pem, *.key   → NOT committed\n```\n\nPre-commit check: `git diff --cached | grep -iE \"password|secret|api_key|token\"`. Better: `safe-repo --diff` as part of `ship`.\n\n## Rules\n\n- Validate at boundaries; trust internal code\n- Use framework defaults (helmet, parameterized ORMs, framework escaping)\n- Read secrets from env vars; never inline\n- Authorization checked on every protected endpoint\n- Strip sensitive fields from API responses by default\n- Never trust client-side validation as a security boundary\n\n## Red Flags\n\n- User input flowing directly into SQL string, shell command, or HTML render\n- Secrets in source code or commit history\n- API endpoint without authentication or authorization check\n- CORS wildcard (`*`) or no CORS config\n- No rate limit on auth endpoints\n- Stack traces returned to users\n- Dependencies with known critical CVEs and no plan\n\n## Verification\n\nAfter implementing security-relevant code, confirm:\n\n- [ ] `deps-audit` shows no critical or high CVEs (or each is documented with review date)\n- [ ] `safe-repo --diff` clean\n- [ ] All user input validated at system boundaries\n- [ ] Authorization checked on every protected endpoint (ownership, not just auth)\n- [ ] Security headers present in response (check via DevTools or `curl -I`)\n- [ ] Error responses don't expose internal details\n- [ ] Rate limit active on auth endpoints\n\n## Common Rationalizations\n\n| Rationalization | Reality |\n|-----------------|---------|\n| \"Internal tool, security doesn't matter\" | Internal tools get compromised; attackers target the weakest link |\n| \"We'll add security later\" | Retrofitting is 10x harder than building it in |\n| \"No one would exploit this\" | Automated scanners will; security-by-obscurity is not security |\n| \"Framework handles security\" | Frameworks provide tools, not guarantees |\n| \"It's a prototype\" | Prototypes become production; habits compound |","tags":["harden","agent","skills","helderberto","agent-skills","ai-tools","antigravity","claude-code","cursor","developer-tools","gemini-cli","markdown"],"capabilities":["skill","source-helderberto","skill-harden","topic-agent-skills","topic-ai-tools","topic-antigravity","topic-claude-code","topic-cursor","topic-developer-tools","topic-gemini-cli","topic-markdown","topic-plugin","topic-sdlc","topic-skills","topic-tracer-bullet"],"categories":["agent-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/helderberto/agent-skills/harden","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add helderberto/agent-skills","source_repo":"https://github.com/helderberto/agent-skills","install_from":"skills.sh"}},"qualityScore":"0.454","qualityRationale":"deterministic score 0.45 from registry signals: · indexed on github topic:agent-skills · 8 github stars · SKILL.md body (5,685 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-18T19:09:13.605Z","embedding":null,"createdAt":"2026-05-18T13:14:53.733Z","updatedAt":"2026-05-18T19:09:13.605Z","lastSeenAt":"2026-05-18T19:09:13.605Z","tsv":"'10':297 '10x':774 '12':168 '422':427 'accept':87 'access':280,340,382,480 'activ':744 'ad':31,105 'add':769 'allowlist':387,448,521,534 'alway':117,402 'anyth':85 'api':30,102,131,371,567,611,646 'app':495 'appli':62 'approv':201 'ask':198 'attack':762 'audit':79,483,688 'auth':19,54,207,285,325,380,663,723,746 'authent':91,204,315,351,479,649 'author':93,343,601,651,714 'auto':151,330 'auto-escap':150,329 'autom':785 'avail':511 'bcrypt/argon2':316 'bcrypt/scrypt/argon2':165 'becom':808 'behind':478 'better':570 'blocker':509 'bound':41,308 'boundari':9,115,128,260,401,407,582,624,713 'broken':314,339 'build':84,777 'busi':414,432 'bypass':155 'byte':460 'cach':562 'categori':212 'chang':209,224,232 'check':342,458,559,602,652,715,729 'clean':706 'client':254,279,618 'client-access':278 'client-sid':253,617 'code':3,36,439,491,585,642,684 'command':635 'commit':240,538,544,549,555,558,644 'common':748 'communic':161 'compound':811 'compromis':761 'concat':311 'concaten':138 'config':658 'configur':223 'confirm':685 'constraint':46 'consum':411 'content':182 'contract':424 'control':244,341 'conveni':265 'cooki':186,321 'cor':222,356,653,657 'critic':467,486,673,691 'cross':38 'csp':174 'curl':733 'cve':484 'cves':80,674,694 'data':27,53,75,98,215,247,274,365,436 'databas':135 'date':524,531,701 'default':588,614 'defer':525 'defin':422 'deni':451 'denial':383 'dep':78,520,687 'depend':670 'deps-audit':77,686 'destin':389 'detail':741 'dev':501 'dev-on':500 'devtool':731 'diff':561,574,705 'direct':630 'disabl':261 'document':527,698 'doesn':755 'dompurifi':334 'e.g':282 'edg':444 'elev':234 'encod':143 'endpoint':326,346,606,647,664,719,747 'enforc':453 'enter':13 'entri':132 'env':362,373,542,597 'env.example':537 'env.local':547 'environ':191 'error':292,429,735 'escap':152,331,593 'eval':267 'everi':48,122,345,604,717 'except':120 'exploit':783 'expos':287,739 'exposur':366 'extens':471 'extern':29,56,101,123,160,219 'failur':381 'fetch':391 'field':369,609 'file':32,106,225,445,462 'find':485 'first':199 'fix':497,505,510 'flag':626 'flow':205,629 'frame':178 'framework':149,328,587,592,795,798 'full':250 'get':760 'git':560 'grant':233 'grep':563 'guarante':802 'habit':810 'handl':20,111,796 'handler':130,227,409 'harden':1,2,42 'harder':775 'hash':162,317 'header':173,263,355,725 'helmet':353,589 'high':488,693 'histori':645 'hsts':175 'html':336,637 'httpon':187,318 'https':157 'human':200 'ie':564 'immedi':498 'implement':18,64,90,680 'inject':302 'inlin':197,600 'innerhtml':269 'input':12,22,89,124,140,313,384,398,628,709 'insuffici':377 'integr':28,99,221 'intern':291,438,584,740,752,758 'joi':419 'key':553,568 'known':359,672 'later':771 'limit':230,324,661,743 'line':49 'link':766 'll':768 'local':550 'localstorag':283 'log':245,378,379 'logic':208,415,433 'magic':459 'manag':536 'matter':757 'max':454 'messag':410 'mime':449 'mimetyp':473 'misconfigur':352 'mitig':301 'never':137,169,196,238,310,390,532,599,615 'new':203,211,218 'npm':482 'obscur':791 'one':781 'option':179,184 'origin':360 'orm':306,591 'outbound':388 'output':144 'outsid':475 'overrid':551 'owasp':295 'ownership':348,720 'pan':251 'param':309 'parameter':133,304,590 'part':576 'password':163,248,565 'patch':515 'path':504 'payment':108,217 'pem':552 'permiss':235 'pii':110,216 'placehold':540 'plaintext':170 'plan':677 'pre':557 'pre-commit':556 'present':726 'prevent':146 'proactiv':4,61 'process':457 'product':809 'protect':605,718 'prototyp':806,807 'provid':273,394,799 'pydant':421 'queri':136,305 'quick':298 'rate':229,323,660,742 'rate-limit':228,322 'ration':749,750 'reachabl':492 'reactiv':68 'read':594 'real':545 'realiti':751 'reason':528 'red':625 'refer':195,299 'reject':385,425 'relev':683 'render':638 'replac':519 'repo':73,573,704 'requir':202 'respons':372,612,728,736 'restrict':357 'retrofit':772 'return':667 'review':523,530,700 'risk':300 'role':237 'round':167 'rout':129,408 'rule':579 'safe':72,572,703 'safe-repo':71,571,702 'safeti':447 'salt':166 'samesit':189,320 'sanit':332 'scan':69 'scanner':786 'schema':416 'secret':194,241,376,535,546,566,595,639 'secur':43,172,188,259,262,319,466,623,682,724,754,770,789,794,797 'security-by-obscur':788 'security-crit':465 'security-relev':681 'sensit':26,74,97,214,246,364,368,608 'servic':220 'session':185,276 'set':171 'sever':489 'shell':634 'ship':578 'show':689 'side':255,619 'silent':533 'size':455 'skill':59 'skill-harden' 'soon':506 'sourc':641 'source-helderberto' 'sql':142,632 'sql/nosql/os':303 'ssrf':386 'stack':288,665 'storag':281 'store':23,94,210,275,474 'string':633 'strip':367,607 'structur':428 'system':15,57,116,127,406,712 'target':763 'templat':539 'three':113 'three-tier':112 'throttl':231 'tier':114 'token':249,286,569 'tool':753,759,800 'top':296 'topic-agent-skills' 'topic-ai-tools' 'topic-antigravity' 'topic-claude-code' 'topic-cursor' 'topic-developer-tools' 'topic-gemini-cli' 'topic-markdown' 'topic-plugin' 'topic-sdlc' 'topic-skills' 'topic-tracer-bullet' 'touch':51,434 'trace':289,666 'transmit':25,96 'triag':481 'trust':40,252,437,470,583,616 'type':183,450,463 'unavoid':338 'unknown':452 'untrust':11 'unus':503 'updat':513 'upload':33,107,226,446 'url':395 'use':16,70,83,148,190,266,586 'user':21,52,88,139,272,294,312,393,627,669,708 'user-provid':271,392 'valid':121,256,397,399,403,417,440,580,620,710 'valu':541 'var':363,374,598 'variabl':192 'verif':678 'verifi':347 'version':243,516 'via':361,730 'vulner':6,490 'weakest':765 'webhook':104 'webroot':476 'wildcard':654 'without':396,648 'workaround':518 'would':782 'x':177,181 'x-content-type-opt':180 'x-frame-opt':176 'xss':147,327 'yes':496,512 'yup':420 'zod':418","prices":[{"id":"37d094b8-fe09-4cf9-8d91-64af05b11006","listingId":"85b060e4-5b12-4bd3-9d8b-2e348a381caf","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"helderberto","category":"agent-skills","install_from":"skills.sh"},"createdAt":"2026-05-18T13:14:53.733Z"}],"sources":[{"listingId":"85b060e4-5b12-4bd3-9d8b-2e348a381caf","source":"github","sourceId":"helderberto/agent-skills/harden","sourceUrl":"https://github.com/helderberto/agent-skills/tree/main/skills/harden","isPrimary":false,"firstSeenAt":"2026-05-18T13:14:53.733Z","lastSeenAt":"2026-05-18T19:09:13.605Z"}],"details":{"listingId":"85b060e4-5b12-4bd3-9d8b-2e348a381caf","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"helderberto","slug":"harden","github":{"repo":"helderberto/agent-skills","stars":8,"topics":["agent-skills","ai","ai-tools","antigravity","claude-code","cursor","developer-tools","gemini-cli","markdown","plugin","sdlc","skills","tracer-bullet"],"license":"mit","html_url":"https://github.com/helderberto/agent-skills","pushed_at":"2026-05-14T11:37:47Z","description":"My personal SDLC toolbelt for AI coding agents — PRD to ship.","skill_md_sha":"69068e9dd95ab8888157db2f734977dbdabb2ecf","skill_md_path":"skills/harden/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/helderberto/agent-skills/tree/main/skills/harden"},"layout":"multi","source":"github","category":"agent-skills","frontmatter":{"name":"harden","description":"Harden code proactively against vulnerabilities at the boundary where untrusted input enters the system. Use when implementing auth, handling user input, storing or transmitting sensitive data, integrating external APIs, adding file uploads, or any code that crosses a trust boundary. Don't use for reactive secret scanning (use `safe-repo`) or dependency CVE checks (use `deps-audit`)."},"skills_sh_url":"https://skills.sh/helderberto/agent-skills/harden"},"updatedAt":"2026-05-18T19:09:13.605Z"}}