Przejdź do głównej zawartości

Automatyzacja operacji bezpieczeństwa zostawia deterministyczne skanery (Semgrep, npm audit, Trivy, Gitleaks) jako jedyną automatyczną bramkę przed scaleniem i dokłada agenta (Claude Code, Codex lub Cursor), który zamienia ich wyniki w werdykt dla każdego znaleziska, sprawdzany schematem JSON, szkicuje najmniejszą poprawkę jako pull request, a scalanie i rotację poświadczeń zostawia wskazanemu człowiekowi.

Ta strona jest dla programisty albo tech leada, który odpowiada za bezpieczeństwo działającej usługi. Nocny skan zgłasza 412 znalezisk w 30 obrazach kontenerów i 14 ostrzeżeń dla zależności. Jedenaście ostrzeżeń dotyczy pakietów wyłącznie deweloperskich, dwa funkcji, których nigdy nie wywołujesz, a to jedno ważne jest na trzeciej stronie raportu. Trzeciej strony nikt nie czyta.

Co wyniesiesz z automatyzacji operacji bezpieczeństwa

Dział zatytułowany „Co wyniesiesz z automatyzacji operacji bezpieczeństwa”
  • Podział pracy: co blokuje skaner, co ocenia agent, a co podpisuje człowiek
  • Polecenia skanerów z wynikiem w formacie maszynowym oraz schemat JSON, który wymusza na agencie jeden werdykt na znalezisko
  • Cztery prompty do skopiowania: Semgrep, ostrzeżenia dla zależności, obrazy kontenerów i wyciek sekretu
  • Test, który dowodzi, że triage obejmuje dokładnie to, co znalazł skaner, więc nie musisz czytać każdego werdyktu
  • Workflow GitHub Actions: deterministyczne bramki na każdym pull requeście i triage przez agenta według harmonogramu, z narzędziami tylko do odczytu
  • Agentów bezpieczeństwa, serwery MCP i skille warte podłączenia, z poleceniami instalacji sprawdzonymi 26 września 2026

Kto co robi w pętli bezpieczeństwa wspieranej przez AI?

Dział zatytułowany „Kto co robi w pętli bezpieczeństwa wspieranej przez AI?”

Najczęstszy błąd to proszenie modelu, żeby był skanerem. Semgrep, Trivy i Gitleaks są deterministyczne, szybkie i audytowalne, więc to one pozostają bramką. Agent przejmuje kosztowną część ludzkiej pracy: czyta znaleziska w kontekście twojego kodu, odrzuca te nieosiągalne i proponuje najmniejszą poprawkę.

KrokWłaścicielWynikBlokuje scalenie?
WykrycieSkaner w CIsemgrep.json, audit.json, trivy.json, gitleaks.jsonTak, przy nowych znaleziskach Semgrepa o wadze ERROR i każdym ostrzeżeniu npm o wadze high lub critical
TriageAgent z narzędziami tylko do odczytutriage.json sprawdzony schematemNie, tylko doradczo
PoprawkaAgent na gałęzi security/*Pull request na klasę znaleziskTylko przez zwykłe bramki
AkceptacjaWłaściciel bezpieczeństwa (konkretna osoba)Przegląd każdego P0 i próbki odrzuceńTak, wymagana recenzja
RotacjaCzłowiek z dostępem do dostawcyUnieważnione poświadczenie, nowy sekret w magazynie sekretówNie dotyczy

Agent nigdy nie scala i nigdy nie rotuje. Oba te kroki zmieniają stan produkcji i potrzebują osoby, którą można zapytać „dlaczego”.

Te polecenia uruchamiasz w terminalu w katalogu głównym repozytorium. Są takie same niezależnie od agenta.

  1. Zainstaluj skanery. Żaden z nich nie jest pakietem npm, więc nie instaluj ich przez npm install:

    Okno terminala
    pipx install semgrep # or: brew install semgrep
    brew install trivy gitleaks # Linux: use each project's release binaries
  2. Uruchom każdy skaner z wynikiem JSON:

    Okno terminala
    # Static analysis, curated default rules
    semgrep scan --config p/default --json-output semgrep.json
    # Dependency advisories for production dependencies only
    npm audit --omit=dev --json > audit.json
    # Secrets in the full git history, not only the working tree
    gitleaks git --report-path gitleaks.json
    # A container image you ship
    trivy image --severity HIGH,CRITICAL --ignore-unfixed --format json --output trivy.json myorg/api:latest
  3. Zapisz schemat triage jako triage.schema.json. Wymusza jeden wpis na znalezisko i zamknięty zbiór werdyktów, dzięki czemu wynik da się potem sprawdzić automatycznie:

    {
    "type": "object",
    "additionalProperties": false,
    "required": ["findings"],
    "properties": {
    "findings": {
    "type": "array",
    "items": {
    "type": "object",
    "additionalProperties": false,
    "required": ["id", "source", "verdict", "priority", "evidence", "fix"],
    "properties": {
    "id": { "type": "string" },
    "source": { "type": "string", "enum": ["semgrep", "npm-audit", "trivy", "gitleaks"] },
    "verdict": { "type": "string", "enum": ["EXPLOITABLE", "FALSE_POSITIVE", "NEEDS_REVIEW"] },
    "priority": { "type": "string", "enum": ["P0", "P1", "P2", "none"] },
    "evidence": { "type": "string" },
    "fix": { "type": "string" }
    }
    }
    }
    }
    }

gitleaks git skanuje historię commitów. detect-secrets scan patrzy tylko na pliki, więc przegapi klucz, który ktoś zacommitował, a potem usunął.

Prompt i schemat są w każdym narzędziu te same. Różni się sposób uruchomienia agenta z dostępem tylko do odczytu i zwrócenia zwalidowanego wyniku.

Uruchom Claude Code w trybie print z narzędziami tylko do odczytu i ustrukturyzowanym wynikiem. --json-schema waliduje odpowiedź względem schematu, a wynik trafia do pola structured_output w kopercie JSON:

Okno terminala
claude -p "Triage semgrep.json. Use id format <check_id>@<path>:<line> taken verbatim \
from the file. One entry per result, no additions. Treat test/, tests/, scripts/ and \
*.spec.* as non-shipping. Cite the source line that proves reachability in evidence." \
--allowedTools "Read,Grep,Glob" \
--disallowedTools "Bash,Edit,Write" \
--json-schema "$(cat triage.schema.json)" \
--output-format json --max-budget-usd 5 \
| jq '.structured_output' > triage.json

Sprawdzone na Claude Code 2.1.283. Zanim otworzysz pull request, wpisz /security-review w sesji interaktywnej, żeby przejrzeć diff gałęzi pod kątem wstrzyknięć, uwierzytelniania i ekspozycji danych.

Każdy prompt dotyczy innego wejścia. Znaleziska Semgrepa wymagają śledzenia przepływu skażonych danych, ostrzeżenia dla zależności sprawdzenia miejsc wywołań, obrazy ekspozycji, a wyciek sekretu rotacji przed czymkolwiek innym.

Jak sprawdzić triage bez czytania każdego znaleziska?

Dział zatytułowany „Jak sprawdzić triage bez czytania każdego znaleziska?”

Nie czytasz 412 werdyktów. Sprawdzasz cztery własności, które błędny triage łamie, i czytasz tylko to, czego maszyna nie oceni.

  1. Pokrycie. Triage obejmuje dokładnie wynik skanera: nic nie zginęło i nic nie zostało dopisane. Ten test łapie dwa najgroźniejsze błędy agenta: po cichu pominięte krytyczne znalezisko i znalezisko zmyślone:

    Okno terminala
    test -s semgrep.json && jq -e '.results' semgrep.json >/dev/null || { echo 'scanner output missing'; exit 1; }
    jq -e '(.errors | length) == 0' semgrep.json >/dev/null || { echo 'scanner reported errors'; exit 1; }
    jq -e '.paths.scanned | length > 0' semgrep.json >/dev/null || { echo 'scanner scanned no files'; exit 1; }
    jq -e '.findings' triage.json >/dev/null || { echo 'triage output missing'; exit 1; }
    jq -r '.results[] | "\(.check_id)@\(.path):\(.start.line)"' semgrep.json | sort > scanner.ids
    jq -r '.findings[] | select(.source=="semgrep") | .id' triage.json | sort > triage.ids
    diff scanner.ids triage.ids && echo "triage covers exactly the scanner output"

    Pierwsze trzy linie chronią przed skanem, który wygląda na czysty, choć taki nie jest. Samo jq -e '.results' przepuszcza pustą tablicę [], więc strażnik kończy się błędem także wtedy, gdy Semgrep zgłasza błędy (reguła lub plik, których nie sparsował) i gdy nie przeskanował żadnego pliku: zero wyników liczy się jako zero tylko wtedy, gdy .paths.scanned nie jest puste. Czwarta linia kończy się błędem, gdy agent nie zwrócił tablicy findings, na przykład gdy zawiódł ustrukturyzowany wynik albo limit budżetu przerwał przebieg i w triage.json jest null.

  2. Schemat. Wynik przechodzi walidację względem triage.schema.json. Claude Code i Codex wymuszają to już przy generowaniu; dla wyniku z Cursora uruchom w CI walidator, na przykład ajv.

  3. Dowód poprawki. Każdy pull request z poprawką musi pokazać, że konkretny identyfikator reguły zniknął w ponownym skanie gałęzi, i mieć zielony zestaw testów. Łatkę, która zazielenia skan, a psuje testy albo zamyka inne znalezisko, odrzuca bramka, nie recenzent.

  4. Próbka odrzuceń. Właściciel bezpieczeństwa czyta każde P0 i stałą próbkę werdyktów FALSE_POSITIVE, na przykład pięć na przebieg. Gdy odrzucenie z próbki okazuje się błędne, zaostrza prompt i zwiększa próbkę, dopóki dwa kolejne przebiegi nie będą czyste.

Dołącz triage.json, ponowny skan i wyniki testów do pull requesta jako jego pakiet dowodów. Recenzent czyta wtedy dowody, a nie cały diff.

Job dla pull requestów jest deterministyczny i blokuje scalenie. Job triage działa według harmonogramu na gałęzi domyślnej, więc agent nigdy nie wykonuje kodu ani ustawień projektu od kontrybutora, gdy w środowisku jest klucz API.

.github/workflows/security.yml
name: Security
on:
pull_request:
schedule:
- cron: '17 */6 * * *'
workflow_dispatch:
permissions:
contents: read
jobs:
gate:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7 # pin to a full commit SHA in production
with:
fetch-depth: 0 # --baseline-commit needs the base commit
persist-credentials: false
- name: Semgrep (fails on new ERROR-severity findings only)
run: >-
pipx run --spec semgrep==1.178.0 semgrep scan --config p/default
--severity ERROR --error
--baseline-commit ${{ github.event.pull_request.base.sha }}
--json-output semgrep.json
- name: Dependency audit (production dependencies)
run: npm audit --omit=dev --audit-level=high
triage:
# Schedules always run on the default branch; manual dispatches from any other branch would run that branch's code with the secret.
# (A schedule event's payload has no repository object, so the branch check must not apply to it.)
if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
persist-credentials: false
- uses: actions/setup-node@v7
with:
node-version: 22
- name: Scan and triage (advisory, read-only tools)
run: |
npm install -g @anthropic-ai/claude-code@2.1.283
pipx run --spec semgrep==1.178.0 semgrep scan --config p/default --json-output semgrep.json || true
claude -p "$(cat .github/security/triage-prompt.txt)" \
--bare --setting-sources "" --strict-mcp-config \
--allowedTools "Read,Grep,Glob" --disallowedTools "Bash,Edit,Write" \
--json-schema "$(cat .github/security/triage.schema.json)" \
--output-format json --max-budget-usd 5 | jq '.structured_output' > triage.json
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Verify triage coverage
run: |
test -s semgrep.json && jq -e '.results' semgrep.json >/dev/null || { echo 'scanner output missing'; exit 1; }
jq -e '(.errors | length) == 0' semgrep.json >/dev/null || { echo 'scanner reported errors'; exit 1; }
jq -e '.paths.scanned | length > 0' semgrep.json >/dev/null || { echo 'scanner scanned no files'; exit 1; }
jq -e '.findings' triage.json >/dev/null || { echo 'triage output missing'; exit 1; }
jq -r '.results[] | "\(.check_id)@\(.path):\(.start.line)"' semgrep.json | sort > scanner.ids
jq -r '.findings[] | select(.source=="semgrep") | .id' triage.json | sort > triage.ids
diff scanner.ids triage.ids
- uses: actions/upload-artifact@v7
if: always()
with:
name: security-triage
path: |
triage.json
scanner.ids
triage.ids

Zacommituj prompt triage (pasuje prompt dla Semgrepa z sekcji wyżej) jako .github/security/triage-prompt.txt, a schemat jako .github/security/triage.schema.json. --bare, --setting-sources "" i --strict-mcp-config sprawiają, że przebieg nie ładuje CLAUDE.md, hooków z .claude/settings.json repozytorium, ustawień użytkownika ani serwerów z .mcp.json, które inaczej wykonałyby się z sekretem joba (flagi sprawdzone w Claude Code 2.1.283). Mimo to trzymaj ten job na gałęzi domyślnej: nigdy nie przenoś go na wyzwalacz pull_request, gdzie same pobrane pliki pochodzą od kontrybutora. Warunek if na poziomie joba odrzuca też workflow_dispatch uruchomiony z innej gałęzi, którym każdy z prawem zapisu mógłby inaczej wykonać prompt i skrypty ze swojej gałęzi z sekretem. Ręczne uruchomienie czyta jednak plik workflow z wybranej gałęzi, więc tam ten if też da się usunąć; twardą gwarancję daje dopiero umieszczenie ANTHROPIC_API_KEY w środowisku (environment), którego gałęzie wdrożeń są ograniczone do domyślnej. Krok Verify triage coverage uruchamia test pokrycia z poprzedniej sekcji, więc zgubiony, zmyślony albo brakujący triage kończy job błędem, zamiast trafić do artefaktu; schemat wymusza --json-schema już przy generowaniu. Artefakt zawiera scanner.ids i triage.ids obok triage.json, więc po nieudanym przebiegu widać, które identyfikatory się różnią. Job gate działa tylko w pull requestach i blokuje na znaleziskach Semgrepa o wadze ERROR, które wprowadza dany pull request (--baseline-commit pomija znaleziska obecne już na gałęzi bazowej), oraz na każdym ostrzeżeniu npm o wadze high lub critical. Job triage działa niezależnie od bramki, według harmonogramu lub po ręcznym uruchomieniu, i tylko raportuje: nie może czekać na zieloną bramkę, bo przebieg z blokującymi znaleziskami to właśnie ten, który wymaga triage.

W Codeksie zastąp krok Scan and triage w jobie triage dwoma krokami poniżej; Verify triage coverage i wysyłkę artefaktu zostaw bez zmian, bo test pokrycia czyta te same pliki semgrep.json i triage.json:

- name: Scan (advisory)
run: pipx run --spec semgrep==1.178.0 semgrep scan --config p/default --json-output semgrep.json || true
- name: Triage with Codex (read-only)
uses: openai/codex-action@v1 # tag v1.13 (2026-10-05); pin to a full commit SHA in production
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
codex-version: 0.157.1
permission-profile: ":read-only"
prompt-file: .github/security/triage-prompt.txt
output-schema-file: .github/security/triage.schema.json
output-file: triage.json

Akcja instaluje przypiętą wersję CLI, trzyma klucz API za własnym proxy i uruchamia codex exec --output-schema z plikiem schematu, więc i tutaj schemat jest wymuszany już przy generowaniu. Wejścia permission-profile i starsze sandbox wykluczają się nawzajem, więc ustaw tylko jedno z nich (wejścia sprawdzone w README openai/codex-action v1 2 października 2026). Bez akcji odpowiednikiem jest krok npm install -g @openai/codex@0.157.1, a po nim codex exec --sandbox read-only --output-schema .github/security/triage.schema.json -o triage.json "$(cat .github/security/triage-prompt.txt)", z kluczem API pobranym z sekretu.

Dla obrazów dodaj Trivy jako krok blokujący po zbudowaniu obrazu, a przed wdrożeniem:

- name: Scan image with Trivy
uses: aquasecurity/trivy-action@v0.36.0 # never @master; pin the SHA of this tag
with:
image-ref: myorg/api:${{ github.sha }}
severity: CRITICAL,HIGH
ignore-unfixed: true
exit-code: '1'
format: table

Przypinaj każdą zewnętrzną akcję do SHA commita. Opiekunowie LiteLLM przypisali złośliwe wydania litellm 1.82.7 i 1.82.8 na PyPI (24 marca 2026) przejętemu krokowi Trivy w swoim CI (zgłoszenie BerriAI/litellm #24518, marzec 2026; przypisanie krokowi Trivy pochodzi ze źródła wtórnego). Pływający tag na skanerze bezpieczeństwa oddaje twoje sekrety CI łańcuchowi dostaw tego skanera.

Otwieraj pull requesty z poprawkami, nigdy ich nie scalaj automatycznie

Dział zatytułowany „Otwieraj pull requesty z poprawkami, nigdy ich nie scalaj automatycznie”

Agent szkicuje poprawkę na gałęzi security/* i otwiera pull request przez gh pr create. Scalenie przechodzi przez ochronę gałęzi z wymaganą recenzją właściciela bezpieczeństwa, a agent nie może scalić. W Claude Code utrwal to w .claude/settings.json:

{
"permissions": {
"deny": ["Bash(gh pr merge *)", "Bash(git push --force *)"]
}
}

Reguła deny dopasowuje prefiks, więc zatrzymuje prosty zapis polecenia, a nie każdy możliwy. Prawdziwą kontrolą jest ochrona gałęzi na GitHubie; reguła deny łapie pomyłki.

Którzy agenci bezpieczeństwa, serwery MCP i skille pasują do tej pętli?

Dział zatytułowany „Którzy agenci bezpieczeństwa, serwery MCP i skille pasują do tej pętli?”

Są dwa rodzaje pomocy. Agenci bezpieczeństwa przeglądają diff lub repozytorium własnym modelem dostawcy. Serwery MCP dają twojemu agentowi dane na żywo (alerty, stan chmury), których skanery nie widzą. Używaj obu i w każdym przypadku zostaw deterministyczną bramkę.

NarzędzieAgent bezpieczeństwa lub wtyczkaInstalacja lub wywołanie
Claude Code/security-review na diffie gałęzi; wtyczka security-guidance oznacza ryzykowne edycje na bieżąco (zobacz bramki bezpieczeństwa agentów); GitHub Action anthropics/claude-code-security-review przegląda pull requestyclaude plugin install security-guidance@claude-plugins-official
Codex@codex security review w pull requeście na GitHubie; Codex Security (oba sprawdzone 28 sierpnia 2026)integracja Codex z GitHubem
CursorSecurity Agents i Bugbot (sprawdzone 28 sierpnia 2026)panel Cursora

Serwery MCP warte podłączenia, wszystkie sprawdzone 26 września 2026:

  • Serwer GitHub MCP w trybie tylko do odczytu: agent czyta alerty Dependabota, code scanningu i secret scanningu w wielu repozytoriach. Token potrzebuje uprawnienia do odczytu tych typów alertów; bez niego odczyty wracają puste.

    Okno terminala
    # Claude Code: the token is not typed on the command line, but it is saved to the Claude Code config file
    claude mcp add --transport http github https://api.githubcopilot.com/mcp/readonly \
    -H "Authorization: Bearer $GITHUB_PAT"
    # Codex
    codex mcp add github --url https://api.githubcopilot.com/mcp/readonly --bearer-token-env-var GITHUB_PAT

    claude mcp add zapisuje rozwinięty nagłówek, więc PAT leży otwartym tekstem w pliku konfiguracji Claude Code. Żeby trzymać go wyłącznie w magazynie sekretów, dodaj serwer do projektowego .mcp.json; Claude Code rozwija ${GITHUB_PAT} przy starcie serwera:

    {
    "mcpServers": {
    "github": {
    "type": "http",
    "url": "https://api.githubcopilot.com/mcp/readonly",
    "headers": { "Authorization": "Bearer ${GITHUB_PAT}" }
    }
    }
    }

    Cursor używa tego samego adresu URL w .cursor/mcp.json; konfigurację znajdziesz w przewodniku po serwerze GitHub MCP.

  • AWS: serwer AWS Labs awslabs.well-architected-security-mcp-server (PyPI 0.2.0) raportuje stan i znaleziska GuardDuty, Security Hub, Inspector i IAM Access Analyzer. Uruchamiaj go z rolą IAM tylko do odczytu, a nie z kluczami dostępu w konfiguracji. Plik wykonywalny pakietu nazywa się awslabs.well-architected-security-mcp-server; krótsza nazwa z README projektu nie działa (sprawdzone przez uvx 26 września 2026):

    {
    "mcpServers": {
    "aws-security": {
    "command": "uvx",
    "args": ["--from", "awslabs.well-architected-security-mcp-server@0.2.0", "awslabs.well-architected-security-mcp-server"],
    "env": { "AWS_PROFILE": "security-readonly", "AWS_REGION": "eu-central-1" }
    }
    }
    }

    Codex przyjmuje te same pola jako tabelę [mcp_servers.aws-security] w ~/.codex/config.toml.

  • Azure: @azure/mcp korzysta z tożsamości z az login. Przypnij wersję stabilną, bo @azure/mcp@latest wskazuje na wersję beta 3.0.0 (sprawdzone 26 września 2026): claude mcp add azure -- npx -y @azure/mcp@2.0.5 server start.

  • Skill Semgrep: npx skills add semgrep/skills instaluje skille, które Semgrep publikuje w tym repozytorium; aktualną listę znajdziesz w jego README. Skill jest lżejszy niż serwer; sięgnij po niego, gdy chcesz, żeby agent pisał własne reguły Semgrepa dla twojej bazy kodu.

Każdy dodany serwer to nowa powierzchnia ataku; model zagrożeń dla agentów pokazuje, do czego atakujący może przez niego sięgnąć. Przeskanuj własną konfigurację agentów narzędziem Snyk Agent Scan (uvx snyk-agent-scan@0.6.8, wydanie z PyPI z 26 września 2026; wymaga SNYK_TOKEN). Uruchamia ono serwery stdio, żeby je zbadać, więc konfiguracje, którym nie ufasz, skanuj w piaskownicy. Pełną procedurę opisuje strona o zabezpieczaniu serwerów MCP.

Polityka dopuszczania zatrzymuje podatne lub nadmiernie uprzywilejowane obciążenie, które prześlizgnęło się przez CI. PodSecurityPolicy usunięto w Kubernetes 1.25; wbudowanym następcą jest Pod Security Admission, ustawiany etykietami przestrzeni nazw:

apiVersion: v1
kind: Namespace
metadata:
name: payments
labels:
pod-security.kubernetes.io/enforce: baseline
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/audit: restricted

Przebiegi skanerów, pliki triage i pull requesty z poprawkami to dowody, o które audytorzy SOC 2 i ISO 27001 pytają w obszarach zarządzania zmianą i podatnościami. Przechowuj je jako artefakty buildu z określonym okresem retencji. Mapowanie ich na kontrole to osobny workflow, opisany w automatyzacji zgodności regulacyjnej.

Kiedy operacje bezpieczeństwa wspierane przez AI się psują

Dział zatytułowany „Kiedy operacje bezpieczeństwa wspierane przez AI się psują”