hunter-party-ts/security-hunter-ts/SKILL.md
Audit TypeScript code for security vulnerabilities — hardcoded secrets, injection risks, missing input validation at trust boundaries, insecure defaults, auth gaps, sensitive data exposure, and unsafe patterns like eval or innerHTML. Use when: reviewing TypeScript code before deployment, auditing trust boundaries, preparing for a security review, onboarding third-party integrations, or hardening an application.
npx skillsauth add skyosev/agent-skills security-hunter-tsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Audit code for security vulnerabilities — places where untrusted input flows into sensitive operations without validation, secrets are embedded in source, defaults are permissive, or auth checks are missing. The goal: every trust boundary validates its inputs, no secrets live in code, and the principle of least privilege is applied throughout.
Trust boundaries are the perimeter. Every point where external data enters the system — HTTP requests, file uploads, environment variables, database reads, third-party API responses — is a trust boundary. Input must be validated, sanitized, or escaped before it flows into operations that could be exploited.
Secrets belong in the environment, never in code. API keys, passwords, tokens, certificates, and connection strings must come from environment variables, secret managers, or encrypted config — never from source files, comments, or committed config.
Default to deny. Defaults should be restrictive: CORS origins explicit not *, auth required not optional,
permissions minimal not broad. Permissive defaults are the most common class of security misconfiguration.
Defense in depth. No single validation layer is sufficient. Validate at the boundary, escape at the output, enforce at the data layer. If one layer fails, another should catch the exploit.
Fail closed. When auth/authz checks fail or throw, the result should be denial, not access. Error paths must not bypass security controls.
API keys, passwords, tokens, or connection strings embedded directly in source code.
Signals:
sk_live_, AKIA, ghp_, Bearer , password=postgres://user:pass@host.env files or secret-bearing config committed to the repoAction: Move to environment variables or a secret manager. Add the pattern to .gitignore. Rotate any committed
secret immediately — it's already compromised if the repo was ever shared.
Untrusted input concatenated into strings that are interpreted as code, queries, or commands.
Signals:
exec(), spawn(), system() with user-controlled argumentsfs.readFile(), path.join(), or URL construction without sanitizationnew RegExp() (ReDoS risk)eval(), Function(), setTimeout(string) with dynamic inputAction: Use parameterized queries, allowlists, path canonicalization, template auto-escaping, or regex escaping. Never concatenate untrusted input into interpreted strings.
Endpoints, handlers, or integration points that accept external data without schema validation or sanitization.
Signals:
req.body or req.query without validation (no Zod, Joi, class-validator, etc.)Action: Add schema validation at every trust boundary. Validate type, format, range, and length. Reject invalid input explicitly — don't coerce or default.
Permissive defaults that weaken security posture when not explicitly overridden.
Signals:
Access-Control-Allow-Origin: * or credentials: true with wildcard originSecure, HttpOnly, or SameSite attributesrejectUnauthorized: false)Action: Restrict defaults. Require explicit opt-in for permissive settings with justification.
Missing or inconsistent auth checks that allow unauthorized access or privilege escalation.
Signals:
Action: Ensure every endpoint has explicit auth/authz. Check authorization server-side against the session, not client-provided claims. Use bcrypt/scrypt/argon2 for password hashing.
Personal data, secrets, or internal details leaked through logs, errors, responses, or storage.
Signals:
Action: Strip sensitive fields from logs and error responses. Filter API responses to only required fields. Use encrypted/httpOnly storage for sensitive client-side data.
Package dependencies with known vulnerabilities, unvetted postinstall scripts, or lockfile integrity issues.
Signals:
package.json dependencies pinned to * or very wide ranges without a lockfilepackage-lock.json, pnpm-lock.yaml, yarn.lock) not committed to the repopostinstall, preinstall, or install lifecycle scripts that execute arbitrary codenpm audit or equivalent)Action: Commit the lockfile. Pin dependency ranges. Run npm audit and address critical/high findings. Review
lifecycle scripts in new dependencies before adding them.
Language-level patterns that create exploitable conditions.
Signals:
innerHTML, dangerouslySetInnerHTML, document.write() with dynamic content (XSS)Object.assign() or spread from untrusted input without allowlisting properties (prototype pollution)JSON.parse() of untrusted input without a try/catch or schema validation__proto__ or constructor.prototype accessible via user-controlled object keys (prototype pollution)Action: Use textContent instead of innerHTML, allowlist properties on untrusted objects, validate schemas
after parsing, use crypto.timingSafeEqual() for secret comparison. Use Object.create(null) for lookup maps
populated from user input to prevent prototype pollution.
Leveraging (or failing to leverage) TypeScript's type system for security enforcement.
Signals:
as any or as unknown as T bypassing validation at trust boundaries — input flows from any into typed
operations without runtime validationstring for SQL fragments, HTML content, URLs,
file paths) — allows accidental mixing of validated and unvalidated valuestypeof / instanceof checks that should narrow
to validated types but are plain boolean checks insteadstrict: false or strictNullChecks: false in tsconfig.json — permissive compiler settings that weaken
security invariants@ts-ignore or @ts-expect-error suppressing errors in security-sensitive code (auth, validation, crypto)Action: Use branded types for security-sensitive values (type SafeHtml = string & { __brand: 'SafeHtml' }).
Enable strict: true in tsconfig.json. Use assertion functions or type guards (asserts input is ValidatedRequest)
at trust boundaries. Derive downstream types from validation schemas (z.infer<typeof schema>).
Missing protections specific to GraphQL APIs and WebSocket connections.
Signals:
Action: Add query depth and complexity limits (e.g., graphql-depth-limit, graphql-query-complexity).
Disable introspection in production. Validate WebSocket origin headers. Authenticate WebSocket connections on
handshake. Rate-limit WebSocket messages. Validate all user input from WebSocket messages with schema validation.
main/master)BASE=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo main)
SCOPE=$(git diff --name-only $(git merge-base HEAD $BASE)...HEAD)
Constrain all subsequent scans to the resolved surface.EXCLUDE='--glob !**/node_modules/** --glob !**/dist/** --glob !**/*.test.* --glob !**/*.spec.*'
# Hardcoded secrets (common patterns)
rg --pcre2 '(sk_live_|AKIA|ghp_|password\s*=\s*["\x27]|secret\s*=\s*["\x27])' $EXCLUDE
rg --pcre2 '(postgres|mysql|mongodb)://\w+:\w+@' $EXCLUDE
# Injection: eval, exec, spawn, Function constructor
rg '\beval\s*\(|\bexec\s*\(|\bspawn\s*\(|\bFunction\s*\(' --type ts $EXCLUDE
# TypeScript-specific security
rg 'as any|as unknown' --type ts $EXCLUDE
rg 'strict.*false|strictNullChecks.*false' --glob 'tsconfig*.json'
rg '@ts-ignore|@ts-expect-error' --type ts $EXCLUDE
# GraphQL security
rg 'introspection|depthLimit|queryComplexity' --type ts $EXCLUDE
# WebSocket security
rg 'WebSocket|socket\.io|ws\.' --type ts $EXCLUDE
# Injection: string concatenation in queries
rg --pcre2 '(query|sql|execute)\s*\(\s*[`"\x27].*\$\{' --type ts $EXCLUDE
# XSS: innerHTML, dangerouslySetInnerHTML, document.write
rg 'innerHTML|dangerouslySetInnerHTML|document\.write' --type ts $EXCLUDE
# Insecure config
rg --pcre2 'Access-Control-Allow-Origin.*\*|rejectUnauthorized.*false|secure.*false' $EXCLUDE
# Missing auth (route definitions without auth middleware — project-specific, adapt pattern)
rg '(app|router)\.(get|post|put|patch|delete)\s*\(' --type ts $EXCLUDE
# Logging of sensitive data
rg --pcre2 'console\.(log|info|debug).*\b(password|token|secret|authorization|cookie)\b' -i $EXCLUDE
# Regex from user input
rg 'new RegExp\(' --type ts $EXCLUDE
# Dependency risks: lifecycle scripts in package.json
rg '"(pre|post)?install"' --glob 'package.json'
For each trust boundary found in Phase 1:
eval, exec, a database query, or DOM manipulation?Save as YYYY-MM-DD-security-hunter-audit-{$LLM-name}.md in the project's docs folder (or project root if no docs folder exists).
# Security Hunter Audit — {date}
## Scope
- Surface: {diff / path / codebase}
- Files: {count or list}
- Exclusions: {list}
## Trust Boundary Map
| # | Boundary | Location | Validation | Auth |
| - | -------- | -------- | ---------- | ---- |
| 1 | POST /api/users | file:line | Zod schema | JWT middleware |
| 2 | Webhook /hooks/stripe | file:line | None | None |
## Findings
### Hardcoded Secrets
| # | Location | Pattern | Severity | Action |
| - | -------- | ------- | -------- | ------ |
| 1 | file:line | `sk_live_...` Stripe key | Critical | Move to env, rotate immediately |
### Injection Risks
| # | Location | Type | Untrusted Input | Action |
| - | -------- | ---- | --------------- | ------ |
| 1 | file:line | SQL injection | `req.query.id` concatenated into query | Use parameterized query |
### Missing Input Validation
| # | Location | Boundary | Input | Action |
| - | -------- | -------- | ----- | ------ |
| 1 | file:line | POST /api/orders | `req.body` used without validation | Add Zod/Joi schema |
### Insecure Defaults
| # | Location | Setting | Current | Action |
| - | -------- | ------- | ------- | ------ |
| 1 | file:line | CORS origin | `*` | Restrict to allowed origins |
### Auth/Authz Gaps
| # | Location | Endpoint | Issue | Action |
| - | -------- | -------- | ----- | ------ |
| 1 | file:line | GET /admin/users | No auth middleware | Add admin auth |
### Sensitive Data Exposure
| # | Location | Data | Channel | Action |
| - | -------- | ---- | ------- | ------ |
| 1 | file:line | Auth token | console.log | Remove from logs |
### Unsafe Patterns
| # | Location | Pattern | Risk | Action |
| - | -------- | ------- | ---- | ------ |
| 1 | file:line | `innerHTML = userInput` | XSS | Use `textContent` |
### Dependency / Supply-Chain Risks
| # | Location | Issue | Severity | Action |
| - | -------- | ----- | -------- | ------ |
| 1 | package.json | No lockfile committed | High | Commit lockfile |
## Recommendations (Priority Order)
1. **Critical**: {hardcoded secrets to rotate, injection vulnerabilities, auth bypasses}
2. **Must-fix**: {missing input validation, insecure defaults, auth gaps, dependency CVEs}
3. **Should-fix**: {sensitive data exposure, unsafe patterns, supply-chain hygiene}
file/path.ext:line with the exact code.SameSite cookie attribute. Prioritize accordingly.new RegExp() with a hardcoded pattern is fine. A new RegExp(userInput) is
a ReDoS risk. Grep finds both — judgment separates them.development
Transforms vague feature ideas into precise, codebase-grounded technical requirements. Use when requirements are ambiguous/incomplete, the user struggles to describe behavior, terminology is unclear, or multiple concepts are mixed. Output is a requirements spec—NOT an implementation plan.
tools
Audit TypeScript type definitions for design debt — duplicated shapes, missing derivations, over-engineered generics, under-constrained type parameters, reinvented utility types, and disorganized type architecture. Type structure and maintainability, not type enforcement. Use when: reviewing type definitions for maintainability, reducing type duplication, simplifying over-engineered type-level logic, or reorganizing type architecture after growth.
development
Audit TypeScript test code for quality gaps — missing coverage on critical paths, brittle tests coupled to implementation, over-mocking, assertion-free tests, missing edge cases, and duplicated test setup. Focuses on test effectiveness, not production code structure. Use when: reviewing TypeScript test suites for reliability, reducing false-positive test failures, improving coverage of critical business logic, or cleaning up test debt.
tools
Audit TypeScript class and interface design for SOLID violations — god classes, rigid extension points, broken substitutability, fat interfaces, and concrete dependency chains. Focuses on responsibility assignment and abstraction fitness. Use when: reviewing class hierarchies, preparing for extension with new variants, reducing coupling between services, or improving testability of class-heavy code.