Files
FabledScribe/tests/test_forge_token_sealing.py
bvandeusenandClaude Opus 5.5 07bc51b890
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 56s
CI & Build / integration (push) Successful in 1m8s
CI & Build / Python tests (push) Successful in 1m54s
CI & Build / Build & push image (push) Successful in 36s
feat(523): forge-connection tokens are stored sealed under SECRET_KEY, like the backup S3 secret
The column becomes token_sealed (migration 0122 seals existing rows with a
frozen copy of the v1 key), every reader opens it through connection_token,
a row this instance cannot open is skipped with a warning, and Settings asks
for that token again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 18:08:11 -04:00

48 lines
1.8 KiB
Python

"""Forge-connection tokens are stored sealed (milestone 523, #5877).
Migration 0122 seals the rows that existed before, with its OWN frozen copy of
the v1 key derivation (so a later edit to services/secrets.py can never change
what an old migration writes). That copy is only right while it agrees with
what the app opens today — which is what this pins. If secrets.py ever moves
to a v2 key, it must still open v1 values, and this test says so.
"""
import importlib.util
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch
from scribe.services import secrets
from scribe.services.forge_connections import connection_token
_MIGRATION = Path(__file__).resolve().parents[1] / "alembic" / "versions" / "0122_seal_forge_tokens.py"
def _migration():
spec = importlib.util.spec_from_file_location("migration_0122", _MIGRATION)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_what_the_migration_seals_the_app_opens():
sealed = _migration().seal_v1("gitea-read-token")
assert "gitea-read-token" not in sealed
assert secrets.unseal(sealed) == "gitea-read-token"
def test_a_row_reads_through_connection_token():
row = SimpleNamespace(token_sealed=secrets.seal("ghp_abc"))
assert connection_token(row) == "ghp_abc"
def test_a_row_sealed_under_another_key_reads_as_none():
row = SimpleNamespace(token_sealed=secrets.seal("ghp_abc"))
with patch.object(secrets.Config, "SECRET_KEY", "a-different-instance"):
assert connection_token(row) is None
def test_a_left_over_plain_text_token_is_not_used_as_one():
"""A value that never went through seal is refused, not passed to the
forge as if it were the token: a reader sees None, never the raw bytes."""
assert connection_token(SimpleNamespace(token_sealed="plain-text-token")) is None