Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 30 additions & 63 deletions src/main/java/com/redhat/exhort/providers/GoModulesProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@

import static com.redhat.exhort.impl.ExhortApi.debugLoggingIsNeeded;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.packageurl.MalformedPackageURLException;
import com.github.packageurl.PackageURL;
import com.redhat.exhort.Api;
Expand All @@ -43,7 +40,6 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
Expand All @@ -56,10 +52,7 @@ public final class GoModulesProvider extends Provider {

public static final String PROP_EXHORT_GO_MVS_LOGIC_ENABLED = "EXHORT_GO_MVS_LOGIC_ENABLED";
private static final Logger log = LoggersFactory.getLogger(GoModulesProvider.class.getName());
private static final String GO_HOST_ARCHITECTURE_ENV_NAME = "GOHOSTARCH";
private static final String GO_HOST_OPERATION_SYSTEM_ENV_NAME = "GOHOSTOS";
public static final String DEFAULT_MAIN_VERSION = "v0.0.0";
private final TreeMap<String, String> goEnvironmentVariableForPurl;
private final String goExecutable;

public String getMainModuleVersion() {
Expand All @@ -71,7 +64,6 @@ public String getMainModuleVersion() {
public GoModulesProvider(Path manifest) {
super(Type.GOLANG, manifest);
this.goExecutable = Operations.getExecutable("go", "version");
this.goEnvironmentVariableForPurl = getQualifiers();
this.mainModuleVersion = getDefaultMainModuleVersion();
}

Expand All @@ -97,34 +89,32 @@ public Content provideComponent() throws IOException {
sbom.getAsJsonString().getBytes(StandardCharsets.UTF_8), Api.CYCLONEDX_MEDIA_TYPE);
}

private PackageURL toPurl(
String dependency, String delimiter, TreeMap<String, String> qualifiers) {
private PackageURL toPurl(String dependency, String delimiter) {
try {
int lastSlashIndex = dependency.lastIndexOf("/");
// there is no '/' char in module/package, so there is no namespace, only name
if (lastSlashIndex == -1) {
String[] splitParts = dependency.split(delimiter);
if (splitParts.length == 2) {
return new PackageURL(
Type.GOLANG.getType(), null, splitParts[0], splitParts[1], qualifiers, null);
Type.GOLANG.getType(), null, splitParts[0], splitParts[1], null, null);
} else {
return new PackageURL(
Type.GOLANG.getType(), null, splitParts[0], this.mainModuleVersion, qualifiers, null);
Type.GOLANG.getType(), null, splitParts[0], this.mainModuleVersion, null, null);
}
}
String namespace = dependency.substring(0, lastSlashIndex);
String dependencyAndVersion = dependency.substring(lastSlashIndex + 1);
String[] parts = dependencyAndVersion.split(delimiter);

if (parts.length == 2) {
return new PackageURL(
Type.GOLANG.getType(), namespace, parts[0], parts[1], qualifiers, null);
return new PackageURL(Type.GOLANG.getType(), namespace, parts[0], parts[1], null, null);
// in this case, there is no version (happens with main module), thus need to take it from
// precalculated
// main module version.
} else {
return new PackageURL(
Type.GOLANG.getType(), namespace, parts[0], this.mainModuleVersion, qualifiers, null);
Type.GOLANG.getType(), namespace, parts[0], this.mainModuleVersion, null, null);
}
} catch (MalformedPackageURLException e) {
throw new IllegalArgumentException(
Expand Down Expand Up @@ -294,17 +284,19 @@ private Sbom buildSbomFromGraph(
// Build Sbom
String rootPackage = getParentVertex(linesList.get(0));

PackageURL root = toPurl(rootPackage, "@", this.goEnvironmentVariableForPurl);
PackageURL root = toPurl(rootPackage, "@");
Sbom sbom = SbomFactory.newInstance(Sbom.BelongingCondition.PURL, "sensitive");
sbom.addRoot(root);
edges.forEach(
(key, value) -> {
PackageURL source = toPurl(key, "@", this.goEnvironmentVariableForPurl);
value.forEach(
dep -> {
PackageURL targetPurl = toPurl(dep, "@", this.goEnvironmentVariableForPurl);
sbom.addDependency(source, targetPurl, null);
});
PackageURL source = toPurl(key, "@");
value.stream()
.filter(dep -> !isGoToolchainEntry(dep))
.forEach(
dep -> {
PackageURL targetPurl = toPurl(dep, "@");
sbom.addDependency(source, targetPurl, null);
});
});
List<String> ignoredDepsPurl =
ignoredDeps.stream().map(PackageURL::getCoordinates).collect(Collectors.toList());
Expand Down Expand Up @@ -379,41 +371,14 @@ private static List<String> collectAllDirectDependencies(List<String> targetLine
return targetLines.stream()
.filter(line -> getParentVertex(line).equals(getParentVertex(edge)))
.map(GoModulesProvider::getChildVertex)
.filter(dep -> !isGoToolchainEntry(dep))
.collect(Collectors.toList());
}

private TreeMap<String, String> getQualifiers() {
var goEnvironmentVariables = getGoEnvironmentVariables();
var qualifiers = new TreeMap<String, String>();
qualifiers.put("type", "module");
if (goEnvironmentVariables.containsKey(GO_HOST_ARCHITECTURE_ENV_NAME)) {
qualifiers.put("goarch", goEnvironmentVariables.get(GO_HOST_ARCHITECTURE_ENV_NAME));
}
if (goEnvironmentVariables.containsKey(GO_HOST_OPERATION_SYSTEM_ENV_NAME)) {
qualifiers.put("goos", goEnvironmentVariables.get(GO_HOST_OPERATION_SYSTEM_ENV_NAME));
}

return qualifiers;
}

private Map<String, String> getGoEnvironmentVariables() {
String goEnvironmentVariables =
Operations.runProcessGetOutput(null, goExecutable, "env", "--json");
JsonNode tree;
try {
tree = new ObjectMapper().readTree(goEnvironmentVariables);
} catch (JsonProcessingException e) {
throw new RuntimeException("Failed to parse go environment variables: " + e.getMessage());
}
var envMap = new HashMap<String, String>();
tree.fields()
.forEachRemaining(
entry -> {
String key = entry.getKey();
String value = entry.getValue().asText();
envMap.put(key, value);
});
return envMap;
private static boolean isGoToolchainEntry(String dependency) {
// Filter out Go toolchain entries like "go@1.18", "go@1.19", etc.
// These are not actual dependencies but the Go toolchain itself
return dependency.startsWith("go@");
}

private String buildGoModulesDependencies(Path manifestPath) {
Expand All @@ -435,19 +400,21 @@ private String buildGoModulesDependencies(Path manifestPath) {
private Sbom buildSbomFromList(String golangDeps, List<PackageURL> ignoredDeps) {
String[] allModulesFlat = golangDeps.split(Operations.GENERIC_LINE_SEPARATOR);
String parentVertex = getParentVertex(allModulesFlat[0]);
PackageURL root = toPurl(parentVertex, "@", this.goEnvironmentVariableForPurl);
PackageURL root = toPurl(parentVertex, "@");
// Get only direct dependencies of root package/module, and that's it.
List<String> deps = collectAllDirectDependencies(Arrays.asList(allModulesFlat), parentVertex);

Sbom sbom = SbomFactory.newInstance(Sbom.BelongingCondition.PURL, "sensitive");
sbom.addRoot(root);
deps.forEach(
dep -> {
PackageURL targetPurl = toPurl(dep, "@", this.goEnvironmentVariableForPurl);
if (dependencyNotToBeIgnored(ignoredDeps, targetPurl)) {
sbom.addDependency(root, targetPurl, null);
}
});
deps.stream()
.filter(dep -> !isGoToolchainEntry(dep))
.forEach(
dep -> {
PackageURL targetPurl = toPurl(dep, "@");
if (dependencyNotToBeIgnored(ignoredDeps, targetPurl)) {
sbom.addDependency(root, targetPurl, null);
}
});
List<String> ignoredDepsByName = new ArrayList<>();
ignoredDeps.forEach(
purl -> {
Expand All @@ -467,7 +434,7 @@ private List<PackageURL> getIgnoredDeps(Path manifestPath) throws IOException {
goModlines.stream()
.filter(this::IgnoredLine)
.map(this::extractPackageName)
.map(dep -> toPurl(dep, "\\s{1,3}", this.goEnvironmentVariableForPurl))
.map(dep -> toPurl(dep, "\\s{1,3}"))
.collect(Collectors.toList());
return ignored;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ public String getNextTagVersion(TagInfo tagInfo) {
}

public String getPseudoVersion(TagInfo tagInfo, String newTagVersion) {
String stringTS = tagInfo.getCommitTimestamp().toString().replaceAll("[:-]|T", "");
String stringTS =
tagInfo.getCommitTimestamp().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
String commitHash12 = tagInfo.getCurrentCommitDigest().substring(0, 12);
return String.format("%s.%s-%s", newTagVersion, stringTS, commitHash12);
}
Expand Down
20 changes: 10 additions & 10 deletions src/test/resources/msc/golang/expected_sbom_ca.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"group" : "github.com/sample",
"name" : "demo-app",
"version" : "v0.0.0",
"purl" : "pkg:golang/github.com/sample/demo-app@v0.0.0?goarch=amd64&goos=linux&type=module"
"purl" : "pkg:golang/github.com/sample/demo-app@v0.0.0"
}
},
"components" : [
Expand All @@ -19,71 +19,71 @@
"group" : "github.com/sample",
"name" : "demo-app",
"version" : "v0.0.0",
"purl" : "pkg:golang/github.com/sample/demo-app@v0.0.0?goarch=amd64&goos=linux&type=module"
"purl" : "pkg:golang/github.com/sample/demo-app@v0.0.0"
},
{
"type" : "library",
"bom-ref" : "pkg:golang/github.com/gin-gonic/gin@v1.6.0",
"group" : "github.com/gin-gonic",
"name" : "gin",
"version" : "v1.6.0",
"purl" : "pkg:golang/github.com/gin-gonic/gin@v1.6.0?goarch=amd64&goos=linux&type=module"
"purl" : "pkg:golang/github.com/gin-gonic/gin@v1.6.0"
},
{
"type" : "library",
"bom-ref" : "pkg:golang/github.com/go-logr/zapr@v1.2.0",
"group" : "github.com/go-logr",
"name" : "zapr",
"version" : "v1.2.0",
"purl" : "pkg:golang/github.com/go-logr/zapr@v1.2.0?goarch=amd64&goos=linux&type=module"
"purl" : "pkg:golang/github.com/go-logr/zapr@v1.2.0"
},
{
"type" : "library",
"bom-ref" : "pkg:golang/github.com/google/uuid@v1.1.1",
"group" : "github.com/google",
"name" : "uuid",
"version" : "v1.1.1",
"purl" : "pkg:golang/github.com/google/uuid@v1.1.1?goarch=amd64&goos=linux&type=module"
"purl" : "pkg:golang/github.com/google/uuid@v1.1.1"
},
{
"type" : "library",
"bom-ref" : "pkg:golang/github.com/ipld/go-car@v0.3.0",
"group" : "github.com/ipld",
"name" : "go-car",
"version" : "v0.3.0",
"purl" : "pkg:golang/github.com/ipld/go-car@v0.3.0?goarch=amd64&goos=linux&type=module"
"purl" : "pkg:golang/github.com/ipld/go-car@v0.3.0"
},
{
"type" : "library",
"bom-ref" : "pkg:golang/github.com/json-iterator/go@v1.1.12",
"group" : "github.com/json-iterator",
"name" : "go",
"version" : "v1.1.12",
"purl" : "pkg:golang/github.com/json-iterator/go@v1.1.12?goarch=amd64&goos=linux&type=module"
"purl" : "pkg:golang/github.com/json-iterator/go@v1.1.12"
},
{
"type" : "library",
"bom-ref" : "pkg:golang/github.com/labstack/echo/v4@v4.1.18-0.20201215153152-4422e3b66b9f",
"group" : "github.com/labstack/echo",
"name" : "v4",
"version" : "v4.1.18-0.20201215153152-4422e3b66b9f",
"purl" : "pkg:golang/github.com/labstack/echo/v4@v4.1.18-0.20201215153152-4422e3b66b9f?goarch=amd64&goos=linux&type=module"
"purl" : "pkg:golang/github.com/labstack/echo/v4@v4.1.18-0.20201215153152-4422e3b66b9f"
},
{
"type" : "library",
"bom-ref" : "pkg:golang/github.com/russellhaering/goxmldsig@v1.1.0",
"group" : "github.com/russellhaering",
"name" : "goxmldsig",
"version" : "v1.1.0",
"purl" : "pkg:golang/github.com/russellhaering/goxmldsig@v1.1.0?goarch=amd64&goos=linux&type=module"
"purl" : "pkg:golang/github.com/russellhaering/goxmldsig@v1.1.0"
},
{
"type" : "library",
"bom-ref" : "pkg:golang/go.elastic.co/apm@v1.11.0",
"group" : "go.elastic.co",
"name" : "apm",
"version" : "v1.11.0",
"purl" : "pkg:golang/go.elastic.co/apm@v1.11.0?goarch=amd64&goos=linux&type=module"
"purl" : "pkg:golang/go.elastic.co/apm@v1.11.0"
}
],
"dependencies" : [
Expand Down
Loading
Loading