hardening — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited hardening (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Assume input is hostile and the network is not your friend. Most vulnerabilities come from trusting something you shouldn't: user input, a token, a default config, an upstream response, or text stuffed into an LLM prompt. Validate at boundaries, authorize every action, protect secrets, and fail closed when something is wrong.
Security is not a final scan — it's built into every change that crosses a trust boundary. Retrofitting after ship is slower and always misses something.
Run the security checklist alongside this process for a quick pass. For supply-chain and dependency CVEs, pair with [[dependency-hygiene]]. For review workflow, see [[review-gate]] and the security-auditor agent. For logging without leaking data, see [[observability]].
Skip as the primary skill for pure internal refactors with no boundary change — still scan if the diff touches auth, validation, or data access paths.
Work in order. Map boundaries before diving into individual lines.
List where untrusted data enters and where privileged actions happen:
| Boundary | Untrusted inputs | Privileged actions |
|---|---|---|
| Public API | Query, body, headers | Read/write any user's data |
| Admin UI | Same + file uploads | Bulk export, role changes |
| Webhook handler | Raw body, signature header | Trigger internal jobs |
| Background job | Queue message, S3 key | DB writes, outbound calls |
| LLM pipeline | User text, retrieved docs | Tool calls, SQL generation |
| Browser | DOM, localStorage | Usually server must re-verify |
Every arrow from untrusted → trusted is where controls must live. Client-side checks are UX, not security — repeat validation and authorization server-side.
Validate as soon as data enters your code — type, range, format, size — before it reaches SQL, shell, templates, or business logic.
Allowlist what's acceptable; don't blocklist "bad" strings (attackers invent new ones).
Good: email must match RFC-ish pattern; age 0–120; enum status in {pending, paid}
Bad: strip '<script>' and hopeSize limits on everything — body size, array length, string length, upload bytes. Unbounded input is a DoS vector.
Types — parse JSON/schema strictly; reject unknown fields when the contract is fixed ([[interface-design]]).
Files — verify type and size; store outside web root; generate safe names — never use user path as filesystem path (see uploads below).
Reject invalid input with a generic client message; log details server-side without secrets.
Injection happens when untrusted data becomes syntax in SQL, shell, HTML, URLs, or templates.
| Risk | Unsafe | Safe |
|---|---|---|
| SQL | "SELECT ... WHERE id = " + id | Parameterized queries / prepared statements |
| Shell | os.system("convert " + path) | Argument array APIs; no shell |
| HTML/XSS | Template with unescaped user HTML | Auto-escape templates; CSP |
| NoSQL | {$where: userInput} | Typed queries; operator allowlist |
| LDAP/XML | String concat | Library APIs with parameters |
Output encoding matters at the sink — HTML context ≠ JS context ≠ URL context. Encode for where data lands.
For redirects and links: allowlist destinations or map IDs to URLs — don't redirect(userSuppliedUrl) (open redirect).
Authentication (who are you?) ≠ authorization (may you do this to this object?).
For every endpoint and mutation:
IDOR (insecure direct object reference) — classic bug:
GET /api/orders/123 — does 123 belong to this user/tenant?
DELETE /files/{id} — verify ownership before deleteCheck authorization in the handler or service layer — not only hiding buttons in the UI.
Multi-tenant: scope every query by tenant_id (or equivalent) from the authenticated session — never from a client-supplied field without verification ([[data-modeling]]).
Least privilege — roles, service accounts, and DB credentials get minimum permissions needed.
HttpOnly, Secure, SameSiteframework session stores)
Verify webhook signatures (HMAC, timestamp) before trusting body — treat unsigned webhooks as untrusted input.
Never in:
Do:
Classify data — what's PII, payment, health — and model retention/delete paths ([[data-modeling]], [[decision-docs]] for compliance rationale).
When auth, validation, or dependency checks fail — deny, don't degrade to open:
| Wrong | Right |
|---|---|
catch { return true } | Reject request; log internally |
| Missing role → full access | Missing role → deny |
Config default auth=false | Default deny; explicit opt-in to expose |
| Error returns stack trace to client | Generic message; details in server log |
Production defaults:
Content-Security-Policy, X-Content-Type-Options, X-Frame-Options or CSPframe-ancestors
* with credentialsSSRF — user-supplied URLs fetched server-side:
169.254.x, internal ranges)File upload / path traversal
open(userSuppliedPath) — map to internal ID or sanitized nameDeserialization — don't deserialize untrusted data into rich objects (Java, Python pickle, etc.)
Mass assignment — don't bind request JSON directly to DB models without field allowlist
Caching — personalized or auth responses not cached under global keys ([[caching-strategy]])
LLM / AI ([[llm-feature-engineering]])
eval model output; validate structured output against schemaA vulnerable library is your vulnerability ([[dependency-hygiene]]):
Don't disable security features of frameworks to "make it work."
Before merge or launch:
git log, trufflehog, IDE patterns.For significant surface changes, use security-auditor persona or peer review with attack scenarios.
New public API endpoint
Boundary map → schema validation → authz on object → parameterized queries → rate limit → checklist.
Login / signup
Rate limit, credential stuffing protection, secure cookies, generic errors ("invalid credentials"), no user enumeration via error messages.
Admin or bulk export
Strong authz role check, audit log who exported what, limit row count, no IDOR on export filters.
Webhook receiver
Verify signature + timestamp → parse JSON → treat as untrusted → idempotent handler ([[resilience]]).
Multi-tenant feature
Tenant from session only; every query scoped; test cross-tenant access explicitly.
LLM feature with tools
Allowlist tools; validate tool args; same authz as direct API; block instruction override in user text where possible (delimiter patterns, separation — not foolproof, layer defenses).
Security regression in PR
Diff trust boundaries → focus review on changed edges → checklist → targeted tests for authz bypass.
GET handler changes state without authz checktenant_id or userId taken from request body without verificationcatch (e) { } or default-allow on auth failuresAccess-Control-Allow-Origin: * with credentialseval, pickle.loads, or equivalent on untrusted data~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.