dotkernel-security

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 a

SKILL.md

GitHub

Security

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.