mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
[sandbox-ext] Phase 3: parameter surface — schema + project field + verb override + cache-by-features
Migration 072 adds projects.sandbox_extensions (jsonb null): a per-service
extension/module map a venture declares up front (e.g. {"postgres":
["vector","postgis"],"redis":["search"]}). Additive + nullable so
existing opted-in projects stay byte-for-byte bare — no default set, opters
set the extensions they need explicitly (TimescaleDB out unless asked).
Project model validates the map against SANDBOX_ENGINE_FEATURES: unknown
service keys and unallowed features are rejected at the model boundary with
the allowlist named (plpython3u — superuser-RCE — excluded by construction),
empty feature lists drop to bare, order normalized + deduped. The allowlist
is the security containment, not privilege. Mirrors sandbox_services: not on
ProjectCreate, only Project + ProjectUpdate.
request_sandbox gains an extensions arg; _sandbox_features_scope unions a
per-call override with the project's standing set (trusted), bounds it to the
opted set + allowlist, rejects a non-opted service or unallowed feature with
the allowlist named in remediate — scope-first priority preserved by
rej_scope or rej_features. ensure_sandbox threads features through to
provision(); cache-by-features: a cached entry satisfies a new call iff
services are a subset AND every requested feature per service is already
cached — a feature superset re-provisions (rotates creds), mirroring the
services-superset case. available_extensions rides the evidence payload so an
agent doesn't guess what was activated.
Gate: ruff clean, mypy clean (9 modules), 51 tests pass (incl. migration
round-trip).
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
"""Per-project sandbox extensions/modules opt-in column.
|
||||
|
||||
The parameterized sandbox (docs/internal/specs/2026-07-13-sandbox-extensions-
|
||||
on-the-fly.md) lets a venture declare the extensions/modules its sandboxed dev
|
||||
DB should activate (e.g. ``{"postgres": ["vector", "postgis"], "redis":
|
||||
["search"]}``). The provisioner activates them post-ready via ``docker exec``.
|
||||
Additive and nullable: an unset service gets no extensions (bare), so existing
|
||||
opted-in projects stay byte-for-byte unchanged on the bare path. Feature names
|
||||
are allowlist-validated by the Project pydantic model before reaching here
|
||||
(``SANDBOX_ENGINE_FEATURES``), so a ``plpython3u`` can never be persisted.
|
||||
|
||||
Revision ID: 072_project_sandbox_extensions
|
||||
Revises: 071_review_findings
|
||||
Create Date: 2026-07-13
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
revision = "072_project_sandbox_extensions"
|
||||
down_revision = "071_review_findings"
|
||||
branch_labels: dict[str, str] | None = None
|
||||
depends_on: dict[str, str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column(
|
||||
"projects",
|
||||
sa.Column("sandbox_extensions", sa.JSONB(), nullable=True),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column("projects", "sandbox_extensions")
|
||||
Reference in New Issue
Block a user