This is the engineering baseline every contributor is expected to meet. It is
not the marketplace authorization model (that arrives with the vendor/checkout
work) and it is not a full security audit. For reporting a vulnerability, see
the repository SECURITY.md.
- Secrets live in environment variables, read through
config/, never throughenv()calls scattered in application code. Anarch()test enforces this. .env,.env.backup,.env.productionare git-ignored. Never commit one..env.examplecontains only safe placeholders — no real keys, tokens, URLs, or credentials.- A committed secret is a compromised secret: rotate it, then scrub history.
- Third-party credentials (payment provider, storage, search, mail) are added to
.env.exampleas empty placeholders with a comment, and toconfig/services.php.
APP_ENVandAPP_DEBUGare environment-driven.APP_DEBUG=trueonly in local/testing; production runs withAPP_DEBUG=false.- Local and test databases are never a production database. The test suite is
pinned to in-memory SQLite in
phpunit.xml. - Destructive or money-touching operations are tested against a staging database that is not production.
- Every route that exposes vendor-owned or user-owned data checks authorization — a policy, a gate, form-request authorization, or an explicit ownership check. "The UI doesn't link to it" is not authorization.
- When vendor scoping lands (ADR-0003), vendor-context queries are scoped to the authenticated vendor explicitly and that scoping is enforced at a base layer, not left to each query. Cross-vendor read/write denial is a merge-gate test suite.
- Default to deny. Add access deliberately.
- Validate at the boundary: form requests for HTTP, explicit validation for console/queue input, signature verification for webhooks.
- Never trust client-supplied IDs, prices, totals, quantities, or state transitions — re-derive or re-check them server-side.
- Use Eloquent / the query builder with bindings. No string-interpolated SQL.
- Passwords are hashed (
hashedcast). Tokens are hashed at rest where the framework supports it. - Model
$hidden/ theHiddenattribute keeps secrets out of serialized output. - Do not put personal data, card data, or full tokens in URLs, flash messages, or client-visible errors.
See error-handling.md. In short: never log passwords,
tokens, API keys, full card numbers, or full request bodies for auth/payment
endpoints.
App\Http\Middleware\SecurityHeaders runs first in the global stack, so
every response gets them, error and maintenance pages included
(NFR-SEC-08, tested in tests/Feature/SecurityHeadersTest.php):
- CSP with a fresh nonce per request and no
unsafe-inlineorunsafe-eval:default-src 'self', scripts and styles from'self'plus the nonce,object-src 'none',base-uri 'self',form-action 'self',frame-ancestors 'none'. Laravel's Vite helper adds the nonce to@viteand@fontstags; thecsp-noncemeta inapp.blade.phppasses it to Vite and Inertia for styles they inject. Whilenpm run devruns, the Vite origin, its WebSocket and inline styles are allowed (development only). X-Frame-Options: DENY,X-Content-Type-Options: nosniff,Referrer-Policy: strict-origin-when-cross-origin,Cross-Origin-Opener-Policy: same-origin, and aPermissions-Policydenying camera, microphone and geolocation.- HSTS (
max-age=31536000) on HTTPS requests outside local development. Behind a TLS-terminating proxy the demo host must be a trusted proxy, or requests will not count as HTTPS. - Adding a third-party origin (payments, analytics, images) means adding it to the policy in the same PR, with a test.
-
composer auditruns in CI and incomposer check. A new advisory fails the build; triage it before merging unrelated work. -
npm audit --omit=devruns last in theNode 24 / Vue 3CI job and is blocking. It covers every package shipped to browsers. An advisory that reaches a production dependency blocks merging immediately. -
Development-only npm advisories do not block CI, but each one is recorded below with its package chain, why it is accepted and when it was last reassessed. Reassess with a full
npm auditwhenever@vue/test-utilsorvitestis updated, and at the start of each milestone.Advisory Chain Why accepted Last reassessed GHSA-3g3m-2fjr-6pw4 (moderate, glob13.0.6)@vue/test-utils→js-beautify→globTest-time only; never bundled or shipped. No fixed @vue/test-utils2.5.x yet;npm audit fix --forcewould downgrade to 2.4.112026-11-08 GHSA-g3hv-75wg-m249 (moderate, minimatch10.2.6)@vue/test-utils→js-beautify→editorconfig/glob→minimatchSame as above; inputs are our own repository paths, not untrusted patterns 2026-11-08 -
Add a dependency only when this phase needs it. Prefer a small, maintained package or a few lines of first-party code over a large transitive tree.
Security issues are reported privately per SECURITY.md,
never in a public issue or pull request.