Skip to content

Commit

Permalink
Merge pull request #43 from RHEcosystemAppEng/fix-maven-exhortignore
Browse files Browse the repository at this point in the history
fix: fix maven exhortignore
feat: add mechanism to determine exhort backend URL address based on EXHORT_DEV_MODE Property/Environment Variable , by default it'll be prod exhort URL.
  • Loading branch information
zvigrinberg committed Sep 10, 2023
2 parents f4d903b + 093ec78 commit afd9786
Show file tree
Hide file tree
Showing 23 changed files with 2,630 additions and 39 deletions.
11 changes: 11 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@
<id>TomerFi</id>
<url>https://github.com/TomerFi</url>
</developer>
<developer>
<id>zvigrinberg</id>
<url>https://github.com/zvigrinberg</url>
</developer>

</developers>

<issueManagement>
Expand Down Expand Up @@ -534,11 +539,17 @@ limitations under the License.]]>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
<goal>add-resource</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources</source>
</sources>
<resources>
<resource>
<directory>src/main/resources/exhort</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
Expand Down
61 changes: 51 additions & 10 deletions src/main/java/com/redhat/exhort/impl/ExhortApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,19 @@
package com.redhat.exhort.impl;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Version;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.stream.Stream;

import com.fasterxml.jackson.core.JsonProcessingException;
Expand All @@ -45,14 +46,15 @@
/** Concrete implementation of the Exhort {@link Api} Service. **/
public final class ExhortApi implements Api {

private static final String DEFAULT_ENDPOINT = "http://alpha-exhort.apps.sssc-cl01.appeng.rhecoeng.com";
private static final System.Logger LOG = System.getLogger(ExhortApi.class.getName());
private static final String DEFAULT_ENDPOINT = "https://rhda.rhcloud.com";
private static final String DEFAULT_ENDPOINT_DEV = "http://alpha-exhort.apps.sssc-cl01.appeng.rhecoeng.com";
private final String endpoint;

public static final void main(String[] args) throws IOException, InterruptedException, ExecutionException {
ExhortApi exhortApi = new ExhortApi();
exhortApi.stackAnalysis("/home/zgrinber/git/exhort-java-api/src/test/resources/tst_manifests/golang/go_mod_light_no_ignore/go.mod");
// exhortApi.componentAnalysis("go.mod", Files.readAllBytes(Path.of("/home/zgrinber/git/exhort-java-api/src/test/resources/tst_manifests/golang/go_mod_light_no_ignore/go.mod"))).get();
// System.out.println(new ObjectMapper().writeValueAsString(analysisReport));
AnalysisReport analysisReport = new ExhortApi()
.stackAnalysis("/home/rromerom/workspace/github.com/RHEcosystemAppEng/exhort-java-api/src/test/resources/tst_manifests/maven/deps_with_no_ignore/pom.xml").get();
System.out.println(new ObjectMapper().writeValueAsString(analysisReport));
}
/**
* Enum for identifying token environment variables and their
Expand Down Expand Up @@ -96,9 +98,48 @@ static HttpClient.Version getHttpVersion() {
ExhortApi(final HttpClient client) {
this.client = client;
this.mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
this.endpoint = Objects.requireNonNullElse(
System.getenv("EXHORT_URL"), ExhortApi.DEFAULT_ENDPOINT
);
// Take default from config.properties in case client didn't override DEV MODE
if(System.getProperty("EXHORT_DEV_MODE") == null ) {
try {
InputStream exhortConfig = this.getClass().getClassLoader().getResourceAsStream("config.properties");
if (exhortConfig == null)
{
LOG.log(System.Logger.Level.INFO,"config.properties not found on the class path, fallback to default DEV MODE = false");
System.setProperty("EXHORT_DEV_MODE", "false");
}
else {
Properties properties = new Properties();
properties.load(exhortConfig);
System.setProperty("EXHORT_DEV_MODE", (String) properties.get("EXHORT_DEV_MODE"));
}
} catch (IOException e) {
LOG.log(System.Logger.Level.INFO,String.format("Error loading config.properties , fallback to set default property DEV MODE = false, Error message = %s",e.getMessage()));
System.setProperty("EXHORT_DEV_MODE", "false");
}
}

this.endpoint = getExhortUrl();
}

public String getExhortUrl() {
String endpoint;
if(getBooleanValueEnvironment("EXHORT_DEV_MODE")) {
endpoint = getStringValueEnvironment("DEV_EXHORT_BACKEND_URL", DEFAULT_ENDPOINT_DEV);
}
else
{
endpoint = DEFAULT_ENDPOINT;
}
return endpoint;
}

private boolean getBooleanValueEnvironment(String key) {
String result = Objects.requireNonNullElse(System.getenv(key), Objects.requireNonNullElse(System.getProperty(key), "false"));
return Boolean.parseBoolean(result.trim().toLowerCase());
}
private String getStringValueEnvironment(String key,String defaultValue) {
String result = Objects.requireNonNullElse(System.getenv(key), Objects.requireNonNullElse(System.getProperty(key), defaultValue));
return result;
}

@Override
Expand Down
26 changes: 17 additions & 9 deletions src/main/java/com/redhat/exhort/providers/JavaMavenProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@
* Component analysis.
**/
public final class JavaMavenProvider extends Provider {

public static void main(String[] args) throws IOException {
JavaMavenProvider javaMavenProvider = new JavaMavenProvider();
Content content = javaMavenProvider.provideStack(Path.of("/tmp/bug-070923/pom.xml"));
String report = new String(content.buffer);
System.out.println(report);
}
public JavaMavenProvider() {
super(Type.MAVEN);
}
Expand Down Expand Up @@ -73,20 +78,18 @@ public Content provideStack(final Path manifestPath) throws IOException {
// if we have dependencies marked as ignored, exclude them from the tree command
var ignored = getDependencies(manifestPath).stream()
.filter(d -> d.ignored)
.map(DependencyAggregator::toString)
.collect(Collectors.joining(","));
if (ignored.length() > 0) {
mvnTreeCmd.add(String.format("-Dexcludes=%s", ignored));
}
.map(DependencyAggregator::toPurl)
.map(PackageURL::getCoordinates)
.collect(Collectors.toList());
// execute the tree command
Operations.runProcess(mvnTreeCmd.toArray(String[]::new));
var sbom = buildSbomFromDot(tmpFile);
// build and return content for constructing request to the backend
return new Content(sbom.getAsJsonString().getBytes(), Api.CYCLONEDX_MEDIA_TYPE);
return new Content(sbom.filterIgnoredDeps(ignored).getAsJsonString().getBytes(), Api.CYCLONEDX_MEDIA_TYPE);
}

private Sbom buildSbomFromDot(Path dotFile) throws IOException {
var sbom = SbomFactory.newInstance();
var sbom = SbomFactory.newInstance(Sbom.BelongingCondition.PURL);
var reader = new BufferedReader(Files.newBufferedReader(dotFile));
String line = reader.readLine();
while (line != null) {
Expand Down Expand Up @@ -150,7 +153,7 @@ public Content provideComponent(byte[] manifestContent) throws IOException {
var sbom = SbomFactory.newInstance().addRoot(getRoot(tmpEffPom));
deps.stream()
.map(DependencyAggregator::toPurl)
.dropWhile(ignored::contains)
.filter(dep -> !ignored.contains(dep))
.forEach(d -> sbom.addDependency(sbom.getRoot(), d));

// build and return content for constructing request to the backend
Expand Down Expand Up @@ -325,5 +328,10 @@ public boolean equals(Object o) {
Objects.equals(this.artifactId, that.artifactId) &&
Objects.equals(this.version, that.version);
}

@Override
public int hashCode() {
return Objects.hash(groupId, artifactId, version);
}
}
}
79 changes: 62 additions & 17 deletions src/main/java/com/redhat/exhort/sbom/CycloneDXSbom.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import com.github.packageurl.MalformedPackageURLException;
import org.cyclonedx.BomGeneratorFactory;
import org.cyclonedx.CycloneDxSchema.Version;
import org.cyclonedx.model.Bom;
Expand All @@ -35,10 +38,16 @@
public class CycloneDXSbom implements Sbom {

private static final Version VERSION = Version.VERSION_14;

private Bom bom;
private Bom bom;
private PackageURL root;

private BiPredicate<Collection,Component> belongingCriteriaBinaryAlgorithm;

private <X,Y> Predicate<Y> genericComparator(BiPredicate<X,Y> binaryBelongingCriteriaAlgorithm, X container)
{
return dep -> binaryBelongingCriteriaAlgorithm.test(container, dep);
}

public CycloneDXSbom() {
bom = new Bom();
bom.setVersion(1);
Expand All @@ -47,9 +56,36 @@ public CycloneDXSbom() {
bom.setMetadata(metadata);
bom.setComponents(new ArrayList<>());
bom.setDependencies(new ArrayList<>());
belongingCriteriaBinaryAlgorithm = getBelongingConditionByName();

}

private static BiPredicate<Collection, Component> getBelongingConditionByName() {
return (collection, component) -> collection.contains(component.getName());
}

public CycloneDXSbom(BelongingCondition belongingCondition) {
this();
if(belongingCondition.equals(BelongingCondition.NAME))
{
belongingCriteriaBinaryAlgorithm = getBelongingConditionByName();
}
else if (belongingCondition.equals(BelongingCondition.PURL)){
belongingCriteriaBinaryAlgorithm = getBelongingConditionByPurl();
}
else
{
// fallback to belonging condition by name ( default) - this one in case the enum type will be extended and new BelongingType won't be implemented right away.
belongingCriteriaBinaryAlgorithm = getBelongingConditionByName();
}

}

public Sbom addRoot(PackageURL rootRef) {
private BiPredicate<Collection, Component> getBelongingConditionByPurl() {
return (collection, component) -> collection.contains(componentToPurl(component).getCoordinates());
}

public Sbom addRoot(PackageURL rootRef) {
this.root = rootRef;
Component rootComponent = newRootComponent(rootRef);
bom.getMetadata().setComponent(rootComponent);
Expand Down Expand Up @@ -84,15 +120,24 @@ private Component newComponent(PackageURL ref) {
return c;
}

private PackageURL componentToPurl(Component component) {
try {
return new PackageURL(component.getPurl());
} catch (MalformedPackageURLException e) {
throw new RuntimeException(e);
}
}

private Dependency newDependency(PackageURL ref) {
return new Dependency(ref.getCoordinates());
}

@Override
public Sbom filterIgnoredDeps(Collection<String> ignoredDeps) {
public <T> Sbom filterIgnoredDeps(Collection<T> ignoredDeps) {

List<String> initialIgnoreRefs = bom.getComponents()
.stream()
.filter(c -> ignoredDeps.contains(c.getName()))
.filter(c -> genericComparator(this.belongingCriteriaBinaryAlgorithm,ignoredDeps).test(c))
.map(Component::getBomRef).collect(Collectors.toList());
List<String> refsToIgnore = createIgnoreFilter(bom.getDependencies(),
initialIgnoreRefs);
Expand All @@ -118,19 +163,19 @@ public Sbom filterIgnoredDeps(Collection<String> ignoredDeps) {
}

private List<String> createIgnoreFilter(List<Dependency> deps, Collection<String> toIgnore) {
List<String> result = new ArrayList<>(toIgnore);
List<String> t = deps.stream()
.filter(d -> toIgnore.contains(d.getRef()))
.dropWhile(d -> d.getDependencies() == null)
.map(Dependency::getDependencies)
.flatMap(Collection::stream)
.map(Dependency::getRef)
.collect(Collectors.toList());
if (t.isEmpty()) {
return result;
List<String> result = new ArrayList<>(toIgnore);
for (Dependency dep : deps) {
if (toIgnore.contains(dep.getRef()) && dep.getDependencies() != null) {
List collected = dep.getDependencies().stream().map(p -> p.getRef()).collect(Collectors.toList());
result.addAll(collected);
if (dep.getDependencies().stream().filter(p -> p != null).count() > 0) {
result= createIgnoreFilter(dep.getDependencies(), result);
}

}
result.addAll(t);
return createIgnoreFilter(deps, result);

}
return result;
}

@Override
Expand Down
19 changes: 18 additions & 1 deletion src/main/java/com/redhat/exhort/sbom/Sbom.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,32 @@
package com.redhat.exhort.sbom;

import java.util.Collection;
import java.util.function.Predicate;

import com.github.packageurl.PackageURL;

public interface Sbom {

public Sbom addRoot(PackageURL root);
public PackageURL getRoot();
public Sbom filterIgnoredDeps(Collection<String> ignoredDeps);
public <T> Sbom filterIgnoredDeps(Collection<T> ignoredDeps);
public Sbom addDependency(PackageURL sourceRef, PackageURL targetRef);
public String getAsJsonString();

public enum BelongingCondition
{
NAME("name"),
PURL("purl");

String belongingCondition;

BelongingCondition(String belongingCondition) {
this.belongingCondition = belongingCondition;
}

public String getBelongingCondition() {
return belongingCondition;
}
}

}
3 changes: 3 additions & 0 deletions src/main/java/com/redhat/exhort/sbom/SbomFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,8 @@ public class SbomFactory {
public static Sbom newInstance() {
return new CycloneDXSbom();
}
public static Sbom newInstance(Sbom.BelongingCondition belongingCondition) {
return new CycloneDXSbom(belongingCondition);
}

}
1 change: 1 addition & 0 deletions src/main/resources/config.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
EXHORT_DEV_MODE=false
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Java_Maven_Provider_Test {
// - expected_sbom.json: the SBOM expected to be provided
static Stream<String> testFolders() {
return Stream.of(
"deps_no_trivial_with_ignore",
"deps_with_ignore_on_artifact",
"deps_with_ignore_on_dependency",
"deps_with_ignore_on_group",
Expand All @@ -52,7 +53,7 @@ void test_the_provideStack(String testFolder) throws IOException, InterruptedExc
}
// load expected SBOM
String expectedSbom;
try (var is = getClass().getModule().getResourceAsStream(String.join("/","tst_manifests", "maven", testFolder, "expected_sbom.json"))) {
try (var is = getClass().getModule().getResourceAsStream(String.join("/","tst_manifests", "maven", testFolder, "expected_stack_sbom.json"))) {
expectedSbom = new String(is.readAllBytes());
}
// when providing stack content for our pom
Expand All @@ -75,7 +76,7 @@ void test_the_provideComponent(String testFolder) throws IOException, Interrupte
}
// load expected SBOM
String expectedSbom = "";
try (var is = getClass().getModule().getResourceAsStream(String.join("/","tst_manifests", "maven", testFolder, "expected_sbom.json"))) {
try (var is = getClass().getModule().getResourceAsStream(String.join("/","tst_manifests", "maven", testFolder, "expected_component_sbom.json"))) {
expectedSbom = new String(is.readAllBytes());
}
// when providing component content for our pom
Expand Down
Loading

0 comments on commit afd9786

Please sign in to comment.