feat: secrets masqués — couverture universelle clés API/mots de passe + clic pour copier (#188)
This commit is contained in:
@@ -845,6 +845,95 @@ class TestSecretRedactor:
|
||||
text = f"value {blob} end"
|
||||
assert redact_file_content(text) == text
|
||||
|
||||
# ── Feature #188 : couverture universelle + clic → copie ──────────
|
||||
|
||||
@pytest.mark.parametrize("secret", [
|
||||
"sk-" + "a" * 30, # OpenAI / OpenRouter
|
||||
"sk_live_" + "b" * 24, # Stripe
|
||||
"whsec_" + "c" * 24, # Stripe / Svix
|
||||
"glpat-" + "d" * 24, # GitLab
|
||||
"github_pat_" + "e" * 30, # GitHub fine-grained
|
||||
"npm_" + "f" * 36, # npm
|
||||
"dckr_pat_" + "g" * 24, # Docker Hub
|
||||
"hf_" + "h" * 32, # Hugging Face
|
||||
"AIza" + "I" * 35, # Google API key
|
||||
"ya29." + "J" * 25, # Google OAuth
|
||||
"xoxb-" + "K" * 24, # Slack
|
||||
"SG." + "L" * 24, # SendGrid
|
||||
"re_" + "M" * 40, # Resend
|
||||
"sq0atp-" + "N" * 24, # Square
|
||||
"ATATT" + "O" * 24, # Atlassian
|
||||
"AKIA" + "P" * 16, # AWS
|
||||
"ASIA" + "Q" * 16, # AWS STS
|
||||
"ghp_" + "R" * 36, # GitHub legacy
|
||||
"123456789:AA" + "S" * 33, # Telegram bot (35 chars after ":")
|
||||
"Bearer " + "T" * 32, # Authorization header
|
||||
])
|
||||
def test_universal_provider_keys_masked(self, secret):
|
||||
"""#188 — the large majority of token formats end up masked."""
|
||||
from backend.secret_redactor import redact_file_content
|
||||
result = redact_file_content(f"here: {secret}")
|
||||
assert secret not in result
|
||||
assert "MASQUÉ" in result
|
||||
|
||||
@pytest.mark.parametrize("text", [
|
||||
"password=hunter2",
|
||||
"PASSWORD=short",
|
||||
"db_password=abc12345",
|
||||
'password: "quoted1"',
|
||||
'{"passwd": "jsonpass"}',
|
||||
"passphrase=longenough",
|
||||
"mot de passe=chezmoi",
|
||||
"user_pwd=s3cret",
|
||||
"token=abcdef12", # 8+ char generic values are masked too
|
||||
])
|
||||
def test_passwords_masked_any_length(self, text):
|
||||
"""#188 — passwords are masked whatever their length / quoting."""
|
||||
from backend.secret_redactor import redact_file_content
|
||||
result = redact_file_content(text)
|
||||
assert "MASQUÉ" in result
|
||||
# No raw value left behind
|
||||
assert text.split("=", 1)[-1].split(":", 1)[-1].strip('"\' ') not in result
|
||||
|
||||
def test_pwd_env_var_not_masked(self):
|
||||
"""``PWD=`` is a shell working directory, not a password."""
|
||||
from backend.secret_redactor import redact_file_content
|
||||
text = "PWD=/home/bruno/notes"
|
||||
assert redact_file_content(text) == text
|
||||
|
||||
def test_placeholders_restore_clickable_mask(self):
|
||||
"""#188 — placeholders become spans carrying the real value."""
|
||||
from backend.secret_redactor import redact_with_placeholders, restore_masks
|
||||
secret = "sk-" + "a" * 30
|
||||
text, entries = redact_with_placeholders(f"key: {secret}")
|
||||
assert secret not in text and entries
|
||||
html = restore_masks(text, entries, click_to_copy=True)
|
||||
assert f'data-secret="{secret}"' in html
|
||||
assert 'class="secret-mask"' in html
|
||||
# Plain restore never leaks the value
|
||||
plain = restore_masks(text, entries)
|
||||
assert secret not in plain and "data-secret" not in plain
|
||||
|
||||
def test_render_markdown_masks_clickable_in_app(self):
|
||||
"""#188 — the authenticated preview serves clickable masks."""
|
||||
from backend.render import _render_markdown
|
||||
secret = "sk-" + "a" * 30
|
||||
md = f"```bash\nexport KEY={secret}\n```\n"
|
||||
html = _render_markdown(md, "test_vault", None, click_to_copy=True)
|
||||
# The value only ever appears inside the data-secret attribute
|
||||
assert 'class="secret-mask"' in html
|
||||
assert f'data-secret="{secret}"' in html
|
||||
assert f">{secret}" not in html # never as visible text
|
||||
|
||||
def test_render_markdown_plain_without_click(self):
|
||||
"""Public shares / PDF exports get the plain label only."""
|
||||
from backend.render import _render_markdown
|
||||
secret = "sk-" + "a" * 30
|
||||
html = _render_markdown(f"Cle: {secret}\n", "test_vault", None)
|
||||
assert secret not in html
|
||||
assert "[CLÉ API MASQUÉE]" in html
|
||||
assert "data-secret" not in html
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# Static / PWA caching (Cloudflare / mobile freshness)
|
||||
|
||||
Reference in New Issue
Block a user