Falešně pozitivní výsledky ve statické analýze kódu

Jak snížit počet falešně pozitivních výsledků ve statické analýze kódu

Nástroj pro statickou analýzu, který v každém pull requestu označí deset problémů, kde dva jsou skutečné problémy a osm falešných poplachů, se neopraví, ale deaktivuje. Únava z poplachů je nejčastějším důvodem, proč programy statické analýzy v praxi selhávají. Vývojáři, kteří prošetří osm falešných poplachů, aby našli dva skutečné problémy, začnou vyšetřování přeskakovat. Nástroj brzy běží, generuje varování, která nikdo nečte, a vytváří zdání bezpečnostní praxe, ale realita je neúplná.

Problém není v tom, že nástroje pro statickou analýzu produkují falešně pozitivní výsledky, ale v tom, že nadměrná aproximace je inherentní pro jejich konstrukci, protože každý spolehlivý statický analyzátor musí označit nějaký kód, který je teoreticky bezpečný. Problémem jsou falešně pozitivní výsledky, které jsou předvídatelné, reprodukovatelné a opravitelné konfigurací, potlačením nebo lepším vybavením. Snížení těchto výsledků nevyžaduje opuštění důslednosti. Vyžaduje pochopení, proč ke každému falešně pozitivnímu výsledku došlo, zda jej lze eliminovat laděním pravidel nebo potlačením a jak měřit, zda se míra v průběhu času zlepšuje.

Přestaňte vyšetřovat zjištění v mrtvém kódu

SMART TS XL identifikuje, které označené vzory jsou v nedosažitelném kódu, než na nich váš tým ztrácí čas.

Více informací

Co je falešně pozitivní výsledek ve statické analýze kódu?

K falešně pozitivnímu výsledku dochází, když nástroj pro statickou analýzu označí kód jako problematický, i když je ve skutečnosti správný, označený kód za běhu nezpůsobí chybu, zranitelnost ani narušení kvality. Analýza nástroje dospěla k závěru, který neodpovídá skutečnému chování programu.

Pochopení úplné taxonomie pomáhá určit priority, které je třeba opravit:

Typ výsledkuNástroj říkáRealitaCo dělat,
Pravda pozitivníNalezený problémSkutečný problém existujeOpravte kód
Falešně pozitivníNalezený problémŽádný skutečný problémPotlačit nebo vyladit pravidlo
Pravda negativníŽádný problémŽádný problém neexistujeOčekávané, dobré
Falešně negativníŽádný problémSkutečný problém existujeZlepšení hloubky/pravidel analýzy

Kompromis: Snížení počtu falešně pozitivních výsledků (zvýšení přesnosti) často zvyšuje počet falešně negativních výsledků. Snížení citlivosti pravidla snižuje šum, ale také snižuje pravděpodobnost odhalení skutečných problémů. Cílem není nula falešně pozitivních výsledků, ale dostatečně nízká míra falešně pozitivních výsledků, aby vývojáři nástroji důvěřovali a prošetřili každý nález.

Proč statická analýza produkuje falešně pozitivní výsledky: Technické důvody

Pochopení mechanismu každého typu falešně pozitivních signálů určuje správnou opravu.

1. Intraprocedurální analýza bez kontextu

Mnoho pravidel funguje v rámci jedné funkce, aniž by vědělo, co volající již provedl. Funkce, která dereferencuje ukazatel bez kontroly hodnoty null, může být označena příznakem, i když volající před voláním ukazatel vždy ověří. Analyzátor nevidí za hranice funkce.

c

// Caller always validates before calling -- analyzer doesn't know this
void process(Data *d) {
    int result = d->value;  // flagged: potential null dereference
    // But every caller looks like:
    // if (d != NULL) process(d);
}

Oprava: Přepněte na interprocedurální analýzu nebo použijte anotaci k informování analyzátoru o předběžné podmínce.

2. Nadměrná aproximace hodnotových rozsahů

Intervalový analyzátor, který konzervativně sleduje rozsahy proměnných, může označit dělení jako potenciálně dělitelné nulou, i když rozsah dělitele vylučuje nulu ve všech dosažitelných stavech.

Jáva

// Analyzer computes divisor range as [0, 100] and flags division by zero
// Actual runtime: config.getMinBatchSize() always returns >= 1
int batchCount = totalItems / config.getMinBatchSize();  // flagged

Fix: Přidejte tvrzení nebo předběžnou podmínku, která zužuje rozsah sledovaný analyzátorem, nebo nakonfigurujte analyzátor s modelem pro getMinBatchSize().

3. Falešné poplachy z knihoven třetích stran

Statické analyzátory obvykle postrádají modely pro chování knihoven třetích stran. Výstupy kryptografické knihovní funkce, která interně ověřuje své vstupy, budou považovány za potenciálně nedůvěryhodné, protože analyzátor nemůže zkontrolovat zdrojový kód knihovny.

4. Pravidla vzorů bez sémantického porozumění

Mnoho bezpečnostních pravidel je založeno na vzorcích: „jakékoli zřetězení uživatelského vstupu do řetězce SQL je SQL injection.“ Toto se správně spustí u zranitelného kódu a nesprávně u kódu, který před zřetězením sanitizuje vstup, protože pravidlo vzoru nemůže ověřit, zda je sanitizace správná nebo úplná.

5. Staticky vyhodnocené podmínky

Toto je specifický problém, který stojí za dotazem SC „kód není analyzován, protože podmínka je staticky vyhodnocena jako nepravdivá“. Běžné varování analyzátoru Coverity/Clang, které si zaslouží vlastní sekci.

„Kód není analyzován, protože podmínka je staticky vyhodnocena jako nepravdivá.“

Toto varování se zobrazí v nástrojích Coverity, Clang Static Analyzer a podobných, když analyzátor zjistí, že podmínka větve je vždy nepravdivá, což znamená, že kód uvnitř dané větve nelze nikdy dosáhnout při žádném spuštění, a proto se v ní zastaví analýza.

Proč k tomu dochází:

c

#define DEBUG 0  // compile-time constant

void process_record(Record *r) {
    if (DEBUG) {
        validate_record(r);  // never analyzed -- condition always false
    }
    use_record(r);  // potential issue here not caught if validate_record was needed
}

Analyzátor vyhodnocuje if (DEBUG) as if (0), vždy false a neanalyzuje tělo kódu. Toto je správné chování: kód je skutečně nedosažitelný. Varování je informativní, nikoli falešně pozitivní o chybě.

Když se z toho stane problém:

Pokud nedostupná větev obsahuje bezpečnostní kontroly, které měly být vždy spuštěny, varování signalizuje logickou chybu, nikoli chybu analýzy. Kód byl nesprávně podmíněn konstantou, která jej činí nedostupným.

Běžné příčiny:

c

// Pattern 1: debug-only guard on production-required code
if (ENABLE_VALIDATION) { validate_input(data); }  // if ENABLE_VALIDATION=0, no validation

// Pattern 2: error return always overwritten before checked
int result = do_operation();
result = 0;  // overwrites result -- subsequent if (result != 0) is always false
if (result != 0) { handle_error(); }  // never reached

// Pattern 3: overly conservative NULL check after guaranteed assignment
ptr = malloc(sizeof(Data));
if (ptr == NULL) { ... }  // valid -- malloc can return NULL
ptr->value = 0;
if (ptr == NULL) { ... }  // always false -- analyzer warns here correctly

Řešení: Pokud by větev měla být dosažitelná, opravte stav. Pokud se jedná o záměrně nefunkční kód, který lze odstranit, odstraňte jej. Pokud se jedná o kód určený pouze pro ladění, který je správně podmíněný, varování je očekávané a lze jej potlačit.

Mechanismy potlačení napříč nástroji

Potlačení říká nástroji, aby ignoroval konkrétní nález na určitém místě. Každý hlavní nástroj pro statickou analýzu poskytuje syntaxi pro potlačení. Potlačení použijte pro potvrzené falešně pozitivní výsledky, kde ladění pravidel není praktické.

Varování: Záznamy o potlačení by měly být pravidelně kontrolovány. Potlačení přidané z důvodu falešně pozitivního výsledku v roce 2023 může potlačit skutečnou zranitelnost zavedenou na stejném místě v roce 2025.

ESLint (JavaScript / TypeScript)

javascript

// Suppress next line
// eslint-disable-next-line no-unused-vars
const legacyAdapter = require('./legacy');

// Suppress a block
/* eslint-disable @typescript-eslint/no-explicit-any */
function processLegacyData(data: any): void { ... }
/* eslint-enable @typescript-eslint/no-explicit-any */

SonarQube / SonarLint

Jáva

@SuppressWarnings("java:S2077")  // Suppress SQL injection rule for this method
public List<User> searchUsers(String query) {
    // This method uses a parameterized query builder, not raw string concat
    return queryBuilder.executeParameterized(query);
}

Nebo použití vložených komentářů pro SonarQube:

Jáva

String hash = md5(password);  // NOSONAR - md5 used for non-security cache key only

Pylint (Python)

krajta

import os  # pylint: disable=unused-import  -- required for side-effect registration

def legacy_function():
    pass  # pylint: disable=W0107  -- intentionally empty for interface compliance

Semgrep

yaml

# .semgrepignore -- exclude paths
tests/fixtures/
vendor/

# Inline: suppress specific rule at a line
result = eval(expression)  # nosemgrep: python.lang.security.audit.eval-injection

Krytí

c

/* coverity[null_returns] */
Data *ptr = get_config();  // Coverity: ptr may be NULL
// Function contract guarantees non-NULL return when config is initialized

Ladění pravidel pro snížení systematických falešně pozitivních výsledků

Potlačení řeší jednotlivé instance. Ladění pravidel řeší systematické vzorce, kdy pravidlo konzistentně produkuje falešně pozitivní výsledky u legitimního kódu.

yaml

# SonarQube quality profile configuration
# Reduce sensitivity for cognitive complexity rule
sonar.java.cognitive.complexity.threshold=20  # default 15; raises bar for flagging

# Exclude generated code from analysis
sonar.exclusions=**/generated/**,**/proto/**,**/target/**
sonar.coverage.exclusions=**/*Test.java,**/*Spec.java

# Configure security hotspot categories by risk
# In sonar-project.properties:
sonar.security.hotspot.threshold=HIGH  # only show HIGH severity hotspots

yaml

# ESLint: rule-level tuning
# .eslintrc or eslint.config.js
rules:
  "@typescript-eslint/no-explicit-any": "warn"   # was "error" -- downgrade for gradual migration
  "complexity": ["warn", { "max": 20 }]           # was 10 -- adjust for legacy codebase baseline
  "max-lines-per-function": ["warn", { "max": 60, "skipBlankLines": true }]

Vyloučení cesty je jednou z nejhodnotnějších ladicích akcí. Vygenerované soubory, testovací přípravky, kód dodavatele a migrační skripty produkují legitimní, ale označené vzory. Jejich vyloučení z rozsahu analýzy okamžitě snižuje objem falešně pozitivních výsledků, aniž by se snížilo pokrytí produkčního kódu.

Falešně pozitivní výsledky v CI/CD kanálech

V CI/CD pipeline, který blokuje slučování na základě analytických zjištění, falešně pozitivní výsledky přímo ovlivňují rychlost vývojářů. Request na změnu (pull request) blokovaný třemi falešně pozitivními výsledky při každém slučování nutí vývojáře hledat způsoby, jak bránu obejít, spíše než aby jí důvěřovali.

Strategie pro správu falešně pozitivních výsledků specifických pro daný kanál:

Pouze nové brány kvality kódu. Nakonfigurujte SonarQube, CodeClimate nebo ekvivalent tak, aby se brány kvality používaly pouze na kód uvedený v žádosti o změnu, nikoli na celou kódovou základnu. Existující falešně pozitivní výsledky v kódové základně neblokují novou práci; blokují pouze nové poznatky v novém kódu.

yaml

# .github/workflows/analysis.yml
- name: SonarCloud Scan
  uses: SonarSource/sonarcloud-github-action@master
  with:
    args: >
      -Dsonar.pullrequest.base=${{ github.base_ref }}
      -Dsonar.pullrequest.branch=${{ github.head_ref }}
      # New-code analysis only: existing findings don't block

Prahové hodnoty závažnosti. Kanál selže pouze u KRITICKÝCH a VYSOKÝCH závažností. STŘEDNÍ a NÍZKÉ závažnosti se zobrazí jako varování bez blokování.

Soubory základních hodnot. Nástroje jako Semgrep a Grype podporují soubor základních hodnot, který zaznamenává nálezy přítomné v daném commitu. Nová spuštění hlásí pouze nálezy zavedené od základní hodnoty, stávající falešně pozitivní výsledky jsou ve výchozím nastavení potlačeny, aniž by bylo nutné potlačit jednotlivé instance.

praštit

# Semgrep: establish baseline, then compare
semgrep scan --baseline-commit=main --output=results.sarif src/
# Only findings introduced since main are reported

Měření a sledování míry falešně pozitivních výsledků

Snížení falešně pozitivních výsledků bez měření je otázkou odhadu. Sledujte tyto metriky v průběhu času:

metrickýJak vypočítatCíl
Falešně pozitivní sazbaPotvrzené FP / Celkový počet zjištění × 100 %Pod 20 % u bezpečnostních nástrojů; pod 10 % u kvalitních nástrojů
Hustota potlačeníPotlačení na 1 000 řádků kóduRostoucí trend = systematický problém s FP; je třeba doladit pravidla
Poměr nalezených a opravených problémůOpravené nálezy / Celkový počet nálezůRostoucí poměr = zvyšující se důvěra v nástroj
Čas na prošetřeníPrůměrný čas, který vývojáři stráví jedním nálezemPokles v čase = zlepšení FP rate

Hustota potlačení sledování je obzvláště užitečná. Pokud počet potlačení vložených kódů roste rychleji než kódová základna, znamená to, že ladění pravidel by bylo efektivnější než potlačení pro jednotlivé instance.

Klíčový princip: Potlačení, které je do produkčního prostředí dodáno bez dokumentace, je technický dluh. Každé potlačení by mělo obsahovat komentář vysvětlující, proč je nález falešně pozitivní, nejen NOSONAR anotace.

Jak SMART TS XL Snižuje falešně pozitivní výsledky pomocí strukturální analýzy

Většina falešně pozitivních výsledků ve statické analýze vzniká z nástrojů, které analyzují soubory nebo funkce, aniž by chápaly širší kontext: co volající již ověřil, jak vypadá graf závislostí, jaké cesty jsou skutečně dosažitelné z produkčních vstupních bodů.

SMART TS XLJe statická analýza kódu Před označením problémů vytvoří kompletní strukturální model kódové základny, graf závislostí, tok řízení napříč procedurami a tok dat napříč moduly, namísto samostatné analýzy každého souboru. Tento strukturální kontext odlišuje falešně pozitivní výsledky generované porovnáváním vzorů v rámci souboru od zjištění založených na skutečné dosažitelnosti a toku dat programu.

Schopnost mapování závislostí aplikací snižuje počet falešně pozitivních výsledků, které vznikají z chybějícího kontextu o tom, jak komponenty interagují. Pokud lze bezpečnostní vzorec programu v COBOLu pochopit pouze na základě znalosti toho, která úloha JCL řídí její prostředí pro provádění nebo co volající program již ověřil před jejím vyvoláním, je tento kontext napříč komponentami v analýze k dispozici, nikoli v ní chybí.

Funkce analýzy dopadů podporuje falešně pozitivní triáž v rozsáhlých starších kódových databázích: před investováním času do zkoumání označeného vzoru mohou týmy určit, zda je vzor dosažitelný z jakékoli produkční cesty. Nálezy v mrtvém kódu, vzory, které by teoreticky mohly být nebezpečné, ale v praxi jsou nedosažitelné, jsou deprivovány na základě strukturálních důkazů o dosažitelnosti, nikoli pouze na základě úsudku vývojáře.

Důvěra je metrika, na které záleží

Měřítkem programu na snížení falešně pozitivních výsledků není míra falešně pozitivních výsledků, ale důvěra vývojářů ve výsledky analýzy. Tým, který prošetřuje každý nález, protože ví, že nástroj signalizuje skutečné problémy, je tým, který získává hodnotu ze statické analýzy. Tým, který nálezy automaticky odmítá, protože většina z nich byla historicky falešnými poplachy, je tým, jehož analytický program již selhal.

Dosažení tohoto cíle vyžaduje kombinaci popsanou v této příručce: pochopení, proč se vyskytuje každá třída falešně pozitivních výsledků, potlačení potvrzených falešně pozitivních výsledků s dokumentovaným zdůvodněním, ladění pravidel tam, kde se objevují systematické vzorce, konfigurace kanálů tak, aby blokovaly skutečné nálezy bez blokování šumem, a měření rychlosti v čase, aby se zjistilo, zda se zlepšuje. Statická analýza se vyplatí. Disciplína ve správě falešně pozitivních výsledků je to, co tuto investici vyplatí.