-
Notifications
You must be signed in to change notification settings - Fork 187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for Yocto scanner #1085
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package edu.hm.hafner.analysis.parser; | ||
|
||
import org.json.JSONArray; | ||
import org.json.JSONObject; | ||
|
||
import edu.hm.hafner.analysis.Issue; | ||
import edu.hm.hafner.analysis.IssueBuilder; | ||
import edu.hm.hafner.analysis.Report; | ||
import edu.hm.hafner.analysis.Severity; | ||
|
||
import static j2html.TagCreator.*; | ||
|
||
/** | ||
* Parser for Yocto Scanner CLI (bitbake) tool. | ||
* | ||
* @author Michael Trimarchi | ||
*/ | ||
public class YoctoScannerParser extends JsonIssueParser { | ||
private static final String VALUE_NOT_SET = "-"; | ||
private static final long serialVersionUID = 1L; | ||
private static final Double INVALID_SCORE = -1.0; | ||
|
||
@Override | ||
protected void parseJsonObject(final Report report, final JSONObject jsonReport, final IssueBuilder issueBuilder) { | ||
JSONArray packages = jsonReport.optJSONArray("package"); | ||
if (packages != null) { | ||
parseResources(report, packages, issueBuilder); | ||
} | ||
} | ||
|
||
private void parseResources(final Report report, final JSONArray packages, final IssueBuilder issueBuilder) { | ||
for (int i = 0; i < packages.length(); i++) { | ||
final Object item = packages.get(i); | ||
if (item instanceof JSONObject) { | ||
final JSONObject resourceWrapper = (JSONObject) item; | ||
if (!resourceWrapper.isNull("issue")) { | ||
parseVulnerabilities(report, issueBuilder, resourceWrapper); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private void parseVulnerabilities(final Report report, final IssueBuilder issueBuilder, | ||
final JSONObject resourceWrapper) { | ||
final JSONArray vulnerabilities = resourceWrapper.getJSONArray("issue"); | ||
for (Object vulnerability : vulnerabilities) { | ||
if (vulnerability instanceof JSONObject) { | ||
final JSONObject obj = (JSONObject) vulnerability; | ||
final String status = obj.getString("status"); | ||
boolean unpatched = "Unpatched".equals(status); | ||
if (unpatched) { | ||
report.add(convertToIssue(resourceWrapper, obj, issueBuilder)); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private Issue convertToIssue(final JSONObject resource, final JSONObject vulnerability, | ||
final IssueBuilder issueBuilder) { | ||
final String packageName = resource.getString("name"); | ||
final String fileName = vulnerability.optString("id", "UNKNOWN"); | ||
return issueBuilder | ||
.setType(fileName) | ||
.setFileName(packageName) | ||
.setSeverity(mapSeverity(vulnerability)) | ||
.setMessage(vulnerability.optString("id", "UNKNOWN")) | ||
.setDescription(formatDescription(packageName, resource, vulnerability)) | ||
.buildAndClean(); | ||
} | ||
|
||
private Severity mapSeverity(final JSONObject vulnerability) { | ||
Double score = INVALID_SCORE; | ||
boolean hasScoreV3 = vulnerability.has("scorev3"); | ||
|
||
if (hasScoreV3) { | ||
score = vulnerability.getDouble("scorev3"); | ||
} | ||
|
||
if (score <= 0) { | ||
score = vulnerability.getDouble("scorev2"); | ||
} | ||
|
||
if (score >= 0 && score < 4.0) { | ||
return Severity.WARNING_LOW; | ||
} | ||
else if (score >= 4.0 && score < 7.0) { | ||
return Severity.WARNING_NORMAL; | ||
} | ||
else if (score >= 7.0 && score <= 10.0) { | ||
return Severity.WARNING_HIGH; | ||
} | ||
|
||
return Severity.ERROR; | ||
Check warning on line 93 in src/main/java/edu/hm/hafner/analysis/parser/YoctoScannerParser.java ci.jenkins.io / Code CoverageNot covered line
|
||
} | ||
|
||
private String formatDescription(final String packageName, final JSONObject resource, final JSONObject vulnerability) { | ||
final String version = resource.optString("version", VALUE_NOT_SET); | ||
final String layer = resource.optString("layer", "UNKOWN"); | ||
final String vector = vulnerability.optString("vector", "UNKOWN"); | ||
final String link = vulnerability.optString("link", "UNKOWN"); | ||
final String description = vulnerability.optString("summary", ""); | ||
|
||
return join(div(b("Package: "), text(packageName)), | ||
div(b("Version: "), text(version)), | ||
div(b("Link: "), a(link).withHref(link)), | ||
div(b("Yocto Layer: "), text(layer)), | ||
div(b("Vector: "), text(vector)), | ||
p(text(description))).render(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package edu.hm.hafner.analysis.registry; | ||
|
||
import edu.hm.hafner.analysis.IssueParser; | ||
import edu.hm.hafner.analysis.parser.YoctoScannerParser; | ||
|
||
import static j2html.TagCreator.*; | ||
|
||
/** | ||
* A descriptor for Yocto Scanner. | ||
* | ||
* @author Michael Trimarchi | ||
*/ | ||
class YoctoScannerDescriptor extends ParserDescriptor { | ||
private static final String ID = "yoctocli"; | ||
private static final String NAME = "Yocto Scanner"; | ||
|
||
YoctoScannerDescriptor() { | ||
super(ID, NAME); | ||
} | ||
|
||
@Override | ||
public IssueParser createParser(final Option... options) { | ||
return new YoctoScannerParser(); | ||
} | ||
|
||
@Override | ||
public String getHelp() { | ||
return join(text("Use commandline"), | ||
code("bitbake <your product image>"), | ||
text(", add INHERIT += \"cve-check\" in your local.conf"), | ||
a("Yocto Scanner").withHref("https://docs.yoctoproject.org/dev/dev-manual/vulnerabilities.html"), | ||
text("for usage details.")).render(); | ||
Check warning on line 32 in src/main/java/edu/hm/hafner/analysis/registry/YoctoScannerDescriptor.java ci.jenkins.io / Code CoverageNot covered lines
|
||
} | ||
|
||
@Override | ||
public String getUrl() { | ||
return "https://docs.yoctoproject.org/dev/dev-manual/vulnerabilities.html"; | ||
} | ||
|
||
@Override | ||
public String getIconUrl() { | ||
return "https://www.yoctoproject.org/wp-content/uploads/sites/32/2023/09/YoctoProject_Logo_RGB_White_small.svg"; | ||
Check warning on line 42 in src/main/java/edu/hm/hafner/analysis/registry/YoctoScannerDescriptor.java ci.jenkins.io / Code CoverageNot covered line
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package edu.hm.hafner.analysis.parser; | ||
|
||
import org.junit.jupiter.api.Test; | ||
|
||
import edu.hm.hafner.analysis.IssueParser; | ||
import edu.hm.hafner.analysis.ParsingException; | ||
import edu.hm.hafner.analysis.Report; | ||
import edu.hm.hafner.analysis.Severity; | ||
import edu.hm.hafner.analysis.assertions.SoftAssertions; | ||
import edu.hm.hafner.analysis.registry.AbstractParserTest; | ||
|
||
import static edu.hm.hafner.analysis.assertions.Assertions.*; | ||
|
||
/** | ||
* Tests the class {@link YoctoScannerParser}. | ||
*/ | ||
class YoctoScannerParserTest extends AbstractParserTest { | ||
YoctoScannerParserTest() { | ||
super("yocto_scanner_result.json"); | ||
} | ||
|
||
@Override | ||
protected void assertThatIssuesArePresent(final Report report, final SoftAssertions softly) { | ||
softly.assertThat(report).hasSize(25); | ||
|
||
softly.assertThat(report.get(0)) | ||
.hasSeverity(Severity.WARNING_LOW) | ||
.hasFileName("acl") | ||
.hasType("CVE-2009-4411") | ||
.hasDescription("<div><b>Package: </b>acl</div> <div><b>Version: </b>2.3.2</div>" | ||
+ " <div><b>Link: </b><a href=\"https://nvd.nist.gov/vuln/detail/CVE-2009-4411\"" | ||
+ ">https://nvd.nist.gov/vuln/detail/CVE-2009-4411</a></div>" | ||
+ " <div><b>Yocto Layer: </b>meta</div> <div><b>Vector: </b>LOCAL</div> <p>" | ||
+ "The (1) setfacl and (2) getfacl commands in XFS acl 2.2.47, when running" | ||
+ " in recursive (-R) mode, follow symbolic links even when the --physical" | ||
+ " (aka -P) or -L option is specified, which might allow local users to modify" | ||
+ " the ACL for arbitrary files or directories via a symlink attack.</p>"); | ||
softly.assertThat(report.get(3)) | ||
.hasSeverity(Severity.WARNING_NORMAL) | ||
.hasFileName("automake-native") | ||
.hasType("CVE-2012-3386"); | ||
softly.assertThat(report.get(12)) | ||
.hasSeverity(Severity.WARNING_HIGH) | ||
.hasFileName("avahi") | ||
.hasType("CVE-2017-6519"); | ||
} | ||
|
||
@Test | ||
void shouldHandleEmptyResultsJenkins67296() { | ||
Report report = parse("issue67296.json"); | ||
|
||
assertThat(report).isEmpty(); | ||
} | ||
|
||
@Test | ||
void brokenInput() { | ||
assertThatThrownBy(() -> parse("eclipse.txt")).isInstanceOf(ParsingException.class); | ||
} | ||
|
||
@Override | ||
protected IssueParser createParser() { | ||
return new YoctoScannerParser(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed that to https://upload.wikimedia.org/wikipedia/commons/0/00/Yocto_Project_logo.svg, otherwise the text is white on white background. It seems that the project needs to define a SVG logo that correctly assigns the colors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@uhafner very good, and sorry, you have done a fantastic job here in this plugin