/**
* Find Security Bugs
* Copyright (c) Philippe Arteau, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package com.h3xstream.findbugs.test.service;
import static org.mockito.Mockito.mock;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
import javax.annotation.concurrent.NotThreadSafe;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.DetectorFactoryCollection;
import edu.umd.cs.findbugs.FindBugs2;
import edu.umd.cs.findbugs.FindBugsProgress;
import edu.umd.cs.findbugs.Plugin;
import edu.umd.cs.findbugs.PluginException;
import edu.umd.cs.findbugs.Project;
import edu.umd.cs.findbugs.config.ProjectFilterSettings;
import edu.umd.cs.findbugs.config.UserPreferences;
@NotThreadSafe
public class FindBugsLauncher {
private static final Logger log = LoggerFactory.getLogger(FindBugsLauncher.class);
static Plugin loadedPlugin;
/**
* Launch an analysis on the given source files.
*
* @param classFiles
* @param bugReporter
* @throws java.io.IOException
* @throws InterruptedException
* @throws edu.umd.cs.findbugs.PluginException
* @throws URISyntaxException
*
*/
public void analyze(String[] classFiles, BugReporter bugReporter) throws IOException, InterruptedException,
PluginException, NoSuchFieldException, IllegalAccessException, URISyntaxException {
analyze(classFiles, new String[] {}, bugReporter);
}
/**
* Launch an analysis on the given source files.
*
* @param classFiles
* @param classPaths
* @param bugReporter
* @throws java.io.IOException
* @throws InterruptedException
* @throws edu.umd.cs.findbugs.PluginException
* @throws URISyntaxException
*
*/
public void analyze(String[] classFiles, String[] classPaths, BugReporter bugReporter) throws IOException, InterruptedException,
PluginException, NoSuchFieldException, IllegalAccessException, URISyntaxException {
Project project = new Project();
project.setProjectName("automate-test-project");
for (String file : classFiles) {
project.addFile(file);
}
// Add classpath list to project's auxclasspath
for (String classpath : classPaths) {
project.addAuxClasspathEntry(classpath);
}
if (loadedPlugin == null) {
//Initialize the plugin base on the findbugs.xml
byte[] archive = buildFakePluginJar();
File f = new File(System.getProperty("java.io.tmpdir"), "plugin.jar");
log.info("Writing " + f.getCanonicalPath());
f.deleteOnExit();
FileOutputStream out = new FileOutputStream(f);
out.write(archive);
out.close();
loadedPlugin = Plugin.loadCustomPlugin(f.toURI().toURL(), project);
}
//FindBugs engine
FindBugs2 engine = new FindBugs2();
engine.setNoClassOk(true);
engine.setMergeSimilarWarnings(false);
engine.setBugReporter(bugReporter);
engine.setProject(project);
engine.setProgressCallback(mock(FindBugsProgress.class));
engine.setDetectorFactoryCollection(DetectorFactoryCollection.instance());
//User preferences set to miss no bugs report
UserPreferences prefs = UserPreferences.createDefaultUserPreferences();
ProjectFilterSettings filter = prefs.getFilterSettings();
filter.setMinRank(20);
filter.setDisplayFalseWarnings(true);
filter.setMinPriority("Low");
engine.setUserPreferences(prefs);
log.info("Analyzing... {}", classFiles);
engine.execute();
}
/**
* The minimum requirement to have a "valid" archive plugin is to include
* findbugs.xml, messages.xml and MANIFEST.MF files. The rest of the
* resources are load using the parent ClassLoader (Not requires to be in
* the jar).
* <p>
* Instead of building a file on disk, the result of the stream is kept in
* memory and return as a byte array.
*
* @return
* @throws IOException
* @throws URISyntaxException
*/
private byte[] buildFakePluginJar() throws IOException, URISyntaxException {
ClassLoader cl = getClass().getClassLoader();
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
JarOutputStream jar = new JarOutputStream(buffer);
final URL metadata = cl.getResource("metadata");
if (metadata != null) {
final File dir = new File(metadata.toURI());
//Add files to the jar stream
addFilesToStream(cl, jar, dir, "");
}
jar.finish();
jar.close();
return buffer.toByteArray();
}
private void addFilesToStream(final ClassLoader cl, final JarOutputStream jar, final File dir,
final String path) throws IOException {
for (final File nextFile : dir.listFiles()) {
if (nextFile.isFile()) {
final String resource = path + nextFile.getName();
jar.putNextEntry(new ZipEntry(resource));
jar.write(IOUtils.toByteArray(cl.getResourceAsStream("metadata/" + resource)));
} else {
addFilesToStream(cl, jar, nextFile, path + nextFile.getName() + "/");
}
}
}
}