SAST Vulnerability Checker
The snippet can be accessed without any authentication.
Authored by
Zacharia Mansouri
https://cylab.be/blog/181/secure-your-project-with-the-gitlab-sast-analyzers
This checker is supposed to be used in a GitLab pipeline to retrieve a JSON report provided by a SAST analyzer job from the previous stage and go through each of the vulnerabilities the analyzer found.
All the detected vulnerabilities that have been explained with the @SAST-BYPASS[justification_text] tag (written as a commentary) will not trigger the failure of the job running this PHP script. Any presence of unexplained vulnerabilities will trigger it.
Here is the color system applied to the logs of the job running this script:
- orange: explained vulnerability
- green: vulnerability explanation
- red: unexplained vulnerability
Usage:
php sast-vuln-checker.php <JSON REPORT FILENAME>
Example of a simple .gitlab-ci.yml file using this script in its pipeline:
stages:
- test
- sast-vuln
phpcs-security-audit-sast:
image: registry.gitlab.com/security-products/sast/phpcs-security-audit:2
stage: test
artifacts:
paths:
- gl-sast-report.json
script:
- /analyzer run
sast-vuln-phpcs-security-audit-sast:
image: cylab/php74
stage: sast-vuln
needs:
- job: phpcs-security-audit-sast
script:
- php sast-vuln-checker.php gl-sast-report.json
sast-vuln-checker.php 3.21 KiB
<?php
/**
* sast-vuln-checker.php
*
* Check if the SAST analyzer found vulnerabilities, using the content of its report.
*/
if ($argc != 2) {
echo "Usage: " . $argv[0] . " <path/to/report.json>\n";
exit(-1);
}
$toParse = file_get_contents($argv[1]); // @SAST-BYPASS[$argv[1] is defined in .gitlab-ci.yml, this is not a user input]
if ($toParse === false) {
echo "Failed to open the report file!\n";
exit(-1);
}
$parsed = json_decode($toParse, true);
echo "Decoding report file: ";
switch (json_last_error()) {
case JSON_ERROR_NONE:
echo "No errors\n";
break;
case JSON_ERROR_DEPTH:
echo "Maximum stack depth exceeded\n";
break;
case JSON_ERROR_STATE_MISMATCH:
echo "Underflow or the modes mismatch\n";
break;
case JSON_ERROR_CTRL_CHAR:
echo "Unexpected control character found\n";
break;
case JSON_ERROR_SYNTAX:
echo "Syntax error, malformed JSON\n";
break;
case JSON_ERROR_UTF8:
echo "Malformed UTF-8 characters, possibly incorrectly encoded\n";
break;
default:
echo "Unknown error\n";
break;
}
if ($parsed === null) {
echo "Failed to decode the report file to JSON!\n";
exit(-1);
}
if (count($parsed['vulnerabilities']) > 0) {
$nbPassingVulns = 0;
$k = 0;
foreach ($parsed['vulnerabilities'] as $vuln) {
$k++;
echo "+" . str_repeat("-", 2 + strlen((string)($k))) . "+\n| " . $k . " |\n" . "+" . str_repeat("-", 2 + strlen((string)($k))) . "+\n";
$file = file($vuln['location']['file']); // @SAST-BYPASS[the vulnerabilities reports are supposed to be trusted]
$startLine = intval($vuln['location']['start_line']) - 1;
$endLine = $startLine;
if (array_key_exists('end_line', $vuln['location'])) {
$endLine = intval($vuln['location']['end_line']) - 1;
}
$isJustified = false;
for ($i = $startLine; $i <= $endLine; $i++) {
$line = $file[$i];
if (strpos($line, '@SAST-BYPASS') !== false) {
$pattern = '/@SAST-BYPASS\[(.*?)\]/';
$matches = [];
preg_match($pattern, $line, $matches);
$printedVuln = print_r($vuln, true);
echo "\e[38;5;215m" . $printedVuln . "\e[0m";
echo "\e[38;5;82mVulnerability ignored because:\n> " . $matches[1] . "\e[0m\n";
$nbPassingVulns++;
$isJustified = true;
break;
}
}
if (! $isJustified) {
$printedVuln = print_r($vuln, true);
echo "\e[38;5;196m" . $printedVuln . "\e[0m";
}
}
echo "\n+---------+\n| Summary |\n+---------+\n";
if ($nbPassingVulns > 0) {
echo "\e[38;5;215m" . $nbPassingVulns . " found vulnerabilities due to security checker misinterpretation.\e[0m\n";
}
$failed = false;
if (count($parsed['vulnerabilities']) > $nbPassingVulns) {
echo "\e[38;5;196m" . (count($parsed['vulnerabilities']) - $nbPassingVulns) . " vulnerabilities found in the " . $argv[1] . " report.\e[0m\n";
$failed = true;
}
echo "Download the full report from the pipeline artifacts for more details.\n";
if ($failed) {
exit(-1);
}
}
Please register or sign in to comment