From 2d53d3ab00133a8deafb76e9a81edb2a921627eb Mon Sep 17 00:00:00 2001 From: san-zrl Date: Tue, 3 Feb 2026 15:17:33 +0100 Subject: [PATCH 1/2] added Go indexer and scanner Signed-off-by: san-zrl --- pom.xml | 2 +- .../org/pqca/indexing/IndexingService.java | 19 ++- .../org/pqca/indexing/go/GoBuildType.java | 28 ++++ .../org/pqca/indexing/go/GoIndexService.java | 102 +++++++++++++ .../org/pqca/scanning/IScannerService.java | 3 +- src/main/java/org/pqca/scanning/Language.java | 26 ++++ .../go/GoDetectionCollectionRule.java | 46 ++++++ .../pqca/scanning/go/GoScannerService.java | 142 ++++++++++++++++++ .../org/pqca/indexing/GoIndexServiceTest.java | 42 ++++++ src/test/testdata/go/simple/go.mod | 0 src/test/testdata/go/simple/module1/file.go | 0 11 files changed, 402 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/pqca/indexing/go/GoBuildType.java create mode 100644 src/main/java/org/pqca/indexing/go/GoIndexService.java create mode 100644 src/main/java/org/pqca/scanning/Language.java create mode 100644 src/main/java/org/pqca/scanning/go/GoDetectionCollectionRule.java create mode 100644 src/main/java/org/pqca/scanning/go/GoScannerService.java create mode 100644 src/test/java/org/pqca/indexing/GoIndexServiceTest.java create mode 100644 src/test/testdata/go/simple/go.mod create mode 100644 src/test/testdata/go/simple/module1/file.go diff --git a/pom.xml b/pom.xml index d2279c8..351e2c2 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ 21 UTF-8 - 1.4.8 + 2.0.0-SNAPSHOT 13.3.0.3209 25.8.0.112029 diff --git a/src/main/java/org/pqca/indexing/IndexingService.java b/src/main/java/org/pqca/indexing/IndexingService.java index 837b4b7..3f5911a 100644 --- a/src/main/java/org/pqca/indexing/IndexingService.java +++ b/src/main/java/org/pqca/indexing/IndexingService.java @@ -46,9 +46,9 @@ public abstract class IndexingService { private static final Logger LOGGER = LoggerFactory.getLogger(IndexingService.class); @Nullable private final IProgressDispatcher progressDispatcher; - @Nonnull private final String languageIdentifier; - @Nonnull private final String languageFileExtension; - @Nonnull private File baseDirectory; + @Nonnull protected final String languageIdentifier; + @Nonnull protected final String languageFileExtension; + @Nonnull protected File baseDirectory; @Nullable private IBuildType mainBuildType; private List excludePatterns = new ArrayList(); @@ -178,12 +178,11 @@ void collectInputFiles( } continue; } - if (file.getName().endsWith(this.languageFileExtension) - && !this.excludeFromIndexing(file)) { + if (this.isLanguageFile(file) && !this.excludeFromIndexing(file)) { try { final TestInputFileBuilder builder = createTestFileBuilder(projectDirectory, file); - builder.setLanguage(this.languageIdentifier); + builder.setLanguage(getLanguage(file)); inputFiles.add(builder.build()); } catch (IOException iox) { LOGGER.debug(iox.getLocalizedMessage()); @@ -233,5 +232,13 @@ protected String getProjectIdentifier(@Nonnull File directory) { public abstract boolean isModule(@Nonnull File directory); + protected boolean isLanguageFile(@Nonnull File file) { + return file.getName().endsWith(languageFileExtension); + } + + protected String getLanguage(File file) { + return this.languageIdentifier; + } + @Nullable public abstract IBuildType getMainBuildTypeFromModuleDirectory(@Nonnull File directory); } diff --git a/src/main/java/org/pqca/indexing/go/GoBuildType.java b/src/main/java/org/pqca/indexing/go/GoBuildType.java new file mode 100644 index 0000000..5d973aa --- /dev/null +++ b/src/main/java/org/pqca/indexing/go/GoBuildType.java @@ -0,0 +1,28 @@ +/* + * 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.go; + +import org.pqca.indexing.IBuildType; + +public enum GoBuildType implements IBuildType { + GO, + MAVEN, + GRADLE +} diff --git a/src/main/java/org/pqca/indexing/go/GoIndexService.java b/src/main/java/org/pqca/indexing/go/GoIndexService.java new file mode 100644 index 0000000..9c23573 --- /dev/null +++ b/src/main/java/org/pqca/indexing/go/GoIndexService.java @@ -0,0 +1,102 @@ +/* + * 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.go; + +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 GoIndexService extends IndexingService { + + public GoIndexService(@Nonnull File baseDirectory) { + this(null, baseDirectory); + } + + public GoIndexService( + @Nullable IProgressDispatcher progressDispatcher, @Nonnull File baseDirectory) { + super(progressDispatcher, baseDirectory, "go", ".go"); + this.setExcludePatterns(null); + } + + public void setExcludePatterns(@Nullable List patterns) { + if (patterns == null) { + super.setExcludePatterns(List.of("test/", "_test.go$")); + } else { + super.setExcludePatterns(patterns); + } + } + + @Override + public boolean isModule(@Nonnull File directory) { + if (!directory.isDirectory()) { + return false; + } + for (String buildFileName : + List.of("go.mod", "pom.xml", "build.gradle", "build.gradle.kts")) { + final File file = new File(directory, buildFileName); + if (file.exists() && file.isFile()) { + return true; + } + } + return false; + } + + @Override + public boolean isLanguageFile(@Nonnull File file) { + return file.getName().endsWith(languageFileExtension) || file.getName().equals("go.mod"); + } + + @Override + public String getLanguage(File file) { + if (file.getName().equals("go.mod")) { + return "gomod"; + } + return this.languageIdentifier; + } + + @Override + @Nullable public IBuildType getMainBuildTypeFromModuleDirectory(@Nonnull File directory) { + if (!directory.isDirectory()) { + return null; + } + // go + final File goModFile = new File(directory, "go.mod"); + if (goModFile.exists() && goModFile.isFile()) { + return GoBuildType.GO; + } + // maven + final File pomFile = new File(directory, "pom.xml"); + if (pomFile.exists() && pomFile.isFile()) { + return GoBuildType.MAVEN; + } + // gradle + for (String gradleFileName : List.of("build.gradle", "build.gradle.kts")) { + final File gradleFile = new File(directory, gradleFileName); + if (gradleFile.exists() && gradleFile.isFile()) { + return GoBuildType.GRADLE; + } + } + return null; + } +} diff --git a/src/main/java/org/pqca/scanning/IScannerService.java b/src/main/java/org/pqca/scanning/IScannerService.java index 19aa008..186cf99 100644 --- a/src/main/java/org/pqca/scanning/IScannerService.java +++ b/src/main/java/org/pqca/scanning/IScannerService.java @@ -23,10 +23,11 @@ import jakarta.annotation.Nonnull; import java.util.List; import java.util.function.Consumer; +import org.pqca.errors.ClientDisconnected; import org.pqca.indexing.ProjectModule; public interface IScannerService extends Consumer> { @Nonnull - ScanResultDTO scan(@Nonnull List index) throws Exception; + ScanResultDTO scan(@Nonnull List index) throws ClientDisconnected; } diff --git a/src/main/java/org/pqca/scanning/Language.java b/src/main/java/org/pqca/scanning/Language.java new file mode 100644 index 0000000..e335af3 --- /dev/null +++ b/src/main/java/org/pqca/scanning/Language.java @@ -0,0 +1,26 @@ +/* + * 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; + +public enum Language { + JAVA, + PYTHON, + GO +} diff --git a/src/main/java/org/pqca/scanning/go/GoDetectionCollectionRule.java b/src/main/java/org/pqca/scanning/go/GoDetectionCollectionRule.java new file mode 100644 index 0000000..20c53a8 --- /dev/null +++ b/src/main/java/org/pqca/scanning/go/GoDetectionCollectionRule.java @@ -0,0 +1,46 @@ +/* + * 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.go; + +import com.ibm.engine.detection.Finding; +import com.ibm.engine.language.go.GoScanContext; +import com.ibm.mapper.model.INode; +import com.ibm.plugin.rules.GoInventoryRule; +import jakarta.annotation.Nonnull; +import java.util.List; +import java.util.function.Consumer; +import org.sonar.go.symbols.Symbol; +import org.sonar.plugins.go.api.Tree; +import org.sonar.plugins.go.api.checks.GoCheck; + +public class GoDetectionCollectionRule extends GoInventoryRule { + private final Consumer> handler; + + public GoDetectionCollectionRule(@Nonnull Consumer> findingConsumer) { + this.handler = findingConsumer; + } + + @Override + public void update(@Nonnull Finding finding) { + super.update(finding); + final List nodes = goTranslationProcess.initiate(finding.detectionStore()); + handler.accept(nodes); + } +} diff --git a/src/main/java/org/pqca/scanning/go/GoScannerService.java b/src/main/java/org/pqca/scanning/go/GoScannerService.java new file mode 100644 index 0000000..29b5a3d --- /dev/null +++ b/src/main/java/org/pqca/scanning/go/GoScannerService.java @@ -0,0 +1,142 @@ +/* + * 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.go; + +import com.ibm.plugin.CryptoGoSensor; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +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; +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; +import org.sonar.plugins.go.api.checks.GoCheck; + +public final class GoScannerService extends ScannerService { + + private final GoConverter goConverter; + + public GoScannerService(@Nonnull File projectDirectory) { + this(null, projectDirectory); + } + + public GoScannerService( + @Nullable IProgressDispatcher progressDispatcher, @Nonnull File projectDirectory) { + super(progressDispatcher, projectDirectory); + + try { + DefaultTempFolder tempFolder = new DefaultTempFolder(createDirectory()); + goConverter = new GoConverter(tempFolder.newDir()); + } catch (IOException ioe) { + throw new IllegalStateException(ioe); + } + } + + @Nonnull + private File createDirectory() throws IOException { + // create directory + final String folderId = UUID.randomUUID().toString().replace("-", ""); + final File tempDir = new File(this.projectDirectory + File.separator + folderId); + if (tempDir.exists()) { + throw new IOException("Temp dir already exists " + tempDir.getPath()); + } + if (!tempDir.mkdirs()) { + throw new IOException("Could not create temp dir" + tempDir.getPath()); + } + return tempDir; + } + + @Override + public @Nonnull ScanResultDTO scan(@Nonnull List index) + throws ClientDisconnected { + LOGGER.info("Start scanning {} go projects", index.size()); + + long scanTimeStart = System.currentTimeMillis(); + int counter = 1; + int numberOfScannedLines = 0; + int numberOfScannedFiles = 0; + + GoCheck visitor = new GoDetectionCollectionRule(this); + GoChecks checks = new GoRuleChecks(visitor); + final SensorContextTester sensorContext = SensorContextTester.create(projectDirectory); + // Go scanner (CryptoGoSensor) 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 go project " + projectStr)); + } + LOGGER.info("Scanning go project {}", projectStr); + + CryptoGoSensor.execute((SensorContext) sensorContext, goConverter, checks); + + counter += 1; + } + LOGGER.info("Scanned {} go projects", index.size()); + + return new ScanResultDTO( + scanTimeStart, + System.currentTimeMillis(), + numberOfScannedLines, + numberOfScannedFiles, + this.getBOM().map(CBOM::new).orElse(null)); + } + + private class GoRuleChecks extends GoChecks { + private final List checks = new ArrayList<>(); + + public GoRuleChecks(GoCheck check) { + super(null); + checks.add(check); + } + + public List all() { + return this.checks; + } + + @SuppressWarnings("null") + public RuleKey ruleKey(GoCheck check) { + return RuleKey.of("sonar-cryptography", "go-rule"); + } + } +} diff --git a/src/test/java/org/pqca/indexing/GoIndexServiceTest.java b/src/test/java/org/pqca/indexing/GoIndexServiceTest.java new file mode 100644 index 0000000..3611860 --- /dev/null +++ b/src/test/java/org/pqca/indexing/GoIndexServiceTest.java @@ -0,0 +1,42 @@ +/* + * 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.go.GoIndexService; + +class GoIndexServiceTest { + @Test + void testDefaultExclusion() throws ClientDisconnected { + final GoIndexService goIndexService = + new GoIndexService(new File("src/test/testdata/go/simple")); + final List projectModules = goIndexService.index(null); + assertThat(projectModules).hasSize(1); + assertThat(projectModules.getFirst().inputFileList()) + .extracting(Object::toString) + .containsExactlyInAnyOrder("go.mod", "module1/file.go"); + } +} diff --git a/src/test/testdata/go/simple/go.mod b/src/test/testdata/go/simple/go.mod new file mode 100644 index 0000000..e69de29 diff --git a/src/test/testdata/go/simple/module1/file.go b/src/test/testdata/go/simple/module1/file.go new file mode 100644 index 0000000..e69de29 From 3bc8ff71fc46fd904ff9932542821b275b0d793c Mon Sep 17 00:00:00 2001 From: san-zrl Date: Thu, 5 Feb 2026 12:17:31 +0100 Subject: [PATCH 2/2] upgraded to sonar-crypto 1.5.1 with support for go Signed-off-by: san-zrl --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 351e2c2..a859759 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ 21 UTF-8 - 2.0.0-SNAPSHOT + 1.5.1 13.3.0.3209 25.8.0.112029