Compare commits

..

5 Commits

Author SHA1 Message Date
cd3b2e0694 Merge branch 'dev' into master: project bootstrap 2026-04-21 14:25:13 -05:00
22f357f3e8 Merge branch 'chore/docs-reorg' into dev 2026-04-21 14:25:00 -05:00
a376207243 chore: move code_guidelines and security under docs/
Keeps repo root lean: CLAUDE.md is the only doc at root. All
reference/architecture material lives under docs/.

Also updates all cross-references in CLAUDE.md, docs/README.md,
and the FastAPI override note in code_guidelines.md so links stay
valid after the move.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 14:24:54 -05:00
5f3bd69e95 Merge branch 'docs/bootstrap' into dev 2026-04-21 14:22:39 -05:00
bf4352231a docs: bootstrap project instructions and roadmap
Add CLAUDE.md with authoritative stack (FastAPI + Jinja2 + SQLite),
deployment topology (CF-proxied -> OPNsense -> Caddy -> VM),
security must-haves, magic-link auth, and project git flow.

Add docs/ROADMAP.md with phased build plan, dataclasses, SQL schema,
caching strategy, visual design tokens (light-blue farm palette),
and .env contract.

Commit generic code_guidelines.md and security.md, note FastAPI
per-project override. Add docs/README.md. Commit logo assets and
.gitignore (venv, .env, data/, SQLite, caches).

No app code in this pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 14:22:34 -05:00
9 changed files with 2845 additions and 0 deletions

42
.gitignore vendored Normal file
View File

@@ -0,0 +1,42 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.egg-info/
.pytest_cache/
.mypy_cache/
.ruff_cache/
# Virtual environments
venv/
.venv/
env/
# Secrets / local config
.env
.env.*
!.env.example
# SQLite runtime
*.sqlite
*.sqlite3
*.db
*.db-journal
*.db-wal
*.db-shm
# Runtime data (DB + media uploads live here in prod)
data/
!data/.gitkeep
# Editors / IDE
.vscode/
.idea/
*.swp
*.swo
.DS_Store
# Build / Docker
build/
dist/
*.log

116
CLAUDE.md Normal file
View File

@@ -0,0 +1,116 @@
# Chicken Babies R Us — Project Instructions
Small farm website for **Chicken Babies R Us** (Morrison TN — address is intentionally **not** displayed publicly). Public brochure site with a blog-style home, About, Contact, and a disabled "Shop (coming soon)" placeholder. The owner's wife, **Head Hen**, edits all content through an admin area protected by email magic-link authentication.
> This file is authoritative for *this project*. Where it conflicts with `docs/code_guidelines.md`, this file wins.
---
## Stack (authoritative for this project)
| Layer | Choice |
|---|---|
| Language | Python 3.12 |
| Web framework | **FastAPI** (overrides the Flask default in `docs/code_guidelines.md`) |
| Templates | Jinja2 |
| ASGI server | Uvicorn, behind Caddy reverse proxy |
| Database | SQLite (WAL mode) — single-writer is fine at this scale |
| Cache | In-process TTL cache + row-level rendered-HTML cache (no Redis) |
| Email | Resend (contact form + magic-link auth) |
| Anti-spam | hCaptcha + honeypot + SlowAPI rate limits |
| Logging | `structlog` |
| Container | Docker (multi-stage), target = Debian 12 VM on home server |
| Repo host | Gitea (Actions for image build/publish wired later) |
## Deployment Topology
```
Internet
→ Cloudflare (proxied DNS)
→ OPNsense firewall (inbound 443 allowed only from Cloudflare IP ranges)
→ Virtual IP
→ Debian 12 VM
→ Caddy (TLS termination)
→ Uvicorn + FastAPI container
```
The app MUST trust `X-Forwarded-For` / `X-Forwarded-Proto` **only from Caddy's IP**. Run Uvicorn with `--proxy-headers --forwarded-allow-ips=<caddy-ip>`. Never read `X-Forwarded-*` in application code directly — let Starlette's `ProxyHeadersMiddleware` do it.
## Target Repository Layout (after Phase 0)
```
/ repo root
├── app/ FastAPI application package
│ ├── main.py app factory + startup
│ ├── config.py typed config loader (pydantic-settings)
│ ├── models/ dataclasses + SQL schema + migrations
│ ├── routes/ public_router.py, admin_router.py, auth_router.py
│ ├── services/ auth, email, cache, markdown, media, hcaptcha
│ ├── templates/ Jinja2 (public/ + admin/ + emails/)
│ └── static/ CSS, JS, site images, logo
├── data/ runtime (SQLite DB + uploads) — mounted volume in prod
├── docs/ business + architecture docs (NO application code)
│ ├── README.md
│ ├── ROADMAP.md
│ ├── code_guidelines.md
│ ├── security.md
│ └── MANUAL_TESTING.md (added in Phase 1)
├── tests/ pytest
├── Logo/ brand assets (source)
├── Dockerfile
├── docker-compose.yml
├── requirements.txt
├── .env.example
├── .gitignore
└── CLAUDE.md
```
## Security Must-Haves (in addition to `docs/security.md`)
- **SQL**: parameterized statements only (sqlite3 `?` placeholders or SQLAlchemy Core bind params). Never f-string a query.
- **Markdown**: hardened pipeline `markdown-it-py``bleach` allowlist. No raw HTML pass-through.
- **Image uploads**: validate magic bytes with `python-magic`, cap at 8 MB, re-encode through Pillow, store under a random filename, discard the client-supplied extension.
- **CSRF**: double-submit cookie on every admin `POST` / `PUT` / `DELETE`.
- **Cookies**: `Secure`, `HttpOnly`, `SameSite=Lax`; session IDs signed with `itsdangerous`.
- **Security headers** (middleware): strict nonce-based CSP, HSTS, `X-Content-Type-Options: nosniff`, `Referrer-Policy: strict-origin-when-cross-origin`, `Permissions-Policy`.
- **Magic-link tokens**: 256-bit random (`secrets.token_urlsafe(32)`), stored hashed (SHA-256), single-use, 15-minute expiry. IP is logged but not enforced (mobile roaming).
- **Rate limits** on auth endpoints: 5 requests / 15 min / IP *and* / email.
- **Admin allowlist**: only addresses in `ADMIN_EMAILS` env var may request a magic link. Any other address silently succeeds (no user enumeration) but sends no email.
- **Secrets**: env / `.env` only, never committed. `.env.example` is the public contract.
- **Audit logging**: every auth event (link requested, link consumed, session created/revoked, rate-limit hit) at INFO. Never log raw tokens or email bodies.
## How to Run (dev)
```bash
python3.12 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # fill RESEND_API_KEY, HCAPTCHA_*, ADMIN_EMAILS, SECRET_KEY
uvicorn app.main:app --reload
```
Docker (parity with prod):
```bash
docker compose up --build
```
## Testing
- `pytest` for auth, magic-link lifecycle, markdown sanitization, rate limits, contact form.
- **Never mock the DB** in auth/magic-link tests — use a temp SQLite file so behavior matches prod.
- Manual test checklist lives in `docs/MANUAL_TESTING.md` (added in roadmap Phase 1).
## Git Strategy (this project)
- Branches: `master` (prod) · `dev` (integration) · `feat/*`, `chore/*`, `docs/*`, `fix/*` (work branches).
- **Work branches off `dev`.** Local `--no-ff` merge into `dev`, then push `dev`. Promote `dev``master` with `--no-ff` for releases. Tag `master` with `vX.Y.Z`.
- Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, `test:`.
- Do **not** push directly to `master`. Do **not** force-push shared branches.
## Pointers
- Generic Python standards: `docs/code_guidelines.md` (FastAPI overrides its Flask default)
- Security baseline: `docs/security.md`
- Phased roadmap, dataclasses, SQL schema, visual design: `docs/ROADMAP.md`
- Docs folder guide: `docs/README.md`

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

2166
Logo/chicken babies r us.ai Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

27
docs/README.md Normal file
View File

@@ -0,0 +1,27 @@
# Documentation Folder
This folder contains business planning, architecture decisions, and documentation.
**No application code belongs here.**
## Contents
| Document | Purpose |
|----------|---------|
| `ROADMAP.md` | Phased build plan, data model (dataclasses), SQL schema, visual design, env-var contract |
| `code_guidelines.md` | Generic Python coding standards (FastAPI overrides its Flask default for this project) |
| `security.md` | Python security baseline (OWASP-aligned) |
| `MANUAL_TESTING.md` *(added in Phase 1)* | Manual test checklist for the public site + admin |
## Related Docs (in repo root)
| Document | Purpose |
|----------|---------|
| `../CLAUDE.md` | Project instructions — stack, topology, security must-haves, git flow |
## Guidelines
- Keep documents focused and concise.
- Update docs when architecture decisions change.
- Use markdown tables for structured information.
- Link to external documentation where relevant.

326
docs/ROADMAP.md Normal file
View File

@@ -0,0 +1,326 @@
# Chicken Babies R Us — Roadmap
High-level phased plan. Each phase ends in a mergeable `dev` state and a passing manual test. Claude implements phase-by-phase. This document intentionally avoids application code; the only code here is **data model (dataclasses)** and **SQL schema**, which are authoritative.
---
## Phase 0 — Foundation
- Scaffold `app/` package, `requirements.txt`, `.env.example`, `Dockerfile`, `docker-compose.yml`.
- Pinned deps (upper bounds):
`fastapi`, `uvicorn[standard]`, `jinja2`, `pydantic`, `pydantic-settings`, `sqlalchemy` (Core only), `markdown-it-py`, `bleach`, `Pillow`, `python-magic`, `resend`, `slowapi`, `structlog`, `itsdangerous`, `python-multipart`, `pytest`, `httpx`.
- `structlog` init at app startup.
- Health endpoint `/healthz` (returns app version + commit SHA from env).
- Typed config loader reading env via `pydantic-settings`.
## Phase 1 — Public Site Skeleton
- Base Jinja layout: header with logo, nav (Home · About · Contact · Shop (disabled)), footer.
- Mobile-first responsive CSS, no JS framework. CSS custom properties from the palette below.
- Routes: `/`, `/about`, `/contact`, `/shop` (shop shows "Coming soon" card, no form).
- `/` renders the blog index from DB (empty list is acceptable this phase).
- Manual test checklist → `docs/MANUAL_TESTING.md`.
## Phase 2 — Content Model + Cache
- SQLite schema (below) with `PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON;`.
- Dataclasses (below) as the in-app model; SQL → dataclass mapper lives in `app/models/`.
- Row-level rendered-HTML cache (`body_html_cached`) regenerated on write.
- In-process TTL cache (60 s) over *hot query results* (published posts list, page-by-slug); invalidated on admin writes.
- Initial migration seeds one welcome blog post + an About page so the site is not blank before admin exists.
## Phase 3 — Admin Auth (Magic Link)
- `/admin/login` — email-only form.
- POST creates a magic-link token (256-bit, hashed at rest, 15-min TTL, single-use), sends via Resend.
- Click link → create signed session cookie (30-day) → redirect `/admin`.
- Logout revokes the session row (does not delete — audit trail).
- Rate limits (SlowAPI): 5 / 15 min / IP *and* / email.
- Allowlist from `ADMIN_EMAILS` env var. Non-allowlisted addresses: return same success message, send no email (no user enumeration).
- Audit log row for every auth event.
## Phase 4 — Admin CMS
- `/admin` dashboard: lists pages + posts, links to edit.
- Markdown editor: textarea + live preview + drag-and-drop image upload. Prefer minimal hand-rolled (a single `textarea` + `fetch`-based upload) over a heavy library; EasyMDE acceptable only if the minimal path proves clunky in manual testing.
- Media upload endpoint: magic-byte validation, 8 MB cap, Pillow re-encode (JPEG/WebP/PNG), random storage name under `data/media/<yyyy>/<mm>/<random>.<ext>`.
- CRUD: pages (About), posts (blog) with publish toggle and slug auto-gen.
- Save path: markdown → `markdown-it-py``bleach` allowlist → stored in `body_html_cached`.
## Phase 5 — Contact Form
- `/contact` POST flow: field validation → hCaptcha verify → honeypot check → rate limit → `Resend` send → persist submission row → success page.
- `FROM` on verified domain; `Reply-To` = submitter's email.
- No internal errors leak to the user; they see a generic "something went wrong, please try again".
## Phase 6 — Hardening + Deploy
- Security headers middleware (strict nonce-based CSP, HSTS, etc.).
- CSRF middleware on admin routes (double-submit cookie).
- Structured access log + error log (structlog JSON in prod, pretty in dev).
- Backup script: `sqlite3 data/app.db ".backup data/backups/app-<ts>.db"` plus a tar of `data/media/`; cron nightly on the VM.
- Dockerfile hardening: non-root user, slim base, `HEALTHCHECK`.
- Gitea Action: on tag push, build image and publish to the internal registry.
## Phase 7 (Future) — Shop
- Stripe Checkout integration (test-mode first).
- Product catalog: eggs (fertile/hatchable, eating), live birds (geese, ducks, chickens).
- Inventory counts, order history, email confirmations.
- Admin views for orders.
---
## Data Model (dataclasses)
```python
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from typing import Optional
class PostStatus(str, Enum):
DRAFT = "draft"
PUBLISHED = "published"
@dataclass
class User:
id: int
email: str
display_name: str
created_at: datetime
last_login_at: Optional[datetime]
active: bool
@dataclass
class MagicLinkToken:
id: int
email: str
token_hash: str # sha256 of raw token; raw token never stored
created_at: datetime
expires_at: datetime
used_at: Optional[datetime]
request_ip: str
@dataclass
class Session:
id: int
user_id: int
token_hash: str
created_at: datetime
expires_at: datetime
ip: str
user_agent: str
revoked_at: Optional[datetime]
@dataclass
class Page:
id: int
slug: str # e.g. "about"
title: str
body_md: str
body_html_cached: str # sanitized, ready-to-render HTML
updated_at: datetime
published: bool
@dataclass
class Post:
id: int
slug: str
title: str
body_md: str
body_html_cached: str
status: PostStatus
published_at: Optional[datetime]
updated_at: datetime
author_user_id: int
@dataclass
class Media:
id: int
filename: str # random storage name
original_filename: str
content_type: str
size_bytes: int
stored_path: str # e.g. data/media/2026/04/abc123.jpg
alt_text: str
uploaded_by: int
uploaded_at: datetime
@dataclass
class ContactSubmission:
id: int
name: str
email: str
message: str
ip: str
user_agent: str
submitted_at: datetime
handled: bool
@dataclass
class AuthEvent:
id: int
event_type: str # link_requested | link_consumed | session_revoked | rate_limited
email: Optional[str]
user_id: Optional[int]
ip: str
user_agent: str
created_at: datetime
detail: str # JSON string
```
## SQLite Schema (authoritative)
```sql
PRAGMA journal_mode = WAL;
PRAGMA foreign_keys = ON;
CREATE TABLE users (
id INTEGER PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL,
created_at TEXT NOT NULL,
last_login_at TEXT,
active INTEGER NOT NULL DEFAULT 1
);
CREATE TABLE magic_link_tokens (
id INTEGER PRIMARY KEY,
email TEXT NOT NULL,
token_hash TEXT NOT NULL UNIQUE,
created_at TEXT NOT NULL,
expires_at TEXT NOT NULL,
used_at TEXT,
request_ip TEXT NOT NULL
);
CREATE INDEX idx_magic_email_created ON magic_link_tokens(email, created_at);
CREATE TABLE sessions (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id),
token_hash TEXT NOT NULL UNIQUE,
created_at TEXT NOT NULL,
expires_at TEXT NOT NULL,
ip TEXT NOT NULL,
user_agent TEXT NOT NULL,
revoked_at TEXT
);
CREATE TABLE pages (
id INTEGER PRIMARY KEY,
slug TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
body_md TEXT NOT NULL,
body_html_cached TEXT NOT NULL,
updated_at TEXT NOT NULL,
published INTEGER NOT NULL DEFAULT 1
);
CREATE TABLE posts (
id INTEGER PRIMARY KEY,
slug TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
body_md TEXT NOT NULL,
body_html_cached TEXT NOT NULL,
status TEXT NOT NULL CHECK (status IN ('draft','published')),
published_at TEXT,
updated_at TEXT NOT NULL,
author_user_id INTEGER NOT NULL REFERENCES users(id)
);
CREATE INDEX idx_posts_status_pub ON posts(status, published_at DESC);
CREATE TABLE media (
id INTEGER PRIMARY KEY,
filename TEXT NOT NULL UNIQUE,
original_filename TEXT NOT NULL,
content_type TEXT NOT NULL,
size_bytes INTEGER NOT NULL,
stored_path TEXT NOT NULL,
alt_text TEXT NOT NULL DEFAULT '',
uploaded_by INTEGER NOT NULL REFERENCES users(id),
uploaded_at TEXT NOT NULL
);
CREATE TABLE contact_submissions (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
message TEXT NOT NULL,
ip TEXT NOT NULL,
user_agent TEXT NOT NULL,
submitted_at TEXT NOT NULL,
handled INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE auth_events (
id INTEGER PRIMARY KEY,
event_type TEXT NOT NULL,
email TEXT,
user_id INTEGER REFERENCES users(id),
ip TEXT NOT NULL,
user_agent TEXT NOT NULL,
created_at TEXT NOT NULL,
detail TEXT NOT NULL DEFAULT '{}'
);
CREATE INDEX idx_auth_events_created ON auth_events(created_at DESC);
```
## Caching Strategy
- **Row-level cache**: `pages.body_html_cached` and `posts.body_html_cached` store the fully rendered, sanitized HTML. Regenerated on write, never on read. Request path = one indexed SELECT + one template render.
- **Query cache**: a small in-process TTL cache (60 s) wraps hot queries (published-posts list, page-by-slug). Keyed by route + slug. Explicitly invalidated by any admin write.
- **No Redis / memcached**: traffic scale (single-digit requests/second at most) doesn't justify it.
## Visual Design
Palette anchored on light blues (Head Hen's preference), softened with farm neutrals. CSS custom-property tokens:
| Token | Value | Use |
|---|---|---|
| `--c-sky` | `#A9CCE3` | Primary surfaces, header accent |
| `--c-sky-deep` | `#5D8AA8` | Links, active states |
| `--c-cream` | `#FAF3E7` | Page background |
| `--c-wheat` | `#E4D4A8` | Card surfaces, subtle emphasis |
| `--c-ink` | `#2B3A42` | Body text |
| `--c-leaf` | `#7FA66B` | Success / small accent |
Typography:
- One serif display face for headers (system serif fallback).
- One humanist sans for body (system sans fallback).
- Self-host any webfont. Do **not** hit Google Fonts on the hot path.
Logo:
- Source assets in `Logo/`.
- Ship PNG (and ideally a WebP conversion) in `app/static/img/`.
- Header uses logo at ~48 px tall; include `alt="Chicken Babies R Us"`.
---
## Environment Variables (contract)
`.env.example` will expose these. None have defaults baked in for secrets.
| Var | Purpose |
|---|---|
| `APP_ENV` | `development` \| `production` |
| `SECRET_KEY` | itsdangerous signer for cookies / CSRF |
| `DATABASE_URL` | `sqlite:///data/app.db` |
| `RESEND_API_KEY` | Resend server key |
| `RESEND_FROM` | e.g. `no-reply@chickenbabies.example` |
| `ADMIN_EMAILS` | Comma-separated allowlist |
| `ADMIN_CONTACT_EMAIL` | Target inbox for contact form |
| `HCAPTCHA_SITE_KEY` | Public key (rendered in template) |
| `HCAPTCHA_SECRET` | Server verification secret |
| `FORWARDED_ALLOW_IPS` | Caddy LAN IP (for Uvicorn) |
| `SESSION_MAX_DAYS` | Default `30` |
| `MAGIC_LINK_TTL_MIN` | Default `15` |

122
docs/code_guidelines.md Normal file
View File

@@ -0,0 +1,122 @@
### Coding Standards
**Style & Structure**
- Prefer longer, explicit code over compact one-liners
- Always include docstrings for functions/classes + inline comments
- Strongly prefer OOP-style code (classes over functional/nested functions)
- Strong typing throughout (dataclasses, TypedDict, Enums, type hints)
- Value future-proofing and expanded usage insights
**Data Design**
- Use dataclasses for internal data modeling
- Typed JSON structures
- Functions return fully typed objects (no loose dicts)
- Snapshot files in JSON or YAML
- Human-readable fields (e.g., `scan_duration`)
**Templates & UI**
- Don't mix large HTML/CSS blocks in Python code
- Prefer Jinja templates for HTML rendering
- Clean CSS, minimal inline clutter, readable template logic
**Writing & Documentation**
- Markdown documentation
- Clear section headers
- Roadmap/Phase/Feature-Session style documents
- Boilerplate templates first, then refinements
**Logging**
- Use structlog (pip package)
- Setup logging at app start: `logger = logging.get_logger(__file__)`
**Preferred Pip Packages**
- API/Web Server: Flask
- HTTP: Requests
- Logging: Structlog
- Scheduling: APScheduler
> **Per-project override:** the `chicken_babies_site` project uses **FastAPI** (not Flask). See `../CLAUDE.md` for the full authoritative stack for that project.
### Error Handling
- Custom exception classes for domain-specific errors
- Consistent error response formats (JSON structure)
- Logging severity levels (ERROR vs WARNING)
### Configuration
- Each component has environment-specific configs in its own `/config/*.yaml`
- API: `/api/config/development.yaml`, `/api/config/production.yaml`
- Web: `/public_web/config/development.yaml`, `/public_web/config/production.yaml`
- `.env` for secrets (never committed)
- Maintain `.env.example` in each component for documentation
- Typed config loaders using dataclasses
- Validation on startup
### Containerization & Deployment
- Explicit Dockerfiles
- Production-friendly hardening (distroless/slim when meaningful)
- Clear build/push scripts that:
- Use git branch as tag
- Ask whether to tag `:latest`
- Ask whether to push
- Support private registries
### API Design
- RESTful conventions
- Versioning strategy (`/api/v1/...`)
- Standardized response format:
```json
{
"app": "<APP NAME>",
"version": "<APP VERSION>",
"status": <HTTP STATUS CODE>,
"timestamp": "<UTC ISO8601>",
"request_id": "<optional request id>",
"result": <data OR null>,
"error": {
"code": "<optional machine code>",
"message": "<human message>",
"details": {}
},
"meta": {}
}
```
### Dependency Management
- Use `requirements.txt` and virtual environments (`python3 -m venv venv`)
- Use path `venv` for all virtual environments
- Pin versions to version ranges
- Activate venv before running code (unless in Docker)
### Testing Standards
- Manual testing preferred for applications
- **API Backend:** Maintain `api/docs/API_TESTING.md` with endpoint examples, curl/httpie commands, expected responses
- **Unit tests:** Use pytest for API backend (`api/tests/`)
- **Web Frontend:** If using a web frontend, Manual testing checklist are created in `public_web/docs`
### Git Standards
**Branch Strategy:**
- `master` - Production-ready code only
- `dev` - Main development branch, integration point
- `beta` - (Optional) Public pre-release testing
**Workflow:**
- Feature work branches off `dev` (e.g., `feature/add-scheduler`)
- Merge features back to `dev` for testing
- Promote `dev``beta` for public testing (when applicable)
- Promote `beta` (or `dev`) → `master` for production
**Commit Messages:**
- Use conventional commit format: `feat:`, `fix:`, `docs:`, `refactor:`, etc.
- Keep commits atomic and focused
- Write clear, descriptive messages
**Tagging:**
- Tag releases on `master` with semantic versioning (e.g., `v1.2.3`)
- Optionally tag beta releases (e.g., `v1.2.3-beta.1`)
---
## Workflow Preference
I follow a pattern: **brainstorm → design → code → revise**

46
docs/security.md Normal file
View File

@@ -0,0 +1,46 @@
## Foundational Security Instructions
- Act as a security-aware software engineer generating secure Python code.
- Produce implementations that are **secure-by-design and secure-by-default**, not merely cosmetically "secured."
- Focus on **preventing vulnerabilities**, not renaming functions or adding superficial security wrappers.
- Explicitly identify **trust boundaries** (user input, external systems, internal components) and apply stricter controls at all boundary crossings.
- Treat **all external input as untrusted by default**, regardless of source, and validate or sanitize it before use.
- Explicitly consider **data sensitivity** (e.g., public, internal, confidential, regulated) and enforce controls appropriate to the highest sensitivity level involved.
- Clearly distinguish between **authentication**, **authorization**, and **session management**, and never conflate their responsibilities.
- Ensure implementations **fail securely**: errors, exceptions, and edge cases MUST NOT expose sensitive data or weaken security guarantees.
- Use inline comments (when generating code) to clearly highlight critical security controls, assumptions, and security-relevant design decisions.
- Adhere strictly to OWASP best practices, with particular consideration for the OWASP ASVS.
- **Avoid slopsquatting and dependency confusion**: never guess package names or APIs; only reference well-known, reputable, and maintained libraries. Explicitly note any uncommon or low-reputation dependencies.
- Do not hardcode secrets, credentials, tokens, or cryptographic material. Always require secure external configuration or secret management mechanisms.
---
## Common Weaknesses for Python
### CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
**Summary:** Failure to properly sanitize or encode user input can lead to injection of malicious scripts into web pages, enabling XSS attacks.
**Mitigation Rule:** All user input rendered in web pages MUST be sanitized and contextually encoded using a secure library such as `bleach` or `html.escape`.
### CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
**Summary:** Unsanitized user input in SQL queries can allow attackers to execute arbitrary SQL commands, compromising data integrity and confidentiality.
**Mitigation Rule:** SQL queries MUST use parameterized statements or prepared statements provided by libraries such as `sqlite3` or `SQLAlchemy`. Direct concatenation of user input into queries MUST NOT be used.
### CWE-327: Use of a Broken or Risky Cryptographic Algorithm
**Summary:** Using outdated or insecure cryptographic algorithms can compromise data confidentiality and integrity.
**Mitigation Rule:** Cryptographic operations MUST use secure algorithms provided by the `cryptography` library. Deprecated algorithms such as MD5 or SHA-1 MUST NOT be used.
### CWE-798: Use of Hard-coded Credentials
**Summary:** Hardcoding credentials in source code can lead to unauthorized access if the code is exposed or leaked.
**Mitigation Rule:** Secrets, credentials, and tokens MUST be stored securely using environment variables, secret management tools, or configuration files outside the source code repository.
### CWE-200: Exposure of Sensitive Information to an Unauthorized Actor
**Summary:** Improper error handling or logging can expose sensitive data to unauthorized users.
**Mitigation Rule:** Error messages and logs MUST NOT include sensitive information such as stack traces, database connection strings, or user credentials. Use logging libraries such as `logging` with appropriate log levels and sanitization.
### CWE-502: Deserialization of Untrusted Data
**Summary:** Deserializing untrusted data can lead to arbitrary code execution or data tampering.
**Mitigation Rule:** Deserialization MUST only be performed on trusted data sources. Unsafe libraries such as `pickle` MUST NOT be used for deserialization of untrusted input.
### CWE-829: Inclusion of Functionality from Untrusted Control Sphere
**Summary:** Using dependencies or code from untrusted sources can introduce malicious functionality or vulnerabilities.
**Mitigation Rule:** Dependencies MUST be sourced from reputable package repositories such as PyPI. Verify the integrity and reputation of packages before use, and pin dependency versions to avoid supply chain attacks.