Feature: Redaction des secrets dans les rapports
Contexte
AttackSim ne possède aucune fonction de redaction dans report/reporter.py. Les champs evidence, detail, location sont écrits en clair dans tous les formats de sortie (console, JSON, SARIF, HTML, Markdown). Un module qui capture un mot de passe opérateur, un token JWT, ou un secret application (ex: secret_scanner qui récolte des creds Kafka/Okta) le fait fuiter intégralement dans l'artefact de rapport, qui peut être archivé, commité ou partagé.
Ceci est contradictoire avec la promesse centrale du produit : "credentials never written to the report".
Description
Ajouter une couche de redaction automatique appliquée à tous les champs textuels des findings avant sérialisation dans les rapports.
Spécifications fonctionnelles
Patterns à détecter et masquer
| Type |
Regex |
Remplacement |
Exemple |
| AWS Access Key ID |
AKIA[0-9A-Z]{16} |
AKIA****REDACTED |
AKIAIOSFODNN7EXAMPLE → AKIA****REDACTED |
| AWS Secret Key |
[A-Za-z0-9/+=]{40} après aws_secret_access_key |
****REDACTED |
contextuel |
| JWT |
eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,} |
eyJ****REDACTED.****.**** |
|
| Private key PEM |
-----BEGIN (RSA |EC |OPENSSH |PGP )?PRIVATE KEY-----[\s\S]*?-----END |
[REDACTED PRIVATE KEY] |
|
| Password in URL |
://([^:]+):([^@]+)@ |
://$1:****@ |
http://admin:secret@host → http://admin:****@host |
| Bearer token |
Bearer\s+[A-Za-z0-9._\-+/=]{20,} |
Bearer ****REDACTED |
|
| Slack webhook |
https://hooks.slack.com/services/[A-Z0-9/]+ |
[REDACTED SLACK WEBHOOK] |
|
| GitHub token |
gh[pousr]_[A-Za-z0-9]{36,} |
ghp_****REDACTED |
|
| Generic API key |
[A-Za-z0-9]{32,} après api_key|apikey|api-key|token (contextuel) |
****REDACTED |
|
| Connection string |
mongodb(\+srv)?://[^:]+:[^@]+@ |
mongodb://$1:****@ |
|
| Credit card |
\b(?:\d[ -]*?){13,16}\b (Luhn check) |
[REDACTED CC] |
|
| Private IP in evidence |
(optionnel) 10\.\d+\.\d+\.\d+ |
garder (info utile) |
|
| Email |
(optionnel, configurable) |
garder par défaut |
|
Champs traités
Appliquer la redaction sur tous les champs textuels du finding :
title (mais garder le contexte : AWS access key appears live: AKIA****REDACTED)
evidence
detail
location (masquer le password dans l'URL seulement)
refs (pas masqué — ce sont des CWE/CVE)
Mode
- Par défaut : redaction active sur tous les rapports
--no-redact : désactive la redaction (pour debug, nécessite --yes)
- L'état de la redaction est tracé dans le rapport :
"redaction": "enabled" ou "redaction": "disabled (--no-redact)"
Output
{
"name": "secret-harvester",
"title": "AWS access key appears live: AKIA****REDACTED",
"evidence": "STS response HTTP 200: 'Arn': 'arn:aws:iam::123456:****REDACTED'",
"redacted": true,
"redaction_patterns_applied": ["aws_access_key_id", "aws_secret_key"]
}
Spécifications techniques
Fichier à créer
sandbox/redaction.py
import re
from dataclasses import dataclass
@dataclass
class RedactionPattern:
name: str
pattern: re.Pattern
replacement: str
PATTERNS = [
RedactionPattern("aws_access_key_id",
re.compile(r"AKIA[0-9A-Z]{16}"), "AKIA****REDACTED"),
RedactionPattern("aws_secret_key",
re.compile(r"(aws_secret_access_key\s*[=:]\s*)([A-Za-z0-9/+=]{40})"),
r"\1****REDACTED"),
RedactionPattern("jwt",
re.compile(r"eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}"),
"eyJ****REDACTED.****.****"),
RedactionPattern("private_key_pem",
re.compile(r"-----BEGIN (?:RSA |EC |OPENSSH |PGP )?PRIVATE KEY-----[\s\S]*?-----END"),
"[REDACTED PRIVATE KEY]"),
RedactionPattern("password_in_url",
re.compile(r"(://[^:/@\s]+:)([^@\s]+)(@)"), r"\1****\3"),
RedactionPattern("bearer_token",
re.compile(r"(Bearer\s+)[A-Za-z0-9._\-+/=]{20,}"), r"\1****REDACTED"),
RedactionPattern("slack_webhook",
re.compile(r"https://hooks\.slack\.com/services/[A-Z0-9/]+"),
"[REDACTED SLACK WEBHOOK]"),
RedactionPattern("github_token",
re.compile(r"gh[pousr]_[A-Za-z0-9]{36,}"), "ghp_****REDACTED"),
RedactionPattern("mongodb_connstring",
re.compile(r"(mongodb(?:\+srv)?://[^:]+:)([^@]+)(@)"), r"\1****\3"),
RedactionPattern("generic_api_key",
re.compile(r"((?:api_key|apikey|api-key|token)\s*[=:]\s*)([A-Za-z0-9]{32,})"),
r"\1****REDACTED"),
]
def redact(text: str, patterns=None) -> tuple[str, list[str]]:
if not text or not isinstance(text, str):
return text, []
applied = []
for p in (patterns or PATTERNS):
if p.pattern.search(text):
text = p.pattern.sub(p.replacement, text)
applied.append(p.name)
return text, applied
Fichier à modifier
report/reporter.py
Changements
- Importer
from sandbox.redaction import redact
- Ajouter un paramètre
redact: bool = True à render_console, write_json, write_sarif, write_html, write_markdown
- Avant chaque sérialisation d'un finding, appliquer
redact() sur title, evidence, detail, location
- Ajouter le champ
"redacted": True/False et "redaction_patterns_applied": [...] au JSON/SARIF
- Le metadata du rapport inclut
"redaction": "enabled" | "disabled (--no-redact)"
Changements dans main.py
- Ajouter l'argument
--no-redact (necessite --yes)
- Passer
redact=not args.no_redact aux fonctions du reporter
Changements dans exploits/secret_scanner.py
- Ligne 131 :
title=f"AWS access key appears live: {_access_key_id[:8]}****" (tronquer dans le titre)
- Le
detail et evidence sont redacted par la couche globale
Tests
tests/test_redaction.py (nouveau) :
redact("token=AKIAIOSFODNN7EXAMPLE") → "token=AKIA****REDACTED", applied=["aws_access_key_id"]
redact("Authorization: Bearer eyJhbGci...") → bearer masqué
redact("-----BEGIN RSA PRIVATE KEY-----...") → [REDACTED PRIVATE KEY]
redact("http://admin:secret@host/") → "http://admin:****@host/"
redact("mongodb://user:pass@host/") → "mongodb://user:****@host/"
redact("no secrets here") → "no secrets here", applied=[]
redact("") → "", applied=[]
redact(None) → None, applied=[]
tests/test_reporter_outputs.py (existant) — vérifier que les tests passent toujours avec redaction active.
Acceptance criteria
Références
Feature: Redaction des secrets dans les rapports
Contexte
AttackSim ne possède aucune fonction de redaction dans
report/reporter.py. Les champsevidence,detail,locationsont écrits en clair dans tous les formats de sortie (console, JSON, SARIF, HTML, Markdown). Un module qui capture un mot de passe opérateur, un token JWT, ou un secret application (ex:secret_scannerqui récolte des creds Kafka/Okta) le fait fuiter intégralement dans l'artefact de rapport, qui peut être archivé, commité ou partagé.Ceci est contradictoire avec la promesse centrale du produit : "credentials never written to the report".
Description
Ajouter une couche de redaction automatique appliquée à tous les champs textuels des findings avant sérialisation dans les rapports.
Spécifications fonctionnelles
Patterns à détecter et masquer
AKIA[0-9A-Z]{16}AKIA****REDACTEDAKIAIOSFODNN7EXAMPLE→AKIA****REDACTED[A-Za-z0-9/+=]{40}aprèsaws_secret_access_key****REDACTEDeyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}eyJ****REDACTED.****.****-----BEGIN (RSA |EC |OPENSSH |PGP )?PRIVATE KEY-----[\s\S]*?-----END[REDACTED PRIVATE KEY]://([^:]+):([^@]+)@://$1:****@http://admin:secret@host→http://admin:****@hostBearer\s+[A-Za-z0-9._\-+/=]{20,}Bearer ****REDACTEDhttps://hooks.slack.com/services/[A-Z0-9/]+[REDACTED SLACK WEBHOOK]gh[pousr]_[A-Za-z0-9]{36,}ghp_****REDACTED[A-Za-z0-9]{32,}aprèsapi_key|apikey|api-key|token(contextuel)****REDACTEDmongodb(\+srv)?://[^:]+:[^@]+@mongodb://$1:****@\b(?:\d[ -]*?){13,16}\b(Luhn check)[REDACTED CC]10\.\d+\.\d+\.\d+Champs traités
Appliquer la redaction sur tous les champs textuels du finding :
title(mais garder le contexte :AWS access key appears live: AKIA****REDACTED)evidencedetaillocation(masquer le password dans l'URL seulement)refs(pas masqué — ce sont des CWE/CVE)Mode
--no-redact: désactive la redaction (pour debug, nécessite--yes)"redaction": "enabled"ou"redaction": "disabled (--no-redact)"Output
{ "name": "secret-harvester", "title": "AWS access key appears live: AKIA****REDACTED", "evidence": "STS response HTTP 200: 'Arn': 'arn:aws:iam::123456:****REDACTED'", "redacted": true, "redaction_patterns_applied": ["aws_access_key_id", "aws_secret_key"] }Spécifications techniques
Fichier à créer
sandbox/redaction.pyFichier à modifier
report/reporter.pyChangements
from sandbox.redaction import redactredact: bool = Trueàrender_console,write_json,write_sarif,write_html,write_markdownredact()surtitle,evidence,detail,location"redacted": True/Falseet"redaction_patterns_applied": [...]au JSON/SARIF"redaction": "enabled" | "disabled (--no-redact)"Changements dans
main.py--no-redact(necessite--yes)redact=not args.no_redactaux fonctions du reporterChangements dans
exploits/secret_scanner.pytitle=f"AWS access key appears live: {_access_key_id[:8]}****"(tronquer dans le titre)detailetevidencesont redacted par la couche globaleTests
tests/test_redaction.py(nouveau) :redact("token=AKIAIOSFODNN7EXAMPLE")→"token=AKIA****REDACTED", applied=["aws_access_key_id"]redact("Authorization: Bearer eyJhbGci...")→ bearer masquéredact("-----BEGIN RSA PRIVATE KEY-----...")→[REDACTED PRIVATE KEY]redact("http://admin:secret@host/")→"http://admin:****@host/"redact("mongodb://user:pass@host/")→"mongodb://user:****@host/"redact("no secrets here")→"no secrets here", applied=[]redact("")→"", applied=[]redact(None)→None, applied=[]tests/test_reporter_outputs.py(existant) — vérifier que les tests passent toujours avec redaction active.Acceptance criteria
--no-redactdisponible (nécessite--yes)"redaction": "enabled/disabled")secret_scannerne fait plus fuiter d'access key ID complet dans le titreRéférences