Przejdź do głównej zawartości

Planowy skan bezpieczeństwa łączy przypięte wersjami skanery deterministyczne (SAST, sekrety, zależności) z przebiegiem agenta tylko do odczytu, który śledzi autoryzację, izolację tenantów i przejścia stanu między plikami. Oba zapisują wersjonowane dowody. Nazwany właściciel ocenia każde znalezisko po fingerprincie, a zaakceptowane poprawki trafiają do kodu wyłącznie jako pull requesty zaczynające się od czerwonego testu regresyjnego.

Bot zależności co tydzień otwiera PR-y z podbiciem wersji, a zeszłoroczny pentest wyszedł czysto. Potem klient zgłasza, że po zmianie ID w adresie URL widzi faktury innego tenanta. To luka w autoryzacji, której żadna reguła oparta na wzorcach nie zobaczy, bo brakujące sprawdzenie rozciąga się na trzy pliki. Ta strona jest dla dewelopera, który buduje planowy skan, i dla tech leada, który odpowiada za triage i zatwierdza poprawki.

Pytanie Q24 ze scorecardu: Jak skanujesz bazę kodu pod kątem głębokich podatności i długu architektonicznego?

Odpowiedź za maksimum (3 punkty): Planowane przebiegi skanera i agenta zachowują dowody, kierują patche przez PR i dodają evale regresyjne dla zaakceptowanych napraw.

  • Skrypt, który przypina wersje Semgrepa, Gitleaks i OSV-Scannera oraz zapisuje wersję i kod wyjścia każdego narzędzia.
  • Schemat znalezisk, który zmusza agenta do wskazania pliku, symbolu, ścieżki wywołań i dowodów, których mu brakuje.
  • Cotygodniowy job dla Claude Code lub Codeksa (i odpowiednik w Cursorze), który nie może edytować plików ani sięgnąć produkcji.
  • Rejestr decyzji kluczowany fingerprintem, dzięki któremu odrzucone znalezisko nie wraca w każdy poniedziałek.
  • Ścieżkę napraw od testu, w której każda przyjęta poprawka zostaje na stałe testem regresyjnym.

Przebieg agenta nie zastępuje skanerów. Każda warstwa zostawia inny rodzaj dowodu, a triage potrzebuje wszystkich.

WarstwaCo wykrywaJaki dowód musi zostawić
SAST (Semgrep)Znane niebezpieczne wzorce w jednym pliku lub funkcjiWersja narzędzia, ID reguły, plik i linia (SARIF)
Skanowanie sekretów (Gitleaks)Klucze i tokeny w drzewie roboczym i historii gitaWersja narzędzia, ID reguły, commit, zamaskowane dopasowanie
Skanowanie zależności (OSV-Scanner)Pakiety z opublikowanymi advisoryWersja narzędzia, ID advisory, ścieżka lockfile’a
Przebieg agenta (tylko odczyt)Brak autoryzacji, odczyty między tenantami, niebezpieczne przejścia stanu, luki w logice biznesowej rozciągnięte na kilka plikówŚcieżka wywołań przez konkretne symbole, brakujące dowody, proponowany test reprodukujący
Triage przez człowiekaWykorzystywalność, wpływ biznesowy, priorytetAkceptacja lub odrzucenie, właściciel, uzasadnienie, data wygaśnięcia
Zestaw regresyjnyPowrót tej samej podatnościTest czerwony przed poprawką i zielony po niej

Testy penetracyjne i modelowanie zagrożeń zostają w kalendarzu. Planowy skan zawęża to, co muszą obejrzeć, ale ich nie zastępuje.

  1. Przypnij wersje skanerów deterministycznych. Zacommituj jeden skrypt, którego używa każdy przebieg. Wtedy zmiana wyniku oznacza zmianę kodu, a nie narzędzia. Wersje poniżej były najnowszymi wydaniami 2026-09-30: wszystkie przypięte wersje na tej stronie sprawdzono ponownie tego dnia, już po aktualizacji strony z 2026-09-26. Krok instalacji w workflow pobiera dokładnie te wersje i porównuje SHA-256 każdego pliku z plikiem sum kontrolnych wydania; podbijaj je tylko w osobnym pull requeście (zobacz awarie niżej).

    scripts/security/run-scanners.sh
    #!/usr/bin/env bash
    # Pinned: semgrep 1.178.0, gitleaks 8.30.1, osv-scanner 2.6.0 (latest releases, re-checked 2026-09-30)
    set -u
    out=security-out
    mkdir -p "$out"
    : > "$out/exit-codes.txt"
    echo "commit=${GITHUB_SHA:-$(git rev-parse HEAD)}" > "$out/run-meta.txt"
    { semgrep --version; gitleaks version; osv-scanner --version | head -1; } > "$out/tool-versions.txt"
    semgrep scan --config p/default --metrics=off --error --sarif-output="$out/semgrep.sarif"
    echo "semgrep=$?" >> "$out/exit-codes.txt"
    gitleaks git --redact --report-format sarif --report-path "$out/gitleaks.sarif"
    echo "gitleaks=$?" >> "$out/exit-codes.txt"
    osv-scanner scan source -r --format sarif --output-file "$out/osv.sarif" .
    echo "osv=$?" >> "$out/exit-codes.txt"
    # 0 = clean, 1 = findings; any other code = the tool failed
    # (127 = not installed; osv-scanner 128 = no lockfile or manifest found)
    awk -F= '$2>1{bad=1} END{exit bad}' "$out/exit-codes.txt" || exit 1
    for f in semgrep gitleaks osv; do test -s "$out/$f.sarif" || exit 1; done

    Skrypt zapisuje kody wyjścia, zamiast zatrzymać się na pierwszym niezerowym. Planowy przebieg zbiera dowody; blokowanie merge’a należy do bramek na pull requestach. Dwie ostatnie linie odróżniają znaleziska od awarii: kod 1 oznacza znaleziska i job przechodzi, a każdy inny niezerowy kod albo brak pliku SARIF kończy job błędem, więc pusty raport nigdy nie udaje czystego tygodnia. Żeby to działało, trzeba pamiętać o dwóch szczegółach narzędzi. Semgrep kończy się kodem 0 nawet wtedy, gdy coś znajdzie, chyba że dostanie --error, dlatego skrypt ustawia tę flagę. OSV-Scanner zwraca 127 przy ogólnym błędzie (także gdy baza podatności jest nieosiągalna) i 128, gdy nie znajdzie żadnego lockfile’a ani manifestu do przeskanowania; oba kody celowo kończą job błędem, bo skan zależności, który nie zobaczył żadnego pakietu, nie jest czystym wynikiem. Dodaj --allow-no-lockfiles tylko w repozytorium, które naprawdę nie ma zależności. Uruchamiaj skrypt przez bash scripts/security/run-scanners.sh albo raz nadaj mu chmod +x i zacommituj ten tryb.

  2. Określ, co musi zawierać znalezisko. Schemat to kontrakt między agentem a triage’em. Ogranicza przebieg do 10 znalezisk i wymaga ścieżki wywołań oraz brakujących dowodów, co trzyma narracje o niskiej pewności z dala od kolejki.

    .github/security/findings.schema.json
    {
    "type": "object",
    "additionalProperties": false,
    "required": ["findings"],
    "properties": {
    "findings": {
    "type": "array",
    "maxItems": 10,
    "items": {
    "type": "object",
    "additionalProperties": false,
    "required": ["category", "file", "line", "symbol", "call_path", "missing_evidence", "reproduction_test", "severity_guess"],
    "properties": {
    "category": { "enum": ["authorization", "tenant-isolation", "injection", "secret-exposure", "unsafe-state-transition", "other"] },
    "file": { "type": "string" },
    "line": { "type": "integer" },
    "symbol": { "type": "string" },
    "call_path": { "type": "array", "items": { "type": "string" } },
    "missing_evidence": { "type": "string" },
    "reproduction_test": { "type": "string" },
    "severity_guess": { "enum": ["low", "medium", "high", "critical"] }
    }
    }
    }
    }
    }

    Każdy obiekt ma additionalProperties: false i wszystkie właściwości jako wymagane, więc ten sam plik działa z --json-schema w Claude Code i z --output-schema w Codeksie.

  3. Uruchamiaj przebieg agenta tylko do odczytu według harmonogramu. Daj mu stały prompt z repozytorium, żadnych narzędzi zapisu, żadnych poświadczeń produkcyjnych oraz limit czasu i kosztu. Zakładki poniżej pokazują konfigurację dla każdego narzędzia.

  4. Policz fingerprinty i usuń duplikaty, zanim ktokolwiek przeczyta wynik. Fingerprint to skrót z category, file i symbol, a nie z numeru linii, bo linie przesuwają się przy każdym refaktorze. Licz fingerprint w jobie, nigdy w modelu; krok triage’u odrzuca potem znaleziska z niewygasłą decyzją w rejestrze (krok 5). Workflow niżej zapisuje security-out/fingerprints.txt tą pętlą jq i sha256sum.

    Okno terminala
    jq -r '.findings[] | "\(.category)|\(.file)|\(.symbol)"' security-out/agent-findings.json |
    while read -r k; do
    printf '%s %s\n' "$(printf '%s' "$k" | sha256sum | cut -c1-8)" "$k"
    done > security-out/fingerprints.txt
  5. Zapisuj wyniki triage’u w rejestrze decyzji. Nazwany właściciel zapisuje jedną decyzję na fingerprint w recenzowanym pliku, więc historia podróżuje razem z kodem. Fingerprint celowo pomija treść kodu, dlatego code_sha zapisuje commit, na którym podjęto decyzję: recenzent otwiera znalezisko ponownie, gdy git log -L :<symbol>:<file> <code_sha>..HEAD albo diff PR-a pokaże zmianę w symbolu, albo gdy decyzja wygaśnie.

    security/dispositions.yaml
    - fingerprint: 3f9c1e07
    decision: accepted # accepted | rejected | risk-accepted
    owner: security-owner-payments
    rationale: "GET /invoices/:id loads by id without a tenant filter"
    evidence: "security-out/agent-findings.json, run 2026-09-21"
    code_sha: 9b1d4e2 # commit the decision was made against
    expires: 2026-12-21
  6. Każde zaakceptowane znalezisko prowadź przez pull request zaczynający się od testu. Agent pisze na osobnej gałęzi czerwony test reprodukujący, a potem najmniejszą poprawkę. PR zawiera wynik skanera, test i rezultaty CI, a merge zatwierdza właściciel bezpieczeństwa. Większa naprawa staje się plikiem intent.md i wraca do łańcucha artefaktów, zamiast rozrastać się w jednym PR-ze.

  7. Zachowaj test na zawsze. Test reprodukujący trafia do zwykłego zestawu testów, a prompt, który znalazł problem, staje się przypadkiem w ciągłych ewaluacjach. Jeśli późniejsza zmiana promptu lub modelu przestanie go znajdować, eval zrobi się czerwony.

Harmonogram przebiegu agenta w Claude Code, Codeksie lub Cursorze

Dział zatytułowany „Harmonogram przebiegu agenta w Claude Code, Codeksie lub Cursorze”

Job skanerów i format dowodów są takie same we wszystkich narzędziach. Różni się sposób wymuszenia trybu tylko do odczytu i ustrukturyzowanego wyniku.

W CI uruchom CLI w trybie headless wyłącznie z narzędziami do czytania plików. --bare, --setting-sources "" i --strict-mcp-config sprawiają, że job nie ładuje hooków, ustawień ani serwerów MCP z repozytorium, które inaczej działałyby z kluczem API joba. Flagi sprawdzone z claude --help 2.1.285 (npm latest, sprawdzone 2026-09-30). claude-opus-5-5 wymaga v2.1.280 lub nowszej, którą mają oba kanały wydań (stable v2.1.292, latest v2.1.302 na 2026-10-17). Job kończy się błędem, gdy przebieg zgłosi is_error albo nie zwróci ustrukturyzowanego wyniku, a także gdy wynik nie przejdzie walidacji schematem.

.github/workflows/security-scan.yml
name: weekly-security-scan
on:
schedule:
- cron: "17 3 * * 1" # Mondays 03:17 UTC
workflow_dispatch:
permissions:
contents: read
jobs:
scanners:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with: { persist-credentials: false, fetch-depth: 0 }
- name: Install pinned scanners
run: |
cd "$RUNNER_TEMP"
pipx install semgrep==1.178.0
curl -sSfLo gitleaks.tgz https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz
echo "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb gitleaks.tgz" | sha256sum -c -
tar -xzf gitleaks.tgz gitleaks && sudo install gitleaks /usr/local/bin/
curl -sSfLo osv-scanner https://github.com/google/osv-scanner/releases/download/v2.6.0/osv-scanner_linux_amd64
echo "ca69b3d3cd08f889a49dc0a383122f71cc528b83803671df5fd874d97485b108 osv-scanner" | sha256sum -c -
sudo install osv-scanner /usr/local/bin/
- run: bash scripts/security/run-scanners.sh
- uses: actions/upload-artifact@v7
with: { name: scanner-results, path: security-out/ }
agent-pass:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v7
with: { persist-credentials: false }
- run: npm install -g @anthropic-ai/claude-code@2.1.285
- name: Read-only agent pass
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
mkdir -p security-out
claude -p "$(cat .github/security/scan-prompt.md)" \
--bare --setting-sources "" --strict-mcp-config \
--tools "Read,Grep,Glob" --permission-mode dontAsk \
--model claude-opus-5-5 --max-budget-usd 20 \
--output-format json \
--json-schema "$(cat .github/security/findings.schema.json)" \
> security-out/agent-raw.json
printf 'commit=%s\nmodel=claude-opus-5-5\n' "$GITHUB_SHA" > security-out/run-meta.txt
jq -e '.is_error == false and .structured_output != null' security-out/agent-raw.json >/dev/null
jq '.structured_output' security-out/agent-raw.json > security-out/agent-findings.json
- name: Validate findings against the schema
run: npx --yes ajv-cli@5.0.0 validate -s .github/security/findings.schema.json -d security-out/agent-findings.json
- name: Fingerprint findings
run: |
jq -r '.findings[] | "\(.category)|\(.file)|\(.symbol)"' security-out/agent-findings.json |
while read -r k; do
printf '%s %s\n' "$(printf '%s' "$k" | sha256sum | cut -c1-8)" "$k"
done > security-out/fingerprints.txt
- if: always()
uses: actions/upload-artifact@v7
with: { name: agent-findings, path: security-out/ }

Anthropic oferuje dwie alternatywy. Routines (/schedule, research preview, minimalny odstęp jedna godzina) uruchamiają prompt w chmurze Anthropic według harmonogramu. Routine nie ma wyboru trybu uprawnień i może użyć każdego narzędzia z każdego dołączonego konektora, więc usuń wszystkie konektory i utrzymuj prompt w trybie tylko do odczytu. Wtyczka Claude Security (/plugin install claude-security@claude-plugins-official w prompcie Claude Code) uruchamia w twojej sesji skan wieloagentowy, w którym niezależni agenci weryfikują każde znalezisko, i zapisuje wyniki w SARIF i JSONL oraz znacznik rewizji z przeskanowanym commitem. Jej skany też są niedeterministyczne, więc przepuszczaj jej wyniki przez ten sam fingerprinting i rejestr decyzji, zamiast liczyć na to, że wtyczka rozpozna znalezisko zgłoszone tydzień wcześniej. Działa z twoimi uprawnieniami oraz z ustawieniami i hookami samego repozytorium, więc używaj jej na kodzie, który kontrolujesz.

Pierwszy prompt zapisz jako .github/security/scan-prompt.md, żeby każdy przebieg używał tego samego, zrecenzowanego tekstu. Prompty są po angielsku, bo tak trafiają do repozytorium i CI.

Zamień 3f9c1e07 na fingerprint zaakceptowanego znaleziska.

Skąd wiesz, że skan działa, bez czytania każdego znaleziska?

Dział zatytułowany „Skąd wiesz, że skan działa, bez czytania każdego znaleziska?”

Weryfikujesz pipeline, a nie każde zdanie agenta. Większość ciężaru niesie pięć kontroli:

  • Walidacja schematu. Nieudany przebieg, przebieg bez ustrukturyzowanego wyniku albo wynik niezgodny ze schematem kończy job błędem (kroki jq -e i ajv-cli). Do triage’u nie trafia nic nieustrukturyzowanego.
  • Stemple dowodowe. Każdy przebieg zapisuje jako artefakt builda tool-versions.txt, exit-codes.txt, run-meta.txt (SHA commita i ID modelu) oraz surowy wynik agenta. Gdy wynik się zmienia, wiesz, czy zmienił się kod, skaner, czy model.
  • Kanarek z podatnością. Trzymaj gałąź, która cofa podatność naprawioną wcześniej, na przykład brakujący filtr tenanta z przykładu wyżej. Uruchamiaj na niej przebieg agenta co miesiąc i po każdej zmianie promptu lub modelu. Jeśli przebieg przestanie ją zgłaszać, czułość spadła; traktuj to jak nieudany eval, a nie pech.
  • Czerwony przed, zielony po. Zaakceptowana poprawka wchodzi tylko z testem, który w CI nie przechodził na starym kodzie. Recenzent sprawdza test i log CI, a nie każdą linię patcha.
  • Zatwierdzenie przez właściciela. Właściciel kodu w danym obszarze zatwierdza każdy PR bezpieczeństwa. Dla autoryzacji, płatności i granic tenantów jest to nazwany właściciel bezpieczeństwa, zgodnie z twoją polityką warstwowego review.

Skany agentowe są niedeterministyczne: dwa przebiegi na tym samym commicie mogą zgłosić różne znaleziska. Kanarek i rejestr decyzji sprawiają, że ta zmienność jest widoczna i da się nią zarządzać, zamiast pozostawać ukryta.

Co psuje się w planowych skanach bezpieczeństwa i jak to naprawić

Dział zatytułowany „Co psuje się w planowych skanach bezpieczeństwa i jak to naprawić”

Pewność agenta uchodzi za walidację. Znalezisko brzmi przekonująco i ktoś je łata bez reprodukcji. Naprawa: odrzucaj każde znalezisko bez ścieżki wywołań i testu reprodukującego, a czerwonego testu wymagaj, zanim ktokolwiek zacznie recenzować poprawkę.

Te same fałszywe alarmy wracają co tydzień. Pojawia się zmęczenie triage’em i ludzie przestają czytać raport. Naprawa: licz fingerprint z category|file|symbol, zapisuj odrzucenia z datą wygaśnięcia w security/dispositions.yaml i otwieraj fingerprint ponownie tylko wtedy, gdy decyzja wygaśnie albo git log -L pokaże zmianę w symbolu od jego code_sha.

Job skanu może sięgnąć produkcji. Sekret w środowisku CI albo konektor z prawem zapisu poszerza zasięg prompt injection ukrytego w fixturze lub treści issue. Naprawa: najpierw usuń poświadczenie lub konektor, zrotuj wszystko, co job mógł odczytać, a dopiero potem dostrajaj prompt. Jedynym sekretem joba agenta jest jego klucz API do modelu.

Patch osłabia test, żeby znalezisko zniknęło. Naprawa: chroń pliki testów w review (wystarczy reguła CODEOWNERS na katalogach testów) i odrzucaj każdy PR bezpieczeństwa, który zmienia istniejącą asercję bez pisemnego uzasadnienia.

Skan po cichu przestaje działać. Harmonogram cron, który zawodzi bez hałasu, wygląda jak czysty tydzień. Naprawa: dodaj codzienny job, który kończy się błędem, a więc powiadamia właścicieli workflow, gdy w ostatnich ośmiu dniach nie było udanego przebiegu, na przykład test "$(gh run list --workflow security-scan.yml --status success --created ">=$(date -d '-8 days' +%F)" --json databaseId -q 'length')" -gt 0, i traktuj „zero znalezisk” z przebiegu agenta jako podejrzane, dopóki przebieg na kanarku nie potwierdzi czułości.

Aktualizacja skanera zalewa kolejkę. Nowy zestaw reguł Semgrepa dodaje z dnia na dzień falę znalezisk. Naprawa: podbijaj wersje skanerów w osobnym pull requeście, porównaj SARIF z poprzednim przebiegiem i oceń różnicę jedną partią, zanim zajmie się nią cotygodniowy job.

  • Skanery i przebieg agenta mają przypięte wersje, a każdy przebieg zapisuje wersje narzędzi, kody wyjścia, commit i ID modelu.
  • Przebieg agenta nie może pisać do repozytorium, ładować hooków z repozytorium ani sięgać produkcji.
  • Każde znalezisko wskazuje plik, symbol, ścieżkę wywołań i brakujące dowody.
  • Decyzje leżą w recenzowanym pliku kluczowanym fingerprintem, z właścicielem i datą wygaśnięcia.
  • Zaakceptowane poprawki wchodzą tylko z testem czerwonym przed i zielonym po oraz z zielonym CI.
  • Nazwany właściciel bezpieczeństwa zatwierdza merge’e dotykające wrażliwych granic.
  • Kanarek i przypadek w ciągłych ewaluacjach dowodzą, że przebieg wciąż znajduje znane problemy.
  • Większa naprawa zaczyna się od intent.md, zamiast rozrastać się w jednym PR-ze.

Scorecard pyta też o dług architektoniczny. Planowe kontrole dryfu i złożoności opisuje strona o utrzymaniu zdrowia kodu pisanego przez agentów. Kontrole bezpieczeństwa na każdym pull requeście, a nie według harmonogramu, znajdziesz w skanowaniu bezpieczeństwa i testowaniu podatności. Resztę klucza odpowiedzi zawiera przewodnik po scorecardzie dewelopera.

Najczęstsze pytania

Dlaczego planowy skan bezpieczeństwa łączy deterministyczne skanery z przebiegiem agenta tylko do odczytu?

Przypięte wersjami skanery deterministyczne (SAST, sekrety, zależności) wykrywają znane wzorce, a agent read-only śledzi autoryzację, izolację tenantów i przejścia stanu między wieloma plikami, których reguły wzorcowe nie widzą.

Jak przebiega triage i naprawa znalezisk z planowych skanów bezpieczeństwa?

Oba przebiegi zapisują wersjonowane dowody, wskazany właściciel ocenia znaleziska po fingerprincie, a zaakceptowane poprawki trafiają do repozytorium wyłącznie jako pull requesty zaczynające się od czerwonego testu regresyjnego.