diff --git a/pom.xml b/pom.xml
index d2279c8..a859759 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,7 +13,7 @@
21
UTF-8
- 1.4.8
+ 1.5.1
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