Use when touching anything security-relevant in a Dotkernel project — authentication, OAuth2 or session login, authorization config and role permissions or guards, CORS, content negotiation, session and cookie settings, error handling and error-reporting endpoints, documentation exposure, secrets and *.local.php versus *.global.php, demo credentials, ownership checks, raw DQL or SQL, or adding a dependency. Also use when reviewing a diff for security problems or when asked whether something i...
Installs into .claude/skills of the current project.
Are you the author of Dotkernel Security?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/dotkernel-dotkernel-security)
---
name: dotkernel-security
description: >
Use when touching anything security-relevant in a Dotkernel project — authentication,
OAuth2 or session login, authorization config and role permissions or guards, CORS, content
negotiation, session and cookie settings, error handling and error-reporting endpoints,
documentation exposure, secrets and *.local.php versus *.global.php, demo credentials,
ownership checks, raw DQL or SQL, or adding a dependency. Also use when reviewing a diff for
security problems or when asked whether something is safe to commit.
---
# Security
```text
Applies to
Projects: - all
Packages: - none
```
Assume every request is hostile. Ignoring security is cheap today and expensive later — lost
data, a GDPR fine, lost client trust. Every item below has a real place in the codebase; check
it rather than reinventing it.
Input validation has its own skill (`dotkernel-input-validation`) — that is the first line and
the most commonly skipped.
## Authentication
| Variant | Mechanism |
| --- | --- |
| API | OAuth 2.0 (`mezzio/mezzio-authentication-oauth2`) — access + refresh tokens |
| Admin / Frontend | session-based login |
| Light / Queue | none in the stock repositories |
For OAuth2 projects:
- Replace or update the default clients before production.
- Review token lifetimes against requirements (commonly one day access, one month refresh); do
not lengthen without discussion.
- Keys are generated by the repo's key-generation script into a data directory. **Never commit
them, never edit them, never read them into context.**
For session-based projects: `cookie_httponly`, `cookie_samesite` and `cookie_secure` must stay hardened in
the session config, and every form needs CSRF.
## Authorization
| Variant | Where |
| --- | --- |
| API | RBAC roles and route-name permissions in `config/autoload/authorization.global.php` |
| Admin | roles → permissions in `authorization.global.php`; route name → permission in `authorization-guards.global.php` |
| Frontend | the same two files; guards keyed by route **and controller action** |
| Light / Queue | none |
- **The default differs, and it is the dangerous part.** The API denies a route that has no
permission entry. The Admin and Frontend guards use `POLICY_ALLOW`: a route or action with no
guard rule is **open to everyone, guests included**. Forgetting the entry there never surfaces
as a 403 — it surfaces as an unprotected page.
- **Every new route needs an explicit entry** — per role in the API, a guard rule in Admin and
Frontend.
- Do not grant anything to a guest role unless the route is genuinely public (registration,
activation, password recovery).
- Check the role hierarchy — inheritance direction in these config files is not always what
people assume. Read the comment at the top of the file.
- **Route-level access is not object-level access.** A user permitted to view "an account" must
only see their own. Use the resource guard mechanism where the project provides one, or an explicit
ownership check in the service. This is the most common real vulnerability in CRUD code.
## Configuration and secrets
| File | Committed? | May contain secrets? |
| --- | --- | --- |
| `*.global.php` | yes | **no** |
| `*.php.dist` | yes | **no** — placeholders only |
| `*.local.php` | no (git-ignored) | yes |
Never write a credential, key, token, password-bearing DSN or SMTP password into a committed
file. Need a new setting? Add the placeholder to the `.dist` and read it through config.
Development mode enables debug output, error details and disables config caching. It must be
disabled in production.
## HTTP-layer protections
Configured in `config/pipeline.php` and `config/autoload/` — keep the order, and justify any
change to it:
- **CORS** (where present): must be piped **before** the routing middleware. Never widen origins
to `*` in a committed file.
- **Content negotiation**: honours `Content-Type` and `Accept`; do not bypass it.
- **Malformed body handling**: rejects unparseable payloads early.
- **Error handling**: outermost layer, renders a safe error body. Throw typed exceptions; never
leak stack traces, SQL, paths or internal class names.
- **Response headers**: security headers are set centrally, not per handler.
## Persistence
Use the query builder with bound parameters. **No string concatenation or interpolation into DQL
or SQL, ever.** Whitelist user-supplied sort and filter values against an explicit column list.
If you believe you need raw SQL, explain why and ask.
## Error-reporting endpoints
Where the project exposes one, it is gated by a token and a permission middleware. Do not disable
the token check or widen allowed IPs in a committed file.
## Documentation exposure
Never document a real credential, internal hostname, or an example payload containing production
data. Interactive documentation should not be enabled in production.
## Dependencies and supply chain
- **Adding, removing or upgrading a Composer or npm package requires explicit user approval.**
Propose it, name the maintainer, and say why the existing stack cannot cover the need.
- The security-advisories dev dependency refuses known-vulnerable versions. If it blocks an
install, that is a signal, not an obstacle to route around.
- Let the automated dependency bot drive routine bumps.
- JavaScript is a live supply-chain target — widely used npm packages have been compromised. Use
`npm audit` and prefer packages from reliable sources.
- Transitive dependencies count too.
## Review checklist for a diff
- [ ] Every write path validates before business logic.
- [ ] Every new route has an explicit authorization entry; no accidental guest access.
- [ ] Object-level ownership checked where a resource belongs to a user.
- [ ] CSRF present and validated on every form (templated projects).
- [ ] No secrets outside `*.local.php`; no keys committed.
- [ ] No raw SQL/DQL interpolation; all parameters bound.
- [ ] Sort/filter values whitelisted; `limit` clamped.
- [ ] Uploads validated and stored outside executable paths.
- [ ] Errors thrown as typed exceptions with no internal detail.
- [ ] No new dependency without approval.
- [ ] Pipeline order unchanged, or the change justified.