mirror of
https://github.com/ysoftdevs/DependencyCheck.git
synced 2026-03-20 16:24:11 +01:00
added support for fully executable jar files per issue #454
This commit is contained in:
@@ -31,6 +31,7 @@ import java.util.Enumeration;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.apache.commons.collections.buffer.CircularFifoBuffer;
|
||||||
|
|
||||||
import org.apache.commons.compress.archivers.ArchiveEntry;
|
import org.apache.commons.compress.archivers.ArchiveEntry;
|
||||||
import org.apache.commons.compress.archivers.ArchiveInputStream;
|
import org.apache.commons.compress.archivers.ArchiveInputStream;
|
||||||
@@ -58,8 +59,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* An analyzer that extracts files from archives and ensures any supported files contained within the archive are added to the
|
* An analyzer that extracts files from archives and ensures any supported files
|
||||||
* dependency list.</p>
|
* contained within the archive are added to the dependency list.</p>
|
||||||
*
|
*
|
||||||
* @author Jeremy Long
|
* @author Jeremy Long
|
||||||
*/
|
*/
|
||||||
@@ -70,7 +71,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
*/
|
*/
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(ArchiveAnalyzer.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(ArchiveAnalyzer.class);
|
||||||
/**
|
/**
|
||||||
* The count of directories created during analysis. This is used for creating temporary directories.
|
* The count of directories created during analysis. This is used for
|
||||||
|
* creating temporary directories.
|
||||||
*/
|
*/
|
||||||
private static int dirCount = 0;
|
private static int dirCount = 0;
|
||||||
/**
|
/**
|
||||||
@@ -78,7 +80,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
*/
|
*/
|
||||||
private File tempFileLocation = null;
|
private File tempFileLocation = null;
|
||||||
/**
|
/**
|
||||||
* The max scan depth that the analyzer will recursively extract nested archives.
|
* The max scan depth that the analyzer will recursively extract nested
|
||||||
|
* archives.
|
||||||
*/
|
*/
|
||||||
private static final int MAX_SCAN_DEPTH = Settings.getInt("archive.scan.depth", 3);
|
private static final int MAX_SCAN_DEPTH = Settings.getInt("archive.scan.depth", 3);
|
||||||
/**
|
/**
|
||||||
@@ -100,13 +103,15 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
*/
|
*/
|
||||||
private static final Set<String> ZIPPABLES = newHashSet("zip", "ear", "war", "jar", "sar", "apk", "nupkg");
|
private static final Set<String> ZIPPABLES = newHashSet("zip", "ear", "war", "jar", "sar", "apk", "nupkg");
|
||||||
/**
|
/**
|
||||||
* The set of file extensions supported by this analyzer. Note for developers, any additions to this list will need to be
|
* The set of file extensions supported by this analyzer. Note for
|
||||||
* explicitly handled in {@link #extractFiles(File, File, Engine)}.
|
* developers, any additions to this list will need to be explicitly handled
|
||||||
|
* in {@link #extractFiles(File, File, Engine)}.
|
||||||
*/
|
*/
|
||||||
private static final Set<String> EXTENSIONS = newHashSet("tar", "gz", "tgz", "bz2", "tbz2");
|
private static final Set<String> EXTENSIONS = newHashSet("tar", "gz", "tgz", "bz2", "tbz2");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Detects files with extensions to remove from the engine's collection of dependencies.
|
* Detects files with extensions to remove from the engine's collection of
|
||||||
|
* dependencies.
|
||||||
*/
|
*/
|
||||||
private static final FileFilter REMOVE_FROM_ANALYSIS = FileFilterBuilder.newInstance().addExtensions("zip", "tar", "gz", "tgz", "bz2", "tbz2")
|
private static final FileFilter REMOVE_FROM_ANALYSIS = FileFilterBuilder.newInstance().addExtensions("zip", "tar", "gz", "tgz", "bz2", "tbz2")
|
||||||
.build();
|
.build();
|
||||||
@@ -157,7 +162,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
//</editor-fold>
|
//</editor-fold>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the key used in the properties file to reference the analyzer's enabled property.
|
* Returns the key used in the properties file to reference the analyzer's
|
||||||
|
* enabled property.
|
||||||
*
|
*
|
||||||
* @return the analyzer's enabled property setting key
|
* @return the analyzer's enabled property setting key
|
||||||
*/
|
*/
|
||||||
@@ -169,7 +175,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
/**
|
/**
|
||||||
* The initialize method does nothing for this Analyzer.
|
* The initialize method does nothing for this Analyzer.
|
||||||
*
|
*
|
||||||
* @throws Exception is thrown if there is an exception deleting or creating temporary files
|
* @throws Exception is thrown if there is an exception deleting or creating
|
||||||
|
* temporary files
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void initializeFileTypeAnalyzer() throws Exception {
|
public void initializeFileTypeAnalyzer() throws Exception {
|
||||||
@@ -186,9 +193,11 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The close method deletes any temporary files and directories created during analysis.
|
* The close method deletes any temporary files and directories created
|
||||||
|
* during analysis.
|
||||||
*
|
*
|
||||||
* @throws Exception thrown if there is an exception deleting temporary files
|
* @throws Exception thrown if there is an exception deleting temporary
|
||||||
|
* files
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void close() throws Exception {
|
public void close() throws Exception {
|
||||||
@@ -205,8 +214,9 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Analyzes a given dependency. If the dependency is an archive, such as a WAR or EAR, the contents are extracted, scanned,
|
* Analyzes a given dependency. If the dependency is an archive, such as a
|
||||||
* and added to the list of dependencies within the engine.
|
* WAR or EAR, the contents are extracted, scanned, and added to the list of
|
||||||
|
* dependencies within the engine.
|
||||||
*
|
*
|
||||||
* @param dependency the dependency to analyze
|
* @param dependency the dependency to analyze
|
||||||
* @param engine the engine scanning
|
* @param engine the engine scanning
|
||||||
@@ -249,7 +259,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a zip file was identified as a possible JAR, this method will add the zip to the list of dependencies.
|
* If a zip file was identified as a possible JAR, this method will add the
|
||||||
|
* zip to the list of dependencies.
|
||||||
*
|
*
|
||||||
* @param dependency the zip file
|
* @param dependency the zip file
|
||||||
* @param engine the engine
|
* @param engine the engine
|
||||||
@@ -349,7 +360,9 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
final String archiveExt = FileUtils.getFileExtension(archive.getName()).toLowerCase();
|
final String archiveExt = FileUtils.getFileExtension(archive.getName()).toLowerCase();
|
||||||
try {
|
try {
|
||||||
if (ZIPPABLES.contains(archiveExt)) {
|
if (ZIPPABLES.contains(archiveExt)) {
|
||||||
extractArchive(new ZipArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
|
BufferedInputStream in = new BufferedInputStream(fis);
|
||||||
|
ensureReadableJar(archiveExt, in);
|
||||||
|
extractArchive(new ZipArchiveInputStream(in), destination, engine);
|
||||||
} else if ("tar".equals(archiveExt)) {
|
} else if ("tar".equals(archiveExt)) {
|
||||||
extractArchive(new TarArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
|
extractArchive(new TarArchiveInputStream(new BufferedInputStream(fis)), destination, engine);
|
||||||
} else if ("gz".equals(archiveExt) || "tgz".equals(archiveExt)) {
|
} else if ("gz".equals(archiveExt) || "tgz".equals(archiveExt)) {
|
||||||
@@ -377,13 +390,65 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the file being scanned is a JAR that begins with '#!/bin' which
|
||||||
|
* indicates it is a fully executable jar. If a fully executable JAR is identified
|
||||||
|
* the input stream will be advanced to the start of the actual JAR file (
|
||||||
|
* skipping the script).
|
||||||
|
*
|
||||||
|
* @see
|
||||||
|
* <a href="http://docs.spring.io/spring-boot/docs/1.3.0.BUILD-SNAPSHOT/reference/htmlsingle/#deployment-install">Installing
|
||||||
|
* Spring Boot Applications</a>
|
||||||
|
* @param archiveExt the file extension
|
||||||
|
* @param in the input stream
|
||||||
|
* @throws IOException thrown if there is an error reading the stream
|
||||||
|
*/
|
||||||
|
private void ensureReadableJar(final String archiveExt, BufferedInputStream in) throws IOException {
|
||||||
|
if ("jar".equals(archiveExt) && in.markSupported()) {
|
||||||
|
in.mark(7);
|
||||||
|
byte[] b = new byte[7];
|
||||||
|
in.read(b);
|
||||||
|
if (b[0] == '#'
|
||||||
|
&& b[1] == '!'
|
||||||
|
&& b[2] == '/'
|
||||||
|
&& b[3] == 'b'
|
||||||
|
&& b[4] == 'i'
|
||||||
|
&& b[5] == 'n'
|
||||||
|
&& b[6] == '/') {
|
||||||
|
boolean stillLooking = true;
|
||||||
|
int chr;
|
||||||
|
CircularFifoBuffer buf = new CircularFifoBuffer(6);
|
||||||
|
while (stillLooking && (chr = in.read()) != -1) {
|
||||||
|
if (chr == '\n' || chr == '\r') {
|
||||||
|
if ('e' == (Integer) buf.remove()
|
||||||
|
&& 'x' == (Integer) buf.remove()
|
||||||
|
&& 'i' == (Integer) buf.remove()
|
||||||
|
&& 't' == (Integer) buf.remove()
|
||||||
|
&& ' ' == (Integer) buf.remove()
|
||||||
|
&& '0' == (Integer) buf.remove()) {
|
||||||
|
in.mark(2);
|
||||||
|
if (in.read() == 'P' && in.read() == 'K') {
|
||||||
|
stillLooking = false;
|
||||||
|
in.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.add(chr);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
in.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts files from an archive.
|
* Extracts files from an archive.
|
||||||
*
|
*
|
||||||
* @param input the archive to extract files from
|
* @param input the archive to extract files from
|
||||||
* @param destination the location to write the files too
|
* @param destination the location to write the files too
|
||||||
* @param engine the dependency-check engine
|
* @param engine the dependency-check engine
|
||||||
* @throws ArchiveExtractionException thrown if there is an exception extracting files from the archive
|
* @throws ArchiveExtractionException thrown if there is an exception
|
||||||
|
* extracting files from the archive
|
||||||
*/
|
*/
|
||||||
private void extractArchive(ArchiveInputStream input, File destination, Engine engine) throws ArchiveExtractionException {
|
private void extractArchive(ArchiveInputStream input, File destination, Engine engine) throws ArchiveExtractionException {
|
||||||
ArchiveEntry entry;
|
ArchiveEntry entry;
|
||||||
@@ -442,7 +507,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
*
|
*
|
||||||
* @param inputStream the compressed file
|
* @param inputStream the compressed file
|
||||||
* @param outputFile the location to write the decompressed file
|
* @param outputFile the location to write the decompressed file
|
||||||
* @throws ArchiveExtractionException thrown if there is an exception decompressing the file
|
* @throws ArchiveExtractionException thrown if there is an exception
|
||||||
|
* decompressing the file
|
||||||
*/
|
*/
|
||||||
private void decompressFile(CompressorInputStream inputStream, File outputFile) throws ArchiveExtractionException {
|
private void decompressFile(CompressorInputStream inputStream, File outputFile) throws ArchiveExtractionException {
|
||||||
LOGGER.debug("Decompressing '{}'", outputFile.getPath());
|
LOGGER.debug("Decompressing '{}'", outputFile.getPath());
|
||||||
@@ -462,7 +528,8 @@ public class ArchiveAnalyzer extends AbstractFileTypeAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close the given {@link Closeable} instance, ignoring nulls, and logging any thrown {@link IOException}.
|
* Close the given {@link Closeable} instance, ignoring nulls, and logging
|
||||||
|
* any thrown {@link IOException}.
|
||||||
*
|
*
|
||||||
* @param closeable to be closed
|
* @param closeable to be closed
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user