Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the ability to resolve shaded dependencies. #5

Merged
merged 1 commit into from
Feb 11, 2022
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
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@
<artifactId>picocli</artifactId>
<version>4.6.2</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
<scope>test</scope>
</dependency>


</dependencies>

Expand Down
19 changes: 11 additions & 8 deletions src/main/java/com/contrastsecurity/Jbom.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand All @@ -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();
Expand Down
65 changes: 42 additions & 23 deletions src/main/java/com/contrastsecurity/Libraries.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -32,6 +33,8 @@ public class Libraries {
private Set<String> codesourceExamined = new HashSet<>();
private Set<Component> libraries = new HashSet<>();
private Set<org.cyclonedx.model.Dependency> dependencies = new HashSet<>();
private Hash rootSHA1;
private Hash rootMD5;

public void runScan(File jarPath) throws Exception {
addAllLibraries( null, jarPath.getAbsolutePath() );
Expand Down Expand Up @@ -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" );
Expand All @@ -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) {
Expand All @@ -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." );
}
}
}
}
Expand Down Expand Up @@ -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 ) {
Expand All @@ -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( "!/" ) ) {
Expand Down
15 changes: 10 additions & 5 deletions src/main/java/com/contrastsecurity/Library.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
72 changes: 68 additions & 4 deletions src/test/java/com/contrastsecurity/LibrariesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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> 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));
}









}
Binary file added src/test/resources/callback-2.18.0-SNAPSHOT.jar
Binary file not shown.
Binary file not shown.