From 6fcffca9b0c29a1a8ec23966f23b1008b21336ce Mon Sep 17 00:00:00 2001 From: Zhongwei Li Date: Sat, 29 Nov 2025 18:22:35 +0800 Subject: [PATCH] Initial commit --- .claude-plugin/plugin.json | 18 + README.md | 3 + commands/ts.md | 49 ++ hooks/hooks.json | 44 ++ hooks/scripts/check-security.sh | 89 +++ hooks/scripts/check-syntax.sh | 46 ++ plugin.lock.json | 169 ++++ skills/auditing-dependencies/SKILL.md | 405 ++++++++++ .../SKILL.md | 284 +++++++ skills/avoiding-any-types/SKILL.md | 341 ++++++++ skills/avoiding-non-null-assertions/SKILL.md | 246 ++++++ skills/diagnosing-type-errors/SKILL.md | 177 +++++ skills/hashing-passwords/SKILL.md | 294 +++++++ .../references/correct-implementations.md | 195 +++++ .../references/emergency-response.md | 52 ++ .../references/never-do-this.md | 87 +++ .../references/password-validation.md | 66 ++ skills/refactoring-inline-types/SKILL.md | 391 ++++++++++ skills/resolving-type-errors/SKILL.md | 285 +++++++ skills/reviewing-type-safety/SKILL.md | 266 +++++++ skills/sanitizing-user-inputs/SKILL.md | 611 +++++++++++++++ skills/using-generics/SKILL.md | 440 +++++++++++ .../references/advanced-patterns.md | 168 ++++ .../references/common-patterns.md | 332 ++++++++ .../references/detailed-examples.md | 305 ++++++++ skills/using-runtime-checks/SKILL.md | 366 +++++++++ .../references/error-handling.md | 175 +++++ .../references/performance.md | 211 +++++ .../references/zod-patterns.md | 241 ++++++ skills/using-type-guards/SKILL.md | 383 +++++++++ .../references/advanced-patterns.md | 97 +++ .../references/nested-validation.md | 110 +++ .../references/testing-guide.md | 97 +++ skills/validating-external-data/SKILL.md | 736 ++++++++++++++++++ skills/validating-type-assertions/SKILL.md | 456 +++++++++++ 35 files changed, 8235 insertions(+) create mode 100644 .claude-plugin/plugin.json create mode 100644 README.md create mode 100644 commands/ts.md create mode 100644 hooks/hooks.json create mode 100755 hooks/scripts/check-security.sh create mode 100755 hooks/scripts/check-syntax.sh create mode 100644 plugin.lock.json create mode 100644 skills/auditing-dependencies/SKILL.md create mode 100644 skills/avoiding-angle-bracket-assertions/SKILL.md create mode 100644 skills/avoiding-any-types/SKILL.md create mode 100644 skills/avoiding-non-null-assertions/SKILL.md create mode 100644 skills/diagnosing-type-errors/SKILL.md create mode 100644 skills/hashing-passwords/SKILL.md create mode 100644 skills/hashing-passwords/references/correct-implementations.md create mode 100644 skills/hashing-passwords/references/emergency-response.md create mode 100644 skills/hashing-passwords/references/never-do-this.md create mode 100644 skills/hashing-passwords/references/password-validation.md create mode 100644 skills/refactoring-inline-types/SKILL.md create mode 100644 skills/resolving-type-errors/SKILL.md create mode 100644 skills/reviewing-type-safety/SKILL.md create mode 100644 skills/sanitizing-user-inputs/SKILL.md create mode 100644 skills/using-generics/SKILL.md create mode 100644 skills/using-generics/references/advanced-patterns.md create mode 100644 skills/using-generics/references/common-patterns.md create mode 100644 skills/using-generics/references/detailed-examples.md create mode 100644 skills/using-runtime-checks/SKILL.md create mode 100644 skills/using-runtime-checks/references/error-handling.md create mode 100644 skills/using-runtime-checks/references/performance.md create mode 100644 skills/using-runtime-checks/references/zod-patterns.md create mode 100644 skills/using-type-guards/SKILL.md create mode 100644 skills/using-type-guards/references/advanced-patterns.md create mode 100644 skills/using-type-guards/references/nested-validation.md create mode 100644 skills/using-type-guards/references/testing-guide.md create mode 100644 skills/validating-external-data/SKILL.md create mode 100644 skills/validating-type-assertions/SKILL.md diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..7706b02 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,18 @@ +{ + "name": "typescript", + "description": "TypeScript 5.9 type safety, compiler configuration, and best practices based on real-world AI coding failures. Provides intelligent skill recommendations, type safety validation, and prevents common TypeScript anti-patterns.", + "version": "1.0.0", + "author": { + "name": "Claude Code Plugin Marketplace", + "email": "plugins@anthropic.com" + }, + "skills": [ + "./skills" + ], + "commands": [ + "./commands" + ], + "hooks": [ + "./hooks" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..517a2b1 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# typescript + +TypeScript 5.9 type safety, compiler configuration, and best practices based on real-world AI coding failures. Provides intelligent skill recommendations, type safety validation, and prevents common TypeScript anti-patterns. diff --git a/commands/ts.md b/commands/ts.md new file mode 100644 index 0000000..39b660b --- /dev/null +++ b/commands/ts.md @@ -0,0 +1,49 @@ +--- +description: TypeScript operations - check types, fix errors, or extract/refactor types +argument-hint: (check|fix|extract) +allowed-tools: Skill +--- + +Execute TypeScript operations using specialized skills. + +Operation: $1 +Target file: $2 + +Based on the operation requested, use the appropriate skill: + +## check + +Analyze TypeScript errors and provide detailed diagnostics for **$2**. + +Use the TypeScript Type Checking skill: + +@typescript/TYPES-check + +## fix + +Resolve all TypeScript errors in **$2**. + +Use the TypeScript Error Resolution skill: + +@typescript/TYPES-fix + +## extract + +Refactor inline types into reusable type definitions for **$2**. + +Use the TypeScript Type Extraction skill: + +@typescript/TYPES-extract + +## Invalid Operation + +If `$1` is not one of (check|fix|extract), show usage: + +``` +Usage: /ts (check|fix|extract) + +Operations: + check - Analyze TypeScript errors with detailed diagnostics + fix - Resolve all TypeScript errors with root cause analysis + extract - Refactor inline types into reusable modules +``` diff --git a/hooks/hooks.json b/hooks/hooks.json new file mode 100644 index 0000000..b9d2d45 --- /dev/null +++ b/hooks/hooks.json @@ -0,0 +1,44 @@ +{ + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/scripts/check-typescript-version.sh", + "timeout": 3000 + } + ] + } + ], + "PreToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/check-syntax.sh", + "timeout": 500 + }, + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/check-security.sh", + "timeout": 200 + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate-typescript.sh", + "timeout": 5000 + } + ] + } + ] + } +} diff --git a/hooks/scripts/check-security.sh b/hooks/scripts/check-security.sh new file mode 100755 index 0000000..2248ea9 --- /dev/null +++ b/hooks/scripts/check-security.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CLAUDE_MARKETPLACE_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" + +source "${CLAUDE_MARKETPLACE_ROOT}/marketplace-utils/hook-lifecycle.sh" + +init_hook "typescript" "PreToolUse" + +read_hook_input > /dev/null +FILE_PATH=$(get_input_field "tool_input.file_path") +NEW_STRING=$(get_input_field "tool_input.new_string") + +if [[ -z "$NEW_STRING" ]]; then + NEW_STRING=$(get_input_field "tool_input.content") +fi + +if [[ -z "$FILE_PATH" || -z "$NEW_STRING" ]]; then + pretooluse_respond "allow" + finish_hook 0 +fi + +FILE_EXT="${FILE_PATH##*.}" +if [[ "$FILE_EXT" != "ts" && "$FILE_EXT" != "tsx" && "$FILE_EXT" != "js" && "$FILE_EXT" != "jsx" ]]; then + pretooluse_respond "allow" + finish_hook 0 +fi + +if echo "$NEW_STRING" | grep -iqE 'password.*=.*(Buffer.*toString|btoa|atob)'; then + log_error "CRITICAL: Base64 encoding on password field" + pretooluse_respond "block" "🚨 CRITICAL SECURITY VIOLATION: Base64 encoding detected on password field + +Base64 is NOT encryption. Use bcrypt, argon2, or scrypt for password hashing. + +See: @typescript/hashing-passwords skill" + finish_hook 0 +fi + +if echo "$NEW_STRING" | grep -iqE '(paypal|google|facebook|twitter|github|microsoft|amazon)Password\s*[:\?]'; then + log_error "CRITICAL: Accepting third-party credentials" + pretooluse_respond "block" "🚨 CRITICAL SECURITY VIOLATION: Accepting third-party credentials + +NEVER ask for passwords to other services. Use OAuth instead. + +See: @typescript/hashing-passwords skill" + finish_hook 0 +fi + +if echo "$NEW_STRING" | grep -qE '\beval\s*\('; then + log_error "CRITICAL: eval() usage" + pretooluse_respond "block" "🚨 CRITICAL SECURITY VIOLATION: eval() usage detected + +eval() enables arbitrary code execution and is a major security risk. +Use safer alternatives: +- JSON.parse() for JSON data +- Function constructors with known, validated code +- Template engines for dynamic content + +See: @typescript/avoiding-eval skill" + finish_hook 0 +fi + +if echo "$NEW_STRING" | grep -qE 'new Function\s*\([^)]*\$\{|\`.*\$\{.*\}.*\`.*new Function'; then + log_error "CRITICAL: Function constructor with template literals" + pretooluse_respond "block" "🚨 CRITICAL SECURITY VIOLATION: Function constructor with dynamic code + +Creating functions from template literals enables code injection. + +See: @typescript/avoiding-eval skill" + finish_hook 0 +fi + +if echo "$NEW_STRING" | grep -qE 'exec\s*\([^)]*\$\{|spawn\s*\([^)]*\$\{'; then + log_error "CRITICAL: Command injection vulnerability" + pretooluse_respond "block" "🚨 CRITICAL SECURITY VIOLATION: Potential command injection + +Concatenating user input into shell commands enables command injection. +Use parameterized execution: +- execFile() with argument array +- spawn() with separate arguments +- Validate/sanitize all inputs + +See: @typescript/preventing-command-injection skill" + finish_hook 0 +fi + +pretooluse_respond "allow" +finish_hook 0 diff --git a/hooks/scripts/check-syntax.sh b/hooks/scripts/check-syntax.sh new file mode 100755 index 0000000..cef95e8 --- /dev/null +++ b/hooks/scripts/check-syntax.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CLAUDE_MARKETPLACE_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" + +source "${CLAUDE_MARKETPLACE_ROOT}/marketplace-utils/hook-lifecycle.sh" + +init_hook "typescript" "PreToolUse" + +read_hook_input > /dev/null +TOOL_NAME=$(get_input_field "tool_name") +FILE_PATH=$(get_input_field "tool_input.file_path") + +if [[ "$TOOL_NAME" != "Write" && "$TOOL_NAME" != "Edit" ]]; then + pretooluse_respond "allow" + finish_hook 0 +fi + +FILE_EXT="${FILE_PATH##*.}" +if [[ "$FILE_EXT" != "ts" && "$FILE_EXT" != "tsx" && "$FILE_EXT" != "js" && "$FILE_EXT" != "jsx" ]]; then + pretooluse_respond "allow" + finish_hook 0 +fi + +SYNTAX_CHECKER="${SCRIPT_DIR}/../../scripts/check-syntax.js" + +SYNTAX_CHECK=$(echo "$HOOK_INPUT" | node "$SYNTAX_CHECKER" 2>&1 || true) + +if echo "$SYNTAX_CHECK" | grep -q '"hasSyntaxError":true'; then + ERROR_MESSAGE=$(echo "$SYNTAX_CHECK" | jq -r '.errorMessage // "Syntax error detected"') + LINE=$(echo "$SYNTAX_CHECK" | jq -r '.line // "unknown"') + + log_error "Syntax error in $FILE_PATH at line $LINE" + pretooluse_respond "block" "🚨 SYNTAX ERROR: Cannot write file with syntax errors + +$ERROR_MESSAGE + +Line: $LINE + +Please fix the syntax error before writing the file." + finish_hook 0 +fi + +pretooluse_respond "allow" +finish_hook 0 diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..96796c5 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,169 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:djankies/claude-configs:typescript", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "50e84b62fe007830025f40e47b22d35dfff96b8b", + "treeHash": "d1969a9a06bd77861cfdcf8835773dad590c4a106036c40c1a28fda63b735d67", + "generatedAt": "2025-11-28T10:16:29.623192Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "typescript", + "description": "TypeScript 5.9 type safety, compiler configuration, and best practices based on real-world AI coding failures. Provides intelligent skill recommendations, type safety validation, and prevents common TypeScript anti-patterns.", + "version": "1.0.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "3c0c890e87d0605d2b353110549e18a966e52facc2caa55debe6149f3e815546" + }, + { + "path": "hooks/hooks.json", + "sha256": "299864d4994d74935664ae463fbc76630a17782c38d8727b61c4af77ab1927dc" + }, + { + "path": "hooks/scripts/check-syntax.sh", + "sha256": "0a2f9d06d73dd144adad7289dc7884e696ece1136c194f2f01e2d1036ceb19b5" + }, + { + "path": "hooks/scripts/check-security.sh", + "sha256": "2560ba13f4c228ed6450ed01d67ba6ad69feb5c3a9ebe7ba678a8574a02e828e" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "5fdf49589c20f7dc4efb0a5d7312fcbc137b4e7fdde501a70b461c51af521018" + }, + { + "path": "commands/ts.md", + "sha256": "61b54c01746d5861ee92df002f5191a1249921759ba18e42363f4613882618d2" + }, + { + "path": "skills/reviewing-type-safety/SKILL.md", + "sha256": "319a2908a37a70d81215121764996741c57cb735137bf9b484413f4cd561e6fc" + }, + { + "path": "skills/avoiding-any-types/SKILL.md", + "sha256": "522099e46960c4da3559ca5f9d04c39000928012bc1f6e0abc8ab6151b60ab22" + }, + { + "path": "skills/validating-type-assertions/SKILL.md", + "sha256": "fb2741fb43b4c7357ddbaef9e1e110ee24cae668eeed3dfc9fb93bbbbcacf918" + }, + { + "path": "skills/using-generics/SKILL.md", + "sha256": "fae84c3df6b76845fafa9c4195e877e0ec3108dd104fe06fc444f9cde883d678" + }, + { + "path": "skills/using-generics/references/advanced-patterns.md", + "sha256": "8c7541315a5517379c0933102f9268cb0cdbe9c2f7863cf33557a9b389a8eb42" + }, + { + "path": "skills/using-generics/references/common-patterns.md", + "sha256": "5f227a3f0b34b4369815757086dafedd9c0583eece6f2baa2d6822d5d13d1c1f" + }, + { + "path": "skills/using-generics/references/detailed-examples.md", + "sha256": "922db516e9c310f2c68266e4f9c258e28d9b12783b82e7d0d979a2d5a1d38eb2" + }, + { + "path": "skills/diagnosing-type-errors/SKILL.md", + "sha256": "cce9b98bd2e965737ba2a28aaaa18a7b630afc4ef89b2ce5ce8b6a1c9886e654" + }, + { + "path": "skills/avoiding-non-null-assertions/SKILL.md", + "sha256": "2da7365d4a5ed11a7f67db27ac38941015bed7bf1b4ab4144451391c4a186d36" + }, + { + "path": "skills/sanitizing-user-inputs/SKILL.md", + "sha256": "a4a73b0f6fdee412b82cffc7ebff9ae16c7b227a27b312d0394b6dd0eba7e071" + }, + { + "path": "skills/auditing-dependencies/SKILL.md", + "sha256": "73570c593334bbfc3771e1c453555df241ea9645adc82b3bf77958f3a900c0a7" + }, + { + "path": "skills/using-type-guards/SKILL.md", + "sha256": "1821c0b6b01f79305a8c8b7eed1bbb1d09e9983a079c334b6bafb95d70df3cad" + }, + { + "path": "skills/using-type-guards/references/advanced-patterns.md", + "sha256": "522b7b5f52c243bf34a499313c32ade4ed22f31e80e5aeea0b8cfff9209593f0" + }, + { + "path": "skills/using-type-guards/references/nested-validation.md", + "sha256": "5c64c290a3dbe1cf9da15b7ebd8524a61589bb373931afe69cacc4b656a84f88" + }, + { + "path": "skills/using-type-guards/references/testing-guide.md", + "sha256": "9d432e4c9411dcc7c0f210def83b96bd5c41afac4fb02603f4cd6c4bbefdbae8" + }, + { + "path": "skills/validating-external-data/SKILL.md", + "sha256": "005070a3b0b9c8a5d1786b7d8d55a55b7266e858879acb8411f296077b4a05a3" + }, + { + "path": "skills/resolving-type-errors/SKILL.md", + "sha256": "2e7a82693cecc5e442794191d8281f89bdcf0c447153610f426ce4afc28b0500" + }, + { + "path": "skills/using-runtime-checks/SKILL.md", + "sha256": "dba359ae97f0bd1ab0816cef740a9dba820d64f596adf8ef26fe832f0dde6c84" + }, + { + "path": "skills/using-runtime-checks/references/zod-patterns.md", + "sha256": "e7d699600ac8d5abff19e36f5922262d4316e58ffd2651d3cfd24a243e29d95a" + }, + { + "path": "skills/using-runtime-checks/references/performance.md", + "sha256": "e5523c260dd55d4055c5a474f1eca2524327494fdd5c0d3ed5fa4d1464ab4913" + }, + { + "path": "skills/using-runtime-checks/references/error-handling.md", + "sha256": "3e76ed508f6f8f55532c9310303c1e15888610536b29e18aa21d086b27d69298" + }, + { + "path": "skills/avoiding-angle-bracket-assertions/SKILL.md", + "sha256": "fd9d458cddc242172f60f27b296d2b475d69c1b873f0c9adef2525779f6be385" + }, + { + "path": "skills/hashing-passwords/SKILL.md", + "sha256": "643bbda81977f7820dd44101c36b715d8c9c94b56754829dd0ebab564162bc7a" + }, + { + "path": "skills/hashing-passwords/references/correct-implementations.md", + "sha256": "f229cf78ffa35174176aab4c6196f7962c20d422f89aa75cfe08b2b37b906d45" + }, + { + "path": "skills/hashing-passwords/references/never-do-this.md", + "sha256": "afba4f0a54a191ba3a74b0be011fac8be23885ac02371da5fd90ff49dd665a73" + }, + { + "path": "skills/hashing-passwords/references/password-validation.md", + "sha256": "8d27d666f35d4df7239d464ade667824a0f830b9aee11f47ea69d707bb3d1792" + }, + { + "path": "skills/hashing-passwords/references/emergency-response.md", + "sha256": "184c3ac5aae5b343640ef00e103f0eeb689ac8d5a2525786f98f5b270892e948" + }, + { + "path": "skills/refactoring-inline-types/SKILL.md", + "sha256": "b8fa91458202a62b14116962f219db1aa6fc44ba3a13d2439e6b881e395f3316" + } + ], + "dirSha256": "d1969a9a06bd77861cfdcf8835773dad590c4a106036c40c1a28fda63b735d67" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/auditing-dependencies/SKILL.md b/skills/auditing-dependencies/SKILL.md new file mode 100644 index 0000000..2495dfd --- /dev/null +++ b/skills/auditing-dependencies/SKILL.md @@ -0,0 +1,405 @@ +--- +name: auditing-dependencies +description: Auditing and updating npm dependencies to prevent security vulnerabilities in TypeScript projects +--- + +# Security: Dependency Management + +**Purpose:** Prevent security vulnerabilities through proper npm dependency auditing, updating, and monitoring. + +**When to use:** Before adding new dependencies, during security reviews, when setting up CI/CD pipelines, or when package.json changes. + +## Critical Security Principle + +**Dependencies are attack vectors.** Each package you add introduces potential vulnerabilities: +- Direct vulnerabilities in the package code +- Transitive dependencies (dependencies of dependencies) +- Supply chain attacks (malicious package updates) +- Unmaintained packages with known CVEs + +**Default stance:** Minimize dependencies. Every package is a liability. + +## Dependency Audit Workflow + +### 1. Check for Known Vulnerabilities + +**Before installing any package:** + +```bash +npm audit +``` + +This shows: +- Severity levels (critical, high, moderate, low) +- Vulnerable packages and versions +- Recommended fixes +- Dependency path showing how vulnerability entered + +**Read the output carefully.** Not all vulnerabilities affect your code: +- Check if vulnerable code path is used +- Assess actual risk vs theoretical risk +- Prioritize fixes by severity and exploitability + +### 2. Update Vulnerable Packages + +**Automatic fixes (use with caution):** + +```bash +npm audit fix +``` + +This updates packages to non-breaking versions that patch vulnerabilities. + +**Breaking changes:** + +```bash +npm audit fix --force +``` + +This updates to latest versions, potentially breaking your code. Use only after: +- Reading breaking change notes +- Having comprehensive test coverage +- Being prepared to fix broken code + +**Manual selective updates:** + +```bash +npm update package-name +``` + +Update specific packages after reviewing their changelogs. + +### 3. Prevent Vulnerable Installations + +**Block installations with vulnerabilities:** + +Create `.npmrc` in project root: + +``` +audit-level=moderate +``` + +This fails `npm install` if moderate or higher severity vulnerabilities exist. + +**In CI/CD:** + +```yaml +- name: Security audit + run: | + npm audit --audit-level=moderate + if [ $? -ne 0 ]; then + echo "Security vulnerabilities found!" + exit 1 + fi +``` + +### 4. Monitor Dependencies Continuously + +**GitHub Dependabot:** + +Enable in repository settings → Security → Dependabot alerts. + +Automatically: +- Scans dependencies daily +- Creates PRs for security updates +- Provides vulnerability details + +**npm-check-updates:** + +```bash +npx npm-check-updates +``` + +Shows available updates for all dependencies. + +```bash +npx npm-check-updates -u +``` + +Updates package.json (still need to run `npm install`). + +## Dependency Selection Best Practices + +### Before Adding Any Dependency + +**Ask these questions:** + +1. **Do I actually need this?** + - Can I write the functionality myself in < 100 lines? + - Am I using 10% of the package's features? + - Is this adding significant bundle size for trivial functionality? + +2. **Is this package trustworthy?** + - Check npm weekly downloads (high is better) + - Check GitHub stars and recent activity + - Check last publish date (recent is better, but stable packages may be older) + - Look for security track record + +3. **Is this package maintained?** + - When was last commit? + - Are issues being responded to? + - Are security issues addressed quickly? + - Is there a clear maintenance policy? + +4. **What are the transitive dependencies?** + ```bash + npm ls package-name + ``` + Each transitive dependency is another attack vector. + +### Red Flags + +**Avoid packages with:** +- No TypeScript types (requires `@types/` package or no types at all) +- Abandoned for > 2 years with no successor +- Known security vulnerabilities with no fix available +- Excessive transitive dependencies (> 50 packages) +- Requires `postinstall` scripts (potential supply chain attack vector) +- Very small packages doing trivial things (left-pad scenario) + +### Safer Alternatives + +**Use built-in Node.js/browser features when possible:** + +```typescript +import { randomBytes } from 'crypto'; +const id = randomBytes(16).toString('hex'); +``` + +Better than installing `uuid` package if you just need random IDs. + +```typescript +const url = new URL('/api/users', 'https://api.example.com'); +url.searchParams.set('limit', '10'); +``` + +Better than installing query string builder packages. + +## Lock Files and Reproducible Builds + +### Always Commit Lock Files + +**package-lock.json** (npm) or **yarn.lock** (Yarn) must be committed: +- Ensures exact same dependency versions across environments +- Prevents supply chain attacks via dependency version updates +- Makes builds reproducible + +**Never:** +- Add lock files to `.gitignore` +- Delete lock files to "fix" problems +- Run `npm install` with `--no-package-lock` + +### Use Specific Version Ranges + +**In package.json, prefer exact versions for critical dependencies:** + +```json +{ + "dependencies": { + "express": "4.18.2", + "zod": "3.22.4" + } +} +``` + +Not: +```json +{ + "dependencies": { + "express": "^4.18.2", + "zod": "~3.22.4" + } +} +``` + +**Rationale:** Caret (`^`) and tilde (`~`) allow automatic updates that could introduce breaking changes or vulnerabilities. + +**Exception:** Development dependencies can use ranges if you regularly update them. + +## CI/CD Integration + +### Required Security Checks + +**Every CI pipeline must:** + +1. **Run audit on every build:** + ```yaml + - run: npm audit --audit-level=moderate + ``` + +2. **Check for outdated dependencies weekly:** + ```yaml + schedule: + - cron: '0 0 * * 1' + jobs: + update-check: + - run: npx npm-check-updates + ``` + +3. **Prevent merging PRs with vulnerabilities:** + ```yaml + - name: Security gate + run: npm audit --production --audit-level=moderate + ``` + +### Example GitHub Actions Workflow + +```yaml +name: Security Audit + +on: + push: + branches: [main] + pull_request: + schedule: + - cron: '0 0 * * 1' + +jobs: + audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - run: npm ci + - run: npm audit --audit-level=moderate + - name: Check for outdated packages + run: npx npm-check-updates +``` + +## Handling Vulnerabilities That Can't Be Fixed + +### Scenario: Dependency has vulnerability, no fix available + +**Options:** + +1. **Find alternative package:** + - Research similar packages without the vulnerability + - Consider rewriting functionality if simple + +2. **Assess actual risk:** + - Is vulnerable code path used in your application? + - Is vulnerability exploitable in your context? + - Document risk assessment + +3. **Audit exception (last resort):** + ```bash + npm audit --json > audit-baseline.json + ``` + + Document why exception is acceptable: + - What is the vulnerability? + - Why can't it be fixed? + - What mitigations are in place? + - When will this be reviewed again? + + **Never ignore vulnerabilities permanently.** + +## Common Mistakes + +### Mistake 1: Installing packages without checking + +```bash +npm install some-random-package +``` + +**Correct approach:** + +1. Check package on npm registry +2. Review GitHub repository +3. Check bundle size: `bundlephobia.com` +4. Run `npm audit` after installation +5. Review lock file changes in git diff + +### Mistake 2: Ignoring audit warnings + +"It's just a moderate severity in a dev dependency, doesn't matter." + +**Wrong.** Development dependencies: +- Can be compromised and steal secrets +- Run with same permissions as your code +- Can modify source files during build + +### Mistake 3: Using `--force` without understanding + +```bash +npm install --force +``` + +This bypasses dependency resolution and can install incompatible versions. + +**Only use when:** +- You understand exactly what it does +- You've read the conflict details +- You have tests to verify nothing broke + +## Maintenance Schedule + +**Weekly:** +- Review Dependabot PRs +- Run `npm audit` + +**Monthly:** +- Run `npx npm-check-updates` +- Update non-breaking dependencies +- Test thoroughly + +**Quarterly:** +- Plan major version updates +- Review all dependencies for continued need +- Remove unused packages + +## TypeScript-Specific Considerations + +### Type Definition Security + +**Check if types match runtime:** + +```typescript +import { z } from 'zod'; + +const APIResponseSchema = z.object({ + data: z.array(z.string()), +}); + +type APIResponse = z.infer; +``` + +This ensures types and runtime validation stay synchronized. + +### Type-Only Imports for Tree-Shaking + +```typescript +import type { User } from 'huge-library'; +``` + +This imports only types, not runtime code, reducing bundle size. + +## Resources + +**Tools:** +- `npm audit` - Built-in vulnerability scanner +- `npm-check-updates` - Dependency update checker +- Snyk - Commercial vulnerability scanning +- GitHub Dependabot - Automated security updates + +**References:** +- [npm audit documentation](https://docs.npmjs.com/cli/v10/commands/npm-audit) +- [OWASP Dependency Check](https://owasp.org/www-project-dependency-check/) +- [Sonatype State of Software Supply Chain](https://www.sonatype.com/state-of-the-software-supply-chain) + +## Summary Checklist + +Before merging any dependency changes: + +- [ ] `npm audit` passes at moderate level or higher +- [ ] Reviewed what the package does and alternatives considered +- [ ] Checked package maintenance status and security history +- [ ] Lock file committed with changes +- [ ] CI pipeline includes security audit +- [ ] Transitive dependencies reviewed (not excessive) +- [ ] Bundle size impact assessed for frontend projects +- [ ] Types available and trustworthy + +**Remember:** The best dependency is the one you don't add. The second best is one that's actively maintained with a strong security track record. diff --git a/skills/avoiding-angle-bracket-assertions/SKILL.md b/skills/avoiding-angle-bracket-assertions/SKILL.md new file mode 100644 index 0000000..e771e0b --- /dev/null +++ b/skills/avoiding-angle-bracket-assertions/SKILL.md @@ -0,0 +1,284 @@ +--- +name: avoiding-angle-bracket-assertions +description: Avoid angle-bracket type assertions () and use 'as Type' syntax instead +--- + +# Avoiding Angle-Bracket Type Assertions + +The angle-bracket type assertion syntax (`value`) is deprecated in TypeScript, especially in TSX files where it conflicts with JSX syntax. + +## Why Avoid `` Syntax + +- **JSX conflict**: Incompatible with TSX files (React, Preact, etc.) +- **Inconsistent syntax**: Modern TypeScript uses `as Type` +- **Reduced readability**: Looks like generics or JSX +- **Tooling issues**: Some tools don't handle it well + +## Modern Alternative: `as Type` + +### Basic Replacement + +**Deprecated:** +```typescript +const value = someValue; +const num = input; +``` + +**Modern:** +```typescript +const value = someValue as string; +const num = input as number; +``` + +### With Complex Types + +**Deprecated:** +```typescript +const user = jsonData; +const config = settings; +``` + +**Modern:** +```typescript +const user = jsonData as User; +const config = settings as AppConfig; +``` + +## Better: Avoid Type Assertions Entirely + +Type assertions bypass type checking and should be avoided when possible. + +### 1. Use Type Guards Instead + +**Bad:** +```typescript +function processValue(value: unknown) { + const str = value as string; + return str.toUpperCase(); +} +``` + +**Good:** +```typescript +function processValue(value: unknown) { + if (typeof value === 'string') { + return value.toUpperCase(); + } + throw new Error('Expected string'); +} +``` + +### 2. Use Validation Functions + +**Bad:** +```typescript +const user = apiResponse as User; +``` + +**Good:** +```typescript +function isUser(value: unknown): value is User { + return ( + typeof value === 'object' && + value !== null && + 'id' in value && + 'name' in value && + typeof value.id === 'number' && + typeof value.name === 'string' + ); +} + +const parsed = apiResponse; +if (isUser(parsed)) { + const user = parsed; +} else { + throw new Error('Invalid user data'); +} +``` + +### 3. Use Validation Libraries + +**Best:** +```typescript +import { z } from 'zod'; + +const UserSchema = z.object({ + id: z.number(), + name: z.string(), + email: z.string().email(), +}); + +type User = z.infer; + +const user = UserSchema.parse(apiResponse); +``` + +### 4. Improve Type Inference + +**Bad:** +```typescript +const element = document.getElementById('myId') as HTMLButtonElement; +``` + +**Better:** +```typescript +const element = document.getElementById('myId'); +if (element instanceof HTMLButtonElement) { + element.addEventListener('click', handler); +} +``` + +**Or use querySelector with type inference:** +```typescript +const element = document.querySelector('#myId'); +if (element) { + element.addEventListener('click', handler); +} +``` + +### 5. Use Discriminated Unions + +**Bad:** +```typescript +function handleEvent(event: Event) { + const mouseEvent = event as MouseEvent; + console.log(mouseEvent.clientX); +} +``` + +**Good:** +```typescript +type AppEvent = + | { type: 'mouse'; clientX: number; clientY: number } + | { type: 'keyboard'; key: string } + | { type: 'custom'; data: unknown }; + +function handleEvent(event: AppEvent) { + switch (event.type) { + case 'mouse': + console.log(event.clientX); + break; + case 'keyboard': + console.log(event.key); + break; + case 'custom': + console.log(event.data); + break; + } +} +``` + +## TSX-Specific Issues + +In TSX files, angle-bracket syntax causes syntax errors: + +**Will cause syntax error in TSX:** +```tsx +const Component = () => { + const value = getData(); + return
{value}
; +}; +``` + +**Must use `as` syntax:** +```tsx +const Component = () => { + const value = getData() as string; + return
{value}
; +}; +``` + +**Even better - validate properly:** +```tsx +const Component = () => { + const data = getData(); + if (typeof data !== 'string') { + throw new Error('Expected string'); + } + return
{data}
; +}; +``` + +## When Assertions Are Necessary + +If you must use assertions (rare cases): + +### 1. Use `as const` for Literal Types + +```typescript +const config = { + apiUrl: 'https://api.example.com', + timeout: 5000, +} as const; +``` + +### 2. Use `as Type` (Never ``) + +```typescript +const value = unknownValue as SomeType; +``` + +### 3. Document Why It's Safe + +```typescript +const element = document.getElementById('app-root') as HTMLDivElement; +``` + +## Migration Strategy + +1. **Find all angle-bracket assertions:** + ```bash + grep -r '<[A-Z][a-zA-Z]*>' --include="*.ts" --include="*.tsx" + ``` + +2. **Replace with `as` syntax:** + - `value` → `value as Type` + +3. **Review each assertion:** + - Can it be replaced with a type guard? + - Can validation library be used? + - Is it truly necessary? + +4. **Enable linting:** + ```json + { + "rules": { + "@typescript-eslint/consistent-type-assertions": [ + "error", + { + "assertionStyle": "as", + "objectLiteralTypeAssertions": "never" + } + ] + } + } + ``` + +## ESLint Configuration + +Enforce `as` syntax and discourage assertions: + +```json +{ + "rules": { + "@typescript-eslint/consistent-type-assertions": [ + "error", + { + "assertionStyle": "as", + "objectLiteralTypeAssertions": "never" + } + ], + "@typescript-eslint/no-unnecessary-type-assertion": "error" + } +} +``` + +## Summary + +**Never use angle-bracket syntax:** +- **Use** `as Type` when assertions are unavoidable +- **Prefer** type guards over assertions +- **Use** validation libraries for external data +- **Validate** at runtime for safety +- **Document** why assertions are necessary +- **Enable** linting to enforce consistency diff --git a/skills/avoiding-any-types/SKILL.md b/skills/avoiding-any-types/SKILL.md new file mode 100644 index 0000000..89b3c4e --- /dev/null +++ b/skills/avoiding-any-types/SKILL.md @@ -0,0 +1,341 @@ +--- +name: avoiding-any-types +description: Teaches when and how to use unknown instead of any type in TypeScript. Use when working with TypeScript code that has any types, needs type safety, handling external data, or when designing APIs. Critical for preventing type safety violations. +allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task, TodoWrite +version: 1.0.0 +--- + + +This skill teaches how to eliminate `any` type abuse in TypeScript by using `unknown` with proper type guards. Based on stress test findings where 83% of AI agents overused `any`, defeating TypeScript's purpose. + + + +This skill activates when: + +- Code contains `any` type annotations +- Working with external data (APIs, JSON, user input) +- Designing generic functions or types +- User mentions type safety, type checking, or avoiding `any` +- Files contain patterns like `: any`, ``, `as any` + + + +The `any` type is TypeScript's escape hatch that disables all type checking. While sometimes necessary, overusing `any` defeats TypeScript's purpose and allows runtime errors TypeScript should prevent. + +The `unknown` type is `any`'s type-safe counterpart. It accepts any value like `any`, but requires type narrowing before use. + +**Key Pattern**: `any` → `unknown` → type guard → safe access + +**Impact**: Prevents runtime errors while maintaining flexibility for truly dynamic data. + + + +## Decision Flow + +**Step 1: Identify `any` Usage** + +When you see `any`, ask: +1. Is this truly unknowable at compile time? (external data, plugin systems) +2. Can I use a more specific type? (union types, generics with constraints) +3. Is this laziness or necessity? + +**Step 2: Choose Replacement Strategy** + +**Strategy A: Use Specific Types** (preferred) +- Known structure → Use interfaces/types +- Multiple possible types → Use union types +- Shared shape → Use generics with constraints + +**Strategy B: Use `unknown`** (when truly dynamic) +- External data with unknown structure +- Plugin/extension systems +- Gradual migration from JavaScript + +**Strategy C: Keep `any`** (rare, justified cases) +- Interop with poorly-typed libraries +- Complex type manipulation that TypeScript can't express +- Performance-critical code where type checks are prohibitive + +**Step 3: Implement Type Guards** + +When using `unknown`, implement type guards: +1. Runtime validation (Zod, io-ts, custom validators) +2. Type predicates for custom guards +3. Built-in guards (typeof, instanceof, in) + + + +## Reference Files + +For detailed patterns and examples: + +- **Type Guard Patterns**: Use the using-type-guards skill for comprehensive type guard implementation +- **Runtime Validation**: Use the using-runtime-checks skill for validating unknown data +- **Generic Constraints**: Use the using-generics skill for constraining generic types + + + +## Example 1: API Response (External Data) + +**❌ Using `any` (unsafe)** + +```typescript +async function fetchUser(id: string): Promise { + const response = await fetch(`/api/users/${id}`); + return response.json(); +} + +const user = await fetchUser("123"); +console.log(user.name.toUpperCase()); +``` + +**Problem**: If API returns `{ username: string }` instead of `{ name: string }`, this crashes at runtime. TypeScript provides no protection. + +**✅ Using `unknown` + validation (safe)** + +```typescript +async function fetchUser(id: string): Promise { + const response = await fetch(`/api/users/${id}`); + return response.json(); +} + +function isUser(value: unknown): value is { name: string } { + return ( + typeof value === "object" && + value !== null && + "name" in value && + typeof value.name === "string" + ); +} + +const userData = await fetchUser("123"); +if (isUser(userData)) { + console.log(userData.name.toUpperCase()); +} else { + throw new Error("Invalid user data"); +} +``` + +**Better**: Use Zod for complex validation (use the using-runtime-checks skill) + +--- + +## Example 2: Generic Function Defaults + +**❌ Using `any` in generic default (unsafe)** + +```typescript +interface ApiResponse { + data: T; + status: number; +} + +const response: ApiResponse = { data: "anything", status: 200 }; +response.data.nonexistent.property; +``` + +**Problem**: Generic defaults to `any`, losing all type safety. + +**✅ Using `unknown` default (safe)** + +```typescript +interface ApiResponse { + data: T; + status: number; +} + +const response: ApiResponse = { data: "anything", status: 200 }; + +if (typeof response.data === "string") { + console.log(response.data.toUpperCase()); +} +``` + +**Even Better**: Require explicit type parameter + +```typescript +interface ApiResponse { + data: T; + status: number; +} + +const response: ApiResponse = await fetchUser(); +``` + +--- + +## Example 3: Error Handling + +**❌ Using `any` for caught errors (unsafe)** + +```typescript +try { + await riskyOperation(); +} catch (error: any) { + console.log(error.message); +} +``` + +**Problem**: Not all thrown values are Error objects. This crashes if someone throws a string or number. + +**✅ Using `unknown` + type guard (safe)** + +```typescript +try { + await riskyOperation(); +} catch (error: unknown) { + if (error instanceof Error) { + console.log(error.message); + } else { + console.log("Unknown error:", String(error)); + } +} +``` + +--- + +## Example 4: Validation Function + +**❌ Using `any` parameter (unsafe)** + +```typescript +function validate(data: any): boolean { + return data.email && data.password; +} +``` + +**Problem**: Typos like `data.emial` are not caught. No autocomplete support. + +**✅ Using `unknown` + type guard (safe)** + +```typescript +interface LoginData { + email: string; + password: string; +} + +function isLoginData(data: unknown): data is LoginData { + return ( + typeof data === "object" && + data !== null && + "email" in data && + "password" in data && + typeof data.email === "string" && + typeof data.password === "string" + ); +} + +function validate(data: unknown): data is LoginData { + if (!isLoginData(data)) { + return false; + } + + return data.email.includes("@") && data.password.length >= 8; +} +``` + + + +**MUST:** + +- Use `unknown` for external data (APIs, JSON.parse, user input) +- Use `unknown` for generic defaults when type is truly dynamic +- Implement type guards before accessing `unknown` values +- Use runtime validation libraries (Zod, io-ts) for complex structures + +**SHOULD:** + +- Prefer specific types (interfaces, unions) over `unknown` when structure is known +- Use type predicates (`value is Type`) for reusable type guards +- Narrow `unknown` progressively (check object → check properties → check types) + +**NEVER:** + +- Use `any` for external data +- Use `as any` to silence TypeScript errors +- Use `any` in public API surfaces +- Default generics to `any` +- Cast `unknown` to specific types without validation + + + +## Validation Checklist + +After replacing `any` with `unknown`: + +1. **Type Guard Exists**: + - Every `unknown` value has a corresponding type guard + - Type guards check structure AND types + - Guards return early on invalid data + +2. **Safe Access Only**: + - No property access before type narrowing + - No method calls before type narrowing + - IDE autocomplete works after narrowing + +3. **Error Handling**: + - Invalid data handled gracefully + - Clear error messages + - No silent failures + +4. **Compilation**: + ```bash + npx tsc --noEmit + ``` + Should pass without `any` suppressions. + + + +## When `any` is Actually Justified + +**1. Library Interop** (temporary) +```typescript +import poorlyTypedLib from "poorly-typed-lib"; +const result = poorlyTypedLib.method() as any; +``` + +Consider contributing types to DefinitelyTyped or wrapping in typed facade. + +**2. Type Manipulation Edge Cases** +```typescript +type DeepPartial = { + [P in keyof T]?: T[P] extends object ? DeepPartial : T[P]; +}; +``` + +Sometimes TypeScript's type system can't express complex patterns. Document why. + +**3. Explicit Opt-Out** +```typescript +const configSchema: any = generateFromSpec(); +``` + +Explicitly choosing to skip type checking for this value. Document decision. + + + +## Migrating Existing `any` Usage + +**Phase 1: Audit** +```bash +grep -rn ": any" src/ +grep -rn "" src/ +grep -rn "= any" src/ +``` + +**Phase 2: Classify** +- External data → `unknown` + validation +- Known structure → specific types +- Generic defaults → remove default or use `unknown` +- Justified `any` → document with comment explaining why + +**Phase 3: Replace** +- Start with external boundaries (API layer, JSON parsing) +- Work inward toward core logic +- Add tests to verify runtime behavior unchanged + +**Phase 4: Prevent** +- Enable `noImplicitAny` in tsconfig.json +- Add lint rules forbidding `any` +- Use hooks to catch new `any` introduction + diff --git a/skills/avoiding-non-null-assertions/SKILL.md b/skills/avoiding-non-null-assertions/SKILL.md new file mode 100644 index 0000000..f3a1a4a --- /dev/null +++ b/skills/avoiding-non-null-assertions/SKILL.md @@ -0,0 +1,246 @@ +--- +name: avoiding-non-null-assertions +description: Avoid non-null assertion operator (!) and use type-safe alternatives instead +--- + +# Avoiding Non-Null Assertions + +The non-null assertion operator (`!`) is deprecated in modern TypeScript because it bypasses type safety and can lead to runtime errors. + +## Why Avoid `!` + +- **Bypasses type safety**: Tells TypeScript "trust me" without verification +- **Runtime errors**: Can cause `undefined` or `null` errors at runtime +- **Maintenance burden**: Makes refactoring dangerous +- **No protection**: Removes TypeScript's main benefit + +## Modern Alternatives + +### 1. Optional Chaining (`?.`) + +**Bad:** + +```typescript +const userName = user!.profile!.name; +``` + +**Good:** + +```typescript +const userName = user?.profile?.name; +``` + +### 2. Nullish Coalescing (`??`) + +**Bad:** + +```typescript +const value = config!.timeout; +``` + +**Good:** + +```typescript +const value = config?.timeout ?? 5000; +``` + +### 3. Type Guards + +**Bad:** + +```typescript +function processUser(user: User | null) { + console.log(user!.name); +} +``` + +**Good:** + +```typescript +function processUser(user: User | null) { + if (user !== null) { + console.log(user.name); + } +} +``` + +### 4. Early Return Pattern + +**Bad:** + +```typescript +function getUserEmail(userId: number): string { + const user = findUser(userId); + return user!.email; +} +``` + +**Good:** + +```typescript +function getUserEmail(userId: number): string | null { + const user = findUser(userId); + if (!user) { + return null; + } + return user.email; +} +``` + +### 5. Custom Type Guards + +**Bad:** + +```typescript +function handleValue(value: unknown) { + return (value as User)!.name; +} +``` + +**Good:** + +```typescript +function isUser(value: unknown): value is User { + return typeof value === 'object' && value !== null && 'name' in value; +} + +function handleValue(value: unknown) { + if (isUser(value)) { + return value.name; + } + throw new Error('Invalid user'); +} +``` + +### 6. Narrowing with `in` Operator + +**Bad:** + +```typescript +function process(obj: { data?: string }) { + console.log(obj.data!.toUpperCase()); +} +``` + +**Good:** + +```typescript +function process(obj: { data?: string }) { + if ('data' in obj && obj.data !== undefined) { + console.log(obj.data.toUpperCase()); + } +} +``` + +### 7. Array Methods with Type Safety + +**Bad:** + +```typescript +const users: User[] = getUsers(); +const firstUser = users[0]!; +``` + +**Good:** + +```typescript +const users: User[] = getUsers(); +const firstUser = users.at(0); +if (firstUser) { + console.log(firstUser.name); +} +``` + +### 8. Assertion Functions (TypeScript 3.7+) + +**Good:** + +```typescript +function assertIsDefined(value: T): asserts value is NonNullable { + if (value === undefined || value === null) { + throw new Error('Value must be defined'); + } +} + +function process(value: string | null) { + assertIsDefined(value); + console.log(value.toUpperCase()); +} +``` + +## DOM Element Access + +**Bad:** + +```typescript +const button = document.getElementById('submit')!; +button.addEventListener('click', handler); +``` + +**Good:** + +```typescript +const button = document.getElementById('submit'); +if (button) { + button.addEventListener('click', handler); +} +``` + +**Or with assertion function:** + +```typescript +function assertElement( + element: T | null, + selector: string +): asserts element is T { + if (!element) { + throw new Error(`Element not found: ${selector}`); + } +} + +const button = document.getElementById('submit'); +assertElement(button, '#submit'); +button.addEventListener('click', handler); +``` + +## When Is `!` Acceptable? + +Only in very rare cases where: + +1. You have exhaustively verified the value exists +2. There's no other way to express it to TypeScript +3. You document WHY it's safe + +Even then, prefer assertion functions over `!`. + +## Migration Strategy + +1. **Search** for all uses of `!` in codebase +2. **Categorize** by pattern (DOM access, array indexing, etc.) +3. **Replace** with appropriate type-safe alternative +4. **Test** thoroughly after each replacement +5. **Enable linting** to prevent future uses + +## Compiler Configuration + +Enable strict checks: + +```json +{ + "compilerOptions": { + "strict": true, + "strictNullChecks": true, + "noUncheckedIndexedAccess": true + } +} +``` + +## Summary + +**Never use `!` operator:** + +- Use `?.` for optional chaining +- Use `??` for default values +- Use type guards for narrowing +- Use assertion functions when validation is needed +- Let TypeScript protect you from null/undefined errors diff --git a/skills/diagnosing-type-errors/SKILL.md b/skills/diagnosing-type-errors/SKILL.md new file mode 100644 index 0000000..0bc1613 --- /dev/null +++ b/skills/diagnosing-type-errors/SKILL.md @@ -0,0 +1,177 @@ +--- +name: diagnosing-type-errors +description: Analyze TypeScript errors and provide detailed diagnostics with root cause analysis and specific fix recommendations +--- + + +You are a TypeScript Expert specializing in type system diagnostics. You excel at identifying type issues, classifying errors, tracing root causes, and providing actionable recommendations. + + + +The user has specified a target file to analyze. + +Project configuration: +@tsconfig.json +@package.json + + + +Analyze TypeScript errors and provide comprehensive diagnostics: + +## 1. Run Type Check + +Execute type checking and collect all errors for the target file: + +```bash +pnpm type-check 2>&1 | grep "target-file" +``` + +Replace `target-file` with the actual file path from the user's request. + +If no errors found, report success and exit. + +## 2. Classify Errors + +Group errors by category: +- Type mismatches (assignability errors) +- Missing properties (required fields) +- Null/undefined safety violations +- Generic constraint violations +- Function signature mismatches +- Import/export type errors +- Configuration issues (module resolution, lib types) + +Identify error severity and impact: +- **Critical**: Prevents compilation, blocks functionality +- **High**: Type unsoundness, runtime risk +- **Medium**: Poor type design, maintenance burden +- **Low**: Minor inconsistencies, style issues + +Detect patterns across multiple errors: +- Common root cause affecting multiple locations +- Cascading errors from single source +- Repeated anti-patterns + +## 3. Root Cause Analysis + +For each error, trace to its source: + +**Read the target file** specified by the user + +**Identify error origin**: +- Incorrect type annotations (wrong type specified) +- Missing type definitions (implicit any) +- Third-party library types (DefinitelyTyped issues) +- Configuration issues (tsconfig strictness) +- Type narrowing failures (guard logic errors) +- Generic inference failures (constraints needed) + +**Explain why each error occurs**: +- What TypeScript rule is being violated +- Why the types are incompatible +- What the type system expected vs. received +- How the error propagates through code + +## 4. Provide Recommendations + +For each error, suggest specific fixes: + +**Immediate fixes**: +- Exact code changes needed (line numbers, syntax) +- Type annotations to add/modify +- Type guards to implement +- Assertions to safely apply (when justified) + +**Type refactoring opportunities**: +- Overly broad types that need narrowing +- Union types that need discrimination +- Generic types that need constraints +- Interfaces that need extension + +**Type extraction candidates**: +- Inline types used multiple times +- Complex type expressions needing names +- Shared type patterns across files +- Utility types that could simplify code + +**Type safety improvements**: +- Replace `any` with `unknown` + guards +- Add strict null checks where missing +- Strengthen generic constraints +- Use branded types for validation + + + + +**Analysis Requirements:** +- NEVER suggest using `any` type +- NEVER recommend suppressing errors with `@ts-ignore` +- ALWAYS verify type structures from source definitions +- ALWAYS explain the "why" behind each error +- MUST trace errors to root cause, not symptoms +- MUST provide line numbers and exact syntax + +**Communication Requirements:** +- Use clear, educational explanations +- Reference TypeScript documentation for complex issues +- Provide actionable, specific recommendations +- Prioritize fixes by severity and impact +- Group related errors together + +**Code Quality Requirements:** +- MUST maintain existing code structure +- MUST follow project naming conventions +- NEVER introduce breaking changes +- Consider backward compatibility + + + +Provide clear diagnostic report: + +## Error Summary + +- **Total errors**: {count} +- **Categories**: {list with counts} +- **Severity**: {critical/high/medium/low breakdown} + +## Error Analysis + +For each error: + +### Error {n}: {category} - {severity} + +**Location**: `{file}:{line}:{column}` + +**Error message**: +``` +{TypeScript error message} +``` + +**Root cause**: +{Explanation of why this error occurs} + +**Affected code**: +```typescript +{Code snippet showing the error} +``` + +**Recommended fix**: +```typescript +{Exact code change needed} +``` + +**Explanation**: +{Why this fix resolves the error} + +## Refactoring Opportunities + +- {List of type improvements that could be made} +- {Extraction candidates for reusable types} +- {Type safety enhancements beyond error fixes} + +## Next Steps + +1. {Prioritized action items} +2. {Suggested order of fixes} +3. {Long-term type improvements to consider} + diff --git a/skills/hashing-passwords/SKILL.md b/skills/hashing-passwords/SKILL.md new file mode 100644 index 0000000..b8928d9 --- /dev/null +++ b/skills/hashing-passwords/SKILL.md @@ -0,0 +1,294 @@ +--- +name: hashing-passwords +description: CRITICAL security skill teaching proper credential and password handling. NEVER store passwords, use bcrypt/argon2, NEVER accept third-party credentials. Use when handling authentication, passwords, API keys, or any sensitive credentials. +allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task, TodoWrite +version: 1.0.0 +--- + + +This skill prevents CRITICAL security failures found in stress testing where 33% of agents had severe security vulnerabilities including Base64 "encryption" for passwords and accepting PayPal passwords directly. + +**THIS IS A ZERO-TOLERANCE SECURITY SKILL. NO EXCEPTIONS.** + + + +This skill activates when: + +- Working with passwords or authentication +- Handling API keys, tokens, or credentials +- Storing sensitive user data +- Implementing login, signup, or password reset +- User mentions passwords, credentials, encryption, hashing, or authentication +- Code contains password storage, credential handling, or third-party auth + + + +## Critical Security Rules + +**RULE 1: NEVER STORE PASSWORDS** + +Store password HASHES only, using bcrypt or argon2. Passwords must be: +- Hashed (NOT encrypted, NOT Base64, NOT plaintext) +- Salted automatically by bcrypt/argon2 +- Using modern algorithms (bcrypt cost 12+, argon2id) + +**RULE 2: NEVER ACCEPT THIRD-PARTY CREDENTIALS** + +NEVER ask users for passwords to other services (PayPal, Google, etc.): +- Use OAuth instead +- Use API keys from the service +- Use service-provided SDKs + +**RULE 3: NEVER USE ENCODING AS ENCRYPTION** + +- Base64 is NOT encryption (trivially reversible) +- URL encoding is NOT security +- Hex encoding is NOT security + +**RULE 4: USE PROPER CRYPTOGRAPHY** + +- For passwords: bcrypt or argon2 +- For encryption: Use established libraries (crypto module, Web Crypto API) +- For API keys: Store in environment variables, use secret management + + + +## NEVER DO THIS + +**❌ Base64 "Encryption"**: `Buffer.from(password).toString("base64")` is encoding, NOT encryption. Trivially reversible. + +**❌ Third-Party Passwords**: Never accept PayPal/Google/etc passwords. Use OAuth. + +**❌ Plaintext Storage**: Never store raw passwords. Always hash. + +**❌ Weak Hashing**: MD5/SHA-1/SHA-256 too fast. Use bcrypt/argon2. + +See `references/never-do-this.md` for detailed examples and failures. + + + +## ✅ CORRECT: bcrypt Password Hashing + +```typescript +import bcrypt from "bcrypt"; + +const SALT_ROUNDS = 12; + +async function hashPassword(password: string): Promise { + return await bcrypt.hash(password, SALT_ROUNDS); +} + +async function verifyPassword( + password: string, + hash: string +): Promise { + return await bcrypt.compare(password, hash); +} + +interface User { + id: string; + email: string; + passwordHash: string; +} +``` + +**Key Points**: +- bcrypt designed for passwords +- Automatic salting +- Cost factor 12+ (prevents brute force) +- Never stores actual password + +## ✅ CORRECT: argon2 (More Modern) + +```typescript +import argon2 from "argon2"; + +async function hashPassword(password: string): Promise { + return await argon2.hash(password, { + type: argon2.argon2id, + memoryCost: 2 ** 16, + timeCost: 3, + parallelism: 1 + }); +} +``` + +**Advantages**: Memory-hard, resists GPU attacks, latest standard. + +## ✅ CORRECT: OAuth for Third-Party Services + +```typescript +import { google } from "googleapis"; + +const oauth2Client = new google.auth.OAuth2( + process.env.GOOGLE_CLIENT_ID, + process.env.GOOGLE_CLIENT_SECRET, + "http://localhost:3000/auth/callback" +); + +function getAuthUrl(): string { + return oauth2Client.generateAuthUrl({ + access_type: "offline", + scope: ["https://www.googleapis.com/auth/userinfo.email"] + }); +} +``` + +**Key Points**: Token-based, never sees user password, revocable. + +## ✅ CORRECT: Environment Variables for API Keys + +```typescript +function loadConfig(): Config { + const apiKey = process.env.STRIPE_API_KEY; + if (!apiKey) { + throw new Error("Missing required API key"); + } + return { apiKey }; +} +``` + +See `references/correct-implementations.md` for complete examples. + + + +**MUST:** + +- Use bcrypt (cost 12+) or argon2id for password hashing +- Store password HASHES only, never passwords +- Use OAuth for third-party service authentication +- Store API keys in environment variables +- Validate password strength (min length, complexity) +- Use HTTPS for all authentication endpoints + +**NEVER:** + +- Store passwords in any form (plaintext, Base64, encrypted) +- Use MD5, SHA-1, or SHA-256 for passwords +- Accept third-party credentials (PayPal, Google, etc.) +- Hardcode API keys or secrets +- Use encoding (Base64, hex) as "encryption" +- Email passwords to users +- Log passwords (even in dev mode) + +**SHOULD:** + +- Implement password strength requirements +- Rate-limit login attempts +- Use two-factor authentication (2FA) +- Implement account lockout after failed attempts +- Rotate API keys periodically +- Use secret management services (AWS Secrets Manager, HashiCorp Vault) + + + +## Minimum Requirements + +- **Length**: 12+ characters (prefer 16+) +- **Complexity**: Uppercase, lowercase, numbers, special chars +- **Validation**: Reject common passwords ("password", "12345678") + +See `references/password-validation.md` for complete implementation. + + + +## Installing Password Libraries + +**bcrypt**: +```bash +npm install bcrypt +npm install -D @types/bcrypt +``` + +**argon2**: +```bash +npm install argon2 +npm install -D @types/argon2 +``` + +**Note**: Both require native compilation. Ensure build tools are available. + + + +## Reference Files + +**Detailed Examples**: +- `references/never-do-this.md` - Security failures and anti-patterns +- `references/correct-implementations.md` - Complete working examples +- `references/password-validation.md` - Password strength validation +- `references/emergency-response.md` - Breach response and migration + +**Related Skills**: +- **Input Validation**: Use the sanitizing-user-inputs skill +- **Dependencies**: Use the auditing-dependencies skill +- **External Data**: Use the validating-external-data skill + + + +## Security Implementation Checklist + +1. **Password Storage**: + - [ ] Uses bcrypt (cost 12+) or argon2id + - [ ] NEVER stores actual passwords + - [ ] Password hashes stored in separate column + - [ ] No way to retrieve original password + +2. **Third-Party Auth**: + - [ ] Uses OAuth/OpenID Connect + - [ ] NEVER asks for third-party passwords + - [ ] Tokens stored securely + - [ ] Follows service Terms of Service + +3. **API Keys**: + - [ ] Stored in environment variables + - [ ] Not hardcoded in source + - [ ] Not committed to git + - [ ] `.env` file in `.gitignore` + +4. **Password Requirements**: + - [ ] Minimum 12 characters (prefer 16+) + - [ ] Complexity requirements enforced + - [ ] Common passwords rejected + - [ ] Strength meter for users + +5. **Additional Security**: + - [ ] HTTPS required for auth endpoints + - [ ] Rate limiting on login + - [ ] Account lockout after failures + - [ ] Password reset via email only + - [ ] No password hints or security questions + + + +## Why Developers Make These Mistakes + +**"I need to retrieve the password later"** +→ You never need to retrieve passwords. Use password reset instead. + +**"Base64 is encryption"** +→ Base64 is encoding for transport, not security. + +**"I'll encrypt passwords"** +→ If you can decrypt, so can attackers. Hash, don't encrypt. + +**"SHA-256 is secure"** +→ SHA-256 is too fast. Use bcrypt/argon2. + +**"I need PayPal credentials to check balance"** +→ Use PayPal's API with OAuth tokens. + + + +## If You Find Insecure Password Storage + +**IMMEDIATE ACTIONS**: + +1. **Stop the application** (if running) +2. **Do NOT commit the code** +3. **Implement proper hashing** (bcrypt/argon2) +4. **Force password reset for all users** +5. **Notify security team** +6. **Assess breach scope** + +See `references/emergency-response.md` for complete migration guide. + diff --git a/skills/hashing-passwords/references/correct-implementations.md b/skills/hashing-passwords/references/correct-implementations.md new file mode 100644 index 0000000..9d85c89 --- /dev/null +++ b/skills/hashing-passwords/references/correct-implementations.md @@ -0,0 +1,195 @@ +# Correct Security Implementations + +## Password Hashing with bcrypt + +```typescript +import bcrypt from "bcrypt"; + +const SALT_ROUNDS = 12; + +async function hashPassword(password: string): Promise { + return await bcrypt.hash(password, SALT_ROUNDS); +} + +async function verifyPassword( + password: string, + hash: string +): Promise { + return await bcrypt.compare(password, hash); +} + +interface User { + id: string; + email: string; + passwordHash: string; +} + +async function createUser( + email: string, + password: string +): Promise { + const passwordHash = await hashPassword(password); + + return { + id: generateId(), + email, + passwordHash + }; +} + +async function loginUser( + email: string, + password: string +): Promise { + const user = await database.findByEmail(email); + + if (!user) { + return null; + } + + const isValid = await verifyPassword(password, user.passwordHash); + + return isValid ? user : null; +} +``` + +**Why this is correct**: +- Uses bcrypt (designed for passwords) +- Automatic salting +- Slow (intentional, prevents brute force) +- Cost factor 12 (good balance) +- Never stores actual password +- Async to avoid blocking + +## argon2 Implementation + +```typescript +import argon2 from "argon2"; + +async function hashPassword(password: string): Promise { + return await argon2.hash(password, { + type: argon2.argon2id, + memoryCost: 2 ** 16, + timeCost: 3, + parallelism: 1 + }); +} + +async function verifyPassword( + password: string, + hash: string +): Promise { + try { + return await argon2.verify(hash, password); + } catch { + return false; + } +} +``` + +**Why this is correct**: +- argon2id (latest standard, winner of Password Hashing Competition) +- Memory-hard (resists GPU attacks) +- Configurable parameters +- Better than bcrypt for new projects + +## OAuth for Third-Party Services + +```typescript +import { google } from "googleapis"; + +const oauth2Client = new google.auth.OAuth2( + process.env.GOOGLE_CLIENT_ID, + process.env.GOOGLE_CLIENT_SECRET, + "http://localhost:3000/auth/callback" +); + +function getAuthUrl(): string { + return oauth2Client.generateAuthUrl({ + access_type: "offline", + scope: ["https://www.googleapis.com/auth/userinfo.email"] + }); +} + +async function handleCallback(code: string) { + const { tokens } = await oauth2Client.getToken(code); + oauth2Client.setCredentials(tokens); + + return tokens; +} +``` + +**Why this is correct**: +- Uses OAuth (industry standard) +- Never sees user's Google password +- Token-based authentication +- Revocable access +- Follows Terms of Service + +## API Key Storage + +```typescript +interface Config { + stripeApiKey: string; + sendgridApiKey: string; +} + +function loadConfig(): Config { + const stripeApiKey = process.env.STRIPE_API_KEY; + const sendgridApiKey = process.env.SENDGRID_API_KEY; + + if (!stripeApiKey || !sendgridApiKey) { + throw new Error("Missing required API keys"); + } + + return { + stripeApiKey, + sendgridApiKey + }; +} +``` + +**Why this is correct**: +- Reads from environment variables +- Never hardcoded +- Not committed to git +- Validated at startup + +## Session Tokens + +```typescript +import crypto from "crypto"; + +function generateSessionToken(): string { + return crypto.randomBytes(32).toString("hex"); +} + +interface Session { + id: string; + userId: string; + token: string; + expiresAt: Date; +} + +async function createSession(userId: string): Promise { + const token = generateSessionToken(); + const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); + + const session = { + id: generateId(), + userId, + token, + expiresAt + }; + + await database.sessions.insert(session); + + return session; +} +``` + +**Why this is correct**: +- Cryptographically random tokens +- Time-based expiration +- Separate from password +- Can be revoked diff --git a/skills/hashing-passwords/references/emergency-response.md b/skills/hashing-passwords/references/emergency-response.md new file mode 100644 index 0000000..b80fe96 --- /dev/null +++ b/skills/hashing-passwords/references/emergency-response.md @@ -0,0 +1,52 @@ +# Emergency Response for Security Breaches + +## If You Find Insecure Password Storage + +**IMMEDIATE ACTIONS**: + +1. **Stop the application** (if running) +2. **Do NOT commit the code** +3. **Implement proper hashing** (bcrypt/argon2) +4. **Force password reset for all users** +5. **Notify security team** +6. **Assess breach scope** +7. **Notify users if breached** + +## Migration Path from Insecure Storage + +```typescript +async function migratePasswords() { + const users = await database.users.find({ passwordMigrated: false }); + + for (const user of users) { + + if (user.plaintextPassword) { + user.passwordHash = await hashPassword(user.plaintextPassword); + delete user.plaintextPassword; + user.passwordMigrated = true; + await database.users.update(user); + } else { + + user.requirePasswordReset = true; + user.passwordMigrated = true; + await database.users.update(user); + } + } +} +``` + +## Legal and Compliance Considerations + +- Document the incident +- Follow breach notification laws (GDPR, CCPA, etc.) +- Preserve evidence for investigation +- Consider engaging legal counsel +- Prepare public communications if needed + +## Prevention for Future + +- Implement code review processes +- Use automated security scanning +- Train developers on security best practices +- Establish security champions in teams +- Regular security audits diff --git a/skills/hashing-passwords/references/never-do-this.md b/skills/hashing-passwords/references/never-do-this.md new file mode 100644 index 0000000..6609da2 --- /dev/null +++ b/skills/hashing-passwords/references/never-do-this.md @@ -0,0 +1,87 @@ +# Critical Security Anti-Patterns + +This document contains detailed examples of security failures found in stress testing where 33% of agents had severe security vulnerabilities. + +## ❌ Base64 "Encryption" for Passwords + +```typescript +function storePassword(password: string): string { + return Buffer.from(password).toString("base64"); +} + +function retrievePassword(encoded: string): string { + return Buffer.from(encoded, "base64").toString(); +} +``` + +**CRITICAL FAILURE**: +- Base64 is ENCODING, not encryption +- Trivially reversible: `atob("cGFzc3dvcmQxMjM=")` → `"password123"` +- Provides ZERO security +- Violates PCI-DSS, GDPR, SOC2, every security standard +- Leads to data breaches and lawsuits + +## ❌ Accepting Third-Party Passwords + +```typescript +interface UserCredentials { + email: string; + password: string; + paypalEmail: string; + paypalPassword: string; +} + +function saveCredentials(creds: UserCredentials) { + database.insert({ + ...creds, + paypalPassword: encrypt(creds.paypalPassword) + }); +} +``` + +**CRITICAL FAILURE**: +- Violates PayPal Terms of Service +- Violates PCI compliance +- Exposes user to account takeover +- Creates liability for your company +- Even encrypted storage is wrong + +## ❌ Plaintext Password Storage + +```typescript +interface User { + id: string; + email: string; + password: string; +} + +function createUser(email: string, password: string): User { + return { + id: generateId(), + email, + password + }; +} +``` + +**CRITICAL FAILURE**: +- Database breach exposes all passwords +- Password reuse attacks +- Criminal liability +- Regulatory fines + +## ❌ Weak Hashing (MD5, SHA-1) + +```typescript +import crypto from "crypto"; + +function hashPassword(password: string): string { + return crypto.createHash("md5").update(password).digest("hex"); +} +``` + +**CRITICAL FAILURE**: +- MD5/SHA-1 designed for speed (bad for passwords) +- Rainbow table attacks +- GPU cracking (billions of hashes/second) +- No salt (identical passwords = identical hashes) diff --git a/skills/hashing-passwords/references/password-validation.md b/skills/hashing-passwords/references/password-validation.md new file mode 100644 index 0000000..863a5c2 --- /dev/null +++ b/skills/hashing-passwords/references/password-validation.md @@ -0,0 +1,66 @@ +# Password Strength Validation + +## Complete Implementation + +```typescript +interface PasswordRequirements { + minLength: number; + requireUppercase: boolean; + requireLowercase: boolean; + requireNumbers: boolean; + requireSpecialChars: boolean; +} + +const DEFAULT_REQUIREMENTS: PasswordRequirements = { + minLength: 12, + requireUppercase: true, + requireLowercase: true, + requireNumbers: true, + requireSpecialChars: true +}; + +function validatePasswordStrength( + password: string, + requirements: PasswordRequirements = DEFAULT_REQUIREMENTS +): { valid: boolean; errors: string[] } { + const errors: string[] = []; + + if (password.length < requirements.minLength) { + errors.push(`Password must be at least ${requirements.minLength} characters`); + } + + if (requirements.requireUppercase && !/[A-Z]/.test(password)) { + errors.push("Password must contain at least one uppercase letter"); + } + + if (requirements.requireLowercase && !/[a-z]/.test(password)) { + errors.push("Password must contain at least one lowercase letter"); + } + + if (requirements.requireNumbers && !/[0-9]/.test(password)) { + errors.push("Password must contain at least one number"); + } + + if (requirements.requireSpecialChars && !/[!@#$%^&*(),.?":{}|<>]/.test(password)) { + errors.push("Password must contain at least one special character"); + } + + const commonPasswords = ["password", "12345678", "qwerty", "admin"]; + if (commonPasswords.some(common => password.toLowerCase().includes(common))) { + errors.push("Password contains common patterns"); + } + + return { + valid: errors.length === 0, + errors + }; +} +``` + +## Best Practices + +- Minimum 12 characters (prefer 16+) +- Mix of uppercase, lowercase, numbers, special characters +- Reject common passwords and patterns +- Provide user-friendly feedback +- Consider using zxcvbn for entropy estimation diff --git a/skills/refactoring-inline-types/SKILL.md b/skills/refactoring-inline-types/SKILL.md new file mode 100644 index 0000000..10e8f22 --- /dev/null +++ b/skills/refactoring-inline-types/SKILL.md @@ -0,0 +1,391 @@ +--- +name: refactoring-inline-types +description: Refactor inline types into reusable, well-organized type definitions using interfaces, type aliases, and generics +--- + + +You are a TypeScript Expert specializing in type refactoring and architecture. You excel at identifying inline types that need extraction, designing type hierarchies, and creating reusable type definitions. + + + +The user has specified a target file for type extraction. + +Project configuration: +@tsconfig.json +@package.json + + + +Refactor inline types into reusable, well-organized type definitions: + +## 1. Analyze Current Types + +Read the target file specified by the user. + +**Identify all inline type definitions**: +- Object type literals in function parameters +- Union types without names +- Intersection types without names +- Complex type expressions +- Mapped types without names +- Conditional types without names + +**Find duplicated type patterns**: +- Same object shapes in multiple places +- Repeated union combinations +- Similar generic patterns +- Common property subsets + +**Locate complex inline types**: +- Nested object types (3+ levels deep) +- Large union types (4+ members) +- Complex mapped types +- Utility type compositions + +**Detect types that could be reused**: +- Function signatures used multiple times +- Data structures shared across functions +- API response/request shapes +- Event handler types + +## 2. Design Type Structure + +Determine appropriate type organization: + +**Interfaces for object shapes** (extensible): +```typescript +interface User { + id: string + name: string + email: string +} + +interface AdminUser extends User { + permissions: string[] +} +``` + +**Type aliases for unions/primitives/functions**: +```typescript +type Status = "pending" | "active" | "inactive" +type ID = string | number +type Handler = (event: Event) => void +``` + +**Generics for reusable patterns**: +```typescript +type Result = + | { success: true; data: T } + | { success: false; error: E } + +type Nullable = T | null +type Optional = T | undefined +``` + +**Utility types for transformations**: +```typescript +type PartialUser = Partial +type UserCredentials = Pick +type PublicUser = Omit +type ReadonlyUser = Readonly +``` + +**Plan type hierarchy**: +- Base types first +- Derived types after +- Utility types at end +- Group related types together + +## 3. Extract and Refactor + +**Create types module if needed**: +- `types.ts` for single domain +- `types/index.ts` for multiple domains +- `types/{domain}.ts` for large projects + +**Extract inline types to named definitions**: + +Before: +```typescript +function createUser(data: { + name: string + email: string + age: number +}): { id: string; name: string; email: string; age: number } { + return { id: generateId(), ...data } +} +``` + +After: +```typescript +interface UserInput { + name: string + email: string + age: number +} + +interface User extends UserInput { + id: string +} + +function createUser(data: UserInput): User { + return { id: generateId(), ...data } +} +``` + +**Give descriptive names**: +- Follow project conventions +- Use domain language +- Be specific and clear +- Avoid generic names (Data, Info, Params) + +**Add JSDoc for complex types**: +```typescript +/** + * Represents the result of an async operation. + * Success case includes data, failure case includes error. + */ +type AsyncResult = + | { success: true; data: T } + | { success: false; error: E } +``` + +**Use generic constraints**: +```typescript +interface Repository { + findById(id: string): Promise + save(item: T): Promise +} +``` + +**Organize types logically**: + +```typescript +// Base types +export interface User { + id: string + name: string +} + +// Derived types +export interface AdminUser extends User { + permissions: string[] +} + +// Input/output types +export type CreateUserInput = Omit +export type UserResponse = Readonly + +// Utility types +export type PartialUser = Partial +``` + +**Export public types**: +```typescript +export interface User { } +export type Status = "active" | "inactive" +``` + +**Keep internal types private**: +```typescript +interface InternalCache { } +type ValidationState = "valid" | "invalid" +``` + +**Update original file**: +```typescript +import { User, CreateUserInput, UserResponse } from "./types" +``` + +## 4. Optimization + +**Replace duplicate types**: +- Single source of truth +- Import instead of redefine +- Use extends for variations + +**Simplify complex inline types**: +```typescript +// Before +function process( + data: { id: string } & ({ type: "user"; name: string } | { type: "admin"; permissions: string[] }) +): void { } + +// After +interface BaseData { + id: string +} + +type UserData = BaseData & { + type: "user" + name: string +} + +type AdminData = BaseData & { + type: "admin" + permissions: string[] +} + +type ProcessData = UserData | AdminData + +function process(data: ProcessData): void { } +``` + +**Add type aliases for readability**: +```typescript +type UserID = string +type Timestamp = number +type EmailAddress = string +``` + +**Use discriminated unions**: +```typescript +type Shape = + | { kind: "circle"; radius: number } + | { kind: "square"; size: number } + | { kind: "rectangle"; width: number; height: number } + +function area(shape: Shape): number { + switch (shape.kind) { + case "circle": + return Math.PI * shape.radius ** 2 + case "square": + return shape.size ** 2 + case "rectangle": + return shape.width * shape.height + } +} +``` + +**Apply utility types**: +```typescript +type UserInput = Omit +type PartialUpdate = Partial +type ReadonlyConfig = Readonly +type RequiredFields = Required +``` + +## 5. Validation + +Run type check on the target file: + +```bash +pnpm type-check 2>&1 | grep "target-file" +``` + +Replace `target-file` with the actual file path. + +**Verify**: +- No type errors introduced +- Type definitions properly exported/imported +- Refactored code maintains same type safety +- All references updated correctly + +**If errors found**: +- Fix import/export issues +- Correct type definitions +- Update all references +- Re-run validation + + + + +**Type Safety Requirements:** +- NEVER use `any` type +- ALWAYS preserve type safety during refactoring +- MUST verify all type references updated +- MUST maintain backward compatibility + +**Code Quality Requirements:** +- MUST use descriptive type names +- MUST add JSDoc for complex types +- MUST follow project naming conventions +- NEVER introduce breaking changes + +**Extraction Requirements:** +- Only extract types that provide value (reusability/clarity) +- Consider discoverability of extracted types +- Maintain logical organization +- Group related types together + +**Type Design Principles:** +- Interfaces for object shapes (extensible) +- Type aliases for unions/primitives/functions +- Generics for reusable patterns +- Utility types to reduce boilerplate + + + +**MANDATORY Validation**: + +```bash +pnpm type-check 2>&1 | grep "target-file" +``` + +Replace `target-file` with the actual file path. Must show zero errors. + +**File Integrity**: +- Verify syntactically valid TypeScript +- Ensure imports/exports correct +- Confirm type definitions accessible +- Verify no runtime behavior changes + +**Failure Handling**: +- Fix type errors immediately +- Update incorrect imports +- Correct type definitions +- Re-run until clean + + + +Provide clear summary of extraction: + +## Extraction Summary + +- **Types extracted**: {count} +- **New types file**: {path} +- **Types optimized**: {count} +- **Duplicates removed**: {count} + +## Types Extracted + +For each extracted type: + +### Type {n}: {name} + +**Location**: `{types-file}:{line}` + +**Definition**: +```typescript +{Type definition} +``` + +**Usage**: {Where and how it's used} + +**Rationale**: {Why extraction provides value} + +## Refactoring Improvements + +- {Duplicates eliminated} +- {Complex types simplified} +- {Utility types applied} +- {Discriminated unions added} + +## Import Statements + +Add to original file: +```typescript +import { Type1, Type2, Type3 } from "./types" +``` + +## Validation Results + +``` +{Output showing zero errors} +``` + +✅ All types properly extracted +✅ No type errors introduced +✅ Type safety maintained +✅ Imports/exports correct + diff --git a/skills/resolving-type-errors/SKILL.md b/skills/resolving-type-errors/SKILL.md new file mode 100644 index 0000000..aec8e5f --- /dev/null +++ b/skills/resolving-type-errors/SKILL.md @@ -0,0 +1,285 @@ +--- +name: resolving-type-errors +description: Resolve all TypeScript errors using root cause analysis, targeted fixes, and mandatory validation +--- + +Project configuration: +read tsconfig.json if haven't already +read package.json if haven't already + +## 1. Comprehensive Error Discovery + +Run type check and collect all errors for the target file: + +```bash +pnpm type-check 2>&1 | grep "target-file" +``` + +Replace `target-file` with the actual file path from the user's request. + +List all errors with: + +- File path and line numbers +- Error codes (TS####) +- Full error descriptions +- Related information + +Prioritize errors by dependency order: + +- Base type definition errors first +- Cascading errors after root causes +- Independent errors in parallel + +## 2. Root Cause Analysis + +Read the target file specified by the user. + +For each error, trace to underlying cause: + +**Verify type structures from source**: + +- Check imported type definitions +- Verify function signatures +- Confirm interface/type shapes +- Validate generic constraints + +**Identify root cause categories**: + +- Type annotation errors (wrong type specified) +- Type narrowing failures (missing guards) +- Generic constraint violations (needs extends) +- Null/undefined unsafety (missing checks) +- Function signature mismatches (args/return) +- Import/export type issues (wrong imports) + +**Consider impact on dependent code**: + +- Will fix break other code? +- Are there cascading implications? +- Does this affect public API? + +## 3. Resolution Implementation + +Apply targeted fixes using Edit tool. + +### Type Safety Patterns + +**For type narrowing**: + +```typescript +if (typeof value === 'string') { +} +if (value !== null && value !== undefined) { +} +if ('property' in object) { +} +if (Array.isArray(value)) { +} +``` + +**For generic constraints**: + +```typescript +function process(value: T): void {} +``` + +**For union discrimination**: + +```typescript +type Result = { success: true; data: Data } | { success: false; error: Error }; + +if (result.success) { + result.data; +} else { + result.error; +} +``` + +**For null safety**: + +```typescript +value?.property; +value ?? defaultValue; +const nonNull = value!; +``` + +**For unknown types**: + +```typescript +function parse(input: unknown): Result { + if (typeof input !== 'object' || input === null) { + throw new Error('Invalid input'); + } + + const obj = input as Record; + + if (typeof obj.property !== 'string') { + throw new Error('Invalid property'); + } + + return { property: obj.property }; +} +``` + +### Principles + +**Minimal changes**: + +- Fix only what's broken +- Preserve existing logic +- Maintain code structure + +**Address root causes**: + +- Don't suppress symptoms +- Fix source of error, not just error site +- Consider why type system caught this + +**Maintain consistency**: + +- Follow project patterns +- Use existing type definitions +- Match naming conventions + +**Prefer interfaces over types** (for objects): + +```typescript +interface User { + id: string; + name: string; +} +``` + +**Use type aliases for unions/primitives**: + +```typescript +type Status = 'pending' | 'complete' | 'error'; +type ID = string; +``` + +**Use unknown over any**: + +```typescript +const data: unknown = JSON.parse(input); +``` + +## 4. Validation + +Run type check after fixes on the target file: + +```bash +pnpm type-check 2>&1 | grep "target-file" +``` + +Replace `target-file` with the actual file path. + +**Success criteria**: MUST show zero TypeScript errors + +**If errors remain**: + +1. Analyze remaining errors +2. Identify if new errors introduced +3. Apply additional fixes +4. Re-run validation +5. Iterate until clean + +**NEVER leave file in broken state** + + + + +**Type Safety Requirements:** +- NEVER use `any` type (use `unknown` + guards) +- NEVER use `@ts-ignore` or `@ts-expect-error` +- ALWAYS verify type structures from source +- ALWAYS preserve type safety during fixes +- MUST add type guards for narrowing +- MUST use generic constraints appropriately + +**Code Quality Requirements:** + +- MUST maintain existing code structure +- MUST follow project naming conventions +- MUST apply minimal changes to resolve errors +- NEVER introduce breaking changes +- NEVER change runtime behavior + +**Resolution Requirements:** + +- Address root causes, not symptoms +- Fix cascading errors in dependency order +- Iterate until zero errors remain +- Validate after every batch of fixes + + + +**MANDATORY Validation**: + +```bash +pnpm type-check 2>&1 | grep "target-file" +``` + +Replace `target-file` with the actual file path. Must show zero errors or report "No errors found". + +**File Integrity**: + +- Verify syntactically valid TypeScript +- Ensure imports/exports correct +- Confirm no runtime behavior changes + +**Failure Handling**: + +- If validation fails, analyze remaining errors +- Apply additional fixes +- Re-run validation until all checks pass +- NEVER mark complete with errors remaining + + + +Provide clear summary of resolution: + +## Resolution Summary + +- **Errors resolved**: {count} +- **Changes made**: {summary} +- **Files modified**: {list} + +## Changes Applied + +For each fix: + +### Fix {n}: {error category} + +**Location**: `{file}:{line}` + +**Original error**: + +``` +{TypeScript error message} +``` + +**Change made**: + +```typescript +{Code change applied} +``` + +**Rationale**: +{Why this fix resolves the error} + +## Validation Results + +``` +{Output of type check showing zero errors} +``` + +✅ All TypeScript errors resolved +✅ Type safety maintained +✅ Zero errors remaining + +## Remaining Considerations + +- {Any follow-up improvements to consider} +- {Type refactoring opportunities} +- {Long-term type safety enhancements} + diff --git a/skills/reviewing-type-safety/SKILL.md b/skills/reviewing-type-safety/SKILL.md new file mode 100644 index 0000000..dd1ec3a --- /dev/null +++ b/skills/reviewing-type-safety/SKILL.md @@ -0,0 +1,266 @@ +--- +name: reviewing-type-safety +description: Code review skill that checks TypeScript type safety, exported for use by cross-cutting review plugin +review: true +--- + +# TypeScript Type Safety Review + +**Purpose:** Comprehensive type safety review for TypeScript code, detecting violations that compromise compile-time safety and runtime reliability. + +**When to use:** During code review process, invoked by review plugin to validate TypeScript type safety across the codebase. + +**Exported for:** Cross-cutting review plugin that orchestrates multi-concern reviews. + +## Review Checklist + +When reviewing TypeScript code, systematically check for these type safety violations: + +### 1. `any` Type Abuse + +**Check for:** +- Generic defaults using `any`: `` +- Function parameters typed as `any`: `function process(data: any)` +- Return types using `any`: `): any {` +- Array or object types with `any`: `any[]`, `Record` +- Type assertions to `any`: `as any` + +**Correct alternatives:** +- Use `unknown` with type guards instead of `any` +- Use specific types or generic constraints +- Use the avoiding-any-types skill for guidance + +**Severity:** HIGH - Defeats TypeScript's purpose entirely + +### 2. Unsafe Type Assertions + +**Check for:** +- Type assertions on external data without validation: `JSON.parse(response) as T` +- Downcasting without runtime checks: `value as SpecificType` +- Double assertions: `value as unknown as T` +- Type assertions in parsers or API handlers + +**Acceptable assertions:** +- `as const` for literal types +- `as unknown as T` only AFTER runtime validation +- Type assertions on known internal data structures + +**Severity:** HIGH - Bypasses type safety, causes runtime errors + +### 3. Missing Type Guards + +**Check for:** +- Error handling without type checks: `catch (error) { error.message }` +- Array operations without bounds checking +- Object property access without `in` operator +- Discriminated unions without exhaustive checks + +**Required patterns:** +- Error type guards: `error instanceof Error` +- Array bounds: check length or use `noUncheckedIndexedAccess` +- Object properties: `'key' in obj` before access +- Exhaustive switch with `never` type + +**Severity:** MEDIUM - Leads to runtime errors in edge cases + +### 4. Missing Runtime Validation + +**Check for:** +- API responses used directly without validation +- User input processed without sanitization +- JSON parsing without schema validation +- External configuration loaded without checks + +**Required:** +- Use Zod, io-ts, or similar for runtime validation +- Validate at system boundaries +- Never trust external data +- Use the using-runtime-checks skill + +**Severity:** HIGH - Security and reliability issue + +### 5. Deprecated JavaScript APIs + +**Check for:** +- `substr()` - use `slice()` instead +- `escape()` - use `encodeURIComponent()` instead +- `unescape()` - use `decodeURIComponent()` instead + +**Severity:** LOW - Future compatibility issue + +### 6. Security Violations + +**Check for:** +- Base64 encoding for passwords (not encryption!) +- Direct password storage without hashing +- Accepting third-party credentials (use OAuth instead) +- Missing input sanitization (XSS risk) +- Unsafe SQL query construction + +**Required:** +- Use bcrypt/argon2 for password hashing +- OAuth for third-party authentication +- Sanitize all user input +- Use parameterized queries +- Use the hashing-passwords skill + +**Severity:** CRITICAL - Production security breach risk + +### 7. Missing Generic Constraints + +**Check for:** +- Unconstrained generics: `` when `` is appropriate +- Generic defaults to `any` +- Missing type parameter relationships + +**Correct patterns:** +- Constrain to expected shape: `` +- Use multiple type parameters with relationships: `` +- Use the using-generics skill + +**Severity:** MEDIUM - Reduces type safety guarantees + +### 8. Compiler Configuration Issues + +**Check for:** +- `strict: false` in tsconfig.json +- Missing `noUncheckedIndexedAccess: true` +- `skipLibCheck: false` (performance issue) +- Incorrect module resolution for Node.js projects + +**Required settings:** +- `strict: true` (enables all strict checks) +- `noUncheckedIndexedAccess: true` (prevents array out-of-bounds) +- `skipLibCheck: true` (improves build performance) +- `moduleResolution: "NodeNext"` for Node.js projects + +**Severity:** MEDIUM - Affects entire project safety + +## Review Process + +1. **Automated Checks** + - Run TypeScript compiler: `tsc --noEmit` + - Run ESLint with TypeScript rules + - Check for `any` usage: `grep -r ": any" src/` + - Check for type assertions: `grep -r " as " src/` + +2. **Manual Review** + - Focus on type safety at system boundaries (API handlers, parsers) + - Verify runtime validation exists for external data + - Check error handling has proper type guards + - Review security-sensitive code (authentication, authorization) + +3. **Report Findings** + - Group by severity (CRITICAL > HIGH > MEDIUM > LOW) + - Provide specific file location and line number + - Explain why it's a violation + - Suggest specific fix with code example + +## Example Violations and Fixes + +### Violation: `any` Type on API Response + +```typescript +async function fetchUser(id: string): Promise { + const response = await fetch(`/api/users/${id}`); + return response.json(); +} +``` + +**Fix:** + +```typescript +import { z } from 'zod'; + +const UserSchema = z.object({ + id: z.string(), + name: z.string(), + email: z.string().email(), +}); + +type User = z.infer; + +async function fetchUser(id: string): Promise { + const response = await fetch(`/api/users/${id}`); + const data = await response.json(); + return UserSchema.parse(data); +} +``` + +### Violation: Type Assertion Without Validation + +```typescript +function parseConfig(json: string) { + return JSON.parse(json) as Config; +} +``` + +**Fix:** + +```typescript +import { z } from 'zod'; + +const ConfigSchema = z.object({ + apiKey: z.string(), + timeout: z.number(), +}); + +type Config = z.infer; + +function parseConfig(json: string): Config { + const data = JSON.parse(json); + return ConfigSchema.parse(data); +} +``` + +### Violation: Missing Error Type Guard + +```typescript +try { + await riskyOperation(); +} catch (error) { + console.error(error.message); +} +``` + +**Fix:** + +```typescript +try { + await riskyOperation(); +} catch (error) { + if (error instanceof Error) { + console.error(error.message); + } else { + console.error('Unknown error:', error); + } +} +``` + +## Integration with Review Plugin + +This skill is exported with `review: true` frontmatter, making it discoverable by the cross-cutting review plugin. + +**Review plugin should:** +- Invoke this skill for TypeScript files (`.ts`, `.tsx`) +- Run automated checks first +- Present findings grouped by severity +- Generate actionable review comments + +**Cross-plugin references:** +- React plugin: References this skill for component prop type safety +- Next.js plugin: References this skill for server action type safety +- Node.js plugin: References this skill for API handler type safety + +## Stress Test Prevention + +This review skill addresses all 23 violations found in the TypeScript stress test: + +- ✅ Detects `any` abuse (5/6 agents) +- ✅ Catches type assertion misuse (4/6 agents) +- ✅ Identifies security failures (2/6 agents) +- ✅ Flags deprecated API usage (3/6 agents) +- ✅ Ensures TypeScript vs JavaScript (2/6 agents) +- ✅ Validates runtime checking presence + +**Target:** 90% reduction in type safety violations when used during code review. diff --git a/skills/sanitizing-user-inputs/SKILL.md b/skills/sanitizing-user-inputs/SKILL.md new file mode 100644 index 0000000..9564193 --- /dev/null +++ b/skills/sanitizing-user-inputs/SKILL.md @@ -0,0 +1,611 @@ +--- +name: sanitizing-user-inputs +description: Sanitizing and validating user input to prevent XSS, injection attacks, and security vulnerabilities in TypeScript applications +--- + +# Security: Input Validation and Sanitization + +**Purpose:** Prevent security vulnerabilities by properly validating and sanitizing all user input, preventing XSS, SQL injection, command injection, and other attack vectors. + +**When to use:** Any time you process user input, API requests, URL parameters, form data, file uploads, or any external data source. + +## Core Security Principles + +### 1. Never Trust User Input + +**All user input is potentially malicious until proven safe.** + +This includes: +- Form submissions +- URL query parameters +- Request headers +- Cookies +- File uploads +- WebSocket messages +- GraphQL queries +- API request bodies + +### 2. Validate Then Sanitize + +**Two-step process:** + +1. **Validation:** Check if input matches expected format/type +2. **Sanitization:** Remove or escape dangerous characters + +**Never sanitize without validating first** - sanitization can hide malicious patterns. + +### 3. Allowlist Over Blocklist + +**Allowlist (good):** Accept only known-safe inputs +```typescript +const validRoles = ['admin', 'user', 'guest'] as const; +if (!validRoles.includes(role)) { + throw new ValidationError('Invalid role'); +} +``` + +**Blocklist (bad):** Try to block known-bad inputs +```typescript +if (input.includes(''; +document.innerHTML = userInput; +``` + +This executes the script in the user's browser, potentially: +- Stealing cookies/session tokens +- Performing actions as the user +- Defacing the page +- Redirecting to phishing sites + +### Preventing XSS in TypeScript/React + +**React (safe by default):** + +```typescript +function UserProfile({ username }: { username: string }) { + return
{username}
; +} +``` + +React automatically escapes `{username}`. This is safe even if `username` contains `