Hand 80% of your work to AI. Pay for the result 

Fajl dana

Skripta za Claude Code koja pronalazi lozinke ostavljene u fajlovima kao običan tekst

Slava Sazhin 9. oktobar 2026 3 min čitanja

Zaštitite svoje naloge i novac: Claude Code pronalazi fajlove u kojima su vaše lozinke običan tekst, pa ih na vreme uklonite.

Današnji „Fajl dana“ je skripta za Claude Code. Lozinka za banku u belešci, lozinka za Wi-Fi u zapisniku sa sastanka, tabela sa nalozima u Excelu, API ključ u .env fajlu projekta: ko dobije kopiju takve fascikle, dobija i naloge, preko izgubljenog laptopa, deljenog diska ili bekapa na pogrešnom mestu. Skripta pronalazi takve fajlove, pa vi stignete prvi da iz njih uklonite lozinke.

Kako radi

Claude pokreće find_secrets.py na fascikli koju navedete. Skripta čita svaki fajl u njoj, uključujući Word dokumente (.docx), Excel tabele (.xlsx) i CSV fajlove, i ispisuje putanju do svakog fajla u kome je lozinka, ključ za pristup ili privatni ključ, i šta je tačno tamo pronašla. Samu lozinku nikad ne ispisuje, pa vrednosti ne završe u četu, i ništa ne menja. Lozinka se računa kad pored nje stoji reč „lozinka“ na engleskom, ruskom, portugalskom ili srpskom (password, пароль, senha, lozinka); dovoljna je i kolona „Lozinka“ u tabeli. Lozinku pored koje takve reči nema skripta neće pronaći. Ključeve prepoznaje po izgledu: privatni ključevi, AWS i GitLab ključevi, JWT tokeni, fajlovi Google servisnih naloga. Fascikle .git, node_modules i venv preskače, a kod velikog fajla čita samo prva 2 MB.

U našem testu navela je svih 21 fajl koje smo podmetnuli: beleške sa lozinkama na četiri jezika, tabelu lozinki u Wordu i u Excelu, izvoz iz pregledača u CSV-u, .env fajl, privatni ključ, AWS, GitLab i JWT tokene, string za povezivanje sa bazom podataka. Šablone kao što su changeme, ${DB_PASSWORD} i os.environ i rečenicu kao „nikome ne govorite svoju lozinku“ preskočila je. Zatim smo na sva četiri jezika zamolili Claude-a da je pokrene na probnoj fascikli Documents i pokaže šta je pronašla. U 6 pokretanja od 6 Claude je naveo fajlove i šta je u njima, nijedan nije otvorio i ponudio je da pogleda unutra samo ako to zatražite.

Šta promeniti

Logiku nismo menjali. Dva znaka crte unutar jednog obrasca za pretragu zamenili smo njihovim kodovima, da bi engleski fajl bio čist ASCII; pronalazi potpuno isto. Pokrećite skriptu na fascikli sa dokumentima, a ne na celoj početnoj fascikli: velika fascikla traje minutima, do sekunde po svakom velikom fajlu, a u početnoj fascikli ima i fajlova u kojima ključ treba da stoji, na primer u ~/.ssh. Da biste hvatali lozinku pored reči na drugom jeziku, recimo Passwort, zamolite Claude-a da je doda u red LABELS u skripti. Kad skripta navede fajl, otvorite ga sami, prebacite lozinku u menadžer lozinki i promenite je ako je fascikla negde deljena. Ako zamolite Claude-a da otvori fajl, lozinka završava u četu.

Kako instalirati

Tačni zahtevi su u uputstvu ispod. Prvi stavlja skriptu u ~/.claude/scripts i proverava da li na računaru postoji python3. U toj fascikli su podešavanja samog Claude-a, pa će pre upisa tražiti dozvolu: odgovorite Yes. Drugi je pokreće na vašoj fascikli Documents. Provera iz uputstva: probna fascikla sa fajlom note.txt u kome je red „Lozinka za mejl: Kofe2026sad“. Skripta navodi note.txt i reč „lozinka“, a Kofe2026sad ne ispisuje.

find_secrets.py 5 KB Preuzmi
#!/usr/bin/env python3
"""Pronalazi u fascikli fajlove u kojima je lozinka ili ključ za pristup zapisan kao običan tekst.

Pokretanje: python3 find_secrets.py <fascikla>
Ispisuje putanju do fajla i šta je u njemu pronađeno. Same lozinke nikad ne ispisuje.
Ništa ne menja, samo čita. Čita i Word dokumente i Excel tabele.
"""
import html
import os
import re
import sys
import zipfile

# Koliko se čita iz jednog fajla: kod velikog fajla čita se samo početak.
READ_MAX = 2_000_000
# Fascikle u koje provera ne ulazi.
SKIP_DIRS = {'.git', 'node_modules', '__pycache__', '.venv', 'venv'}
# Reč „lozinka“ na engleskom, ruskom, portugalskom i crnogorskom.
LABELS = r'password|passwd|pwd|db_pass|\u043f\u0430\u0440\u043e\u043b|senha|lozink'

# Jaki znaci: ovakav tekst ima samo pravi ključ.
STRONG = [
    ('privatni ključ', re.compile(r'-----BEGIN [A-Z ]*PRIVATE KEY-----')),
    ('JWT token', re.compile(r'\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.')),
    ('ključ servisnog naloga Google', re.compile(r'"type"\s*:\s*"service_account"')),
    ('GitLab token', re.compile(r'glpat-[A-Za-z0-9_-]{15,}')),
    ('AWS ključ', re.compile(r'\bAKIA[0-9A-Z]{16}\b')),
    ('reč „lozinka“ sama u redu ili ćeliji', re.compile(r'(?im)^[ \t"]*(?:' + LABELS + r')\w{0,30}(?:[ \t]+\w{1,30}){0,2}[ \t"\r]*$')),
]
# Slabi znaci: reč kao „lozinka“, secret ili api_key i vrednost posle nje.
SOFT = [
    ('lozinka', re.compile(r'(?i)\b(?:' + LABELS + r')[\w \t"\'-]{0,60}[=:]\s*(["\']?)(\S{6,})')),
    ('lozinka', re.compile(r'(?i)\b(?:' + LABELS + r')\w{0,30}(?:[ \t]+\S{1,30}){0,6}?'
                          r'(?:[ \t]*[-\u2013\u2014:=][ \t]*|[ \t]+)(["\']?)((?=\S{0,200}?[\d!@#$%^&*?_+])\S{6,200})')),
    ('tajna', re.compile(r'(?i)\b[A-Z0-9_]*secret[A-Z0-9_]*["\']?\s*[=:]\s*(["\']?)(\S{8,})')),
    ('ključ za pristup', re.compile(r'(?i)\b[A-Z0-9_]*(?:api[_-]?key|apikey|access[_-]?key|auth[_-]?'
                                r'token|bot[_-]?token)[A-Z0-9_]*["\']?\s*[=:]\s*(["\']?)(\S{8,})')),
    ('lozinka u konekcionom stringu', re.compile(r'(?i)(?:Password|Pwd)\s*=\s*([^;\s]{4,});')),
]
# Vrednost je referenca na promenljivu ili podešavanje, a ne sama lozinka.
REFERENCE = re.compile(r"""(?ix)^(
   os\.environ|os\.getenv|environ|getenv|process\.env|System\.|Environment\.|configuration|
   config\.|self\.|this\.|cfg\.|settings\.|require\(|import\b|input\(|prompt
)""")
BRACKET = re.compile(r'^(\$\{?[A-Za-z_]|%\(|\{\{|\{[A-Za-z_]|<[A-Za-z_]|@[A-Za-z_]|\[)')
# Vrednost je šablon iz primera, a ne lozinka. Poredi se u celini.
DUMMY = re.compile(r'(?ix)^(None|null|nil|true|false|undefined|x{3,}|y{3,}|\*+|\.{3,}|'
                   r'change_?me|your_?\w*|test|password|secret|dummy|example|sample|'
                   r'placeholder|\d{1,4}|["\'`]*)$')


def value(m):
    g = [x for x in m.groups() if x is not None]
    return (g[-1] if g else '').strip('"\'`,;)')


def is_secret(v):
    if not v or len(v) < 6:
        return False
    if REFERENCE.match(v) or BRACKET.match(v):
        return False
    if DUMMY.fullmatch(v):
        return False
    return True


def read_text(path):
    """Tekst fajla. Kod Word dokumenta ili Excel tabele, tekst iz njihovih delova, po jedan pasus ili ćelija u redu."""
    low = path.lower()
    if low.endswith(('.docx', '.xlsx')):
        with zipfile.ZipFile(path) as z:
            parts = [z.open(n).read(READ_MAX) for n in z.namelist() if n.endswith('.xml')]
        text = b'\n'.join(parts).decode('utf-8', 'ignore')
        text = re.sub(r'</(?:w:p|w:tc|si|c|row)>', '\n', text)
        return html.unescape(re.sub(r'<[^>]+>', '', text))
    with open(path, 'rb') as fh:
        text = fh.read(READ_MAX).decode('utf-8', 'ignore')
    # U CSV tabeli svako polje ide u poseban red.
    return re.sub(r'[,;\t]', '\n', text) if low.endswith(('.csv', '.tsv')) else text


def signs(path):
    """Šta je pronađeno u fajlu. Same vrednosti se nikad ne vraćaju."""
    try:
        data = read_text(path)
    except Exception:
        return []
    found = [name for name, rx in STRONG if rx.search(data)]
    for name, rx in SOFT:
        for m in rx.finditer(data):
            if is_secret(value(m)):
                found.append(name)
                break
    return sorted(set(found))


def main():
    sys.stdout.reconfigure(encoding='utf-8', errors='replace')
    root = sys.argv[1] if len(sys.argv) > 1 else '.'
    if not os.path.isdir(root):
        sys.exit(f'Nema takve fascikle: {root}')
    total = 0
    for folder, dirs, files in os.walk(root):
        dirs[:] = [d for d in dirs if d not in SKIP_DIRS]
        for f in files:
            path = os.path.join(folder, f)
            if os.path.islink(path) or not os.path.isfile(path):
                continue
            found = signs(path)
            if found:
                total += 1
                print(f'{path}: {", ".join(found)}')
    print(f'Fajlova sa otvorenom lozinkom ili ključem: {total}')


if __name__ == '__main__':
    main()
kako-instalirati.md 2 KB Preuzmi
# Kako pronaći lozinke koje u vašim fajlovima stoje kao običan tekst

1. Zamolite Claude-a: „Stavi preuzeti fajl find_secrets.py u fasciklu ~/.claude/scripts i proveri da li na računaru postoji python3; ako ne postoji, instaliraj ga“.
2. Zamolite Claude-a: „Pokreni ~/.claude/scripts/find_secrets.py na fascikli $HOME/Documents i pokaži mi šta je pronašao“. Umesto Documents navedite fasciklu u kojoj su vaši radni fajlovi.
3. Skripta ispisuje putanju do svakog pronađenog fajla i šta je u njemu: lozinka, ključ za pristup ili privatni ključ. Lozinku traži pored reči „lozinka“ ili password, a lozinku pored koje takve reči nema neće pronaći. Čita i Word dokumente, Excel tabele i CSV fajlove. Same lozinke ne prikazuje i ništa u fajlovima ne menja.

Provera:

1. Zamolite Claude-a: „Napravi fasciklu $HOME/secrets-test, stavi u nju fajl note.txt sa redom Lozinka za mejl: Kofe2026sad i pokreni ~/.claude/scripts/find_secrets.py na toj fascikli“. Skripta ispisuje putanju do fajla note.txt i reč „lozinka“, a samu lozinku ne prikazuje.
2. Zamolite Claude-a: „Obriši fasciklu $HOME/secrets-test“.

Da li je ovaj članak bio vredan vašeg vremena?

Novi master klas svake nedelje

Praktičar pred kamerom prolazi kroz postavku koja radi i predaje vam sve fajlove iz nje. $11 mesečno.

Najnoviji snimak · 30. septembar 2026 Claude apologised and promised never to do it again? Do not trust promises, set up hooks Slava Sazhin
Svi master klasovi

Kontaktirajte tim Combobulating