diff --git a/src/main/java/com/jfrog/ide/common/deptree/DepTree.java b/src/main/java/com/jfrog/ide/common/deptree/DepTree.java index 9ffd4092..b632c2a9 100644 --- a/src/main/java/com/jfrog/ide/common/deptree/DepTree.java +++ b/src/main/java/com/jfrog/ide/common/deptree/DepTree.java @@ -1,14 +1,21 @@ package com.jfrog.ide.common.deptree; +import java.util.List; import java.util.Map; /** * Represents a dependency tree. * - * @param rootId The root node ID - * @param nodes A map of the nodes in the tree by their component IDs + * @param rootId The root node ID + * @param nodes A map of the nodes in the tree by their component IDs, merged across all modules + * @param modules The per-module trees, for projects whose modules may resolve a component differently. + * Empty for ecosystems that don't report per-module dependency trees. */ -public record DepTree(String rootId, Map nodes) { +public record DepTree(String rootId, Map nodes, List modules) { + + public DepTree(String rootId, Map nodes) { + this(rootId, nodes, List.of()); + } public DepTreeNode getRootNode() { return nodes.get(rootId); diff --git a/src/main/java/com/jfrog/ide/common/deptree/DepTreeModule.java b/src/main/java/com/jfrog/ide/common/deptree/DepTreeModule.java new file mode 100644 index 00000000..27f89a80 --- /dev/null +++ b/src/main/java/com/jfrog/ide/common/deptree/DepTreeModule.java @@ -0,0 +1,12 @@ +package com.jfrog.ide.common.deptree; + +import java.util.Map; + +/** + * The dependency tree of a single module of a multi-module project, as resolved by that module. + * + * @param rootId the module's root node ID + * @param nodes the nodes reachable from the module's root, by their component IDs + */ +public record DepTreeModule(String rootId, Map nodes) { +} diff --git a/src/main/java/com/jfrog/ide/common/gradle/GradleTreeBuilder.java b/src/main/java/com/jfrog/ide/common/gradle/GradleTreeBuilder.java index 65f34053..79319978 100644 --- a/src/main/java/com/jfrog/ide/common/gradle/GradleTreeBuilder.java +++ b/src/main/java/com/jfrog/ide/common/gradle/GradleTreeBuilder.java @@ -4,6 +4,7 @@ import com.jfrog.GradleDepTreeResults; import com.jfrog.GradleDependencyNode; import com.jfrog.ide.common.deptree.DepTree; +import com.jfrog.ide.common.deptree.DepTreeModule; import com.jfrog.ide.common.deptree.DepTreeNode; import org.jfrog.build.api.util.Log; import org.jfrog.build.extractor.scan.GeneralInfo; @@ -11,9 +12,13 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; /** * Build Gradle dependency tree before the Xray scan. @@ -56,27 +61,47 @@ public DepTree buildTree(Log logger) throws IOException { * @throws IOException in case of any I/O error. */ private DepTree createDependencyTrees(List gradleDependenciesFiles) throws IOException { + List modules = new ArrayList<>(); + for (File moduleDepsFile : gradleDependenciesFiles) { + modules.add(readModule(moduleDepsFile)); + } + Map nodes = mergeModules(modules); + Set moduleRootIds = modules.stream().map(DepTreeModule::rootId).collect(Collectors.toCollection(HashSet::new)); + if (moduleRootIds.size() == 1) { + return new DepTree(moduleRootIds.iterator().next(), nodes, modules); + } String rootId = projectDir.getFileName().toString(); - DepTreeNode rootNode = new DepTreeNode().descriptorFilePath(descriptorFilePath); + nodes.put(rootId, new DepTreeNode().descriptorFilePath(descriptorFilePath).children(moduleRootIds)); + return new DepTree(rootId, nodes, modules); + } + private DepTreeModule readModule(File moduleDepsFile) throws IOException { + GradleDepTreeResults results = objectMapper.readValue(moduleDepsFile, GradleDepTreeResults.class); Map nodes = new HashMap<>(); - for (File moduleDepsFile : gradleDependenciesFiles) { - GradleDepTreeResults results = objectMapper.readValue(moduleDepsFile, GradleDepTreeResults.class); - for (Map.Entry nodeEntry : results.getNodes().entrySet()) { - String compId = nodeEntry.getKey(); - GradleDependencyNode gradleDep = nodeEntry.getValue(); - DepTreeNode node = new DepTreeNode().scopes(gradleDep.getConfigurations()).children(gradleDep.getChildren()); - nodes.put(compId, node); - } - String moduleRootId = results.getRoot(); - nodes.get(moduleRootId).descriptorFilePath(descriptorFilePath); - rootNode.getChildren().add(moduleRootId); - } - if (rootNode.getChildren().size() == 1) { - return new DepTree(rootNode.getChildren().iterator().next(), nodes); + results.getNodes().forEach((compId, gradleDep) -> nodes.put(compId, new DepTreeNode() + .scopes(new HashSet<>(gradleDep.getConfigurations())) + .children(new HashSet<>(gradleDep.getChildren())))); + nodes.get(results.getRoot()).descriptorFilePath(descriptorFilePath); + return new DepTreeModule(results.getRoot(), nodes); + } + + /** + * A component shared by several modules may be resolved with different configurations and transitive + * dependencies in each, so its nodes are merged rather than replaced. + */ + private Map mergeModules(List modules) { + Map merged = new HashMap<>(); + for (DepTreeModule module : modules) { + module.nodes().forEach((compId, moduleNode) -> { + DepTreeNode node = merged.computeIfAbsent(compId, id -> new DepTreeNode()); + node.getScopes().addAll(moduleNode.getScopes()); + node.getChildren().addAll(moduleNode.getChildren()); + if (moduleNode.getDescriptorFilePath() != null) { + node.descriptorFilePath(moduleNode.getDescriptorFilePath()); + } + }); } - nodes.put(rootId, rootNode); - return new DepTree(rootId, nodes); + return merged; } private GeneralInfo createGeneralInfo(String id, GradleDependencyNode node) { diff --git a/src/main/java/com/jfrog/ide/common/parse/SarifParser.java b/src/main/java/com/jfrog/ide/common/parse/SarifParser.java index 0f71154e..bc4c6594 100644 --- a/src/main/java/com/jfrog/ide/common/parse/SarifParser.java +++ b/src/main/java/com/jfrog/ide/common/parse/SarifParser.java @@ -67,7 +67,17 @@ private List parseScanFindings(List runs){ List resultsList = run.getResults(); // get the scanner tool name with characters only String sourceCodeToolName = run.getTool().getDriver().getName().replaceAll("[^a-zA-Z\\s]", "").trim(); - SourceCodeScanType reporter = SourceCodeScanType.fromParam(sourceCodeToolName); + SourceCodeScanType reporter; + try { + reporter = SourceCodeScanType.fromParam(sourceCodeToolName); + } catch (IllegalArgumentException e) { + if (resultsList == null || resultsList.isEmpty()) { + log.debug("Skipping an unsupported scanner with no results: " + sourceCodeToolName); + } else { + log.warn("Skipping " + resultsList.size() + " results of an unsupported scanner: " + sourceCodeToolName); + } + continue; + } for (Result result : resultsList){ ReportingDescriptor rule = run.getTool().getDriver().getRules().stream() diff --git a/src/test/java/com/jfrog/ide/common/configuration/JfrogCliDriverTest.java b/src/test/java/com/jfrog/ide/common/configuration/JfrogCliDriverTest.java index 4fbe7ca1..b46d03ee 100644 --- a/src/test/java/com/jfrog/ide/common/configuration/JfrogCliDriverTest.java +++ b/src/test/java/com/jfrog/ide/common/configuration/JfrogCliDriverTest.java @@ -5,6 +5,7 @@ import com.jfrog.ide.common.nodes.subentities.Severity; import com.jfrog.ide.common.nodes.subentities.SourceCodeScanType; import com.jfrog.ide.common.parse.SarifParser; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.SystemUtils; import org.jfrog.build.api.util.NullLog; import org.jfrog.build.extractor.executor.CommandResults; @@ -46,6 +47,7 @@ public class JfrogCliDriverTest { private final String XRAY_URL = SERVER_URL + "xray/"; private String testServerId; private File tempDir; + private File exampleProjectCopy; private final SarifParser parser = new SarifParser(new NullLog()); private final Logger logger = LoggerFactory.getLogger(JfrogCliDriverTest.class); @@ -204,8 +206,8 @@ public void testAddServerConfig_withBadCredentials() { @Test public void testRunAudit_NpmProject() { try { - Path exampleProjectsFolder = Path.of("src/test/resources/example-projects/npm"); - CommandResults response = jfrogCliDriver.runCliAudit(exampleProjectsFolder.toFile(), + File exampleProject = copyExampleProject("npm"); + CommandResults response = jfrogCliDriver.runCliAudit(exampleProject, null, testServerId, testEnv); assertEquals(response.getExitValue(),0); logger.info("Audit debug logs: \n" + response.getErr()); @@ -214,8 +216,8 @@ public void testRunAudit_NpmProject() { assertNotNull(findings); assertFalse(findings.isEmpty(), "Expected findings in SARIF output for npm project"); // Verify the findings - assertEquals(findings.size(), 1, "Expected exactly one file with findings"); - FileTreeNode node = findings.get(0); + FileTreeNode node = findings.stream().filter(finding -> finding.getTitle().equals("package.json")).findFirst().orElse(null); + assertNotNull(node, "Expected SCA findings in package.json"); assertEquals(node.getChildren().size(), 1, "Expected exactly one vulnerabilities"); FileIssueNode issue = (FileIssueNode) node.getChildren().get(0); assertEquals(issue.getSeverity(), Severity.High, "Expected severity to be HIGH"); @@ -229,8 +231,8 @@ public void testRunAudit_NpmProject() { public void testRunAudit_MultiMavenProject() { List projectsToCheck = new ArrayList<>(Arrays.asList("multi1", "multi2")); try { - Path exampleProjectsFolder = Path.of("src/test/resources/example-projects/maven-example"); - CommandResults response = jfrogCliDriver.runCliAudit(exampleProjectsFolder.toFile(), + File exampleProject = copyExampleProject("maven-example"); + CommandResults response = jfrogCliDriver.runCliAudit(exampleProject, projectsToCheck, testServerId, testEnv); assertEquals(response.getExitValue(), 0); logger.info("Audit debug logs: \n" + response.getErr()); @@ -239,8 +241,8 @@ public void testRunAudit_MultiMavenProject() { assertNotNull(findings); assertFalse(findings.isEmpty(), "Expected findings in SARIF output for multi-maven project"); // Verify the findings - assertEquals(findings.size(), 1, "Expected exactly one file with findings"); - FileTreeNode node = findings.get(0); + FileTreeNode node = findings.stream().filter(finding -> finding.getTitle().equals("pom.xml")).findFirst().orElse(null); + assertNotNull(node, "Expected SCA findings in pom.xml"); assertEquals(node.getChildren().size(), 3, "Expected exactly three vulnerabilities"); assertEquals(node.getSeverity(), Severity.High, "Expected severity to be HIGH"); FileIssueNode issue = (FileIssueNode) node.getChildren().get(0); @@ -250,21 +252,31 @@ public void testRunAudit_MultiMavenProject() { } } - private String createServerId() { + /** + * Audits run on a copy of the example project, because jf audit skips a working directory whose absolute + * path matches its default exclusion patterns, and this repository keeps its fixtures under src/test. + */ + private File copyExampleProject(String projectName) throws IOException { + exampleProjectCopy = Files.createTempDirectory("ide-plugins-common-audit").toFile(); + FileUtils.copyDirectory(Path.of("src/test/resources/example-projects", projectName).toFile(), exampleProjectCopy); + return exampleProjectCopy; + } + + private String createServerId() { return "ide-plugins-common-test-server-" + timeStampFormat.format(System.currentTimeMillis()); } @Test public void testRunAudit_WithExcludedPattern() { try { - Path exampleProjectsFolder = Path.of("src/test/resources/example-projects/maven-example"); + File exampleProject = copyExampleProject("maven-example"); AuditConfig config = new AuditConfig.Builder() .serverId(testServerId) .excludedPattern(new ArrayList<>(List.of("*multi3*"))) .serverId(testServerId) .envVars(testEnv) .build(); - CommandResults response = jfrogCliDriver.runCliAudit(exampleProjectsFolder.toFile(), config); + CommandResults response = jfrogCliDriver.runCliAudit(exampleProject, config); assertEquals(response.getExitValue(), 0); logger.info("Audit debug logs: \n" + response.getErr()); logger.info("Audit response: \n" + response.getRes()); @@ -272,8 +284,8 @@ public void testRunAudit_WithExcludedPattern() { assertNotNull(findings); assertFalse(findings.isEmpty(), "Expected findings in SARIF output for multi-maven project"); // Verify the findings - assertEquals(findings.size(), 1, "Expected exactly one file with findings"); - FileTreeNode node = findings.get(0); + FileTreeNode node = findings.stream().filter(finding -> finding.getTitle().equals("pom.xml")).findFirst().orElse(null); + assertNotNull(node, "Expected SCA findings in pom.xml"); assertEquals(node.getChildren().size(), 3, "Expected exactly three vulnerabilities"); assertEquals(node.getSeverity(), Severity.High, "Expected severity to be HIGH"); FileIssueNode issue = (FileIssueNode) node.getChildren().get(0); @@ -285,6 +297,7 @@ public void testRunAudit_WithExcludedPattern() { @AfterMethod public void cleanUp(Method method) { + FileUtils.deleteQuietly(exampleProjectCopy); try { if (!TEST_NAME_TO_SKIP_CLI_DOWNLOAD.equals(method.getName())) { String[] serverConfigCmdArgs = {"config", "remove", testServerId, "--quiet"}; diff --git a/src/test/java/com/jfrog/ide/common/gradle/GradleTreeBuilderTest.java b/src/test/java/com/jfrog/ide/common/gradle/GradleTreeBuilderTest.java index 564ae701..587e9210 100644 --- a/src/test/java/com/jfrog/ide/common/gradle/GradleTreeBuilderTest.java +++ b/src/test/java/com/jfrog/ide/common/gradle/GradleTreeBuilderTest.java @@ -3,6 +3,7 @@ import com.jfrog.GradleDependencyNode; import com.jfrog.ide.common.TestUtils; import com.jfrog.ide.common.deptree.DepTree; +import com.jfrog.ide.common.deptree.DepTreeModule; import com.jfrog.ide.common.deptree.DepTreeNode; import org.apache.commons.io.FileUtils; import org.jfrog.build.api.util.NullLog; @@ -80,6 +81,45 @@ public void gradleTreeBuilderUnresolvedTest(String projectPath) throws IOExcepti assertTrue(missing.getScopes().contains("testImplementation")); } + /** + * Data provider for a project whose modules resolve the same dependency with different transitive + * dependencies - 'modb' excludes 'commons-lang3' from 'commons-text', 'moda' doesn't. + * + * @return 'sharedDependency'. + */ + @DataProvider + private Object[][] gradleTreeBuilderSharedDependencyProvider() { + return new Object[][]{{"sharedDependency"}}; + } + + @SuppressWarnings("unused") + @Test(dataProvider = "gradleTreeBuilderSharedDependencyProvider") + public void gradleTreeBuilderSharedDependencyTest(String projectPath) throws IOException { + final String COMMONS_TEXT = "org.apache.commons:commons-text:1.9"; + final String COMMONS_LANG3 = "org.apache.commons:commons-lang3:3.11"; + DepTree depTree = buildGradleDependencyTree(projectPath); + + DepTreeNode commonsText = depTree.nodes().get(COMMONS_TEXT); + assertNotNull(commonsText, "Couldn't find node '" + COMMONS_TEXT + "'."); + assertTrue(commonsText.getChildren().contains(COMMONS_LANG3), + "The merged tree must keep the edge resolved by 'moda'"); + + Map modulesByRoot = new HashMap<>(); + depTree.modules().forEach(module -> modulesByRoot.put(module.rootId(), module)); + DepTreeModule moda = modulesByRoot.get("org.jfrog.test.gradle.shared:moda:1.0-SNAPSHOT"); + assertNotNull(moda, "Couldn't find the 'moda' module tree in " + modulesByRoot.keySet()); + DepTreeNode modaCommonsText = moda.nodes().get(COMMONS_TEXT); + assertNotNull(modaCommonsText, "Couldn't find '" + COMMONS_TEXT + "' in the 'moda' module tree."); + assertTrue(modaCommonsText.getChildren().contains(COMMONS_LANG3)); + assertTrue(modaCommonsText.getScopes().contains("implementation")); + DepTreeModule modb = modulesByRoot.get("org.jfrog.test.gradle.shared:modb:1.0-SNAPSHOT"); + assertNotNull(modb, "Couldn't find the 'modb' module tree in " + modulesByRoot.keySet()); + DepTreeNode modbCommonsText = modb.nodes().get(COMMONS_TEXT); + assertNotNull(modbCommonsText, "Couldn't find '" + COMMONS_TEXT + "' in the 'modb' module tree."); + assertFalse(modbCommonsText.getChildren().contains(COMMONS_LANG3), + "'modb' excludes commons-lang3, so its own tree must not contain the edge"); + } + private DepTree buildGradleDependencyTree(String projectPath) throws IOException { // Add path to gradle-dep-tree JAR to "pluginLibDir" environment variable, to be read in gradle-dep-tree.gradle init script Map env = new HashMap<>(System.getenv()); diff --git a/src/test/java/com/jfrog/ide/common/parse/SarifParserTest.java b/src/test/java/com/jfrog/ide/common/parse/SarifParserTest.java index f55449a2..91f03505 100644 --- a/src/test/java/com/jfrog/ide/common/parse/SarifParserTest.java +++ b/src/test/java/com/jfrog/ide/common/parse/SarifParserTest.java @@ -46,6 +46,14 @@ public void testParseInvalidSarifReport() { assertThrows(NullPointerException.class, () -> parser.parse(readSarifReportFromFile(resourcesDir + "invalid_sarif_no_results.json"))); } + @Test + public void testParseSarifReportSkipsUnsupportedScanners() throws IOException { + results = parser.parse(readSarifReportFromFile(resourcesDir + "sca_and_unsupported_scanners.json")); + + assertEquals(results.size(), 1); + assertEquals(results.get(0).getChildren().size(), 10); + } + @Test public void testParseSarifReportWithOnlyScaResults() throws IOException { results = parser.parse(readSarifReportFromFile(resourcesDir + "sca_no_jas.json")); diff --git a/src/test/resources/gradle/sharedDependency/build.gradle b/src/test/resources/gradle/sharedDependency/build.gradle new file mode 100644 index 00000000..795af685 --- /dev/null +++ b/src/test/resources/gradle/sharedDependency/build.gradle @@ -0,0 +1,25 @@ +allprojects { + group = 'org.jfrog.test.gradle.shared' + version = '1.0-SNAPSHOT' + repositories { + mavenCentral() + } +} + +subprojects { + apply plugin: 'java' +} + +project('moda') { + dependencies { + implementation 'org.apache.commons:commons-text:1.9' + } +} + +project('modb') { + dependencies { + implementation('org.apache.commons:commons-text:1.9') { + exclude group: 'org.apache.commons', module: 'commons-lang3' + } + } +} diff --git a/src/test/resources/gradle/sharedDependency/moda/src/main/java/Moda.java b/src/test/resources/gradle/sharedDependency/moda/src/main/java/Moda.java new file mode 100644 index 00000000..2b6fbcdf --- /dev/null +++ b/src/test/resources/gradle/sharedDependency/moda/src/main/java/Moda.java @@ -0,0 +1,2 @@ +public class Moda { +} diff --git a/src/test/resources/gradle/sharedDependency/modb/src/main/java/Modb.java b/src/test/resources/gradle/sharedDependency/modb/src/main/java/Modb.java new file mode 100644 index 00000000..91fc5298 --- /dev/null +++ b/src/test/resources/gradle/sharedDependency/modb/src/main/java/Modb.java @@ -0,0 +1,2 @@ +public class Modb { +} diff --git a/src/test/resources/gradle/sharedDependency/settings.gradle b/src/test/resources/gradle/sharedDependency/settings.gradle new file mode 100644 index 00000000..2e05e372 --- /dev/null +++ b/src/test/resources/gradle/sharedDependency/settings.gradle @@ -0,0 +1 @@ +include "moda", "modb" diff --git a/src/test/resources/parse/sca_and_unsupported_scanners.json b/src/test/resources/parse/sca_and_unsupported_scanners.json new file mode 100644 index 00000000..83728438 --- /dev/null +++ b/src/test/resources/parse/sca_and_unsupported_scanners.json @@ -0,0 +1,713 @@ +{ + "version": "2.1.0", + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "runs": [ + { + "tool": { + "driver": { + "informationUri": "https://docs.jfrog-applications.jfrog.io/jfrog-security-features/sca", + "name": "JFrog Xray Scanner", + "rules": [ + { + "id": "CVE-2024-30172_BouncyCastle_1.8.9", + "name": "CVE-2024-30172-BouncyCastle-1.8.9", + "shortDescription": { + "text": "[CVE-2024-30172] BouncyCastle 1.8.9" + }, + "fullDescription": { + "text": "An issue was discovered in Bouncy Castle Java Cryptography APIs before 1.78. An Ed25519 verification code infinite loop can occur via a crafted signature and public key.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.3 | Not Covered | `BouncyCastle 1.8.9` | No fix available |" + }, + "help": { + "text": "An issue was discovered in Bouncy Castle Java Cryptography APIs before 1.78. An Ed25519 verification code infinite loop can occur via a crafted signature and public key.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.3 | Not Covered | `BouncyCastle 1.8.9` | No fix available |" + }, + "properties": { + "impactPaths": [ + [ + { + "name": "TestApp1", + "version": "" + }, + { + "name": "BouncyCastle", + "version": "1.8.9" + } + ] + ], + "security-severity": "5.3" + } + }, + { + "id": "CVE-2024-30171_BouncyCastle_1.8.9", + "name": "CVE-2024-30171-BouncyCastle-1.8.9", + "shortDescription": { + "text": "[CVE-2024-30171] BouncyCastle 1.8.9" + }, + "fullDescription": { + "text": "An issue was discovered in Bouncy Castle Java TLS API and JSSE Provider before 1.78. Timing-based leakage may occur in RSA based handshakes because of exception processing.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.9 | Not Covered | `BouncyCastle 1.8.9` | No fix available |" + }, + "help": { + "text": "An issue was discovered in Bouncy Castle Java TLS API and JSSE Provider before 1.78. Timing-based leakage may occur in RSA based handshakes because of exception processing.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.9 | Not Covered | `BouncyCastle 1.8.9` | No fix available |" + }, + "properties": { + "impactPaths": [ + [ + { + "name": "TestApp1", + "version": "" + }, + { + "name": "BouncyCastle", + "version": "1.8.9" + } + ] + ], + "security-severity": "5.9" + } + }, + { + "id": "CVE-2020-1045_Microsoft.AspNetCore.Http_2.1.0", + "name": "CVE-2020-1045-Microsoft.AspNetCore.Http-2.1.0", + "shortDescription": { + "text": "[CVE-2020-1045] Microsoft.AspNetCore.Http 2.1.0" + }, + "fullDescription": { + "text": "

A security feature bypass vulnerability exists in the way Microsoft ASP.NET Core parses encoded cookie names.

\n

The ASP.NET Core cookie parser decodes entire cookie strings which could allow a malicious attacker to set a second cookie with the name being percent encoded.

\n

The security update addresses the vulnerability by fixing the way the ASP.NET Core cookie parser handles encoded names.

", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 7.5 | Not Applicable | `Microsoft.AspNetCore.Hosting 2.1.0` | [2.1.22] |" + }, + "help": { + "text": "

A security feature bypass vulnerability exists in the way Microsoft ASP.NET Core parses encoded cookie names.

\n

The ASP.NET Core cookie parser decodes entire cookie strings which could allow a malicious attacker to set a second cookie with the name being percent encoded.

\n

The security update addresses the vulnerability by fixing the way the ASP.NET Core cookie parser handles encoded names.

", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 7.5 | Not Applicable | `Microsoft.AspNetCore.Hosting 2.1.0` | [2.1.22] |" + }, + "properties": { + "impactPaths": [ + [ + { + "name": "ClassLibrary1", + "version": "" + }, + { + "name": "Microsoft.AspNetCore.Hosting", + "version": "2.1.0" + }, + { + "name": "Microsoft.AspNetCore.Http", + "version": "2.1.0" + } + ] + ], + "security-severity": "7.5" + } + }, + { + "id": "CVE-2020-1045_Microsoft.AspNetCore.Http_2.1.1", + "name": "CVE-2020-1045-Microsoft.AspNetCore.Http-2.1.1", + "shortDescription": { + "text": "[CVE-2020-1045] Microsoft.AspNetCore.Http 2.1.1" + }, + "fullDescription": { + "text": "

A security feature bypass vulnerability exists in the way Microsoft ASP.NET Core parses encoded cookie names.

\n

The ASP.NET Core cookie parser decodes entire cookie strings which could allow a malicious attacker to set a second cookie with the name being percent encoded.

\n

The security update addresses the vulnerability by fixing the way the ASP.NET Core cookie parser handles encoded names.

", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 7.5 | Not Applicable | `Microsoft.AspNetCore.Hosting 2.1.1` | [2.1.22] |" + }, + "help": { + "text": "

A security feature bypass vulnerability exists in the way Microsoft ASP.NET Core parses encoded cookie names.

\n

The ASP.NET Core cookie parser decodes entire cookie strings which could allow a malicious attacker to set a second cookie with the name being percent encoded.

\n

The security update addresses the vulnerability by fixing the way the ASP.NET Core cookie parser handles encoded names.

", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 7.5 | Not Applicable | `Microsoft.AspNetCore.Hosting 2.1.1` | [2.1.22] |" + }, + "properties": { + "impactPaths": [ + [ + { + "name": "TestApp1", + "version": "" + }, + { + "name": "Microsoft.AspNetCore.Hosting", + "version": "2.1.1" + }, + { + "name": "Microsoft.AspNetCore.Http", + "version": "2.1.1" + } + ] + ], + "security-severity": "7.5" + } + }, + { + "id": "CVE-2021-26701_System.Text.Encodings.Web_4.5.0", + "name": "CVE-2021-26701-System.Text.Encodings.Web-4.5.0", + "shortDescription": { + "text": "[CVE-2021-26701] System.Text.Encodings.Web 4.5.0" + }, + "fullDescription": { + "text": ".NET Core Remote Code Execution Vulnerability", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 8.1 | Not Applicable | `Microsoft.AspNetCore.Hosting 2.1.0`
`Serilog.AspNetCore 2.1.1` | [4.5.1], [4.7.2], [5.0.1] |" + }, + "help": { + "text": ".NET Core Remote Code Execution Vulnerability", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 8.1 | Not Applicable | `Microsoft.AspNetCore.Hosting 2.1.0`
`Serilog.AspNetCore 2.1.1` | [4.5.1], [4.7.2], [5.0.1] |" + }, + "properties": { + "impactPaths": [ + [ + { + "name": "TestApp1", + "version": "" + }, + { + "name": "Serilog.AspNetCore", + "version": "2.1.1" + }, + { + "name": "Microsoft.AspNetCore.Hosting.Abstractions", + "version": "2.1.1" + }, + { + "name": "Microsoft.AspNetCore.Http.Abstractions", + "version": "2.1.1" + }, + { + "name": "System.Text.Encodings.Web", + "version": "4.5.0" + } + ], + [ + { + "name": "ClassLibrary1", + "version": "" + }, + { + "name": "Microsoft.AspNetCore.Hosting", + "version": "2.1.0" + }, + { + "name": "Microsoft.AspNetCore.Http", + "version": "2.1.0" + }, + { + "name": "Microsoft.AspNetCore.WebUtilities", + "version": "2.1.0" + }, + { + "name": "System.Text.Encodings.Web", + "version": "4.5.0" + } + ] + ], + "security-severity": "8.1" + } + }, + { + "id": "CVE-2024-21907_Newtonsoft.Json_12.0.3", + "name": "CVE-2024-21907-Newtonsoft.Json-12.0.3", + "shortDescription": { + "text": "[CVE-2024-21907] Newtonsoft.Json 12.0.3" + }, + "fullDescription": { + "text": "Newtonsoft.Json before version 13.0.1 is affected by a mishandling of exceptional conditions vulnerability. Crafted data that is passed to the JsonConvert.DeserializeObject method may trigger a StackOverflow exception resulting in denial of service. Depending on the usage of the library, an unauthenticated and remote attacker may be able to cause the denial of service condition.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 7.5 | Not Applicable | `Newtonsoft.Json 12.0.3`
`Google.Cloud.Kms.V1 2.0.0` | [13.0.1] |" + }, + "help": { + "text": "Newtonsoft.Json before version 13.0.1 is affected by a mishandling of exceptional conditions vulnerability. Crafted data that is passed to the JsonConvert.DeserializeObject method may trigger a StackOverflow exception resulting in denial of service. Depending on the usage of the library, an unauthenticated and remote attacker may be able to cause the denial of service condition.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 7.5 | Not Applicable | `Newtonsoft.Json 12.0.3`
`Google.Cloud.Kms.V1 2.0.0` | [13.0.1] |" + }, + "properties": { + "impactPaths": [ + [ + { + "name": "TestApp1", + "version": "" + }, + { + "name": "Google.Cloud.Kms.V1", + "version": "2.0.0" + }, + { + "name": "Google.Api.Gax.Grpc.GrpcCore", + "version": "3.0.0" + }, + { + "name": "Google.Api.Gax.Grpc", + "version": "3.0.0" + }, + { + "name": "Google.Api.Gax", + "version": "3.0.0" + }, + { + "name": "Newtonsoft.Json", + "version": "12.0.3" + } + ], + [ + { + "name": "ClassLibrary1", + "version": "" + }, + { + "name": "Newtonsoft.Json", + "version": "12.0.3" + } + ] + ], + "security-severity": "7.5" + } + }, + { + "id": "CVE-2021-22570_Google.Protobuf_3.11.4", + "name": "CVE-2021-22570-Google.Protobuf-3.11.4", + "shortDescription": { + "text": "[CVE-2021-22570] Google.Protobuf 3.11.4" + }, + "fullDescription": { + "text": "Nullptr dereference when a null char is present in a proto symbol. The symbol is parsed incorrectly, leading to an unchecked call into the proto file's name during generation of the resulting error message. Since the symbol is incorrectly parsed, the file is nullptr. We recommend upgrading to version 3.15.0 or greater.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.5 | Not Covered | `Google.Cloud.Kms.V1 2.0.0` | [3.15.0] |" + }, + "help": { + "text": "Nullptr dereference when a null char is present in a proto symbol. The symbol is parsed incorrectly, leading to an unchecked call into the proto file's name during generation of the resulting error message. Since the symbol is incorrectly parsed, the file is nullptr. We recommend upgrading to version 3.15.0 or greater.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.5 | Not Covered | `Google.Cloud.Kms.V1 2.0.0` | [3.15.0] |" + }, + "properties": { + "impactPaths": [ + [ + { + "name": "TestApp1", + "version": "" + }, + { + "name": "Google.Cloud.Kms.V1", + "version": "2.0.0" + }, + { + "name": "Google.Api.Gax.Grpc.GrpcCore", + "version": "3.0.0" + }, + { + "name": "Google.Api.Gax.Grpc", + "version": "3.0.0" + }, + { + "name": "Google.Api.CommonProtos", + "version": "2.0.0" + }, + { + "name": "Google.Protobuf", + "version": "3.11.4" + } + ] + ], + "security-severity": "5.5" + } + }, + { + "id": "CVE-2024-29857_BouncyCastle_1.8.9", + "name": "CVE-2024-29857-BouncyCastle-1.8.9", + "shortDescription": { + "text": "[CVE-2024-29857] BouncyCastle 1.8.9" + }, + "fullDescription": { + "text": "An issue was discovered in ECCurve.java and ECCurve.cs in Bouncy Castle Java (BC Java) before 1.78, BC Java LTS before 2.73.6, BC-FJA before 1.0.2.5, and BC C# .Net before 2.3.1. Importing an EC certificate with crafted F2m parameters can lead to excessive CPU consumption during the evaluation of the curve parameters.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.3 | Not Covered | `BouncyCastle 1.8.9` | No fix available |" + }, + "help": { + "text": "An issue was discovered in ECCurve.java and ECCurve.cs in Bouncy Castle Java (BC Java) before 1.78, BC Java LTS before 2.73.6, BC-FJA before 1.0.2.5, and BC C# .Net before 2.3.1. Importing an EC certificate with crafted F2m parameters can lead to excessive CPU consumption during the evaluation of the curve parameters.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.3 | Not Covered | `BouncyCastle 1.8.9` | No fix available |" + }, + "properties": { + "impactPaths": [ + [ + { + "name": "TestApp1", + "version": "" + }, + { + "name": "BouncyCastle", + "version": "1.8.9" + } + ] + ], + "security-severity": "5.3" + } + } + ], + "version": "3.115.6" + } + }, + "invocations": [ + { + "executionSuccessful": true, + "workingDirectory": { + "uri": "C:\\Users\\User\\Desktop\\nuget\\multi" + } + } + ], + "results": [ + { + "properties": { + "applicability": "Not Applicable", + "fixedVersion": "[2.1.22]" + }, + "ruleId": "CVE-2020-1045_Microsoft.AspNetCore.Http_2.1.0", + "ruleIndex": 2, + "level": "error", + "message": { + "text": "[CVE-2020-1045] Microsoft.AspNetCore.Hosting 2.1.0", + "markdown": "[CVE-2020-1045] Microsoft.AspNetCore.Hosting 2.1.0" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "TestSolution.sln" + } + } + } + ] + }, + { + "properties": { + "applicability": "Not Applicable", + "fixedVersion": "[2.1.22]" + }, + "ruleId": "CVE-2020-1045_Microsoft.AspNetCore.Http_2.1.1", + "ruleIndex": 3, + "level": "error", + "message": { + "text": "[CVE-2020-1045] Microsoft.AspNetCore.Hosting 2.1.1", + "markdown": "[CVE-2020-1045] Microsoft.AspNetCore.Hosting 2.1.1" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "TestSolution.sln" + } + } + } + ] + }, + { + "properties": { + "applicability": "Not Applicable", + "fixedVersion": "[4.5.1], [4.7.2], [5.0.1]" + }, + "ruleId": "CVE-2021-26701_System.Text.Encodings.Web_4.5.0", + "ruleIndex": 4, + "level": "error", + "message": { + "text": "[CVE-2021-26701] Microsoft.AspNetCore.Hosting 2.1.0", + "markdown": "[CVE-2021-26701] Microsoft.AspNetCore.Hosting 2.1.0" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "TestSolution.sln" + } + } + } + ] + }, + { + "properties": { + "applicability": "Not Applicable", + "fixedVersion": "[4.5.1], [4.7.2], [5.0.1]" + }, + "ruleId": "CVE-2021-26701_System.Text.Encodings.Web_4.5.0", + "ruleIndex": 4, + "level": "error", + "message": { + "text": "[CVE-2021-26701] Serilog.AspNetCore 2.1.1", + "markdown": "[CVE-2021-26701] Serilog.AspNetCore 2.1.1" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "TestSolution.sln" + } + } + } + ] + }, + { + "properties": { + "applicability": "Not Applicable", + "fixedVersion": "[13.0.1]" + }, + "ruleId": "CVE-2024-21907_Newtonsoft.Json_12.0.3", + "ruleIndex": 5, + "level": "error", + "message": { + "text": "[CVE-2024-21907] Newtonsoft.Json 12.0.3", + "markdown": "[CVE-2024-21907] Newtonsoft.Json 12.0.3" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "TestSolution.sln" + } + } + } + ] + }, + { + "properties": { + "applicability": "Not Applicable", + "fixedVersion": "[13.0.1]" + }, + "ruleId": "CVE-2024-21907_Newtonsoft.Json_12.0.3", + "ruleIndex": 5, + "level": "error", + "message": { + "text": "[CVE-2024-21907] Google.Cloud.Kms.V1 2.0.0", + "markdown": "[CVE-2024-21907] Google.Cloud.Kms.V1 2.0.0" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "TestSolution.sln" + } + } + } + ] + }, + { + "properties": { + "applicability": "Not Covered", + "fixedVersion": "[3.15.0]" + }, + "ruleId": "CVE-2021-22570_Google.Protobuf_3.11.4", + "ruleIndex": 6, + "level": "warning", + "message": { + "text": "[CVE-2021-22570] Google.Cloud.Kms.V1 2.0.0", + "markdown": "[CVE-2021-22570] Google.Cloud.Kms.V1 2.0.0" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "TestSolution.sln" + } + } + } + ] + }, + { + "properties": { + "applicability": "Not Covered", + "fixedVersion": "No fix available" + }, + "ruleId": "CVE-2024-29857_BouncyCastle_1.8.9", + "ruleIndex": 7, + "level": "warning", + "message": { + "text": "[CVE-2024-29857] BouncyCastle 1.8.9", + "markdown": "[CVE-2024-29857] BouncyCastle 1.8.9" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "TestSolution.sln" + } + } + } + ] + }, + { + "properties": { + "applicability": "Not Covered", + "fixedVersion": "No fix available" + }, + "ruleId": "CVE-2024-30172_BouncyCastle_1.8.9", + "ruleIndex": 0, + "level": "warning", + "message": { + "text": "[CVE-2024-30172] BouncyCastle 1.8.9", + "markdown": "[CVE-2024-30172] BouncyCastle 1.8.9" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "TestSolution.sln" + } + } + } + ] + }, + { + "properties": { + "applicability": "Not Covered", + "fixedVersion": "No fix available" + }, + "ruleId": "CVE-2024-30171_BouncyCastle_1.8.9", + "ruleIndex": 1, + "level": "warning", + "message": { + "text": "[CVE-2024-30171] BouncyCastle 1.8.9", + "markdown": "[CVE-2024-30171] BouncyCastle 1.8.9" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "TestSolution.sln" + } + } + } + ] + } + ] + }, + { + "tool": { + "driver": { + "name": "JFrog Services scanner", + "rules": [ + { + "id": "CVE-2024-30172_BouncyCastle_1.8.9", + "name": "CVE-2024-30172-BouncyCastle-1.8.9", + "shortDescription": { + "text": "[CVE-2024-30172] BouncyCastle 1.8.9" + }, + "fullDescription": { + "text": "An issue was discovered in Bouncy Castle Java Cryptography APIs before 1.78. An Ed25519 verification code infinite loop can occur via a crafted signature and public key.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.3 | Not Covered | `BouncyCastle 1.8.9` | No fix available |" + }, + "help": { + "text": "An issue was discovered in Bouncy Castle Java Cryptography APIs before 1.78. An Ed25519 verification code infinite loop can occur via a crafted signature and public key.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.3 | Not Covered | `BouncyCastle 1.8.9` | No fix available |" + }, + "properties": { + "impactPaths": [ + [ + { + "name": "TestApp1", + "version": "" + }, + { + "name": "BouncyCastle", + "version": "1.8.9" + } + ] + ], + "security-severity": "5.3" + } + } + ] + } + }, + "results": [] + }, + { + "tool": { + "driver": { + "name": "JFrog Services scanner", + "rules": [ + { + "id": "CVE-2024-30172_BouncyCastle_1.8.9", + "name": "CVE-2024-30172-BouncyCastle-1.8.9", + "shortDescription": { + "text": "[CVE-2024-30172] BouncyCastle 1.8.9" + }, + "fullDescription": { + "text": "An issue was discovered in Bouncy Castle Java Cryptography APIs before 1.78. An Ed25519 verification code infinite loop can occur via a crafted signature and public key.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.3 | Not Covered | `BouncyCastle 1.8.9` | No fix available |" + }, + "help": { + "text": "An issue was discovered in Bouncy Castle Java Cryptography APIs before 1.78. An Ed25519 verification code infinite loop can occur via a crafted signature and public key.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.3 | Not Covered | `BouncyCastle 1.8.9` | No fix available |" + }, + "properties": { + "impactPaths": [ + [ + { + "name": "TestApp1", + "version": "" + }, + { + "name": "BouncyCastle", + "version": "1.8.9" + } + ] + ], + "security-severity": "5.3" + } + } + ] + } + }, + "results": [ + { + "properties": { + "applicability": "Not Applicable", + "fixedVersion": "[2.1.22]" + }, + "ruleId": "CVE-2020-1045_Microsoft.AspNetCore.Http_2.1.0", + "ruleIndex": 2, + "level": "error", + "message": { + "text": "[CVE-2020-1045] Microsoft.AspNetCore.Hosting 2.1.0", + "markdown": "[CVE-2020-1045] Microsoft.AspNetCore.Hosting 2.1.0" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "TestSolution.sln" + } + } + } + ] + } + ] + }, + { + "tool": { + "driver": { + "name": "JFrog Services scanner", + "rules": [ + { + "id": "CVE-2024-30172_BouncyCastle_1.8.9", + "name": "CVE-2024-30172-BouncyCastle-1.8.9", + "shortDescription": { + "text": "[CVE-2024-30172] BouncyCastle 1.8.9" + }, + "fullDescription": { + "text": "An issue was discovered in Bouncy Castle Java Cryptography APIs before 1.78. An Ed25519 verification code infinite loop can occur via a crafted signature and public key.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.3 | Not Covered | `BouncyCastle 1.8.9` | No fix available |" + }, + "help": { + "text": "An issue was discovered in Bouncy Castle Java Cryptography APIs before 1.78. An Ed25519 verification code infinite loop can occur via a crafted signature and public key.", + "markdown": "| Severity Score | Contextual Analysis | Direct Dependencies | Fixed Versions |\n| :---: | :---: | :---: | :---: |\n| 5.3 | Not Covered | `BouncyCastle 1.8.9` | No fix available |" + }, + "properties": { + "impactPaths": [ + [ + { + "name": "TestApp1", + "version": "" + }, + { + "name": "BouncyCastle", + "version": "1.8.9" + } + ] + ], + "security-severity": "5.3" + } + } + ] + } + } + } + ] +} \ No newline at end of file