# Security Policy

`mgf-sqlalchemy` is a federation sibling of [`mgf-common`](https://pypi.org/project/mgf-common/),
shipping async SQLAlchemy helpers (engine factory, sessionmaker,
Postgres RLS tenant-scoping). A vulnerability here may affect
every consumer that uses this library for database access, so
we take security reports seriously.

## Supported versions

In the 0.x window, **only the latest MINOR receives security
patches.** Per [SemVer 0.x](https://semver.org/#spec-item-4) and
rule **AP-03** from `mgf-standard/docs/standards/API_DESIGN.md`,
each MINOR release MAY break the public API; we do not
branch-and-backport across minors.

| Version | Supported |
|---------|-----------|
| `0.2.x`  | ✅ — latest MINOR; security patches ship as `0.2.Y` |
| `0.1.x`  | ❌ — superseded by `0.2.x` |

## Reporting a vulnerability

**Please do not open a public issue or pull request** until we have
had a chance to triage and patch.

- **Email:** `bassam@magogi.org` with subject prefix
  `[mgf-sqlalchemy security]`.
- **Codeberg private security advisory:** open a private
  vulnerability report at
  [the repository's Security tab](https://codeberg.org/magogi-admin/mgf-sqlalchemy/security).

### What to include

- A description of the vulnerability and its impact.
- Steps to reproduce — a minimal failing test is ideal.
  For RLS / tenant-scoping issues, the exact SQL that exposed
  the cross-tenant data matters.
- The version(s) of `mgf-sqlalchemy`, `mgf-common`, and
  `sqlalchemy` affected.
- Python version + OS + Postgres version (if applicable).

### What to expect

| Stage | Timeline |
|---|---|
| Acknowledgement | within 3 business days |
| Initial assessment + severity rating | within 7 business days |
| Patch + release | within 30 days for high-severity, 90 days for medium |
| Credit + public disclosure | coordinated with the reporter |

90-day default embargo. After expiry (or after a patch ships),
the vulnerability is disclosed in the [`CHANGELOG.md`](CHANGELOG.md)
entry for the fixing release.

## Scope

**In scope:**

- Code in `src/mgf/sqlalchemy/` (the published `mgf-sqlalchemy`
  package). Specifically: `tenant_session` (the RLS GUC setter
  is the load-bearing security boundary — UUID validation
  BEFORE the `SET LOCAL` interpolation prevents SQL injection
  through `tenant_id`).
- Build / release infrastructure (`pyproject.toml`, the manual
  release flow specified in
  [`mgf-standard/docs/standards/RELEASING.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/main/docs/standards/RELEASING.md)).

**Out of scope:**

- Vulnerabilities in `mgf-common` itself — report to
  [`mgf-common/SECURITY.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/main/SECURITY.md).
- Vulnerabilities in `sqlalchemy`, `asyncpg`, `psycopg`, or
  other transitive deps — report to the upstream project.
- Postgres misconfiguration on the consumer side (missing RLS
  policy on a table, GUC not declared, role grants too
  permissive) — that's deployment hardening, not library
  scope.

## Hardening recommendations for consumers

Consumers of `mgf-sqlalchemy` adopt the
[`mgf-standard/docs/standards/SECURITY_STANDARD.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/main/docs/standards/SECURITY_STANDARD.md)
engineering standard as their baseline.

Particular attention paid to:
- **RLS policies on every multi-tenant table.** The
  `tenant_session` GUC setter is necessary but not sufficient —
  every tenant-scoped table MUST also have a Postgres
  `ROW LEVEL SECURITY` policy referencing
  `current_setting('app.current_tenant')`. Without the policy,
  the GUC is just a value the queries ignore.
- **Connection-pool sizing.** `create_engine`'s defaults
  (`pool_size=5`, `max_overflow=10`) are conservative; tune
  per the actual concurrent-request profile, but DO NOT
  disable `pool_pre_ping=True` — it catches stale connections
  cheaply and the alternative is a flood of 5xx during a
  database failover.
- **`expire_on_commit=False` semantics.** The SQLAlchemy 2.x
  default is the right one for async — committed objects stay
  accessible across awaits. Setting it back to `True` for
  legacy compatibility is a foot-gun; document the reason
  if you flip it.

## A note on the standards

`mgf-sqlalchemy` is a **federation sibling** under the
project-shape taxonomy from
[`mgf-standard/docs/standards/DOCUMENTATION.md`](https://codeberg.org/magogi-admin/mgf_common/src/branch/main/docs/standards/DOCUMENTATION.md) §2.0.
The cross-project engineering standards live in `mgf-common`;
this project inherits them. See
[`docs/standards/README.md`](docs/standards/README.md) for the
pointer.
