diff --git a/docs/00-CORE.md b/docs/00-CORE.md index a562a46..c878db5 100644 --- a/docs/00-CORE.md +++ b/docs/00-CORE.md @@ -40,6 +40,7 @@ erp-monorepo/ └── docs/ # ALL documentation ├── 00-CORE.md # ← you are here (hub) ├── 01-DOC-GUIDE.md # documentation map + tracking conventions + ├── 02-SECURITY.md # accepted-risks register + per-feature security checklist ├── 10-BACKEND-PHASE1.md # backend spec: SRS + ER/entities + tech + architecture ├── 11-BACKEND-PHASE1.md # backend API reference (complete req/res) └── 20-FRONTEND.md # frontend user-flows + architecture rules + validation posture @@ -336,6 +337,7 @@ All frontend work is governed by `20-FRONTEND.md`. | Requirements, business rules, entities, ER model, data types, architecture detail | **`10-BACKEND-PHASE1.md`** | | API endpoints, request/response shapes, error catalog, enums | **`11-BACKEND-PHASE1.md`** | | Frontend user-flows, screen flow, architecture rules, validation posture | **`20-FRONTEND.md`** | +| Security risks per feature, accepted-risk register, pre-ship checklist | **`02-SECURITY.md`** | | Understanding the doc system, reading order, tracking conventions | **`01-DOC-GUIDE.md`** | | Recording backend changes made | **`Backend/PROGRESS.md`** | | Recording frontend changes made | **`Frontend/PROGRESS.md`** | @@ -344,6 +346,7 @@ Quick resolver: - *"Where is the model / an entity defined?"* → `10-BACKEND-PHASE1.md` (schema is authoritative there). - *"What does this endpoint accept/return?"* → `11-BACKEND-PHASE1.md`. - *"How should the UI flow / what do I validate where?"* → `20-FRONTEND.md`. +- *"What security risks / checks apply to this feature?"* → `02-SECURITY.md`. --- @@ -355,6 +358,8 @@ Two checklists track what has actually been built, and travel with code via git: These are **Claude-maintained**: whenever a change is made on either side, the corresponding `PROGRESS.md` must be updated (tick items, add entries). The format and rules for these files are defined in **`01-DOC-GUIDE.md` §Tracking**. Both files must exist before feature work begins. +> **Security gate:** before ticking any data-mutating feature `[x]`, run its checklist in **`02-SECURITY.md`** (Part C) plus the foundational controls (Part B). Accepted Phase-1 exposures are recorded in `02-SECURITY.md` Part A — do not "fix" them ad-hoc. + --- ## 9. Documentation maintenance rule diff --git a/docs/01-DOC-GUIDE.md b/docs/01-DOC-GUIDE.md index 3ab5da1..005d802 100644 --- a/docs/01-DOC-GUIDE.md +++ b/docs/01-DOC-GUIDE.md @@ -14,15 +14,17 @@ ┌─────────────────┐ any task → │ 00-CORE.md │ (hub: structure, stack, setup, routing) └────────┬────────┘ - ┌────────────────┼─────────────────┬───────────────────┐ - ▼ ▼ ▼ ▼ - 10-BACKEND-PHASE1 11-BACKEND-PHASE1 20-FRONTEND 01-DOC-GUIDE - (SRS+ER+arch) (API req/res) (flows+rules) (this file) - │ │ │ - ▼ ▼ ▼ + ┌────────────┬───┴────────┬─────────────┬────────────┐ + ▼ ▼ ▼ ▼ ▼ + 10-BACKEND-P1 11-BACKEND-P1 20-FRONTEND 02-SECURITY 01-DOC-GUIDE + (SRS+ER+arch) (API req/res) (flows+rules) (risks+ (this file) + │ │ │ checklist) + ▼ ▼ ▼ Backend/PROGRESS.md (record changes) Frontend/PROGRESS.md ``` +02-SECURITY is cross-cutting: consult it alongside the backend/frontend docs, and run its checklist before ticking any feature in a PROGRESS.md. + --- ## 2. Document index @@ -31,13 +33,14 @@ |---|---|---|---|---| | `00-CORE.md` | High | **Hub.** Structure, tech stack, runnable backend init, routing. | First — every task. | Claude + humans | | `01-DOC-GUIDE.md` | High | **This file.** Doc map, reading order, tracking conventions. | To understand the doc system. | Claude + humans | +| `02-SECURITY.md` | High (cross-cutting) | **Security review aid:** accepted-risks register + per-feature checklist. | Before ticking any feature in a PROGRESS.md; during security review. | Claude + humans | | `10-BACKEND-PHASE1.md` | High | Backend spec: **full SRS**, **ER model / 38-entity list**, tech stack detail, layer/architecture rules. Schema is **authoritative** here. | Any backend model / business-rule / requirement work. | Claude + humans | | `11-BACKEND-PHASE1.md` | High | Backend **API reference**: every endpoint with complete request/response, error catalog, enums. | Any API contract / controller / client work. | Claude + humans | | `20-FRONTEND.md` | High | Frontend **user-flows**, architecture rules to follow, **validation posture**. | Any frontend work. | Claude + humans | | `Backend/PROGRESS.md` | Low | Backend **change checklist**, git-shared. | After making backend changes. | **Claude** | | `Frontend/PROGRESS.md` | Low | Frontend **change checklist**, git-shared. | After making frontend changes. | **Claude** | -Numbering convention: `0x` = high-level hub/guide, `1x` = backend, `2x` = frontend. Numbers sort in read order in any file browser. +Numbering convention: `0x` = high-level hub/guide/cross-cutting, `1x` = backend, `2x` = frontend. Numbers sort in read order in any file browser. --- @@ -46,6 +49,7 @@ Numbering convention: `0x` = high-level hub/guide, `1x` = backend, `2x` = fronte 1. **`00-CORE.md`** — understand the project, structure, stack, and how to stand up the backend. 2. **`01-DOC-GUIDE.md`** (this file) — understand the doc system and tracking. 3. Then, per task, jump via the hub to `10-`, `11-`, or `20-`. +4. **`02-SECURITY.md`** — read the accepted-risks register once, then use its checklists during development. You do not need to read `10/11/20` end-to-end before starting; open the section relevant to your task via the hub. @@ -59,9 +63,11 @@ You do not need to read `10/11/20` end-to-end before starting; open the section | "What does endpoint Y accept and return?" / "What's the error code?" | `11-BACKEND-PHASE1.md` | | "How does the user move through the UI?" / "What do I validate on the client vs server?" | `20-FRONTEND.md` | | "How do I set up / run the project?" | `00-CORE.md` | +| "What security risks apply to this feature?" / "What must I check before shipping it?" | `02-SECURITY.md` | +| "Is this exposure a bug or an accepted Phase-1 risk?" | `02-SECURITY.md` (Part A register) | | "Where do I record what I changed?" | `Backend/PROGRESS.md` or `Frontend/PROGRESS.md` (this file, §6) | -If a question spans backend + frontend (e.g. a new feature), read the backend spec/API first (the contract), then the frontend doc (how the UI consumes it). +If a question spans backend + frontend (e.g. a new feature), read the backend spec/API first (the contract), then the frontend doc (how the UI consumes it). For any feature that mutates data, also run its `02-SECURITY.md` checklist. --- diff --git a/docs/02-SECURITY.md b/docs/02-SECURITY.md new file mode 100644 index 0000000..0adb00f --- /dev/null +++ b/docs/02-SECURITY.md @@ -0,0 +1,131 @@ +# 02 · SECURITY — Phase 1 Review Aid (Accepted Risks + Per-Feature Checklist) + +> **What this is:** a review aid, not a policy manual. Tick items during development and review. Before marking any feature `[x]` in `Backend/PROGRESS.md`, run its checklist here (Part C) plus the foundational controls (Part B). +> **Navigation:** you arrived from `00-CORE.md`. Business rules are in `10-BACKEND-PHASE1.md`, the API contract in `11-BACKEND-PHASE1.md`, the frontend validation posture in `20-FRONTEND.md`. +> **Context:** Phase 1 is single-tenant with **RBAC deferred** and **approvals auto-on**. Several exposures below are *deliberate, accepted* decisions — they are recorded in Part A so they are explicit, not implicit. + +--- + +## Part A — Accepted Risks Register + +These are known, deliberately-accepted Phase-1 exposures. Each has a compensating control and a trigger to revisit. Do not "fix" them ad-hoc during Phase 1 — they are tracked here and closed in Part D order. + +| ID | Risk | Why accepted | Compensating control | Revisit trigger | +|---|---|---|---|---| +| **AR-01** | **No authorization** — any authenticated user can call any endpoint (post adjustments, confirm GRN, cancel PO, write off stock). | RBAC deferred (FR-X-01), Phase-1 scope. | Authentication required; **immutable audit trail** attributes every action. | First post-Phase-1 hardening → enable RBAC. | +| **AR-02** | **Approvals auto-on** — no value gate on PO or adjustment. | Phase-1 decision (FR-PROC-04, FR-STK-07). | Audit + mandatory reason codes. | Enable adjustment approval first, then PO approval. | +| **AR-03** | **IDOR** — sequential IDs + no RBAC → a user can access another's document by guessing the ID. | Consequence of AR-01. | Audit trail. | Closed with RBAC. | +| **AR-04** | **GRN-without-PO** — receive (and value) goods with no order; a fraud vector. | Emergency/direct receipt is useful (FR-GRN-02). | Permission-gated (when RBAC on); flagged for review; audit; cost entered here gets extra scrutiny (see C.3). | With RBAC / policy on direct receipts. | +| **AR-05** | **In-transit loss window** — dispatched-but-not-received stock is untracked shrinkage risk. | Inherent to in-transit transfers (FR-STK-05). | In-transit aging monitoring report. | Add stuck-transfer alert (Part D). | +| **AR-06** | **Localhost dev secrets** in `appsettings.Development.json`. | Local-dev convenience, current phase. | `.gitignore` + localhost only. | Before any shared/staging/prod → User Secrets / env vars; rotate. | + +--- + +## Part B — Foundational Controls (cross-cutting — apply everywhere) + +### B.1 Authentication & JWT +- [ ] Strong signing key (≥ 256-bit) from env/secrets; the dev `CHANGE_ME` key never reaches non-local environments +- [ ] Algorithm pinned (HS256 or RS256); reject `alg: none` and algorithm-confusion +- [ ] Validate issuer, audience, lifetime, and signature on every request +- [ ] Short access-token lifetime; define refresh strategy if needed +- [ ] Passwords hashed with a strong KDF (ASP.NET Identity / PBKDF2 / bcrypt / argon2) — never plaintext/reversible +- [ ] `/auth/login` rate-limited + backoff/lockout (brute-force) +- [ ] Generic auth-failure messages (no account-enumeration signal) + +### B.2 Token storage & CSRF *(httpOnly-cookie decision)* +- [ ] Token in an **httpOnly, Secure** cookie (never localStorage) — removes XSS token theft +- [ ] `SameSite=Strict` (or `Lax`) on the auth cookie +- [ ] **CSRF protection on every state-changing request** (anti-forgery / double-submit token) — cookies are sent automatically, so CSRF is now in scope +- [ ] CORS locked to the known frontend origin(s); credentials mode aligned with the cookie +- [ ] Cookie scoped minimally (path/domain), Secure flag on + +### B.3 Audit integrity *(this is the compensating control for AR-01 — it must hold)* +- [ ] Audit log **and** stock ledger are append-only **at the DB level** (the app's DB role has no `UPDATE`/`DELETE` on those tables) +- [ ] Every mutation records who / when / old→new / reason (FR-X-02) +- [ ] No API path edits or deletes a posted transaction (FR-X-05); corrections are reversing entries +- [ ] Actor derived from token `sub`, never from the request body + +### B.4 Transport & headers +- [ ] HTTPS only; HSTS in production +- [ ] Security headers: CSP, `X-Content-Type-Options: nosniff`, frame-ancestors/`X-Frame-Options`, `Referrer-Policy` + +### B.5 Error & logging hygiene +- [ ] `ProblemDetails.detail` leaks no stack traces / connection strings / internals in production +- [ ] Serilog never logs tokens, passwords, cookies, or secret-bearing request bodies +- [ ] Correlation/trace id present for support without exposing sensitive data + +### B.6 Input & injection +- [ ] No raw or string-concatenated SQL; EF Core parameterizes — keep it that way +- [ ] **DTOs are narrow** — no mass-assignment/over-posting; client cannot set `status`, ids, `createdBy`, timestamps, or computed fields +- [ ] Server-side validation is authoritative (mirrors `20-FRONTEND §3`); client validation is UX only +- [ ] Pagination bounds enforced (`pageSize` max) to prevent resource exhaustion + +### B.7 Concurrency as integrity +- [ ] FIFO layer consumption is **row-locked** inside the UoW transaction (no double-spend) +- [ ] `ETag`/`If-Match` on mutable resources; `412` on stale writes + +--- + +## Part C — Per-Feature Checklists + +### C.1 Master Data (Item / UOM / Category / Vendor / Warehouse / Bin) +- [ ] Create/update DTOs exclude server-controlled fields (`status`, ids, timestamps) +- [ ] Deactivate — not delete — referenced masters (FR-MD-08); hard delete blocked → `MASTER_IN_USE` +- [ ] Nested/reference writes validate the target exists and is active + +### C.2 Procurement (Requisition / RFQ / PO / Purchase Return) +- [ ] PO totals computed **server-side** from lines (never trust client totals) +- [ ] PO **edit-while-open**: every change audit-logged old→new (this is the tampering surface) +- [ ] PO cancel blocked if any receipt exists +- [ ] Purchase-return quantity validated against received/available (no over-return) +- [ ] Note in review: **AR-01/AR-02/AR-03** apply to these endpoints + +### C.3 GRN +- [ ] `unitCost` **derived from the PO line server-side**; any client-supplied cost is ignored *(decision locked)* +- [ ] `receivedValue` computed server-side (qty × PO-line cost), not accepted from client +- [ ] Direct GRN (no PO) is the exception where cost is entered → extra scrutiny + review flag + audit (**AR-04**) +- [ ] Over-receipt tolerance enforced server-side → `OVER_RECEIPT_TOLERANCE` +- [ ] On-hold stock is not issuable (FR-WH-07); expired batch blocked + +### C.4 Stock Core (FIFO / Ledger) +- [ ] FIFO consumption row-locked (B.7) — integrity + no double-spend +- [ ] Negative-stock block enforced server-side → `STOCK_NEGATIVE_BLOCKED` +- [ ] Ledger append-only (B.3) +- [ ] Valuation computed from layers server-side; never from client input + +### C.5 Adjustments — **HIGHEST-RISK FEATURE IN THE PHASE** +Auto-post + no approval + direct write-off = the primary theft/fraud surface. Reason code + user stamp are the *only* live controls. +- [ ] Reason code mandatory, server-enforced → `REASON_CODE_REQUIRED` +- [ ] User stamp mandatory (from token) +- [ ] Decrease cannot drive available negative → `STOCK_NEGATIVE_BLOCKED` +- [ ] `qtyDelta` sanity bounds; large write-offs surface on a review report +- [ ] Review note: **AR-02** — re-enabling adjustment approval is the **first** control to turn on post-Phase-1 (Part D) + +### C.6 Transfers +- [ ] Dispatch validates available ≥ qty → block otherwise +- [ ] Cost-preserving (dest inherits source cost) — no revaluation via transfer +- [ ] `destWarehouseId != srcWarehouseId` +- [ ] In-transit aging monitored (**AR-05**: stuck/never-received transfers) + +### C.7 Counts +- [ ] Variance posting is an adjustment in disguise → apply C.5 controls +- [ ] System-qty snapshot immutable once the count is opened +- [ ] Large variances flagged for review + +--- + +## Part D — Post-Phase-1 controls to enable (in order) +1. **Adjustment approval** (config flag already reserved) — closes the top fraud surface (AR-02, C.5). +2. **RBAC enforcement** (role→permission) — closes AR-01, AR-03, AR-04. +3. **PO approval** (value thresholds) — closes remaining AR-02. +4. **Monitoring reports** — stuck-transfer aging (AR-05) and large-variance/write-off review. + +--- + +## Part E — Locked security-relevant decisions +- **GRN `unitCost` from the PO line** (server-derived; not user-entered). Direct GRN is the audited exception. +- **Frontend token in an httpOnly Secure cookie + CSRF protection** (not localStorage). + +--- + +*End of 02-SECURITY.md. Run the relevant checklist before ticking a feature in `Backend/PROGRESS.md`.*