"""Regression: the bulk settings GET redacts secrets whose leaf name isn't a classic secret token (client_secret, bearer_token, …). ``GET /settings/api/bulk`` redacts via ``redact_value(key, None, value)`` — it passes no ``ui_element``, so a password-typed setting is masked only if its leaf name is in ``DEFAULT_SENSITIVE_KEYS``. These names were previously missing, so such settings shipped in the clear. Uses a real in-memory ``SettingsManager`` and mirrors the endpoint's redaction call; fails if a name is dropped from the set. """ import pytest from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from local_deep_research.database.models import Base from local_deep_research.database.models.settings import Setting from local_deep_research.security.data_sanitizer import DataSanitizer from local_deep_research.settings.manager import SettingsManager @pytest.mark.parametrize( "leaf", ["client_secret", "secret_key", "bearer_token", "api_secret", "app_secret"], ) def test_bulk_get_redacts_named_secret(leaf): key = f"integrations.oauth.{leaf}" engine = create_engine("sqlite:///:memory:") Base.metadata.create_all(engine) session = sessionmaker(bind=engine)() session.add( Setting( key=key, value="topsecret-value", type="app", name=key, ui_element="password", ) ) session.commit() manager = SettingsManager(db_session=session) # mirror GET /settings/api/bulk: get_setting(key) then redact_value(key, None, value) shipped = DataSanitizer.redact_value(key, None, manager.get_setting(key)) assert shipped == DataSanitizer.REDACTION_TEXT