CodeWorlds
Powrót do kolekcji
Przewodnik20 min czytaniaZespół CodeWorlds

Semgrep, wzorce zamiast regexpów i granica wersji Pro

Semgrep 1.174.0 na LGPL 2.1: co potrafi silnik otwarty, co wymaga konta i opłaty, jak napisać własną regułę YAML i czym tłumić fałszywe alarmy.

Semgrep, wzorce zamiast regexpów i granica wersji Pro

Semgrep dopasowuje reguły do drzewa składniowego, a nie do tekstu, więc zmiana nazwy zmiennej albo łamania linii nie psuje wzorca. Wersja 1.174.0 z 20 sierpnia 2026 roku jest wydana na LGPL 2.1, ale otwarty jest sam silnik. Analiza międzyplikowa, trzy języki, wykrywanie sekretów i reguły z rejestru wymagają konta, a w większości przypadków także opłaty.

Jak działa dopasowanie do składni

Zwykły grep pracuje na znakach. Jeśli szukasz wywołania z niebezpiecznym argumentem, musisz przewidzieć wszystkie zapisy: spacje wokół przecinka, argumenty nazwane w innej kolejności, wywołanie rozbite na trzy linie, zmienną nazwaną raz cmd, raz command. Wyrażenie regularne, które to obejmie, staje się nieczytelne po kilku iteracjach i i tak przegapi czwarty wariant.

Semgrep parsuje plik do drzewa składniowego i porównuje z nim wzorzec zapisany w składni docelowego języka. Wzorzec subprocess.$FUNC(..., shell=$TRUE, ...) czyta się jak kod Pythona, bo nim jest, z dwoma dodatkami. $FUNC i $TRUE to metazmienne, które łapią dowolne wyrażenie i zapamiętują je do dalszych warunków. Trzy kropki to operator wielokropka, który dopasowuje dowolną liczbę argumentów, instrukcji albo elementów w danym miejscu. Formatowanie, komentarze i nazwy własne zmiennych przestają mieć znaczenie.

Najkrótsza droga do sprawdzenia tego pomysłu nie wymaga pliku z regułą.

Code
Bash
# instalacja do własnego środowiska wirtualnego
python3 -m venv .venv && .venv/bin/pip install semgrep==1.174.0

# jednorazowy wzorzec bez pliku reguł
semgrep --lang python --pattern 'subprocess.$FUNC(..., shell=True, ...)' src/

# wzorzec z podmianą, tryb podglądu
semgrep --lang python --pattern 'assert $X' --replacement 'if not $X: raise AssertionError' --dryrun src/

# reguły z pliku lokalnego, bez sieci i bez telemetrii
semgrep scan --config ./rules --metrics off .

# wypisanie plików, które faktycznie trafią do skanu
semgrep scan --config ./rules --x-ls .

Flaga --metrics przyjmuje wartości auto, on i off. Domyślne auto oznacza, według komentarza w metrics.py, że dane są wysyłane tylko wtedy, gdy konfiguracja została pobrana z serwera. Skan wyłącznie na regułach lokalnych nic nie wysyła. Skan z --config p/default wysyła, i to jest zachowanie domyślne, o którym łatwo zapomnieć przy wprowadzaniu narzędzia w firmie z restrykcyjną polityką danych.

Wersja, licencja i metadane, które milczą

Bieżące wydanie to 1.174.0. Paczka źródłowa trafiła na PyPI 20 sierpnia 2026 roku o 15:59 UTC, koła kilka minut później, a wpis wydania w repozytorium nosi znacznik z 15:58 UTC tego samego dnia. Dziewięć wydań ukazało się między 11 czerwca a 20 sierpnia 2026 roku, czyli średnio co dziewięć dni, z jedną dłuższą przerwą szesnastu dni na przełomie lipca i sierpnia. W rejestrze leżą 353 wersje i ani jedna nie jest oznaczona jako wycofana. Ostatnia zmiana w gałęzi develop pochodzi z 20 sierpnia 2026 roku.

Licencja jest jednoznaczna dopiero wtedy, gdy się jej poszuka we właściwym miejscu. Plik LICENSE w katalogu głównym repozytorium pod znacznikiem v1.174.0 zawiera pełny tekst GNU Lesser General Public License w wersji 2.1 z lutego 1999 roku, 504 linie. cli/LICENSE to dowiązanie symboliczne do tego samego pliku, a jego surowa treść w serwisie to dosłownie ../LICENSE. Poza tym w repozytorium nie ma pliku COPYING, NOTICE, LICENSING.md ani osobnych plików licencyjnych w katalogach src, libs, languages, tools czy interfaces.

Problem zaczyna się w metadanych rejestru. pyproject.toml deklaruje license = "LGPL-2.1-or-later" i license-files = ["LICENSE"], czyli nowy zapis z PEP 639. W efekcie PKG-INFO ma Metadata-Version: 2.4 i pole License-Expression: LGPL-2.1-or-later. Stare pole License jest puste, a w interfejsie JSON serwisu PyPI info.license zwraca null. Co gorsza, lista klasyfikatorów nie zawiera ani jednego wpisu zaczynającego się od License ::. Klasyfikatorów jest jedenaście i opisują wyłącznie system operacyjny, wersje Pythona i kategorię tematyczną.

Praktyczna konsekwencja jest taka, że skaner zależności czytający info.license albo klasyfikatory zobaczy pustkę i zgłosi pakiet jako pozbawiony licencji. Skaner czytający license_expression albo PKG-INFO zobaczy poprawne LGPL-2.1-or-later. Jeśli w firmie masz automat blokujący pakiety bez licencji, ten konkretny wpadnie w pułapkę mimo poprawnie oznaczonego kodu. Warto też odnotować drobną niespójność: nagłówki w plikach źródłowych mówią o wersji 2.1 licencji bez klauzuli o wersjach późniejszych, natomiast wyrażenie w metadanych brzmi or-later. Przy dokładnym audycie zgodności to różnica, którą trzeba rozstrzygnąć z działem prawnym, a nie z dokumentacją.

Zawartość opublikowanej paczki potwierdza deklarację, ale odsłania też podział na dwie części. Archiwum źródłowe waży 507 637 bajtów i zawiera wyłącznie kod Pythona: pakiety semgrep i semdep, plik LICENSE z tekstem LGPL, pyproject.toml i README.md. Silnika tam nie ma. Silnik jest w kołach. Koło dla macOS na architekturze arm64 waży 49 480 382 bajty w postaci skompresowanej i zawiera plik semgrep/bin/semgrep-core o rozmiarze 208 085 424 bajtów, czyli około 198 MiB, wraz z bibliotekami libtree-sitter, libpcre2-8, libgmp, libzstd, libdwarf i libev. Obok leży semgrep-1.174.0.dist-info/licenses/LICENSE o rozmiarze 26 526 bajtów, ten sam tekst LGPL 2.1. Zgodność deklaracji z zawartością jest więc pełna, a kod jest w paczce naprawdę.

Zależności runtime są liczne: dwadzieścia siedem pakietów plus pywin32 na Windowsie. Dwie mają przypięcie dokładne, mcp==1.29.0 i ruamel.yaml.clib==0.2.15, a wymagany Python to co najmniej 3.10. Sam plik pyproject.toml w komentarzu zaleca osobne środowisko wirtualne albo obraz kontenera i nie jest to nadgorliwość, bo przypięcie dokładne dwóch bibliotek potrafi zablokować instalację w środowisku dzielonym z innym narzędziem.

Granica między wersją otwartą a płatną

To jest miejsce, w którym większość tekstów o Semgrepie opisuje możliwości, nie mówiąc, że są płatne. Podział widać wprost w kodzie, w pliku engine.py, gdzie wyliczenie EngineType ma cztery wartości: OSS, PRO_LANG, PRO_INTRAFILE i PRO_INTERFILE.

PoziomFlagaZakres analizyWymaga konta
OSS--oss-onlypojedyncza funkcja, taint w obrębie funkcjinie
PRO_LANG--pro-languagesjak OSS plus Apex, Elixir i Gosutak
PRO_INTRAFILE--pro-intrafilemiędzyfunkcyjna w obrębie jednego plikutak
PRO_INTERFILE--promiędzyplikowa w całym repozytoriumtak

Metoda decide_engine_type ustala domyślny poziom według prostej reguły: zalogowany użytkownik uruchamiający semgrep ci dostaje PRO_INTRAFILE, wszyscy pozostali dostają OSS. Skan różnicowy obniża żądany PRO_INTERFILE do PRO_INTRAFILE, żeby nie wydłużać czasu w zgłoszeniu zmian. Skan wyłącznie łańcucha dostaw robi to samo, bo tryb międzyplikowy domyślnie pracuje na jednym wątku.

Odpowiedź na pytanie z tytułu tej sekcji brzmi więc: analiza międzyfunkcyjna i międzyplikowa nie jest w wersji otwartej. Jest w binarce semgrep-core-proprietary, którą pobiera polecenie semgrep install-semgrep-pro. Kod tego polecenia jest jednoznaczny. Bez tokenu, czyli bez semgrep login albo bez zmiennej SEMGREP_APP_TOKEN, polecenie kończy się kodem błędu nieprawidłowego klucza. Binarka jest pobierana ze ścieżki api/agent/deployments/deepbinary/{platform} na serwerze Semgrepa. Odpowiedź 401 oznacza nieważny token, a 403 komunikat, że zalogowana instalacja nie ma dostępu do silnika Pro. Obok binarki ląduje plik ze znacznikiem wersji, żeby aktualizacja samego pakietu nie zostawiła niezgodnego silnika.

Trzy dodatkowe rzeczy są zamknięte razem z silnikiem. Wykrywanie sekretów zwraca w kodzie wprost wyjątek z komunikatem, że nie jest częścią silnika otwartego, a przy jawnym --oss-only skan przerywa się informacją, że jest to rozszerzenie własnościowe. Śledzenie przepływu danych w wyniku, czyli --dataflow-traces, jest dostępne tylko dla poziomów PRO_INTRAFILE i PRO_INTERFILE. Języki oznaczone jako własnościowe to dokładnie trzy: Apex, Elixir i Gosu. Wiadomo to z pliku lang.json dostarczanego razem z pakietem, gdzie na pięćdziesiąt wpisów tylko te trzy mają znacznik is_proprietary.

Ten sam plik warto przeczytać także z innego powodu. Dojrzałość języków rozkłada się nierówno: jedenaście wpisów ma status ga (C#, Go, JSON, Java, JavaScript, PHP, Python, Ruby, Scala, Terraform, TypeScript), jeden beta (Kotlin), dwadzieścia pięć alpha i trzynaście develop. Strona z cennikiem podaje dla wszystkich planów liczbę „35+” obsługiwanych języków. Obie liczby są prawdziwe w swoich ramach, bo pięćdziesiąt wpisów obejmuje też pozycje pomocnicze w rodzaju generic, regex i aliengrep oraz osobne warianty dla Pythona 2 i 3, ale rozbieżność jest na tyle duża, że przy wyborze narzędzia dla konkretnego stosu lepiej sprawdzić lang.json niż stronę marketingową.

Rejestr reguł jest usługą, a nie częścią otwartego kodu. Widać to w config_resolver.py: identyfikatory z przedrostkami r/, p/ i s/ (reguła, paczka, wycinek) są zamieniane na adres {semgrep_url}/c/{id}, a --config auto na {semgrep_url}/c/auto. W repozytorium LGPL nie ma ani jednej reguły produkcyjnej. Reguły edycji społecznościowej mieszkają w osobnym repozytorium semgrep-rules i tam liczyłem 2091 identyfikatorów w 2076 plikach YAML, z czego 378 dla Pythona, 351 dla Terraforma, 257 dla trybu generycznego, 182 dla JavaScriptu, 132 w katalogu dotyczącym bibliotek AI i 130 dla Javy.

Licencja tego repozytorium to osobna sprawa i jest to najczęściej przeoczany punkt całej układanki. Plik LICENSE zawiera jedno zdanie odsyłające do Semgrep Rules License v1.0, dokumentu z datą ostatniej aktualizacji 13 grudnia 2024 roku. To nie jest licencja otwarta. Udzielony zakres to licencja niewyłączna, bezpłatna, ogólnoświatowa, bez prawa sublicencjonowania i bez prawa przeniesienia. Ograniczenie jest sformułowane przez cel, a nie przez próg: reguł wolno używać wyłącznie do własnych wewnętrznych celów biznesowych. Licencja nie pozwala ich rozpowszechniać ani udostępniać innym jako usługę. Nie wolno usuwać ani zasłaniać not licencyjnych, a kopiując regułę trzeba przenieść notę razem z nią. Modyfikacje trzeba wyraźnie oznaczyć w kopii. Jest klauzula patentowa, która wygasa natychmiast, jeśli użytkownik albo jego firma zgłosi roszczenie patentowe wobec reguł lub dowolnego produktu Semgrepa. Naruszenie warunków kończy licencję automatycznie.

Czego w tej licencji nie ma, jest równie ważne. Nie ma progu liczby użytkowników, progu przychodu ani daty przekształcenia w licencję otwartą, jaką znamy z modelu Business Source License. Ograniczenie jest trwałe i dotyczy rodzaju użycia. Konsekwencje praktyczne: wolno uruchamiać te reguły na własnym kodzie w firmie, nie wolno wbudować ich we własny produkt skanujący, nie wolno zbudować na nich usługi skanowania dla klientów i nie wolno opublikować ich forka jako niezależnego zbioru. Kontrybucja do tego repozytorium oznacza udzielenie firmie Semgrep licencji na dystrybucję zgłoszonej reguły na tych samych warunkach. Reguły Pro, pisane przez zespół badawczy dostawcy, w ogóle nie trafiają do tego repozytorium i są dostępne tylko przez rejestr.

Podsumowując podział: kod silnika otwartego jest na LGPL 2.1, silnik Pro jest zamkniętą binarką za logowaniem, reguły edycji społecznościowej są na własnej licencji zakazującej redystrybucji, a reguły Pro są własnościowe i dostępne wyłącznie jako usługa.

Cennik platformy i jego arytmetyka

Strona z cennikiem renderuje się bez JavaScriptu, więc dane poniżej pochodzą z surowego HTML pobranego 22 sierpnia 2026 roku.

PlanCenaKontrybutorzyRepozytoria prywatneKredyty AI
Free Edition0 USDmaksymalnie 101060
Teamsod 30 USD za kontrybutora miesięczniebez limitu w cennikudo 50020 na programistę miesięcznie
Enterprisewycena indywidualnabez limitubez limitu50 na programistę miesięcznie

Rozbicie planu Teams na produkty: Code 30 USD, Supply Chain 30 USD, Secrets 15 USD, wszystko za kontrybutora miesięcznie. Zespół chcący mieć wszystkie trzy płaci więc 75 USD za kontrybutora miesięcznie, co przy dziesięciu kontrybutorach daje 750 USD miesięcznie i 9000 USD rocznie. Plan darmowy obejmuje Code i Supply Chain po 0 USD, a Secrets w nim nie występuje.

Dwie rzeczy w tym cenniku wymagają uwagi. Pierwsza to rozbieżność wewnątrz samej strony: w opisie planu darmowego czytamy „Scan up to 10 repositories”, natomiast w tabeli porównawczej ten sam plan ma repozytoria publiczne bez limitu i repozytoria prywatne najwyżej dziesięć. Podaję obie wartości, bo nie da się z tej strony rozstrzygnąć, która obowiązuje.

Druga to kredyty AI. Plan darmowy dostaje sześćdziesiąt kredytów opisanych jako „included”, bez wskazania okresu rozliczeniowego. Plan Teams dostaje dwadzieścia na programistę miesięcznie, więc zespół trzyosobowy osiąga te same sześćdziesiąt kredytów, tyle że co miesiąc. Jeśli sześćdziesiąt w planie darmowym jest pulą jednorazową, różnica między planami jest znacznie większa, niż wynika z zestawienia liczb. Strona tego nie precyzuje.

Kontrybutor jest zdefiniowany jako osoba, która w ciągu ostatnich dziewięćdziesięciu dni wykonała co najmniej jeden zapis w prywatnym repozytorium organizacji skanowanym przez Semgrep. Przy rotacji w zespole i przy botach wykonujących zapisy ta definicja potrafi zaskoczyć na fakturze.

Odnotujmy jeszcze jeden fakt, który wygląda na sprzeczność, a nią nie jest. Plan darmowy platformy obejmuje analizę międzyplikową z regułami Pro. To nie oznacza, że te funkcje są w otwartym kodzie. Oznacza, że dostawca udostępnia je bezpłatnie małym zespołom przez swoją platformę, po zalogowaniu przez GitHuba albo GitLaba, z limitem dziesięciu kontrybutorów. Bezpłatne i otwarte to w tym wypadku dwie różne rzeczy.

Jak napisać własną regułę

Własna reguła jest tym, co odróżnia Semgrepa od gotowego skanera, i jest też jedynym powodem, żeby wybrać go zamiast czegoś prostszego. Format opisuje plik rule_schema_v1.yaml dostarczany wewnątrz pakietu, więc nazwy pól poniżej pochodzą z niego, a nie z dokumentacji.

Plik z regułami ma jeden klucz najwyższego poziomu, rules, będący listą. W trybie wyszukiwania reguła wymaga id, message, languages, severity oraz jednego z pól opisujących wzorzec.

Code
YAML
rules:
  - id: request-bez-limitu-czasu
    message: >-
      Wywołanie requests.$METHOD bez argumentu timeout może zawiesić proces
      na czas nieokreślony. Ustaw timeout jawnie.
    languages: [python]
    severity: WARNING
    patterns:
      - pattern-either:
          - pattern: requests.get(...)
          - pattern: requests.post(...)
          - pattern: requests.request(...)
      - pattern-not: requests.$METHOD(..., timeout=$T, ...)
    paths:
      exclude:
        - tests/
        - "**/conftest.py"
    metadata:
      category: correctness
      confidence: HIGH

Kolejność operatorów wewnątrz patterns jest koniunkcją: wszystkie warunki muszą być spełnione jednocześnie. pattern-either to alternatywa. pattern-not odejmuje przypadki już poprawne. paths z podpolami include i exclude ogranicza regułę do części repozytorium bez ruszania globalnej konfiguracji.

Pole severity przyjmuje ERROR, WARNING i INFO, a od wersji 1.72.0 także CRITICAL, HIGH, MEDIUM i LOW. Schemat dopuszcza dodatkowo eksperymentalne INVENTORY i EXPERIMENT. Nowe i stare nazwy współistnieją, więc w jednym repozytorium łatwo skończyć z mieszanką i z filtrem --severity łapiącym połowę tego, co powinien.

Druga reguła pokazuje warunki nakładane na metazmienne oraz automatyczną poprawkę.

Code
YAML
rules:
  - id: slaby-algorytm-podpisu-jwt
    message: Token JWT podpisany algorytmem $ALG. Użyj HS256 albo RS256.
    languages: [python]
    severity: ERROR
    min-version: 1.72.0
    patterns:
      - pattern: jwt.encode(..., algorithm=$ALG, ...)
      - metavariable-regex:
          metavariable: $ALG
          regex: ^["'](none|HS1|MD5)["']$
      - focus-metavariable: $ALG
    fix: '"HS256"'
    metadata:
      cwe:
        - "CWE-327: Use of a Broken or Risky Cryptographic Algorithm"

metavariable-regex przykłada wyrażenie regularne do treści złapanej metazmiennej, czyli miesza oba światy w kontrolowany sposób: składnia wybiera miejsce, regexp zawęża wartość. focus-metavariable przesuwa podkreślenie w wyniku z całego wywołania na sam argument, dzięki czemu poprawka fix podmienia tylko jego. Obok tego schemat definiuje metavariable-pattern (dopasowanie wzorca do zawartości metazmiennej, także w innym języku), metavariable-comparison z polami metavariable, comparison, strip i base, metavariable-type, metavariable-name oraz metavariable-analysis z polami analyzer i metavariable, używane między innymi do analizy entropii. Pole min-version istnieje po to, żeby starszy Semgrep pominął regułę zamiast wywalić się na nieznanym polu. Schemat celowo ustawia additionalProperties: true na poziomie reguły z tego samego powodu.

Trzeci wariant to tryb skażenia, jedyny sposób na powiązanie źródła danych z miejscem ich użycia.

Code
YAML
rules:
  - id: os-command-z-parametru-flask
    mode: taint
    message: Dane z żądania trafiają do os.system bez walidacji.
    languages: [python]
    severity: ERROR
    pattern-sources:
      - patterns:
          - pattern-inside: |
              @app.route(...)
              def $HANDLER(...):
                  ...
          - pattern: flask.request.$ANYTHING
    pattern-sanitizers:
      - pattern: shlex.quote(...)
      - pattern: re.fullmatch("...", ...)
    pattern-sinks:
      - pattern: os.system(...)

Reguła w trybie taint wymaga id, message, languages, severity, pattern-sources i pattern-sinks. pattern-sanitizers i pattern-propagators są opcjonalne. I tu wraca podział z poprzedniej sekcji: w silniku otwartym śledzenie działa wewnątrz jednej funkcji. Jeśli źródło jest w kontrolerze, a wywołanie systemowe w funkcji pomocniczej w tym samym pliku, potrzebujesz --pro-intrafile. Jeśli funkcja pomocnicza siedzi w innym module, potrzebujesz --pro. Reguła napisana bez tej świadomości wygląda na niedziałającą, choć jest poprawna.

Regułę sprawdza się poleceniem semgrep --test, które szuka pliku o tej samej nazwie bazowej z rozszerzeniem języka i porównuje trafienia z adnotacjami w komentarzach. Rozpoznawane są ruleid, ok, todoruleid i todook, a dodatkowo warianty proruleid, deepruleid i deepok dla trafień wymagających silnika Pro. semgrep --validate --config ./rules sprawdza sam schemat bez uruchamiania skanu.

Fałszywe alarmy i koszt utrzymania

Analiza statyczna generuje szum, a zespół, który dostaje sto ostrzeżeń dziennie, przestaje je czytać po tygodniu. Wdrożenie Semgrepa nie jest jednorazową konfiguracją i lepiej to założyć od początku niż odkryć w trzecim miesiącu.

Mechanizmów tłumienia jest kilka i różnią się zasięgiem. Najwęższy to komentarz w kodzie. Z constants.py wynika, że rozpoznawany jest wzorzec nosem albo nosemgrep, wielkość liter nie ma znaczenia, a po dwukropku można podać listę identyfikatorów reguł rozdzieloną przecinkami. Komentarz działa w tej samej linii co trafienie albo w linii bezpośrednio nad nim.

Code
Python
# nosemgrep: request-bez-limitu-czasu
resp = requests.get(url)

resp = requests.get(url)  # nosem: request-bez-limitu-czasu,slaby-algorytm-podpisu-jwt

# nosemgrep
resp = requests.get(url)

Ostatni wariant, bez identyfikatora, wycisza w tym miejscu wszystko i jest tym, który po roku nikomu nic nie mówi. Warto wymusić w przeglądzie kodu podawanie identyfikatora reguły i powodu. Całą obsługę tych komentarzy wyłącza --disable-nosem, co bywa przydatne w okresowym audycie zaległości.

Szczebel wyżej jest plik .semgrepignore ze składnią wzorców ścieżek, uzupełniany domyślnie o .gitignore, chyba że podasz --no-git-ignore. Jest tu haczyk widoczny w target_manager.py: dla części produktów plik .semgrepignore jest świadomie ignorowany, bo typowa jego zawartość odsiewa katalogi, które akurat te produkty muszą przeskanować. Nazwę pliku zmienia --x-semgrepignore-filename, a jego pomijanie --x-ignore-semgrepignore-files. Przedrostek --x- oznacza flagę eksperymentalną, więc nie budowałbym na niej stałego procesu.

Najszerszy poziom to wybór reguł i progu. --exclude-rule usuwa regułę po identyfikatorze, --exclude i --include filtrują ścieżki, a --severity przepuszcza tylko wybrany poziom. Najskuteczniejszy w praktyce jest jednak --baseline-commit: skan zgłasza wyłącznie trafienia, których nie było w podanym punkcie historii. Dzięki temu istniejące repozytorium nie zaczyna od tysiąca ostrzeżeń, a nowy kod jest pilnowany od pierwszego dnia.

Do tego dochodzą limity wydajnościowe, o które łatwo się potknąć. Domyślny czas na regułę w pliku to pięć sekund, a plik większy niż milion bajtów jest pomijany. Tryb międzyplikowy w potoku ciągłej integracji ma własne wartości domyślne: limit pamięci 8192 MiB i limit czasu 10800 sekund, czyli trzy godziny. Na Linuksie limit pamięci jest dodatkowo wyliczany jako dziewięćdziesiąt procent wartości z /sys/fs/cgroup/memory.max, jeśli taki limit istnieje. Skan, który „nic nie znalazł”, bywa skanem, który po cichu pominął połowę plików, dlatego --x-ls i sekcja pominiętych plików w raporcie to pierwsze miejsce do sprawdzenia.

Uczciwa prognoza kosztu wygląda tak, przy czym poniższe czasy są moim szacunkiem z praktyki, a nie liczbą z dokumentacji. Pierwsze uruchomienie na dojrzałym repozytorium zajmuje kilkanaście minut. Przejrzenie wyniku i ustalenie linii bazowej zajmuje kilka dni. Potem dochodzi stały narzut: każda aktualizacja rejestru może dodać reguły, które w Twoim kodzie generują szum, każda nowa reguła własna wymaga testu i przeglądu, a każde wyciszenie wymaga uzasadnienia, inaczej po roku nikt nie wie, czy komentarz nosemgrep w pliku dotyczy realnego wyjątku, czy zniecierpliwienia.

Semgrep obok linterów i skanerów modeli

Granica między linterem a narzędziem bezpieczeństwa bywa płynna, bo oba czytają kod bez uruchamiania go. Różnica jest w celu i w tym, co potrafią zobaczyć.

CechaSemgrepBiomeOxlintLakera Guard, Prompt Security
Przedmiot analizywzorce i podatności w kodziestyl i błędy w kodziestyl i błędy w kodziedane wejściowe i wyjściowe modelu
Moment działaniaanaliza statycznaanaliza statycznaanaliza statycznaczas wykonania
Reguły własneYAML, pełny formatGritQLJavaScriptkonfiguracja polityk
Zasięg międzyplikowytylko w wariancie płatnymnie dotyczynie dotyczynie dotyczy
Licencja koduLGPL 2.1MIT albo Apache 2.0MITusługa zamknięta
Licencja regułSemgrep Rules License v1.0ta sama co kodta sama co kodnie dotyczy

Biome i Oxlint pilnują stylu i typowych pomyłek w JavaScripcie oraz w TypeScripcie. Działają szybciej, bo mają węższy zakres i pracują na jednym pliku bez modelu przepływu danych. Semgrep nie zastąpi ich w kwestii formatowania i nie sprawdzi typów, natomiast one nie napiszą reguły opisującej przepływ danych od parametru żądania do wywołania systemowego. Sensowny układ to lintowanie przy zapisie pliku i Semgrep w potoku ciągłej integracji, z linią bazową ustawioną na gałąź główną.

Narzędzia w rodzaju Lakera Guard, Prompt Security i Protect AI należą do innej kategorii i mieszanie ich z Semgrepem prowadzi do złych decyzji. One sprawdzają, co wchodzi do modelu językowego i co z niego wychodzi, w czasie wykonania. Semgrep sprawdza kod, zanim ten w ogóle się uruchomi. Jedyny punkt styczny jest taki, że w repozytorium reguł edycji społecznościowej istnieje katalog ai ze 132 regułami dotyczącymi bibliotek do pracy z modelami, i to jest analiza kodu wywołującego model, nie ruchu do modelu.

Osobna sprawa to kod pisany przez asystentów. Jeśli w zespole używacie GitHub Copilot, skan wzorcowy w potoku ma większą wartość niż wcześniej, bo objętość kodu przechodzącego przez przegląd rośnie szybciej niż uwaga recenzentów. Semgrep 1.174.0 dostarcza zresztą własny serwer MCP w module semgrep.mcp i wpina zależność mcp==1.29.0, więc integracja z narzędziami agentowymi jest w pakiecie, choć jej opisu nie sprawdzałem poza obecnością kodu.

Typowe błędy

Pierwszy to założenie, że --config auto działa lokalnie i bez sieci. Ten skrót rozwija się do adresu {semgrep_url}/c/auto i wymaga połączenia z serwerem dostawcy oraz włącza wysyłkę telemetrii, bo domyślne --metrics auto uruchamia się przy każdej konfiguracji pobranej zdalnie.

Drugi to napisanie reguły w trybie taint i uznanie, że silnik jest zepsuty, bo nic nie znajduje. Bez --pro-intrafile śledzenie kończy się na granicy funkcji, a to pokrywa mniejszość realnych przypadków.

Trzeci to skopiowanie reguł z repozytorium semgrep-rules do własnego produktu albo do własnej publicznej kolekcji. Licencja tych reguł zabrania redystrybucji i udostępniania ich jako usługi, niezależnie od tego, że repozytorium jest publiczne na GitHubie.

Czwarty to blokada w automacie zgodności z powodu rzekomego braku licencji. Pakiet ma poprawną deklarację LGPL-2.1-or-later, tylko w polu License-Expression, a nie w klasyfikatorach ani w starym polu License. Naprawą jest aktualizacja skanera, a nie wyjątek na pakiet.

Piąty to instalacja obok innych narzędzi w jednym środowisku wirtualnym. Przypięcia dokładne mcp==1.29.0 i ruamel.yaml.clib==0.2.15 kolidują z innymi pakietami częściej, niż się wydaje.

Szósty to uruchomienie skanu na całym repozytorium bez linii bazowej. Wynik na dojrzałym projekcie liczy setki trafień, zespół zamyka go jednym wyciszeniem i narzędzie jest martwe. --baseline-commit istnieje dokładnie po to.

Siódmy to mieszanie starych i nowych nazw poziomów ważności w jednym zbiorze reguł. ERROR i HIGH to dwie różne wartości w schemacie, a filtr --severity nie zna między nimi żadnego związku.

FAQ

Czy Semgrep jest darmowy?

Silnik i klient wiersza poleceń są na LGPL 2.1 i można ich używać bez konta. Analiza międzyfunkcyjna, międzyplikowa, wykrywanie sekretów oraz trzy języki (Apex, Elixir, Gosu) wymagają binarki Pro pobieranej po zalogowaniu. Platforma ma plan darmowy dla maksymalnie dziesięciu kontrybutorów i planów płatnych od 30 USD za kontrybutora miesięcznie.

Czy mogę używać reguł z repozytorium semgrep-rules we własnym narzędziu?

Nie. Semgrep Rules License v1.0 pozwala używać reguł wyłącznie do własnych wewnętrznych celów biznesowych i wprost zabrania ich rozpowszechniania oraz udostępniania innym jako usługi. Nie ma tam ani progu przychodu, ani daty przejścia na licencję otwartą.

Czym Semgrep różni się od lintera?

Linter pilnuje stylu i typowych pomyłek według wbudowanego zestawu reguł. Semgrep dopasowuje dowolny wzorzec, który sam napiszesz w składni docelowego języka, i potrafi śledzić przepływ danych między źródłem a miejscem użycia. Za to nie formatuje kodu i nie sprawdza typów.

Czy skan wysyła mój kod do Semgrepa?

Kod nie opuszcza maszyny przy skanie lokalnym. Wysyłane są metadane skanu, i to tylko wtedy, gdy reguły pochodzą z serwera. --metrics off wyłącza to zupełnie, a --config ./rules z plikami lokalnymi nie uruchamia wysyłki w ogóle.

Ile reguł dostanę bez konta?

Rejestr działa anonimowo dla paczek publicznych z przedrostkami p/, r/ i s/. Odpowiednikiem tego zbioru na GitHubie jest repozytorium edycji społecznościowej, w którym naliczyłem 2091 reguł. Reguły Pro nie są w tym repozytorium i wymagają konta.

Czy warto pisać własne reguły, czy wystarczą gotowe?

Gotowe reguły łapią typowe podatności i nie wymagają pracy. Własne mają sens tam, gdzie chodzi o konwencje konkretnego projektu: zakaz użycia wycofanej funkcji wewnętrznej, wymóg przekazywania kontekstu żądania, wychwycenie wzorca przed refaktoryzacją. To jest jedyna część, której gotowy skaner nie zastąpi.

Czytaj dalej

Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie