Merge remote-tracking branch 'upstream/timeline-event-mgr' into 1192-manually_created_events

This commit is contained in:
millmanorama 2019-03-15 11:37:38 +01:00
commit e9b47ea45d
42 changed files with 2800 additions and 221 deletions

1
.gitignore vendored
View File

@ -49,6 +49,7 @@ genfiles.properties
!/test/input/NSRL.txt-md5.idx
/test/output/*
!/test/output/gold
/test/script/output_dir_link.txt
/test/output/gold/tmp
/test/script/ScriptLog.txt
/test/script/__pycache__/

View File

@ -21,7 +21,11 @@ package org.sleuthkit.autopsy.casemodule;
import java.util.Collection;
import java.util.Iterator;
import java.util.logging.Level;
import org.netbeans.spi.sendopts.OptionProcessor;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.commandlineingest.CommandLineIngestManager;
import org.sleuthkit.autopsy.commandlineingest.CommandLineOptionProcessor;
import org.sleuthkit.autopsy.commandlineingest.CommandLineStartupWindow;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
@ -54,6 +58,17 @@ public class StartupWindowProvider implements StartupWindowInterface {
private void init() {
if (startupWindowToUse == null) {
// first check whether we are running from command line
if (isRunningFromCommandLine()) {
// Autopsy is running from command line
logger.log(Level.INFO, "Running from command line"); //NON-NLS
System.out.println("Running from command line");
startupWindowToUse = new CommandLineStartupWindow();
// kick off command line processing
new CommandLineIngestManager().start();
return;
}
//discover the registered windows
Collection<? extends StartupWindowInterface> startupWindows
= Lookup.getDefault().lookupAll(StartupWindowInterface.class);
@ -93,6 +108,30 @@ public class StartupWindowProvider implements StartupWindowInterface {
}
}
/**
* Checks whether Autopsy is running from command line. There is an
* OptionProcessor that is responsible for processing command line inputs.
* If Autopsy is indeed running from command line, then use the command line
* startup window.
*
* @return True if running from command line, false otherwise
*/
private boolean isRunningFromCommandLine() {
// first look up all OptionProcessors and see if running from command line option is set
Collection<? extends OptionProcessor> optionProcessors = Lookup.getDefault().lookupAll(OptionProcessor.class);
Iterator<? extends OptionProcessor> optionsIterator = optionProcessors.iterator();
while (optionsIterator.hasNext()) {
// find CommandLineOptionProcessor
OptionProcessor processor = optionsIterator.next();
if ((processor instanceof CommandLineOptionProcessor)) {
// check if we are running from command line
return ((CommandLineOptionProcessor) processor).isRunFromCommandLine();
}
}
return false;
}
@Override
public void open() {
if (startupWindowToUse != null) {

View File

@ -0,0 +1,96 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.util.List;
import java.util.UUID;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import org.sleuthkit.datamodel.Content;
/**
* A "callback" that collects the results of running a data source processor on
* a data source and unblocks the job processing thread when the data source
* processor finishes running in its own thread.
*/
class AddDataSourceCallback extends DataSourceProcessorCallback {
private final Case caseForJob;
private final DataSource dataSourceInfo;
private final UUID taskId;
private final Object lock;
/**
* Constructs a "callback" that collects the results of running a data
* source processor on a data source and unblocks the job processing thread
* when the data source processor finishes running in its own thread.
*
* @param caseForJob The case for the current job.
* @param dataSourceInfo The data source
* @param taskId The task id to associate with ingest job events.
*/
AddDataSourceCallback(Case caseForJob, DataSource dataSourceInfo, UUID taskId, Object lock) {
this.caseForJob = caseForJob;
this.dataSourceInfo = dataSourceInfo;
this.taskId = taskId;
this.lock = lock;
}
/**
* Called by the data source processor when it finishes running in its own
* thread.
*
* @param result The result code for the processing of the data source.
* @param errorMessages Any error messages generated during the processing
* of the data source.
* @param dataSourceContent The content produced by processing the data
* source.
*/
@Override
public void done(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errorMessages, List<Content> dataSourceContent) {
if (!dataSourceContent.isEmpty()) {
caseForJob.notifyDataSourceAdded(dataSourceContent.get(0), taskId);
} else {
caseForJob.notifyFailedAddingDataSource(taskId);
}
dataSourceInfo.setDataSourceProcessorOutput(result, errorMessages, dataSourceContent);
dataSourceContent.addAll(dataSourceContent);
synchronized (lock) {
lock.notifyAll();
}
}
/**
* Called by the data source processor when it finishes running in its own
* thread, if that thread is the AWT (Abstract Window Toolkit) event
* dispatch thread (EDT).
*
* @param result The result code for the processing of the data source.
* @param errorMessages Any error messages generated during the processing
* of the data source.
* @param dataSourceContent The content produced by processing the data
* source.
*/
@Override
public void doneEDT(DataSourceProcessorCallback.DataSourceProcessorResult result, List<String> errorMessages, List<Content> dataSources) {
done(result, errorMessages, dataSources);
}
}

View File

@ -0,0 +1,17 @@
OpenIDE-Module-Name=CommandLineAutopsy
OptionsCategory_Keywords_Command_Line_Ingest_Settings=Command Line Ingest Settings
OptionsCategory_Keywords_General=Options
OptionsCategory_Name_Command_Line_Ingest=Command Line Ingest
CommandLineIngestSettingsPanel.ResultsDirectoryUnspecified=Output folder must be set
CommandLineIngestSettingsPanel.PathInvalid=Path is not valid
CommandLineIngestSettingsPanel.CannotAccess=Cannot access
CommandLineIngestSettingsPanel.CheckPermissions=Check permissions.
CommandLineIngestSettingsPanel.jLabelSelectOutputFolder.text=Select output folder:
CommandLineIngestSettingsPanel.jLabelInvalidResultsFolder.text=jLabelInvalidOutputFolder
CommandLineIngestSettingsPanel.outputPathTextField.toolTipText=Output folder for command line processing, i.e., the location where case folder will be created by command line processing mode.
CommandLineIngestSettingsPanel.outputPathTextField.text=
CommandLineIngestSettingsPanel.browseOutputFolderButton.text=Browse
CommandLineIngestSettingsPanel.bnEditIngestSettings.toolTipText=Ingest job settings for the command line processing mode context.
CommandLineIngestSettingsPanel.bnEditIngestSettings.text=Ingest Module Settings
CommandLinePanel.jLabel1.text=Ingest is running from command line
CommandLineStartupWindow.title.text=Running in Command Line Mode

View File

@ -0,0 +1,17 @@
OpenIDE-Module-Name=CommandLineAutopsy
OptionsCategory_Keywords_Command_Line_Ingest_Settings=Command Line Ingest Settings
OptionsCategory_Keywords_General=Options
OptionsCategory_Name_Command_Line_Ingest=Command Line Ingest
CommandLineIngestSettingsPanel.ResultsDirectoryUnspecified=Output folder must be set
CommandLineIngestSettingsPanel.PathInvalid=Path is not valid
CommandLineIngestSettingsPanel.CannotAccess=Cannot access
CommandLineIngestSettingsPanel.CheckPermissions=Check permissions.
CommandLineIngestSettingsPanel.jLabelSelectOutputFolder.text=Select output folder:
CommandLineIngestSettingsPanel.jLabelInvalidResultsFolder.text=jLabelInvalidOutputFolder
CommandLineIngestSettingsPanel.outputPathTextField.toolTipText=Output folder for command line processing, i.e., the location where case folder will be created by command line processing mode.
CommandLineIngestSettingsPanel.outputPathTextField.text=
CommandLineIngestSettingsPanel.browseOutputFolderButton.text=Browse
CommandLineIngestSettingsPanel.bnEditIngestSettings.toolTipText=Ingest job settings for the command line processing mode context.
CommandLineIngestSettingsPanel.bnEditIngestSettings.text=Ingest Module Settings
CommandLinePanel.jLabel1.text=Ingest is running from command line
CommandLineStartupWindow.title.text=Running in Command Line Mode

View File

@ -0,0 +1,613 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FilenameFilter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.UUID;
import java.util.Collection;
import java.util.Iterator;
import java.util.logging.Level;
import org.netbeans.spi.sendopts.OptionProcessor;
import org.openide.LifecycleManager;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CaseActionException;
import org.sleuthkit.autopsy.casemodule.CaseDetails;
import org.sleuthkit.autopsy.casemodule.CaseMetadata;
import org.sleuthkit.autopsy.core.RuntimeProperties;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback;
import static org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.TimeStampUtils;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.autopsy.ingest.IngestJob;
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestJobStartResult;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.IngestModuleError;
import org.sleuthkit.autopsy.report.ReportProgressPanel;
import org.sleuthkit.autopsy.report.caseuco.CaseUcoFormatExporter;
import org.sleuthkit.autopsy.report.caseuco.ReportCaseUco;
import org.sleuthkit.datamodel.Content;
/**
* Allows Autopsy to be invoked with a command line arguments. Causes Autopsy to
* create a case, add a specified data source, run ingest on that data source,
* produce a CASE/UCO report and exit.
*/
public class CommandLineIngestManager {
private static final Logger LOGGER = Logger.getLogger(CommandLineIngestManager.class.getName());
private Path rootOutputDirectory;
public CommandLineIngestManager() {
}
public void start() {
new Thread(new JobProcessingTask()).start();
}
public void stop() {
try {
// close current case if there is one open
Case.closeCurrentCase();
} catch (CaseActionException ex) {
LOGGER.log(Level.WARNING, "Unable to close the case while shutting down command line ingest manager", ex); //NON-NLS
}
// shut down Autopsy
LifecycleManager.getDefault().exit();
}
private final class JobProcessingTask implements Runnable {
private final Object ingestLock;
private JobProcessingTask() {
ingestLock = new Object();
try {
RuntimeProperties.setRunningWithGUI(false);
LOGGER.log(Level.INFO, "Set running with desktop GUI runtime property to false");
} catch (RuntimeProperties.RuntimePropertiesException ex) {
LOGGER.log(Level.SEVERE, "Failed to set running with desktop GUI runtime property to false", ex);
}
}
public void run() {
LOGGER.log(Level.INFO, "Job processing task started");
try {
// read command line inputs
LOGGER.log(Level.INFO, "Autopsy is running from command line"); //NON-NLS
String dataSourcePath = "";
String baseCaseName = "";
// first look up all OptionProcessors and get input data from CommandLineOptionProcessor
Collection<? extends OptionProcessor> optionProcessors = Lookup.getDefault().lookupAll(OptionProcessor.class);
Iterator<? extends OptionProcessor> optionsIterator = optionProcessors.iterator();
while (optionsIterator.hasNext()) {
// find CommandLineOptionProcessor
OptionProcessor processor = optionsIterator.next();
if (processor instanceof CommandLineOptionProcessor) {
// check if we are running from command line
dataSourcePath = ((CommandLineOptionProcessor) processor).getPathToDataSource();
baseCaseName = ((CommandLineOptionProcessor) processor).getBaseCaseName();
}
}
LOGGER.log(Level.INFO, "Data source path = {0}", dataSourcePath); //NON-NLS
LOGGER.log(Level.INFO, "Case name = {0}", baseCaseName); //NON-NLS
System.out.println("Data source path = " + dataSourcePath);
System.out.println("Case name = " + baseCaseName);
// verify inputs
if (dataSourcePath.isEmpty()) {
LOGGER.log(Level.SEVERE, "Data source path not specified");
System.out.println("Data source path not specified");
return;
}
if (baseCaseName.isEmpty()) {
LOGGER.log(Level.SEVERE, "Case name not specified");
System.out.println("Case name not specified");
return;
}
if (!(new File(dataSourcePath).exists())) {
LOGGER.log(Level.SEVERE, "Data source file not found {0}", dataSourcePath);
System.out.println("Data source file not found " + dataSourcePath);
return;
}
// read options panel configuration
String rootOutputDir = UserPreferences.getCommandLineModeResultsFolder();
LOGGER.log(Level.INFO, "Output directory = {0}", rootOutputDir); //NON-NLS
System.out.println("Output directoryh = " + rootOutputDir);
if (rootOutputDir.isEmpty()) {
LOGGER.log(Level.SEVERE, "Output directory not specified, please configure Command Line Options Panel (in Tools -> Options)");
System.out.println("Output directory not specified, please configure Command Line Options Panel (in Tools -> Options)");
return;
}
if (!(new File(rootOutputDir).exists())) {
LOGGER.log(Level.SEVERE, "The output directory doesn't exist {0}", rootOutputDir);
System.out.println("The output directory doesn't exist " + rootOutputDir);
return;
}
rootOutputDirectory = Paths.get(rootOutputDir);
// open case
Case caseForJob;
try {
caseForJob = openCase(baseCaseName);
} catch (CaseActionException ex) {
LOGGER.log(Level.SEVERE, "Error creating or opening case " + baseCaseName, ex);
System.out.println("Error creating or opening case " + baseCaseName);
return;
}
if (caseForJob == null) {
LOGGER.log(Level.SEVERE, "Error creating or opening case {0}", baseCaseName);
System.out.println("Error creating or opening case " + baseCaseName);
return;
}
DataSource dataSource = new DataSource("", Paths.get(dataSourcePath));
try {
// run data source processor
runDataSourceProcessor(caseForJob, dataSource);
// run ingest manager
analyze(dataSource);
// generate CASE-UCO report
Long selectedDataSourceId = getDataSourceId(dataSource);
Path reportFolderPath = Paths.get(caseForJob.getReportDirectory(), "CASE-UCO", "Data_Source_ID_" + selectedDataSourceId.toString() + "_" + TimeStampUtils.createTimeStamp(), ReportCaseUco.getReportFileName()); //NON_NLS
ReportProgressPanel progressPanel = new ReportProgressPanel("CASE_UCO", rootOutputDir); // dummy progress panel
CaseUcoFormatExporter.generateReport(selectedDataSourceId, reportFolderPath.toString(), progressPanel);
} catch (InterruptedException | AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException | AnalysisStartupException ex) {
LOGGER.log(Level.SEVERE, "Unable to ingest data source " + dataSourcePath + ". Exiting...", ex);
System.out.println("Unable to ingest data source " + dataSourcePath + ". Exiting...");
} catch (Throwable ex) {
/*
* Unexpected runtime exceptions firewall. This task is designed to
* be able to be run in an executor service thread pool without
* calling get() on the task's Future<Void>, so this ensures that
* such errors get logged.
*/
LOGGER.log(Level.SEVERE, "Unexpected error while ingesting data source " + dataSourcePath, ex);
System.out.println("Unexpected error while ingesting data source " + dataSourcePath + ". Exiting...");
} finally {
try {
Case.closeCurrentCase();
} catch (CaseActionException ex) {
LOGGER.log(Level.WARNING, "Exception while closing case", ex);
System.out.println("Exception while closing case");
}
}
} finally {
LOGGER.log(Level.INFO, "Job processing task finished");
System.out.println("Job processing task finished");
// shut down Autopsy
stop();
}
}
/**
* Provides object ID of the data source by reading it from Content
* object.
*
* @param dataSource DataSource object
* @return object ID
*/
private Long getDataSourceId(DataSource dataSource) {
Content content = dataSource.getContent().get(0);
return content.getId();
}
private Case openCase(String baseCaseName) throws CaseActionException {
LOGGER.log(Level.INFO, "Opening case {0}", baseCaseName);
Path caseDirectoryPath = findCaseDirectory(rootOutputDirectory, baseCaseName);
if (null != caseDirectoryPath) {
// found an existing case directory for same case name. the input case name must be unique. Exit.
LOGGER.log(Level.SEVERE, "Case {0} already exists. Case name must be unique. Exiting", baseCaseName);
throw new CaseActionException("Case " + baseCaseName + " already exists. Case name must be unique. Exiting");
} else {
caseDirectoryPath = createCaseFolderPath(rootOutputDirectory, baseCaseName);
// Create the case directory
Case.createCaseDirectory(caseDirectoryPath.toString(), Case.CaseType.SINGLE_USER_CASE);
CaseDetails caseDetails = new CaseDetails(baseCaseName);
Case.createAsCurrentCase(Case.CaseType.SINGLE_USER_CASE, caseDirectoryPath.toString(), caseDetails);
}
Case caseForJob = Case.getCurrentCase();
LOGGER.log(Level.INFO, "Opened case {0}", caseForJob.getName());
return caseForJob;
}
/**
* Passes the data source for the current job through a data source
* processor that adds it to the case database.
*
* @param dataSource The data source.
*
* @throws
* AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
* if there was a DSP processing error
*
* @throws InterruptedException if the thread running the job processing
* task is interrupted while blocked, i.e., if auto ingest is shutting
* down.
*/
private void runDataSourceProcessor(Case caseForJob, DataSource dataSource) throws InterruptedException, AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException {
LOGGER.log(Level.INFO, "Adding data source {0} ", dataSource.getPath().toString());
// Get an ordered list of data source processors to try
List<AutoIngestDataSourceProcessor> validDataSourceProcessors;
try {
validDataSourceProcessors = DataSourceProcessorUtility.getOrderedListOfDataSourceProcessors(dataSource.getPath());
} catch (AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException ex) {
LOGGER.log(Level.SEVERE, "Exception while determining best data source processor for {0}", dataSource.getPath());
// rethrow the exception.
throw ex;
}
// did we find a data source processor that can process the data source
if (validDataSourceProcessors.isEmpty()) {
// This should never happen. We should add all unsupported data sources as logical files.
LOGGER.log(Level.SEVERE, "Unsupported data source {0}", dataSource.getPath()); // NON-NLS
return;
}
DataSourceProcessorProgressMonitor progressMonitor = new DoNothingDSPProgressMonitor();
synchronized (ingestLock) {
// Try each DSP in decreasing order of confidence
for (AutoIngestDataSourceProcessor selectedProcessor : validDataSourceProcessors) {
UUID taskId = UUID.randomUUID();
caseForJob.notifyAddingDataSource(taskId);
DataSourceProcessorCallback callBack = new AddDataSourceCallback(caseForJob, dataSource, taskId, ingestLock);
caseForJob.notifyAddingDataSource(taskId);
LOGGER.log(Level.INFO, "Identified data source type for {0} as {1}", new Object[]{dataSource.getPath(), selectedProcessor.getDataSourceType()});
selectedProcessor.process(dataSource.getDeviceId(), dataSource.getPath(), progressMonitor, callBack);
ingestLock.wait();
// at this point we got the content object(s) from the current DSP.
// check whether the data source was processed successfully
if ((dataSource.getResultDataSourceProcessorResultCode() == CRITICAL_ERRORS)
|| dataSource.getContent().isEmpty()) {
// move onto the the next DSP that can process this data source
logDataSourceProcessorResult(dataSource);
continue;
}
logDataSourceProcessorResult(dataSource);
return;
}
// If we get to this point, none of the processors were successful
LOGGER.log(Level.SEVERE, "All data source processors failed to process {0}", dataSource.getPath());
// Throw an exception. It will get caught & handled upstream and will result in AIM auto-pause.
throw new AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException("Failed to process " + dataSource.getPath() + " with all data source processors");
}
}
/**
* Logs the results of running a data source processor on the data
* source for the current job.
*
* @param dataSource The data source.
*/
private void logDataSourceProcessorResult(DataSource dataSource) {
DataSourceProcessorCallback.DataSourceProcessorResult resultCode = dataSource.getResultDataSourceProcessorResultCode();
if (null != resultCode) {
switch (resultCode) {
case NO_ERRORS:
LOGGER.log(Level.INFO, "Added data source to case");
if (dataSource.getContent().isEmpty()) {
LOGGER.log(Level.SEVERE, "Data source failed to produce content");
}
break;
case NONCRITICAL_ERRORS:
for (String errorMessage : dataSource.getDataSourceProcessorErrorMessages()) {
LOGGER.log(Level.WARNING, "Non-critical error running data source processor for {0}: {1}", new Object[]{dataSource.getPath(), errorMessage});
}
LOGGER.log(Level.INFO, "Added data source to case");
if (dataSource.getContent().isEmpty()) {
LOGGER.log(Level.SEVERE, "Data source failed to produce content");
}
break;
case CRITICAL_ERRORS:
for (String errorMessage : dataSource.getDataSourceProcessorErrorMessages()) {
LOGGER.log(Level.SEVERE, "Critical error running data source processor for {0}: {1}", new Object[]{dataSource.getPath(), errorMessage});
}
LOGGER.log(Level.SEVERE, "Failed to add data source to case");
break;
}
} else {
LOGGER.log(Level.WARNING, "No result code for data source processor for {0}", dataSource.getPath());
}
}
/**
* Analyzes the data source content returned by the data source
* processor using the configured set of data source level and file
* level analysis modules.
*
* @param dataSource The data source to analyze.
*
* @throws AnalysisStartupException if there is an error analyzing the
* data source.
* @throws InterruptedException if the thread running the job processing
* task is interrupted while blocked, i.e., if auto ingest is shutting
* down.
*/
private void analyze(DataSource dataSource) throws AnalysisStartupException, InterruptedException {
LOGGER.log(Level.INFO, "Starting ingest modules analysis for {0} ", dataSource.getPath());
IngestJobEventListener ingestJobEventListener = new IngestJobEventListener();
IngestManager.getInstance().addIngestJobEventListener(ingestJobEventListener);
try {
synchronized (ingestLock) {
IngestJobSettings ingestJobSettings = new IngestJobSettings(UserPreferences.getCommandLineModeIngestModuleContextString());
List<String> settingsWarnings = ingestJobSettings.getWarnings();
if (settingsWarnings.isEmpty()) {
IngestJobStartResult ingestJobStartResult = IngestManager.getInstance().beginIngestJob(dataSource.getContent(), ingestJobSettings);
IngestJob ingestJob = ingestJobStartResult.getJob();
if (null != ingestJob) {
/*
* Block until notified by the ingest job event
* listener or until interrupted because auto ingest
* is shutting down.
*/
ingestLock.wait();
LOGGER.log(Level.INFO, "Finished ingest modules analysis for {0} ", dataSource.getPath());
IngestJob.ProgressSnapshot jobSnapshot = ingestJob.getSnapshot();
for (IngestJob.ProgressSnapshot.DataSourceProcessingSnapshot snapshot : jobSnapshot.getDataSourceSnapshots()) {
if (!snapshot.isCancelled()) {
List<String> cancelledModules = snapshot.getCancelledDataSourceIngestModules();
if (!cancelledModules.isEmpty()) {
LOGGER.log(Level.WARNING, String.format("Ingest module(s) cancelled for %s", dataSource.getPath()));
for (String module : snapshot.getCancelledDataSourceIngestModules()) {
LOGGER.log(Level.WARNING, String.format("%s ingest module cancelled for %s", module, dataSource.getPath()));
}
}
LOGGER.log(Level.INFO, "Analysis of data source completed");
} else {
LOGGER.log(Level.WARNING, "Analysis of data source cancelled");
IngestJob.CancellationReason cancellationReason = snapshot.getCancellationReason();
if (IngestJob.CancellationReason.NOT_CANCELLED != cancellationReason && IngestJob.CancellationReason.USER_CANCELLED != cancellationReason) {
throw new AnalysisStartupException(String.format("Analysis cancelled due to %s for %s", cancellationReason.getDisplayName(), dataSource.getPath()));
}
}
}
} else if (!ingestJobStartResult.getModuleErrors().isEmpty()) {
for (IngestModuleError error : ingestJobStartResult.getModuleErrors()) {
LOGGER.log(Level.SEVERE, String.format("%s ingest module startup error for %s", error.getModuleDisplayName(), dataSource.getPath()), error.getThrowable());
}
LOGGER.log(Level.SEVERE, "Failed to analyze data source due to ingest job startup error");
throw new AnalysisStartupException(String.format("Error(s) during ingest module startup for %s", dataSource.getPath()));
} else {
LOGGER.log(Level.SEVERE, String.format("Ingest manager ingest job start error for %s", dataSource.getPath()), ingestJobStartResult.getStartupException());
throw new AnalysisStartupException("Ingest manager error starting job", ingestJobStartResult.getStartupException());
}
} else {
for (String warning : settingsWarnings) {
LOGGER.log(Level.SEVERE, "Ingest job settings error for {0}: {1}", new Object[]{dataSource.getPath(), warning});
}
LOGGER.log(Level.SEVERE, "Failed to analyze data source due to settings errors");
throw new AnalysisStartupException("Error(s) in ingest job settings");
}
}
} finally {
IngestManager.getInstance().removeIngestJobEventListener(ingestJobEventListener);
}
}
/**
* Creates a case folder path. Does not create the folder described by
* the path.
*
* @param caseFoldersPath The root case folders path.
* @param caseName The name of the case.
*
* @return A case folder path with a time stamp suffix.
*/
Path createCaseFolderPath(Path caseFoldersPath, String caseName) {
String folderName = caseName + "_" + TimeStampUtils.createTimeStamp();
return Paths.get(caseFoldersPath.toString(), folderName);
}
/**
* Searches a given folder for the most recently modified case folder
* for a case.
*
* @param folderToSearch The folder to be searched.
* @param caseName The name of the case for which a case folder is to be
* found.
*
* @return The path of the case folder, or null if it is not found.
*/
Path findCaseDirectory(Path folderToSearch, String caseName) {
File searchFolder = new File(folderToSearch.toString());
if (!searchFolder.isDirectory()) {
return null;
}
Path caseFolderPath = null;
String[] candidateFolders = searchFolder.list(new CaseFolderFilter(caseName));
long mostRecentModified = 0;
for (String candidateFolder : candidateFolders) {
File file = new File(candidateFolder);
if (file.lastModified() >= mostRecentModified) {
mostRecentModified = file.lastModified();
caseFolderPath = Paths.get(folderToSearch.toString(), file.getPath());
}
}
return caseFolderPath;
}
/**
* An ingest job event listener that allows the job processing task to
* block until the analysis of a data source by the data source level
* and file level ingest modules is completed.
* <p>
* Note that the ingest job can spawn "child" ingest jobs (e.g., if an
* embedded virtual machine is found), so the job processing task must
* remain blocked until ingest is no longer running.
*/
private class IngestJobEventListener implements PropertyChangeListener {
/**
* Listens for local ingest job completed or cancelled events and
* notifies the job processing thread when such an event occurs and
* there are no "child" ingest jobs running.
*
* @param event
*/
@Override
public void propertyChange(PropertyChangeEvent event) {
if (AutopsyEvent.SourceType.LOCAL == ((AutopsyEvent) event).getSourceType()) {
String eventType = event.getPropertyName();
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) || eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())) {
synchronized (ingestLock) {
ingestLock.notify();
}
}
}
}
};
/**
* A data source processor progress monitor does nothing. There is
* currently no mechanism for showing or recording data source processor
* progress during an ingest job.
*/
private class DoNothingDSPProgressMonitor implements DataSourceProcessorProgressMonitor {
/**
* Does nothing.
*
* @param indeterminate
*/
@Override
public void setIndeterminate(final boolean indeterminate) {
}
/**
* Does nothing.
*
* @param progress
*/
@Override
public void setProgress(final int progress) {
}
/**
* Does nothing.
*
* @param text
*/
@Override
public void setProgressText(final String text) {
}
}
/**
* Exception type thrown when there is a problem analyzing a data source
* with data source level and file level ingest modules for an ingest
* job.
*/
private final class AnalysisStartupException extends Exception {
private static final long serialVersionUID = 1L;
private AnalysisStartupException(String message) {
super(message);
}
private AnalysisStartupException(String message, Throwable cause) {
super(message, cause);
}
}
}
private static class CaseFolderFilter implements FilenameFilter {
private final String caseName;
private final static String CASE_METADATA_EXT = CaseMetadata.getFileExtension();
CaseFolderFilter(String caseName) {
this.caseName = caseName;
}
@Override
public boolean accept(File folder, String fileName) {
File file = new File(folder, fileName);
if (fileName.length() > TimeStampUtils.getTimeStampLength() && file.isDirectory()) {
if (TimeStampUtils.endsWithTimeStamp(fileName)) {
if (null != caseName) {
String fileNamePrefix = fileName.substring(0, fileName.length() - TimeStampUtils.getTimeStampLength());
if (fileNamePrefix.equals(caseName)) {
return hasCaseMetadataFile(file);
}
} else {
return hasCaseMetadataFile(file);
}
}
}
return false;
}
/**
* Determines whether or not there is a case metadata file in a given
* folder.
*
* @param folder The file object representing the folder to search.
*
* @return True or false.
*/
private static boolean hasCaseMetadataFile(File folder) {
for (File file : folder.listFiles()) {
if (file.getName().toLowerCase().endsWith(CASE_METADATA_EXT) && file.isFile()) {
return true;
}
}
return false;
}
}
}

View File

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="nodeScrollPane" alignment="1" pref="864" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="nodeScrollPane" alignment="0" pref="421" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="nodeScrollPane">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="nodePanel">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 100]"/>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="outputPathTextField" min="-2" pref="630" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="browseOutputFolderButton" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="bnEditIngestSettings" alignment="0" min="-2" pref="155" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="jLabelSelectOutputFolder" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="jLabelInvalidResultsFolder" min="-2" pref="544" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace pref="355" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="40" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabelSelectOutputFolder" alignment="3" min="-2" pref="21" max="-2" attributes="0"/>
<Component id="jLabelInvalidResultsFolder" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="browseOutputFolderButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="outputPathTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="25" max="-2" attributes="0"/>
<Component id="bnEditIngestSettings" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="389" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="bnEditIngestSettings">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.bnEditIngestSettings.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.bnEditIngestSettings.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="actionCommand" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commandlineingest/Bundle.properties" key="CommandLineIngestSettingsPanel.bnEditIngestSettings.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bnEditIngestSettingsActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="browseOutputFolderButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.browseOutputFolderButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commandlineingest/Bundle.properties" key="CommandLineIngestSettingsPanel.browseOutputFolderButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseOutputFolderButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="outputPathTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.outputPathTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.outputPathTextField.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jLabelInvalidResultsFolder">
<Properties>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.jLabelInvalidResultsFolder.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commandlineingest/Bundle.properties" key="CommandLineIngestSettingsPanel.jLabelInvalidResultsFolder.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
</Component>
<Component class="javax.swing.JLabel" name="jLabelSelectOutputFolder">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="viking/commandline/Bundle.properties" key="CommandLineIngestSettingsPanel.jLabelSelectOutputFolder.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="verticalAlignment" type="int" value="3"/>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commandlineingest/Bundle.properties" key="CommandLineIngestSettingsPanel.jLabelSelectOutputFolder.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,395 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.io.File;
import java.nio.file.Files;
import java.util.List;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.ingest.IngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestJobSettingsPanel;
import java.nio.file.Paths;
import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.coreutils.Logger;
/**
* Configuration panel for auto ingest settings.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
public class CommandLineIngestSettingsPanel extends javax.swing.JPanel {
private final CommandLineIngestSettingsPanelController controller;
private final JFileChooser fc = new JFileChooser();
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(CommandLineIngestSettingsPanel.class.getName());
/**
* Creates new form AutoIngestSettingsPanel
*
* @param theController Controller to notify of changes.
*/
public CommandLineIngestSettingsPanel(CommandLineIngestSettingsPanelController theController) {
controller = theController;
initComponents();
load(true);
outputPathTextField.getDocument().addDocumentListener(new MyDocumentListener());
jLabelInvalidResultsFolder.setText("");
}
private class MyDocumentListener implements DocumentListener {
@Override
public void changedUpdate(DocumentEvent e) {
valid();
controller.changed();
}
@Override
public void removeUpdate(DocumentEvent e) {
valid();
controller.changed();
}
@Override
public void insertUpdate(DocumentEvent e) {
valid();
controller.changed();
}
};
/**
* Load mode from persistent storage.
*
* @param inStartup True if we're doing the initial population of the UI
*/
final void load(boolean inStartup) {
String results = org.sleuthkit.autopsy.commandlineingest.UserPreferences.getCommandLineModeResultsFolder();
if (results != null) {
outputPathTextField.setText(results);
} else {
outputPathTextField.setText("");
}
valid();
}
/**
* Save mode to persistent storage.
*/
void store() {
String resultsFolderPath = getNormalizedFolderPath(outputPathTextField.getText().trim());
org.sleuthkit.autopsy.commandlineingest.UserPreferences.setCommandLineModeResultsFolder(resultsFolderPath);
}
/**
* Validate current panel settings.
*/
boolean valid() {
if (validateResultsPath()) {
return true;
}
return false;
}
/**
* Normalizes a path to make sure there are no "space" characters at the end
*
* @param path Path to a directory
*
* @return Path without "space" characters at the end
*/
String normalizePath(String path) {
while (path.length() > 0) {
if (path.charAt(path.length() - 1) == ' ') {
path = path.substring(0, path.length() - 1);
} else {
break;
}
}
return path;
}
/**
* Validates that a path is valid and points to a folder.
*
* @param path A path to be validated
*
* @return boolean returns true if valid and points to a folder, false
* otherwise
*/
boolean isFolderPathValid(String path) {
try {
File file = new File(normalizePath(path));
// check if it's a symbolic link
if (Files.isSymbolicLink(file.toPath())) {
return true;
}
// local folder
if (file.exists() && file.isDirectory()) {
return true;
}
} catch (Exception ex) {
// Files.isSymbolicLink (and other "files" methods) throw exceptions on seemingly innocent inputs.
// For example, it will throw an exception when either " " is last character in path or
// a path starting with ":".
// We can just ignore these exceptions as they occur in process of user typing in the path.
return false;
}
return false;
}
/**
* Returns a path that was normalized by file system.
*
* @param path A path to be normalized. Normalization occurs inside a call
* to new File().
*
* @return String returns normalized OS path
*/
String getNormalizedFolderPath(String path) {
// removes "/", "\", and " " characters at the end of path string.
// normalizePath() removes spaces at the end of path and a call to "new File()"
// internally formats the path string to remove "/" and "\" characters at the end of path.
File file = new File(normalizePath(path));
return file.getPath();
}
/**
* Validate results path. Display warnings if invalid.
*/
boolean validateResultsPath() {
String outputPath = outputPathTextField.getText().trim();
if (outputPath.isEmpty()) {
jLabelInvalidResultsFolder.setVisible(true);
jLabelInvalidResultsFolder.setText(NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.ResultsDirectoryUnspecified"));
return false;
}
if (!isFolderPathValid(outputPath)) {
jLabelInvalidResultsFolder.setVisible(true);
jLabelInvalidResultsFolder.setText(NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.PathInvalid"));
return false;
}
if (false == permissionsAppropriate(outputPath)) {
jLabelInvalidResultsFolder.setVisible(true);
jLabelInvalidResultsFolder.setText(NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.CannotAccess")
+ " " + outputPath + " "
+ NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.CheckPermissions"));
return false;
}
jLabelInvalidResultsFolder.setVisible(false);
return true;
}
private void displayIngestJobSettingsPanel() {
this.getParent().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
IngestJobSettings ingestJobSettings = new IngestJobSettings(org.sleuthkit.autopsy.commandlineingest.UserPreferences.getCommandLineModeIngestModuleContextString());
showWarnings(ingestJobSettings);
IngestJobSettingsPanel ingestJobSettingsPanel = new IngestJobSettingsPanel(ingestJobSettings);
add(ingestJobSettingsPanel, BorderLayout.PAGE_START);
if (JOptionPane.showConfirmDialog(this, ingestJobSettingsPanel, "Ingest Module Configuration", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE) == JOptionPane.OK_OPTION) {
// store the updated settings
ingestJobSettings = ingestJobSettingsPanel.getSettings();
ingestJobSettings.save();
showWarnings(ingestJobSettings);
}
this.getParent().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
private static void showWarnings(IngestJobSettings ingestJobSettings) {
List<String> warnings = ingestJobSettings.getWarnings();
if (warnings.isEmpty() == false) {
StringBuilder warningMessage = new StringBuilder();
for (String warning : warnings) {
warningMessage.append(warning).append("\n");
}
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), warningMessage.toString());
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
nodeScrollPane = new javax.swing.JScrollPane();
nodePanel = new javax.swing.JPanel();
bnEditIngestSettings = new javax.swing.JButton();
browseOutputFolderButton = new javax.swing.JButton();
outputPathTextField = new javax.swing.JTextField();
jLabelInvalidResultsFolder = new javax.swing.JLabel();
jLabelSelectOutputFolder = new javax.swing.JLabel();
nodeScrollPane.setMinimumSize(new java.awt.Dimension(0, 0));
nodePanel.setMinimumSize(new java.awt.Dimension(100, 100));
org.openide.awt.Mnemonics.setLocalizedText(bnEditIngestSettings, org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.bnEditIngestSettings.text")); // NOI18N
bnEditIngestSettings.setToolTipText(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.bnEditIngestSettings.toolTipText")); // NOI18N
bnEditIngestSettings.setActionCommand(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.bnEditIngestSettings.text")); // NOI18N
bnEditIngestSettings.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
bnEditIngestSettingsActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(browseOutputFolderButton, org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.browseOutputFolderButton.text")); // NOI18N
browseOutputFolderButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
browseOutputFolderButtonActionPerformed(evt);
}
});
outputPathTextField.setText(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.outputPathTextField.text")); // NOI18N
outputPathTextField.setToolTipText(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.outputPathTextField.toolTipText")); // NOI18N
jLabelInvalidResultsFolder.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(jLabelInvalidResultsFolder, org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.jLabelInvalidResultsFolder.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jLabelSelectOutputFolder, org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.jLabelSelectOutputFolder.text")); // NOI18N
jLabelSelectOutputFolder.setVerticalAlignment(javax.swing.SwingConstants.BOTTOM);
javax.swing.GroupLayout nodePanelLayout = new javax.swing.GroupLayout(nodePanel);
nodePanel.setLayout(nodePanelLayout);
nodePanelLayout.setHorizontalGroup(
nodePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(nodePanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(nodePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(nodePanelLayout.createSequentialGroup()
.addComponent(outputPathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 630, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(browseOutputFolderButton))
.addComponent(bnEditIngestSettings, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(nodePanelLayout.createSequentialGroup()
.addComponent(jLabelSelectOutputFolder)
.addGap(18, 18, 18)
.addComponent(jLabelInvalidResultsFolder, javax.swing.GroupLayout.PREFERRED_SIZE, 544, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap(355, Short.MAX_VALUE))
);
nodePanelLayout.setVerticalGroup(
nodePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(nodePanelLayout.createSequentialGroup()
.addGap(40, 40, 40)
.addGroup(nodePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabelSelectOutputFolder, javax.swing.GroupLayout.PREFERRED_SIZE, 21, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabelInvalidResultsFolder))
.addGap(1, 1, 1)
.addGroup(nodePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(browseOutputFolderButton)
.addComponent(outputPathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(25, 25, 25)
.addComponent(bnEditIngestSettings)
.addContainerGap(389, Short.MAX_VALUE))
);
browseOutputFolderButton.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.browseOutputFolderButton.text")); // NOI18N
jLabelInvalidResultsFolder.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.jLabelInvalidResultsFolder.text")); // NOI18N
jLabelSelectOutputFolder.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(CommandLineIngestSettingsPanel.class, "CommandLineIngestSettingsPanel.jLabelSelectOutputFolder.text")); // NOI18N
nodeScrollPane.setViewportView(nodePanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(nodeScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 864, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(nodeScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 421, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
private void browseOutputFolderButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseOutputFolderButtonActionPerformed
String oldText = outputPathTextField.getText().trim();
// set the current directory of the FileChooser if the oldText is valid
File currentDir = new File(oldText);
if (currentDir.exists()) {
fc.setCurrentDirectory(currentDir);
}
fc.setDialogTitle("Select case output folder:");
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int retval = fc.showOpenDialog(this);
if (retval == JFileChooser.APPROVE_OPTION) {
String path = fc.getSelectedFile().getPath();
outputPathTextField.setText(path);
valid();
controller.changed();
}
}//GEN-LAST:event_browseOutputFolderButtonActionPerformed
private void bnEditIngestSettingsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_bnEditIngestSettingsActionPerformed
displayIngestJobSettingsPanel();
}//GEN-LAST:event_bnEditIngestSettingsActionPerformed
boolean permissionsAppropriate(String path) {
return FileUtil.hasReadWriteAccess(Paths.get(path));
}
private void resetUI() {
load(true);
controller.changed();
}
void setEnabledState(boolean enabled) {
bnEditIngestSettings.setEnabled(enabled);
browseOutputFolderButton.setEnabled(enabled);
jLabelInvalidResultsFolder.setEnabled(enabled);
jLabelSelectOutputFolder.setEnabled(enabled);
outputPathTextField.setEnabled(enabled);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton bnEditIngestSettings;
private javax.swing.JButton browseOutputFolderButton;
private javax.swing.JLabel jLabelInvalidResultsFolder;
private javax.swing.JLabel jLabelSelectOutputFolder;
private javax.swing.JPanel nodePanel;
private javax.swing.JScrollPane nodeScrollPane;
private javax.swing.JTextField outputPathTextField;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,132 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javax.swing.JComponent;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.HelpCtx;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger;
@OptionsPanelController.TopLevelRegistration(categoryName = "#OptionsCategory_Name_Command_Line_Ingest",
iconBase = "org/sleuthkit/autopsy/images/command_line_icon.png",
position = 17,
keywords = "#OptionsCategory_Keywords_Command_Line_Ingest_Settings",
keywordsCategory = "Command Line Ingest")
public final class CommandLineIngestSettingsPanelController extends OptionsPanelController {
private CommandLineIngestSettingsPanel panel;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private boolean changed;
private static final Logger logger = Logger.getLogger(CommandLineIngestSettingsPanelController.class.getName());
@Override
public void update() {
getPanel().load(false);
changed = false;
}
@Override
public void applyChanges() {
getPanel().store();
changed = false;
}
@Override
public void cancel() {
}
@Override
public boolean isValid() {
return getPanel().valid();
}
@Override
public boolean isChanged() {
return changed;
}
@Override
public HelpCtx getHelpCtx() {
return null;
}
@Override
public JComponent getComponent(Lookup masterLookup) {
return getPanel();
}
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
if (pcs.getPropertyChangeListeners().length == 0) {
pcs.addPropertyChangeListener(l);
}
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
/**
* Note the NetBeans Framework does not appear to call this at all. We
* are using NetBeans 7.3.1 Build 201306052037. Perhaps in a future
* version of the Framework this will be resolved, but for now, simply
* don't unregister anything and add one time only in the
* addPropertyChangeListener() method above.
*/
}
private CommandLineIngestSettingsPanel getPanel() {
if (panel == null) {
panel = new CommandLineIngestSettingsPanel(this);
panel.setSize(750, 600); //makes the panel large enough to hide the scroll bar
}
return panel;
}
void changed() {
if (!changed) {
changed = true;
try {
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
} catch (Exception e) {
logger.log(Level.SEVERE, "GeneralOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
}
try {
pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
} catch (Exception e) {
logger.log(Level.SEVERE, "GeneralOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
}
}

View File

@ -0,0 +1,168 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.io.File;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.netbeans.api.sendopts.CommandException;
import org.netbeans.spi.sendopts.Env;
import org.netbeans.spi.sendopts.Option;
import org.netbeans.spi.sendopts.OptionProcessor;
import org.openide.util.lookup.ServiceProvider;
/**
* This class can be used to add command line options to Autopsy
*/
@ServiceProvider(service = OptionProcessor.class)
public class CommandLineOptionProcessor extends OptionProcessor {
private static final Logger logger = Logger.getLogger(CommandLineOptionProcessor.class.getName());
private final Option pathToDataSourceOption = Option.optionalArgument('l', "inputPath");
private final Option caseNameOption = Option.optionalArgument('2', "caseName");
private final Option runFromCommandLineOption = Option.optionalArgument('3', "runFromCommandLine");
private String pathToDataSource;
private String baseCaseName;
private boolean runFromCommandLine = false;
@Override
protected Set<Option> getOptions() {
Set<Option> set = new HashSet<>();
set.add(pathToDataSourceOption);
set.add(caseNameOption);
set.add(runFromCommandLineOption);
return set;
}
@Override
protected void process(Env env, Map<Option, String[]> values) throws CommandException {
logger.log(Level.INFO, "Processing Autopsy command line options"); //NON-NLS
System.out.println("Processing Autopsy command line options using CommandLineOptionProcessor");
if (values.containsKey(pathToDataSourceOption) && values.containsKey(caseNameOption) && values.containsKey(runFromCommandLineOption)) {
// parse input parameters
String inputPath;
String inputCaseName;
String modeString;
if (values.size() < 3) {
logger.log(Level.SEVERE, "Insufficient number of input arguments. Exiting");
System.out.println("Insufficient number of input arguments. Exiting");
this.runFromCommandLine = false;
return;
} else {
String[] argDirs = values.get(pathToDataSourceOption);
if (argDirs.length < 1) {
logger.log(Level.SEVERE, "Missing argument 'inputPath'. Exiting");
System.out.println("Missing argument 'inputPath'. Exiting");
this.runFromCommandLine = false;
return;
}
inputPath = argDirs[0];
argDirs = values.get(caseNameOption);
if (argDirs.length < 1) {
logger.log(Level.SEVERE, "Missing argument 'caseName'. Exiting");
System.out.println("Missing argument 'caseName'. Exiting");
this.runFromCommandLine = false;
return;
}
inputCaseName = argDirs[0];
argDirs = values.get(runFromCommandLineOption);
if (argDirs.length < 1) {
logger.log(Level.SEVERE, "Missing argument 'runFromCommandLine'. Exiting");
System.out.println("Missing argument 'runFromCommandLine'. Exiting");
this.runFromCommandLine = false;
return;
}
modeString = argDirs[0];
// verify inputs
if (modeString == null || modeString.isEmpty()) {
this.runFromCommandLine = false;
System.out.println("runFromCommandLine argument is empty");
return;
}
if (modeString.equalsIgnoreCase("true")) {
this.runFromCommandLine = true;
}
System.out.println("runFromCommandLine = " + this.runFromCommandLine);
}
// verify inputs
if (inputPath == null || inputPath.isEmpty() || !(new File(inputPath).exists())) {
logger.log(Level.SEVERE, "Input file {0} doesn''t exist. Exiting", inputPath);
System.out.println("Input file " + inputPath + " doesn't exist. Exiting");
this.runFromCommandLine = false;
return;
}
if (inputCaseName == null || inputCaseName.isEmpty()) {
logger.log(Level.SEVERE, "Case name argument is empty. Exiting");
System.out.println("Case name argument is empty. Exiting");
this.runFromCommandLine = false;
return;
}
// save the inputs
this.pathToDataSource = inputPath;
this.baseCaseName = inputCaseName;
logger.log(Level.INFO, "Input file = {0}", this.pathToDataSource); //NON-NLS
logger.log(Level.INFO, "Case name = {0}", this.baseCaseName); //NON-NLS
logger.log(Level.INFO, "runFromCommandLine = {0}", this.runFromCommandLine); //NON-NLS
System.out.println("Input file = " + this.pathToDataSource);
System.out.println("Case name = " + this.baseCaseName);
System.out.println("runFromCommandLine = " + this.runFromCommandLine);
} else {
System.out.println("Missing input arguments for CommandLineOptionProcessor. Exiting");
logger.log(Level.SEVERE, "Missing input arguments. Exiting");
}
}
/**
* Returns user specified path to data source
*
* @return the inputPath
*/
String getPathToDataSource() {
return pathToDataSource;
}
/**
* Returns user specified case name
*
* @return the inputCaseName
*/
String getBaseCaseName() {
return baseCaseName;
}
/**
* Returns whether Autopsy should be running in command line mode or not.
*
* @return true if running in command line mode, false otherwise.
*/
public boolean isRunFromCommandLine() {
return runFromCommandLine;
}
}

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 65]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[403, 65]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="20" max="-2" attributes="0"/>
<Component id="jLabel1" pref="305" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="27" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="jLabel1">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="18" style="0"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/commandlineingest/Bundle.properties" key="CommandLinePanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,71 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import javax.swing.JPanel;
/**
* Panel that is displayed when Autopsy is running from command line.
*/
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
class CommandLinePanel extends JPanel {
CommandLinePanel() {
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jLabel1 = new javax.swing.JLabel();
setMinimumSize(new java.awt.Dimension(0, 65));
setPreferredSize(new java.awt.Dimension(403, 65));
jLabel1.setFont(new java.awt.Font("Tahoma", 0, 18)); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(CommandLinePanel.class, "CommandLinePanel.jLabel1.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(20, 20, 20)
.addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, 305, Short.MAX_VALUE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jLabel1)
.addContainerGap(27, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel jLabel1;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,57 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.awt.Dimension;
import javax.swing.JDialog;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.StartupWindowInterface;
/**
* Implementation of startup window for running Autopsy from command line.
*
* IMPORTANT: We do NOT want to register this class as ServiceProvider. We want only
* the default Autopsy StartupWindow to be found via
* Lookup.getDefault().lookupAll(StartupWindowInterface.class);
*/
public class CommandLineStartupWindow extends JDialog implements StartupWindowInterface {
private static final String TITLE = NbBundle.getMessage(CommandLineStartupWindow.class, "CommandLineStartupWindow.title.text");
private static final Dimension DIMENSIONS = new Dimension(300, 50);
public CommandLineStartupWindow() {
super(WindowManager.getDefault().getMainWindow(), TITLE, true);
setSize(DIMENSIONS);
add(new CommandLinePanel());
pack();
setResizable(false);
}
@Override
public void open() {
setLocationRelativeTo(WindowManager.getDefault().getMainWindow());
setVisible(true);
}
@Override
public void close() {
setVisible(false);
}
}

View File

@ -0,0 +1,66 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback.DataSourceProcessorResult;
import org.sleuthkit.datamodel.Content;
class DataSource {
private final String deviceId;
private final Path path;
private DataSourceProcessorResult resultCode;
private List<String> errorMessages;
private List<Content> content;
DataSource(String deviceId, Path path) {
this.deviceId = deviceId;
this.path = path;
}
String getDeviceId() {
return deviceId;
}
Path getPath() {
return this.path;
}
synchronized void setDataSourceProcessorOutput(DataSourceProcessorResult result, List<String> errorMessages, List<Content> content) {
this.resultCode = result;
this.errorMessages = new ArrayList<>(errorMessages);
this.content = new ArrayList<>(content);
}
synchronized DataSourceProcessorResult getResultDataSourceProcessorResultCode() {
return resultCode;
}
synchronized List<String> getDataSourceProcessorErrorMessages() {
return new ArrayList<>(errorMessages);
}
synchronized List<Content> getContent() {
return new ArrayList<>(content);
}
}

View File

@ -0,0 +1,121 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import java.nio.file.Path;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor;
import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException;
/**
* A utility class to find Data Source Processors
*/
final class DataSourceProcessorUtility {
private DataSourceProcessorUtility() {
}
/**
* A utility method to find all Data Source Processors (DSP) that are able
* to process the input data source. Only the DSPs that implement
* AutoIngestDataSourceProcessor interface are used.
*
* @param dataSourcePath Full path to the data source
* @return Hash map of all DSPs that can process the data source along with
* their confidence score
* @throws
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/
static Map<AutoIngestDataSourceProcessor, Integer> getDataSourceProcessorForFile(Path dataSourcePath, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException {
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = new HashMap<>();
for (AutoIngestDataSourceProcessor processor : processorCandidates) {
int confidence = processor.canProcess(dataSourcePath);
if (confidence > 0) {
validDataSourceProcessorsMap.put(processor, confidence);
}
}
return validDataSourceProcessorsMap;
}
/**
* A utility method to find all Data Source Processors (DSP) that are able
* to process the input data source. Only the DSPs that implement
* AutoIngestDataSourceProcessor interface are used. Returns ordered list of
* data source processors. DSPs are ordered in descending order from highest
* confidence to lowest.
*
* @param dataSourcePath Full path to the data source
*
* @return Ordered list of data source processors. DSPs are ordered in
* descending order from highest confidence to lowest.
*
* @throws
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/
static List<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath) throws AutoIngestDataSourceProcessorException {
// lookup all AutomatedIngestDataSourceProcessors
Collection<? extends AutoIngestDataSourceProcessor> processorCandidates = Lookup.getDefault().lookupAll(AutoIngestDataSourceProcessor.class);
return getOrderedListOfDataSourceProcessors(dataSourcePath, processorCandidates);
}
/**
* A utility method to find all Data Source Processors (DSP) that are able
* to process the input data source. Only the DSPs that implement
* AutoIngestDataSourceProcessor interface are used. Returns ordered list of
* data source processors. DSPs are ordered in descending order from highest
* confidence to lowest.
*
* @param dataSourcePath Full path to the data source
* @param processorCandidates Collection of AutoIngestDataSourceProcessor objects to use
*
* @return Ordered list of data source processors. DSPs are ordered in
* descending order from highest confidence to lowest.
*
* @throws
* org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor.AutoIngestDataSourceProcessorException
*/
static List<AutoIngestDataSourceProcessor> getOrderedListOfDataSourceProcessors(Path dataSourcePath, Collection<? extends AutoIngestDataSourceProcessor> processorCandidates) throws AutoIngestDataSourceProcessorException {
Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap = getDataSourceProcessorForFile(dataSourcePath, processorCandidates);
return orderDataSourceProcessorsByConfidence(validDataSourceProcessorsMap);
}
/**
* A utility method to get an ordered list of data source processors. DSPs
* are ordered in descending order from highest confidence to lowest.
*
* @param validDataSourceProcessorsMap Hash map of all DSPs that can process
* the data source along with their confidence score
* @return Ordered list of data source processors
*/
static List<AutoIngestDataSourceProcessor> orderDataSourceProcessorsByConfidence(Map<AutoIngestDataSourceProcessor, Integer> validDataSourceProcessorsMap) {
List<AutoIngestDataSourceProcessor> validDataSourceProcessors = validDataSourceProcessorsMap.entrySet().stream()
.sorted(Map.Entry.<AutoIngestDataSourceProcessor, Integer>comparingByValue().reversed())
.map(Map.Entry::getKey)
.collect(Collectors.toList());
return validDataSourceProcessors;
}
}

View File

@ -0,0 +1,64 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.commandlineingest;
import org.openide.util.NbPreferences;
/**
* Provides convenient access to a UserPreferences node for user preferences
* with default values.
*/
public final class UserPreferences {
private static final java.util.prefs.Preferences preferences = NbPreferences.forModule(UserPreferences.class);
private static final String COMMAND_LINE_MODE_RESULTS_FOLDER = "CommandLineModeResultsFolder"; // NON-NLS
private static final String COMMAND_LINE_MODE_CONTEXT_STRING = "CommandLineModeContext"; // NON-NLS
// Prevent instantiation.
private UserPreferences() {
}
/**
* Get results folder for command line mode from persistent storage.
*
* @return String Selected output folder.
*/
public static String getCommandLineModeResultsFolder() {
return preferences.get(COMMAND_LINE_MODE_RESULTS_FOLDER, "");
}
/**
* Set results folder for command line mode from persistent storage.
*
* @param folder Selected output folder.
*/
public static void setCommandLineModeResultsFolder(String folder) {
preferences.put(COMMAND_LINE_MODE_RESULTS_FOLDER, folder);
}
/**
* Get context string for command line mode ingest module settings.
*
* @return String Context string for command line mode ingest module
* settings.
*/
public static String getCommandLineModeIngestModuleContextString() {
return COMMAND_LINE_MODE_CONTEXT_STRING;
}
}

View File

@ -32,8 +32,10 @@ GstVideoPanel.progress.buffering=Buffering...
GstVideoPanel.progressLabel.bufferingErr=Error buffering file
GstVideoPanel.progress.infoLabel.updateErr=Error updating video progress: {0}
GstVideoPanel.ExtractMedia.progress.buffering=Buffering {0}
Html_text_display_error=The HTML text cannot be displayed, it may not be correctly formed HTML.
HtmlPanel_showImagesToggleButton_hide=Hide Images
HtmlPanel_showImagesToggleButton_show=Show Images
HtmlViewer_file_error=This file is missing or unreadable.
MediaFileViewer.AccessibleContext.accessibleDescription=
MediaFileViewer.title=Media
MediaFileViewer.toolTip=Displays supported multimedia files (images, videos, audio)

View File

@ -92,20 +92,24 @@ final class HtmlPanel extends javax.swing.JPanel {
*/
@Messages({
"HtmlPanel_showImagesToggleButton_show=Show Images",
"HtmlPanel_showImagesToggleButton_hide=Hide Images"
"HtmlPanel_showImagesToggleButton_hide=Hide Images",
"Html_text_display_error=The HTML text cannot be displayed, it may not be correctly formed HTML.",
})
private void refresh() {
if (false == htmlText.isEmpty()) {
if (showImagesToggleButton.isSelected()) {
showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_hide());
this.htmlbodyTextPane.setText(wrapInHtmlBody(htmlText));
} else {
showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_show());
this.htmlbodyTextPane.setText(wrapInHtmlBody(cleanseHTML(htmlText)));
try {
if (showImagesToggleButton.isSelected()) {
showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_hide());
this.htmlbodyTextPane.setText(wrapInHtmlBody(htmlText));
} else {
showImagesToggleButton.setText(Bundle.HtmlPanel_showImagesToggleButton_show());
this.htmlbodyTextPane.setText(wrapInHtmlBody(cleanseHTML(htmlText)));
}
showImagesToggleButton.setEnabled(true);
htmlbodyTextPane.setCaretPosition(0);
} catch(Exception ex) {
this.htmlbodyTextPane.setText(wrapInHtmlBody(Bundle.Html_text_display_error()));
}
htmlbodyTextPane.setCaretPosition(0);
showImagesToggleButton.setEnabled(true);
}
}

View File

@ -23,6 +23,7 @@ import java.awt.Cursor;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
@ -56,6 +57,9 @@ final class HtmlViewer extends javax.swing.JPanel implements FileTypeViewer {
*
* @return The text content of the file.
*/
@NbBundle.Messages({
"HtmlViewer_file_error=This file is missing or unreadable.",
})
private String getHtmlText(AbstractFile abstractFile) {
try {
int fileSize = (int) abstractFile.getSize();
@ -65,9 +69,8 @@ final class HtmlViewer extends javax.swing.JPanel implements FileTypeViewer {
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("Unable to read from file '%s' (id=%d).",
abstractFile.getName(), abstractFile.getId()), ex);
return String.format("<p>%s</p>", Bundle.HtmlViewer_file_error());
}
return null;
}
/**

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

View File

@ -1,9 +1,8 @@
/*
*
/*
* Autopsy Forensic Browser
*
* Copyright 2012-2018 Basis Technology Corp.
* Project Contact/Architect: carrier <at> sleuthkit <dot> org
* Copyright 2018-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,21 +16,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.case_uco;
package org.sleuthkit.autopsy.report.caseuco;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.logging.Level;
import javax.swing.JPanel;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.SimpleTimeZone;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -39,44 +37,30 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.report.GeneralReportModule;
import org.sleuthkit.autopsy.report.ReportProgressPanel;
import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus;
import org.sleuthkit.datamodel.*;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* ReportCaseUco generates a report in the CASE-UCO format. It saves basic
file info like full caseDirPath, name, MIME type, times, and hash.
* Generates CASE-UCO report file for a data source
*/
class ReportCaseUco implements GeneralReportModule {
public final class CaseUcoFormatExporter {
private static final Logger logger = Logger.getLogger(ReportCaseUco.class.getName());
private static ReportCaseUco instance = null;
private ReportCaseUcoConfigPanel configPanel;
private static final Logger logger = Logger.getLogger(CaseUcoFormatExporter.class.getName());
private static final String REPORT_FILE_NAME = "CASE_UCO_output.json-ld";
// Hidden constructor for the report
private ReportCaseUco() {
}
// Get the default implementation of this report
public static synchronized ReportCaseUco getDefault() {
if (instance == null) {
instance = new ReportCaseUco();
}
return instance;
private CaseUcoFormatExporter() {
}
/**
* Generates a CASE-UCO format report.
* Generates CASE-UCO report for the selected data source.
*
* @param baseReportDir caseDirPath to save the report
* @param progressPanel panel to update the report's progress
* @param selectedDataSourceId Object ID of the data source
* @param reportOutputPath Full path to directory where to save CASE-UCO
* report file
* @param progressPanel ReportProgressPanel to update progress
*/
@NbBundle.Messages({
"ReportCaseUco.notInitialized=CASE-UCO settings panel has not been initialized",
"ReportCaseUco.noDataSourceSelected=No data source selected for CASE-UCO report",
"ReportCaseUco.noCaseOpen=Unable to open currect case",
"ReportCaseUco.unableToCreateDirectories=Unable to create directory for CASE-UCO report",
"ReportCaseUco.initializing=Creating directories...",
@ -85,24 +69,8 @@ class ReportCaseUco implements GeneralReportModule {
"ReportCaseUco.processing=Saving files in CASE-UCO format...",
"ReportCaseUco.srcModuleName.text=CASE-UCO Report"
})
@Override
@SuppressWarnings("deprecation")
public void generateReport(String baseReportDir, ReportProgressPanel progressPanel) {
if (configPanel == null) {
logger.log(Level.SEVERE, "CASE-UCO settings panel has not been initialized"); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_notInitialized());
progressPanel.complete(ReportStatus.ERROR);
return;
}
Long selectedDataSourceId = configPanel.getSelectedDataSourceId();
if (selectedDataSourceId == ReportCaseUcoConfigPanel.NO_DATA_SOURCE_SELECTED) {
logger.log(Level.SEVERE, "No data source selected for CASE-UCO report"); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_noDataSourceSelected());
progressPanel.complete(ReportStatus.ERROR);
return;
}
public static void generateReport(Long selectedDataSourceId, String reportOutputPath, ReportProgressPanel progressPanel) {
// Start the progress bar and setup the report
progressPanel.setIndeterminate(false);
@ -111,14 +79,13 @@ class ReportCaseUco implements GeneralReportModule {
// Create the JSON generator
JsonFactory jsonGeneratorFactory = new JsonFactory();
String reportPath = baseReportDir + getRelativeFilePath();
java.io.File reportFile = Paths.get(reportPath).toFile();
java.io.File reportFile = Paths.get(reportOutputPath).toFile();
try {
Files.createDirectories(Paths.get(reportFile.getParent()));
} catch (IOException ex) {
logger.log(Level.SEVERE, "Unable to create directory for CASE-UCO report", ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_unableToCreateDirectories());
progressPanel.complete(ReportStatus.ERROR);
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
return;
}
@ -127,7 +94,6 @@ class ReportCaseUco implements GeneralReportModule {
MessageNotifyUtil.Message.warn(Bundle.ReportCaseUco_ingestWarning());
}
JsonGenerator jsonGenerator = null;
SimpleTimeZone timeZone = new SimpleTimeZone(0, "GMT");
try {
@ -163,7 +129,7 @@ class ReportCaseUco implements GeneralReportModule {
// Loop files and write info to CASE-UCO report
while (resultSet.next()) {
if (progressPanel.getStatus() == ReportStatus.CANCELED) {
if (progressPanel.getStatus() == ReportProgressPanel.ReportStatus.CANCELED) {
break;
}
@ -185,21 +151,21 @@ class ReportCaseUco implements GeneralReportModule {
// create the required CASE-UCO entries at the end of the output file
finilizeJsonOutputFile(jsonGenerator);
Case.getCurrentCaseThrows().addReport(reportPath, Bundle.ReportCaseUco_srcModuleName_text(), "");
Case.getCurrentCaseThrows().addReport(reportOutputPath, Bundle.ReportCaseUco_srcModuleName_text(), "");
progressPanel.complete(ReportStatus.COMPLETE);
progressPanel.complete(ReportProgressPanel.ReportStatus.COMPLETE);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Failed to get list of files from case database", ex); //NON-NLS
progressPanel.complete(ReportStatus.ERROR);
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Failed to create JSON output for the CASE-UCO report", ex); //NON-NLS
progressPanel.complete(ReportStatus.ERROR);
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
} catch (SQLException ex) {
logger.log(Level.WARNING, "Unable to read result set", ex); //NON-NLS
progressPanel.complete(ReportStatus.ERROR);
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "No current case open", ex); //NON-NLS
progressPanel.complete(ReportStatus.ERROR);
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
} finally {
if (jsonGenerator != null) {
try {
@ -211,18 +177,29 @@ class ReportCaseUco implements GeneralReportModule {
}
}
private void initializeJsonOutputFile(JsonGenerator catalog) throws IOException {
private static void initializeJsonOutputFile(JsonGenerator catalog) throws IOException {
catalog.writeStartObject();
catalog.writeFieldName("@graph");
catalog.writeStartArray();
}
private void finilizeJsonOutputFile(JsonGenerator catalog) throws IOException {
private static void finilizeJsonOutputFile(JsonGenerator catalog) throws IOException {
catalog.writeEndArray();
catalog.writeEndObject();
}
private String saveCaseInfo(SleuthkitCase skCase, JsonGenerator catalog) throws TskCoreException, SQLException, IOException, NoCurrentCaseException {
/**
* Save info about the Autopsy case in CASE-UCo format
*
* @param skCase SleuthkitCase object
* @param catalog JsonGenerator object
* @return CASE-UCO trace ID object for the Autopsy case entry
* @throws TskCoreException
* @throws SQLException
* @throws IOException
* @throws NoCurrentCaseException
*/
private static String saveCaseInfo(SleuthkitCase skCase, JsonGenerator catalog) throws TskCoreException, SQLException, IOException, NoCurrentCaseException {
// create a "trace" entry for the Autopsy case iteself
String uniqueCaseName;
@ -265,7 +242,19 @@ class ReportCaseUco implements GeneralReportModule {
return caseTraceId;
}
private String saveDataSourceInfo(Long selectedDataSourceId, String caseTraceId, SleuthkitCase skCase, JsonGenerator jsonGenerator) throws TskCoreException, SQLException, IOException {
/**
* Save info about the data source in CASE-UCo format
*
* @param selectedDataSourceId Object ID of the data source
* @param caseTraceId CASE-UCO trace ID object for the Autopsy case entry
* @param skCase SleuthkitCase object
* @param catalog JsonGenerator object
* @return
* @throws TskCoreException
* @throws SQLException
* @throws IOException
*/
private static String saveDataSourceInfo(Long selectedDataSourceId, String caseTraceId, SleuthkitCase skCase, JsonGenerator jsonGenerator) throws TskCoreException, SQLException, IOException {
Long imageSize = (long) 0;
String imageName = "";
@ -307,10 +296,10 @@ class ReportCaseUco implements GeneralReportModule {
return saveDataSourceInCaseUcoFormat(jsonGenerator, imageName, imageSize, selectedDataSourceId, caseTraceId);
}
private String saveDataSourceInCaseUcoFormat(JsonGenerator catalog, String imageName, Long imageSize, Long selectedDataSourceId, String caseTraceId) throws IOException {
private static String saveDataSourceInCaseUcoFormat(JsonGenerator catalog, String imageName, Long imageSize, Long selectedDataSourceId, String caseTraceId) throws IOException {
// create a "trace" entry for the data source
String dataSourceTraceId = "data-source-"+selectedDataSourceId;
String dataSourceTraceId = "data-source-" + selectedDataSourceId;
catalog.writeStartObject();
catalog.writeStringField("@id", dataSourceTraceId);
catalog.writeStringField("@type", "Trace");
@ -359,7 +348,7 @@ class ReportCaseUco implements GeneralReportModule {
return dataSourceTraceId;
}
private void saveFileInCaseUcoFormat(Long objectId, String fileName, String parent_path, String md5Hash, String mime_type, long size, String ctime,
private static void saveFileInCaseUcoFormat(Long objectId, String fileName, String parent_path, String md5Hash, String mime_type, long size, String ctime,
String atime, String mtime, String extension, JsonGenerator catalog, String dataSourceTraceId) throws IOException {
String fileTraceId = "file-" + objectId;
@ -433,33 +422,4 @@ class ReportCaseUco implements GeneralReportModule {
catalog.writeEndObject();
}
@Override
public String getName() {
String name = NbBundle.getMessage(this.getClass(), "ReportCaseUco.getName.text");
return name;
}
@Override
public String getRelativeFilePath() {
return REPORT_FILE_NAME;
}
@Override
public String getDescription() {
String desc = NbBundle.getMessage(this.getClass(), "ReportCaseUco.getDesc.text");
return desc;
}
@Override
public JPanel getConfigurationPanel() {
try {
configPanel = new ReportCaseUcoConfigPanel();
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
logger.log(Level.SEVERE, "Failed to initialize CASE-UCO settings panel", ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_notInitialized());
configPanel = null;
}
return configPanel;
}
}

View File

@ -0,0 +1,128 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2018-2019 Basis Technology Corp.
* Project Contact/Architect: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.report.caseuco;
import java.util.logging.Level;
import javax.swing.JPanel;
import java.sql.SQLException;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.report.GeneralReportModule;
import org.sleuthkit.autopsy.report.ReportProgressPanel;
import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus;
import org.sleuthkit.datamodel.*;
/**
* ReportCaseUco generates a report in the CASE-UCO format. It saves basic file
* info like full caseDirPath, name, MIME type, times, and hash.
*/
public final class ReportCaseUco implements GeneralReportModule {
private static final Logger logger = Logger.getLogger(ReportCaseUco.class.getName());
private static ReportCaseUco instance = null;
private ReportCaseUcoConfigPanel configPanel;
private static final String REPORT_FILE_NAME = "CASE_UCO_output.json-ld";
// Hidden constructor for the report
private ReportCaseUco() {
}
// Get the default implementation of this report
public static synchronized ReportCaseUco getDefault() {
if (instance == null) {
instance = new ReportCaseUco();
}
return instance;
}
@Override
public String getName() {
String name = NbBundle.getMessage(this.getClass(), "ReportCaseUco.getName.text");
return name;
}
@Override
public String getRelativeFilePath() {
return REPORT_FILE_NAME;
}
@Override
public String getDescription() {
String desc = NbBundle.getMessage(this.getClass(), "ReportCaseUco.getDesc.text");
return desc;
}
@Override
public JPanel getConfigurationPanel() {
try {
configPanel = new ReportCaseUcoConfigPanel();
} catch (NoCurrentCaseException | TskCoreException | SQLException ex) {
logger.log(Level.SEVERE, "Failed to initialize CASE-UCO settings panel", ex); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_notInitialized());
configPanel = null;
}
return configPanel;
}
/**
* Returns CASE-UCO report file name
*
* @return the REPORT_FILE_NAME
*/
public static String getReportFileName() {
return REPORT_FILE_NAME;
}
/**
* Generates a CASE-UCO format report.
*
* @param baseReportDir caseDirPath to save the report
* @param progressPanel panel to update the report's progress
*/
@NbBundle.Messages({
"ReportCaseUco.notInitialized=CASE-UCO settings panel has not been initialized",
"ReportCaseUco.noDataSourceSelected=No data source selected for CASE-UCO report"
})
@Override
@SuppressWarnings("deprecation")
public void generateReport(String baseReportDir, ReportProgressPanel progressPanel) {
if (configPanel == null) {
logger.log(Level.SEVERE, "CASE-UCO settings panel has not been initialized"); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_notInitialized());
progressPanel.complete(ReportStatus.ERROR);
return;
}
Long selectedDataSourceId = configPanel.getSelectedDataSourceId();
if (selectedDataSourceId == ReportCaseUcoConfigPanel.NO_DATA_SOURCE_SELECTED) {
logger.log(Level.SEVERE, "No data source selected for CASE-UCO report"); //NON-NLS
MessageNotifyUtil.Message.error(Bundle.ReportCaseUco_noDataSourceSelected());
progressPanel.complete(ReportStatus.ERROR);
return;
}
String reportPath = baseReportDir + getRelativeFilePath();
CaseUcoFormatExporter.generateReport(selectedDataSourceId, reportPath, progressPanel);
}
}

View File

@ -56,7 +56,7 @@
<Component class="javax.swing.JLabel" name="jLabelSelectDataSource">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/modules/case_uco/Bundle.properties" key="ReportCaseUcoConfigPanel.jLabelSelectDataSource.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/report/caseuco/Bundle.properties" key="ReportCaseUcoConfigPanel.jLabelSelectDataSource.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AuxValues>

View File

@ -2,7 +2,7 @@
*
* Autopsy Forensic Browser
*
* Copyright 2018 Basis Technology Corp.
* Copyright 2019-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -17,7 +17,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.case_uco;
package org.sleuthkit.autopsy.report.caseuco;
import java.sql.SQLException;
import java.util.Map;

View File

@ -1,44 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.configuration;
import javax.swing.JPanel;
/**
* Interface to run an ingest job in the background.
*/
public interface IngestJobRunningService {
/**
* Starts the service
*/
void start();
/**
* Stops the service
*/
void stop();
/**
* Returns a panel to be displayed while using this service
*
* @return panel to be displayed while using this service
*/
JPanel getStartupWindow();
}

View File

@ -1,4 +1,5 @@
OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Long-Description=Keyword Search ingest module.\n\nThe module indexes files found in the disk image at ingest time.\nIt then periodically runs the search on the indexed files using one or more keyword lists (containing pure words and/or regular expressions) and posts results.\n\n\The module also contains additional tools integrated in the main GUI, such as keyword list configuration, keyword search bar in the top-right corner, extracted text viewer and search results viewer showing highlighted keywords found.
OpenIDE-Module-Name=KeywordSearch
OptionsCategory_Name_KeywordSearchOptions=Keyword Search

View File

@ -55,8 +55,16 @@ ExtractSafari_Error_Getting_History=An error occurred while processing Safari hi
ExtractSafari_Error_Parsing_Bookmark=An error occured while processing Safari Bookmark files
ExtractSafari_Error_Parsing_Cookies=An error occured while processing Safari Cookies files
ExtractSafari_Module_Name=Safari
ExtractZone_Internet=Internet Zone
ExtractZone_Local_Intranet=Local Intranet Zone
ExtractZone_Local_Machine=Local Machine Zone
ExtractZone_process_errMsg=An error occured processing ':Zone.Indentifier' files.
ExtractZone_process_errMsg_find=A failure occured while searching for :Zone.Indentifier files.
ExtractZone_progress_Msg=Extracting :Zone.Identifer files
ExtractZone_Restricted=Restricted Sites Zone
ExtractZone_Trusted=Trusted Sites Zone
OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\nThe module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web acitivity (sites visited, stored cookies, bookmarked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images. \nThe plugin is also fully functional when deployed on Windows version of Autopsy.
OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\nThe module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web acitivity (sites visited, stored cookies, bookmarked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy.
OpenIDE-Module-Name=RecentActivity
OpenIDE-Module-Short-Description=Recent Activity finder ingest module
Chrome.moduleName=Chrome

View File

@ -0,0 +1,388 @@
/*
*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.recentactivity;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.NetworkUtils;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.ingest.IngestServices;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE;
import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD;
import org.sleuthkit.datamodel.BlackboardAttribute;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ReadContentInputStream;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Extract the <i>:Zone.Indentifier<i> alternate data stream files. A file with
* a <i>:Zone.Indentifier<i> extention contains information about the similarly
* named (with out zone identifer extension) downloaded file.
*/
final class ExtractZoneIdentifier extends Extract {
private static final Logger LOG = Logger.getLogger(ExtractEdge.class.getName());
private static final String ZONE_IDENTIFIER_FILE = "%:Zone.Identifier"; //NON-NLS
private static final String ZONE_IDENTIFIER = ":Zone.Identifier"; //NON-NLS
@Messages({
"ExtractZone_process_errMsg_find=A failure occured while searching for :Zone.Indentifier files.",
"ExtractZone_process_errMsg=An error occured processing ':Zone.Indentifier' files.",
"ExtractZone_progress_Msg=Extracting :Zone.Identifer files"
})
@Override
void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
progressBar.progress(Bundle.ExtractZone_progress_Msg());
List<AbstractFile> zoneFiles = null;
try {
zoneFiles = currentCase.getServices().getFileManager().findFiles(dataSource, ZONE_IDENTIFIER_FILE);
} catch (TskCoreException ex) {
addErrorMessage(Bundle.ExtractZone_process_errMsg_find());
LOG.log(Level.SEVERE, "Unable to find zone identifier files, exception thrown. ", ex); // NON-NLS
}
if (zoneFiles == null || zoneFiles.isEmpty()) {
return;
}
Set<Long> knownPathIDs = null;
try {
knownPathIDs = getPathIDsForType(TSK_WEB_DOWNLOAD);
} catch (TskCoreException ex) {
addErrorMessage(Bundle.ExtractZone_process_errMsg());
LOG.log(Level.SEVERE, "Failed to build PathIDs List for TSK_WEB_DOWNLOAD", ex); // NON-NLS
}
if (knownPathIDs == null) {
return;
}
Collection<BlackboardArtifact> sourceArtifacts = new ArrayList<>();
Collection<BlackboardArtifact> downloadArtifacts = new ArrayList<>();
for (AbstractFile zoneFile : zoneFiles) {
try {
processZoneFile(context, dataSource, zoneFile, sourceArtifacts, downloadArtifacts, knownPathIDs);
} catch (TskCoreException ex) {
addErrorMessage(Bundle.ExtractZone_process_errMsg());
String message = String.format("Failed to process zone identifier file %s", zoneFile.getName()); //NON-NLS
LOG.log(Level.WARNING, message, ex);
}
}
IngestServices services = IngestServices.getInstance();
if (!sourceArtifacts.isEmpty()) {
services.fireModuleDataEvent(new ModuleDataEvent(
RecentActivityExtracterModuleFactory.getModuleName(),
TSK_DOWNLOAD_SOURCE, sourceArtifacts));
}
if (!downloadArtifacts.isEmpty()) {
services.fireModuleDataEvent(new ModuleDataEvent(
RecentActivityExtracterModuleFactory.getModuleName(),
TSK_WEB_DOWNLOAD, downloadArtifacts));
}
}
/**
* Process a single Zone Identifier file.
*
* @param context IngetJobContext
* @param dataSource Content
* @param zoneFile Zone Indentifier file
* @param sourceArtifacts List for TSK_DOWNLOAD_SOURCE artifacts
* @param downloadArtifacts List for TSK_WEB_DOWNLOAD aritfacts
*
* @throws TskCoreException
*/
private void processZoneFile(IngestJobContext context, Content dataSource,
AbstractFile zoneFile, Collection<BlackboardArtifact> sourceArtifacts,
Collection<BlackboardArtifact> downloadArtifacts,
Set<Long> knownPathIDs) throws TskCoreException {
ZoneIdentifierInfo zoneInfo = null;
try {
zoneInfo = new ZoneIdentifierInfo(zoneFile);
} catch (IOException ex) {
String message = String.format("Unable to parse temporary File for %s", zoneFile.getName()); //NON-NLS
LOG.log(Level.WARNING, message, ex);
}
if (zoneInfo == null) {
return;
}
AbstractFile downloadFile = getDownloadFile(dataSource, zoneFile);
if (downloadFile != null) {
// Only create a new TSK_WEB_DOWNLOAD artifact if one does not exist for downloadFile
if (!knownPathIDs.contains(downloadFile.getDataSourceObjectId())) {
// The zone identifier file is the parent of this artifact
// because it is the file we parsed to get the data
BlackboardArtifact downloadBba = createDownloadArtifact(zoneFile, zoneInfo);
if (downloadBba != null) {
downloadArtifacts.add(downloadBba);
}
}
// check if download has a child TSK_DOWNLOAD_SOURCE artifact, if not create one
if (downloadFile.getArtifactsCount(TSK_DOWNLOAD_SOURCE) == 0) {
BlackboardArtifact sourceBba = createDownloadSourceArtifact(downloadFile, zoneInfo);
if (sourceBba != null) {
sourceArtifacts.add(sourceBba);
}
}
}
}
/**
* Find the file that the Zone.Identifer file was created alongside.
*
* @param dataSource Content
* @param zoneFile The zone identifier case file
*
* @return The downloaded file or null if a file was not found
*
* @throws TskCoreException
*/
private AbstractFile getDownloadFile(Content dataSource, AbstractFile zoneFile) throws TskCoreException {
AbstractFile downloadFile = null;
org.sleuthkit.autopsy.casemodule.services.FileManager fileManager
= currentCase.getServices().getFileManager();
String downloadFileName = zoneFile.getName().replace(ZONE_IDENTIFIER, ""); //NON-NLS
List<AbstractFile> fileList = fileManager.findFiles(dataSource, downloadFileName, zoneFile.getParentPath());
if (fileList.size() == 1) {
downloadFile = fileList.get(0);
// Check that the download file and the zone file came from the same dir
if (!downloadFile.getParentPath().equals(zoneFile.getParentPath())) {
downloadFile = null;
} else if (zoneFile.getMetaAddr() != downloadFile.getMetaAddr()) {
downloadFile = null;
}
}
return downloadFile;
}
/**
* Create a Download Source Artifact for the given ZoneIdentifierInfo
* object.
*
* @param downloadFile AbstractFile representing the file downloaded, not
* the zone indentifier file.
* @param zoneInfo Zone Indentifer file wrapper object
*
* @return TSK_DOWNLOAD_SOURCE object for given parameters
*/
private BlackboardArtifact createDownloadSourceArtifact(AbstractFile downloadFile, ZoneIdentifierInfo zoneInfo) {
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
bbattributes.addAll(Arrays.asList(
new BlackboardAttribute(TSK_URL,
RecentActivityExtracterModuleFactory.getModuleName(),
StringUtils.defaultString(zoneInfo.getURL(), "")),
new BlackboardAttribute(TSK_DOMAIN,
RecentActivityExtracterModuleFactory.getModuleName(),
(zoneInfo.getURL() != null) ? NetworkUtils.extractDomain(zoneInfo.getURL()) : ""),
new BlackboardAttribute(TSK_LOCATION,
RecentActivityExtracterModuleFactory.getModuleName(),
StringUtils.defaultString(zoneInfo.getZoneIdAsString(), "")))); //NON-NLS
return addArtifact(TSK_DOWNLOAD_SOURCE, downloadFile, bbattributes);
}
/**
* Create a TSK_WEB_DOWNLOAD Artifact for the given zone indentifier file.
*
* @param zoneFile Zone identifier file
* @param zoneInfo ZoneIdentifierInfo file wrapper object
*
* @return BlackboardArifact for the given parameters
*/
private BlackboardArtifact createDownloadArtifact(AbstractFile zoneFile, ZoneIdentifierInfo zoneInfo) {
Collection<BlackboardAttribute> bbattributes = createDownloadAttributes(
null, null,
zoneInfo.getURL(), null,
(zoneInfo.getURL() != null ? NetworkUtils.extractDomain(zoneInfo.getURL()) : ""),
null);
return addArtifact(TSK_WEB_DOWNLOAD, zoneFile, bbattributes);
}
/**
* Creates a list of PathIDs for the given Artifact type.
*
* @param type BlackboardArtifact.ARTIFACT_TYPE
*
* @return A list of PathIDs
*
* @throws TskCoreException
*/
private Set<Long> getPathIDsForType(BlackboardArtifact.ARTIFACT_TYPE type) throws TskCoreException {
Set<Long> idList = new HashSet<>();
for (BlackboardArtifact artifact : currentCase.getSleuthkitCase().getBlackboardArtifacts(type)) {
BlackboardAttribute pathIDAttribute = artifact.getAttribute(new BlackboardAttribute.Type(TSK_PATH_ID));
if (pathIDAttribute != null) {
long contentID = pathIDAttribute.getValueLong();
if (contentID != -1) {
idList.add(contentID);
}
}
}
return idList;
}
@Messages({
"ExtractZone_Local_Machine=Local Machine Zone",
"ExtractZone_Local_Intranet=Local Intranet Zone",
"ExtractZone_Trusted=Trusted Sites Zone",
"ExtractZone_Internet=Internet Zone",
"ExtractZone_Restricted=Restricted Sites Zone"
})
/**
* Wrapper class for information in the :ZoneIdentifier file. The
* Zone.Identifier file has a simple format of <i>key<i>=<i>value<i>. There
* are four known keys: ZoneId, ReferrerUrl, HostUrl, and
* LastWriterPackageFamilyName. Not all browsers will put all values in the
* file, in fact most will only supply the ZoneId. Only Edge supplies the
* LastWriterPackageFamilyName.
*/
private final static class ZoneIdentifierInfo {
private static final String ZONE_ID = "ZoneId"; //NON-NLS
private static final String REFERRER_URL = "ReferrerUrl"; //NON-NLS
private static final String HOST_URL = "HostUrl"; //NON-NLS
private static final String FAMILY_NAME = "LastWriterPackageFamilyName"; //NON-NLS
private final Properties properties = new Properties(null);
/**
* Opens the zone file, reading for the key\value pairs and puts them
* into a HashMap.
*
* @param zoneFile The ZoneIdentifier file
*
* @throws FileNotFoundException
* @throws IOException
*/
ZoneIdentifierInfo(AbstractFile zoneFile) throws IOException {
properties.load(new ReadContentInputStream(zoneFile));
}
/**
* Get the integer zone id
*
* @return interger zone id or -1 if unknown
*/
private int getZoneId() {
int zoneValue = -1;
String value = properties.getProperty(ZONE_ID);
if (value != null) {
zoneValue = Integer.parseInt(value);
}
return zoneValue;
}
/**
* Get the string description of the zone id.
*
* @return String description or null if a zone id was not found
*/
private String getZoneIdAsString() {
switch (getZoneId()) {
case 0:
return Bundle.ExtractZone_Local_Machine();
case 1:
return Bundle.ExtractZone_Local_Intranet();
case 2:
return Bundle.ExtractZone_Trusted();
case 3:
return Bundle.ExtractZone_Internet();
case 4:
return Bundle.ExtractZone_Restricted();
default:
return null;
}
}
/**
* Get the URL from which the file was downloaded.
*
* @return String url or null if a host url was not found
*/
private String getURL() {
return properties.getProperty(HOST_URL);
}
/**
* Get the referrer url.
*
* @return String url or null if a host url was not found
*/
private String getReferrer() {
return properties.getProperty(REFERRER_URL);
}
/**
* Gets the string value for the key LastWriterPackageFamilyName.
*
* @return String value or null if the value was not found
*/
private String getFamilyName() {
return properties.getProperty(FAMILY_NAME);
}
}
}

View File

@ -76,6 +76,7 @@ public final class RAImageIngestModule implements DataSourceIngestModule {
Extract osExtract = new ExtractOs();
Extract dataSourceAnalyzer = new DataSourceUsageAnalyzer();
Extract safari = new ExtractSafari();
Extract zoneInfo = new ExtractZoneIdentifier();
extractors.add(chrome);
extractors.add(firefox);
@ -87,6 +88,7 @@ public final class RAImageIngestModule implements DataSourceIngestModule {
extractors.add(registry); // this should run after quicker modules like the browser modules and needs to run before the DataSourceUsageAnalyzer
extractors.add(osExtract); // this needs to run before the DataSourceUsageAnalyzer
extractors.add(dataSourceAnalyzer); //this needs to run after ExtractRegistry and ExtractOs
extractors.add(zoneInfo); // this needs to run after the web browser modules
browserExtractors.add(chrome);
browserExtractors.add(firefox);

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1,48 @@
/*! \page portable_case_page Portable Cases
\section portable_case_overview Overview
A portable case is a partial copy of a normal Autopsy case that can be opened from anywhere. The general use case is as follows:
<ol>
<li>Alice is analyzing one or more data sources using Autopsy. She tags files and results that are of particular interest.
<li>Alice wants to share her findings with Bob but is unable to send him the original data sources.
<li>Alice creates a portable case which will contain only her tagged files and results, plus any files associated with those results, and sends it to Bob.
<li>Bob can open the portable case in Autopsy and view all files and results Alice tagged, and run any of the normal Autopsy features.
</ol>
For example, Alice's original case could look like this:
\image html portable_case_original_version.png
The portable version could like this:
\image html portable_case_portable_version.png
Alice only tagged eight files and results, so most of the original content is no longer in the case. Some of the data sources had no tagged items so they're not included at all. The file structure of any tagged files is preserved - you can see that the tagged image in the screenshot is still in the same location, but the non-tagged files are gone. Note that although the original images (such as "image1.vhd") appear in the tree, their contents are not included in the portable case.
\section portable_case_creation Creating a Portable Case
First you'll want to make sure that all the files and results you want included in the portable case are tagged - see the \ref tagging_page page for more details.
You can see what tags you've added in the \ref tree_viewer_page.
\image html portable_case_tags.png
Portable cases are created through the \ref reporting_page feature. The Generate Report dialog will display a list of all tags that are in use in the current case and you can choose which ones you would like to include. At the bottom you can select the output folder for the new case. By default it will be placed in the "Reports" folder in the current case.
\image html portable_case_report_panel.png
Here you can see the new portable case. It will be named with the original case name plus "(Portable)". The portable case is initially missing many of the normal Autopsy folders - these will be created the first time a user opens it. The portable case folder can be zipped and sent to a different user.
\image html portable_case_folder.png
\section portable_case_usage Using a Portable Case
Portable cases generally behave like any other Autopsy case. You can run ingest, do keyword searches, use the timeline viewer, etc. One point to note is that while the original data source names appear in the case, the data sources themselves were not copied into the portable case.
\image html portable_case_empty_image.png
This may cause warning or error messages when using ingest modules that run on the full image, such as the \ref data_source_integrity_page. You will also not be able to view the data sources in the content viewer.
You can also add additonal data sources to the portable case if you wish. The case will no longer be portable, but if desired you could generate a new portable case that will include tagged files and results from the new data sources as well as the original case.
*/

View File

@ -82,6 +82,10 @@ This report module generates a KML file from any GPS data in the case. This file
\image html reports_kml.png
\subsection report_portable_case Portable Case
This report module generates a new Autopsy case from any tagged files and results. See the \ref portable_case_page page for additional information.
\subsection report_stix STIX
The STIX module allows you to generate a report and Interesting File artifacts by running a STIX file (or files) against the data sources in the case.