Release v0.1.8
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
def sensitive_key_tokens(key: object) -> set[str]:
|
||||
value = str(key).strip()
|
||||
value = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", value)
|
||||
return {item for item in re.split(r"[^A-Za-z0-9]+", value.lower()) if item}
|
||||
|
||||
|
||||
def is_sensitive_key(key: object) -> bool:
|
||||
tokens = sensitive_key_tokens(key)
|
||||
if not tokens:
|
||||
return False
|
||||
if tokens & {"authorization", "cookie", "credential", "credentials", "password", "passwd", "secret", "token"}:
|
||||
return True
|
||||
return bool({"api", "key"} <= tokens or {"access", "key"} <= tokens or {"private", "key"} <= tokens)
|
||||
|
||||
|
||||
def redact_secret_values(value: object, *, placeholder: str = "<redacted>") -> object:
|
||||
if isinstance(value, Mapping):
|
||||
result: dict[str, object] = {}
|
||||
for key, item in value.items():
|
||||
if is_sensitive_key(key):
|
||||
result[str(key)] = placeholder
|
||||
else:
|
||||
result[str(key)] = redact_secret_values(item, placeholder=placeholder)
|
||||
return result
|
||||
if isinstance(value, list):
|
||||
return [redact_secret_values(item, placeholder=placeholder) for item in value]
|
||||
return value
|
||||
|
||||
|
||||
def contains_plain_secret(value: object) -> bool:
|
||||
if not isinstance(value, Mapping):
|
||||
return False
|
||||
for key, item in value.items():
|
||||
normalized_key = str(key).strip().casefold().replace("-", "_")
|
||||
if normalized_key in {"credential_ref", "secret_ref", "secret_reference"}:
|
||||
continue
|
||||
if is_sensitive_key(key) and item not in (None, "", {"secret_ref": ""}):
|
||||
return True
|
||||
if isinstance(item, Mapping) and contains_plain_secret(item):
|
||||
return True
|
||||
return False
|
||||
Reference in New Issue
Block a user