Skip to content

Credential Hygiene Specification

Overview

Adds a Secret wrapper type that makes credential leakage structurally impossible at the config layer, plus SFTP enum coercion and a logging redaction filter.

Module: src/remote_store/_config.py Dependencies: None (pure Python, always available) Related: 002-registry-config.md (CFG sections), 009-sftp-backend.md (SFTP-001), AF-008 (backend repr masking), ID-039.


Secret Wrapper

SEC-001: Secret Class

Invariant: Secret(value) wraps a string credential. __repr__ returns "Secret('***')", __str__ returns "***", .reveal() returns the plain-text value. __eq__ and __hash__ compare/hash the underlying value. __bool__ delegates to the underlying string. Secret does not implement __iter__, __getitem__, or __len__ to prevent accidental unpacking. Raises: TypeError if value is not a str.

SEC-002: Secret Immutability

Invariant: Secret.__setattr__ and __delattr__ raise AttributeError after construction. The class uses __slots__ = ("_value",) and bypasses the override in __init__ via object.__setattr__. __reduce__ is implemented so that pickle and copy.deepcopy reconstruct via the public constructor instead of hitting the __setattr__ override.


Config Integration

SEC-003: from_dict() Secret Wrapping

Invariant: RegistryConfig.from_dict() wraps string values for keys in _SENSITIVE_KEYS (key, secret, password, account_key, sas_token, connection_string) inside Secret(). Non-string values (e.g., None, credential objects) are left unchanged.

SEC-004: Backend Secret Acceptance

Invariant: Backend __init__ methods accept str | Secret | None for credential parameters and call _reveal() to unwrap them. Backends store revealed plain strings internally — they already have AF-008 __repr__ masking, and SDK clients need plain strings.

Backend Parameters
S3 key, secret
S3-PyArrow key, secret
SFTP password
Azure account_key, sas_token, connection_string

SEC-005: SFTP Host Key Policy Enum Coercion

Invariant: SFTPBackend.__init__ accepts host_key_policy as either a HostKeyPolicy enum member or a string value ("strict", "tofu", "auto"). String values are coerced to the enum via HostKeyPolicy(value). Invalid strings raise ValueError.

SEC-006: Config Repr Safety

Invariant: repr(BackendConfig) never leaks secret values. This is structurally guaranteed by Secret.__repr__ returning "Secret('***')" for any wrapped credential in the options dict.


Logging

SEC-007: SecretRedactionFilter

Invariant: SecretRedactionFilter is a logging.Filter subclass that replaces Secret instances in record.args (tuple or dict) with "***" before the log message is formatted. Always returns True (does not suppress records).


Regression

SEC-008: No-Leak Regression Tests

Invariant: Test suite includes assertions that known test secrets (e.g., "AKID_TEST", "SK_TEST") never appear in the repr() of config objects constructed via from_dict().