From c7aa0c44a55522e8adad6c5f7cf89608e487bead Mon Sep 17 00:00:00 2001 From: Sahiba Mittal Date: Thu, 29 Feb 2024 15:20:51 +0000 Subject: [PATCH 1/3] add parsing for Nvd version range --- pom.xml | 1 - .../io/github/nscuro/versatile/VersUtils.java | 50 +++++++++++++++++++ .../nscuro/versatile/VersUtilsTest.java | 32 ++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0b654a0..049bca9 100644 --- a/pom.xml +++ b/pom.xml @@ -123,7 +123,6 @@ io.github.jeremylong open-vulnerability-clients ${lib.open-vulnerability-clients.version} - test diff --git a/src/main/java/io/github/nscuro/versatile/VersUtils.java b/src/main/java/io/github/nscuro/versatile/VersUtils.java index 358bf45..e3c1cd4 100644 --- a/src/main/java/io/github/nscuro/versatile/VersUtils.java +++ b/src/main/java/io/github/nscuro/versatile/VersUtils.java @@ -18,6 +18,7 @@ */ package io.github.nscuro.versatile; +import io.github.jeremylong.openvulnerability.client.nvd.CpeMatch; import io.github.nscuro.versatile.version.InvalidVersionException; import io.github.nscuro.versatile.version.VersioningScheme; @@ -25,6 +26,8 @@ import java.util.Map; import java.util.Set; +import static org.apache.commons.lang3.StringUtils.trimToNull; + public final class VersUtils { private VersUtils() { @@ -117,6 +120,53 @@ public static Vers versFromOsvRange(final String type, final String ecosystem, f return versBuilder.build(); } + /** + * Convert a cpeMatch or exact version as used by NVD to a {@link Vers} range. + * + * @param cpeMatch CpeMatch object for the CVE + * @param exactVersion The exact version in CpeMatch + * @return The resulting {@link Vers} + * @throws IllegalArgumentException When the provided cpe match is invalid, + * or the provided {@code events} contains an invalid event + * @throws VersException When the produced {@link Vers} is invalid + * @throws InvalidVersionException When any version in the range is invalid according to the inferred {@link VersioningScheme} + */ + public static Vers versFromNvdRange(final CpeMatch cpeMatch, final String exactVersion) { + + // Using 'generic' as versioning scheme for NVD due to lack of package data. + final var versBuilder = Vers.builder(VersioningScheme.GENERIC); + + if (trimToNull(cpeMatch.getVersionStartExcluding()) != null) { + versBuilder.withConstraint(Comparator.GREATER_THAN, cpeMatch.getVersionStartExcluding()); + } + if (trimToNull(cpeMatch.getVersionStartIncluding()) != null) { + versBuilder.withConstraint(Comparator.GREATER_THAN_OR_EQUAL, cpeMatch.getVersionStartIncluding()); + } + if (trimToNull(cpeMatch.getVersionEndExcluding()) != null) { + versBuilder.withConstraint(Comparator.LESS_THAN, cpeMatch.getVersionEndExcluding()); + } + if (trimToNull(cpeMatch.getVersionEndIncluding()) != null) { + versBuilder.withConstraint(Comparator.LESS_THAN_OR_EQUAL, cpeMatch.getVersionEndIncluding()); + } + // If CpeMatch does not define a version range, but the CPE itself can + // still give us the information we need. The version field can either be: + // * an exact version (e.g. "1.0.0") + // * a wildcard matching all versions ("*") + // * a "not applicable", matching no version at all ("-") + if (!versBuilder.hasConstraints() && exactVersion != null) { + if (!"*".equals(exactVersion) && !"-".equals(exactVersion)) { + // If we have neither upper, nor lower bound, and the CPE version + // is not a wildcard, only a specific version is vulnerable. + versBuilder.withConstraint(Comparator.EQUAL, exactVersion); + } else if ("*".equals(exactVersion)) { + // If we have neither upper, nor lower bound, and the CPE version + // is a wildcard, all versions are vulnerable, and we can safely use a vers wildcard. + versBuilder.withConstraint(Comparator.WILDCARD, null); + } + } + return versBuilder.build(); + } + static VersioningScheme schemeFromGhsaEcosystem(final String ecosystem) { // Can be one of: actions, composer, erlang, go, maven, npm, nuget, other, pip, pub, rubygems, rust. return switch (ecosystem.toLowerCase()) { diff --git a/src/test/java/io/github/nscuro/versatile/VersUtilsTest.java b/src/test/java/io/github/nscuro/versatile/VersUtilsTest.java index 7e16438..75c637d 100644 --- a/src/test/java/io/github/nscuro/versatile/VersUtilsTest.java +++ b/src/test/java/io/github/nscuro/versatile/VersUtilsTest.java @@ -30,6 +30,7 @@ import io.github.jeremylong.openvulnerability.client.ghsa.GitHubSecurityAdvisoryClient; import io.github.jeremylong.openvulnerability.client.ghsa.SecurityAdvisory; import io.github.jeremylong.openvulnerability.client.ghsa.Vulnerabilities; +import io.github.jeremylong.openvulnerability.client.nvd.CpeMatch; import io.github.nscuro.versatile.version.VersioningScheme; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -59,6 +60,7 @@ import static io.github.nscuro.versatile.VersUtils.schemeFromGhsaEcosystem; import static io.github.nscuro.versatile.VersUtils.schemeFromOsvEcosystem; import static io.github.nscuro.versatile.VersUtils.versFromGhsaRange; +import static io.github.nscuro.versatile.VersUtils.versFromNvdRange; import static io.github.nscuro.versatile.VersUtils.versFromOsvRange; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -283,4 +285,34 @@ void testVersFromOsvRangeWithAllRanges(final String ecosystem) throws Exception } } + private static Stream testVersFromNvdRangeArguments() { + return Stream.of( + arguments( + new CpeMatch(true, "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", null, null, "2.2.0", null, "2.2.13"), + "*", + "vers:generic/>=2.2.0|<=2.2.13" + ), + arguments( + new CpeMatch(true, "cpe:2.3:a:thinkcmf:thinkcmf:6.0.7:*:*:*:*:*:*:*", null, null, null, null, null), + "6.0.7", + "vers:generic/6.0.7" + ), + arguments( + new CpeMatch(true, "cpe:2.3:a:thinkcmf:thinkcmf:6.0.7:*:*:*:*:*:*:*", null, null, null, null, null), + "*", + "vers:generic/*" + ), + arguments( + new CpeMatch(true, "cpe:2.3:o:linux:linux_kernel:6.0.7:*:*:*:*:*:*:*:*", null, null, "2.2.0", null, null), + "6.0.7", + "vers:generic/>=2.2.0" + ) + ); + } + + @ParameterizedTest + @MethodSource("testVersFromNvdRangeArguments") + void testVersFromNvdRange(final CpeMatch cpeMatch, final String exactVersion, final String expectedVers) { + assertThat(versFromNvdRange(cpeMatch, exactVersion)).hasToString(expectedVers); + } } \ No newline at end of file From 6ed38adcd094d1797397d73266503e29ae7e31ff Mon Sep 17 00:00:00 2001 From: Sahiba Mittal Date: Thu, 29 Feb 2024 15:44:59 +0000 Subject: [PATCH 2/3] removed usage of CpeMatch --- pom.xml | 1 + .../io/github/nscuro/versatile/VersUtils.java | 21 ++++++++++--------- .../nscuro/versatile/VersUtilsTest.java | 20 ++++++++---------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pom.xml b/pom.xml index 049bca9..0b654a0 100644 --- a/pom.xml +++ b/pom.xml @@ -123,6 +123,7 @@ io.github.jeremylong open-vulnerability-clients ${lib.open-vulnerability-clients.version} + test diff --git a/src/main/java/io/github/nscuro/versatile/VersUtils.java b/src/main/java/io/github/nscuro/versatile/VersUtils.java index e3c1cd4..89b35c1 100644 --- a/src/main/java/io/github/nscuro/versatile/VersUtils.java +++ b/src/main/java/io/github/nscuro/versatile/VersUtils.java @@ -18,7 +18,6 @@ */ package io.github.nscuro.versatile; -import io.github.jeremylong.openvulnerability.client.nvd.CpeMatch; import io.github.nscuro.versatile.version.InvalidVersionException; import io.github.nscuro.versatile.version.VersioningScheme; @@ -131,22 +130,24 @@ public static Vers versFromOsvRange(final String type, final String ecosystem, f * @throws VersException When the produced {@link Vers} is invalid * @throws InvalidVersionException When any version in the range is invalid according to the inferred {@link VersioningScheme} */ - public static Vers versFromNvdRange(final CpeMatch cpeMatch, final String exactVersion) { + public static Vers versFromNvdRange(final String versionStartExcluding, final String versionStartIncluding, + final String versionEndExcluding, final String versionEndIncluding, + final String exactVersion) { // Using 'generic' as versioning scheme for NVD due to lack of package data. final var versBuilder = Vers.builder(VersioningScheme.GENERIC); - if (trimToNull(cpeMatch.getVersionStartExcluding()) != null) { - versBuilder.withConstraint(Comparator.GREATER_THAN, cpeMatch.getVersionStartExcluding()); + if (trimToNull(versionStartExcluding) != null) { + versBuilder.withConstraint(Comparator.GREATER_THAN, versionStartExcluding); } - if (trimToNull(cpeMatch.getVersionStartIncluding()) != null) { - versBuilder.withConstraint(Comparator.GREATER_THAN_OR_EQUAL, cpeMatch.getVersionStartIncluding()); + if (trimToNull(versionStartIncluding) != null) { + versBuilder.withConstraint(Comparator.GREATER_THAN_OR_EQUAL, versionStartIncluding); } - if (trimToNull(cpeMatch.getVersionEndExcluding()) != null) { - versBuilder.withConstraint(Comparator.LESS_THAN, cpeMatch.getVersionEndExcluding()); + if (trimToNull(versionEndExcluding) != null) { + versBuilder.withConstraint(Comparator.LESS_THAN, versionEndExcluding); } - if (trimToNull(cpeMatch.getVersionEndIncluding()) != null) { - versBuilder.withConstraint(Comparator.LESS_THAN_OR_EQUAL, cpeMatch.getVersionEndIncluding()); + if (trimToNull(versionEndIncluding) != null) { + versBuilder.withConstraint(Comparator.LESS_THAN_OR_EQUAL, versionEndIncluding); } // If CpeMatch does not define a version range, but the CPE itself can // still give us the information we need. The version field can either be: diff --git a/src/test/java/io/github/nscuro/versatile/VersUtilsTest.java b/src/test/java/io/github/nscuro/versatile/VersUtilsTest.java index 75c637d..942f5ce 100644 --- a/src/test/java/io/github/nscuro/versatile/VersUtilsTest.java +++ b/src/test/java/io/github/nscuro/versatile/VersUtilsTest.java @@ -30,7 +30,6 @@ import io.github.jeremylong.openvulnerability.client.ghsa.GitHubSecurityAdvisoryClient; import io.github.jeremylong.openvulnerability.client.ghsa.SecurityAdvisory; import io.github.jeremylong.openvulnerability.client.ghsa.Vulnerabilities; -import io.github.jeremylong.openvulnerability.client.nvd.CpeMatch; import io.github.nscuro.versatile.version.VersioningScheme; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -288,23 +287,19 @@ void testVersFromOsvRangeWithAllRanges(final String ecosystem) throws Exception private static Stream testVersFromNvdRangeArguments() { return Stream.of( arguments( - new CpeMatch(true, "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*", null, null, "2.2.0", null, "2.2.13"), - "*", + null, "2.2.0", null, "2.2.13", "*", "vers:generic/>=2.2.0|<=2.2.13" ), arguments( - new CpeMatch(true, "cpe:2.3:a:thinkcmf:thinkcmf:6.0.7:*:*:*:*:*:*:*", null, null, null, null, null), - "6.0.7", + null, null, null, null, "6.0.7", "vers:generic/6.0.7" ), arguments( - new CpeMatch(true, "cpe:2.3:a:thinkcmf:thinkcmf:6.0.7:*:*:*:*:*:*:*", null, null, null, null, null), - "*", + null, null, null, null, "*", "vers:generic/*" ), arguments( - new CpeMatch(true, "cpe:2.3:o:linux:linux_kernel:6.0.7:*:*:*:*:*:*:*:*", null, null, "2.2.0", null, null), - "6.0.7", + null, "2.2.0", null, null, "6.0.7", "vers:generic/>=2.2.0" ) ); @@ -312,7 +307,10 @@ private static Stream testVersFromNvdRangeArguments() { @ParameterizedTest @MethodSource("testVersFromNvdRangeArguments") - void testVersFromNvdRange(final CpeMatch cpeMatch, final String exactVersion, final String expectedVers) { - assertThat(versFromNvdRange(cpeMatch, exactVersion)).hasToString(expectedVers); + void testVersFromNvdRange(final String versionStartExcluding, final String versionStartIncluding, + final String versionEndExcluding, final String versionEndIncluding, + final String exactVersion, final String expectedVers) { + assertThat(versFromNvdRange(versionStartExcluding, versionStartIncluding, versionEndExcluding, versionEndIncluding, exactVersion)) + .hasToString(expectedVers); } } \ No newline at end of file From 0f44c80187cfe517c549fad2b52851e17dcc1f8f Mon Sep 17 00:00:00 2001 From: Sahiba Mittal Date: Mon, 4 Mar 2024 15:36:30 +0000 Subject: [PATCH 3/3] Update VersUtils.java --- src/main/java/io/github/nscuro/versatile/VersUtils.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/nscuro/versatile/VersUtils.java b/src/main/java/io/github/nscuro/versatile/VersUtils.java index 89b35c1..4d4af89 100644 --- a/src/main/java/io/github/nscuro/versatile/VersUtils.java +++ b/src/main/java/io/github/nscuro/versatile/VersUtils.java @@ -120,9 +120,12 @@ public static Vers versFromOsvRange(final String type, final String ecosystem, f } /** - * Convert a cpeMatch or exact version as used by NVD to a {@link Vers} range. + * Convert ranges or exact version as used by NVD to a {@link Vers} range. * - * @param cpeMatch CpeMatch object for the CVE + * @param versionStartExcluding The versionStartExcluding in the range + * @param versionStartIncluding The versionStartIncluding in the range + * @param versionEndExcluding The versionEndExcluding in the range + * @param versionEndIncluding The versionEndIncluding in the range * @param exactVersion The exact version in CpeMatch * @return The resulting {@link Vers} * @throws IllegalArgumentException When the provided cpe match is invalid,