"""Secret references: no literal secret values in persisted configuration. Environment references are intended for single-host development. Production should mount secrets under /run/secrets via Docker/Kubernetes secret volumes. """ import os import re from pathlib import Path ROOT=Path('/run/secrets') class SecretReferenceError(ValueError):pass def resolve(ref:str)->str: if not isinstance(ref,str) or len(ref)>160:raise SecretReferenceError('Invalid secret reference') if ref.startswith('env:'): key=ref[4:] if not re.fullmatch(r'SITEFORGE_[A-Z][A-Z0-9_]{1,110}',key): raise SecretReferenceError('Environment secret name must be SITEFORGE_*') val=os.environ.get(key,'') elif ref.startswith('file:'): name=ref[5:] if not re.fullmatch(r'[a-zA-Z0-9_.-]{1,90}',name) or name.startswith('.'): raise SecretReferenceError('Invalid secret filename') path=(ROOT/name).resolve() if path.parent!=ROOT or not path.is_file():raise SecretReferenceError('Secret unavailable') val=path.read_text(encoding='utf-8').strip() else: raise SecretReferenceError('Expected env: or file: secret reference') if not val:raise SecretReferenceError('Secret is empty or unavailable') return val def validate_reference(ref:str)->str: """Validate a reference without resolving the credential in the Web process.""" if not isinstance(ref,str) or len(ref)>160:raise SecretReferenceError('Invalid secret reference') if ref.startswith('env:') and re.fullmatch(r'SITEFORGE_[A-Z][A-Z0-9_]{1,110}',ref[4:]):return ref if ref.startswith('file:') and re.fullmatch(r'[a-zA-Z0-9][a-zA-Z0-9_.-]{0,89}',ref[5:]):return ref raise SecretReferenceError('Expected env:SITEFORGE_* or file: secret reference')