"""Vasker en logg eller feilmelding før den limes inn i en AI-tjeneste. python vask_logg.py logg_eksempel.txt python snitt.py okter.csv 2>&1 | python vask_logg.py Hører til gribben.no/tech/losninger/feilsok-med-ai.html. Bare standardbiblioteket. Vaskeren i nettleseren på samme side bruker de samme ni reglene i samme rekkefølge. Der \\w står her, står [\\p{L}\\p{N}_] i JavaScript, fordi \\w i JavaScript bare kjenner a–z og ville latt æøå-adresser slippe halvveis. Bytter ut det som oftest lekker: filstier med brukernavn, brukernavn og passord i adresser, Authorization-hoder, kjente tokenformater, nøkkel=verdi, e-post, IP-adresser, elleve siffer (som kan være fødselsnummer) og norske telefonnumre. Rekkefølgen betyr noe: nøkler tas før de generelle mønstrene, ellers blir en del av en nøkkel stående igjen. Dette er et grovfilter, aldri en garanti. Det kjenner ikke navn, adresser, kundenumre i ditt format eller nøkler med et navn det ikke har sett. Les den vaskede teksten selv, linje for linje, før du limer den inn. """ import re import sys REGLER = [ # 1. Filstier: behold filnavnet, fjern mappene. De sier hvem du er. (re.compile(r'(?:(?/"), # 2. Brukernavn og passord i en adresse: https://ola:hemmelig@server (re.compile(r"(\w+://)[^/\s:@]+:[^/\s@]+@"), r"\1@"), # 3. Authorization-hoder: Bearer og Basic (re.compile(r"(?i)\b(bearer|basic)\s+[A-Za-z0-9._~+/=-]{8,}"), r"\1 "), # 4. Tokens med kjent form: JWT, GitHub, AWS-nøkkel-ID, sk-nøkler, Slack (re.compile(r"\beyJ[\w-]+\.[\w-]+\.[\w-]+" r"|\b(?:ghp|gho|ghu|ghs|ghr|github_pat)_\w{20,}" r"|\bAKIA[0-9A-Z]{16}\b" r"|\bsk-[\w-]{20,}" r"|\bxox[abpr]-[\w-]{10,}"), ""), # 5. nøkkel=verdi, også GITHUB_TOKEN=, SECRET_KEY: og "password": "...". # «key» alene tas ikke, ellers forsvinner key=lambda fra Python-kode. (re.compile(r"(?i)([\w-]*(?:api[_-]?key|[_-]key|token|secret|passord|password|pwd|n[oø]kkel))" r"[\"']?\s*[=:]\s*(\"[^\"]*\"|'[^']*'|\S+)"), r"\1="), # 6. E-post (re.compile(r"[\w.+-]+@[\w-]+(?:\.[\w-]+)+"), ""), # 7. IPv4 (re.compile(r"\b(?:\d{1,3}\.){3}\d{1,3}\b"), ""), # 8. Elleve siffer, også 6 + 5 med mellomrom, kan være et fødselsnummer (re.compile(r"(?"), # 9. Norske telefonnumre: 3-2-3 eller 2-2-2-2, med eller uten +47. # Bare vanlig mellomrom som skille, så datoer som 2026-10-08 får stå. (re.compile(r"(?"), ] def vask(tekst: str) -> str: for mønster, erstatning in REGLER: tekst = mønster.sub(erstatning, tekst) return tekst def les_fil(sti: str) -> str: # Filer lagret fra norsk Excel eller eldre Windows-programmer er ofte # cp1252, ikke UTF-8. Da leses de som cp1252, og vi sier fra. try: with open(sti, encoding="utf-8-sig") as f: return f.read() except UnicodeDecodeError: print(f"Merk: {sti} er ikke UTF-8, leser den som cp1252 (Windows).", file=sys.stderr) with open(sti, encoding="cp1252") as f: return f.read() if __name__ == "__main__": sys.stdout.reconfigure(encoding="utf-8") sys.stderr.reconfigure(encoding="utf-8") if len(sys.argv) > 1: inn = les_fil(sys.argv[1]) else: # Fra en pipe: tegn som ikke er UTF-8, blir byttet ut, ikke krasj. sys.stdin.reconfigure(encoding="utf-8", errors="replace") inn = sys.stdin.read() print(vask(inn), end="")