Ctrl K
ComunidadGuías…

SECURITY.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Security policy

Security is part of minddy's release contract. This document describes the
public security model, supported versions, disclosure path, and controls that
self-hosters must configure.

## Supported versions

Security fixes target the latest stable release and `main`. Older releases may
receive a backport when the maintainers judge it safe and practical. Upgrade to
the latest stable release before requesting support for an older version.

## Authentication and sessions

Minddy uses Supabase Auth. The server validates the authenticated user before
authorizing project or account operations. Important properties include:

- redirect targets are normalized and restricted to the configured origin;
- authentication errors do not disclose whether an account exists;
- password reset, email change, account deletion, and other sensitive actions
  require a recent authentication state where applicable;
- MFA factors are enrolled and challenged through server-validated flows;
- active sessions can be listed and revoked;
- session cookies use production-safe attributes through `@supabase/ssr`;
- rate limits apply to password verification and other abuse-prone paths.

The UI password rules are defined in `lib/password-policy.ts`. The Supabase
project remains the enforcement authority and must not use weaker settings.

## Authorization and tenant isolation

Most server writes use a service-role client after explicit authorization in
TypeScript. Row Level Security (RLS) is the independent second line of defense
for direct PostgREST access.

The current schema is defined by
[`supabase/migrations/20270106090000_baseline.sql`](supabase/migrations/20270106090000_baseline.sql)
and later migrations. Security invariants include:

- every application table enables RLS;
- policies target the minimum required roles and enforce project membership or
  ownership;
- cross-project identifiers are reauthorized before service-role access;
- project ownership columns cannot be moved between tenants through a client
  update;
- author identifiers are bound to the authenticated caller;
- privileged `SECURITY DEFINER` functions use a safe `search_path` and revoke
  execution from unintended roles;
- sensitive columns are protected with column privileges in addition to RLS;
- destructive operations use server-side authorization and storage cleanup.

Release validation includes schema guardrails and negative multi-tenant probes.
See [docs/security-model.md](docs/security-model.md) for the threat model and
[docs/security-release-checklist.md](docs/security-release-checklist.md) for
the promotion gate.

## Secrets and encryption

Reversible application secrets are encrypted before storage with AES-256-GCM
and purpose-specific keys. Examples include Git provider tokens, BYOK AI keys,
webhook secrets, and feedback-board SSO secrets. One-way API credentials and
OAuth grants are stored as hashes when the original value is not needed.

Secrets must never appear in client bundles, URLs, captures, logs, release
artifacts, test fixtures, or security reports. Only explicitly public values may
use a `NEXT_PUBLIC_`-style exposure. Operators must provide independent
encryption keys through protected runtime configuration.

The publication barrier scans the current tree and all publishable Git history.
CI also runs a maintained secret scanner with fully redacted output.

## Transport and browser protections

`next.config.mjs` applies the production browser-header policy:

| Header | Required behavior |
| --- | --- |
| `Strict-Transport-Security` | At least one year with `includeSubDomains` |
| `X-Content-Type-Options` | `nosniff` |
| `X-Frame-Options` | Deny framing |
| `Referrer-Policy` | Restrict cross-origin referrer detail |
| `Permissions-Policy` | Disable unused sensors and limit microphone access |
| `Content-Security-Policy` | Restrict framing, base URLs, and form targets |

The OAuth authorization page has a documented `form-action` exception because
the protocol redirects a form response to the registered client URI. The
exception does not relax frame or base-URI protection.

Self-hosted operators must terminate HTTPS correctly, redirect HTTP to HTTPS,
and keep internal database and runner ports off the public network.

## Storage and uploads

Storage buckets are reconciled by
[`scripts/reconcile-storage-buckets.mjs`](scripts/reconcile-storage-buckets.mjs)
and protected by the baseline and later migrations.

- Private attachments use signed, short-lived access paths.
- Public project icons are constrained by content type and size.
- Server-side checks enforce ownership, project access, and object-key shape.
- Active content is not served as executable content.
- Deletion removes both metadata and the corresponding storage object.

Operators must back up database state and object bytes together.

## Input validation and request integrity

API routes validate runtime schemas, identifiers, sizes, and allowed values.
Cookie-authenticated state changes verify the request origin or an equivalent
anti-CSRF signal. Public webhook and OAuth endpoints use protocol-specific
signatures, state, redirect-URI validation, and replay controls.

Outgoing fetches restrict protocols, redirects, hostnames, and private network
targets. Error responses must not reveal credentials, internal paths, SQL, or
stack traces.

## Rate limiting

Abuse-prone authentication, feedback, AI, import, webhook, and integration
operations use scoped rate limits. Self-hosted operators must configure the
shared rate-limit store required by their topology; in-memory limits are not a
multi-instance substitute.

## Public surfaces

Public feedback, shared pages, OAuth discovery, health, and version endpoints
return only their documented projections. Public visibility does not imply
access to team-only comments, internal identities, private attachments, billing
data, or service credentials.

## Coding-agent isolation

Server-side agents run in isolated sandboxes with a restricted environment and
network policy. The sandbox receives only the scoped material required for a
run. It does not receive the host Docker socket, private Supabase network, or
unrelated service credentials.

Repository code is untrusted input. Agent execution, dependency installation,
tests, and build scripts must run away from operator credentials. See
[CONTRIBUTING.md](CONTRIBUTING.md#run-untrusted-changes-safely).

## Security verification

The repository provides:

- schema and authorization tests in the normal test suite;
- `scripts/security-probe.mjs` for fixture-driven live boundary checks;
- `npm run check:public-repo` for current and reachable-history publication
  checks (pull requests limit the history scan to their own commits with
  `--new-since`, while every push to `main` scans the whole history);
- `npm run check:public-repo:remote` for a fresh canonical mirror, including
  server-advertised pull-request refs;
- dependency, container, workflow-pin, and release-policy gates;
- the mandatory release checklist in
  [docs/security-release-checklist.md](docs/security-release-checklist.md).

Run live probes only against an authorized disposable or staging environment.
Never put real credentials or personal data in probe output.

The live authorization probe requires a dedicated synthetic fixture with two
users and separate projects. It deliberately uses public/session credentials;
never provide a service-role key. Configure these variables in the restricted
probe environment:

```text
SUPABASE_URL
SUPABASE_ANON_KEY
SECURITY_PROBE_CROSS_TENANT_TOKEN
SECURITY_PROBE_DUAL_MEMBER_TOKEN
SECURITY_PROBE_SOURCE_PROJECT_ID
SECURITY_PROBE_FOREIGN_PROJECT_ID
SECURITY_PROBE_FOREIGN_ISSUE_ID
SECURITY_PROBE_REASSIGNABLE_ISSUE_ID
SECURITY_PROBE_HARD_DELETE_PAGE_ID
SECURITY_PROBE_SECRET_CONNECTION_ID
SECURITY_PROBE_FOREIGN_STORAGE_PATH
```

After provisioning only synthetic records, run:

```bash
node scripts/security-probe.mjs --confirm
```

## Incident response

If a vulnerability may be active:

1. Preserve minimal, access-restricted evidence.
2. Revoke or rotate affected credentials immediately.
3. Contain the exposed route, integration, or deployment.
4. Determine affected versions, tenants, and data without copying unnecessary
   personal information.
5. Prepare and verify a fix, migration, and rollback path.
6. Coordinate disclosure and notify affected users when required.
7. Record follow-up controls without publishing exploit details prematurely.

## Report a vulnerability

Do not open a public issue or Discussion.

Use GitHub's private vulnerability reporting on the repository's **Security**
tab. If that option is unavailable, email
[hello@minddy.app](mailto:hello@minddy.app). Encrypt highly sensitive evidence
or ask for a secure transfer channel before sending it.

Include the affected version, impact, reproduction conditions, and the minimum
evidence needed to validate the report. Do not include real user data unless it
is essential and you are authorized to share it.

We will acknowledge the report, agree on a correction and disclosure schedule,
and credit the reporter if requested and safe.