OWASP Dependency Check to plugin do Gradle’a pozwalający zautomatyzować sprawdzanie wykorzystywanych bibliotek pod kątem luk bezpieczeństwa i dobra darmowa alternatywa dla Snyka.
Snyk jest popularnym rozwiązaniem i ma swoje zalety, jak choćby przechowywanie danych o używanych przez nas zależnościach na serwerach firmy Snyk, przez co narzędzie może zgłaszać nowe zagrożenia nawet, gdy wstrzymaliśmy development i nie odpalamy buildów naszego projektu, a ten cały czas działa na produkcji. Spotkałem się jednak z tyloma przypadkami nieumiejętności Snyka współdziałania zarówno z Mavenem jak i Gradle’em, że mam dużą rezerwę do tego narzędzia. Istotną wadą może być opłata licencyjna — jeśli nie rozwijamy oprogramowania open source, trzeba za Snyka płacić.
OWASP Dependency Check jest darmowy dla każdego użycia i działa wyłącznie lokalnie. Nie ma serwerów, na które trafiają nasze dane. Musimy sami zadbać o to, żeby uruchamiać na serwerze CI taska z tym narzędziem nawet gdy dłuższy czas nie ma commitów do repo — ale nie jest to rocket science. Współpracuje bardzo dobrze z Gradle’em.
Weźmy przykładowy projekt z proponowanym przeze mnie sposobem integracji z pluginem:
plugins { id 'java-library' id "org.owasp.dependencycheck" version "5.2.4" } repositories { jcenter() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web:2.1.0.RELEASE' testImplementation 'org.springframework.boot:spring-boot-starter-test:2.1.0.RELEASE' } dependencyCheck { failBuildOnCVSS = 9 // suppressionFile = file('config/dependency-check/suppressions.xml') scanConfigurations = ['runtimeClasspath'] } check.dependsOn dependencyCheckAnalyze dependencyCheckAnalyze { inputs.files configurations.runtimeClasspath outputs.upToDateWhen { true } shouldRunAfter test }
Pierwsze uruchomienie trwa dość długo (ponad 2 minuty), ponieważ plugin pobiera bazę podatności; normalne uruchomienie zabiera 2–3 sekundy. Gdy mamy dziury bezpieczeństwa, zobaczymy komunikat w konsoli:
% ./gradlew build Task :dependencyCheckAnalyze FAILED Verifying dependencies for project owasp-demo Checking for updates and analyzing dependencies for vulnerabilities Generating report for project owasp-demo Found 23 vulnerabilities in project owasp-demo One or more dependencies were identified with known vulnerabilities in owasp-demo: jackson-databind-2.9.7.jar (pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.9.7, cpe:2.3:a:fasterxml:jackson:2.9.7:::::::, cpe:2.3:a:fasterxml:jackson-databind:2.9.7:::::::) : CVE-2018-1000873, CVE-2018-19360, CVE-2018-19361, CVE-2018-19362, CVE-2019-12086, CVE-2019-12384, CVE-2019-12814, CVE-2019-14379, CVE-2019-14439, CVE-2019-14540, CVE-2019-16335, CVE-2019-16942, CVE-2019-16943, CVE-2019-17267, CVE-2019-17531 tomcat-embed-websocket-9.0.12.jar (pkg:maven/org.apache.tomcat.embed/tomcat-embed-websocket@9.0.12, cpe:2.3:a:apache:tomcat:9.0.12:::::::, cpe:2.3:a:apache_software_foundation:tomcat:9.0.12:::::::, cpe:2.3:a:apache_tomcat:apache_tomcat:9.0.12:::::::) : CVE-2019-0199, CVE-2019-0221, CVE-2019-0232, CVE-2019-10072 tomcat-embed-core-9.0.12.jar (pkg:maven/org.apache.tomcat.embed/tomcat-embed-core@9.0.12, cpe:2.3:a:apache:tomcat:9.0.12:::::::, cpe:2.3:a:apache_software_foundation:tomcat:9.0.12:::::::, cpe:2.3:a:apache_tomcat:apache_tomcat:9.0.12:::::::) : CVE-2019-0199, CVE-2019-0221, CVE-2019-0232, CVE-2019-10072 See the dependency-check report for more details. FAILURE: Build failed with an exception. What went wrong: Execution failed for task ':dependencyCheckAnalyze'. Dependency-Analyze Failure: One or more dependencies were identified with vulnerabilities that have a CVSS score greater than '9.0': CVE-2019-14379, CVE-2019-16942, CVE-2019-16943, CVE-2018-19362, CVE-2018-19361, CVE-2018-19360, CVE-2019-17267, CVE-2019-17531, CVE-2019-16335, CVE-2019-14540, CVE-2019-0232 See the dependency-check report for more details.
Możemy też przejrzeć szczegóły w HTML-owym raporcie w build/reports/dependency-check-report.html


Trochę wyjaśnienia, co dzieje się w konfiguracji:
failBuildOnCVSS = 9
domyślnie build nigdy nie jest przerywany; tu ustawiamy, by był przerywany przy krytycznych zagrożeniach (9 na skali od 0 do 10)suppressionFile
konfigurujemy lokalizację pliku z listą zagrożeń, którymi nie chcemy się przejmowaćscanConfigurations= ['runtimeClasspath']
domyślnie skanowane są wszystkie konfiguracje z zależnościami; różne pluginy Gradle’a definiują własne konfiguracje na wewnętrzne potrzeby i tylko na czas buildu — zazwyczaj nie ma sensu przejmować się podatnościami w bibliotekach, które nie są elementem produktu działającego na środowisku produkcyjnymshouldRunAfter test
domyślnie task ze skanowaniem zależności odpali się przed testami, tymczasem okresowo ponawiane pobieranie bazy podatności może potrwać bardzo długo; zmodyfikowana tak kolejność jest lepsza dla uzyskania szybkiej pętli zwrotnej przy buildziecheck.dependsOn dependencyCheckAnalyze
będziemy skanować zagrożenia przy każdym buildzieinputs outputs
task z pluginu nie jest napisany tak, żeby był inkrementalny; według mnie nie ma co czekać 2 sekundy przy każdym buildzie, nawet gdy nic się nie zmieniło — ten kod wprowadza mniej więcej poprawną inkrementalność
Plik suppressions.xml
ma tak skomplikowany format, że praktycznie nie da się dodawać do niego nowych wpisów ręcznie. Nie jest to jednak przeszkoda: otwierając na dysku raport HTML-owy przy każdym zagrożeniu mamy przycisk „suppress”, który generuje odpowiedni XML do skopiowania.