Class: RosettAi::Config::SecretResolver
- Inherits:
-
Object
- Object
- RosettAi::Config::SecretResolver
- Defined in:
- lib/rosett_ai/config/secret_resolver.rb
Overview
Resolves $secret:backend:key references in configuration values.
Security constraints:
- NO regex anywhere — deterministic string parsing only (ReDoS prevention)
- Single-pass resolution — resolved values are never re-scanned
- Fail loudly — raises on missing secrets, never returns nil
- File backend: 0600 permissions, owned by Process.uid, 64 KiB cap
- Path backend: rejects ".." traversal
Defined Under Namespace
Classes: SecretError
Constant Summary collapse
- SECRET_PREFIX =
'${secret:'- SECRET_SUFFIX =
'}'- VALID_BACKENDS =
['env', 'file', 'path'].freeze
- MAX_FILE_SIZE =
64 KiB
65_536
Instance Method Summary collapse
-
#resolve(value) ⇒ Object
Resolve a single secret reference string.
-
#resolve_all(obj) ⇒ Object
Walk a nested structure and resolve all secret references.
Instance Method Details
#resolve(value) ⇒ Object
Resolve a single secret reference string.
Returns the original value unchanged if it is not a secret reference.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/rosett_ai/config/secret_resolver.rb', line 31 def resolve(value) return value unless value.is_a?(String) return value unless secret_reference?(value) inner = value[SECRET_PREFIX.length..-2] backend, key = inner.split(':', 2) validate_reference!(backend, key) case backend when 'env' then resolve_env(key) when 'file' then resolve_file(key) when 'path' then resolve_path(key) end end |
#resolve_all(obj) ⇒ Object
Walk a nested structure and resolve all secret references.
Single-pass: resolved values are never re-scanned.
53 54 55 56 57 58 59 60 |
# File 'lib/rosett_ai/config/secret_resolver.rb', line 53 def resolve_all(obj) case obj when Hash then obj.transform_values { |v| resolve_all(v) } when Array then obj.map { |v| resolve_all(v) } when String then resolve(obj) else obj end end |