/*******************************************************************************
* BDD-Security, application security testing framework
*
* Copyright (C) `2014 Stephen de Vries`
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see `<http://www.gnu.org/licenses/>`.
******************************************************************************/
package net.continuumsecurity.runner;
import net.continuumsecurity.Config;
import net.continuumsecurity.web.drivers.DriverFactory;
import net.continuumsecurity.web.steps.AppScanningSteps;
import net.continuumsecurity.web.steps.NessusScanningSteps;
import net.continuumsecurity.web.steps.InfrastructureSteps;
import net.continuumsecurity.web.steps.WebApplicationSteps;
import org.apache.commons.io.FileUtils;
import org.jbehave.core.io.CodeLocations;
import org.jbehave.core.io.StoryFinder;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.InstanceStepsFactory;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.*;
public class StoryRunner extends BaseStoryRunner {
final CmdLineParser parser;
private static final String LATEST_REPORTS = Config.getLatestReportsDir();
private static final String RESOURCES_DIR = "src"+File.separator+"main"+File.separator+"resources";
private static final String REPORTS_DIR = Config.getReportsDir();
@Option(name = "-story", usage = "Name of story meta-tag to run")
private String storyName;
@Option(name = "-id", usage = "ID of scenario meta-tag to run")
private String idName;
@Option(name = "-c")
private boolean justRunConfig = false;
@Option(name = "-h")
private boolean help = false;
public StoryRunner() {
super();
// configuredEmbedder().useEmbedderControls(new
// PropertyBasedEmbedderControls());
parser = new CmdLineParser(this);
}
@Override
public InjectableStepsFactory stepsFactory() {
WebApplicationSteps ws = new WebApplicationSteps();
return new InstanceStepsFactory(configuration(),
ws,
new InfrastructureSteps(),
new NessusScanningSteps(),
new AppScanningSteps());
}
@Override
public List<String> storyPaths() {
List<String> includes = new ArrayList<String>();
includes.add("**/*.story");
List<String> excludes = new ArrayList<String>();
excludes.add("**/configuration.story");
excludes.add("**/navigate_app.story");
return new StoryFinder().findPaths(
CodeLocations.codeLocationFromURL(storyUrl), includes,
excludes);
}
private void prepareReportsDir() throws IOException {
FileUtils.deleteQuietly(new File(LATEST_REPORTS));
File viewDir = new File(LATEST_REPORTS + File.separator+"view");
FileUtils.copyDirectory(new File(RESOURCES_DIR), viewDir);
}
private void copyResultsToStampedReportsDir() throws IOException {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd-HH.mm.ss", Locale.getDefault());
File dirName = new File(REPORTS_DIR+File.separator+formatter.format(new Date()));
FileUtils.forceMkdir(dirName);
FileUtils.copyDirectory(new File(LATEST_REPORTS), dirName);
}
/*
* Add required meta filters to control which stories are run
*/
protected List<String> createFilters() {
List<String> filters = new ArrayList<String>();
//JBehave doesn't propagate meta tags to givenstories, so we have to fix them manually
if (storyName != null) {
if (storyName.equalsIgnoreCase("app_scan")) {
filters.add("-m \"+pre navigate +story "+storyName+"\"");
} else filters.add("-m \"+story "+storyName+"\"");
}
if (idName != null) {
if (idName.startsWith("scan_")) {
filters.add("-m \"+pre navigate +id "+idName+"\"");
} else filters.add("-m \"+id "+idName+"\"");
}
filters.add("-skip");
log.debug(" running with filters:");
for (String filter : filters) {
log.debug("\t"+filter);
}
return filters;
}
public void execute(String... argv) throws CmdLineException,IOException {
parser.parseArgument(argv);
if (help) {
parser.setUsageWidth(Integer.MAX_VALUE);
parser.printUsage(System.err);
System.exit(0);
}
prepareReportsDir();
List<String> filters = createFilters();
configuredEmbedder().useMetaFilters(filters);
if (justRunConfig) {
try {
log.debug("Running configuration stories");
ConfigurationStoryRunner configRunner = new ConfigurationStoryRunner(filters);
configRunner.run();
log.debug("Configuration stories completed.");
} catch (Throwable t) {
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
log.error("Configuration stories failed: " + t.getMessage());
log.error("Halting execution");
log.error(sw.toString());
t.printStackTrace();
wrapUp();
System.exit(1);
}
wrapUp();
System.exit(0);
}
try {
run();
log.debug("Completed StoryRunner.run() successfully.");
} catch (Throwable e) {
log.debug("Caught exception from StoryRunner.execute()");
e.printStackTrace();
} finally {
wrapUp();
}
System.exit(0);
}
public void wrapUp() {
configuredEmbedder().generateReportsView();
try {
copyResultsToStampedReportsDir();
} catch (IOException e) {
log.error(e.getMessage());
e.printStackTrace();
}
DriverFactory.quitAll();
}
public static void main(String... argv) throws CmdLineException,IOException {
StoryRunner storyRunner = new StoryRunner();
storyRunner.execute(argv);
}
}