diff --git a/pom.xml b/pom.xml
index 699024f..1dd155f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,6 +61,13 @@
picocli
4.6.2
+
+ commons-codec
+ commons-codec
+ 1.15
+ test
+
+
diff --git a/src/main/java/com/contrastsecurity/Jbom.java b/src/main/java/com/contrastsecurity/Jbom.java
index a39eeb4..eac96c9 100644
--- a/src/main/java/com/contrastsecurity/Jbom.java
+++ b/src/main/java/com/contrastsecurity/Jbom.java
@@ -135,24 +135,27 @@ public void doLocalProcess(String pid, String exclude, String outputDir, String
generateBOM( pid, name);
}
}
-
public Libraries doLocalFile(String file, String outputDir) {
+ File f = new File( file );
+ return doLocalFile(f,outputDir);
+ }
+
+
+ public Libraries doLocalFile(File file, String outputDir) {
Logger.log( "Analyzing file " + file );
Libraries libs = new Libraries();
-
- File f = new File( file );
- if ( !f.exists() ) {
+ if ( !file.exists() ) {
Logger.log( "Could not find file: " + file );
}
- if ( !f.isFile() ) {
+ if ( !file.isFile() ) {
Logger.log( "Could not open file: " + file );
}
- if ( !libs.isArchive( file ) ) {
+ if ( !libs.isArchive( file.getAbsolutePath() ) ) {
Logger.log( "File does not appear to be an archive: " + file );
}
try{
- String name = file;
+ String name = file.getName();
int idx = name.lastIndexOf('/');
if ( idx != -1 ) {
name = name.substring( idx + 1 );
@@ -162,7 +165,7 @@ public Libraries doLocalFile(String file, String outputDir) {
name = name.substring( 0, idx );
}
name = outputDir + "/jbom-" + name + ( tag == null ? "" : "-" +tag ) + ".json";
- libs.runScan( f );
+ libs.runScan( file );
libs.save(name);
}catch(Exception e){
e.printStackTrace();
diff --git a/src/main/java/com/contrastsecurity/Libraries.java b/src/main/java/com/contrastsecurity/Libraries.java
index 348fd42..1d350cd 100644
--- a/src/main/java/com/contrastsecurity/Libraries.java
+++ b/src/main/java/com/contrastsecurity/Libraries.java
@@ -16,6 +16,7 @@
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
+import java.util.stream.Collectors;
import com.github.packageurl.PackageURL;
@@ -32,6 +33,8 @@ public class Libraries {
private Set codesourceExamined = new HashSet<>();
private Set libraries = new HashSet<>();
private Set dependencies = new HashSet<>();
+ private Hash rootSHA1;
+ private Hash rootMD5;
public void runScan(File jarPath) throws Exception {
addAllLibraries( null, jarPath.getAbsolutePath() );
@@ -72,35 +75,18 @@ public void addAllLibraries( Class clazz, String codesource ) {
codesourceExamined.add( path );
File f = new File( path );
- Library lib = new Library( parts[parts.length-1] ); // last segment
- lib.parsePath( path );
- lib.setType( Library.Type.LIBRARY );
- lib.addProperty( "codesource", path );
- Logger.debug( "MAIN: " + codesource );
-
- // add Contrast custom properties
- lib.addProperty("source", "Contrast Security - https://contrastsecurity.com");
- lib.addProperty("tool", "jbom - https://github.com/Contrast-Security-OSS/jbom");
- lib.setScope( Scope.REQUIRED );
-
- libraries.add( lib );
- invoked.add( lib );
-
- JarInputStream jis1 = new JarInputStream( new FileInputStream( f ) );
- String sha1 = hash( jis1, MessageDigest.getInstance("SHA1") );
- lib.addHash( new Hash( Hash.Algorithm.SHA1, sha1 ) );
+ String sha1 = hash( new FileInputStream( f ), MessageDigest.getInstance("SHA1") );
+ rootSHA1 = new Hash( Hash.Algorithm.SHA1, sha1 );
- JarInputStream jis2 = new JarInputStream( new FileInputStream( f ) );
- String md5 = hash( jis2, MessageDigest.getInstance("MD5") );
- lib.addHash( new Hash( Hash.Algorithm.MD5, md5 ) );
-
- lib.addProperty( "maven", "https://search.maven.org/search?q=1:" + sha1 );
+ String md5 = hash( new FileInputStream( f ), MessageDigest.getInstance("MD5") );
+ rootMD5 = new Hash( Hash.Algorithm.MD5, md5 );
// scan for nested libraries
JarInputStream jis3 = new JarInputStream( new FileInputStream( f ) );
JarFile jarfile = new JarFile( f );
scan( jarfile, jis3, f.getAbsolutePath() );
+ addRootHashesToRootJar();
} catch( Exception e ) {
Logger.log( "The jbom project needs your help to deal with unusual CodeSources." );
Logger.log( "Report issue here: https://github.com/Contrast-Security-OSS/jbom/issues/new/choose" );
@@ -110,6 +96,15 @@ public void addAllLibraries( Class clazz, String codesource ) {
}
}
+ private void addRootHashesToRootJar() {
+ for( Component lib : libraries.stream()
+ .filter(lib-> lib.getHashes()==null||lib.getHashes().isEmpty())
+ .collect(Collectors.toList())) {
+ lib.addHash(rootSHA1);
+ lib.addHash(rootMD5);
+ }
+ }
+
public void scan( JarFile jarFile, JarInputStream jis, String codesource ) throws Exception {
JarEntry entry = null;
while ((entry = jis.getNextJarEntry()) != null) {
@@ -120,6 +115,26 @@ public void scan( JarFile jarFile, JarInputStream jis, String codesource ) throw
Logger.log( "Problem extracting metadata from " + entry.getName() + " based on " + codesource + ". Continuing." );
e.printStackTrace();
}
+ } else if ( isPom(entry)) {
+ try {
+ Library innerlib = new Library();
+ // FIXME: set Scope.EXCLUDED for non-invoked libraries
+ innerlib.setScope( Scope.REQUIRED );
+ innerlib.parsePath( entry.getName() );
+ innerlib.addProperty( "codesource", jarFile.getName() + "!/" + entry.getName() );
+ libraries.add( innerlib );
+ innerlib.setType( Library.Type.LIBRARY );
+ parsePom( jis, innerlib );
+ try {
+ if ( innerlib.getGroup() != null && innerlib.getName() != null ) {
+ innerlib.setPurl(new PackageURL( PackageURL.StandardTypes.MAVEN, innerlib.getGroup(), innerlib.getName(), innerlib.getVersion(), null, null));
+ }
+ } catch( Exception e ) {
+ // continue
+ }
+ } catch( Exception e ) {
+ // Logger.log( "Problem parsing POM from " + nestedName + " based on " + codesource + ". Continuing." );
+ }
}
}
}
@@ -162,7 +177,7 @@ public void scanInner( String codesource, JarFile jarFile, JarInputStream jis, J
InputStream nis4 = jarFile.getInputStream( entry );
JarInputStream innerJis4 = new JarInputStream( nis4 );
while ((entry = innerJis4.getNextJarEntry()) != null) {
- if ( entry.getName().endsWith( "/pom.xml" ) ) {
+ if ( isPom(entry) ) {
try {
parsePom( innerJis4, innerlib );
} catch( Exception e ) {
@@ -181,6 +196,10 @@ public void scanInner( String codesource, JarFile jarFile, JarInputStream jis, J
}
+ private boolean isPom(JarEntry entry){
+ return !entry.isDirectory()&&entry.getName().endsWith("/pom.xml");
+ }
+
public boolean isArchive( String filename ) {
if ( filename.endsWith( "!/" ) ) {
diff --git a/src/main/java/com/contrastsecurity/Library.java b/src/main/java/com/contrastsecurity/Library.java
index 581758b..1680164 100644
--- a/src/main/java/com/contrastsecurity/Library.java
+++ b/src/main/java/com/contrastsecurity/Library.java
@@ -2,6 +2,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.github.packageurl.PackageURL;
@@ -79,14 +80,18 @@ public String toString() {
}
@Override
- public final boolean equals(Object o) {
- Library that = (Library)o;
- return this.jar.equals(that.jar);
+ public boolean equals(Object o) {
+ if(o instanceof Library) {
+ Library that = (Library) o;
+ return (this.getName() + this.getVersion() + this.getGroup()).equals(that.getName() + that.getVersion() + that.getGroup());
+ } else {
+ return false;
+ }
}
@Override
- public final int hashCode() {
- return jar.hashCode();
+ public int hashCode() {
+ return Objects.hash(this.getName()+this.getVersion()+this.getGroup());
}
@Override
diff --git a/src/test/java/com/contrastsecurity/LibrariesTest.java b/src/test/java/com/contrastsecurity/LibrariesTest.java
index f2b0441..4f705c1 100644
--- a/src/test/java/com/contrastsecurity/LibrariesTest.java
+++ b/src/test/java/com/contrastsecurity/LibrariesTest.java
@@ -3,26 +3,90 @@
import static org.junit.Assert.*;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.cyclonedx.model.Component;
+import org.cyclonedx.model.Hash;
import org.junit.Test;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.Optional;
+
public class LibrariesTest {
@Test
public void testFile() throws Exception {
- String jar = "src/test/resources/spring-petclinic-1.5.1.jar";
+ File jar = getPathToResource("/spring-petclinic-1.5.1.jar");
Jbom jbom = new Jbom();
Libraries libs = jbom.doLocalFile( jar, "target/test" );
assertTrue( "Incorrect number of libraries found. " + libs.getLibraries().size() + " instead of 135", libs.getLibraries().size() == 135 );
+ compareHashToFile(jar,libs,"petclinic");
+
+ }
+
+ @Test
+ public void testFileCallBack() throws Exception {
+ File jar = getPathToResource("/callback-2.18.0-SNAPSHOT.jar");
+ Jbom jbom = new Jbom();
+ Libraries libs = jbom.doLocalFile( jar, "target/test" );
+ assertTrue( "Incorrect number of libraries found. " + libs.getLibraries().size() + " instead of 102", libs.getLibraries().size() == 102 );
+ compareHashToFile(jar,libs,"callback");
+
}
+ @Test
+ public void testFileWithShading() throws Exception {
+ File jar = getPathToResource("/provider-search-0.0.1-SNAPSHOT.jar");
+ Jbom jbom = new Jbom();
+ Libraries libs = jbom.doLocalFile( jar, "target/test" );
+ assertTrue( "Incorrect number of libraries found. " + libs.getLibraries().size() + " instead of 26", libs.getLibraries().size() == 26 );
+ compareHashToFile(jar,libs,"provider-search");
+ }
@Test
public void testDir() throws Exception {
- String dir = "src/test/resources";
+ File jar = getPathToResource("/");
Jbom jbom = new Jbom();
- Libraries libs = jbom.doLocalDirectory( dir, "target/test" );
- assertTrue( "Incorrect number of libraries found. " + libs.getLibraries().size() + " instead of 138", libs.getLibraries().size() == 138 );
+ Libraries libs = jbom.doLocalDirectory( jar.getAbsolutePath(), "target/test" );
+ assertTrue( "Incorrect number of libraries found. " + libs.getLibraries().size() + " instead of 265", libs.getLibraries().size() == 265 );
+
+ }
+
+ private File getPathToResource(String path) throws URISyntaxException {
+ return new File(LibrariesTest.class.getResource(path).toURI());
}
+ private void compareHashToFile(File file, Libraries libs, String libName) throws IOException {
+ Optional component = libs.getLibraries().stream().filter(lib->lib.getName().contains(libName)).findFirst();
+ if(!component.isPresent()) {
+ fail("Library : " + libName + " cannot be found");
+ } else {
+ String sha1FromLib = component.get().getHashes().stream().filter(h->h.getAlgorithm().equals("SHA-1")).map(Hash::getValue).findFirst().orElse("SHA1 Hash Not Found");
+ String md5FromLib = component.get().getHashes().stream().filter(h->h.getAlgorithm().equals("MD5")).map(Hash::getValue).findFirst().orElse("MD5 Hash Not Found");
+
+ assertEquals(hashFileSHA1(file),sha1FromLib);
+ assertEquals(hashFileMD5(file),md5FromLib);
+ }
+
+ }
+
+ private String hashFileSHA1(File path) throws IOException {
+ return DigestUtils.sha1Hex(new FileInputStream(path));
+ }
+
+ private String hashFileMD5(File path) throws IOException {
+ return DigestUtils.md5Hex(new FileInputStream(path));
+ }
+
+
+
+
+
+
+
+
+
}
\ No newline at end of file
diff --git a/src/test/resources/callback-2.18.0-SNAPSHOT.jar b/src/test/resources/callback-2.18.0-SNAPSHOT.jar
new file mode 100644
index 0000000..4d02c56
Binary files /dev/null and b/src/test/resources/callback-2.18.0-SNAPSHOT.jar differ
diff --git a/src/test/resources/provider-search-0.0.1-SNAPSHOT.jar b/src/test/resources/provider-search-0.0.1-SNAPSHOT.jar
new file mode 100644
index 0000000..645effc
Binary files /dev/null and b/src/test/resources/provider-search-0.0.1-SNAPSHOT.jar differ