Browse pages
Security
Security answers 3 questions for every operation: who acts, what it touches, and what it keeps. This page states how a LiteSpec product answers them through authentication, actor resolution, authorization, trust boundaries, secrets, browser defenses, personal data, abuse limits, audit events, and release security checks, for anyone who builds or reviews an operation, an endpoint, a frontend, or a release. A product that follows it proves every operation runs for the right actor, touches only what it may, and keeps sensitive data safe.
Stack: any Layer: application
Concepts#
Actors, Roles, and Permissions#
Every protected operation answers one question: who may do what to which target. An actor is whoever acts: a person, a company, a provider, a system, or a guest. An actor holds a role at a scope, such as one organization; a role allows permissions from the shared closed list; and each use case declares the permissions it needs. The application guard authorizes every protected operation from those declarations, so two operations about the same record ask the same question.
Trust Boundaries#
A trust boundary is any point where data crosses from one party to another: a request arriving at the API, a file upload, a provider response, database content rendered into a new output, or generated text shown to a user. Each boundary validates what crosses it before use, because data that was safe in its old context may be unsafe in the new one. Application validators then check message shape, while Domain checks business invariants.
Personal Data and Its Stores#
Personal data spreads further than the database: logs, search indexes, outboxes, snapshots, and backups each hold their own copy. Each store that holds personal data keeps its own inventory entry naming the fields, the purposes, and the retention, so every export, erasure, and breach answer starts from the same map.
Standards#
Keep Backend Authentication Provider-Neutral#
ruleruleguidancelitespec/quality/security#keep-backend-authentication-provider-neutralA backend MUST validate issuer, audience, signature, lifetime, and required claims through OIDC and JWT Bearer.
Applies toartifact: any
Why
The backend accepts one thing: a token whose issuer, audience, signature, lifetime, and required claims validate through OIDC and JWT Bearer. Provider-specific login and session behavior belongs to a frontend or an extension, because the API cannot recheck a login flow it cannot see. An unsigned token, an unvalidated decode, or a client-supplied identity header is never accepted: each one lets a caller mint its own identity.
Checka reviewer answers
Reviewed forThe backend authentication setup validates issuer, audience, signature, lifetime, and required claims, with no identity header accepted.
Marked guidance: a reviewer answers this question.
Derive the Actor from Claims#
ruleruleguidancelitespec/quality/security#derive-the-actor-from-claimsActor resolution MUST follow litespec/quality/security#use-one-current-actor-abstraction.
Applies toartifact: command-handler, query-handler, endpoint
Why
Actor resolution has one home: the WebApi boundary, which maps verified claims to identity, roles, and scopes through the one current-actor accessor. Request data never supplies the current actor, because anything the caller sends can name anyone. One resolution path keeps every operation answering "who acts" from the same verified source.
Checka reviewer answers
Reviewed forActor resolution in WebApi maps verified claims through the one current-actor accessor.
Marked guidance: a reviewer answers this question.
Authorize Each Target Resource#
ruleruleguidancelitespec/quality/security#authorize-each-target-resourceThe application guard MUST authorize every protected operation from its use case's declared permissions, as litespec/backend/application#enforce-target-authorization-in-the-pipeline states, with endpoints adding only coarse policies.
Applies toartifact: use-case, command-handler, query-handler
Why
One decision from one declaration stops two operations about the same record from asking different questions. The use case declares the permissions it needs, the guard resolves the actor's roles at the target's scope, and the decision follows. A message whose target must be the caller carries no target (litespec/core/access#remove-a-target-that-must-be-the-caller). A handler never compares the caller with a target and never throws the forbidden refusal itself; the guard decides. Endpoints add only coarse policies, because a policy that cannot see business data cannot decide about an aggregate.
Checka reviewer answers
Reviewed forEach protected operation passes the application guard from its use case's declared permissions.
Marked guidance: a reviewer answers this question.
Restrict a Protected Collection in the Database#
ruleruleguidancelitespec/quality/security#restrict-a-protected-collection-in-the-databaseA protected collection query MUST apply its authorization scope as a database predicate, before paging, rather than filter a materialized result.
Applies toartifact: query-handler
Why
A collection has no single target, so the check that litespec/quality/security#authorize-each-target-resource states has nothing to resolve against: the scope must become part of the database query itself. Reading the unrestricted set already exposes the rows to the process, and a page assembled from them leaks the total count even when every row is dropped. Filtering after the load is the shape OWASP lists as broken object level authorization.
Example
A query for an organization's orders adds the organization predicate to the database query. It does not load every order and drop the ones the caller may not read.
Checka reviewer answers
Reviewed forEach protected collection query carries its authorization scope as a database predicate before paging.
Marked guidance: a reviewer answers this question.
Validate at Trust Boundaries#
ruleruleguidancelitespec/quality/security#validate-at-trust-boundariesA trust boundary MUST validate transport shape and file metadata before use, leaving message checks to application validators as litespec/backend/application#separate-input-validation-from-invariants states.
Applies toartifact: endpoint
Why
Message shape, required values, length, range, format, and pagination bounds stay with the application validators that litespec/backend/application#separate-input-validation-from-invariants names, and business invariants stay with Domain. Each layer then answers only the questions it can see. Database content, provider responses, file contents, and generated text stay untrusted when they cross into a new output context, because validation for one context proves nothing about the next.
Checka reviewer answers
Reviewed forEach trust boundary validates transport shape and file metadata before use.
Marked guidance: a reviewer answers this question.
Keep Secrets Out of Tracked and Observable Data#
ruleruleguidancelitespec/quality/security#keep-secrets-out-of-tracked-and-observable-dataA secret MUST NOT appear in source, logs, traces, exception messages, generated files, URLs, snapshots, container layers, or browser storage.
Applies toartifact: any
Why
Every place a secret lands is a place it leaks from: source reaches every clone, logs and traces reach every dashboard reader, URLs reach every proxy log, snapshots and container layers outlive the process, and browser storage reaches the user. Platform secret stores hold the values instead, and diagnostics redact sensitive configuration before it is emitted, so the value never enters the observable stream.
Checka reviewer answers
Reviewed forSource, logs, traces, messages, files, URLs, snapshots, layers, and browser storage hold no secret.
Marked guidance: a reviewer answers this question.
Parameterize Database Input#
ruleruleguidancelitespec/quality/security#parameterize-database-inputAn external value reaching the database MUST travel through a provider query API or a parameterized SQL parameter.
Applies toartifact: repository
Why
String concatenation or interpolation into raw command text makes the value part of the statement, so a crafted value rewrites the query. A provider query API or a parameterized SQL parameter keeps the value as data. The provider query API is Marten's under the baseline and EF Core's under its extension, and both parameterize. The reporting extension owns the reviewed raw SQL patterns.
Checka reviewer answers
Reviewed forEach external value reaches the database through a query API call or a SQL parameter.
Marked guidance: a reviewer answers this question.
Limit Public Error Detail#
ruleruleguidancelitespec/quality/security#limit-public-error-detailA public error MUST use the error contract litespec/backend/api#return-stable-problem-details defines.
Applies toartifact: endpoint
Why
The error contract has one home: the HTTP API rules. Every public error then carries the same stable shape with nothing extra. A stack trace, SQL, connection detail, provider body, uncontracted internal identifier, or authorization reason that reveals resource existence gives an attacker the system's structure with every error.
Checka reviewer answers
Reviewed forEach public error carries the HTTP API error contract shape.
Marked guidance: a reviewer answers this question.
Protect Browser Boundaries#
ruleruleguidancelitespec/quality/security#protect-browser-boundariesA frontend MUST apply framework escaping, a documented Content Security Policy, secure headers, redirect validation, forgery protection, and sanitization of allowed rich content.
Applies toartifact: component, route, form
Why
The browser is hostile ground: injected scripts, forged requests, malicious redirects, and hostile markup all arrive as ordinary page content. Framework escaping, a documented Content Security Policy, secure headers, redirect validation, forgery protection, and sanitization of allowed rich content each blocks one of those attacks. A route guard or hidden control is presentation, so it never replaces API authorization.
Checka reviewer answers
Reviewed forEach frontend ships framework escaping, a Content Security Policy, secure headers, redirect validation, forgery protection, and rich-content sanitization.
Marked guidance: a reviewer answers this question.
Minimize Sensitive Data#
ruleruleguidancelitespec/quality/security#minimize-sensitive-dataA use case MUST collect, return, log, export, and retain only the fields its input and results declare.
Applies toartifact: use-case, command-handler, query-handler
Why
Data the use case never collects cannot leak from it. Collecting, returning, logging, exporting, and retaining only the declared fields keeps each use case's exposure exactly what its page promises. A use case handling sensitive data states its classification, access, retention, deletion, and audit behavior on its page, so the promise is written where the fields are.
Checka reviewer answers
Reviewed forEach use case collects, returns, logs, exports, and retains only its declared fields.
Marked guidance: a reviewer answers this question.
Keep a Personal-Data Inventory per Store#
ruleruleguidancelitespec/quality/security#keep-a-personal-data-inventory-per-storeA platform holding personal data MUST keep a personal-data inventory per store naming the fields, purposes, and retention of each store.
Applies toartifact: any
Why
Export, erasure, and breach answers each ask the same opening question: which stores hold this person's data, which fields, for which purposes, and for how long. An inventory the team can read answers it in one place. Without it, each request starts with a search across logs, indexes, and outboxes, and the store nobody remembers stays out of the answer.
Checka reviewer answers
Reviewed forThe inventory names the fields, purposes, and retention of each store holding personal data.
Marked guidance: a reviewer answers this question.
Apply Erasure and Export to Every Store#
ruleruleguidancelitespec/quality/security#apply-erasure-and-export-to-every-storeErasure and export MUST reach every store holding personal data, with backups expiring inside the retention bound the retention schedule states.
Applies toartifact: use-case
Why
Personal data lives in logs, search indexes, and outboxes beside the database. A request honored in the database but left in a log export answers half the duty and breaks the promise to the person. A backup cannot answer field by field, so it carries a retention bound instead, and the data ages out with the backup.
Checka reviewer answers
Reviewed forErasure and export reach every store on the inventory, with backups expiring inside their bound.
Marked guidance: a reviewer answers this question.
Keep Personal Data Out of Logs#
ruleruleguidancelitespec/quality/security#keep-personal-data-out-of-logsA log, trace, or diagnostic record MUST NOT carry personal data in clear.
Applies toartifact: any
Why
Logs flow to stores the product does not control: collectors, dashboards, long-term archives. Redaction at the sink fails once, in one pipeline, and the cleartext is already downstream. A field never emitted fails nowhere.
Checka reviewer answers
Reviewed forLogs, traces, and diagnostic records carry no personal data in clear.
Marked guidance: a reviewer answers this question.
Treat an Unsalted Digest as Personal Data#
ruleruleguidancelitespec/quality/security#treat-an-unsalted-digest-as-personal-dataAn unsalted digest of personal data MUST receive the same handling as the data itself.
Applies toartifact: any
Why
A digest without a salt reverses by table lookup: anyone holding a table of common values recovers the original. It hides nothing, so it gets none of the relaxed handling a truly one-way value earns. The same handling as the data itself keeps the digest from leaking what the data would.
Checka reviewer answers
Reviewed forEach unsalted digest of personal data receives the handling its source data receives.
Marked guidance: a reviewer answers this question.
Report a Breach Within Its Deadline#
ruleruleguidancelitespec/quality/security#report-a-breach-within-its-deadlineA breach-notification procedure MUST name the person who acts and a backup person and meet the deadline the applicable constraint record states.
Applies toartifact: any
Why
A breach deadline starts running at discovery. The applicable constraint record states the deadline the outside rule sets. A deadline with nobody named to meet it slips past the team, and one named person fails on the first absence. Naming both before anything happens keeps the procedure starting on time.
Checka reviewer answers
Reviewed forThe breach procedure names the person who acts, a backup person, and the deadline the constraint record states.
Marked guidance: a reviewer answers this question.
Protect Live Data at Rest#
ruleruleguidancelitespec/quality/security#protect-live-data-at-restLive data at rest MUST carry encryption or a departure exception covering this provision.
Applies toartifact: any
Why
Live data at rest outlives the process: disks, snapshots, and replicas each hold a copy outside the application's control. Encryption keeps each copy unreadable without its key. Where encryption does not apply yet, the exception covering this provision keeps the gap visible with its reason, exit, and expiry date instead of silent.
Checka reviewer answers
Reviewed forEach live store carries encryption, or an exception record covers this provision.
Marked guidance: a reviewer answers this question.
Bound Abuse at Exposed Endpoints#
ruleruleguidancelitespec/quality/security#bound-abuse-at-exposed-endpointsA public, authentication, webhook, search, or upload endpoint MUST declare its limit as a setting record with key and rejection response.
Applies toartifact: endpoint
Why
Public, authentication, webhook, search, and upload endpoints invite abuse: credential stuffing, scraping, flooding. Declaring the bound as a setting record names the key that isolates one caller and the rejection response the caller sees. The rejection is 429 with a stable code, so legitimate callers can back off and retry.
Checka reviewer answers
Reviewed forEach public, authentication, webhook, search, and upload endpoint declares its setting with key and rejection response.
Marked guidance: a reviewer answers this question.
Keep One Limit Store Across Replicas#
ruleruleguidancelitespec/quality/security#keep-one-limit-store-across-replicasA deployment running more than one replica MUST hold its rate and concurrency counters in one store that every replica reads.
Applies toartifact: any
Why
A per-process counter multiplies the declared limit by the replica count, so the limit the specification states is not the limit the system applies. The multiplier also changes whenever the deployment scales, which makes the effective limit unstated and unreviewable. One store every replica reads keeps the applied limit equal to the declared one.
Checka reviewer answers
Reviewed forThe deployment reads rate and concurrency counters from one store on every replica.
Marked guidance: a reviewer answers this question.
Restrict Cross-Origin Access#
ruleruleguidancelitespec/quality/security#restrict-cross-origin-accessA cross-origin policy MUST list its allowed origins, methods, headers, and credential mode without using a wildcard origin.
Applies toartifact: any
Why
A wildcard origin lets any site call the API with the user's credentials, which turns every malicious page into an API client. Listing allowed origins, methods, headers, and credential mode keeps each cross-origin caller explicit. Validated configuration owns the allowlist, so an unlisted origin cannot arrive by mistake. Deployment-owned origins are allowed when the allowlist shape is validated at startup.
Checka reviewer answers
Reviewed forThe cross-origin policy lists origins, methods, headers, and credential mode with no wildcard origin.
Marked guidance: a reviewer answers this question.
Record Security Audit Events#
ruleruleguidancelitespec/quality/security#record-security-audit-eventsA security audit event MUST record actor, action, target, outcome, timestamp, trace identifier, and available reason.
Applies toartifact: any
Why
An incident review reconstructs who did what to which target, with what outcome and why. Without actor, action, target, outcome, timestamp, trace identifier, and reason on each security event, the reconstruction is guesswork. Audit records follow a documented retention and access policy, exclude secrets, and stay available for incident review. A consumer that activates the audit extension records the event through that extension instead, which states a wider field set, a classification rule, and a tamper-evidence rule.
Checka reviewer answers
Reviewed forEach security audit event records actor, action, target, outcome, timestamp, trace identifier, and reason.
Marked guidance: a reviewer answers this question.
Rotate Production Secrets#
ruleruleguidancelitespec/quality/security#rotate-production-secretsA production secret MUST have a secret-inventory entry naming its storage location and next rotation date, with revocation and recovery in a runbook.
Applies toartifact: any
Why
Every secret leaks eventually: through a departing teammate, a copied file, an old backup. A secret-inventory entry with storage location and next rotation date keeps each secret's lifetime bounded, and rotation allows overlap while clients switch. Procedures, logs, artifacts, and audit records carry no secret value, as litespec/quality/security#keep-secrets-out-of-tracked-and-observable-data states, and revocation and recovery live in a runbook.
Checka reviewer answers
Reviewed forEach production secret carries a secret-inventory entry with location and rotation date beside its revocation runbook.
Marked guidance: a reviewer answers this question.
Set an ASVS Level as the Security Target#
ruleruleguidancelitespec/quality/security#set-an-asvs-level-as-the-security-targetA consumer MUST set an ASVS 5.0 level as its security target.
Applies toartifact: record:requirement
Why
ASVS 5.0 defines three levels, L1, L2, and L3, with each requirement verifiable to pass or fail, and the organization picks one by risk. The level lives on a quality requirement with its measure, as litespec/core/intent#state-the-measure-on-a-counted-requirement states, so the target is checked rather than declared. The level choice and any accepted finding are each decided once by a person.
Checka reviewer answers
Reviewed forThe security quality requirement names its ASVS level with a passing check.
Marked guidance: a reviewer answers this question.
Scan Every Release with the ZAP Baseline#
ruleruleguidancelitespec/quality/security#scan-every-release-with-the-zap-baselineCI MUST scan the running API with the ZAP baseline on every pull request.
Applies toartifact: any
Why
The ZAP baseline runs the spider with passive scanning and no active attacks, so it is safe on every pull request, with a WARN, FAIL, and IGNORE config file. Its JSON report feeds the model with the other measured results, and the scan passes with no medium or high alert. ZAP is free and maintained under Apache-2.0.
Checka reviewer answers
Reviewed forThe ZAP result file under TestResults/ shows no medium or high alert for the pull request.
Marked guidance: a reviewer answers this question.
Scan Every Pull Request with a Free Static Analyzer#
ruleruleguidancelitespec/quality/security#scan-every-pull-request-with-a-free-static-analyzerCI MUST scan every pull request for security findings with a free static analyzer.
Applies toartifact: any
Why
CodeQL is free only on public repositories, where no paid plan is required; on a private repository the project uses a free alternative and records the choice in its CI documentation. An accepted finding lives as a gap exception covering the finding, with its reason, exit, and expiry date, so each acceptance stays visible and dated.
Checka reviewer answers
Reviewed forThe static analysis report for the pull request shows its findings with a gap exception for each accepted one.
Marked guidance: a reviewer answers this question.
Conventions#
Use One Current Actor Abstraction#
conventiondefaultguidancelitespec/quality/security#use-one-current-actor-abstractionExpose one narrow current-actor accessor in WebApi that maps verified claims to typed identity, roles, and scopes.
ReplacementA consumer records a current-actor convention naming its accessor and mapping.
Applies toartifact: any
Why
One narrow accessor in WebApi keeps claims mapping in one place: typed identity, roles, and scopes, mapped once. Application receives the actor through input that WebApi creates, or through a narrow trusted port when behavior must stay host-independent. A second accessor invites a second interpretation of the same claims.
Checka reviewer answers
Reviewed forWebApi exposes one current-actor accessor, or the current-actor convention names another arrangement.
Marked guidance: a reviewer answers this question.
Keep Secure Headers in Host Configuration#
conventiondefaultguidancelitespec/quality/security#keep-secure-headers-in-host-configurationDefine Content Security Policy, frame restrictions, content-type protection, referrer policy, and transport security in one host location.
ReplacementA consumer records a secure-headers convention naming where its headers live.
Applies toartifact: any
Why
Content Security Policy, frame restrictions, content-type protection, referrer policy, and transport security form one set: the browser reads them together. Spread across middleware registrations, one of them is always the header nobody remembers. One host location keeps the set reviewable as a whole.
Checka reviewer answers
Reviewed forThe secure headers sit in one host location, or the secure-headers convention names another arrangement.
Marked guidance: a reviewer answers this question.
Use Deny-by-Default Policies#
conventiondefaultguidancelitespec/quality/security#use-deny-by-default-policiesSet a fallback policy requiring an authenticated user, and mark each public endpoint with AllowAnonymous intentionally.
ReplacementA consumer records an endpoint-protection convention naming how each endpoint is protected.
Applies toartifact: endpoint
Why
A new endpoint nobody secured is a public endpoint by accident. A fallback policy requiring an authenticated user keeps each new endpoint protected until somebody marks it public on purpose with AllowAnonymous. Named role or scope policies add coarse checks while the guard retains resource authorization.
Checka reviewer answers
Reviewed forNew endpoints require authentication by default, or the endpoint-protection convention names another arrangement.
Marked guidance: a reviewer answers this question.
Test the Resource Authorization Matrix#
conventiondefaultguidancelitespec/quality/security#test-the-resource-authorization-matrixCover anonymous, invalid-token, unrelated-actor, owner, and each privileged grant for every protected operation.
ReplacementA consumer records an authorization-matrix convention naming the caller cases each operation passes.
Applies toartifact: test
Why
Authorization fails one caller at a time: the anonymous caller who gets in, the unrelated actor who reads another tenant's row. Covering anonymous, invalid-token, unrelated-actor, owner, and each privileged grant for every protected operation proves each caller separately. The matrix also covers disclosure behavior and the collection query predicate. A UI redirect or hidden control is not evidence.
Checka reviewer answers
Reviewed forEach protected operation passes every caller in the matrix, or the authorization-matrix convention names another set.
Marked guidance: a reviewer answers this question.
Reference Example#
This informative example demonstrates litespec/quality/security#authorize-each-target-resource and litespec/quality/security#limit-public-error-detail.
GET /api/posts/{id} for a private draft filters by both post ID and the author ID derived from claims. A different author receives the documented not-found or forbidden response without learning protected fields.