From 408f83f15dcd5710f8af57afc382d08ea6fa2f0c Mon Sep 17 00:00:00 2001 From: san-zrl Date: Tue, 1 Sep 2026 16:31:14 +0200 Subject: [PATCH 1/5] Migrated to sonar-cryptography 1.7.0 and enabled csharp support Signed-off-by: san-zrl --- pom.xml | 2 +- .../pqca/indexing/csharp/CSharpBuildType.java | 26 +++++ .../indexing/csharp/CSharpIndexService.java | 72 ++++++++++++++ .../csharp/CSharpDetectionCollectionRule.java | 47 +++++++++ .../scanning/csharp/CSharpScannerService.java | 95 +++++++++++++++++++ .../pqca/scanning/go/GoScannerService.java | 41 +++----- .../pqca/indexing/CSharpIndexServiceTest.java | 52 ++++++++++ .../scanning/CSharpScannerServiceTest.java | 85 +++++++++++++++++ .../pqca/scanning/GoScannerServiceTest.java | 47 ++------- .../scanning/PythonScannerServiceTest.java | 16 ++-- .../dotnet/DotNetAESPropertyTestFile.cs | 19 ++++ .../csharp/dotnet/DotNetAESTestFile.cs | 14 +++ .../csharp/dotnet/DotNetDESTestFile.cs | 4 + .../csharp/dotnet/DotNetDSATestFile.cs | 4 + .../dotnet/DotNetECDiffieHellmanTestFile.cs | 4 + .../csharp/dotnet/DotNetECDsaTestFile.cs | 4 + .../csharp/dotnet/DotNetHMACTestFile.cs | 8 ++ .../csharp/dotnet/DotNetRC2TestFile.cs | 4 + .../csharp/dotnet/DotNetRSATestFile.cs | 4 + .../DotNetRfc2898DeriveBytesTestFile.cs | 6 ++ .../csharp/dotnet/DotNetSHATestFile.cs | 8 ++ .../csharp/dotnet/DotNetTripleDESTestFile.cs | 4 + .../simple/go.mod => csharp/module/module.cs} | 0 .../file.go => csharp/module/module.csproj} | 0 24 files changed, 490 insertions(+), 76 deletions(-) create mode 100644 src/main/java/org/pqca/indexing/csharp/CSharpBuildType.java create mode 100644 src/main/java/org/pqca/indexing/csharp/CSharpIndexService.java create mode 100644 src/main/java/org/pqca/scanning/csharp/CSharpDetectionCollectionRule.java create mode 100644 src/main/java/org/pqca/scanning/csharp/CSharpScannerService.java create mode 100644 src/test/java/org/pqca/indexing/CSharpIndexServiceTest.java create mode 100644 src/test/java/org/pqca/scanning/CSharpScannerServiceTest.java create mode 100644 src/test/testdata/csharp/dotnet/DotNetAESPropertyTestFile.cs create mode 100755 src/test/testdata/csharp/dotnet/DotNetAESTestFile.cs create mode 100755 src/test/testdata/csharp/dotnet/DotNetDESTestFile.cs create mode 100755 src/test/testdata/csharp/dotnet/DotNetDSATestFile.cs create mode 100755 src/test/testdata/csharp/dotnet/DotNetECDiffieHellmanTestFile.cs create mode 100755 src/test/testdata/csharp/dotnet/DotNetECDsaTestFile.cs create mode 100755 src/test/testdata/csharp/dotnet/DotNetHMACTestFile.cs create mode 100755 src/test/testdata/csharp/dotnet/DotNetRC2TestFile.cs create mode 100755 src/test/testdata/csharp/dotnet/DotNetRSATestFile.cs create mode 100755 src/test/testdata/csharp/dotnet/DotNetRfc2898DeriveBytesTestFile.cs create mode 100755 src/test/testdata/csharp/dotnet/DotNetSHATestFile.cs create mode 100755 src/test/testdata/csharp/dotnet/DotNetTripleDESTestFile.cs rename src/test/testdata/{go/simple/go.mod => csharp/module/module.cs} (100%) rename src/test/testdata/{go/simple/module1/file.go => csharp/module/module.csproj} (100%) diff --git a/pom.xml b/pom.xml index e919b33..e8b7ddb 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ 21 UTF-8 - 1.6.1 + 1.7.0 13.11.0.4459 25.8.0.112029 diff --git a/src/main/java/org/pqca/indexing/csharp/CSharpBuildType.java b/src/main/java/org/pqca/indexing/csharp/CSharpBuildType.java new file mode 100644 index 0000000..0b31547 --- /dev/null +++ b/src/main/java/org/pqca/indexing/csharp/CSharpBuildType.java @@ -0,0 +1,26 @@ +/* + * CBOMkit-lib + * Copyright (C) 2025 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pqca.indexing.csharp; + +import org.pqca.indexing.IBuildType; + +public enum CSharpBuildType implements IBuildType { + CSPROJ +} diff --git a/src/main/java/org/pqca/indexing/csharp/CSharpIndexService.java b/src/main/java/org/pqca/indexing/csharp/CSharpIndexService.java new file mode 100644 index 0000000..541aebf --- /dev/null +++ b/src/main/java/org/pqca/indexing/csharp/CSharpIndexService.java @@ -0,0 +1,72 @@ +/* + * CBOMkit-lib + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pqca.indexing.csharp; + +import jakarta.annotation.Nonnull; +import java.io.File; +import java.util.List; +import javax.annotation.Nullable; +import org.pqca.indexing.IBuildType; +import org.pqca.indexing.IndexingService; +import org.pqca.progress.IProgressDispatcher; + +public final class CSharpIndexService extends IndexingService { + + public CSharpIndexService(@Nonnull File baseDirectory) { + this(null, baseDirectory); + } + + public CSharpIndexService( + @Nullable IProgressDispatcher progressDispatcher, @Nonnull File baseDirectory) { + super(progressDispatcher, baseDirectory, "cs", ".cs"); + this.setExcludePatterns(null); + } + + public void setExcludePatterns(@Nullable List patterns) { + if (patterns == null) { + super.setExcludePatterns(List.of("tests/", "Tests$")); + } else { + super.setExcludePatterns(patterns); + } + } + + @Override + public boolean isModule(@Nonnull File directory) { + if (!directory.isDirectory()) { + return false; + } + + final File file = new File(directory, directory.getName() + ".csproj"); + return file.exists() && file.isFile(); + } + + @Override + @Nullable public IBuildType getMainBuildTypeFromModuleDirectory(@Nonnull File directory) { + if (!directory.isDirectory()) { + return null; + } + // csproj + final File csProjFile = new File(directory, directory.getName() + ".csproj"); + if (csProjFile.exists() && csProjFile.isFile()) { + return CSharpBuildType.CSPROJ; + } + return null; + } +} diff --git a/src/main/java/org/pqca/scanning/csharp/CSharpDetectionCollectionRule.java b/src/main/java/org/pqca/scanning/csharp/CSharpDetectionCollectionRule.java new file mode 100644 index 0000000..532a682 --- /dev/null +++ b/src/main/java/org/pqca/scanning/csharp/CSharpDetectionCollectionRule.java @@ -0,0 +1,47 @@ +/* + * CBOMkit-lib + * Copyright (C) 2024 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pqca.scanning.csharp; + +import com.ibm.engine.detection.Finding; +import com.ibm.engine.language.csharp.CSharpCheck; +import com.ibm.engine.language.csharp.CSharpScanContext; +import com.ibm.engine.language.csharp.CSharpSymbol; +import com.ibm.engine.language.csharp.tree.CSharpTree; +import com.ibm.mapper.model.INode; +import com.ibm.plugin.rules.CSharpInventoryRule; +import jakarta.annotation.Nonnull; +import java.util.List; +import java.util.function.Consumer; + +public class CSharpDetectionCollectionRule extends CSharpInventoryRule { + private final Consumer> handler; + + public CSharpDetectionCollectionRule(@Nonnull Consumer> findingConsumer) { + this.handler = findingConsumer; + } + + @Override + public void update( + @Nonnull Finding finding) { + super.update(finding); + final List nodes = csharpTranslationProcess.initiate(finding.detectionStore()); + handler.accept(nodes); + } +} diff --git a/src/main/java/org/pqca/scanning/csharp/CSharpScannerService.java b/src/main/java/org/pqca/scanning/csharp/CSharpScannerService.java new file mode 100644 index 0000000..7a5d9d8 --- /dev/null +++ b/src/main/java/org/pqca/scanning/csharp/CSharpScannerService.java @@ -0,0 +1,95 @@ +/* + * CBOMkit-lib + * Copyright (C) 2026 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pqca.scanning.csharp; + +import com.ibm.engine.language.csharp.CSharpCheck; +import com.ibm.plugin.CryptoCSharpSensor; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import java.io.File; +import java.util.Collection; +import java.util.List; +import org.pqca.errors.ClientDisconnected; +import org.pqca.indexing.ProjectModule; +import org.pqca.progress.IProgressDispatcher; +import org.pqca.progress.ProgressMessage; +import org.pqca.progress.ProgressMessageType; +import org.pqca.scanning.CBOM; +import org.pqca.scanning.ScanResultDTO; +import org.pqca.scanning.ScannerService; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.internal.SensorContextTester; + +public final class CSharpScannerService extends ScannerService { + + public CSharpScannerService(@Nonnull File projectDirectory) { + this(null, projectDirectory); + } + + public CSharpScannerService( + @Nullable IProgressDispatcher progressDispatcher, @Nonnull File projectDirectory) { + super(progressDispatcher, projectDirectory); + } + + @Override + public @Nonnull ScanResultDTO scan(@Nonnull List index) + throws ClientDisconnected { + LOGGER.info("Start scanning {} C# projects", index.size()); + + long scanTimeStart = System.currentTimeMillis(); + int counter = 1; + int numberOfScannedLines = 0; + int numberOfScannedFiles = 0; + + CSharpCheck visitor = new CSharpDetectionCollectionRule(this); + Collection checks = List.of(visitor); + final SensorContextTester sensorContext = SensorContextTester.create(projectDirectory); + // C# scanner (CryptoCSharpSensor) reads files from context + index.forEach(project -> project.inputFileList().forEach(sensorContext.fileSystem()::add)); + + for (ProjectModule project : index) { + numberOfScannedFiles += project.inputFileList().size(); + numberOfScannedLines += + project.inputFileList().stream().mapToInt(InputFile::lines).sum(); + + final String projectStr = + project.identifier() + " (" + counter + "/" + index.size() + ")"; + if (this.progressDispatcher != null) { + this.progressDispatcher.send( + new ProgressMessage( + ProgressMessageType.LABEL, "Scanning C# project " + projectStr)); + } + LOGGER.info("Scanning C# project {}", projectStr); + + CryptoCSharpSensor.execute((SensorContext) sensorContext, checks); + + counter += 1; + } + LOGGER.info("Scanned {} C# projects", index.size()); + + return new ScanResultDTO( + scanTimeStart, + System.currentTimeMillis(), + numberOfScannedLines, + numberOfScannedFiles, + this.getBOM().map(CBOM::new).orElse(null)); + } +} diff --git a/src/main/java/org/pqca/scanning/go/GoScannerService.java b/src/main/java/org/pqca/scanning/go/GoScannerService.java index 84dca7e..e19676e 100644 --- a/src/main/java/org/pqca/scanning/go/GoScannerService.java +++ b/src/main/java/org/pqca/scanning/go/GoScannerService.java @@ -24,7 +24,6 @@ import jakarta.annotation.Nullable; import java.io.File; import java.io.IOException; -import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.FileUtils; @@ -39,6 +38,7 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.impl.utils.DefaultTempFolder; import org.sonar.api.rule.RuleKey; import org.sonar.go.converter.GoConverter; import org.sonar.go.plugin.GoChecks; @@ -65,8 +65,10 @@ public GoScannerService( int numberOfScannedLines = 0; int numberOfScannedFiles = 0; - try (GoTempFolder goTempFolder = GoTempFolder.create()) { - GoConverter goConverter = new GoConverter(goTempFolder.file()); + File goTempFolder = new DefaultTempFolder(this.projectDirectory).newDir(); + + try { + GoConverter goConverter = new GoConverter(goTempFolder); GoCheck visitor = new GoDetectionCollectionRule(this); GoChecks checks = new GoRuleChecks(visitor); final SensorContextTester sensorContext = SensorContextTester.create(projectDirectory); @@ -95,8 +97,12 @@ public GoScannerService( counter += 1; } LOGGER.info("Scanned {} go projects", index.size()); - } catch (IOException e) { - LOGGER.error("Failed to create temp dir for go scanner", e); + } finally { + try { + FileUtils.deleteDirectory(goTempFolder); + } catch (IOException e) { + LOGGER.error("Failed to delete temp dir {}", goTempFolder); + } } return new ScanResultDTO( @@ -107,31 +113,6 @@ public GoScannerService( this.getBOM().map(CBOM::new).orElse(null)); } - private static final class GoTempFolder implements AutoCloseable { - private final File directory; - - private GoTempFolder(File directory) { - this.directory = directory; - } - - private static GoTempFolder create() throws IOException { - return new GoTempFolder(Files.createTempDirectory("cbomkit-go-").toFile()); - } - - private File file() { - return this.directory; - } - - @Override - public void close() { - try { - FileUtils.deleteDirectory(this.directory); - } catch (IOException e) { - LOGGER.error("Failed to delete temp dir {}", this.directory, e); - } - } - } - private class GoRuleChecks extends GoChecks { private final List checks = new ArrayList<>(); diff --git a/src/test/java/org/pqca/indexing/CSharpIndexServiceTest.java b/src/test/java/org/pqca/indexing/CSharpIndexServiceTest.java new file mode 100644 index 0000000..df57b28 --- /dev/null +++ b/src/test/java/org/pqca/indexing/CSharpIndexServiceTest.java @@ -0,0 +1,52 @@ +/* + * CBOMkit-lib + * Copyright (C) 2025 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pqca.indexing; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.pqca.errors.ClientDisconnected; +import org.pqca.indexing.csharp.CSharpIndexService; + +class CSharpIndexServiceTest { + @Test + void testDefaultExclusion() throws ClientDisconnected { + final CSharpIndexService csharpIndexService = + new CSharpIndexService(new File("src/test/testdata/csharp/dotnet")); + final List projectModules = csharpIndexService.index(null); + assertThat(projectModules).hasSize(1); + assertThat(projectModules.getFirst().identifier()).isEqualTo(""); + assertThat(projectModules.getFirst().inputFileList()).hasSize(12); + } + + @Test + void testExclusion() throws ClientDisconnected { + final CSharpIndexService csharpIndexService = + new CSharpIndexService(new File("src/test/testdata/csharp/dotnet")); + csharpIndexService.setExcludePatterns(List.of("AES")); + final List projectModules = csharpIndexService.index(null); + assertThat(projectModules).hasSize(1); + assertThat(projectModules.getFirst().identifier()).isEqualTo(""); + assertThat(projectModules.getFirst().inputFileList()).hasSize(10); + } +} diff --git a/src/test/java/org/pqca/scanning/CSharpScannerServiceTest.java b/src/test/java/org/pqca/scanning/CSharpScannerServiceTest.java new file mode 100644 index 0000000..51b8668 --- /dev/null +++ b/src/test/java/org/pqca/scanning/CSharpScannerServiceTest.java @@ -0,0 +1,85 @@ +/* + * CBOMkit-lib + * Copyright (C) 2025 PQCA + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pqca.scanning; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.pqca.errors.ClientDisconnected; +import org.pqca.indexing.ProjectModule; +import org.pqca.indexing.csharp.CSharpIndexService; +import org.pqca.scanning.csharp.CSharpScannerService; +import org.pqca.utils.AssertableCBOM; + +class CSharpScannerServiceTest { + + private static final String CSHARP_TEST_BASE_DIR = "src/test/testdata/csharp/dotnet"; + + @Test + void test() throws ClientDisconnected { + // indexing + final File projectDirectory = new File(CSHARP_TEST_BASE_DIR); + final CSharpIndexService cSharpIndexService = new CSharpIndexService(projectDirectory); + final List cSharpModules = cSharpIndexService.index(null); + assertThat(cSharpModules).hasSize(1); + final ProjectModule projectModule = cSharpModules.getFirst(); + assertThat(projectModule.inputFileList()).hasSize(12); + // scanning + final CSharpScannerService cSharpScannerService = + new CSharpScannerService(projectDirectory); + ScanResultDTO scanResult = cSharpScannerService.scan(cSharpModules); + + // check - 21 unique cryptographic assets with 27 total occurrences + AssertableCBOM assertableCBOM = new AssertableCBOM(scanResult.cbom()); + assertThat(scanResult.cbom().cycloneDXbom().getComponents()).hasSize(21); + assertableCBOM.hasNumberOfDetections(27); + + assertThat( + assertableCBOM.hasDetectionWithNameAt( + "SHA-256", CSHARP_TEST_BASE_DIR + "/DotNetSHATestFile.cs", 4)) + .isTrue(); + + assertThat( + assertableCBOM.hasDetectionWithNameAt( + "AES-256-CBC-PKCS7", + CSHARP_TEST_BASE_DIR + "/DotNetAESPropertyTestFile.cs", + 7)) + .isTrue(); + + assertThat( + assertableCBOM.hasDetectionWithNameAt( + "HMAC-SHA-256", CSHARP_TEST_BASE_DIR + "/DotNetHMACTestFile.cs", 4)) + .isTrue(); + + assertThat( + assertableCBOM.hasDetectionWithNameAt( + "RSA", CSHARP_TEST_BASE_DIR + "/DotNetRSATestFile.cs", 3)) + .isTrue(); + + assertThat( + assertableCBOM.hasDetectionWithNameAt( + "PBKDF2", + CSHARP_TEST_BASE_DIR + "/DotNetRfc2898DeriveBytesTestFile.cs", + 4)) + .isTrue(); + } +} diff --git a/src/test/java/org/pqca/scanning/GoScannerServiceTest.java b/src/test/java/org/pqca/scanning/GoScannerServiceTest.java index c9610e4..7e5e257 100644 --- a/src/test/java/org/pqca/scanning/GoScannerServiceTest.java +++ b/src/test/java/org/pqca/scanning/GoScannerServiceTest.java @@ -22,14 +22,9 @@ import static org.assertj.core.api.Assertions.assertThat; import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.pqca.errors.CBOMSerializationFailed; import org.pqca.errors.ClientDisconnected; import org.pqca.indexing.ProjectModule; import org.pqca.indexing.go.GoIndexService; @@ -38,12 +33,12 @@ class GoScannerServiceTest { + private static final String GO_TEST_BASE_DIR = "src/test/testdata/go/gocrypto"; + @Test - void test() throws ClientDisconnected, IOException { + void test() throws ClientDisconnected, CBOMSerializationFailed { // indexing - final File projectDirectory = new File("src/test/testdata/go/gocrypto"); - final Set projectDirectoryEntriesBeforeScan = - listRelativePaths(projectDirectory.toPath()); + final File projectDirectory = new File(GO_TEST_BASE_DIR); final GoIndexService goIndexService = new GoIndexService(projectDirectory); final List goModules = goIndexService.index(null); assertThat(goModules).hasSize(1); @@ -52,7 +47,6 @@ void test() throws ClientDisconnected, IOException { // scanning final GoScannerService goScannerService = new GoScannerService(projectDirectory); ScanResultDTO scanResult = goScannerService.scan(goModules); - // check - verify cryptographic assets are detected AssertableCBOM assertableCBOM = new AssertableCBOM(scanResult.cbom()); assertThat(scanResult.cbom().cycloneDXbom().getComponents()).hasSize(28); @@ -60,48 +54,27 @@ void test() throws ClientDisconnected, IOException { assertThat( assertableCBOM.hasDetectionWithNameAt( - "SHA-256", - "src/test/testdata/go/gocrypto/GoCryptoSHA256TestFile.go", - 10)) + "SHA-256", GO_TEST_BASE_DIR + "/GoCryptoSHA256TestFile.go", 10)) .isTrue(); assertThat( assertableCBOM.hasDetectionWithNameAt( - "AES-GCM", - "src/test/testdata/go/gocrypto/GoCryptoAESTestFile.go", - 18)) + "AES-GCM", GO_TEST_BASE_DIR + "/GoCryptoAESTestFile.go", 18)) .isTrue(); assertThat( assertableCBOM.hasDetectionWithNameAt( - "RSA-2048", - "src/test/testdata/go/gocrypto/GoCryptoRSATestFile.go", - 10)) + "RSA-2048", GO_TEST_BASE_DIR + "/GoCryptoRSATestFile.go", 10)) .isTrue(); assertThat( assertableCBOM.hasDetectionWithNameAt( - "HMAC-SHA-256", - "src/test/testdata/go/gocrypto/GoCryptoHMACTestFile.go", - 11)) + "HMAC-SHA-256", GO_TEST_BASE_DIR + "/GoCryptoHMACTestFile.go", 11)) .isTrue(); assertThat( assertableCBOM.hasDetectionWithNameAt( - "PBKDF2-SHA-256", - "src/test/testdata/go/gocrypto/GoCryptoPBKDF2TestFile.go", - 15)) + "PBKDF2", GO_TEST_BASE_DIR + "/GoCryptoPBKDF2TestFile.go", 15)) .isTrue(); - - assertThat(listRelativePaths(projectDirectory.toPath())) - .isEqualTo(projectDirectoryEntriesBeforeScan); - } - - private static Set listRelativePaths(Path projectDirectory) throws IOException { - try (Stream paths = Files.walk(projectDirectory)) { - return paths.map(projectDirectory::relativize) - .map(Path::toString) - .collect(Collectors.toSet()); - } } } diff --git a/src/test/java/org/pqca/scanning/PythonScannerServiceTest.java b/src/test/java/org/pqca/scanning/PythonScannerServiceTest.java index 718ede4..d78eb37 100644 --- a/src/test/java/org/pqca/scanning/PythonScannerServiceTest.java +++ b/src/test/java/org/pqca/scanning/PythonScannerServiceTest.java @@ -32,10 +32,12 @@ class PythonScannerServiceTest { + private static final String PY_TEST_BASE_DIR = "src/test/testdata/python/pyca"; + @Test void test() throws ClientDisconnected { // indexing - final File projectDirectory = new File("src/test/testdata/python/pyca"); + final File projectDirectory = new File(PY_TEST_BASE_DIR); final PythonIndexService pythonIndexService = new PythonIndexService(projectDirectory); final List projectModules = pythonIndexService.index(null); assertThat(projectModules).hasSize(1); @@ -52,29 +54,27 @@ void test() throws ClientDisconnected { assertThat( assertableCBOM.hasDetectionWithNameAt( - "SHA-256", "src/test/testdata/python/pyca/generate_key.py", 4)) + "SHA-256", PY_TEST_BASE_DIR + "/generate_key.py", 4)) .isTrue(); assertThat( assertableCBOM.hasDetectionWithNameAt( - "AES-128-CBC-PKCS7", - "src/test/testdata/python/pyca/generate_key.py", - 4)) + "AES-128-CBC-PKCS7", PY_TEST_BASE_DIR + "/generate_key.py", 4)) .isTrue(); assertThat( assertableCBOM.hasDetectionWithNameAt( - "HMAC-SHA-256", "src/test/testdata/python/pyca/generate_key.py", 4)) + "HMAC-SHA-256", PY_TEST_BASE_DIR + "/generate_key.py", 4)) .isTrue(); assertThat( assertableCBOM.hasDetectionWithNameAt( - "Fernet", "src/test/testdata/python/pyca/generate_key.py", 4)) + "Fernet", PY_TEST_BASE_DIR + "/generate_key.py", 4)) .isTrue(); assertThat( assertableCBOM.hasDetectionWithNameAt( - "secret-key", "src/test/testdata/python/pyca/generate_key.py", 4)) + "secret-key", PY_TEST_BASE_DIR + "/generate_key.py", 4)) .isTrue(); } } diff --git a/src/test/testdata/csharp/dotnet/DotNetAESPropertyTestFile.cs b/src/test/testdata/csharp/dotnet/DotNetAESPropertyTestFile.cs new file mode 100644 index 0000000..a97e1ad --- /dev/null +++ b/src/test/testdata/csharp/dotnet/DotNetAESPropertyTestFile.cs @@ -0,0 +1,19 @@ +using System.Security.Cryptography; + +public class DotNetAESPropertyTest +{ + public void TestAesWithProperties() + { + var aes = Aes.Create(); + aes.Mode = CipherMode.CBC; + aes.KeySize = 256; + aes.Padding = PaddingMode.PKCS7; + } + + public void TestAesManagedWithMode() + { + var aes = new AesManaged(); + aes.Mode = CipherMode.ECB; + aes.KeySize = 128; + } +} diff --git a/src/test/testdata/csharp/dotnet/DotNetAESTestFile.cs b/src/test/testdata/csharp/dotnet/DotNetAESTestFile.cs new file mode 100755 index 0000000..6f24227 --- /dev/null +++ b/src/test/testdata/csharp/dotnet/DotNetAESTestFile.cs @@ -0,0 +1,14 @@ +using System.Security.Cryptography; + +public class DotNetAESTest +{ + public void TestAesCreate() + { + var aes = Aes.Create(); // Noncompliant + } + + public void TestAesManaged() + { + var aes = new AesManaged(); // Noncompliant + } +} diff --git a/src/test/testdata/csharp/dotnet/DotNetDESTestFile.cs b/src/test/testdata/csharp/dotnet/DotNetDESTestFile.cs new file mode 100755 index 0000000..7e53f08 --- /dev/null +++ b/src/test/testdata/csharp/dotnet/DotNetDESTestFile.cs @@ -0,0 +1,4 @@ +using System.Security.Cryptography; +public class DotNetDESTest { + public void TestDesCreate() { var des = DES.Create(); } // Noncompliant +} diff --git a/src/test/testdata/csharp/dotnet/DotNetDSATestFile.cs b/src/test/testdata/csharp/dotnet/DotNetDSATestFile.cs new file mode 100755 index 0000000..63e24c9 --- /dev/null +++ b/src/test/testdata/csharp/dotnet/DotNetDSATestFile.cs @@ -0,0 +1,4 @@ +using System.Security.Cryptography; +public class DotNetDSATest { + public void TestDsaCreate() { var dsa = DSA.Create(); } // Noncompliant +} diff --git a/src/test/testdata/csharp/dotnet/DotNetECDiffieHellmanTestFile.cs b/src/test/testdata/csharp/dotnet/DotNetECDiffieHellmanTestFile.cs new file mode 100755 index 0000000..2c91853 --- /dev/null +++ b/src/test/testdata/csharp/dotnet/DotNetECDiffieHellmanTestFile.cs @@ -0,0 +1,4 @@ +using System.Security.Cryptography; +public class DotNetECDiffieHellmanTest { + public void TestECDHCreate() { var ecdh = ECDiffieHellman.Create(); } // Noncompliant +} diff --git a/src/test/testdata/csharp/dotnet/DotNetECDsaTestFile.cs b/src/test/testdata/csharp/dotnet/DotNetECDsaTestFile.cs new file mode 100755 index 0000000..addadc0 --- /dev/null +++ b/src/test/testdata/csharp/dotnet/DotNetECDsaTestFile.cs @@ -0,0 +1,4 @@ +using System.Security.Cryptography; +public class DotNetECDsaTest { + public void TestECDsaCreate() { var ecdsa = ECDsa.Create(); } // Noncompliant +} diff --git a/src/test/testdata/csharp/dotnet/DotNetHMACTestFile.cs b/src/test/testdata/csharp/dotnet/DotNetHMACTestFile.cs new file mode 100755 index 0000000..b652359 --- /dev/null +++ b/src/test/testdata/csharp/dotnet/DotNetHMACTestFile.cs @@ -0,0 +1,8 @@ +using System.Security.Cryptography; +public class DotNetHMACTest { + public void TestHmacSha1() { var h = new HMACSHA1(); } // Noncompliant + public void TestHmacSha256() { var h = new HMACSHA256(); } // Noncompliant + public void TestHmacSha384() { var h = new HMACSHA384(); } // Noncompliant + public void TestHmacSha512() { var h = new HMACSHA512(); } // Noncompliant + public void TestHmacMd5() { var h = new HMACMD5(); } // Noncompliant +} diff --git a/src/test/testdata/csharp/dotnet/DotNetRC2TestFile.cs b/src/test/testdata/csharp/dotnet/DotNetRC2TestFile.cs new file mode 100755 index 0000000..ac73952 --- /dev/null +++ b/src/test/testdata/csharp/dotnet/DotNetRC2TestFile.cs @@ -0,0 +1,4 @@ +using System.Security.Cryptography; +public class DotNetRC2Test { + public void TestRc2Create() { var rc2 = RC2.Create(); } // Noncompliant +} diff --git a/src/test/testdata/csharp/dotnet/DotNetRSATestFile.cs b/src/test/testdata/csharp/dotnet/DotNetRSATestFile.cs new file mode 100755 index 0000000..1478482 --- /dev/null +++ b/src/test/testdata/csharp/dotnet/DotNetRSATestFile.cs @@ -0,0 +1,4 @@ +using System.Security.Cryptography; +public class DotNetRSATest { + public void TestRsaCreate() { var rsa = RSA.Create(); } // Noncompliant +} diff --git a/src/test/testdata/csharp/dotnet/DotNetRfc2898DeriveBytesTestFile.cs b/src/test/testdata/csharp/dotnet/DotNetRfc2898DeriveBytesTestFile.cs new file mode 100755 index 0000000..eefd474 --- /dev/null +++ b/src/test/testdata/csharp/dotnet/DotNetRfc2898DeriveBytesTestFile.cs @@ -0,0 +1,6 @@ +using System.Security.Cryptography; +public class DotNetRfc2898DeriveBytesTest { + public void TestPbkdf2() { + var kdf = new Rfc2898DeriveBytes("password", new byte[16], 10000, HashAlgorithmName.SHA256); // Noncompliant + } +} diff --git a/src/test/testdata/csharp/dotnet/DotNetSHATestFile.cs b/src/test/testdata/csharp/dotnet/DotNetSHATestFile.cs new file mode 100755 index 0000000..9ab3b53 --- /dev/null +++ b/src/test/testdata/csharp/dotnet/DotNetSHATestFile.cs @@ -0,0 +1,8 @@ +using System.Security.Cryptography; +public class DotNetSHATest { + public void TestSha1Create() { var h = SHA1.Create(); } // Noncompliant + public void TestSha256Create() { var h = SHA256.Create(); } // Noncompliant + public void TestSha384Create() { var h = SHA384.Create(); } // Noncompliant + public void TestSha512Create() { var h = SHA512.Create(); } // Noncompliant + public void TestMd5Create() { var h = MD5.Create(); } // Noncompliant +} diff --git a/src/test/testdata/csharp/dotnet/DotNetTripleDESTestFile.cs b/src/test/testdata/csharp/dotnet/DotNetTripleDESTestFile.cs new file mode 100755 index 0000000..9c85d16 --- /dev/null +++ b/src/test/testdata/csharp/dotnet/DotNetTripleDESTestFile.cs @@ -0,0 +1,4 @@ +using System.Security.Cryptography; +public class DotNetTripleDESTest { + public void TestTripleDesCreate() { var des = TripleDES.Create(); } // Noncompliant +} diff --git a/src/test/testdata/go/simple/go.mod b/src/test/testdata/csharp/module/module.cs similarity index 100% rename from src/test/testdata/go/simple/go.mod rename to src/test/testdata/csharp/module/module.cs diff --git a/src/test/testdata/go/simple/module1/file.go b/src/test/testdata/csharp/module/module.csproj similarity index 100% rename from src/test/testdata/go/simple/module1/file.go rename to src/test/testdata/csharp/module/module.csproj From bf1dba23d30255814f96817eb58c4266a9332ff9 Mon Sep 17 00:00:00 2001 From: san-zrl Date: Wed, 2 Sep 2026 09:45:46 +0200 Subject: [PATCH 2/5] Go scanner: temp folder in java temp; test workdir unchanged after scan Signed-off-by: san-zrl --- .../pqca/scanning/go/GoScannerService.java | 41 ++++++++++++++----- .../pqca/scanning/GoScannerServiceTest.java | 26 +++++++++++- 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/pqca/scanning/go/GoScannerService.java b/src/main/java/org/pqca/scanning/go/GoScannerService.java index e19676e..84dca7e 100644 --- a/src/main/java/org/pqca/scanning/go/GoScannerService.java +++ b/src/main/java/org/pqca/scanning/go/GoScannerService.java @@ -24,6 +24,7 @@ import jakarta.annotation.Nullable; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.FileUtils; @@ -38,7 +39,6 @@ import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.internal.SensorContextTester; -import org.sonar.api.impl.utils.DefaultTempFolder; import org.sonar.api.rule.RuleKey; import org.sonar.go.converter.GoConverter; import org.sonar.go.plugin.GoChecks; @@ -65,10 +65,8 @@ public GoScannerService( int numberOfScannedLines = 0; int numberOfScannedFiles = 0; - File goTempFolder = new DefaultTempFolder(this.projectDirectory).newDir(); - - try { - GoConverter goConverter = new GoConverter(goTempFolder); + try (GoTempFolder goTempFolder = GoTempFolder.create()) { + GoConverter goConverter = new GoConverter(goTempFolder.file()); GoCheck visitor = new GoDetectionCollectionRule(this); GoChecks checks = new GoRuleChecks(visitor); final SensorContextTester sensorContext = SensorContextTester.create(projectDirectory); @@ -97,12 +95,8 @@ public GoScannerService( counter += 1; } LOGGER.info("Scanned {} go projects", index.size()); - } finally { - try { - FileUtils.deleteDirectory(goTempFolder); - } catch (IOException e) { - LOGGER.error("Failed to delete temp dir {}", goTempFolder); - } + } catch (IOException e) { + LOGGER.error("Failed to create temp dir for go scanner", e); } return new ScanResultDTO( @@ -113,6 +107,31 @@ public GoScannerService( this.getBOM().map(CBOM::new).orElse(null)); } + private static final class GoTempFolder implements AutoCloseable { + private final File directory; + + private GoTempFolder(File directory) { + this.directory = directory; + } + + private static GoTempFolder create() throws IOException { + return new GoTempFolder(Files.createTempDirectory("cbomkit-go-").toFile()); + } + + private File file() { + return this.directory; + } + + @Override + public void close() { + try { + FileUtils.deleteDirectory(this.directory); + } catch (IOException e) { + LOGGER.error("Failed to delete temp dir {}", this.directory, e); + } + } + } + private class GoRuleChecks extends GoChecks { private final List checks = new ArrayList<>(); diff --git a/src/test/java/org/pqca/scanning/GoScannerServiceTest.java b/src/test/java/org/pqca/scanning/GoScannerServiceTest.java index 7e5e257..6c313dd 100644 --- a/src/test/java/org/pqca/scanning/GoScannerServiceTest.java +++ b/src/test/java/org/pqca/scanning/GoScannerServiceTest.java @@ -22,9 +22,16 @@ import static org.assertj.core.api.Assertions.assertThat; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import org.junit.jupiter.api.Test; -import org.pqca.errors.CBOMSerializationFailed; + import org.pqca.errors.ClientDisconnected; import org.pqca.indexing.ProjectModule; import org.pqca.indexing.go.GoIndexService; @@ -36,17 +43,21 @@ class GoScannerServiceTest { private static final String GO_TEST_BASE_DIR = "src/test/testdata/go/gocrypto"; @Test - void test() throws ClientDisconnected, CBOMSerializationFailed { + void test() throws ClientDisconnected, IOException { // indexing final File projectDirectory = new File(GO_TEST_BASE_DIR); + final Set projectDirectoryEntriesBeforeScan = + listRelativePaths(projectDirectory.toPath()); final GoIndexService goIndexService = new GoIndexService(projectDirectory); final List goModules = goIndexService.index(null); assertThat(goModules).hasSize(1); final ProjectModule projectModule = goModules.getFirst(); assertThat(projectModule.inputFileList()).isNotEmpty(); + // scanning final GoScannerService goScannerService = new GoScannerService(projectDirectory); ScanResultDTO scanResult = goScannerService.scan(goModules); + // check - verify cryptographic assets are detected AssertableCBOM assertableCBOM = new AssertableCBOM(scanResult.cbom()); assertThat(scanResult.cbom().cycloneDXbom().getComponents()).hasSize(28); @@ -76,5 +87,16 @@ void test() throws ClientDisconnected, CBOMSerializationFailed { assertableCBOM.hasDetectionWithNameAt( "PBKDF2", GO_TEST_BASE_DIR + "/GoCryptoPBKDF2TestFile.go", 15)) .isTrue(); + + assertThat(listRelativePaths(projectDirectory.toPath())) + .isEqualTo(projectDirectoryEntriesBeforeScan); + } + + private static Set listRelativePaths(Path projectDirectory) throws IOException { + try (Stream paths = Files.walk(projectDirectory)) { + return paths.map(projectDirectory::relativize) + .map(Path::toString) + .collect(Collectors.toSet()); + } } } From 7d7448258e3b9480843a37c1bf11ceb7b018f540 Mon Sep 17 00:00:00 2001 From: san-zrl Date: Wed, 2 Sep 2026 10:02:38 +0200 Subject: [PATCH 3/5] Enable tests in IDE Signed-off-by: san-zrl --- pom.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pom.xml b/pom.xml index e8b7ddb..92e4afc 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,21 @@ junit-jupiter-api test + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.junit.platform + junit-platform-launcher + test + org.assertj assertj-core From 1226a853cccf0bfe017a9e255ad80822cca9650e Mon Sep 17 00:00:00 2001 From: san-zrl Date: Wed, 2 Sep 2026 10:11:54 +0200 Subject: [PATCH 4/5] add csharp language enum Signed-off-by: san-zrl --- src/main/java/org/pqca/scanning/Language.java | 3 ++- src/test/java/org/pqca/scanning/GoScannerServiceTest.java | 4 +--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/pqca/scanning/Language.java b/src/main/java/org/pqca/scanning/Language.java index e335af3..84dc8cb 100644 --- a/src/main/java/org/pqca/scanning/Language.java +++ b/src/main/java/org/pqca/scanning/Language.java @@ -22,5 +22,6 @@ public enum Language { JAVA, PYTHON, - GO + GO, + CSHARP } diff --git a/src/test/java/org/pqca/scanning/GoScannerServiceTest.java b/src/test/java/org/pqca/scanning/GoScannerServiceTest.java index 6c313dd..95cdf23 100644 --- a/src/test/java/org/pqca/scanning/GoScannerServiceTest.java +++ b/src/test/java/org/pqca/scanning/GoScannerServiceTest.java @@ -29,9 +29,7 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; - import org.junit.jupiter.api.Test; - import org.pqca.errors.ClientDisconnected; import org.pqca.indexing.ProjectModule; import org.pqca.indexing.go.GoIndexService; @@ -87,7 +85,7 @@ void test() throws ClientDisconnected, IOException { assertableCBOM.hasDetectionWithNameAt( "PBKDF2", GO_TEST_BASE_DIR + "/GoCryptoPBKDF2TestFile.go", 15)) .isTrue(); - + assertThat(listRelativePaths(projectDirectory.toPath())) .isEqualTo(projectDirectoryEntriesBeforeScan); } From a5ba6e3e26be2dfad2e9259c51c365099812e4e2 Mon Sep 17 00:00:00 2001 From: san-zrl Date: Wed, 2 Sep 2026 15:28:59 +0200 Subject: [PATCH 5/5] corrected exlusion pattern defaults to hide test files Signed-off-by: san-zrl --- .../java/org/pqca/indexing/csharp/CSharpIndexService.java | 2 +- src/main/java/org/pqca/indexing/go/GoIndexService.java | 2 +- src/main/java/org/pqca/indexing/java/JavaIndexService.java | 6 +++++- .../java/org/pqca/indexing/python/PythonIndexService.java | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/pqca/indexing/csharp/CSharpIndexService.java b/src/main/java/org/pqca/indexing/csharp/CSharpIndexService.java index 541aebf..7e6743b 100644 --- a/src/main/java/org/pqca/indexing/csharp/CSharpIndexService.java +++ b/src/main/java/org/pqca/indexing/csharp/CSharpIndexService.java @@ -41,7 +41,7 @@ public CSharpIndexService( public void setExcludePatterns(@Nullable List patterns) { if (patterns == null) { - super.setExcludePatterns(List.of("tests/", "Tests$")); + super.setExcludePatterns(List.of("test/", "tests/", "\\.Tests/", "Tests\\.cs$")); } else { super.setExcludePatterns(patterns); } diff --git a/src/main/java/org/pqca/indexing/go/GoIndexService.java b/src/main/java/org/pqca/indexing/go/GoIndexService.java index 9c23573..4b3bf78 100644 --- a/src/main/java/org/pqca/indexing/go/GoIndexService.java +++ b/src/main/java/org/pqca/indexing/go/GoIndexService.java @@ -41,7 +41,7 @@ public GoIndexService( public void setExcludePatterns(@Nullable List patterns) { if (patterns == null) { - super.setExcludePatterns(List.of("test/", "_test.go$")); + super.setExcludePatterns(List.of("test/", "_test\\.go$")); } else { super.setExcludePatterns(patterns); } diff --git a/src/main/java/org/pqca/indexing/java/JavaIndexService.java b/src/main/java/org/pqca/indexing/java/JavaIndexService.java index 1e38ccb..26b0bf1 100644 --- a/src/main/java/org/pqca/indexing/java/JavaIndexService.java +++ b/src/main/java/org/pqca/indexing/java/JavaIndexService.java @@ -42,7 +42,11 @@ public JavaIndexService( public void setExcludePatterns(@Nullable List patterns) { if (patterns == null) { super.setExcludePatterns( - List.of("src/test/", "/package-info.java$", "/module-info.java$")); + List.of( + "src/test/", + "/package-info\\.java$", + "/module-info\\.java$", + "Test\\.java$")); } else { super.setExcludePatterns(patterns); } diff --git a/src/main/java/org/pqca/indexing/python/PythonIndexService.java b/src/main/java/org/pqca/indexing/python/PythonIndexService.java index a17f604..e6ad25f 100644 --- a/src/main/java/org/pqca/indexing/python/PythonIndexService.java +++ b/src/main/java/org/pqca/indexing/python/PythonIndexService.java @@ -41,7 +41,7 @@ public PythonIndexService( public void setExcludePatterns(@Nullable List patterns) { if (patterns == null) { - super.setExcludePatterns(List.of("src/test/", "tests/")); + super.setExcludePatterns(List.of("src/test/", "tests/", "/test_*\\.py$", "test\\.py$")); } else { super.setExcludePatterns(patterns); }