From 622b7247c5c4b857a7f3248d8484dcb49b8a435d Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Thu, 25 Jun 2020 10:23:25 -0400 Subject: [PATCH 01/78] Add Bluetooth initial Add bluetooth initial support --- .../recentactivity/ExtractRegistry.java | 113 ++++++++++++++++++ thirdparty/rr-full/plugins/system | 1 + 2 files changed, 114 insertions(+) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index 80171f0672..e1c373b3cf 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -82,9 +82,12 @@ import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOC import org.sleuthkit.datamodel.BlackboardAttribute; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; @@ -337,6 +340,10 @@ class ExtractRegistry extends Extract { } catch (IOException | TskCoreException ex) { logger.log(Level.WARNING, String.format("Unable to get shell bags from file %s", regOutputFiles.fullPlugins), ex); } + } else if (regFileNameLocal.toLowerCase().contains("system") && parseSystemPluginOutput(regOutputFiles.fullPlugins, regFile) == false) { + this.addErrorMessage( + NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", + this.getName(), regFileName)); } try { Report report = currentCase.addReport(regOutputFiles.fullPlugins, @@ -930,6 +937,112 @@ class ExtractRegistry extends Extract { return false; } + + private boolean parseSystemPluginOutput(String regfilePath, AbstractFile regAbstractFile) { + File regfile = new File(regfilePath); + try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { + String line = reader.readLine(); + while (line != null) { + line = line.trim(); + + if (line.matches("^network v.*")) { + // parseNetworkInterfaces(regfile, reader); + } else if (line.matches("^bthport v..*")) { + parseBlueToothDevices(regAbstractFile, reader) ; + } + line = reader.readLine(); + } + return true; + } catch (FileNotFoundException ex) { + logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS + } catch (IOException ex) { + logger.log(Level.WARNING, "Error reading the system hive: {0}", ex); //NON-NLS + } + + return false; + + } + + /** + * Create recently used artifacts to parse the regripper plugin output, this format is used in several diffent plugins + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseBlueToothDevices(AbstractFile regFile, BufferedReader reader) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + // date format for plugin Tue Jun 23 10:27:54 2020 Z + SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US); + Long bthLastSeen = Long.valueOf(0); + Long bthLastConnected = Long.valueOf(0); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + if (line.contains("Device Unique ID")) { + // Columns are seperated by colons : + // Data : Values + // Record is 4 lines in length (Device Unique Id, Name, Last Seen, LastConnected + while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Radio Support not found")) { + // Split line on "> " which is the record delimiter between position and file + String deviceTokens[] = line.split(": "); + String deviceUniqueId = deviceTokens[1]; + line = reader.readLine(); + // Default device name to unknown as a device name may not exist. + String deviceName = "Unknown"; + if (line.contains("Name")) { + String nameTokens[] = line.split(": "); + deviceName = nameTokens[1]; + line = reader.readLine(); + } + String lastSeenTokens[] = line.split(": "); + String lastSeen = lastSeenTokens[1].replace(" Z", "");; + line = reader.readLine(); + String lastConnectedTokens[] = line.split(": "); + String lastConnected = lastConnectedTokens[1].replace(" Z", "");; + try { + Date usedSeenDate = dateFormat.parse(lastSeen); + bthLastSeen = usedSeenDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last Seen attribute.", lastSeen), ex); //NON-NLS + } + try { + Date usedConnectedDate = dateFormat.parse(lastConnected); + bthLastConnected = usedConnectedDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last connected attribute.", lastSeen), ex); //NON-NLS + } + + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_DEVICE_ID, getName(), deviceUniqueId)); + attributes.add(new BlackboardAttribute(TSK_NAME, getName(), deviceName)); + attributes.add(new BlackboardAttribute(TSK_DATETIME, getName(), bthLastSeen)); + attributes.add(new BlackboardAttribute(TSK_DATETIME_ACCESSED, getName(), bthLastConnected)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + } + // Read blank line between records then next read line is start of next block + line = reader.readLine(); + line = reader.readLine(); + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + /** * Parse the output of the SAM regripper plugin to get additional Account * information diff --git a/thirdparty/rr-full/plugins/system b/thirdparty/rr-full/plugins/system index f95897e19d..846b9b7a7f 100644 --- a/thirdparty/rr-full/plugins/system +++ b/thirdparty/rr-full/plugins/system @@ -4,6 +4,7 @@ appcompatcache auditfail backuprestore bam +btconfig bthport comfoo compname From 2cde791bafa16eccf087fff5db9b35001dc3e3bc Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 21 Jul 2020 14:20:56 -0400 Subject: [PATCH 02/78] Update ExtractRegistry.java --- .../recentactivity/ExtractRegistry.java | 4258 ++++++++--------- 1 file changed, 2117 insertions(+), 2141 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index e95617e69b..c98965adbe 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -1,2141 +1,2117 @@ -/* - * - * Autopsy Forensic Browser - * - * Copyright 2012-2020 Basis Technology Corp. - * - * Copyright 2012 42six Solutions. - * Contact: aebadirad 42six com - * Project Contact/Architect: carrier sleuthkit 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.recentactivity; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.StringReader; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.logging.Level; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import org.apache.commons.io.FilenameUtils; -import org.openide.modules.InstalledFileLocator; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.coreutils.ExecUtil; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.PlatformUtil; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator; -import org.sleuthkit.autopsy.ingest.IngestJobContext; -import org.sleuthkit.autopsy.recentactivity.UsbDeviceIdMapper.USBInfo; -import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; -import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import java.nio.file.Path; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Scanner; -import java.util.Set; -import java.util.HashSet; -import static java.util.Locale.US; -import static java.util.TimeZone.getTimeZone; -import org.openide.util.Lookup; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; -import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; -import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; -import org.sleuthkit.autopsy.recentactivity.ShellBagParser.ShellBag; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.Account; -import org.sleuthkit.datamodel.BlackboardArtifact; -import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; -import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_ACCOUNT; -import org.sleuthkit.datamodel.BlackboardAttribute; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; -import org.sleuthkit.datamodel.Report; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskDataException; - -/** - * Extract windows registry data using regripper. Runs two versions of - * regripper. One is the generally available set of plug-ins and the second is a - * set that were customized for Autopsy to produce a more structured output of - * XML so that we can parse and turn into blackboard artifacts. - */ -@NbBundle.Messages({ - "RegRipperNotFound=Autopsy RegRipper executable not found.", - "RegRipperFullNotFound=Full version RegRipper executable not found.", - "Progress_Message_Analyze_Registry=Analyzing Registry Files", - "Shellbag_Artifact_Display_Name=Shell Bags", - "Shellbag_Key_Attribute_Display_Name=Key", - "Shellbag_Last_Write_Attribute_Display_Name=Last Write", - "Recently_Used_Artifacts_Office_Trustrecords=Stored in TrustRecords because Office security exception was granted", - "Recently_Used_Artifacts_ArcHistory=Recently opened by 7Zip", - "Recently_Used_Artifacts_Applets=Recently opened according to Applets registry key", - "Recently_Used_Artifacts_Mmc=Recently opened according to Windows Management Console MRU", - "Recently_Used_Artifacts_Winrar=Recently opened according to WinRAR MRU", - "Recently_Used_Artifacts_Officedocs=Recently opened according to Office MRU", - "Recently_Used_Artifacts_Adobe=Recently opened according to Adobe MRU", - "Recently_Used_Artifacts_Mediaplayer=Recently opened according to Media Player MRU", - "Registry_System_Bam=Recently Executed according to Background Activity Moderator (BAM)" -}) -class ExtractRegistry extends Extract { - - private static final String USERNAME_KEY = "Username"; //NON-NLS - private static final String SID_KEY = "SID"; //NON-NLS - private static final String RID_KEY = "RID"; //NON-NLS - private static final String ACCOUNT_CREATED_KEY = "Account Created"; //NON-NLS - private static final String LAST_LOGIN_KEY = "Last Login Date"; //NON-NLS - private static final String LOGIN_COUNT_KEY = "Login Count"; //NON-NLS - private static final String FULL_NAME_KEY = "Full Name"; //NON-NLS - private static final String USER_COMMENT_KEY = "User Comment"; //NON-NLS - private static final String ACCOUNT_TYPE_KEY = "Account Type"; //NON-NLS - private static final String NAME_KEY = "Name"; //NON-NLS - private static final String PWD_RESET_KEY = "Pwd Rest Date"; //NON-NLS - private static final String PWD_FAILE_KEY = "Pwd Fail Date"; //NON-NLS - private static final String INTERNET_NAME_KEY = "InternetName"; //NON-NLS - private static final String PWD_DOES_NOT_EXPIRE_KEY = "Password does not expire"; //NON-NLS - private static final String ACCOUNT_DISABLED_KEY = "Account Disabled"; //NON-NLS - private static final String PWD_NOT_REQUIRED_KEY = "Password not required"; //NON-NLS - private static final String NORMAL_ACCOUNT_KEY = "Normal user account"; //NON-NLS - private static final String HOME_DIRECTORY_REQUIRED_KEY = "Home directory required"; - private static final String TEMPORARY_DUPLICATE_ACCOUNT = "Temporary duplicate account"; - private static final String MNS_LOGON_ACCOUNT_KEY = "MNS logon user account"; - private static final String INTERDOMAIN_TRUST_ACCOUNT_KEY = "Interdomain trust account"; - private static final String WORKSTATION_TRUST_ACCOUNT = "Workstation trust account"; - private static final String SERVER_TRUST_ACCOUNT = "Server trust account"; - private static final String ACCOUNT_AUTO_LOCKED = "Account auto locked"; - private static final String PASSWORD_HINT = "Password Hint"; - - private static final String[] PASSWORD_SETTINGS_FLAGS = {PWD_DOES_NOT_EXPIRE_KEY, PWD_NOT_REQUIRED_KEY}; - private static final String[] ACCOUNT_SETTINGS_FLAGS = {ACCOUNT_AUTO_LOCKED, HOME_DIRECTORY_REQUIRED_KEY, ACCOUNT_DISABLED_KEY}; - private static final String[] ACCOUNT_TYPE_FLAGS = {NORMAL_ACCOUNT_KEY, SERVER_TRUST_ACCOUNT, WORKSTATION_TRUST_ACCOUNT, INTERDOMAIN_TRUST_ACCOUNT_KEY, MNS_LOGON_ACCOUNT_KEY, TEMPORARY_DUPLICATE_ACCOUNT}; - - final private static UsbDeviceIdMapper USB_MAPPER = new UsbDeviceIdMapper(); - final private static String RIP_EXE = "rip.exe"; - final private static String RIP_PL = "rip.pl"; - final private static String RIP_PL_INCLUDE_FLAG = "-I"; - final private static int MS_IN_SEC = 1000; - final private static String NEVER_DATE = "Never"; - final private static String SECTION_DIVIDER = "-------------------------"; - final private static Logger logger = Logger.getLogger(ExtractRegistry.class.getName()); - private final List rrCmd = new ArrayList<>(); - private final List rrFullCmd = new ArrayList<>(); - private final Path rrHome; // Path to the Autopsy version of RegRipper - private final Path rrFullHome; // Path to the full version of RegRipper - private Content dataSource; - private IngestJobContext context; - private Map userNameMap; - - private static final String SHELLBAG_ARTIFACT_NAME = "RA_SHELL_BAG"; //NON-NLS - private static final String SHELLBAG_ATTRIBUTE_LAST_WRITE = "RA_SHELL_BAG_LAST_WRITE"; //NON-NLS - private static final String SHELLBAG_ATTRIBUTE_KEY= "RA_SHELL_BAG_KEY"; //NON-NLS - - BlackboardArtifact.Type shellBagArtifactType = null; - BlackboardAttribute.Type shellBagKeyAttributeType = null; - BlackboardAttribute.Type shellBagLastWriteAttributeType = null; - - ExtractRegistry() throws IngestModuleException { - moduleName = NbBundle.getMessage(ExtractIE.class, "ExtractRegistry.moduleName.text"); - - final File rrRoot = InstalledFileLocator.getDefault().locate("rr", ExtractRegistry.class.getPackage().getName(), false); //NON-NLS - if (rrRoot == null) { - throw new IngestModuleException(Bundle.RegRipperNotFound()); - } - - final File rrFullRoot = InstalledFileLocator.getDefault().locate("rr-full", ExtractRegistry.class.getPackage().getName(), false); //NON-NLS - if (rrFullRoot == null) { - throw new IngestModuleException(Bundle.RegRipperFullNotFound()); - } - - String executableToRun = RIP_EXE; - if (!PlatformUtil.isWindowsOS()) { - executableToRun = RIP_PL; - } - rrHome = rrRoot.toPath(); - String rrPath = rrHome.resolve(executableToRun).toString(); - rrFullHome = rrFullRoot.toPath(); - - if (!(new File(rrPath).exists())) { - throw new IngestModuleException(Bundle.RegRipperNotFound()); - } - String rrFullPath = rrFullHome.resolve(executableToRun).toString(); - if (!(new File(rrFullPath).exists())) { - throw new IngestModuleException(Bundle.RegRipperFullNotFound()); - } - if (PlatformUtil.isWindowsOS()) { - rrCmd.add(rrPath); - rrFullCmd.add(rrFullPath); - } else { - String perl; - File usrBin = new File("/usr/bin/perl"); - File usrLocalBin = new File("/usr/local/bin/perl"); - if (usrBin.canExecute() && usrBin.exists() && !usrBin.isDirectory()) { - perl = "/usr/bin/perl"; - } else if (usrLocalBin.canExecute() && usrLocalBin.exists() && !usrLocalBin.isDirectory()) { - perl = "/usr/local/bin/perl"; - } else { - throw new IngestModuleException("perl not found in your system"); - } - rrCmd.add(perl); - rrCmd.add(RIP_PL_INCLUDE_FLAG); - rrCmd.add(rrHome.toString()); - rrCmd.add(rrPath); - rrFullCmd.add(perl); - rrFullCmd.add(RIP_PL_INCLUDE_FLAG); - rrFullCmd.add(rrFullHome.toString()); - rrFullCmd.add(rrFullPath); - } - } - - /** - * Search for the registry hives on the system. - */ - private List findRegistryFiles() { - List allRegistryFiles = new ArrayList<>(); - org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager(); - - // find the sam hives', process this first so we can map the user id's and sids for later use - try { - allRegistryFiles.addAll(fileManager.findFiles(dataSource, "sam", "/system32/config")); //NON-NLS - } catch (TskCoreException ex) { - String msg = NbBundle.getMessage(this.getClass(), - "ExtractRegistry.findRegFiles.errMsg.errReadingFile", "sam"); - logger.log(Level.WARNING, msg, ex); - this.addErrorMessage(this.getName() + ": " + msg); - } - - - // find the user-specific ntuser-dat files - try { - allRegistryFiles.addAll(fileManager.findFiles(dataSource, "ntuser.dat")); //NON-NLS - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error fetching 'ntuser.dat' file."); //NON-NLS - } - - // find the user-specific ntuser-dat files - try { - allRegistryFiles.addAll(fileManager.findFiles(dataSource, "usrclass.dat")); //NON-NLS - } catch (TskCoreException ex) { - logger.log(Level.WARNING, String.format("Error finding 'usrclass.dat' files."), ex); //NON-NLS - } - - // find the system hives' - String[] regFileNames = new String[]{"system", "software", "security"}; //NON-NLS - for (String regFileName : regFileNames) { - try { - allRegistryFiles.addAll(fileManager.findFiles(dataSource, regFileName, "/system32/config")); //NON-NLS - } catch (TskCoreException ex) { - String msg = NbBundle.getMessage(this.getClass(), - "ExtractRegistry.findRegFiles.errMsg.errReadingFile", regFileName); - logger.log(Level.WARNING, msg, ex); - this.addErrorMessage(this.getName() + ": " + msg); - } - } - return allRegistryFiles; - } - - /** - * Identifies registry files in the database by mtimeItem, runs regripper on - * them, and parses the output. - */ - private void analyzeRegistryFiles() { - List allRegistryFiles = findRegistryFiles(); - - // open the log file - FileWriter logFile = null; - try { - logFile = new FileWriter(RAImageIngestModule.getRAOutputPath(currentCase, "reg") + File.separator + "regripper-info.txt"); //NON-NLS - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - - for (AbstractFile regFile : allRegistryFiles) { - String regFileName = regFile.getName(); - long regFileId = regFile.getId(); - String regFileNameLocal = RAImageIngestModule.getRATempPath(currentCase, "reg") + File.separator + regFileName; - String outputPathBase = RAImageIngestModule.getRAOutputPath(currentCase, "reg") + File.separator + regFileName + "-regripper-" + Long.toString(regFileId); //NON-NLS - File regFileNameLocalFile = new File(regFileNameLocal); - try { - ContentUtils.writeToFile(regFile, regFileNameLocalFile, context::dataSourceIngestIsCancelled); - } catch (ReadContentInputStreamException ex) { - logger.log(Level.WARNING, String.format("Error reading registry file '%s' (id=%d).", - regFile.getName(), regFileId), ex); //NON-NLS - this.addErrorMessage( - NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp", - this.getName(), regFileName)); - continue; - } catch (IOException ex) { - logger.log(Level.SEVERE, String.format("Error writing temp registry file '%s' for registry file '%s' (id=%d).", - regFileNameLocal, regFile.getName(), regFileId), ex); //NON-NLS - this.addErrorMessage( - NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp", - this.getName(), regFileName)); - continue; - } - - if (context.dataSourceIngestIsCancelled()) { - break; - } - - try { - if (logFile != null) { - logFile.write(Long.toString(regFileId) + "\t" + regFile.getUniquePath() + "\n"); - } - } catch (TskCoreException | IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - - logger.log(Level.INFO, "{0}- Now getting registry information from {1}", new Object[]{moduleName, regFileNameLocal}); //NON-NLS - RegOutputFiles regOutputFiles = ripRegistryFile(regFileNameLocal, outputPathBase); - if (context.dataSourceIngestIsCancelled()) { - break; - } - - // parse the autopsy-specific output - if (regOutputFiles.autopsyPlugins.isEmpty() == false && parseAutopsyPluginOutput(regOutputFiles.autopsyPlugins, regFile) == false) { - this.addErrorMessage( - NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", - this.getName(), regFileName)); - } - - // create a report for the full output - if (!regOutputFiles.fullPlugins.isEmpty()) { - //parse the full regripper output from SAM hive files - if (regFileNameLocal.toLowerCase().contains("sam") && parseSamPluginOutput(regOutputFiles.fullPlugins, regFile) == false) { - this.addErrorMessage( - NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", - this.getName(), regFileName)); - } else if (regFileNameLocal.toLowerCase().contains("ntuser") || regFileNameLocal.toLowerCase().contains("usrclass")) { - try { - List shellbags = ShellBagParser.parseShellbagOutput(regOutputFiles.fullPlugins); - createShellBagArtifacts(regFile, shellbags); - createRecentlyUsedArtifacts(regOutputFiles.fullPlugins, regFile); - } catch (IOException | TskCoreException ex) { - logger.log(Level.WARNING, String.format("Unable to get shell bags from file %s", regOutputFiles.fullPlugins), ex); - } - } else if (regFileNameLocal.toLowerCase().contains("system") && parseSystemPluginOutput(regOutputFiles.fullPlugins, regFile) == false) { - this.addErrorMessage( - NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", - this.getName(), regFileName)); - } - try { - Report report = currentCase.addReport(regOutputFiles.fullPlugins, - NbBundle.getMessage(this.getClass(), "ExtractRegistry.parentModuleName.noSpace"), - "RegRipper " + regFile.getUniquePath(), regFile); //NON-NLS - - // Index the report content so that it will be available for keyword search. - KeywordSearchService searchService = Lookup.getDefault().lookup(KeywordSearchService.class); - if (null == searchService) { - logger.log(Level.WARNING, "Keyword search service not found. Report will not be indexed"); - } else { - searchService.index(report); - report.close(); - } - } catch (TskCoreException e) { - this.addErrorMessage("Error adding regripper output as Autopsy report: " + e.getLocalizedMessage()); //NON-NLS - } - } - // delete the hive - regFileNameLocalFile.delete(); - } - - try { - if (logFile != null) { - logFile.close(); - } - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - /** - * Execute regripper on the given registry. - * - * @param regFilePath Path to local copy of registry - * @param outFilePathBase Path to location to save output file to. Base - * mtimeItem that will be extended on - */ - private RegOutputFiles ripRegistryFile(String regFilePath, String outFilePathBase) { - String autopsyType = ""; // Type argument for rr for autopsy-specific modules - String fullType; // Type argument for rr for full set of modules - - RegOutputFiles regOutputFiles = new RegOutputFiles(); - - if (regFilePath.toLowerCase().contains("system")) { //NON-NLS - autopsyType = "autopsysystem"; //NON-NLS - fullType = "system"; //NON-NLS - } else if (regFilePath.toLowerCase().contains("software")) { //NON-NLS - autopsyType = "autopsysoftware"; //NON-NLS - fullType = "software"; //NON-NLS - } else if (regFilePath.toLowerCase().contains("ntuser")) { //NON-NLS - autopsyType = "autopsyntuser"; //NON-NLS - fullType = "ntuser"; //NON-NLS - } else if (regFilePath.toLowerCase().contains("sam")) { //NON-NLS - //fullType sam output files are parsed for user information - fullType = "sam"; //NON-NLS - } else if (regFilePath.toLowerCase().contains("security")) { //NON-NLS - fullType = "security"; //NON-NLS - }else if (regFilePath.toLowerCase().contains("usrclass")) { //NON-NLS - fullType = "usrclass"; //NON-NLS - } else { - return regOutputFiles; - } - - // run the autopsy-specific set of modules - if (!autopsyType.isEmpty()) { - regOutputFiles.autopsyPlugins = outFilePathBase + "-autopsy.txt"; //NON-NLS - String errFilePath = outFilePathBase + "-autopsy.err.txt"; //NON-NLS - logger.log(Level.INFO, "Writing RegRipper results to: {0}", regOutputFiles.autopsyPlugins); //NON-NLS - executeRegRipper(rrCmd, rrHome, regFilePath, autopsyType, regOutputFiles.autopsyPlugins, errFilePath); - } - if (context.dataSourceIngestIsCancelled()) { - return regOutputFiles; - } - - // run the full set of rr modules - if (!fullType.isEmpty()) { - regOutputFiles.fullPlugins = outFilePathBase + "-full.txt"; //NON-NLS - String errFilePath = outFilePathBase + "-full.err.txt"; //NON-NLS - logger.log(Level.INFO, "Writing Full RegRipper results to: {0}", regOutputFiles.fullPlugins); //NON-NLS - executeRegRipper(rrFullCmd, rrFullHome, regFilePath, fullType, regOutputFiles.fullPlugins, errFilePath); - try { - scanErrorLogs(errFilePath); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Unable to run RegRipper", ex); //NON-NLS - this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName())); - } - } - return regOutputFiles; - } - - private void scanErrorLogs(String errFilePath) throws IOException { - File regfile = new File(errFilePath); - try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { - String line = reader.readLine(); - while (line != null) { - line = line.trim(); - if (line.toLowerCase().contains("error") || line.toLowerCase().contains("@inc")) { - logger.log(Level.WARNING, "Regripper file {0} contains errors from run", errFilePath); //NON-NLS - - } - line = reader.readLine(); - } - } - } - - private void executeRegRipper(List regRipperPath, Path regRipperHomeDir, String hiveFilePath, String hiveFileType, String outputFile, String errFile) { - try { - List commandLine = new ArrayList<>(); - for (String cmd : regRipperPath) { - commandLine.add(cmd); - } - commandLine.add("-r"); //NON-NLS - commandLine.add(hiveFilePath); - commandLine.add("-f"); //NON-NLS - commandLine.add(hiveFileType); - - ProcessBuilder processBuilder = new ProcessBuilder(commandLine); - processBuilder.directory(regRipperHomeDir.toFile()); // RegRipper 2.8 has to be run from its own directory - processBuilder.redirectOutput(new File(outputFile)); - processBuilder.redirectError(new File(errFile)); - ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context)); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Unable to run RegRipper", ex); //NON-NLS - this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName())); - } - } - - // @@@ VERIFY that we are doing the right thing when we parse multiple NTUSER.DAT - /** - * - * @param regFilePath Path to the output file produced by RegRipper. - * @param regFile File object for registry that we are parsing (to make - * blackboard artifacts with) - * - * @return - */ - private boolean parseAutopsyPluginOutput(String regFilePath, AbstractFile regFile) { - FileInputStream fstream = null; - List newArtifacts = new ArrayList<>(); - try { - // Read the file in and create a Document and elements - File regfile = new File(regFilePath); - fstream = new FileInputStream(regfile); - String regString = new Scanner(fstream, "UTF-8").useDelimiter("\\Z").next(); //NON-NLS - String startdoc = ""; //NON-NLS - String result = regString.replaceAll("----------------------------------------", ""); - result = result.replaceAll("\\n", ""); //NON-NLS - result = result.replaceAll("\\r", ""); //NON-NLS - result = result.replaceAll("'", "'"); //NON-NLS - result = result.replaceAll("&", "&"); //NON-NLS - result = result.replace('\0', ' '); // NON-NLS - String enddoc = ""; //NON-NLS - String stringdoc = startdoc + result + enddoc; - DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - Document doc = builder.parse(new InputSource(new StringReader(stringdoc))); - - // cycle through the elements in the doc - Element oroot = doc.getDocumentElement(); - NodeList children = oroot.getChildNodes(); - int len = children.getLength(); - for (int i = 0; i < len; i++) { - - if (context.dataSourceIngestIsCancelled()) { - return false; - } - - Element tempnode = (Element) children.item(i); - - String dataType = tempnode.getNodeName(); - NodeList timenodes = tempnode.getElementsByTagName("mtime"); //NON-NLS - Long mtime = null; - if (timenodes.getLength() > 0) { - Element timenode = (Element) timenodes.item(0); - String etime = timenode.getTextContent(); - //sometimes etime will be an empty string and therefore can not be parsed into a date - if (etime != null && !etime.isEmpty()) { - try { - mtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(etime).getTime(); - String Tempdate = mtime.toString(); - mtime = Long.valueOf(Tempdate) / MS_IN_SEC; - } catch (ParseException ex) { - logger.log(Level.WARNING, "Failed to parse epoch time when parsing the registry.", ex); //NON-NLS - } - } - } - - NodeList artroots = tempnode.getElementsByTagName("artifacts"); //NON-NLS - if (artroots.getLength() == 0) { - // If there isn't an artifact node, skip this entry - continue; - } - - Element artroot = (Element) artroots.item(0); - NodeList myartlist = artroot.getChildNodes(); - String parentModuleName = RecentActivityExtracterModuleFactory.getModuleName(); - - // If all artifact nodes should really go under one Blackboard artifact, need to process it differently - switch (dataType) { - case "WinVersion": //NON-NLS - String version = ""; - String systemRoot = ""; - String productId = ""; - String regOwner = ""; - String regOrg = ""; - Long installtime = null; - for (int j = 0; j < myartlist.getLength(); j++) { - Node artchild = myartlist.item(j); - // If it has attributes, then it is an Element (based off API) - if (artchild.hasAttributes()) { - Element artnode = (Element) artchild; - - String value = artnode.getTextContent(); - if (value != null) { - value = value.trim(); - } - String name = artnode.getAttribute("name"); //NON-NLS - if (name == null) { - continue; - } - switch (name) { - case "ProductName": // NON-NLS - version = value; - break; - case "CSDVersion": // NON-NLS - // This is dependant on the fact that ProductName shows up first in the module output - version = version + " " + value; - break; - case "SystemRoot": //NON-NLS - systemRoot = value; - break; - case "ProductId": //NON-NLS - productId = value; - break; - case "RegisteredOwner": //NON-NLS - regOwner = value; - break; - case "RegisteredOrganization": //NON-NLS - regOrg = value; - break; - case "InstallDate": //NON-NLS - if (value != null && !value.isEmpty()) { - try { - installtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(value).getTime(); - String Tempdate = installtime.toString(); - installtime = Long.valueOf(Tempdate) / MS_IN_SEC; - } catch (ParseException e) { - logger.log(Level.WARNING, "RegRipper::Conversion on DateTime -> ", e); //NON-NLS - } - } - break; - default: - break; - } - } - } - try { - Collection bbattributes = new ArrayList<>(); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, version)); - if (installtime != null) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, installtime)); - } - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, parentModuleName, systemRoot)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PRODUCT_ID, parentModuleName, productId)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_OWNER, parentModuleName, regOwner)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ORGANIZATION, parentModuleName, regOrg)); - - // Check if there is already an OS_INFO artifact for this file, and add to that if possible. - ArrayList results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId()); - if (results.isEmpty()) { - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); - bbart.addAttributes(bbattributes); - - newArtifacts.add(bbart); - } else { - results.get(0).addAttributes(bbattributes); - } - - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding installed program artifact to blackboard."); //NON-NLS - } - break; - case "Profiler": // NON-NLS - String os = ""; - String procArch = ""; - String tempDir = ""; - for (int j = 0; j < myartlist.getLength(); j++) { - Node artchild = myartlist.item(j); - // If it has attributes, then it is an Element (based off API) - if (artchild.hasAttributes()) { - Element artnode = (Element) artchild; - - String value = artnode.getTextContent().trim(); - String name = artnode.getAttribute("name"); //NON-NLS - switch (name) { - case "OS": // NON-NLS - os = value; - break; - case "PROCESSOR_ARCHITECTURE": // NON-NLS - procArch = value; - break; - case "PROCESSOR_IDENTIFIER": //NON-NLS - break; - case "TEMP": //NON-NLS - tempDir = value; - break; - default: - break; - } - } - } - try { - Collection bbattributes = new ArrayList<>(); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VERSION, parentModuleName, os)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROCESSOR_ARCHITECTURE, parentModuleName, procArch)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TEMP_DIR, parentModuleName, tempDir)); - - // Check if there is already an OS_INFO artifact for this file and add to that if possible - ArrayList results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId()); - if (results.isEmpty()) { - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); - bbart.addAttributes(bbattributes); - - newArtifacts.add(bbart); - } else { - results.get(0).addAttributes(bbattributes); - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding os info artifact to blackboard."); //NON-NLS - } - break; - case "CompName": // NON-NLS - String compName = ""; - String domain = ""; - for (int j = 0; j < myartlist.getLength(); j++) { - Node artchild = myartlist.item(j); - // If it has attributes, then it is an Element (based off API) - if (artchild.hasAttributes()) { - Element artnode = (Element) artchild; - - String value = artnode.getTextContent().trim(); - String name = artnode.getAttribute("name"); //NON-NLS - - if (name.equals("ComputerName")) { // NON-NLS - compName = value; - } else if (name.equals("Domain")) { // NON-NLS - domain = value; - } - } - } - try { - Collection bbattributes = new ArrayList<>(); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, parentModuleName, compName)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, parentModuleName, domain)); - - // Check if there is already an OS_INFO artifact for this file and add to that if possible - ArrayList results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId()); - if (results.isEmpty()) { - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); - bbart.addAttributes(bbattributes); - - newArtifacts.add(bbart); - } else { - results.get(0).addAttributes(bbattributes); - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding os info artifact to blackboard.", ex); //NON-NLS - } - break; - default: - for (int j = 0; j < myartlist.getLength(); j++) { - Node artchild = myartlist.item(j); - // If it has attributes, then it is an Element (based off API) - if (artchild.hasAttributes()) { - Element artnode = (Element) artchild; - - String value = artnode.getTextContent().trim(); - Collection bbattributes = new ArrayList<>(); - - switch (dataType) { - case "recentdocs": //NON-NLS - // BlackboardArtifact bbart = tskCase.getContentById(orgId).newArtifact(ARTIFACT_TYPE.TSK_RECENT_OBJECT); - // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "RecentActivity", dataType, mtime)); - // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), "RecentActivity", dataType, mtimeItem)); - // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(), "RecentActivity", dataType, value)); - // bbart.addAttributes(bbattributes); - // @@@ BC: Why are we ignoring this... - break; - case "usb": //NON-NLS - try { - Long usbMtime = Long.parseLong(artnode.getAttribute("mtime")); //NON-NLS - usbMtime = Long.valueOf(usbMtime.toString()); - - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, usbMtime)); - String dev = artnode.getAttribute("dev"); //NON-NLS - String make = ""; - String model = dev; - if (dev.toLowerCase().contains("vid")) { //NON-NLS - USBInfo info = USB_MAPPER.parseAndLookup(dev); - if (info.getVendor() != null) { - make = info.getVendor(); - } - if (info.getProduct() != null) { - model = info.getProduct(); - } - } - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE, parentModuleName, make)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL, parentModuleName, model)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_ID, parentModuleName, value)); - bbart.addAttributes(bbattributes); - - newArtifacts.add(bbart); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding device attached artifact to blackboard.", ex); //NON-NLS - } - break; - case "uninstall": //NON-NLS - Long itemMtime = null; - try { - String mTimeAttr = artnode.getAttribute("mtime"); - if (mTimeAttr != null && !mTimeAttr.isEmpty()) { - itemMtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(mTimeAttr).getTime(); //NON-NLS - itemMtime /= MS_IN_SEC; - } - } catch (ParseException ex) { - logger.log(Level.SEVERE, "Failed to parse epoch time for installed program artifact.", ex); //NON-NLS - } - - try { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, value)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, itemMtime)); - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_INSTALLED_PROG); - bbart.addAttributes(bbattributes); - - newArtifacts.add(bbart); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding installed program artifact to blackboard.", ex); //NON-NLS - } - break; - case "office": //NON-NLS - String officeName = artnode.getAttribute("name"); //NON-NLS - - try { - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_RECENT_OBJECT); - // @@@ BC: Consider removing this after some more testing. It looks like an Mtime associated with the root key and not the individual item - if (mtime != null) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, parentModuleName, mtime)); - } - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, parentModuleName, officeName)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE, parentModuleName, value)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, artnode.getNodeName())); - bbart.addAttributes(bbattributes); - - newArtifacts.add(bbart); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding recent object artifact to blackboard.", ex); //NON-NLS - } - break; - - case "ProcessorArchitecture": //NON-NLS - // Architecture is now included under Profiler - //try { - // String processorArchitecture = value; - // if (processorArchitecture.equals("AMD64")) - // processorArchitecture = "x86-64"; - - // BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); - // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROCESSOR_ARCHITECTURE.getTypeID(), parentModuleName, processorArchitecture)); - // bbart.addAttributes(bbattributes); - //} catch (TskCoreException ex) { - // logger.log(Level.SEVERE, "Error adding os info artifact to blackboard."); //NON-NLS - //} - break; - - case "ProfileList": //NON-NLS - try { - String homeDir = value; - String sid = artnode.getAttribute("sid"); //NON-NLS - String username = artnode.getAttribute("username"); //NON-NLS - BlackboardArtifact bbart = null; - try { - //check if any of the existing artifacts match this username - ArrayList existingArtifacts = currentCase.getSleuthkitCase().getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT); - for (BlackboardArtifact artifact : existingArtifacts) { - if (artifact.getDataSource().getId() == regFile.getDataSourceObjectId()) { - BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_ID)); - if (attribute != null && attribute.getValueString().equals(sid)) { - bbart = artifact; - break; - } - } - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting existing os account artifact", ex); - } - if (bbart == null) { - //create new artifact - bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, - parentModuleName, username)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID, - parentModuleName, sid)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, - parentModuleName, homeDir)); - } else { - //add attributes to existing artifact - BlackboardAttribute bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_NAME)); - - if (bbattr == null) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, - parentModuleName, username)); - } - bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH)); - if (bbattr == null) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, - parentModuleName, homeDir)); - } - } - bbart.addAttributes(bbattributes); - newArtifacts.add(bbart); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding account artifact to blackboard.", ex); //NON-NLS - } - break; - - case "NtuserNetwork": // NON-NLS - try { - String localPath = artnode.getAttribute("localPath"); //NON-NLS - String remoteName = value; - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_REMOTE_DRIVE); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCAL_PATH, - parentModuleName, localPath)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REMOTE_PATH, - parentModuleName, remoteName)); - bbart.addAttributes(bbattributes); - newArtifacts.add(bbart); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding network artifact to blackboard.", ex); //NON-NLS - } - break; - case "SSID": // NON-NLS - String adapter = artnode.getAttribute("adapter"); //NON-NLS - try { - Long lastWriteTime = Long.parseLong(artnode.getAttribute("writeTime")); //NON-NLS - lastWriteTime = Long.valueOf(lastWriteTime.toString()); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SSID, parentModuleName, value)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, lastWriteTime)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_ID, parentModuleName, adapter)); - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_WIFI_NETWORK); - bbart.addAttributes(bbattributes); - newArtifacts.add(bbart); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding SSID artifact to blackboard.", ex); //NON-NLS - } - break; - case "shellfolders": // NON-NLS - // The User Shell Folders subkey stores the paths to Windows Explorer folders for the current user of the computer - // (https://technet.microsoft.com/en-us/library/Cc962613.aspx). - // No useful information. Skip. - break; - - default: - logger.log(Level.SEVERE, "Unrecognized node name: {0}", dataType); //NON-NLS - break; - } - } - } - break; - } - } // for - return true; - } catch (FileNotFoundException ex) { - logger.log(Level.WARNING, String.format("Error finding the registry file: %s", regFilePath), ex); //NON-NLS - } catch (SAXException ex) { - logger.log(Level.WARNING, String.format("Error parsing the registry XML: %s", regFilePath), ex); //NON-NLS - } catch (IOException ex) { - logger.log(Level.WARNING, String.format("Error building the document parser: %s", regFilePath), ex); //NON-NLS - } catch (ParserConfigurationException ex) { - logger.log(Level.WARNING, String.format("Error configuring the registry parser: %s", regFilePath), ex); //NON-NLS - } finally { - try { - if (fstream != null) { - fstream.close(); - } - } catch (IOException ex) { - } - - postArtifacts(newArtifacts); - } - return false; - } - - - private boolean parseSystemPluginOutput(String regfilePath, AbstractFile regAbstractFile) { - File regfile = new File(regfilePath); - try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { - String line = reader.readLine(); - while (line != null) { - line = line.trim(); - - if (line.matches("^network v.*")) { - // parseNetworkInterfaces(regfile, reader); - } else if (line.matches("^bthport v..*")) { - parseBlueToothDevices(regAbstractFile, reader) ; - } - line = reader.readLine(); - } - return true; - } catch (FileNotFoundException ex) { - logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS - } catch (IOException ex) { - logger.log(Level.WARNING, "Error reading the system hive: {0}", ex); //NON-NLS - } - - return false; - - } - - /** - * Create recently used artifacts to parse the regripper plugin output, this format is used in several diffent plugins - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseBlueToothDevices(AbstractFile regFile, BufferedReader reader) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - // date format for plugin Tue Jun 23 10:27:54 2020 Z - SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US); - Long bthLastSeen = Long.valueOf(0); - Long bthLastConnected = Long.valueOf(0); - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - if (line.contains("Device Unique ID")) { - // Columns are seperated by colons : - // Data : Values - // Record is 4 lines in length (Device Unique Id, Name, Last Seen, LastConnected - while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Radio Support not found")) { - // Split line on "> " which is the record delimiter between position and file - String deviceTokens[] = line.split(": "); - String deviceUniqueId = deviceTokens[1]; - line = reader.readLine(); - // Default device name to unknown as a device name may not exist. - String deviceName = "Unknown"; - if (line.contains("Name")) { - String nameTokens[] = line.split(": "); - deviceName = nameTokens[1]; - line = reader.readLine(); - } - String lastSeenTokens[] = line.split(": "); - String lastSeen = lastSeenTokens[1].replace(" Z", "");; - line = reader.readLine(); - String lastConnectedTokens[] = line.split(": "); - String lastConnected = lastConnectedTokens[1].replace(" Z", "");; - try { - Date usedSeenDate = dateFormat.parse(lastSeen); - bthLastSeen = usedSeenDate.getTime()/1000; - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed - // we set the timestamp to 0 and continue on processing - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last Seen attribute.", lastSeen), ex); //NON-NLS - } - try { - Date usedConnectedDate = dateFormat.parse(lastConnected); - bthLastConnected = usedConnectedDate.getTime()/1000; - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed - // we set the timestamp to 0 and continue on processing - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last connected attribute.", lastSeen), ex); //NON-NLS - } - - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_DEVICE_ID, getName(), deviceUniqueId)); - attributes.add(new BlackboardAttribute(TSK_NAME, getName(), deviceName)); - attributes.add(new BlackboardAttribute(TSK_DATETIME, getName(), bthLastSeen)); - attributes.add(new BlackboardAttribute(TSK_DATETIME_ACCESSED, getName(), bthLastConnected)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - } - // Read blank line between records then next read line is start of next block - line = reader.readLine(); - line = reader.readLine(); - } - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Parse the output of the SAM regripper plugin to get additional Account - * information - * - * @param regFilePath the path to the registry file being parsed - * @param regAbstractFile the file to associate newly created artifacts with - * - * @return true if successful, false if parsing failed at some point - */ - private boolean parseSamPluginOutput(String regFilePath, AbstractFile regAbstractFile) { - File regfile = new File(regFilePath); - List newArtifacts = new ArrayList<>(); - try (BufferedReader bufferedReader = new BufferedReader(new FileReader(regfile))) { - // Read the file in and create a Document and elements - String userInfoSection = "User Information"; - String previousLine = null; - String line = bufferedReader.readLine(); - Set> userSet = new HashSet<>(); - Map> groupMap = null; - while (line != null) { - if (line.contains(SECTION_DIVIDER) && previousLine != null && previousLine.contains(userInfoSection)) { - readUsers(bufferedReader, userSet); - } - - if (line.contains(SECTION_DIVIDER) && previousLine != null && previousLine.contains("Group Membership Information")) { - groupMap = readGroups(bufferedReader); - } - - previousLine = line; - line = bufferedReader.readLine(); - } - Map> userInfoMap = new HashMap<>(); - //load all the user info which was read into a map - for (Map userInfo : userSet) { - userInfoMap.put(userInfo.get(SID_KEY), userInfo); - } - //get all existing OS account artifacts - List existingOsAccounts = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT); - for (BlackboardArtifact osAccount : existingOsAccounts) { - //if the OS Account artifact was from the same data source check the user id - if (osAccount.getDataSource().getId() == regAbstractFile.getDataSourceObjectId()) { - BlackboardAttribute existingUserId = osAccount.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_ID)); - if (existingUserId != null) { - String userID = existingUserId.getValueString().trim(); - Map userInfo = userInfoMap.remove(userID); - //if the existing user id matches a user id which we parsed information for check if that information exists and if it doesn't add it - if (userInfo != null) { - osAccount.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userID), true, regAbstractFile)); - } - } - } - } - - //add remaining userinfos as accounts; - for (Map userInfo : userInfoMap.values()) { - BlackboardArtifact bbart = regAbstractFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT); - bbart.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userInfo.get(SID_KEY)), false, regAbstractFile)); - // index the artifact for keyword search - newArtifacts.add(bbart); - } - // Get a mapping of user sids to user names and save globally so it can be used for other areas - // of the registry, ie: BAM key - try { - userNameMap = makeUserNameMap(dataSource); - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Unable to create OS Account user name map", ex); - // This is not the end of the world we will just continue without - // user names - userNameMap = new HashMap<>(); - } - return true; - } catch (FileNotFoundException ex) { - logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS - } catch (IOException ex) { - logger.log(Level.WARNING, "Error building the document parser: {0}", ex); //NON-NLS - } catch (ParseException ex) { - logger.log(Level.WARNING, "Error parsing the the date from the registry file", ex); //NON-NLS - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error updating TSK_OS_ACCOUNT artifacts to include newly parsed data.", ex); //NON-NLS - } finally { - postArtifacts(newArtifacts); - } - return false; - } - - /** - * Creates the attribute list for the given user information and group list. - * - * @param userInfo Map of key\value pairs of user information - * @param groupList List of the groups that user belongs - * @param existingUser - * - * @return List - * - * @throws ParseException - */ - Collection getAttributesForAccount(Map userInfo, List groupList, boolean existingUser, AbstractFile regAbstractFile) throws ParseException { - Collection bbattributes = new ArrayList<>(); - - SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'"); - regRipperTimeFormat.setTimeZone(getTimeZone("GMT")); - - if (!existingUser) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID, - getRAModuleName(), userInfo.get(SID_KEY))); - - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, - this.moduleName, userInfo.get(USERNAME_KEY))); - } - - String value = userInfo.get(ACCOUNT_CREATED_KEY); - if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, - getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); - } - - value = userInfo.get(LAST_LOGIN_KEY); - if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, - getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); - } - - value = userInfo.get(LOGIN_COUNT_KEY); - if (value != null && !value.isEmpty()) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, - getRAModuleName(), Integer.parseInt(value))); - } - - value = userInfo.get(ACCOUNT_TYPE_KEY); - if (value != null && !value.isEmpty()) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, - getRAModuleName(), value)); - } - - value = userInfo.get(USER_COMMENT_KEY); - if (value != null && !value.isEmpty()) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DESCRIPTION, - getRAModuleName(), value)); - } - - value = userInfo.get(NAME_KEY); - if (value != null && !value.isEmpty()) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, - getRAModuleName(), value)); - } - - value = userInfo.get(INTERNET_NAME_KEY); - if (value != null && !value.isEmpty()) { - try { - // Create an account for this email, if it doesn't already exist. - Case.getCurrentCaseThrows() - .getSleuthkitCase() - .getCommunicationsManager() - .createAccountFileInstance(Account.Type.EMAIL, - value, getRAModuleName(), regAbstractFile); - } catch (NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.SEVERE, - String.format("Error adding email account with value " - + "%s, to the case database for file %s [objId=%d]", - value, regAbstractFile.getName(), regAbstractFile.getId()), ex); - } - - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL, - getRAModuleName(), value)); - } - - value = userInfo.get(FULL_NAME_KEY); - if (value != null && !value.isEmpty()) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DISPLAY_NAME, - getRAModuleName(), value)); - } - - value = userInfo.get(PWD_RESET_KEY); - if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_RESET, - getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); - } - - value = userInfo.get(PASSWORD_HINT); - if (value != null && !value.isEmpty()) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_HINT, - getRAModuleName(), value)); - } - - value = userInfo.get(PWD_FAILE_KEY); - if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_FAIL, - getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); - } - - String settingString = ""; - for (String setting : PASSWORD_SETTINGS_FLAGS) { - if (userInfo.containsKey(setting)) { - settingString += setting + ", "; - } - } - - if (!settingString.isEmpty()) { - settingString = settingString.substring(0, settingString.length() - 2); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_SETTINGS, - getRAModuleName(), settingString)); - } - - settingString = ""; - for (String setting : ACCOUNT_SETTINGS_FLAGS) { - if (userInfo.containsKey(setting)) { - settingString += setting + ", "; - } - } - - if (!settingString.isEmpty()) { - settingString = settingString.substring(0, settingString.length() - 2); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_SETTINGS, - getRAModuleName(), settingString)); - } - - settingString = ""; - for (String setting : ACCOUNT_TYPE_FLAGS) { - if (userInfo.containsKey(setting)) { - settingString += setting + ", "; - } - } - - if (!settingString.isEmpty()) { - settingString = settingString.substring(0, settingString.length() - 2); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_FLAG, - getRAModuleName(), settingString)); - } - - if (groupList != null && groupList.isEmpty()) { - String groups = ""; - for (String group : groupList) { - groups += group + ", "; - } - - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GROUPS, - getRAModuleName(), groups.substring(0, groups.length() - 2))); - } - - return bbattributes; - } - - /** - * Read the User Information section of the SAM regripper plugin's output - * and collect user account information from the file. - * - * @param bufferedReader a buffered reader for the file which contains the - * user information - * @param users the set to add UserInfo objects representing the - * users found to - * - * @throws IOException - */ - private void readUsers(BufferedReader bufferedReader, Set> users) throws IOException { - String line = bufferedReader.readLine(); - //read until end of file or next section divider - String userName = ""; - String user_rid = ""; - while (line != null && !line.contains(SECTION_DIVIDER)) { - //when a user name field exists read the name and id number - if (line.contains(USERNAME_KEY)) { - String regx = USERNAME_KEY + "\\s*?:"; - String userNameAndIdString = line.replaceAll(regx, ""); - userName = userNameAndIdString.substring(0, userNameAndIdString.lastIndexOf('[')).trim(); - user_rid = userNameAndIdString.substring(userNameAndIdString.lastIndexOf('['), userNameAndIdString.lastIndexOf(']')); - } else if (line.contains(SID_KEY) && !userName.isEmpty()) { - Map.Entry entry = getSAMKeyValue(line); - - HashMap userInfo = new HashMap<>(); - userInfo.put(USERNAME_KEY, userName); - userInfo.put(RID_KEY, user_rid); - userInfo.put(entry.getKey(), entry.getValue()); - - //continue reading this users information until end of file or a blank line between users - line = bufferedReader.readLine(); - while (line != null && !line.isEmpty()) { - entry = getSAMKeyValue(line); - if (entry != null) { - userInfo.put(entry.getKey(), entry.getValue()); - } - line = bufferedReader.readLine(); - } - users.add(userInfo); - - userName = ""; - } - line = bufferedReader.readLine(); - } - } - - /** - * Create recently used artifacts from NTUSER regripper files - * - * @param regFileName name of the regripper output file - * - * @param regFile registry file the artifact is associated with - * - * @throws FileNotFound and IOException - */ - private void createRecentlyUsedArtifacts(String regFileName, AbstractFile regFile) throws FileNotFoundException, IOException { - File regfile = new File(regFileName); - try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { - String line = reader.readLine(); - while (line != null) { - line = line.trim(); - - if (line.matches("^adoberdr v.*")) { - parseAdobeMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Adobe()); - } else if (line.matches("^mpmru v.*")) { - parseMediaPlayerMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Mediaplayer()); - } else if (line.matches("^trustrecords v.*")) { - parseOfficeTrustRecords(regFile, reader, Bundle.Recently_Used_Artifacts_Office_Trustrecords()); - } else if (line.matches("^ArcHistory:")) { - parse7ZipMRU(regFile, reader, Bundle.Recently_Used_Artifacts_ArcHistory()); - } else if (line.matches("^applets v.*")) { - parseGenericMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Applets()); - } else if (line.matches("^mmc v.*")) { - parseGenericMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Mmc()); - } else if (line.matches("^winrar v.*")) { - parseWinRARMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Winrar()); - } else if (line.matches("^officedocs2010 v.*")) { - parseOfficeDocs2010MRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Officedocs()); - } - line = reader.readLine(); - } - } - } - - /** - * Create artifacts from the System registry Hive - * - * @param regFileName name of the regripper output file - * - * @param regFile registry file the artifact is associated with - * - * @throws FileNotFound and IOException - */ - private void createSystemArtifacts(String regFileName, AbstractFile regFile) throws FileNotFoundException, IOException { - File regfile = new File(regFileName); - try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { - String line = reader.readLine(); - while (line != null) { - line = line.trim(); - - if (line.matches("^bam v.*")) { - parseBamKey(regFile, reader, Bundle.Registry_System_Bam()); - } - line = reader.readLine(); - } - } - } - - /** - * Create artifacts from BAM Regripper Plugin records - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseBamKey(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - // Read thru first bam output to get to second bam output which is the same but delimited - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - } - line = reader.readLine(); - line = line.trim(); - while (!line.contains(SECTION_DIVIDER)) { - // Split the line into it parts based on delimiter of "|" - // 1570493613|BAM|||\Device\HarddiskVolume3\Program Files\TechSmith\Snagit 2018\Snagit32.exe (S-1-5-21-3042408413-2583535980-1301764466-1001) - String tokens[] = line.split("\\|"); - Long progRunDateTime = Long.valueOf(tokens[0]); - // Split on " (S-" as this signifies a User SID, if S- not used then may have issues becuase of (x86) in path is valid. - // We can add the S- back to the string that we split on since S- is a valid beginning of a User SID - String fileNameSid[] = tokens[4].split("\\s+\\(S-"); - String userSid = "S-" + fileNameSid[1].substring(0, fileNameSid[1].length() - 1); - String userName = userNameMap.get(userSid); - if (userName == null) { - userName = userSid; - } - String fileName = fileNameSid[0]; - if (fileName.startsWith("\\Device\\HarddiskVolume")) { - // Start at point past the 2nd slash - int fileNameStart = fileName.indexOf('\\', 16); - fileName = fileName.substring(fileNameStart, fileName.length()); - - } - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(), fileName)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, getName(), userName)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), progRunDateTime)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_PROG_RUN, regFile, attributes); - if (bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = reader.readLine(); - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts from adobemru Regripper Plugin records - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseAdobeMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - SimpleDateFormat adobePluginDateFormat = new SimpleDateFormat("yyyyMMddHHmmssZ", US); - Long adobeUsedTime = Long.valueOf(0); - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - if (line.matches("^Key name,file name,sDate,uFileSize,uPageCount")) { - line = reader.readLine(); - // Columns are - // Key name, file name, sDate, uFileSize, uPageCount - while (!line.contains(SECTION_DIVIDER)) { - // Split csv line, handles double quotes around individual file names - // since file names can contain commas - String tokens[] = line.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"); - String fileName = tokens[1].substring(0, tokens[1].length() -1); - fileName = fileName.replace("\"", ""); - if (fileName.charAt(0) == '/') { - fileName = fileName.substring(1,fileName.length() - 1); - fileName = fileName.replaceFirst("/", ":/"); - } - // Check to see if more then 2 tokens, Date may not be populated, will default to 0 - if (tokens.length > 2) { - // Time in the format of 20200131104456-05'00' - try { - String fileUsedTime = tokens[2].replaceAll("'",""); - Date usedDate = adobePluginDateFormat.parse(fileUsedTime); - adobeUsedTime = usedDate.getTime()/1000; - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed - // we set the timestamp to 0 and continue on processing - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for adobe file artifact.", tokens[2]), ex); //NON-NLS - } - } - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), adobeUsedTime)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - fileName = fileName.replace("\0", ""); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = reader.readLine(); - } - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts to parse the Media Player MRU regripper (mpmru) records - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseMediaPlayerMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - if (line.contains("LastWrite")) { - line = reader.readLine(); - // Columns are - // FileX -> - while (!line.contains(SECTION_DIVIDER) && !line.contains("RecentFileList has no values.")) { - // Split line on "> " which is the record delimiter between position and file - String tokens[] = line.split("> "); - String fileName = tokens[1]; - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(fileName, bba); - if (bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - } - line = reader.readLine(); - } - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts to parse the regripper plugin output, this format is used in several diffent plugins - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseGenericMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - if (line.contains("LastWrite")) { - line = reader.readLine(); - // Columns are - // FileX -> - while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Applets") - && !line.contains(("Recent File List"))) { - // Split line on "> " which is the record delimiter between position and file - String tokens[] = line.split("> "); - String fileName = tokens[1]; - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = reader.readLine(); - } - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts to parse the WinRAR Regripper plugin output - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseWinRARMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - if (line.contains("LastWrite")) { - line = reader.readLine(); - // Columns are - // FileX -> - if (!line.isEmpty()) { - while (!line.contains(SECTION_DIVIDER)) { - // Split line on "> " which is the record delimiter between position and file - String tokens[] = line.split("> "); - String fileName = tokens[1]; - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = reader.readLine(); - } - } - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts to parse the runmru ArcHistory (7Zip) regripper plugin records - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parse7ZipMRU(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - line = line.trim(); - if (!line.contains("PathHistory:")) { - while (!line.contains("PathHistory:") && !line.isEmpty()) { - // Columns are - // - String fileName = line; - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if (bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = reader.readLine(); - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts to parse the Office Documents 2010 records Regripper Plugin output - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseOfficeDocs2010MRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - line = line.trim(); - // Reading to the SECTION DIVIDER to get next section of records to process. Dates appear to have - // multiple spaces in them that makes it harder to parse so next section will be easier to parse - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - } - line = reader.readLine(); - while (!line.contains(SECTION_DIVIDER)) { - // record has the following format - // 1294283922|REG|||OfficeDocs2010 - F:\Windows_time_Rules_xp.doc - String tokens[] = line.split("\\|"); - Long docDate = Long.valueOf(tokens[0]); - String fileNameTokens[] = tokens[4].split(" - "); - String fileName = fileNameTokens[1]; - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), docDate)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = reader.readLine(); - line = line.trim(); - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts to parse the Office trust records (trustrecords) Regipper plugin records - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseOfficeTrustRecords(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - String userProfile = regFile.getParentPath(); - userProfile = userProfile.substring(0, userProfile.length() - 1); - List bbartifacts = new ArrayList<>(); - SimpleDateFormat pluginDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", US); - Long usedTime = Long.valueOf(0); - String line = reader.readLine(); - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - usedTime = Long.valueOf(0); - if (!line.contains("**") && !line.contains("----------") && !line.contains("LastWrite") - && !line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("TrustRecords")) { - // Columns are - // Date : / - // Split line on " : " which is the record delimiter between position and file - String fileName = null; - String tokens[] = line.split(" : "); - fileName = tokens[1]; - fileName = fileName.replace("%USERPROFILE%", userProfile); - // Time in the format of Wed May 31 14:33:03 2017 Z - try { - String fileUsedTime = tokens[0].replaceAll(" Z",""); - Date usedDate = pluginDateFormat.parse(fileUsedTime); - usedTime = usedDate.getTime()/1000; - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed - // we set the timestamp to 0 and continue on processing - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for TrustRecords artifact.", tokens[0]), ex); //NON-NLS - } - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), usedTime)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create associated artifacts using file name and path and the artifact it associates with - * - * @param filePathName file and path of object being associated with - * - * @param bba blackboard artifact to associate with - * - * @returnv BlackboardArtifact or a null value - */ - private BlackboardArtifact createAssociatedArtifact(String filePathName, BlackboardArtifact bba) { - org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager(); - String fileName = FilenameUtils.getName(filePathName); - String filePath = FilenameUtils.getPath(filePathName); - List sourceFiles; - try { - sourceFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS - if (!sourceFiles.isEmpty()) { - for (AbstractFile sourceFile : sourceFiles) { - if (sourceFile.getParentPath().endsWith(filePath)) { - Collection bbattributes2 = new ArrayList<>(); - bbattributes2.addAll(Arrays.asList( - new BlackboardAttribute(TSK_ASSOCIATED_ARTIFACT, this.getName(), - bba.getArtifactID()))); - - BlackboardArtifact associatedObjectBba = createArtifactWithAttributes(TSK_ASSOCIATED_OBJECT, sourceFile, bbattributes2); - if (associatedObjectBba != null) { - return associatedObjectBba; - } - } - } - } - } catch (TskCoreException ex) { - // only catching the error and displaying the message as the file may not exist on the - // system anymore - logger.log(Level.WARNING, String.format("Error finding actual file %s. file may not exist", filePathName)); //NON-NLS - } - - return null; - } - - /** - * Create a map of userids to usernames from the OS Accounts. - * - * @param dataSource - * - * @return A Map of userIDs and userNames - * - * @throws TskCoreException - */ - private Map makeUserNameMap(Content dataSource) throws TskCoreException { - Map userNameMap = new HashMap<>(); - - List accounts = blackboard.getArtifacts(TSK_OS_ACCOUNT.getTypeID(), dataSource.getId()); - - for (BlackboardArtifact account : accounts) { - BlackboardAttribute nameAttribute = getAttributeForArtifact(account, TSK_USER_NAME); - BlackboardAttribute idAttribute = getAttributeForArtifact(account, TSK_USER_ID); - - String userName = nameAttribute != null ? nameAttribute.getDisplayString() : ""; - String userID = idAttribute != null ? idAttribute.getDisplayString() : ""; - - if (!userID.isEmpty()) { - userNameMap.put(userID, userName); - } - } - - return userNameMap; - } - - /** - * Gets the attribute for the given type from the given artifact. - * - * @param artifact BlackboardArtifact to get the attribute from - * @param type The BlackboardAttribute Type to get - * - * @return BlackboardAttribute for given artifact and type - * - * @throws TskCoreException - */ - private BlackboardAttribute getAttributeForArtifact(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) throws TskCoreException { - return artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(type.getTypeID()))); - } - - /** - * Create the shellbag artifacts from the list of ShellBag objects. - * - * @param regFile The data source file - * @param shellbags List of shellbags from source file - * - * @throws TskCoreException - */ - void createShellBagArtifacts(AbstractFile regFile, List shellbags) throws TskCoreException { - List artifacts = new ArrayList<>(); - try{ - for (ShellBag bag : shellbags) { - Collection attributes = new ArrayList<>(); - BlackboardArtifact artifact = regFile.newArtifact(getShellBagArtifact().getTypeID()); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), bag.getResource())); - attributes.add(new BlackboardAttribute(getKeyAttribute(), getName(), bag.getKey())); - - long time; - time = bag.getLastWrite(); - if (time != 0) { - attributes.add(new BlackboardAttribute(getLastWriteAttribute(), getName(), time)); - } - - time = bag.getModified(); - if (time != 0) { - attributes.add(new BlackboardAttribute(TSK_DATETIME_MODIFIED, getName(), time)); - } - - time = bag.getCreated(); - if (time != 0) { - attributes.add(new BlackboardAttribute(TSK_DATETIME_CREATED, getName(), time)); - } - - time = bag.getAccessed(); - if (time != 0) { - attributes.add(new BlackboardAttribute(TSK_DATETIME_ACCESSED, getName(), time)); - } - - artifact.addAttributes(attributes); - - artifacts.add(artifact); - } - } finally { - postArtifacts(artifacts); - } - } - - /** - * Returns the custom Shellbag artifact type or creates it if it does not - * currently exist. - * - * @return BlackboardArtifact.Type for shellbag artifacts - * - * @throws TskCoreException - */ - private BlackboardArtifact.Type getShellBagArtifact() throws TskCoreException { - if (shellBagArtifactType == null) { - shellBagArtifactType = tskCase.getArtifactType(SHELLBAG_ARTIFACT_NAME); - - if(shellBagArtifactType == null) { - try { - tskCase.addBlackboardArtifactType(SHELLBAG_ARTIFACT_NAME, Bundle.Shellbag_Artifact_Display_Name()); //NON-NLS - } catch (TskDataException ex) { - // Artifact already exists - logger.log(Level.INFO, String.format("%s may have already been defined for this case", SHELLBAG_ARTIFACT_NAME)); - } - - shellBagArtifactType = tskCase.getArtifactType(SHELLBAG_ARTIFACT_NAME); - } - } - - return shellBagArtifactType; - } - - /** - * Gets the custom BlackboardAttribute type. The attribute type is created - * if it does not currently exist. - * - * @return The BlackboardAttribute type - * - * @throws TskCoreException - */ - private BlackboardAttribute.Type getLastWriteAttribute() throws TskCoreException { - if (shellBagLastWriteAttributeType == null) { - try { - shellBagLastWriteAttributeType = tskCase.addArtifactAttributeType(SHELLBAG_ATTRIBUTE_LAST_WRITE, - BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME, - Bundle.Shellbag_Last_Write_Attribute_Display_Name()); - } catch (TskDataException ex) { - // Attribute already exists get it from the case - shellBagLastWriteAttributeType = tskCase.getAttributeType(SHELLBAG_ATTRIBUTE_LAST_WRITE); - } - } - return shellBagLastWriteAttributeType; - } - - /** - * Gets the custom BlackboardAttribute type. The attribute type is created - * if it does not currently exist. - * - * @return The BlackboardAttribute type - * - * @throws TskCoreException - */ - private BlackboardAttribute.Type getKeyAttribute() throws TskCoreException { - if (shellBagKeyAttributeType == null) { - try { - shellBagKeyAttributeType = tskCase.addArtifactAttributeType(SHELLBAG_ATTRIBUTE_KEY, - BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, - Bundle.Shellbag_Key_Attribute_Display_Name()); - } catch (TskDataException ex) { - // The attribute already exists get it from the case - shellBagKeyAttributeType = tskCase.getAttributeType(SHELLBAG_ATTRIBUTE_KEY); - } - } - return shellBagKeyAttributeType; - } - - /** - * Maps the user groups to the sid that are a part of them. - * - * @param bufferedReader - * - * @return A map if sid and the groups they map too - * - * @throws IOException - */ - Map> readGroups(BufferedReader bufferedReader) throws IOException { - Map> groupMap = new HashMap<>(); - - String line = bufferedReader.readLine(); - - int userCount = 0; - String groupName = null; - - while (line != null && !line.contains(SECTION_DIVIDER)) { - - if (line.contains("Group Name")) { - String value = line.replaceAll("Group Name\\s*?:", "").trim(); - groupName = (value.replaceAll("\\[\\d*?\\]", "")).trim(); - int startIndex = value.indexOf('['); - int endIndex = value.indexOf(']'); - - if (startIndex != -1 && endIndex != -1) { - String countStr = value.substring(startIndex + 1, endIndex); - userCount = Integer.parseInt(countStr); - } - } else if (line.matches("Users\\s*?:")) { - for (int i = 0; i < userCount; i++) { - line = bufferedReader.readLine(); - if (line != null) { - String sid = line.trim(); - List groupList = groupMap.get(sid); - if (groupList == null) { - groupList = new ArrayList<>(); - groupMap.put(sid, groupList); - } - groupList.add(groupName); - } - } - groupName = null; - } - line = bufferedReader.readLine(); - } - return groupMap; - } - - /** - * Gets the key value from user account strings of the format - * key:value or - * --> value - * - * @param line String to parse - * - * @return key value pair - */ - private Map.Entry getSAMKeyValue(String line) { - int index = line.indexOf(':'); - Map.Entry returnValue = null; - String key = null; - String value = null; - - if (index != -1) { - key = line.substring(0, index).trim(); - if (index + 1 < line.length()) { - value = line.substring(index + 1).trim(); - } else { - value = ""; - } - - } else if (line.contains("-->")) { - key = line.replace("-->", "").trim(); - value = "true"; - } - - if (key != null) { - returnValue = new AbstractMap.SimpleEntry<>(key, value); - } - - return returnValue; - } - - @Override - public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) { - this.dataSource = dataSource; - this.context = context; - - progressBar.progress(Bundle.Progress_Message_Analyze_Registry()); - analyzeRegistryFiles(); - - } - - /** - * Private wrapper class for Registry output files - */ - private class RegOutputFiles { - - public String autopsyPlugins = ""; - public String fullPlugins = ""; - } -} +/* + * + * Autopsy Forensic Browser + * + * Copyright 2012-2020 Basis Technology Corp. + * + * Copyright 2012 42six Solutions. + * Contact: aebadirad 42six com + * Project Contact/Architect: carrier sleuthkit 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.recentactivity; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringReader; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.logging.Level; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.apache.commons.io.FilenameUtils; +import org.openide.modules.InstalledFileLocator; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.ExecUtil; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator; +import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.recentactivity.UsbDeviceIdMapper.USBInfo; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; +import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import java.nio.file.Path; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; +import java.util.Set; +import java.util.HashSet; +import static java.util.Locale.US; +import static java.util.TimeZone.getTimeZone; +import org.openide.util.Lookup; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; +import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; +import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; +import org.sleuthkit.autopsy.recentactivity.ShellBagParser.ShellBag; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Account; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_ACCOUNT; +import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; +import org.sleuthkit.datamodel.Report; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskDataException; + +/** + * Extract windows registry data using regripper. Runs two versions of + * regripper. One is the generally available set of plug-ins and the second is a + * set that were customized for Autopsy to produce a more structured output of + * XML so that we can parse and turn into blackboard artifacts. + */ +@NbBundle.Messages({ + "RegRipperNotFound=Autopsy RegRipper executable not found.", + "RegRipperFullNotFound=Full version RegRipper executable not found.", + "Progress_Message_Analyze_Registry=Analyzing Registry Files", + "Shellbag_Artifact_Display_Name=Shell Bags", + "Shellbag_Key_Attribute_Display_Name=Key", + "Shellbag_Last_Write_Attribute_Display_Name=Last Write", + "Recently_Used_Artifacts_Office_Trustrecords=Stored in TrustRecords because Office security exception was granted", + "Recently_Used_Artifacts_ArcHistory=Recently opened by 7Zip", + "Recently_Used_Artifacts_Applets=Recently opened according to Applets registry key", + "Recently_Used_Artifacts_Mmc=Recently opened according to Windows Management Console MRU", + "Recently_Used_Artifacts_Winrar=Recently opened according to WinRAR MRU", + "Recently_Used_Artifacts_Officedocs=Recently opened according to Office MRU", + "Recently_Used_Artifacts_Adobe=Recently opened according to Adobe MRU", + "Recently_Used_Artifacts_Mediaplayer=Recently opened according to Media Player MRU", + "Registry_System_Bam=Recently Executed according to Background Activity Moderator (BAM)" +}) +class ExtractRegistry extends Extract { + + private static final String USERNAME_KEY = "Username"; //NON-NLS + private static final String SID_KEY = "SID"; //NON-NLS + private static final String RID_KEY = "RID"; //NON-NLS + private static final String ACCOUNT_CREATED_KEY = "Account Created"; //NON-NLS + private static final String LAST_LOGIN_KEY = "Last Login Date"; //NON-NLS + private static final String LOGIN_COUNT_KEY = "Login Count"; //NON-NLS + private static final String FULL_NAME_KEY = "Full Name"; //NON-NLS + private static final String USER_COMMENT_KEY = "User Comment"; //NON-NLS + private static final String ACCOUNT_TYPE_KEY = "Account Type"; //NON-NLS + private static final String NAME_KEY = "Name"; //NON-NLS + private static final String PWD_RESET_KEY = "Pwd Rest Date"; //NON-NLS + private static final String PWD_FAILE_KEY = "Pwd Fail Date"; //NON-NLS + private static final String INTERNET_NAME_KEY = "InternetName"; //NON-NLS + private static final String PWD_DOES_NOT_EXPIRE_KEY = "Password does not expire"; //NON-NLS + private static final String ACCOUNT_DISABLED_KEY = "Account Disabled"; //NON-NLS + private static final String PWD_NOT_REQUIRED_KEY = "Password not required"; //NON-NLS + private static final String NORMAL_ACCOUNT_KEY = "Normal user account"; //NON-NLS + private static final String HOME_DIRECTORY_REQUIRED_KEY = "Home directory required"; + private static final String TEMPORARY_DUPLICATE_ACCOUNT = "Temporary duplicate account"; + private static final String MNS_LOGON_ACCOUNT_KEY = "MNS logon user account"; + private static final String INTERDOMAIN_TRUST_ACCOUNT_KEY = "Interdomain trust account"; + private static final String WORKSTATION_TRUST_ACCOUNT = "Workstation trust account"; + private static final String SERVER_TRUST_ACCOUNT = "Server trust account"; + private static final String ACCOUNT_AUTO_LOCKED = "Account auto locked"; + private static final String PASSWORD_HINT = "Password Hint"; + + private static final String[] PASSWORD_SETTINGS_FLAGS = {PWD_DOES_NOT_EXPIRE_KEY, PWD_NOT_REQUIRED_KEY}; + private static final String[] ACCOUNT_SETTINGS_FLAGS = {ACCOUNT_AUTO_LOCKED, HOME_DIRECTORY_REQUIRED_KEY, ACCOUNT_DISABLED_KEY}; + private static final String[] ACCOUNT_TYPE_FLAGS = {NORMAL_ACCOUNT_KEY, SERVER_TRUST_ACCOUNT, WORKSTATION_TRUST_ACCOUNT, INTERDOMAIN_TRUST_ACCOUNT_KEY, MNS_LOGON_ACCOUNT_KEY, TEMPORARY_DUPLICATE_ACCOUNT}; + + final private static UsbDeviceIdMapper USB_MAPPER = new UsbDeviceIdMapper(); + final private static String RIP_EXE = "rip.exe"; + final private static String RIP_PL = "rip.pl"; + final private static String RIP_PL_INCLUDE_FLAG = "-I"; + final private static int MS_IN_SEC = 1000; + final private static String NEVER_DATE = "Never"; + final private static String SECTION_DIVIDER = "-------------------------"; + final private static Logger logger = Logger.getLogger(ExtractRegistry.class.getName()); + private final List rrCmd = new ArrayList<>(); + private final List rrFullCmd = new ArrayList<>(); + private final Path rrHome; // Path to the Autopsy version of RegRipper + private final Path rrFullHome; // Path to the full version of RegRipper + private Content dataSource; + private IngestJobContext context; + private Map userNameMap; + + private static final String SHELLBAG_ARTIFACT_NAME = "RA_SHELL_BAG"; //NON-NLS + private static final String SHELLBAG_ATTRIBUTE_LAST_WRITE = "RA_SHELL_BAG_LAST_WRITE"; //NON-NLS + private static final String SHELLBAG_ATTRIBUTE_KEY= "RA_SHELL_BAG_KEY"; //NON-NLS + + BlackboardArtifact.Type shellBagArtifactType = null; + BlackboardAttribute.Type shellBagKeyAttributeType = null; + BlackboardAttribute.Type shellBagLastWriteAttributeType = null; + + ExtractRegistry() throws IngestModuleException { + moduleName = NbBundle.getMessage(ExtractIE.class, "ExtractRegistry.moduleName.text"); + + final File rrRoot = InstalledFileLocator.getDefault().locate("rr", ExtractRegistry.class.getPackage().getName(), false); //NON-NLS + if (rrRoot == null) { + throw new IngestModuleException(Bundle.RegRipperNotFound()); + } + + final File rrFullRoot = InstalledFileLocator.getDefault().locate("rr-full", ExtractRegistry.class.getPackage().getName(), false); //NON-NLS + if (rrFullRoot == null) { + throw new IngestModuleException(Bundle.RegRipperFullNotFound()); + } + + String executableToRun = RIP_EXE; + if (!PlatformUtil.isWindowsOS()) { + executableToRun = RIP_PL; + } + rrHome = rrRoot.toPath(); + String rrPath = rrHome.resolve(executableToRun).toString(); + rrFullHome = rrFullRoot.toPath(); + + if (!(new File(rrPath).exists())) { + throw new IngestModuleException(Bundle.RegRipperNotFound()); + } + String rrFullPath = rrFullHome.resolve(executableToRun).toString(); + if (!(new File(rrFullPath).exists())) { + throw new IngestModuleException(Bundle.RegRipperFullNotFound()); + } + if (PlatformUtil.isWindowsOS()) { + rrCmd.add(rrPath); + rrFullCmd.add(rrFullPath); + } else { + String perl; + File usrBin = new File("/usr/bin/perl"); + File usrLocalBin = new File("/usr/local/bin/perl"); + if (usrBin.canExecute() && usrBin.exists() && !usrBin.isDirectory()) { + perl = "/usr/bin/perl"; + } else if (usrLocalBin.canExecute() && usrLocalBin.exists() && !usrLocalBin.isDirectory()) { + perl = "/usr/local/bin/perl"; + } else { + throw new IngestModuleException("perl not found in your system"); + } + rrCmd.add(perl); + rrCmd.add(RIP_PL_INCLUDE_FLAG); + rrCmd.add(rrHome.toString()); + rrCmd.add(rrPath); + rrFullCmd.add(perl); + rrFullCmd.add(RIP_PL_INCLUDE_FLAG); + rrFullCmd.add(rrFullHome.toString()); + rrFullCmd.add(rrFullPath); + } + } + + /** + * Search for the registry hives on the system. + */ + private List findRegistryFiles() { + List allRegistryFiles = new ArrayList<>(); + org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager(); + + // find the sam hives', process this first so we can map the user id's and sids for later use + try { + allRegistryFiles.addAll(fileManager.findFiles(dataSource, "sam", "/system32/config")); //NON-NLS + } catch (TskCoreException ex) { + String msg = NbBundle.getMessage(this.getClass(), + "ExtractRegistry.findRegFiles.errMsg.errReadingFile", "sam"); + logger.log(Level.WARNING, msg, ex); + this.addErrorMessage(this.getName() + ": " + msg); + } + + + // find the user-specific ntuser-dat files + try { + allRegistryFiles.addAll(fileManager.findFiles(dataSource, "ntuser.dat")); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error fetching 'ntuser.dat' file."); //NON-NLS + } + + // find the user-specific ntuser-dat files + try { + allRegistryFiles.addAll(fileManager.findFiles(dataSource, "usrclass.dat")); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Error finding 'usrclass.dat' files."), ex); //NON-NLS + } + + // find the system hives' + String[] regFileNames = new String[]{"system", "software", "security"}; //NON-NLS + for (String regFileName : regFileNames) { + try { + allRegistryFiles.addAll(fileManager.findFiles(dataSource, regFileName, "/system32/config")); //NON-NLS + } catch (TskCoreException ex) { + String msg = NbBundle.getMessage(this.getClass(), + "ExtractRegistry.findRegFiles.errMsg.errReadingFile", regFileName); + logger.log(Level.WARNING, msg, ex); + this.addErrorMessage(this.getName() + ": " + msg); + } + } + return allRegistryFiles; + } + + /** + * Identifies registry files in the database by mtimeItem, runs regripper on + * them, and parses the output. + */ + private void analyzeRegistryFiles() { + List allRegistryFiles = findRegistryFiles(); + + // open the log file + FileWriter logFile = null; + try { + logFile = new FileWriter(RAImageIngestModule.getRAOutputPath(currentCase, "reg") + File.separator + "regripper-info.txt"); //NON-NLS + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + + for (AbstractFile regFile : allRegistryFiles) { + String regFileName = regFile.getName(); + long regFileId = regFile.getId(); + String regFileNameLocal = RAImageIngestModule.getRATempPath(currentCase, "reg") + File.separator + regFileName; + String outputPathBase = RAImageIngestModule.getRAOutputPath(currentCase, "reg") + File.separator + regFileName + "-regripper-" + Long.toString(regFileId); //NON-NLS + File regFileNameLocalFile = new File(regFileNameLocal); + try { + ContentUtils.writeToFile(regFile, regFileNameLocalFile, context::dataSourceIngestIsCancelled); + } catch (ReadContentInputStreamException ex) { + logger.log(Level.WARNING, String.format("Error reading registry file '%s' (id=%d).", + regFile.getName(), regFileId), ex); //NON-NLS + this.addErrorMessage( + NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp", + this.getName(), regFileName)); + continue; + } catch (IOException ex) { + logger.log(Level.SEVERE, String.format("Error writing temp registry file '%s' for registry file '%s' (id=%d).", + regFileNameLocal, regFile.getName(), regFileId), ex); //NON-NLS + this.addErrorMessage( + NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp", + this.getName(), regFileName)); + continue; + } + + if (context.dataSourceIngestIsCancelled()) { + break; + } + + try { + if (logFile != null) { + logFile.write(Long.toString(regFileId) + "\t" + regFile.getUniquePath() + "\n"); + } + } catch (TskCoreException | IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + + logger.log(Level.INFO, "{0}- Now getting registry information from {1}", new Object[]{moduleName, regFileNameLocal}); //NON-NLS + RegOutputFiles regOutputFiles = ripRegistryFile(regFileNameLocal, outputPathBase); + if (context.dataSourceIngestIsCancelled()) { + break; + } + + // parse the autopsy-specific output + if (regOutputFiles.autopsyPlugins.isEmpty() == false && parseAutopsyPluginOutput(regOutputFiles.autopsyPlugins, regFile) == false) { + this.addErrorMessage( + NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", + this.getName(), regFileName)); + } + + // create a report for the full output + if (!regOutputFiles.fullPlugins.isEmpty()) { + //parse the full regripper output from SAM hive files + if (regFileNameLocal.toLowerCase().contains("sam") && parseSamPluginOutput(regOutputFiles.fullPlugins, regFile) == false) { + this.addErrorMessage( + NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", + this.getName(), regFileName)); + } else if (regFileNameLocal.toLowerCase().contains("ntuser") || regFileNameLocal.toLowerCase().contains("usrclass")) { + try { + List shellbags = ShellBagParser.parseShellbagOutput(regOutputFiles.fullPlugins); + createShellBagArtifacts(regFile, shellbags); + createRecentlyUsedArtifacts(regOutputFiles.fullPlugins, regFile); + } catch (IOException | TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to get shell bags from file %s", regOutputFiles.fullPlugins), ex); + } + } else if (regFileNameLocal.toLowerCase().contains("system") && parseSystemPluginOutput(regOutputFiles.fullPlugins, regFile) == false) { + this.addErrorMessage( + NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", + this.getName(), regFileName)); + } + try { + Report report = currentCase.addReport(regOutputFiles.fullPlugins, + NbBundle.getMessage(this.getClass(), "ExtractRegistry.parentModuleName.noSpace"), + "RegRipper " + regFile.getUniquePath(), regFile); //NON-NLS + + // Index the report content so that it will be available for keyword search. + KeywordSearchService searchService = Lookup.getDefault().lookup(KeywordSearchService.class); + if (null == searchService) { + logger.log(Level.WARNING, "Keyword search service not found. Report will not be indexed"); + } else { + searchService.index(report); + report.close(); + } + } catch (TskCoreException e) { + this.addErrorMessage("Error adding regripper output as Autopsy report: " + e.getLocalizedMessage()); //NON-NLS + } + } + // delete the hive + regFileNameLocalFile.delete(); + } + + try { + if (logFile != null) { + logFile.close(); + } + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + /** + * Execute regripper on the given registry. + * + * @param regFilePath Path to local copy of registry + * @param outFilePathBase Path to location to save output file to. Base + * mtimeItem that will be extended on + */ + private RegOutputFiles ripRegistryFile(String regFilePath, String outFilePathBase) { + String autopsyType = ""; // Type argument for rr for autopsy-specific modules + String fullType; // Type argument for rr for full set of modules + + RegOutputFiles regOutputFiles = new RegOutputFiles(); + + if (regFilePath.toLowerCase().contains("system")) { //NON-NLS + autopsyType = "autopsysystem"; //NON-NLS + fullType = "system"; //NON-NLS + } else if (regFilePath.toLowerCase().contains("software")) { //NON-NLS + autopsyType = "autopsysoftware"; //NON-NLS + fullType = "software"; //NON-NLS + } else if (regFilePath.toLowerCase().contains("ntuser")) { //NON-NLS + autopsyType = "autopsyntuser"; //NON-NLS + fullType = "ntuser"; //NON-NLS + } else if (regFilePath.toLowerCase().contains("sam")) { //NON-NLS + //fullType sam output files are parsed for user information + fullType = "sam"; //NON-NLS + } else if (regFilePath.toLowerCase().contains("security")) { //NON-NLS + fullType = "security"; //NON-NLS + }else if (regFilePath.toLowerCase().contains("usrclass")) { //NON-NLS + fullType = "usrclass"; //NON-NLS + } else { + return regOutputFiles; + } + + // run the autopsy-specific set of modules + if (!autopsyType.isEmpty()) { + regOutputFiles.autopsyPlugins = outFilePathBase + "-autopsy.txt"; //NON-NLS + String errFilePath = outFilePathBase + "-autopsy.err.txt"; //NON-NLS + logger.log(Level.INFO, "Writing RegRipper results to: {0}", regOutputFiles.autopsyPlugins); //NON-NLS + executeRegRipper(rrCmd, rrHome, regFilePath, autopsyType, regOutputFiles.autopsyPlugins, errFilePath); + } + if (context.dataSourceIngestIsCancelled()) { + return regOutputFiles; + } + + // run the full set of rr modules + if (!fullType.isEmpty()) { + regOutputFiles.fullPlugins = outFilePathBase + "-full.txt"; //NON-NLS + String errFilePath = outFilePathBase + "-full.err.txt"; //NON-NLS + logger.log(Level.INFO, "Writing Full RegRipper results to: {0}", regOutputFiles.fullPlugins); //NON-NLS + executeRegRipper(rrFullCmd, rrFullHome, regFilePath, fullType, regOutputFiles.fullPlugins, errFilePath); + try { + scanErrorLogs(errFilePath); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Unable to run RegRipper", ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName())); + } + } + return regOutputFiles; + } + + private void scanErrorLogs(String errFilePath) throws IOException { + File regfile = new File(errFilePath); + try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { + String line = reader.readLine(); + while (line != null) { + line = line.trim(); + if (line.toLowerCase().contains("error") || line.toLowerCase().contains("@inc")) { + logger.log(Level.WARNING, "Regripper file {0} contains errors from run", errFilePath); //NON-NLS + + } + line = reader.readLine(); + } + } + } + + private void executeRegRipper(List regRipperPath, Path regRipperHomeDir, String hiveFilePath, String hiveFileType, String outputFile, String errFile) { + try { + List commandLine = new ArrayList<>(); + for (String cmd : regRipperPath) { + commandLine.add(cmd); + } + commandLine.add("-r"); //NON-NLS + commandLine.add(hiveFilePath); + commandLine.add("-f"); //NON-NLS + commandLine.add(hiveFileType); + + ProcessBuilder processBuilder = new ProcessBuilder(commandLine); + processBuilder.directory(regRipperHomeDir.toFile()); // RegRipper 2.8 has to be run from its own directory + processBuilder.redirectOutput(new File(outputFile)); + processBuilder.redirectError(new File(errFile)); + ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context)); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Unable to run RegRipper", ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName())); + } + } + + // @@@ VERIFY that we are doing the right thing when we parse multiple NTUSER.DAT + /** + * + * @param regFilePath Path to the output file produced by RegRipper. + * @param regFile File object for registry that we are parsing (to make + * blackboard artifacts with) + * + * @return + */ + private boolean parseAutopsyPluginOutput(String regFilePath, AbstractFile regFile) { + FileInputStream fstream = null; + List newArtifacts = new ArrayList<>(); + try { + // Read the file in and create a Document and elements + File regfile = new File(regFilePath); + fstream = new FileInputStream(regfile); + String regString = new Scanner(fstream, "UTF-8").useDelimiter("\\Z").next(); //NON-NLS + String startdoc = ""; //NON-NLS + String result = regString.replaceAll("----------------------------------------", ""); + result = result.replaceAll("\\n", ""); //NON-NLS + result = result.replaceAll("\\r", ""); //NON-NLS + result = result.replaceAll("'", "'"); //NON-NLS + result = result.replaceAll("&", "&"); //NON-NLS + result = result.replace('\0', ' '); // NON-NLS + String enddoc = ""; //NON-NLS + String stringdoc = startdoc + result + enddoc; + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = builder.parse(new InputSource(new StringReader(stringdoc))); + + // cycle through the elements in the doc + Element oroot = doc.getDocumentElement(); + NodeList children = oroot.getChildNodes(); + int len = children.getLength(); + for (int i = 0; i < len; i++) { + + if (context.dataSourceIngestIsCancelled()) { + return false; + } + + Element tempnode = (Element) children.item(i); + + String dataType = tempnode.getNodeName(); + NodeList timenodes = tempnode.getElementsByTagName("mtime"); //NON-NLS + Long mtime = null; + if (timenodes.getLength() > 0) { + Element timenode = (Element) timenodes.item(0); + String etime = timenode.getTextContent(); + //sometimes etime will be an empty string and therefore can not be parsed into a date + if (etime != null && !etime.isEmpty()) { + try { + mtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(etime).getTime(); + String Tempdate = mtime.toString(); + mtime = Long.valueOf(Tempdate) / MS_IN_SEC; + } catch (ParseException ex) { + logger.log(Level.WARNING, "Failed to parse epoch time when parsing the registry.", ex); //NON-NLS + } + } + } + + NodeList artroots = tempnode.getElementsByTagName("artifacts"); //NON-NLS + if (artroots.getLength() == 0) { + // If there isn't an artifact node, skip this entry + continue; + } + + Element artroot = (Element) artroots.item(0); + NodeList myartlist = artroot.getChildNodes(); + String parentModuleName = RecentActivityExtracterModuleFactory.getModuleName(); + + // If all artifact nodes should really go under one Blackboard artifact, need to process it differently + switch (dataType) { + case "WinVersion": //NON-NLS + String version = ""; + String systemRoot = ""; + String productId = ""; + String regOwner = ""; + String regOrg = ""; + Long installtime = null; + for (int j = 0; j < myartlist.getLength(); j++) { + Node artchild = myartlist.item(j); + // If it has attributes, then it is an Element (based off API) + if (artchild.hasAttributes()) { + Element artnode = (Element) artchild; + + String value = artnode.getTextContent(); + if (value != null) { + value = value.trim(); + } + String name = artnode.getAttribute("name"); //NON-NLS + if (name == null) { + continue; + } + switch (name) { + case "ProductName": // NON-NLS + version = value; + break; + case "CSDVersion": // NON-NLS + // This is dependant on the fact that ProductName shows up first in the module output + version = version + " " + value; + break; + case "SystemRoot": //NON-NLS + systemRoot = value; + break; + case "ProductId": //NON-NLS + productId = value; + break; + case "RegisteredOwner": //NON-NLS + regOwner = value; + break; + case "RegisteredOrganization": //NON-NLS + regOrg = value; + break; + case "InstallDate": //NON-NLS + if (value != null && !value.isEmpty()) { + try { + installtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(value).getTime(); + String Tempdate = installtime.toString(); + installtime = Long.valueOf(Tempdate) / MS_IN_SEC; + } catch (ParseException e) { + logger.log(Level.WARNING, "RegRipper::Conversion on DateTime -> ", e); //NON-NLS + } + } + break; + default: + break; + } + } + } + try { + Collection bbattributes = new ArrayList<>(); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, version)); + if (installtime != null) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, installtime)); + } + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, parentModuleName, systemRoot)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PRODUCT_ID, parentModuleName, productId)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_OWNER, parentModuleName, regOwner)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ORGANIZATION, parentModuleName, regOrg)); + + // Check if there is already an OS_INFO artifact for this file, and add to that if possible. + ArrayList results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId()); + if (results.isEmpty()) { + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); + bbart.addAttributes(bbattributes); + + newArtifacts.add(bbart); + } else { + results.get(0).addAttributes(bbattributes); + } + + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding installed program artifact to blackboard."); //NON-NLS + } + break; + case "Profiler": // NON-NLS + String os = ""; + String procArch = ""; + String tempDir = ""; + for (int j = 0; j < myartlist.getLength(); j++) { + Node artchild = myartlist.item(j); + // If it has attributes, then it is an Element (based off API) + if (artchild.hasAttributes()) { + Element artnode = (Element) artchild; + + String value = artnode.getTextContent().trim(); + String name = artnode.getAttribute("name"); //NON-NLS + switch (name) { + case "OS": // NON-NLS + os = value; + break; + case "PROCESSOR_ARCHITECTURE": // NON-NLS + procArch = value; + break; + case "PROCESSOR_IDENTIFIER": //NON-NLS + break; + case "TEMP": //NON-NLS + tempDir = value; + break; + default: + break; + } + } + } + try { + Collection bbattributes = new ArrayList<>(); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VERSION, parentModuleName, os)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROCESSOR_ARCHITECTURE, parentModuleName, procArch)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TEMP_DIR, parentModuleName, tempDir)); + + // Check if there is already an OS_INFO artifact for this file and add to that if possible + ArrayList results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId()); + if (results.isEmpty()) { + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); + bbart.addAttributes(bbattributes); + + newArtifacts.add(bbart); + } else { + results.get(0).addAttributes(bbattributes); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding os info artifact to blackboard."); //NON-NLS + } + break; + case "CompName": // NON-NLS + String compName = ""; + String domain = ""; + for (int j = 0; j < myartlist.getLength(); j++) { + Node artchild = myartlist.item(j); + // If it has attributes, then it is an Element (based off API) + if (artchild.hasAttributes()) { + Element artnode = (Element) artchild; + + String value = artnode.getTextContent().trim(); + String name = artnode.getAttribute("name"); //NON-NLS + + if (name.equals("ComputerName")) { // NON-NLS + compName = value; + } else if (name.equals("Domain")) { // NON-NLS + domain = value; + } + } + } + try { + Collection bbattributes = new ArrayList<>(); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, parentModuleName, compName)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, parentModuleName, domain)); + + // Check if there is already an OS_INFO artifact for this file and add to that if possible + ArrayList results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId()); + if (results.isEmpty()) { + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); + bbart.addAttributes(bbattributes); + + newArtifacts.add(bbart); + } else { + results.get(0).addAttributes(bbattributes); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding os info artifact to blackboard.", ex); //NON-NLS + } + break; + default: + for (int j = 0; j < myartlist.getLength(); j++) { + Node artchild = myartlist.item(j); + // If it has attributes, then it is an Element (based off API) + if (artchild.hasAttributes()) { + Element artnode = (Element) artchild; + + String value = artnode.getTextContent().trim(); + Collection bbattributes = new ArrayList<>(); + + switch (dataType) { + case "recentdocs": //NON-NLS + // BlackboardArtifact bbart = tskCase.getContentById(orgId).newArtifact(ARTIFACT_TYPE.TSK_RECENT_OBJECT); + // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "RecentActivity", dataType, mtime)); + // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), "RecentActivity", dataType, mtimeItem)); + // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(), "RecentActivity", dataType, value)); + // bbart.addAttributes(bbattributes); + // @@@ BC: Why are we ignoring this... + break; + case "usb": //NON-NLS + try { + Long usbMtime = Long.parseLong(artnode.getAttribute("mtime")); //NON-NLS + usbMtime = Long.valueOf(usbMtime.toString()); + + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, usbMtime)); + String dev = artnode.getAttribute("dev"); //NON-NLS + String make = ""; + String model = dev; + if (dev.toLowerCase().contains("vid")) { //NON-NLS + USBInfo info = USB_MAPPER.parseAndLookup(dev); + if (info.getVendor() != null) { + make = info.getVendor(); + } + if (info.getProduct() != null) { + model = info.getProduct(); + } + } + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE, parentModuleName, make)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL, parentModuleName, model)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_ID, parentModuleName, value)); + bbart.addAttributes(bbattributes); + + newArtifacts.add(bbart); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding device attached artifact to blackboard.", ex); //NON-NLS + } + break; + case "uninstall": //NON-NLS + Long itemMtime = null; + try { + String mTimeAttr = artnode.getAttribute("mtime"); + if (mTimeAttr != null && !mTimeAttr.isEmpty()) { + itemMtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(mTimeAttr).getTime(); //NON-NLS + itemMtime /= MS_IN_SEC; + } + } catch (ParseException ex) { + logger.log(Level.SEVERE, "Failed to parse epoch time for installed program artifact.", ex); //NON-NLS + } + + try { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, value)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, itemMtime)); + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_INSTALLED_PROG); + bbart.addAttributes(bbattributes); + + newArtifacts.add(bbart); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding installed program artifact to blackboard.", ex); //NON-NLS + } + break; + case "office": //NON-NLS + String officeName = artnode.getAttribute("name"); //NON-NLS + + try { + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_RECENT_OBJECT); + // @@@ BC: Consider removing this after some more testing. It looks like an Mtime associated with the root key and not the individual item + if (mtime != null) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, parentModuleName, mtime)); + } + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, parentModuleName, officeName)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE, parentModuleName, value)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, artnode.getNodeName())); + bbart.addAttributes(bbattributes); + + newArtifacts.add(bbart); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding recent object artifact to blackboard.", ex); //NON-NLS + } + break; + + case "ProcessorArchitecture": //NON-NLS + // Architecture is now included under Profiler + //try { + // String processorArchitecture = value; + // if (processorArchitecture.equals("AMD64")) + // processorArchitecture = "x86-64"; + + // BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); + // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROCESSOR_ARCHITECTURE.getTypeID(), parentModuleName, processorArchitecture)); + // bbart.addAttributes(bbattributes); + //} catch (TskCoreException ex) { + // logger.log(Level.SEVERE, "Error adding os info artifact to blackboard."); //NON-NLS + //} + break; + + case "ProfileList": //NON-NLS + try { + String homeDir = value; + String sid = artnode.getAttribute("sid"); //NON-NLS + String username = artnode.getAttribute("username"); //NON-NLS + BlackboardArtifact bbart = null; + try { + //check if any of the existing artifacts match this username + ArrayList existingArtifacts = currentCase.getSleuthkitCase().getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT); + for (BlackboardArtifact artifact : existingArtifacts) { + if (artifact.getDataSource().getId() == regFile.getDataSourceObjectId()) { + BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_ID)); + if (attribute != null && attribute.getValueString().equals(sid)) { + bbart = artifact; + break; + } + } + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error getting existing os account artifact", ex); + } + if (bbart == null) { + //create new artifact + bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + parentModuleName, username)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID, + parentModuleName, sid)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, + parentModuleName, homeDir)); + } else { + //add attributes to existing artifact + BlackboardAttribute bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_NAME)); + + if (bbattr == null) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + parentModuleName, username)); + } + bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH)); + if (bbattr == null) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, + parentModuleName, homeDir)); + } + } + bbart.addAttributes(bbattributes); + newArtifacts.add(bbart); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding account artifact to blackboard.", ex); //NON-NLS + } + break; + + case "NtuserNetwork": // NON-NLS + try { + String localPath = artnode.getAttribute("localPath"); //NON-NLS + String remoteName = value; + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_REMOTE_DRIVE); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCAL_PATH, + parentModuleName, localPath)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REMOTE_PATH, + parentModuleName, remoteName)); + bbart.addAttributes(bbattributes); + newArtifacts.add(bbart); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding network artifact to blackboard.", ex); //NON-NLS + } + break; + case "SSID": // NON-NLS + String adapter = artnode.getAttribute("adapter"); //NON-NLS + try { + Long lastWriteTime = Long.parseLong(artnode.getAttribute("writeTime")); //NON-NLS + lastWriteTime = Long.valueOf(lastWriteTime.toString()); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SSID, parentModuleName, value)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, lastWriteTime)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_ID, parentModuleName, adapter)); + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_WIFI_NETWORK); + bbart.addAttributes(bbattributes); + newArtifacts.add(bbart); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding SSID artifact to blackboard.", ex); //NON-NLS + } + break; + case "shellfolders": // NON-NLS + // The User Shell Folders subkey stores the paths to Windows Explorer folders for the current user of the computer + // (https://technet.microsoft.com/en-us/library/Cc962613.aspx). + // No useful information. Skip. + break; + + default: + logger.log(Level.SEVERE, "Unrecognized node name: {0}", dataType); //NON-NLS + break; + } + } + } + break; + } + } // for + return true; + } catch (FileNotFoundException ex) { + logger.log(Level.WARNING, String.format("Error finding the registry file: %s", regFilePath), ex); //NON-NLS + } catch (SAXException ex) { + logger.log(Level.WARNING, String.format("Error parsing the registry XML: %s", regFilePath), ex); //NON-NLS + } catch (IOException ex) { + logger.log(Level.WARNING, String.format("Error building the document parser: %s", regFilePath), ex); //NON-NLS + } catch (ParserConfigurationException ex) { + logger.log(Level.WARNING, String.format("Error configuring the registry parser: %s", regFilePath), ex); //NON-NLS + } finally { + try { + if (fstream != null) { + fstream.close(); + } + } catch (IOException ex) { + } + + postArtifacts(newArtifacts); + } + return false; + } + + + private boolean parseSystemPluginOutput(String regfilePath, AbstractFile regAbstractFile) { + File regfile = new File(regfilePath); + try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { + String line = reader.readLine(); + while (line != null) { + line = line.trim(); + + if (line.matches("^bam v.*")) { + parseBamKey(regAbstractFile, reader, Bundle.Registry_System_Bam()); + } else if (line.matches("^bthport v..*")) { + parseBlueToothDevices(regAbstractFile, reader) ; + } + line = reader.readLine(); + } + return true; + } catch (FileNotFoundException ex) { + logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS + } catch (IOException ex) { + logger.log(Level.WARNING, "Error reading the system hive: {0}", ex); //NON-NLS + } + + return false; + + } + + /** + * Create recently used artifacts to parse the regripper plugin output, this format is used in several diffent plugins + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseBlueToothDevices(AbstractFile regFile, BufferedReader reader) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + // date format for plugin Tue Jun 23 10:27:54 2020 Z + SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US); + Long bthLastSeen = Long.valueOf(0); + Long bthLastConnected = Long.valueOf(0); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + if (line.contains("Device Unique ID")) { + // Columns are seperated by colons : + // Data : Values + // Record is 4 lines in length (Device Unique Id, Name, Last Seen, LastConnected + while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Radio Support not found")) { + // Split line on "> " which is the record delimiter between position and file + String deviceTokens[] = line.split(": "); + String deviceUniqueId = deviceTokens[1]; + line = reader.readLine(); + // Default device name to unknown as a device name may not exist. + String deviceName = "Unknown"; + if (line.contains("Name")) { + String nameTokens[] = line.split(": "); + deviceName = nameTokens[1]; + line = reader.readLine(); + } + String lastSeenTokens[] = line.split(": "); + String lastSeen = lastSeenTokens[1].replace(" Z", "");; + line = reader.readLine(); + String lastConnectedTokens[] = line.split(": "); + String lastConnected = lastConnectedTokens[1].replace(" Z", "");; + try { + Date usedSeenDate = dateFormat.parse(lastSeen); + bthLastSeen = usedSeenDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last Seen attribute.", lastSeen), ex); //NON-NLS + } + try { + Date usedConnectedDate = dateFormat.parse(lastConnected); + bthLastConnected = usedConnectedDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last connected attribute.", lastSeen), ex); //NON-NLS + } + + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_DEVICE_ID, getName(), deviceUniqueId)); + attributes.add(new BlackboardAttribute(TSK_NAME, getName(), deviceName)); + attributes.add(new BlackboardAttribute(TSK_DATETIME, getName(), bthLastSeen)); + attributes.add(new BlackboardAttribute(TSK_DATETIME_ACCESSED, getName(), bthLastConnected)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + } + // Read blank line between records then next read line is start of next block + line = reader.readLine(); + line = reader.readLine(); + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Parse the output of the SAM regripper plugin to get additional Account + * information + * + * @param regFilePath the path to the registry file being parsed + * @param regAbstractFile the file to associate newly created artifacts with + * + * @return true if successful, false if parsing failed at some point + */ + private boolean parseSamPluginOutput(String regFilePath, AbstractFile regAbstractFile) { + File regfile = new File(regFilePath); + List newArtifacts = new ArrayList<>(); + try (BufferedReader bufferedReader = new BufferedReader(new FileReader(regfile))) { + // Read the file in and create a Document and elements + String userInfoSection = "User Information"; + String previousLine = null; + String line = bufferedReader.readLine(); + Set> userSet = new HashSet<>(); + Map> groupMap = null; + while (line != null) { + if (line.contains(SECTION_DIVIDER) && previousLine != null && previousLine.contains(userInfoSection)) { + readUsers(bufferedReader, userSet); + } + + if (line.contains(SECTION_DIVIDER) && previousLine != null && previousLine.contains("Group Membership Information")) { + groupMap = readGroups(bufferedReader); + } + + previousLine = line; + line = bufferedReader.readLine(); + } + Map> userInfoMap = new HashMap<>(); + //load all the user info which was read into a map + for (Map userInfo : userSet) { + userInfoMap.put(userInfo.get(SID_KEY), userInfo); + } + //get all existing OS account artifacts + List existingOsAccounts = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT); + for (BlackboardArtifact osAccount : existingOsAccounts) { + //if the OS Account artifact was from the same data source check the user id + if (osAccount.getDataSource().getId() == regAbstractFile.getDataSourceObjectId()) { + BlackboardAttribute existingUserId = osAccount.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_ID)); + if (existingUserId != null) { + String userID = existingUserId.getValueString().trim(); + Map userInfo = userInfoMap.remove(userID); + //if the existing user id matches a user id which we parsed information for check if that information exists and if it doesn't add it + if (userInfo != null) { + osAccount.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userID), true, regAbstractFile)); + } + } + } + } + + //add remaining userinfos as accounts; + for (Map userInfo : userInfoMap.values()) { + BlackboardArtifact bbart = regAbstractFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT); + bbart.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userInfo.get(SID_KEY)), false, regAbstractFile)); + // index the artifact for keyword search + newArtifacts.add(bbart); + } + // Get a mapping of user sids to user names and save globally so it can be used for other areas + // of the registry, ie: BAM key + try { + userNameMap = makeUserNameMap(dataSource); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to create OS Account user name map", ex); + // This is not the end of the world we will just continue without + // user names + userNameMap = new HashMap<>(); + } + return true; + } catch (FileNotFoundException ex) { + logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS + } catch (IOException ex) { + logger.log(Level.WARNING, "Error building the document parser: {0}", ex); //NON-NLS + } catch (ParseException ex) { + logger.log(Level.WARNING, "Error parsing the the date from the registry file", ex); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error updating TSK_OS_ACCOUNT artifacts to include newly parsed data.", ex); //NON-NLS + } finally { + postArtifacts(newArtifacts); + } + return false; + } + + /** + * Creates the attribute list for the given user information and group list. + * + * @param userInfo Map of key\value pairs of user information + * @param groupList List of the groups that user belongs + * @param existingUser + * + * @return List + * + * @throws ParseException + */ + Collection getAttributesForAccount(Map userInfo, List groupList, boolean existingUser, AbstractFile regAbstractFile) throws ParseException { + Collection bbattributes = new ArrayList<>(); + + SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'"); + regRipperTimeFormat.setTimeZone(getTimeZone("GMT")); + + if (!existingUser) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID, + getRAModuleName(), userInfo.get(SID_KEY))); + + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + this.moduleName, userInfo.get(USERNAME_KEY))); + } + + String value = userInfo.get(ACCOUNT_CREATED_KEY); + if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, + getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); + } + + value = userInfo.get(LAST_LOGIN_KEY); + if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, + getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); + } + + value = userInfo.get(LOGIN_COUNT_KEY); + if (value != null && !value.isEmpty()) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, + getRAModuleName(), Integer.parseInt(value))); + } + + value = userInfo.get(ACCOUNT_TYPE_KEY); + if (value != null && !value.isEmpty()) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, + getRAModuleName(), value)); + } + + value = userInfo.get(USER_COMMENT_KEY); + if (value != null && !value.isEmpty()) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DESCRIPTION, + getRAModuleName(), value)); + } + + value = userInfo.get(NAME_KEY); + if (value != null && !value.isEmpty()) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, + getRAModuleName(), value)); + } + + value = userInfo.get(INTERNET_NAME_KEY); + if (value != null && !value.isEmpty()) { + try { + // Create an account for this email, if it doesn't already exist. + Case.getCurrentCaseThrows() + .getSleuthkitCase() + .getCommunicationsManager() + .createAccountFileInstance(Account.Type.EMAIL, + value, getRAModuleName(), regAbstractFile); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.SEVERE, + String.format("Error adding email account with value " + + "%s, to the case database for file %s [objId=%d]", + value, regAbstractFile.getName(), regAbstractFile.getId()), ex); + } + + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL, + getRAModuleName(), value)); + } + + value = userInfo.get(FULL_NAME_KEY); + if (value != null && !value.isEmpty()) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DISPLAY_NAME, + getRAModuleName(), value)); + } + + value = userInfo.get(PWD_RESET_KEY); + if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_RESET, + getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); + } + + value = userInfo.get(PASSWORD_HINT); + if (value != null && !value.isEmpty()) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_HINT, + getRAModuleName(), value)); + } + + value = userInfo.get(PWD_FAILE_KEY); + if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_FAIL, + getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); + } + + String settingString = ""; + for (String setting : PASSWORD_SETTINGS_FLAGS) { + if (userInfo.containsKey(setting)) { + settingString += setting + ", "; + } + } + + if (!settingString.isEmpty()) { + settingString = settingString.substring(0, settingString.length() - 2); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_SETTINGS, + getRAModuleName(), settingString)); + } + + settingString = ""; + for (String setting : ACCOUNT_SETTINGS_FLAGS) { + if (userInfo.containsKey(setting)) { + settingString += setting + ", "; + } + } + + if (!settingString.isEmpty()) { + settingString = settingString.substring(0, settingString.length() - 2); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_SETTINGS, + getRAModuleName(), settingString)); + } + + settingString = ""; + for (String setting : ACCOUNT_TYPE_FLAGS) { + if (userInfo.containsKey(setting)) { + settingString += setting + ", "; + } + } + + if (!settingString.isEmpty()) { + settingString = settingString.substring(0, settingString.length() - 2); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_FLAG, + getRAModuleName(), settingString)); + } + + if (groupList != null && groupList.isEmpty()) { + String groups = ""; + for (String group : groupList) { + groups += group + ", "; + } + + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GROUPS, + getRAModuleName(), groups.substring(0, groups.length() - 2))); + } + + return bbattributes; + } + + /** + * Read the User Information section of the SAM regripper plugin's output + * and collect user account information from the file. + * + * @param bufferedReader a buffered reader for the file which contains the + * user information + * @param users the set to add UserInfo objects representing the + * users found to + * + * @throws IOException + */ + private void readUsers(BufferedReader bufferedReader, Set> users) throws IOException { + String line = bufferedReader.readLine(); + //read until end of file or next section divider + String userName = ""; + String user_rid = ""; + while (line != null && !line.contains(SECTION_DIVIDER)) { + //when a user name field exists read the name and id number + if (line.contains(USERNAME_KEY)) { + String regx = USERNAME_KEY + "\\s*?:"; + String userNameAndIdString = line.replaceAll(regx, ""); + userName = userNameAndIdString.substring(0, userNameAndIdString.lastIndexOf('[')).trim(); + user_rid = userNameAndIdString.substring(userNameAndIdString.lastIndexOf('['), userNameAndIdString.lastIndexOf(']')); + } else if (line.contains(SID_KEY) && !userName.isEmpty()) { + Map.Entry entry = getSAMKeyValue(line); + + HashMap userInfo = new HashMap<>(); + userInfo.put(USERNAME_KEY, userName); + userInfo.put(RID_KEY, user_rid); + userInfo.put(entry.getKey(), entry.getValue()); + + //continue reading this users information until end of file or a blank line between users + line = bufferedReader.readLine(); + while (line != null && !line.isEmpty()) { + entry = getSAMKeyValue(line); + if (entry != null) { + userInfo.put(entry.getKey(), entry.getValue()); + } + line = bufferedReader.readLine(); + } + users.add(userInfo); + + userName = ""; + } + line = bufferedReader.readLine(); + } + } + + /** + * Create recently used artifacts from NTUSER regripper files + * + * @param regFileName name of the regripper output file + * + * @param regFile registry file the artifact is associated with + * + * @throws FileNotFound and IOException + */ + private void createRecentlyUsedArtifacts(String regFileName, AbstractFile regFile) throws FileNotFoundException, IOException { + File regfile = new File(regFileName); + try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { + String line = reader.readLine(); + while (line != null) { + line = line.trim(); + + if (line.matches("^adoberdr v.*")) { + parseAdobeMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Adobe()); + } else if (line.matches("^mpmru v.*")) { + parseMediaPlayerMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Mediaplayer()); + } else if (line.matches("^trustrecords v.*")) { + parseOfficeTrustRecords(regFile, reader, Bundle.Recently_Used_Artifacts_Office_Trustrecords()); + } else if (line.matches("^ArcHistory:")) { + parse7ZipMRU(regFile, reader, Bundle.Recently_Used_Artifacts_ArcHistory()); + } else if (line.matches("^applets v.*")) { + parseGenericMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Applets()); + } else if (line.matches("^mmc v.*")) { + parseGenericMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Mmc()); + } else if (line.matches("^winrar v.*")) { + parseWinRARMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Winrar()); + } else if (line.matches("^officedocs2010 v.*")) { + parseOfficeDocs2010MRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Officedocs()); + } + line = reader.readLine(); + } + } + } + + /** + * Create artifacts from BAM Regripper Plugin records + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseBamKey(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + // Read thru first bam output to get to second bam output which is the same but delimited + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + } + line = reader.readLine(); + line = line.trim(); + while (!line.contains(SECTION_DIVIDER)) { + // Split the line into it parts based on delimiter of "|" + // 1570493613|BAM|||\Device\HarddiskVolume3\Program Files\TechSmith\Snagit 2018\Snagit32.exe (S-1-5-21-3042408413-2583535980-1301764466-1001) + String tokens[] = line.split("\\|"); + Long progRunDateTime = Long.valueOf(tokens[0]); + // Split on " (S-" as this signifies a User SID, if S- not used then may have issues becuase of (x86) in path is valid. + // We can add the S- back to the string that we split on since S- is a valid beginning of a User SID + String fileNameSid[] = tokens[4].split("\\s+\\(S-"); + String userSid = "S-" + fileNameSid[1].substring(0, fileNameSid[1].length() - 1); + String userName = userNameMap.get(userSid); + if (userName == null) { + userName = userSid; + } + String fileName = fileNameSid[0]; + if (fileName.startsWith("\\Device\\HarddiskVolume")) { + // Start at point past the 2nd slash + int fileNameStart = fileName.indexOf('\\', 16); + fileName = fileName.substring(fileNameStart, fileName.length()); + + } + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(), fileName)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, getName(), userName)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), progRunDateTime)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_PROG_RUN, regFile, attributes); + if (bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = reader.readLine(); + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts from adobemru Regripper Plugin records + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseAdobeMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + SimpleDateFormat adobePluginDateFormat = new SimpleDateFormat("yyyyMMddHHmmssZ", US); + Long adobeUsedTime = Long.valueOf(0); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + if (line.matches("^Key name,file name,sDate,uFileSize,uPageCount")) { + line = reader.readLine(); + // Columns are + // Key name, file name, sDate, uFileSize, uPageCount + while (!line.contains(SECTION_DIVIDER)) { + // Split csv line, handles double quotes around individual file names + // since file names can contain commas + String tokens[] = line.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"); + String fileName = tokens[1].substring(0, tokens[1].length() -1); + fileName = fileName.replace("\"", ""); + if (fileName.charAt(0) == '/') { + fileName = fileName.substring(1,fileName.length() - 1); + fileName = fileName.replaceFirst("/", ":/"); + } + // Check to see if more then 2 tokens, Date may not be populated, will default to 0 + if (tokens.length > 2) { + // Time in the format of 20200131104456-05'00' + try { + String fileUsedTime = tokens[2].replaceAll("'",""); + Date usedDate = adobePluginDateFormat.parse(fileUsedTime); + adobeUsedTime = usedDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for adobe file artifact.", tokens[2]), ex); //NON-NLS + } + } + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), adobeUsedTime)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + fileName = fileName.replace("\0", ""); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = reader.readLine(); + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts to parse the Media Player MRU regripper (mpmru) records + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseMediaPlayerMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + if (line.contains("LastWrite")) { + line = reader.readLine(); + // Columns are + // FileX -> + while (!line.contains(SECTION_DIVIDER) && !line.contains("RecentFileList has no values.")) { + // Split line on "> " which is the record delimiter between position and file + String tokens[] = line.split("> "); + String fileName = tokens[1]; + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(fileName, bba); + if (bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + } + line = reader.readLine(); + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts to parse the regripper plugin output, this format is used in several diffent plugins + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseGenericMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + if (line.contains("LastWrite")) { + line = reader.readLine(); + // Columns are + // FileX -> + while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Applets") + && !line.contains(("Recent File List"))) { + // Split line on "> " which is the record delimiter between position and file + String tokens[] = line.split("> "); + String fileName = tokens[1]; + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = reader.readLine(); + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts to parse the WinRAR Regripper plugin output + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseWinRARMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + if (line.contains("LastWrite")) { + line = reader.readLine(); + // Columns are + // FileX -> + if (!line.isEmpty()) { + while (!line.contains(SECTION_DIVIDER)) { + // Split line on "> " which is the record delimiter between position and file + String tokens[] = line.split("> "); + String fileName = tokens[1]; + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = reader.readLine(); + } + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts to parse the runmru ArcHistory (7Zip) regripper plugin records + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parse7ZipMRU(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + line = line.trim(); + if (!line.contains("PathHistory:")) { + while (!line.contains("PathHistory:") && !line.isEmpty()) { + // Columns are + // + String fileName = line; + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if (bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = reader.readLine(); + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts to parse the Office Documents 2010 records Regripper Plugin output + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseOfficeDocs2010MRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + line = line.trim(); + // Reading to the SECTION DIVIDER to get next section of records to process. Dates appear to have + // multiple spaces in them that makes it harder to parse so next section will be easier to parse + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + } + line = reader.readLine(); + while (!line.contains(SECTION_DIVIDER)) { + // record has the following format + // 1294283922|REG|||OfficeDocs2010 - F:\Windows_time_Rules_xp.doc + String tokens[] = line.split("\\|"); + Long docDate = Long.valueOf(tokens[0]); + String fileNameTokens[] = tokens[4].split(" - "); + String fileName = fileNameTokens[1]; + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), docDate)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = reader.readLine(); + line = line.trim(); + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts to parse the Office trust records (trustrecords) Regipper plugin records + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseOfficeTrustRecords(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + String userProfile = regFile.getParentPath(); + userProfile = userProfile.substring(0, userProfile.length() - 1); + List bbartifacts = new ArrayList<>(); + SimpleDateFormat pluginDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", US); + Long usedTime = Long.valueOf(0); + String line = reader.readLine(); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + usedTime = Long.valueOf(0); + if (!line.contains("**") && !line.contains("----------") && !line.contains("LastWrite") + && !line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("TrustRecords")) { + // Columns are + // Date : / + // Split line on " : " which is the record delimiter between position and file + String fileName = null; + String tokens[] = line.split(" : "); + fileName = tokens[1]; + fileName = fileName.replace("%USERPROFILE%", userProfile); + // Time in the format of Wed May 31 14:33:03 2017 Z + try { + String fileUsedTime = tokens[0].replaceAll(" Z",""); + Date usedDate = pluginDateFormat.parse(fileUsedTime); + usedTime = usedDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for TrustRecords artifact.", tokens[0]), ex); //NON-NLS + } + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), usedTime)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create associated artifacts using file name and path and the artifact it associates with + * + * @param filePathName file and path of object being associated with + * + * @param bba blackboard artifact to associate with + * + * @returnv BlackboardArtifact or a null value + */ + private BlackboardArtifact createAssociatedArtifact(String filePathName, BlackboardArtifact bba) { + org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager(); + String fileName = FilenameUtils.getName(filePathName); + String filePath = FilenameUtils.getPath(filePathName); + List sourceFiles; + try { + sourceFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS + if (!sourceFiles.isEmpty()) { + for (AbstractFile sourceFile : sourceFiles) { + if (sourceFile.getParentPath().endsWith(filePath)) { + Collection bbattributes2 = new ArrayList<>(); + bbattributes2.addAll(Arrays.asList( + new BlackboardAttribute(TSK_ASSOCIATED_ARTIFACT, this.getName(), + bba.getArtifactID()))); + + BlackboardArtifact associatedObjectBba = createArtifactWithAttributes(TSK_ASSOCIATED_OBJECT, sourceFile, bbattributes2); + if (associatedObjectBba != null) { + return associatedObjectBba; + } + } + } + } + } catch (TskCoreException ex) { + // only catching the error and displaying the message as the file may not exist on the + // system anymore + logger.log(Level.WARNING, String.format("Error finding actual file %s. file may not exist", filePathName)); //NON-NLS + } + + return null; + } + + /** + * Create a map of userids to usernames from the OS Accounts. + * + * @param dataSource + * + * @return A Map of userIDs and userNames + * + * @throws TskCoreException + */ + private Map makeUserNameMap(Content dataSource) throws TskCoreException { + Map userNameMap = new HashMap<>(); + + List accounts = blackboard.getArtifacts(TSK_OS_ACCOUNT.getTypeID(), dataSource.getId()); + + for (BlackboardArtifact account : accounts) { + BlackboardAttribute nameAttribute = getAttributeForArtifact(account, TSK_USER_NAME); + BlackboardAttribute idAttribute = getAttributeForArtifact(account, TSK_USER_ID); + + String userName = nameAttribute != null ? nameAttribute.getDisplayString() : ""; + String userID = idAttribute != null ? idAttribute.getDisplayString() : ""; + + if (!userID.isEmpty()) { + userNameMap.put(userID, userName); + } + } + + return userNameMap; + } + + /** + * Gets the attribute for the given type from the given artifact. + * + * @param artifact BlackboardArtifact to get the attribute from + * @param type The BlackboardAttribute Type to get + * + * @return BlackboardAttribute for given artifact and type + * + * @throws TskCoreException + */ + private BlackboardAttribute getAttributeForArtifact(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) throws TskCoreException { + return artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(type.getTypeID()))); + } + + /** + * Create the shellbag artifacts from the list of ShellBag objects. + * + * @param regFile The data source file + * @param shellbags List of shellbags from source file + * + * @throws TskCoreException + */ + void createShellBagArtifacts(AbstractFile regFile, List shellbags) throws TskCoreException { + List artifacts = new ArrayList<>(); + try{ + for (ShellBag bag : shellbags) { + Collection attributes = new ArrayList<>(); + BlackboardArtifact artifact = regFile.newArtifact(getShellBagArtifact().getTypeID()); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), bag.getResource())); + attributes.add(new BlackboardAttribute(getKeyAttribute(), getName(), bag.getKey())); + + long time; + time = bag.getLastWrite(); + if (time != 0) { + attributes.add(new BlackboardAttribute(getLastWriteAttribute(), getName(), time)); + } + + time = bag.getModified(); + if (time != 0) { + attributes.add(new BlackboardAttribute(TSK_DATETIME_MODIFIED, getName(), time)); + } + + time = bag.getCreated(); + if (time != 0) { + attributes.add(new BlackboardAttribute(TSK_DATETIME_CREATED, getName(), time)); + } + + time = bag.getAccessed(); + if (time != 0) { + attributes.add(new BlackboardAttribute(TSK_DATETIME_ACCESSED, getName(), time)); + } + + artifact.addAttributes(attributes); + + artifacts.add(artifact); + } + } finally { + postArtifacts(artifacts); + } + } + + /** + * Returns the custom Shellbag artifact type or creates it if it does not + * currently exist. + * + * @return BlackboardArtifact.Type for shellbag artifacts + * + * @throws TskCoreException + */ + private BlackboardArtifact.Type getShellBagArtifact() throws TskCoreException { + if (shellBagArtifactType == null) { + shellBagArtifactType = tskCase.getArtifactType(SHELLBAG_ARTIFACT_NAME); + + if(shellBagArtifactType == null) { + try { + tskCase.addBlackboardArtifactType(SHELLBAG_ARTIFACT_NAME, Bundle.Shellbag_Artifact_Display_Name()); //NON-NLS + } catch (TskDataException ex) { + // Artifact already exists + logger.log(Level.INFO, String.format("%s may have already been defined for this case", SHELLBAG_ARTIFACT_NAME)); + } + + shellBagArtifactType = tskCase.getArtifactType(SHELLBAG_ARTIFACT_NAME); + } + } + + return shellBagArtifactType; + } + + /** + * Gets the custom BlackboardAttribute type. The attribute type is created + * if it does not currently exist. + * + * @return The BlackboardAttribute type + * + * @throws TskCoreException + */ + private BlackboardAttribute.Type getLastWriteAttribute() throws TskCoreException { + if (shellBagLastWriteAttributeType == null) { + try { + shellBagLastWriteAttributeType = tskCase.addArtifactAttributeType(SHELLBAG_ATTRIBUTE_LAST_WRITE, + BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME, + Bundle.Shellbag_Last_Write_Attribute_Display_Name()); + } catch (TskDataException ex) { + // Attribute already exists get it from the case + shellBagLastWriteAttributeType = tskCase.getAttributeType(SHELLBAG_ATTRIBUTE_LAST_WRITE); + } + } + return shellBagLastWriteAttributeType; + } + + /** + * Gets the custom BlackboardAttribute type. The attribute type is created + * if it does not currently exist. + * + * @return The BlackboardAttribute type + * + * @throws TskCoreException + */ + private BlackboardAttribute.Type getKeyAttribute() throws TskCoreException { + if (shellBagKeyAttributeType == null) { + try { + shellBagKeyAttributeType = tskCase.addArtifactAttributeType(SHELLBAG_ATTRIBUTE_KEY, + BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, + Bundle.Shellbag_Key_Attribute_Display_Name()); + } catch (TskDataException ex) { + // The attribute already exists get it from the case + shellBagKeyAttributeType = tskCase.getAttributeType(SHELLBAG_ATTRIBUTE_KEY); + } + } + return shellBagKeyAttributeType; + } + + /** + * Maps the user groups to the sid that are a part of them. + * + * @param bufferedReader + * + * @return A map if sid and the groups they map too + * + * @throws IOException + */ + Map> readGroups(BufferedReader bufferedReader) throws IOException { + Map> groupMap = new HashMap<>(); + + String line = bufferedReader.readLine(); + + int userCount = 0; + String groupName = null; + + while (line != null && !line.contains(SECTION_DIVIDER)) { + + if (line.contains("Group Name")) { + String value = line.replaceAll("Group Name\\s*?:", "").trim(); + groupName = (value.replaceAll("\\[\\d*?\\]", "")).trim(); + int startIndex = value.indexOf('['); + int endIndex = value.indexOf(']'); + + if (startIndex != -1 && endIndex != -1) { + String countStr = value.substring(startIndex + 1, endIndex); + userCount = Integer.parseInt(countStr); + } + } else if (line.matches("Users\\s*?:")) { + for (int i = 0; i < userCount; i++) { + line = bufferedReader.readLine(); + if (line != null) { + String sid = line.trim(); + List groupList = groupMap.get(sid); + if (groupList == null) { + groupList = new ArrayList<>(); + groupMap.put(sid, groupList); + } + groupList.add(groupName); + } + } + groupName = null; + } + line = bufferedReader.readLine(); + } + return groupMap; + } + + /** + * Gets the key value from user account strings of the format + * key:value or + * --> value + * + * @param line String to parse + * + * @return key value pair + */ + private Map.Entry getSAMKeyValue(String line) { + int index = line.indexOf(':'); + Map.Entry returnValue = null; + String key = null; + String value = null; + + if (index != -1) { + key = line.substring(0, index).trim(); + if (index + 1 < line.length()) { + value = line.substring(index + 1).trim(); + } else { + value = ""; + } + + } else if (line.contains("-->")) { + key = line.replace("-->", "").trim(); + value = "true"; + } + + if (key != null) { + returnValue = new AbstractMap.SimpleEntry<>(key, value); + } + + return returnValue; + } + + @Override + public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) { + this.dataSource = dataSource; + this.context = context; + + progressBar.progress(Bundle.Progress_Message_Analyze_Registry()); + analyzeRegistryFiles(); + + } + + /** + * Private wrapper class for Registry output files + */ + private class RegOutputFiles { + + public String autopsyPlugins = ""; + public String fullPlugins = ""; + } +} From 9132ceeede036550924d8789d0e5b715da41a89b Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 21 Jul 2020 14:42:35 -0400 Subject: [PATCH 03/78] fix empty statement Fix empty statement codacy --- .../org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index c98965adbe..4c0e2080c5 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -1017,10 +1017,10 @@ class ExtractRegistry extends Extract { line = reader.readLine(); } String lastSeenTokens[] = line.split(": "); - String lastSeen = lastSeenTokens[1].replace(" Z", "");; + String lastSeen = lastSeenTokens[1].replace(" Z", ""); line = reader.readLine(); String lastConnectedTokens[] = line.split(": "); - String lastConnected = lastConnectedTokens[1].replace(" Z", "");; + String lastConnected = lastConnectedTokens[1].replace(" Z", ""); try { Date usedSeenDate = dateFormat.parse(lastSeen); bthLastSeen = usedSeenDate.getTime()/1000; From 9b89e88f2e75d57315c639eb6b05c1dac48e162d Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 22 Jul 2020 11:16:38 -0400 Subject: [PATCH 04/78] Updated MBoxParser to properly handle other mime types --- .../autopsy/thunderbirdparser/MimeJ4MessageParser.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MimeJ4MessageParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MimeJ4MessageParser.java index 229615b7b3..7856da1a7b 100755 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MimeJ4MessageParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MimeJ4MessageParser.java @@ -231,11 +231,11 @@ class MimeJ4MessageParser { || e.getMimeType().equals(ContentTypeField.TYPE_TEXT_PLAIN)) { handleTextBody(email, (TextBody) e.getBody(), e.getMimeType(), e.getHeader().getFields()); } else { - // Ignore other types. - } + handleAttachment(email, e, fileID, index); + } } } - + /** * Extract text out of a body part of the message. * From 4a2308b9b0af8714bd0234caf8e62adb8c2d3383 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 23 Jul 2020 10:09:29 -0400 Subject: [PATCH 05/78] summarizer start --- .../DataSourceSummaryResultViewer.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java diff --git a/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java new file mode 100644 index 0000000000..90c53e6217 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java @@ -0,0 +1,83 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.resultviewers.summary; + +import org.openide.explorer.ExplorerManager; +import org.openide.nodes.Node; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; +import org.sleuthkit.autopsy.corecomponents.AbstractDataResultViewer; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.DataSource; + + + +/** + * + * @author gregd + */ +public class DataSourceSummaryResultViewer extends AbstractDataResultViewer { + private final String title; + + public DataSourceSummaryResultViewer() { + this(null); + } + + @Messages({ + "DataSourceSummaryResultViewer_title=Summary" + }) + public DataSourceSummaryResultViewer(ExplorerManager explorerManager) { + this(explorerManager, Bundle.DataSourceSummaryResultViewer_title()); + } + + public DataSourceSummaryResultViewer(ExplorerManager explorerManager, String title) { + super(explorerManager); + this.title = title; + } + + @Override + public DataResultViewer createInstance() { + return new DataSourceSummaryResultViewer(); + } + + @Override + public boolean isSupported(Node node) { + if (node == null) { + return false; + } + + DataSource contentItem = node.getLookup().lookup(DataSource.class); + if (contentItem == null) { + return false; + } + + Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(datasourceObjId); + } + + @Override + public void setNode(Node node) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public String getTitle() { + return title; + } +} From 66d1713ec5f6dcf2008531372a95d188769cb126 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 23 Jul 2020 10:45:27 -0400 Subject: [PATCH 06/78] Fixed multipart handling to support multiple text or html parts --- .../autopsy/thunderbirdparser/MimeJ4MessageParser.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MimeJ4MessageParser.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MimeJ4MessageParser.java index 7856da1a7b..6a95e8729a 100755 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MimeJ4MessageParser.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/MimeJ4MessageParser.java @@ -21,13 +21,11 @@ package org.sleuthkit.autopsy.thunderbirdparser; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; -import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.logging.Level; -import org.apache.james.mime4j.dom.BinaryBody; import org.apache.james.mime4j.dom.Body; import org.apache.james.mime4j.dom.Entity; import org.apache.james.mime4j.dom.Message; @@ -227,8 +225,8 @@ class MimeJ4MessageParser { } else if (e.getDispositionType() != null && e.getDispositionType().equals(ContentDispositionField.DISPOSITION_TYPE_ATTACHMENT)) { handleAttachment(email, e, fileID, index); - } else if (e.getMimeType().equals(HTML_TYPE) - || e.getMimeType().equals(ContentTypeField.TYPE_TEXT_PLAIN)) { + } else if ((e.getMimeType().equals(HTML_TYPE) && (email.getHtmlBody() == null || email.getHtmlBody().isEmpty())) + || (e.getMimeType().equals(ContentTypeField.TYPE_TEXT_PLAIN) && (email.getTextBody() == null || email.getTextBody().isEmpty()))) { handleTextBody(email, (TextBody) e.getBody(), e.getMimeType(), e.getHeader().getFields()); } else { handleAttachment(email, e, fileID, index); From 1ca277aae8be3115ede39dbe596ea4d6335fbec0 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 23 Jul 2020 15:32:09 -0400 Subject: [PATCH 07/78] panel integration and refactoring --- .../DataSourceSummaryDetailsPanel.java | 165 ++++++++++-------- .../DataSourceSummaryDialog.form | 5 + .../DataSourceSummaryDialog.java | 19 +- .../DataSourceSummaryResultViewer.java | 64 +++++-- 4 files changed, 154 insertions(+), 99 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java index 1b898f4995..61667dbb42 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.table.DefaultTableModel; +import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle.Messages; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.Image; @@ -36,23 +37,48 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel { //Because this panel was made using the gridbaglayout and netbean's Customize Layout tool it will be best to continue to modify it through that private static final long serialVersionUID = 1L; - private Map osDetailMap = new HashMap<>(); private static final Integer SIZE_COVERSION_CONSTANT = 1000; private static final DecimalFormat APPROXIMATE_SIZE_FORMAT = new DecimalFormat("#.##"); - private final Map unallocatedFilesSizeMap; - private final Map usageMap; private static final Logger logger = Logger.getLogger(DataSourceSummaryDetailsPanel.class.getName()); + private DataSource dataSource; + /** * Creates new form DataSourceSummaryDetailsPanel */ @Messages({"DataSourceSummaryDetailsPanel.getDataSources.error.text=Failed to get the list of datasources for the current case.", "DataSourceSummaryDetailsPanel.getDataSources.error.title=Load Failure"}) - DataSourceSummaryDetailsPanel(Map usageMap) { + DataSourceSummaryDetailsPanel() { initComponents(); - this.usageMap = usageMap; - this.unallocatedFilesSizeMap = DataSourceInfoUtilities.getSizeOfUnallocatedFiles(); - osDetailMap = DataSourceInfoUtilities.getOperatingSystems(); + setDataSource(null); + } + + /** + * The datasource currently used as the model in this panel. + * + * @return The datasource currently being used as the model in this panel. + */ + public DataSource getDataSource() { + return dataSource; + } + + /** + * Sets datasource to visualize in the panel. + * + * @param dataSource The datasource to use in this panel. + */ + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + + if (dataSource == null) { + updateDetailsPanelData(null, null, null, null); + } else { + long id = dataSource.getId(); + updateDetailsPanelData(dataSource, + DataSourceInfoUtilities.getSizeOfUnallocatedFiles().get(id), + DataSourceInfoUtilities.getOperatingSystems().get(id), + DataSourceInfoUtilities.getDataSourceTypes().get(id)); + } } /** @@ -60,77 +86,80 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel { * * @param selectedDataSource the DataSource to display details about. */ - void updateDetailsPanelData(DataSource selectedDataSource) { + private void updateDetailsPanelData(DataSource selectedDataSource, Long unallocatedFilesSize, String osDetails, String usage) { clearTableValues(); if (selectedDataSource != null) { - String sizeString = ""; - String sectorSizeString = ""; - String md5String = ""; - String sha1String = ""; - String sha256String = ""; - String acquisitionDetailsString = ""; - String imageTypeString = ""; - String[] filePaths = new String[0]; - String osDetailString = osDetailMap.get(selectedDataSource.getId()) == null ? "" : osDetailMap.get(selectedDataSource.getId()); - String dataSourceTypeString = usageMap.get(selectedDataSource.getId()) == null ? "" : usageMap.get(selectedDataSource.getId()); - try { - acquisitionDetailsString = selectedDataSource.getAcquisitionDetails(); - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Unable to get aquisition details for selected data source", ex); - } - if (selectedDataSource instanceof Image) { - imageTypeString = ((Image) selectedDataSource).getType().getName(); - filePaths = ((Image) selectedDataSource).getPaths(); - sizeString = getSizeString(selectedDataSource.getSize()); - sectorSizeString = getSizeString(((Image) selectedDataSource).getSsize()); - try { - //older databases may have null as the hash values - md5String = ((Image) selectedDataSource).getMd5(); - if (md5String == null) { - md5String = ""; - } - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Unable to get MD5 for selected data source", ex); - } - try { - sha1String = ((Image) selectedDataSource).getSha1(); - if (sha1String == null) { - sha1String = ""; - } - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Unable to get SHA1 for selected data source", ex); - } - try { - sha256String = ((Image) selectedDataSource).getSha256(); - if (sha256String == null) { - sha256String = ""; - } - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Unable to get SHA256 for selected data source", ex); - } - } + unallocatedSizeValue.setText(getSizeString(unallocatedFilesSize)); + operatingSystemValue.setText(StringUtils.isBlank(osDetails) ? "" : osDetails); + dataSourceUsageValue.setText(StringUtils.isBlank(usage) ? "" : usage); + + timeZoneValue.setText(selectedDataSource.getTimeZone()); displayNameValue.setText(selectedDataSource.getName()); originalNameValue.setText(selectedDataSource.getName()); deviceIdValue.setText(selectedDataSource.getDeviceId()); - dataSourceUsageValue.setText(dataSourceTypeString); - operatingSystemValue.setText(osDetailString); - timeZoneValue.setText(selectedDataSource.getTimeZone()); - acquisitionDetailsTextArea.setText(acquisitionDetailsString); - imageTypeValue.setText(imageTypeString); - sizeValue.setText(sizeString); - unallocatedSizeValue.setText(getSizeString(unallocatedFilesSizeMap.get(selectedDataSource.getId()))); - sectorSizeValue.setText(sectorSizeString); - md5HashValue.setText(md5String); - sha1HashValue.setText(sha1String); - sha256HashValue.setText(sha256String); - for (String path : filePaths) { - ((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path}); + + try { + acquisitionDetailsTextArea.setText(selectedDataSource.getAcquisitionDetails()); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get aquisition details for selected data source", ex); + } + + if (selectedDataSource instanceof Image) { + setFieldsForImage((Image) selectedDataSource); } } updateFieldVisibility(); this.repaint(); } + /** + * Sets text fields for an image. This should be called after + * clearTableValues and before updateFieldVisibility to ensure the proper + * rendering. + * + * @param selectedImage The selected image. + */ + private void setFieldsForImage(Image selectedImage) { + imageTypeValue.setText(selectedImage.getType().getName()); + sizeValue.setText(getSizeString(selectedImage.getSize())); + sectorSizeValue.setText(getSizeString(selectedImage.getSsize())); + + for (String path : selectedImage.getPaths()) { + ((DefaultTableModel) filePathsTable.getModel()).addRow(new Object[]{path}); + } + + try { + //older databases may have null as the hash values + String md5String = selectedImage.getMd5(); + if (md5String == null) { + md5String = ""; + } + md5HashValue.setText(md5String); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get MD5 for selected data source", ex); + } + + try { + String sha1String = selectedImage.getSha1(); + if (sha1String == null) { + sha1String = ""; + } + sha1HashValue.setText(sha1String); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get SHA1 for selected data source", ex); + } + + try { + String sha256String = selectedImage.getSha256(); + if (sha256String == null) { + sha256String = ""; + } + sha256HashValue.setText(sha256String); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to get SHA256 for selected data source", ex); + } + } + /** * Get a long size in bytes as a string formated to be read by users. * @@ -147,7 +176,7 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel { "DataSourceSummaryDetailsPanel.units.terabytes= TB", "DataSourceSummaryDetailsPanel.units.petabytes= PB" }) - private String getSizeString(Long size) { + private static String getSizeString(Long size) { if (size == null) { return ""; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.form index 72c45cb5a7..272fc9b041 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.form @@ -68,6 +68,11 @@ + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java index 3c68b7b3ed..e1197fa83a 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java @@ -27,7 +27,6 @@ import java.util.Observer; import java.util.Set; import javax.swing.event.ListSelectionEvent; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent; import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent.Reason; @@ -41,10 +40,8 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser private static final long serialVersionUID = 1L; private static final Set INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.DATA_SOURCE_ANALYSIS_COMPLETED); - private final DataSourceSummaryCountsPanel countsPanel; - private final DataSourceSummaryDetailsPanel detailsPanel; private final DataSourceBrowser dataSourcesPanel; - private final IngestJobInfoPanel ingestHistoryPanel; + private final DataSourceSummaryTabbedPane dataSourceSummaryTabbedPane; /** * Creates new form DataSourceSummaryDialog for displaying a summary of the @@ -61,21 +58,14 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser super(owner, Bundle.DataSourceSummaryDialog_window_title(), true); Map usageMap = DataSourceInfoUtilities.getDataSourceTypes(); Map fileCountsMap = DataSourceInfoUtilities.getCountsOfFiles(); - countsPanel = new DataSourceSummaryCountsPanel(fileCountsMap); - detailsPanel = new DataSourceSummaryDetailsPanel(usageMap); dataSourcesPanel = new DataSourceBrowser(usageMap, fileCountsMap); - ingestHistoryPanel = new IngestJobInfoPanel(); + dataSourceSummaryTabbedPane = new DataSourceSummaryTabbedPane(); initComponents(); dataSourceSummarySplitPane.setLeftComponent(dataSourcesPanel); - dataSourceTabbedPane.addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); - dataSourceTabbedPane.addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); - dataSourceTabbedPane.addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); dataSourcesPanel.addListSelectionListener((ListSelectionEvent e) -> { if (!e.getValueIsAdjusting()) { DataSource selectedDataSource = dataSourcesPanel.getSelectedDataSource(); - countsPanel.updateCountsTableData(selectedDataSource); - detailsPanel.updateDetailsPanelData(selectedDataSource); - ingestHistoryPanel.setDataSource(selectedDataSource); + dataSourceSummaryTabbedPane.setDataSource(selectedDataSource); this.repaint(); } }); @@ -116,7 +106,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser closeButton = new javax.swing.JButton(); dataSourceSummarySplitPane = new javax.swing.JSplitPane(); - dataSourceTabbedPane = new javax.swing.JTabbedPane(); + javax.swing.JTabbedPane dataSourceTabbedPane = dataSourceSummaryTabbedPane; org.openide.awt.Mnemonics.setLocalizedText(closeButton, org.openide.util.NbBundle.getMessage(DataSourceSummaryDialog.class, "DataSourceSummaryDialog.closeButton.text")); // NOI18N closeButton.addActionListener(new java.awt.event.ActionListener() { @@ -173,6 +163,5 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton closeButton; private javax.swing.JSplitPane dataSourceSummarySplitPane; - private javax.swing.JTabbedPane dataSourceTabbedPane; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java index 90c53e6217..4a528c06d2 100644 --- a/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java +++ b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java @@ -18,22 +18,31 @@ */ package org.sleuthkit.autopsy.resultviewers.summary; +import java.awt.BorderLayout; +import java.awt.Cursor; +import java.util.logging.Level; +import javax.swing.SwingUtilities; import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; import org.openide.util.NbBundle.Messages; -import org.sleuthkit.autopsy.casemodule.Case; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryTabbedPane; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.corecomponents.AbstractDataResultViewer; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DataSource; - +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.ThreadConfined; /** - * - * @author gregd + * A tabular result viewer that displays a summary of the selected Data Source. */ +@ServiceProvider(service = DataResultViewer.class) +@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class DataSourceSummaryResultViewer extends AbstractDataResultViewer { + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger(DataSourceSummaryResultViewer.class.getName()); + private final String title; public DataSourceSummaryResultViewer() { @@ -50,6 +59,7 @@ public class DataSourceSummaryResultViewer extends AbstractDataResultViewer { public DataSourceSummaryResultViewer(ExplorerManager explorerManager, String title) { super(explorerManager); this.title = title; + initComponents(); } @Override @@ -59,25 +69,47 @@ public class DataSourceSummaryResultViewer extends AbstractDataResultViewer { @Override public boolean isSupported(Node node) { - if (node == null) { - return false; - } - - DataSource contentItem = node.getLookup().lookup(DataSource.class); - if (contentItem == null) { - return false; - } - - Content content = Case.getCurrentCaseThrows().getSleuthkitCase().getDataSource(datasourceObjId); + return getDataSource(node) != null; + } + + private DataSource getDataSource(Node node) { + return node == null ? null: node.getLookup().lookup(DataSource.class); } @Override + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public void setNode(Node node) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + if (!SwingUtilities.isEventDispatchThread()) { + LOGGER.log(Level.SEVERE, "Attempting to run setNode() from non-EDT thread."); + return; + } + + DataSource dataSource = getDataSource(node); + + if (dataSource == null) { + LOGGER.log(Level.SEVERE, "No datasource for node found."); + return; + } + + this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + try { + summaryPanel.setDataSource(dataSource); + } + finally { + this.setCursor(null); + } } @Override public String getTitle() { return title; } + + private void initComponents() { + summaryPanel = new DataSourceSummaryTabbedPane(); + setLayout(new BorderLayout()); + add(summaryPanel, BorderLayout.CENTER); + } + + private DataSourceSummaryTabbedPane summaryPanel; } From d519a9d896bac02bc1b2b6a401f9d656a840d58e Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 23 Jul 2020 15:32:19 -0400 Subject: [PATCH 08/78] panel integration and refactoring --- .../DataSourceSummaryTabbedPane.java | 64 +++++++++++++++++++ .../summary/Bundle.properties-MERGED | 2 + 2 files changed, 66 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java create mode 100644 Core/src/org/sleuthkit/autopsy/resultviewers/summary/Bundle.properties-MERGED diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java new file mode 100644 index 0000000000..50b78eced9 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java @@ -0,0 +1,64 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.casemodule.datasourcesummary; + +import java.util.Map; +import javax.swing.JTabbedPane; +import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; +import org.sleuthkit.datamodel.DataSource; + +/** + * A tabbed pane showing the summary of a data source including tabs of: + * DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and + * IngestJobInfoPanel. + */ +public class DataSourceSummaryTabbedPane extends JTabbedPane { + private final DataSourceSummaryCountsPanel countsPanel; + private final DataSourceSummaryDetailsPanel detailsPanel; + private final IngestJobInfoPanel ingestHistoryPanel; + + private DataSource dataSource = null; + + public DataSourceSummaryTabbedPane() { + Map fileCountsMap = DataSourceInfoUtilities.getCountsOfFiles(); + + + countsPanel = new DataSourceSummaryCountsPanel(fileCountsMap); + detailsPanel = new DataSourceSummaryDetailsPanel(); + ingestHistoryPanel = new IngestJobInfoPanel(); + + addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); + addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); + addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); + } + + public DataSource getDataSource() { + return dataSource; + } + + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + + + countsPanel.updateCountsTableData(dataSource); + detailsPanel.setDataSource(dataSource); + ingestHistoryPanel.setDataSource(dataSource); + // TODO trigger updates in child components + } +} diff --git a/Core/src/org/sleuthkit/autopsy/resultviewers/summary/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/Bundle.properties-MERGED new file mode 100644 index 0000000000..7965b3e04e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/Bundle.properties-MERGED @@ -0,0 +1,2 @@ +DataSourceSummaryResultViewer_title=Summary +TestPanel.jTextField1.text=It's a datasource From f171de25c6d4785ca5634cfd18c9e284c093a6fe Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 23 Jul 2020 16:36:01 -0400 Subject: [PATCH 09/78] Added ContactCache --- .../autopsy/communications/ContactCache.java | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100755 Core/src/org/sleuthkit/autopsy/communications/ContactCache.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java new file mode 100755 index 0000000000..f711634d4e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java @@ -0,0 +1,182 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.communications; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.IngestManager; +import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.datamodel.Account; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * A singleton cache of the Contact artifacts for accounts. This list of + * TSK_CONTACT artifacts for a given Account retrieved on first access and + * evicted from the ache after 10 minutes. + * + */ +final class ContactCache { + + private static final Logger logger = Logger.getLogger(ContactCache.class.getName()); + + private final LoadingCache> contactCache; + + private static ContactCache instance; + + private final PropertyChangeListener ingestListener; + + /** + * Returns the list of Contacts for the given Account. + * + * @param account Account instance. + * + * @return List of TSK_CONTACT artifacts that references the given Account. + * An empty list is returned if no contacts are found. + * + * @throws ExecutionException + */ + static synchronized List getContacts(Account account) throws ExecutionException { + return getInstance().contactCache.get(account); + } + + /** + * Force the cache to invalidate all entries. + */ + static synchronized void invalidateCache() { + getInstance().contactCache.invalidateAll(); + } + + /** + * Construct a new instance. + */ + private ContactCache() { + contactCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build( + new CacheLoader>() { + @Override + public List load(Account key) { + try { + List contactList = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); + return findContactForAccount(contactList, key); + + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Failed to load contacts for account %d", key.getAccountID()), ex); + } + return new ArrayList<>(); + } + }); + + this.ingestListener = pce -> { + String eventType = pce.getPropertyName(); + if (eventType.equals(DATA_ADDED.toString())) { + ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue(); + if (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID()) { + invalidateCache(); + } + } + }; + + IngestManager.getInstance().addIngestModuleEventListener(EnumSet.of(DATA_ADDED), ingestListener); + } + + /** + * Returns the singleton instance of the cache object. + * + * @return AccountCache instance. + */ + private static synchronized ContactCache getInstance() { + if (instance == null) { + instance = new ContactCache(); + } + + return instance; + } + + /** + * Returns a list of TSK_CONTACT artifacts that reference the given account. + * + * @param allContactList List of existing TSK_CONTACT artifacts. + * @param account Account reference. + * + * @return A list of TSK_CONTACT artifact that reference the given account + * or empty list of none were found. + * + * @throws TskCoreException + */ + private List findContactForAccount(List allContactList, Account account) throws TskCoreException { + List accountContacts = new ArrayList<>(); + + for (BlackboardArtifact contact : allContactList) { + if (isAccountInAttributeList(contact.getAttributes(), account)) { + accountContacts.add(contact); + } + } + + return accountContacts; + } + + /** + * Determine if there is an attribute in the given list that references the + * given account. + * + * @param contactAttributes List of attributes. + * @param account Account object. + * + * @return True if one of the attributes in the list reference the account. + */ + private boolean isAccountInAttributeList(List contactAttributes, Account account) { + for (BlackboardAttribute attribute : contactAttributes) { + if (isAccountInAttribute(attribute, account)) { + return true; + } + } + return false; + } + + /** + * Checks the given attribute to see if it references the given account. + * + * @param attribute BlackboardAttribute to check. + * @param account References account. + * + * @return True if the attribute references the account. + */ + private boolean isAccountInAttribute(BlackboardAttribute attribute, Account account) { + + String typeName = attribute.getAttributeType().getTypeName(); + return (typeName.startsWith("TSK_EMAIL") + || typeName.startsWith("TSK_PHONE") + || typeName.startsWith("TSK_NAME") + || typeName.startsWith("TSK_ID")) + && attribute.getValueString().equals(account.getTypeSpecificID()); + } + +} From 692b4604fa511fc3ea376a615d48fdd01e69940d Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 23 Jul 2020 16:47:23 -0400 Subject: [PATCH 10/78] Address codacy issues --- .../autopsy/communications/ContactCache.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java index f711634d4e..1f34b3cf62 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java +++ b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java @@ -48,12 +48,10 @@ final class ContactCache { private static final Logger logger = Logger.getLogger(ContactCache.class.getName()); - private final LoadingCache> contactCache; + private final LoadingCache> accountMap; private static ContactCache instance; - private final PropertyChangeListener ingestListener; - /** * Returns the list of Contacts for the given Account. * @@ -65,21 +63,21 @@ final class ContactCache { * @throws ExecutionException */ static synchronized List getContacts(Account account) throws ExecutionException { - return getInstance().contactCache.get(account); + return getInstance().accountMap.get(account); } /** * Force the cache to invalidate all entries. */ static synchronized void invalidateCache() { - getInstance().contactCache.invalidateAll(); + getInstance().accountMap.invalidateAll(); } /** * Construct a new instance. */ private ContactCache() { - contactCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build( + accountMap = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build( new CacheLoader>() { @Override public List load(Account key) { @@ -94,7 +92,7 @@ final class ContactCache { } }); - this.ingestListener = pce -> { + PropertyChangeListener ingestListener = pce -> { String eventType = pce.getPropertyName(); if (eventType.equals(DATA_ADDED.toString())) { ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue(); From 1eb5fc5a331831b9ae8cbfed15c5c01404e7d1d3 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 23 Jul 2020 17:19:41 -0400 Subject: [PATCH 11/78] refactoring --- .../DataSourceSummaryTabbedPane.java | 19 +- .../DataSourceSummaryCountsPanel.form | 22 +- .../DataSourceSummaryCountsPanel.java | 375 ++++++------------ 3 files changed, 141 insertions(+), 275 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java index 50b78eced9..0bb1cfbef5 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.casemodule.datasourcesummary; -import java.util.Map; import javax.swing.JTabbedPane; import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; import org.sleuthkit.datamodel.DataSource; @@ -36,29 +35,27 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { private DataSource dataSource = null; public DataSourceSummaryTabbedPane() { - Map fileCountsMap = DataSourceInfoUtilities.getCountsOfFiles(); - - - countsPanel = new DataSourceSummaryCountsPanel(fileCountsMap); + countsPanel = new DataSourceSummaryCountsPanel(); detailsPanel = new DataSourceSummaryDetailsPanel(); ingestHistoryPanel = new IngestJobInfoPanel(); - + setTabs(); + } + + private void setTabs() { + this.removeAll(); addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); } - + public DataSource getDataSource() { return dataSource; } public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; - - - countsPanel.updateCountsTableData(dataSource); + countsPanel.setDataSource(dataSource); detailsPanel.setDataSource(dataSource); ingestHistoryPanel.setDataSource(dataSource); - // TODO trigger updates in child components } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.form index d3a17e0205..b82bd6f3a8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.form @@ -70,18 +70,13 @@ - - - - - - + @@ -93,25 +88,20 @@ - - - - - - + - + @@ -125,12 +115,6 @@ - - - - -
-
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java index 1d99f1c445..8de75b1c24 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java @@ -18,12 +18,9 @@ */ package org.sleuthkit.autopsy.casemodule.datasourcesummary; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JLabel; -import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import org.openide.util.NbBundle.Messages; @@ -34,32 +31,53 @@ import org.sleuthkit.datamodel.DataSource; * Panel for displaying summary information on the known files present in the * specified DataSource */ +@Messages({"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.type.header=File Type", + "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.count.header=Count", + "DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type", + "DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count", + "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type", + "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count" +}) class DataSourceSummaryCountsPanel extends javax.swing.JPanel { - + private static final Object[] MIME_TYPE_COLUMN_HEADERS = new Object[]{ + Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_type_header(), + Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_count_header() + }; + + private static final Object[] FILE_BY_CATEGORY_COLUMN_HEADERS = new Object[]{ + Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_type_header(), + Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_count_header() + }; + + private static final Object[] ARTIFACT_COUNTS_COLUMN_HEADERS = new Object[]{ + Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_type_header(), + Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_count_header() + }; + private static final long serialVersionUID = 1L; - private FilesByMimeTypeTableModel filesByMimeTypeTableModel = new FilesByMimeTypeTableModel(null); - private FilesByCategoryTableModel filesByCategoryTableModel = new FilesByCategoryTableModel(null); private static final Logger logger = Logger.getLogger(DataSourceSummaryCountsPanel.class.getName()); - private final Map allFilesCountsMap; - private final Map slackFilesCountsMap; - private final Map directoriesCountsMap; - private final Map unallocatedFilesCountsMap; - private final Map> artifactsByTypeCountsMap; private final DefaultTableCellRenderer rightAlignedRenderer = new DefaultTableCellRenderer(); + + private DataSource dataSource; /** * Creates new form DataSourceSummaryCountsPanel */ - DataSourceSummaryCountsPanel(Map fileCountsMap) { - this.allFilesCountsMap = fileCountsMap; - this.slackFilesCountsMap = DataSourceInfoUtilities.getCountsOfSlackFiles(); - this.directoriesCountsMap = DataSourceInfoUtilities.getCountsOfDirectories(); - this.unallocatedFilesCountsMap = DataSourceInfoUtilities.getCountsOfUnallocatedFiles(); - this.artifactsByTypeCountsMap = DataSourceInfoUtilities.getCountsOfArtifactsByType(); + DataSourceSummaryCountsPanel() { rightAlignedRenderer.setHorizontalAlignment(JLabel.RIGHT); initComponents(); fileCountsByMimeTypeTable.getTableHeader().setReorderingAllowed(false); fileCountsByCategoryTable.getTableHeader().setReorderingAllowed(false); + setDataSource(null); + } + + public DataSource getDataSource() { + return dataSource; + } + + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + updateCountsTableData(dataSource); } /** @@ -67,36 +85,107 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { * * @param selectedDataSource the DataSource to display file information for */ - void updateCountsTableData(DataSource selectedDataSource) { - filesByMimeTypeTableModel = new FilesByMimeTypeTableModel(selectedDataSource); - fileCountsByMimeTypeTable.setModel(filesByMimeTypeTableModel); + private void updateCountsTableData(DataSource selectedDataSource) { + fileCountsByMimeTypeTable.setModel(new DefaultTableModel(getMimeTypeModel(selectedDataSource), MIME_TYPE_COLUMN_HEADERS)); fileCountsByMimeTypeTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); fileCountsByMimeTypeTable.getColumnModel().getColumn(0).setPreferredWidth(130); - filesByCategoryTableModel = new FilesByCategoryTableModel(selectedDataSource); - fileCountsByCategoryTable.setModel(filesByCategoryTableModel); + + fileCountsByCategoryTable.setModel(new DefaultTableModel(getFileCategoryModel(selectedDataSource), FILE_BY_CATEGORY_COLUMN_HEADERS)); fileCountsByCategoryTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); fileCountsByCategoryTable.getColumnModel().getColumn(0).setPreferredWidth(130); - updateArtifactCounts(selectedDataSource); - this.repaint(); - } - - /** - * Helper method to update the artifact specific counts by clearing the - * table and adding counts for the artifacts which exist in the selected - * data source. - * - * @param selectedDataSource the data source to display artifact counts for - */ - private void updateArtifactCounts(DataSource selectedDataSource) { - ((DefaultTableModel) artifactCountsTable.getModel()).setRowCount(0); - if (selectedDataSource != null && artifactsByTypeCountsMap.get(selectedDataSource.getId()) != null) { - Map artifactCounts = artifactsByTypeCountsMap.get(selectedDataSource.getId()); - for (String key : artifactCounts.keySet()) { - ((DefaultTableModel) artifactCountsTable.getModel()).addRow(new Object[]{key, artifactCounts.get(key)}); - } - } + + artifactCountsTable.setModel(new DefaultTableModel(getArtifactCountsModel(selectedDataSource), ARTIFACT_COUNTS_COLUMN_HEADERS)); artifactCountsTable.getColumnModel().getColumn(0).setPreferredWidth(230); artifactCountsTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); + + this.repaint(); + } + + + private static Object[] pair(String key, Object val) { + return new Object[]{key, val}; + } + + @Messages({ + "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images", + "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos", + "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio", + "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents", + "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables" + }) + private static Object[][] getMimeTypeModel(DataSource dataSource) { + return new Object[][]{ + pair(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_images_row(), + getCount(dataSource, FileTypeUtils.FileTypeCategory.IMAGE)), + pair(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_videos_row(), + getCount(dataSource, FileTypeUtils.FileTypeCategory.VIDEO)), + pair(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_audio_row(), + getCount(dataSource, FileTypeUtils.FileTypeCategory.AUDIO)), + pair(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_documents_row(), + getCount(dataSource, FileTypeUtils.FileTypeCategory.DOCUMENTS)), + pair(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_executables_row(), + getCount(dataSource, FileTypeUtils.FileTypeCategory.EXECUTABLE)) + }; + } + + private static Long getCount(DataSource dataSource, FileTypeUtils.FileTypeCategory category) { + return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(dataSource, category.getMediaTypes()); + } + + + @Messages({ + "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All", + "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated", + "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.unallocated.row=Unallocated", + "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.slack.row=Slack", + "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory" + }) + private static Object[][] getFileCategoryModel(DataSource selectedDataSource) { + Long dataSourceId = selectedDataSource == null ? null : selectedDataSource.getId(); + // or 0 if not found + Long fileCount = zeroIfNull(DataSourceInfoUtilities.getCountsOfFiles().get(dataSourceId)); + Long unallocatedFiles = zeroIfNull(DataSourceInfoUtilities.getCountsOfUnallocatedFiles().get(dataSourceId)); + Long allocatedFiles = zeroIfNull(getAllocatedCount(fileCount, unallocatedFiles)); + Long slackFiles = zeroIfNull(DataSourceInfoUtilities.getCountsOfSlackFiles().get(dataSourceId)); + Long directories = zeroIfNull(DataSourceInfoUtilities.getCountsOfDirectories().get(dataSourceId)); + + return new Object[][]{ + pair(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_all_row(), fileCount), + pair(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_allocated_row(), allocatedFiles), + pair(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_unallocated_row(), unallocatedFiles), + pair(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_slack_row(), slackFiles), + pair(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_directory_row(), directories) + }; + } + + + private static Long zeroIfNull(Long origValue) { + return origValue == null ? 0 : origValue; + } + + + private static long getAllocatedCount(Long allFilesCount, Long unallocatedFilesCount) { + if (allFilesCount == null) { + return 0; + } else if (unallocatedFilesCount == null) { + return allFilesCount; + } else { + return allFilesCount - unallocatedFilesCount; + } + } + + private static Object[][] getArtifactCountsModel(DataSource selectedDataSource) { + Long dataSourceId = selectedDataSource == null ? null : selectedDataSource.getId(); + Map artifactMapping = DataSourceInfoUtilities.getCountsOfArtifactsByType().get(dataSourceId); + if (artifactMapping == null) { + return new Object[][]{}; + } + + return artifactMapping.entrySet().stream() + .filter((entrySet) -> entrySet != null && entrySet.getKey() != null) + .sorted((a,b) -> a.getKey().compareTo(b.getKey())) + .map((entrySet) -> new Object[]{entrySet.getKey(), entrySet.getValue()}) + .toArray(Object[][]::new); } /** @@ -118,12 +207,10 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { artifactCountsScrollPane = new javax.swing.JScrollPane(); artifactCountsTable = new javax.swing.JTable(); - fileCountsByMimeTypeTable.setModel(filesByMimeTypeTableModel); fileCountsByMimeTypeScrollPane.setViewportView(fileCountsByMimeTypeTable); org.openide.awt.Mnemonics.setLocalizedText(byMimeTypeLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.byMimeTypeLabel.text")); // NOI18N - fileCountsByCategoryTable.setModel(filesByCategoryTableModel); fileCountsByCategoryScrollPane.setViewportView(fileCountsByCategoryTable); org.openide.awt.Mnemonics.setLocalizedText(byCategoryLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.byCategoryLabel.text")); // NOI18N @@ -131,22 +218,6 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(DataSourceSummaryCountsPanel.class, "DataSourceSummaryCountsPanel.jLabel1.text")); // NOI18N artifactCountsTable.setAutoCreateRowSorter(true); - artifactCountsTable.setModel(new javax.swing.table.DefaultTableModel( - new Object [][] { - - }, - new String [] { - "Result Type", "Count" - } - ) { - boolean[] canEdit = new boolean [] { - false, false - }; - - public boolean isCellEditable(int rowIndex, int columnIndex) { - return canEdit [columnIndex]; - } - }); artifactCountsScrollPane.setViewportView(artifactCountsTable); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); @@ -206,190 +277,4 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { private javax.swing.JTable fileCountsByMimeTypeTable; private javax.swing.JLabel jLabel1; // End of variables declaration//GEN-END:variables - - /** - * Table model for the files table model to display counts of specific file - * types by mime type found in the currently selected data source. - */ - @Messages({"DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.type.header=File Type", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.count.header=Count"}) - private class FilesByMimeTypeTableModel extends AbstractTableModel { - - private static final long serialVersionUID = 1L; - private final DataSource currentDataSource; - private final List columnHeaders = new ArrayList<>(); - private static final int IMAGES_ROW_INDEX = 0; - private static final int VIDEOS_ROW_INDEX = 1; - private static final int AUDIO_ROW_INDEX = 2; - private static final int DOCUMENTS_ROW_INDEX = 3; - private static final int EXECUTABLES_ROW_INDEX = 4; - - /** - * Create a FilesByMimeTypeTableModel for the speicified datasource. - * - * @param selectedDataSource the datasource which this - * FilesByMimeTypeTablemodel will represent - */ - FilesByMimeTypeTableModel(DataSource selectedDataSource) { - columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_type_header()); - columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_count_header()); - currentDataSource = selectedDataSource; - } - - @Override - public int getRowCount() { - //should be kept equal to the number of types we are displaying in the tables - return 5; - } - - @Override - public int getColumnCount() { - return columnHeaders.size(); - } - - @Messages({ - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents", - "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables" - }) - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - if (columnIndex == 0) { - switch (rowIndex) { - case IMAGES_ROW_INDEX: - return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_images_row(); - case VIDEOS_ROW_INDEX: - return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_videos_row(); - case AUDIO_ROW_INDEX: - return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_audio_row(); - case DOCUMENTS_ROW_INDEX: - return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_documents_row(); - case EXECUTABLES_ROW_INDEX: - return Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_executables_row(); - default: - break; - } - } else if (columnIndex == 1) { - switch (rowIndex) { - case 0: - return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.IMAGE.getMediaTypes()); - case 1: - return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.VIDEO.getMediaTypes()); - case 2: - return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.AUDIO.getMediaTypes()); - case 3: - return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.DOCUMENTS.getMediaTypes()); - case 4: - return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(currentDataSource, FileTypeUtils.FileTypeCategory.EXECUTABLE.getMediaTypes()); - default: - break; - } - } - return null; - } - - @Override - public String getColumnName(int column) { - return columnHeaders.get(column); - } - } - - /** - * Table model for the files table model to display counts of specific file - * types by category found in the currently selected data source. - */ - @Messages({"DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type", - "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count"}) - private class FilesByCategoryTableModel extends AbstractTableModel { - - private static final long serialVersionUID = 1L; - private final DataSource currentDataSource; - private final List columnHeaders = new ArrayList<>(); - private static final int ALL_FILES_ROW_INDEX = 0; - private static final int ALLOCATED_FILES_ROW_INDEX = 1; - private static final int UNALLOCATED_FILES_ROW_INDEX = 2; - private static final int SLACK_FILES_ROW_INDEX = 3; - private static final int DIRECTORIES_ROW_INDEX = 4; - /** - * Create a FilesByCategoryTableModel for the speicified datasource. - * - * @param selectedDataSource the datasource which this - * FilesByCategoryTablemodel will represent - */ - FilesByCategoryTableModel(DataSource selectedDataSource) { - columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_type_header()); - columnHeaders.add(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_count_header()); - currentDataSource = selectedDataSource; - } - - @Override - public int getRowCount() { - //should be kept equal to the number of types we are displaying in the tables - return 5; - } - - @Override - public int getColumnCount() { - return columnHeaders.size(); - } - - @Messages({ - "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All", - "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated", - "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.unallocated.row=Unallocated", - "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.slack.row=Slack", - "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory" - }) - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - if (columnIndex == 0) { - switch (rowIndex) { - case ALL_FILES_ROW_INDEX: - return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_all_row(); - case ALLOCATED_FILES_ROW_INDEX: - return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_allocated_row(); - case UNALLOCATED_FILES_ROW_INDEX: - return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_unallocated_row(); - case SLACK_FILES_ROW_INDEX: - return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_slack_row(); - case DIRECTORIES_ROW_INDEX: - return Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_directory_row(); - default: - break; - } - } else if (columnIndex == 1 && currentDataSource != null) { - switch (rowIndex) { - case 0: - return allFilesCountsMap.get(currentDataSource.getId()) == null ? 0 : allFilesCountsMap.get(currentDataSource.getId()); - case 1: - //All files should be either allocated or unallocated as dir_flags only has two values so any file that isn't unallocated is allocated - Long unallocatedFilesCount = unallocatedFilesCountsMap.get(currentDataSource.getId()); - Long allFilesCount = allFilesCountsMap.get(currentDataSource.getId()); - if (allFilesCount == null) { - return 0; - } else if (unallocatedFilesCount == null) { - return allFilesCount; - } else { - return allFilesCount - unallocatedFilesCount; - } - case 2: - return unallocatedFilesCountsMap.get(currentDataSource.getId()) == null ? 0 : unallocatedFilesCountsMap.get(currentDataSource.getId()); - case 3: - return slackFilesCountsMap.get(currentDataSource.getId()) == null ? 0 : slackFilesCountsMap.get(currentDataSource.getId()); - case 4: - return directoriesCountsMap.get(currentDataSource.getId()) == null ? 0 : directoriesCountsMap.get(currentDataSource.getId()); - default: - break; - } - } - return null; - } - - @Override - public String getColumnName(int column) { - return columnHeaders.get(column); - } - } } From 24e4c80a4c34c75f6df4524b3ea8cc90d2d0fe48 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 23 Jul 2020 17:25:52 -0400 Subject: [PATCH 12/78] adding bundles --- .../casemodule/datasourceSummary/Bundle.properties-MERGED | 2 ++ .../autopsy/resultviewers/summary/Bundle.properties-MERGED | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED index fe998b0e57..eb7affe9ee 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED @@ -1,4 +1,6 @@ CTL_DataSourceSummaryAction=Data Source Summary +DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count +DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count diff --git a/Core/src/org/sleuthkit/autopsy/resultviewers/summary/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/Bundle.properties-MERGED index 7965b3e04e..8a629e17a9 100644 --- a/Core/src/org/sleuthkit/autopsy/resultviewers/summary/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/Bundle.properties-MERGED @@ -1,2 +1 @@ DataSourceSummaryResultViewer_title=Summary -TestPanel.jTextField1.text=It's a datasource From 820006d9ab1b39195a87bd6de4c1d154b3026494 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 24 Jul 2020 08:55:42 -0400 Subject: [PATCH 13/78] code cleanup --- .../DataSourceSummaryTabbedPane.java | 29 +++- .../DataSourceSummaryCountsPanel.java | 135 ++++++++++++------ .../DataSourceSummaryDetailsPanel.java | 5 +- .../DataSourceSummaryResultViewer.java | 55 +++++-- 4 files changed, 159 insertions(+), 65 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java index 0bb1cfbef5..5fbcdeca38 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java @@ -19,21 +19,26 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import javax.swing.JTabbedPane; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; import org.sleuthkit.datamodel.DataSource; /** * A tabbed pane showing the summary of a data source including tabs of: - * DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and + * DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and * IngestJobInfoPanel. */ public class DataSourceSummaryTabbedPane extends JTabbedPane { + private final DataSourceSummaryCountsPanel countsPanel; private final DataSourceSummaryDetailsPanel detailsPanel; private final IngestJobInfoPanel ingestHistoryPanel; - + private DataSource dataSource = null; - + + /** + * Constructs a tabbed pane showing the summary of a data source. + */ public DataSourceSummaryTabbedPane() { countsPanel = new DataSourceSummaryCountsPanel(); detailsPanel = new DataSourceSummaryDetailsPanel(); @@ -47,15 +52,29 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); } - + + /** + * The datasource currently used as the model in this panel. + * + * @return The datasource currently being used as the model in this panel. + */ public DataSource getDataSource() { return dataSource; } + /** + * Sets datasource to visualize in the tabbed panel. + * + * @param dataSource The datasource to use in this panel. + */ public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; + countsPanel.setDataSource(dataSource); detailsPanel.setDataSource(dataSource); - ingestHistoryPanel.setDataSource(dataSource); + + // make sure ingest history panel doesn't try to show a data source when null or case is closed + ingestHistoryPanel.setDataSource( + dataSource != null && Case.isCaseOpen() ? dataSource : null); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java index 8de75b1c24..92337090c9 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java @@ -24,6 +24,7 @@ import javax.swing.JLabel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableModel; import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.FileTypeUtils; import org.sleuthkit.datamodel.DataSource; @@ -39,25 +40,28 @@ import org.sleuthkit.datamodel.DataSource; "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count" }) class DataSourceSummaryCountsPanel extends javax.swing.JPanel { + + private static final Object[][] EMPTY_PAIRS = new Object[][]{}; + private static final Object[] MIME_TYPE_COLUMN_HEADERS = new Object[]{ Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_type_header(), Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_count_header() }; - + private static final Object[] FILE_BY_CATEGORY_COLUMN_HEADERS = new Object[]{ Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_type_header(), Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_count_header() }; - + private static final Object[] ARTIFACT_COUNTS_COLUMN_HEADERS = new Object[]{ Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_type_header(), Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_count_header() }; - + private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(DataSourceSummaryCountsPanel.class.getName()); private final DefaultTableCellRenderer rightAlignedRenderer = new DefaultTableCellRenderer(); - + private DataSource dataSource; /** @@ -71,41 +75,62 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { setDataSource(null); } + /** + * The datasource currently used as the model in this panel. + * + * @return The datasource currently being used as the model in this panel. + */ public DataSource getDataSource() { return dataSource; } + /** + * Sets datasource to visualize in the panel. + * + * @param dataSource The datasource to use in this panel. + */ public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; - updateCountsTableData(dataSource); + if (dataSource == null || !Case.isCaseOpen()) { + updateCountsTableData(EMPTY_PAIRS, + EMPTY_PAIRS, + EMPTY_PAIRS); + } else { + updateCountsTableData(getMimeTypeModel(dataSource), + getFileCategoryModel(dataSource), + getArtifactCountsModel(dataSource)); + } + } /** - * Specify the DataSource to display file information for + * Specify the DataSource to display file information for. * - * @param selectedDataSource the DataSource to display file information for + * @param mimeTypeDataModel The mime type data model. + * @param fileCategoryDataModel The file category data model. + * @param artifactDataModel The artifact type data model. */ - private void updateCountsTableData(DataSource selectedDataSource) { - fileCountsByMimeTypeTable.setModel(new DefaultTableModel(getMimeTypeModel(selectedDataSource), MIME_TYPE_COLUMN_HEADERS)); + private void updateCountsTableData(Object[][] mimeTypeDataModel, Object[][] fileCategoryDataModel, Object[][] artifactDataModel) { + fileCountsByMimeTypeTable.setModel(new DefaultTableModel(mimeTypeDataModel, MIME_TYPE_COLUMN_HEADERS)); fileCountsByMimeTypeTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); fileCountsByMimeTypeTable.getColumnModel().getColumn(0).setPreferredWidth(130); - - fileCountsByCategoryTable.setModel(new DefaultTableModel(getFileCategoryModel(selectedDataSource), FILE_BY_CATEGORY_COLUMN_HEADERS)); + + fileCountsByCategoryTable.setModel(new DefaultTableModel(fileCategoryDataModel, FILE_BY_CATEGORY_COLUMN_HEADERS)); fileCountsByCategoryTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); fileCountsByCategoryTable.getColumnModel().getColumn(0).setPreferredWidth(130); - - artifactCountsTable.setModel(new DefaultTableModel(getArtifactCountsModel(selectedDataSource), ARTIFACT_COUNTS_COLUMN_HEADERS)); + + artifactCountsTable.setModel(new DefaultTableModel(artifactDataModel, ARTIFACT_COUNTS_COLUMN_HEADERS)); artifactCountsTable.getColumnModel().getColumn(0).setPreferredWidth(230); artifactCountsTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); - + this.repaint(); } - - - private static Object[] pair(String key, Object val) { - return new Object[]{key, val}; - } - + + /** + * Determines the JTable data model for datasource mime types. + * @param dataSource The DataSource. + * @return The model to be used with a JTable. + */ @Messages({ "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images", "DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos", @@ -115,24 +140,34 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { }) private static Object[][] getMimeTypeModel(DataSource dataSource) { return new Object[][]{ - pair(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_images_row(), - getCount(dataSource, FileTypeUtils.FileTypeCategory.IMAGE)), - pair(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_videos_row(), - getCount(dataSource, FileTypeUtils.FileTypeCategory.VIDEO)), - pair(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_audio_row(), - getCount(dataSource, FileTypeUtils.FileTypeCategory.AUDIO)), - pair(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_documents_row(), - getCount(dataSource, FileTypeUtils.FileTypeCategory.DOCUMENTS)), - pair(Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_executables_row(), - getCount(dataSource, FileTypeUtils.FileTypeCategory.EXECUTABLE)) + new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_images_row(), + getCount(dataSource, FileTypeUtils.FileTypeCategory.IMAGE)}, + new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_videos_row(), + getCount(dataSource, FileTypeUtils.FileTypeCategory.VIDEO)}, + new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_audio_row(), + getCount(dataSource, FileTypeUtils.FileTypeCategory.AUDIO)}, + new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_documents_row(), + getCount(dataSource, FileTypeUtils.FileTypeCategory.DOCUMENTS)}, + new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_executables_row(), + getCount(dataSource, FileTypeUtils.FileTypeCategory.EXECUTABLE)} }; } + /** + * Retrieves the counts of files of a particular mime type for a particular DataSource. + * @param dataSource The DataSource. + * @param category The mime type category. + * @return The count. + */ private static Long getCount(DataSource dataSource, FileTypeUtils.FileTypeCategory category) { return DataSourceInfoUtilities.getCountOfFilesForMimeTypes(dataSource, category.getMediaTypes()); } - - + + /** + * Determines the JTable data model for datasource file categories. + * @param dataSource The DataSource. + * @return The model to be used with a JTable. + */ @Messages({ "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All", "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated", @@ -150,20 +185,29 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { Long directories = zeroIfNull(DataSourceInfoUtilities.getCountsOfDirectories().get(dataSourceId)); return new Object[][]{ - pair(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_all_row(), fileCount), - pair(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_allocated_row(), allocatedFiles), - pair(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_unallocated_row(), unallocatedFiles), - pair(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_slack_row(), slackFiles), - pair(Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_directory_row(), directories) + new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_all_row(), fileCount}, + new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_allocated_row(), allocatedFiles}, + new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_unallocated_row(), unallocatedFiles}, + new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_slack_row(), slackFiles}, + new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_directory_row(), directories} }; } - + /** + * Returns 0 if value is null. + * @param origValue The original value. + * @return The value or 0 if null. + */ private static Long zeroIfNull(Long origValue) { return origValue == null ? 0 : origValue; } - + /** + * Safely gets the allocated files count. + * @param allFilesCount The count of all files. + * @param unallocatedFilesCount The count of unallocated files. + * @return The count of allocated files. + */ private static long getAllocatedCount(Long allFilesCount, Long unallocatedFilesCount) { if (allFilesCount == null) { return 0; @@ -173,17 +217,22 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { return allFilesCount - unallocatedFilesCount; } } - + + /** + * The counts of different artifact types found in a DataSource. + * @param selectedDataSource The DataSource. + * @return The JTable data model of counts of artifact types. + */ private static Object[][] getArtifactCountsModel(DataSource selectedDataSource) { Long dataSourceId = selectedDataSource == null ? null : selectedDataSource.getId(); Map artifactMapping = DataSourceInfoUtilities.getCountsOfArtifactsByType().get(dataSourceId); if (artifactMapping == null) { - return new Object[][]{}; + return EMPTY_PAIRS; } - + return artifactMapping.entrySet().stream() .filter((entrySet) -> entrySet != null && entrySet.getKey() != null) - .sorted((a,b) -> a.getKey().compareTo(b.getKey())) + .sorted((a, b) -> a.getKey().compareTo(b.getKey())) .map((entrySet) -> new Object[]{entrySet.getKey(), entrySet.getValue()}) .toArray(Object[][]::new); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java index 61667dbb42..7409c2eda3 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java @@ -19,13 +19,12 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.text.DecimalFormat; -import java.util.Map; -import java.util.HashMap; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.table.DefaultTableModel; import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.TskCoreException; @@ -70,7 +69,7 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel { public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; - if (dataSource == null) { + if (dataSource == null || !Case.isCaseOpen()) { updateDetailsPanelData(null, null, null, null); } else { long id = dataSource.getId(); diff --git a/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java index 4a528c06d2..c3ca3ca2a2 100644 --- a/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java +++ b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java @@ -33,35 +33,56 @@ import org.sleuthkit.datamodel.DataSource; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; - /** * A tabular result viewer that displays a summary of the selected Data Source. */ @ServiceProvider(service = DataResultViewer.class) @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives public class DataSourceSummaryResultViewer extends AbstractDataResultViewer { + private static final long serialVersionUID = 1L; private static final Logger LOGGER = Logger.getLogger(DataSourceSummaryResultViewer.class.getName()); - + private final String title; - + + /** + * Constructs a tabular result viewer that displays a summary of the + * selected Data Source. + */ public DataSourceSummaryResultViewer() { this(null); } - + + /** + * Constructs a tabular result viewer that displays a summary of the + * selected Data Source. + * + * @param explorerManager The explorer manager of the ancestor top + * component. + * + */ @Messages({ "DataSourceSummaryResultViewer_title=Summary" }) public DataSourceSummaryResultViewer(ExplorerManager explorerManager) { this(explorerManager, Bundle.DataSourceSummaryResultViewer_title()); } - + + /** + * Constructs a tabular result viewer that displays a summary of the + * selected Data Source. + * + * @param explorerManager The explorer manager of the ancestor top + * component. + * @param title The title. + * + */ public DataSourceSummaryResultViewer(ExplorerManager explorerManager, String title) { super(explorerManager); this.title = title; initComponents(); } - + @Override public DataResultViewer createInstance() { return new DataSourceSummaryResultViewer(); @@ -71,9 +92,16 @@ public class DataSourceSummaryResultViewer extends AbstractDataResultViewer { public boolean isSupported(Node node) { return getDataSource(node) != null; } - + + /** + * Returns the datasource attached to the node or null if none can be found. + * + * @param node The node to search. + * + * @return The datasource or null if not found. + */ private DataSource getDataSource(Node node) { - return node == null ? null: node.getLookup().lookup(DataSource.class); + return node == null ? null : node.getLookup().lookup(DataSource.class); } @Override @@ -83,19 +111,18 @@ public class DataSourceSummaryResultViewer extends AbstractDataResultViewer { LOGGER.log(Level.SEVERE, "Attempting to run setNode() from non-EDT thread."); return; } - + DataSource dataSource = getDataSource(node); - + if (dataSource == null) { LOGGER.log(Level.SEVERE, "No datasource for node found."); return; } - + this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { summaryPanel.setDataSource(dataSource); - } - finally { + } finally { this.setCursor(null); } } @@ -104,7 +131,7 @@ public class DataSourceSummaryResultViewer extends AbstractDataResultViewer { public String getTitle() { return title; } - + private void initComponents() { summaryPanel = new DataSourceSummaryTabbedPane(); setLayout(new BorderLayout()); From de588e9e5a152d20fc951d419ab9be7fdc50883a Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 24 Jul 2020 11:44:59 -0400 Subject: [PATCH 14/78] acquiring single data source in data layer --- .../DataSourceInfoUtilities.java | 45 +++++++++++++++++++ .../DataSourceSummaryCountsPanel.java | 8 ++-- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index c2d8263056..b884118f5f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -97,6 +97,51 @@ final class DataSourceInfoUtilities { } } + private static Long baseCountOfFiles(DataSource currentDataSource, String additionalWhere, String onError) { + if (currentDataSource != null) { + try { + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + return skCase.countFilesWhere( + "COUNT(*) AS value" + + " FROM tsk_files" + + " WHERE dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() + + " AND name<>''" + + " AND data_source_obj_id=" + currentDataSource.getId() + + " AND " + additionalWhere); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, onError, ex); + //unable to get count of files for the specified mimetypes cell will be displayed as empty + } + } + return null; + } + + static Long getCountOfFiles(DataSource currentDataSource) { + return baseCountOfFiles(currentDataSource, + "type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType(), + "Unable to get count of files, providing empty results"); + } + + static Long getCountOfUnallocatedFiles(DataSource currentDataSource) { + return baseCountOfFiles(currentDataSource, + "type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + + " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue(), + "Unable to get counts of unallocated files for datasource, providing empty results"); + } + + static Long getCountOfDirectories(DataSource currentDataSource) { + return baseCountOfFiles(currentDataSource, + "'type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + + " AND meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue(), + "Unable to get count of directories for datasource, providing empty results"); + } + + static Long getCountOfSlackFiles(DataSource currentDataSource) { + return baseCountOfFiles(currentDataSource, + "type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(), + "Unable to get count of slack files for datasources, providing empty results"); + } + /** * Get a map containing the number of artifacts in each data source in the * current case. diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java index 92337090c9..0df73ce93f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java @@ -178,11 +178,11 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { private static Object[][] getFileCategoryModel(DataSource selectedDataSource) { Long dataSourceId = selectedDataSource == null ? null : selectedDataSource.getId(); // or 0 if not found - Long fileCount = zeroIfNull(DataSourceInfoUtilities.getCountsOfFiles().get(dataSourceId)); - Long unallocatedFiles = zeroIfNull(DataSourceInfoUtilities.getCountsOfUnallocatedFiles().get(dataSourceId)); + Long fileCount = zeroIfNull(DataSourceInfoUtilities.getCountOfFiles(selectedDataSource)); + Long unallocatedFiles = zeroIfNull(DataSourceInfoUtilities.getCountOfUnallocatedFiles(selectedDataSource)); Long allocatedFiles = zeroIfNull(getAllocatedCount(fileCount, unallocatedFiles)); - Long slackFiles = zeroIfNull(DataSourceInfoUtilities.getCountsOfSlackFiles().get(dataSourceId)); - Long directories = zeroIfNull(DataSourceInfoUtilities.getCountsOfDirectories().get(dataSourceId)); + Long slackFiles = zeroIfNull(DataSourceInfoUtilities.getCountOfSlackFiles(selectedDataSource)); + Long directories = zeroIfNull(DataSourceInfoUtilities.getCountOfDirectories(selectedDataSource)); return new Object[][]{ new Object[]{Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_all_row(), fileCount}, From d8f5bcec3a5177a0aaa796316e150e05d3e711fc Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 27 Jul 2020 12:12:29 -0400 Subject: [PATCH 15/78] commenting and sql results per data source --- .../DataSourceSummaryTabbedPane.java | 5 +- .../DataSourceInfoUtilities.java | 489 ++++++++++-------- .../DataSourceSummaryCountsPanel.java | 28 +- .../DataSourceSummaryDetailsPanel.java | 7 +- 4 files changed, 312 insertions(+), 217 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java index 5fbcdeca38..d435b5e135 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java @@ -46,6 +46,9 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { setTabs(); } + /** + * Set tabs to the details panel, counts panel, and ingest history panel. + */ private void setTabs() { this.removeAll(); addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); @@ -72,7 +75,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { countsPanel.setDataSource(dataSource); detailsPanel.setDataSource(dataSource); - + // make sure ingest history panel doesn't try to show a data source when null or case is closed ingestHistoryPanel.setDataSource( dataSource != null && Case.isCaseOpen() ? dataSource : null); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index b884118f5f..cfa28d5c38 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.casemodule.datasourcesummary; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -43,6 +45,288 @@ final class DataSourceInfoUtilities { private static final Logger logger = Logger.getLogger(DataSourceInfoUtilities.class.getName()); + /** + * Gets a count of files for a particular datasource where it is not a + * virtual directory and has a name. + * + * @param currentDataSource The datasource. + * @param additionalWhere Additional sql where clauses. + * @param onError The message to log on error. + * + * @return The count of files or null on error. + */ + private static Long getCountOfFiles(DataSource currentDataSource, String additionalWhere, String onError) { + if (currentDataSource != null) { + try { + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + return skCase.countFilesWhere( + "dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() + + " AND name<>''" + + " AND data_source_obj_id=" + currentDataSource.getId() + + " AND " + additionalWhere); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, onError, ex); + //unable to get count of files for the specified types cell will be displayed as empty + } + } + return null; + } + + /** + * Get count of files in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + */ + static Long getCountOfFiles(DataSource currentDataSource) { + return getCountOfFiles(currentDataSource, + "type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType(), + "Unable to get count of files, providing empty results"); + } + + /** + * Get count of unallocated files in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + */ + static Long getCountOfUnallocatedFiles(DataSource currentDataSource) { + return getCountOfFiles(currentDataSource, + "type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + + " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue(), + "Unable to get counts of unallocated files for datasource, providing empty results"); + } + + /** + * Get count of directories in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + */ + static Long getCountOfDirectories(DataSource currentDataSource) { + return getCountOfFiles(currentDataSource, + "'type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + + " AND meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue(), + "Unable to get count of directories for datasource, providing empty results"); + } + + /** + * Get count of slack files in a data source. + * + * @param currentDataSource The data source. + * + * @return The count. + */ + static Long getCountOfSlackFiles(DataSource currentDataSource) { + return getCountOfFiles(currentDataSource, + "type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(), + "Unable to get count of slack files for datasources, providing empty results"); + } + + /** + * An interface for handling a result set and returning a value. + */ + private interface ResultSetHandler { + + T process(ResultSet resultset) throws SQLException; + } + + /** + * Retrieves a result based on the provided query. + * + * @param query The query. + * @param processor The result set handler. + * @param errorMessage The error message to display if there is an error + * retrieving the resultset. + * + * @return The ResultSetHandler value or null if no ResultSet could be + * obtained. + */ + private static T getBaseQueryResult(String query, ResultSetHandler processor, String errorMessage) { + try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query)) { + ResultSet resultSet = dbQuery.getResultSet(); + try { + return processor.process(resultSet); + } catch (SQLException ex) { + logger.log(Level.WARNING, errorMessage, ex); + } + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, errorMessage, ex); + } + return null; + } + + /** + * Gets the size of unallocated files in a particular datasource. + * + * @param currentDataSource The data source. + * + * @return The size or null if the query could not be executed. + */ + static Long getSizeOfUnallocatedFiles(DataSource currentDataSource) { + if (currentDataSource == null) { + return null; + } + + final String valueParam = "value"; + String query = "SELECT SUM(size) AS " + valueParam + + " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() + + " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue() + + " AND name<>''" + + " AND data_source_obj_id=" + currentDataSource.getId(); + + ResultSetHandler handler = (resultSet) -> resultSet.getLong(valueParam); + String errorMessage = "Unable to get size of unallocated files; returning null."; + + return getBaseQueryResult(query, handler, errorMessage); + } + + /** + * Retrieves counts for each artifact type in a data source. + * + * @param selectedDataSource The data source. + * + * @return A mapping of artifact type name to the counts or null if there + * was an error executing the query. + */ + static Map getCountsOfArtifactsByType(DataSource selectedDataSource) { + if (selectedDataSource == null) { + return Collections.emptyMap(); + } + + final String nameParam = "name"; + final String valueParam = "value"; + String query + = "SELECT bbt.display_name AS " + nameParam + ", COUNT(*) AS " + valueParam + + " FROM blackboard_artifacts bba " + + " INNER JOIN blackboard_artifact_types bbt ON bba.artifact_type_id = bbt.artifact_type_id" + + " WHERE bba.data_source_obj_id =" + selectedDataSource.getId() + + " GROUP BY bbt.display_name"; + + ResultSetHandler> handler = (resultSet) -> { + Map toRet = new HashMap<>(); + while (resultSet.next()) { + try { + toRet.put(resultSet.getString(nameParam), resultSet.getLong(valueParam)); + } catch (SQLException ex) { + logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex); + } + } + + return toRet; + }; + + String errorMessage = "Unable to get artifact type counts; returning null."; + + return getBaseQueryResult(query, handler, errorMessage); + } + + /** + * Generates a string which is a concatenation of the value received from + * the result set. + * + * @param query The query. + * @param valueParam The parameter for the value in the result set. + * @param separator The string separator used in concatenation. + * @param errorMessage The error message if the result set could not + * be received. + * @param singleErrorMessage The error message if a single result could not + * be obtained. + * + * @return The concatenated string or null if the query could not be + * executed. + */ + private static String getConcattedStringQuery(String query, String valueParam, String separator, String errorMessage, String singleErrorMessage) { + ResultSetHandler handler = (resultSet) -> { + String toRet = ""; + boolean first = true; + while (resultSet.next()) { + try { + if (first) { + first = false; + } else { + toRet += separator; + } + toRet += resultSet.getString(valueParam); + } catch (SQLException ex) { + logger.log(Level.WARNING, singleErrorMessage, ex); + } + } + + return toRet; + }; + + return getBaseQueryResult(query, handler, errorMessage); + } + + /** + * Generates a concatenated string value based on the text value of a + * particular attribute in a particular artifact for a specific data source. + * + * @param dataSourceId The data source id. + * @param artifactTypeId The artifact type id. + * @param attributeTypeId The attribute type id. + * + * @return The concatenated value or null if the query could not be + * executed. + */ + private static String getConcattedAttrValue(long dataSourceId, int artifactTypeId, int attributeTypeId) { + final String valueParam = "concatted_attribute_value"; + String query = "SELECT attr.value_text AS " + valueParam + + " FROM blackboard_artifacts bba " + + " INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id " + + " WHERE bba.data_source_obj_id = " + dataSourceId + + " AND bba.artifact_type_id = " + artifactTypeId + + " AND attr.attribute_type_id = " + attributeTypeId; + + String errorMessage = "Unable to execute query to retrieve concatted attribute values."; + String singleErrorMessage = "There was an error retrieving one of the results. That result will be omitted from concatted value."; + String separator = ", "; + return getConcattedStringQuery(query, valueParam, separator, errorMessage, singleErrorMessage); + } + + /** + * Retrieves the concatenation of operating system attributes for a + * particular data source. + * + * @param dataSource The data source. + * + * @return The concatenated value or null if the query could not be + * executed. + */ + static String getOperatingSystems(DataSource dataSource) { + if (dataSource == null) { + return null; + } + + return getConcattedAttrValue(dataSource.getId(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_INFO.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID()); + } + + /** + * Retrieves the concatenation of data source usage for a particular data + * source. + * + * @param dataSource The data source. + * + * @return The concatenated value or null if the query could not be + * executed. + */ + static String getDataSourceType(DataSource dataSource) { + if (dataSource == null) { + return null; + } + + return getConcattedAttrValue(dataSource.getId(), + BlackboardArtifact.ARTIFACT_TYPE.TSK_DATA_SOURCE_USAGE.getTypeID(), + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID()); + } + /** * Get a map containing the TSK_DATA_SOURCE_USAGE description attributes * associated with each data source in the current case. @@ -97,51 +381,6 @@ final class DataSourceInfoUtilities { } } - private static Long baseCountOfFiles(DataSource currentDataSource, String additionalWhere, String onError) { - if (currentDataSource != null) { - try { - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - return skCase.countFilesWhere( - "COUNT(*) AS value" - + " FROM tsk_files" - + " WHERE dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() - + " AND name<>''" - + " AND data_source_obj_id=" + currentDataSource.getId() - + " AND " + additionalWhere); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, onError, ex); - //unable to get count of files for the specified mimetypes cell will be displayed as empty - } - } - return null; - } - - static Long getCountOfFiles(DataSource currentDataSource) { - return baseCountOfFiles(currentDataSource, - "type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType(), - "Unable to get count of files, providing empty results"); - } - - static Long getCountOfUnallocatedFiles(DataSource currentDataSource) { - return baseCountOfFiles(currentDataSource, - "type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() - + " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue(), - "Unable to get counts of unallocated files for datasource, providing empty results"); - } - - static Long getCountOfDirectories(DataSource currentDataSource) { - return baseCountOfFiles(currentDataSource, - "'type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() - + " AND meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue(), - "Unable to get count of directories for datasource, providing empty results"); - } - - static Long getCountOfSlackFiles(DataSource currentDataSource) { - return baseCountOfFiles(currentDataSource, - "type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType(), - "Unable to get count of slack files for datasources, providing empty results"); - } - /** * Get a map containing the number of artifacts in each data source in the * current case. @@ -192,39 +431,6 @@ final class DataSourceInfoUtilities { } } - /** - * Get a map containing the names of operating systems joined in a comma - * seperated list to the Data Source they exist on in the current case. No - * item will exist in the map for data sources which do not contain - * TS_OS_INFO artifacts which have a program name. - * - * @return Collection which maps datasource id to a String which is a comma - * seperated list of Operating system names found on the data - * source. - */ - static Map getOperatingSystems() { - Map osDetailMap = new HashMap<>(); - try { - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - ArrayList osInfoArtifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_INFO); - for (BlackboardArtifact osInfo : osInfoArtifacts) { - BlackboardAttribute programName = osInfo.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME)); - if (programName != null) { - String currentOsString = osDetailMap.get(osInfo.getDataSource().getId()); - if (currentOsString == null || currentOsString.isEmpty()) { - currentOsString = programName.getValueString(); - } else { - currentOsString = currentOsString + ", " + programName.getValueString(); - } - osDetailMap.put(osInfo.getDataSource().getId(), currentOsString); - } - } - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Failed to load OS info artifacts.", ex); - } - return osDetailMap; - } - /** * Get the number of files in the case database for the current data source * which have the specified mimetypes. @@ -257,133 +463,6 @@ final class DataSourceInfoUtilities { return null; } - /** - * Get a map containing the number of unallocated files in each data source - * in the current case. - * - * @return Collection which maps datasource id to a count for the number of - * unallocated files in the datasource, will only contain entries - * for datasources which have at least 1 unallocated file - */ - static Map getCountsOfUnallocatedFiles() { - try { - final String countUnallocatedFilesQuery = "data_source_obj_id, COUNT(*) AS value" - + " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() - + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() - + " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue() - + " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS - return getValuesMap(countUnallocatedFilesQuery); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get counts of unallocated files for all datasources, providing empty results", ex); - return Collections.emptyMap(); - } - } - - /** - * Get a map containing the total size of unallocated files in each data - * source in the current case. - * - * @return Collection which maps datasource id to a total size in bytes of - * unallocated files in the datasource, will only contain entries - * for datasources which have at least 1 unallocated file - */ - static Map getSizeOfUnallocatedFiles() { - try { - final String countUnallocatedFilesQuery = "data_source_obj_id, sum(size) AS value" - + " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() - + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() - + " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue() - + " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS - return getValuesMap(countUnallocatedFilesQuery); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get size of unallocated files for all datasources, providing empty results", ex); - return Collections.emptyMap(); - } - } - - /** - * Get a map containing the number of directories in each data source in the - * current case. - * - * @return Collection which maps datasource id to a count for the number of - * directories in the datasource, will only contain entries for - * datasources which have at least 1 directory - */ - static Map getCountsOfDirectories() { - try { - final String countDirectoriesQuery = "data_source_obj_id, COUNT(*) AS value" - + " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() - + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() - + " AND meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue() - + " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS - return getValuesMap(countDirectoriesQuery); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get counts of directories for all datasources, providing empty results", ex); - return Collections.emptyMap(); - } - } - - /** - * Get a map containing the number of slack files in each data source in the - * current case. - * - * @return Collection which maps datasource id to a count for the number of - * slack files in the datasource, will only contain entries for - * datasources which have at least 1 slack file - */ - static Map getCountsOfSlackFiles() { - try { - final String countSlackFilesQuery = "data_source_obj_id, COUNT(*) AS value" - + " FROM tsk_files WHERE type=" + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.getFileType() - + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() - + " AND name<>'' GROUP BY data_source_obj_id"; //NON-NLS - return getValuesMap(countSlackFilesQuery); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get counts of slack files for all datasources, providing empty results", ex); - return Collections.emptyMap(); - } - } - - /** - * Get a map containing maps which map artifact type to the number of times - * it exosts in each data source in the current case. - * - * @return Collection which maps datasource id to maps of artifact display - * name to number of occurences in the datasource, will only contain - * entries for artifacts which have at least one occurence in the - * data source. - */ - static Map> getCountsOfArtifactsByType() { - try { - final String countArtifactsQuery = "blackboard_artifacts.data_source_obj_id, blackboard_artifact_types.display_name AS label, COUNT(*) AS value" - + " FROM blackboard_artifacts, blackboard_artifact_types" - + " WHERE blackboard_artifacts.artifact_type_id = blackboard_artifact_types.artifact_type_id" - + " GROUP BY blackboard_artifacts.data_source_obj_id, blackboard_artifact_types.display_name"; - return getLabeledValuesMap(countArtifactsQuery); - } catch (TskCoreException | NoCurrentCaseException ex) { - logger.log(Level.WARNING, "Unable to get counts of all artifact types for all datasources, providing empty results", ex); - return Collections.emptyMap(); - } - } - - /** - * Helper method to execute a select query with a - * DataSourceLabeledValueCallback. - * - * @param query the portion of the query which should follow the SELECT - * - * @return a map of datasource object IDs to maps of String labels to the - * values associated with them. - * - * @throws TskCoreException - * @throws NoCurrentCaseException - */ - private static Map> getLabeledValuesMap(String query) throws TskCoreException, NoCurrentCaseException { - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - DataSourceLabeledValueCallback callback = new DataSourceLabeledValueCallback(); - skCase.getCaseDbAccessManager().select(query, callback); - return callback.getMapOfLabeledValues(); - } /** * Helper method to execute a select query with a diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java index 0df73ce93f..44a108d0aa 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java @@ -41,18 +41,22 @@ import org.sleuthkit.datamodel.DataSource; }) class DataSourceSummaryCountsPanel extends javax.swing.JPanel { + // Result returned for a data model if no data found. private static final Object[][] EMPTY_PAIRS = new Object[][]{}; + // column headers for mime type table private static final Object[] MIME_TYPE_COLUMN_HEADERS = new Object[]{ Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_type_header(), Bundle.DataSourceSummaryCountsPanel_FilesByMimeTypeTableModel_count_header() }; + // column headers for file by category table private static final Object[] FILE_BY_CATEGORY_COLUMN_HEADERS = new Object[]{ Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_type_header(), Bundle.DataSourceSummaryCountsPanel_FilesByCategoryTableModel_count_header() }; + // column headers for artifact counts table private static final Object[] ARTIFACT_COUNTS_COLUMN_HEADERS = new Object[]{ Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_type_header(), Bundle.DataSourceSummaryCountsPanel_ArtifactCountsTableModel_count_header() @@ -128,7 +132,9 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { /** * Determines the JTable data model for datasource mime types. + * * @param dataSource The DataSource. + * * @return The model to be used with a JTable. */ @Messages({ @@ -154,9 +160,12 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { } /** - * Retrieves the counts of files of a particular mime type for a particular DataSource. + * Retrieves the counts of files of a particular mime type for a particular + * DataSource. + * * @param dataSource The DataSource. - * @param category The mime type category. + * @param category The mime type category. + * * @return The count. */ private static Long getCount(DataSource dataSource, FileTypeUtils.FileTypeCategory category) { @@ -165,7 +174,9 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { /** * Determines the JTable data model for datasource file categories. + * * @param dataSource The DataSource. + * * @return The model to be used with a JTable. */ @Messages({ @@ -176,8 +187,6 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { "DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory" }) private static Object[][] getFileCategoryModel(DataSource selectedDataSource) { - Long dataSourceId = selectedDataSource == null ? null : selectedDataSource.getId(); - // or 0 if not found Long fileCount = zeroIfNull(DataSourceInfoUtilities.getCountOfFiles(selectedDataSource)); Long unallocatedFiles = zeroIfNull(DataSourceInfoUtilities.getCountOfUnallocatedFiles(selectedDataSource)); Long allocatedFiles = zeroIfNull(getAllocatedCount(fileCount, unallocatedFiles)); @@ -195,7 +204,9 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { /** * Returns 0 if value is null. + * * @param origValue The original value. + * * @return The value or 0 if null. */ private static Long zeroIfNull(Long origValue) { @@ -204,8 +215,10 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { /** * Safely gets the allocated files count. - * @param allFilesCount The count of all files. + * + * @param allFilesCount The count of all files. * @param unallocatedFilesCount The count of unallocated files. + * * @return The count of allocated files. */ private static long getAllocatedCount(Long allFilesCount, Long unallocatedFilesCount) { @@ -220,12 +233,13 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { /** * The counts of different artifact types found in a DataSource. + * * @param selectedDataSource The DataSource. + * * @return The JTable data model of counts of artifact types. */ private static Object[][] getArtifactCountsModel(DataSource selectedDataSource) { - Long dataSourceId = selectedDataSource == null ? null : selectedDataSource.getId(); - Map artifactMapping = DataSourceInfoUtilities.getCountsOfArtifactsByType().get(dataSourceId); + Map artifactMapping = DataSourceInfoUtilities.getCountsOfArtifactsByType(selectedDataSource); if (artifactMapping == null) { return EMPTY_PAIRS; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java index 7409c2eda3..21caa2c4b8 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDetailsPanel.java @@ -72,11 +72,10 @@ class DataSourceSummaryDetailsPanel extends javax.swing.JPanel { if (dataSource == null || !Case.isCaseOpen()) { updateDetailsPanelData(null, null, null, null); } else { - long id = dataSource.getId(); updateDetailsPanelData(dataSource, - DataSourceInfoUtilities.getSizeOfUnallocatedFiles().get(id), - DataSourceInfoUtilities.getOperatingSystems().get(id), - DataSourceInfoUtilities.getDataSourceTypes().get(id)); + DataSourceInfoUtilities.getSizeOfUnallocatedFiles(dataSource), + DataSourceInfoUtilities.getOperatingSystems(dataSource), + DataSourceInfoUtilities.getDataSourceType(dataSource)); } } From 4e7ee4142d3c9a98012e174f9ae92d6e9cb89856 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 27 Jul 2020 14:31:12 -0400 Subject: [PATCH 16/78] bug fixes --- .../DataSourceInfoUtilities.java | 16 ++++++++++++---- .../summary/DataSourceSummaryResultViewer.java | 5 ----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index cfa28d5c38..662f3739f0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -108,7 +108,7 @@ final class DataSourceInfoUtilities { */ static Long getCountOfDirectories(DataSource currentDataSource) { return getCountOfFiles(currentDataSource, - "'type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + "type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + " AND meta_type=" + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue(), "Unable to get count of directories for datasource, providing empty results"); } @@ -172,14 +172,23 @@ final class DataSourceInfoUtilities { } final String valueParam = "value"; - String query = "SELECT SUM(size) AS " + valueParam + final String countParam = "count"; + String query = "SELECT SUM(size) AS " + valueParam + ", COUNT(*) AS " + countParam + " FROM tsk_files WHERE type<>" + TskData.TSK_DB_FILES_TYPE_ENUM.VIRTUAL_DIR.getFileType() + " AND dir_type<>" + TskData.TSK_FS_NAME_TYPE_ENUM.VIRT_DIR.getValue() + " AND dir_flags=" + TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC.getValue() + " AND name<>''" + " AND data_source_obj_id=" + currentDataSource.getId(); - ResultSetHandler handler = (resultSet) -> resultSet.getLong(valueParam); + ResultSetHandler handler = (resultSet) -> { + if (resultSet.next()) { + // ensure that there is an unallocated count result that is attached to this data source + long resultCount = resultSet.getLong(valueParam); + return (resultCount > 0) ? resultSet.getLong(valueParam) : null; + } else { + return null; + } + }; String errorMessage = "Unable to get size of unallocated files; returning null."; return getBaseQueryResult(query, handler, errorMessage); @@ -463,7 +472,6 @@ final class DataSourceInfoUtilities { return null; } - /** * Helper method to execute a select query with a * DataSourceSingleValueCallback. diff --git a/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java index c3ca3ca2a2..1b1b3cb127 100644 --- a/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java +++ b/Core/src/org/sleuthkit/autopsy/resultviewers/summary/DataSourceSummaryResultViewer.java @@ -114,11 +114,6 @@ public class DataSourceSummaryResultViewer extends AbstractDataResultViewer { DataSource dataSource = getDataSource(node); - if (dataSource == null) { - LOGGER.log(Level.SEVERE, "No datasource for node found."); - return; - } - this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { summaryPanel.setDataSource(dataSource); From 1b5f43fa2b300e658bac0037eb74637da2ce1b3f Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 27 Jul 2020 14:37:03 -0400 Subject: [PATCH 17/78] removing files to fix directory issue --- .../Bundle.properties-MERGED | 75 ----------------- .../DataSourceSummaryTabbedPane.java | 83 ------------------- 2 files changed, 158 deletions(-) delete mode 100755 Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED delete mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED deleted file mode 100755 index eb7affe9ee..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED +++ /dev/null @@ -1,75 +0,0 @@ -CTL_DataSourceSummaryAction=Data Source Summary -DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count -DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.slack.row=Slack -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type -DataSourceSummaryCountsPanel.FilesByCategoryTableModel.unallocated.row=Unallocated -DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio -DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.count.header=Count -DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents -DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables -DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images -DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.type.header=File Type -DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos -DataSourceSummaryDetailsPanel.getDataSources.error.text=Failed to get the list of datasources for the current case. -DataSourceSummaryDetailsPanel.getDataSources.error.title=Load Failure -DataSourceSummaryDetailsPanel.units.bytes=\ bytes -DataSourceSummaryDetailsPanel.units.gigabytes=\ GB -DataSourceSummaryDetailsPanel.units.kilobytes=\ kB -DataSourceSummaryDetailsPanel.units.megabytes=\ MB -DataSourceSummaryDetailsPanel.units.petabytes=\ PB -DataSourceSummaryDetailsPanel.units.terabytes=\ TB -DataSourceSummaryDialog.closeButton.text=Close -DataSourceSummaryDetailsPanel.displayNameLabel.text=Display Name: -DataSourceSummaryDetailsPanel.originalNameLabel.text=Name: -DataSourceSummaryDetailsPanel.deviceIdLabel.text=Device ID: -DataSourceSummaryDetailsPanel.operatingSystemLabel.text=OS: -DataSourceSummaryDetailsPanel.dataSourceUsageLabel.text=Usage: -DataSourceSummaryDetailsPanel.timeZoneLabel.text=Time Zone: -DataSourceSummaryDetailsPanel.imageTypeLabel.text=Image Type: -DataSourceSummaryDetailsPanel.sizeLabel.text=Size: -DataSourceSummaryDetailsPanel.sectorSizeLabel.text=Sector Size: -DataSourceSummaryDetailsPanel.md5HashLabel.text=MD5: -DataSourceSummaryDetailsPanel.sha1HashLabel.text=SHA1: -DataSourceSummaryDetailsPanel.sha256HashLabel.text=SHA256: -DataSourceSummaryDetailsPanel.filePathsLabel.text=File Paths: -DataSourceSummaryDetailsPanel.displayNameValue.text= -DataSourceSummaryDetailsPanel.originalNameValue.text= -DataSourceSummaryDetailsPanel.deviceIdValue.toolTipText= -DataSourceSummaryDetailsPanel.deviceIdValue.text= -DataSourceSummaryDetailsPanel.dataSourceUsageValue.text= -DataSourceSummaryDetailsPanel.operatingSystemValue.toolTipText= -DataSourceSummaryDetailsPanel.operatingSystemValue.text= -DataSourceSummaryDetailsPanel.timeZoneValue.text= -DataSourceSummaryDetailsPanel.imageTypeValue.toolTipText= -DataSourceSummaryDetailsPanel.imageTypeValue.text= -DataSourceSummaryDetailsPanel.sizeValue.text= -DataSourceSummaryDetailsPanel.sectorSizeValue.text= -DataSourceSummaryDetailsPanel.md5HashValue.toolTipText= -DataSourceSummaryDetailsPanel.md5HashValue.text= -DataSourceSummaryDetailsPanel.sha1HashValue.text= -DataSourceSummaryDetailsPanel.sha256HashValue.text= -DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0= -DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text= -DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text=Acquisition Details: -DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text=Unallocated Space: -DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= -DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type -DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category -DataSourceSummaryCountsPanel.jLabel1.text=Results by Type -DataSourceSummaryDialog.countsTab.title=Counts -DataSourceSummaryDialog.detailsTab.title=Details -DataSourceSummaryDialog.ingestHistoryTab.title=Ingest History -DataSourceSummaryDialog.window.title=Data Sources Summary -DataSourceSummaryNode.column.dataSourceName.header=Data Source Name -DataSourceSummaryNode.column.files.header=Files -DataSourceSummaryNode.column.results.header=Results -DataSourceSummaryNode.column.status.header=Ingest Status -DataSourceSummaryNode.column.tags.header=Tags -DataSourceSummaryNode.column.type.header=Type -DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source -ViewSummaryInformationAction.name.text=View Summary Information diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java deleted file mode 100644 index d435b5e135..0000000000 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2020 Basis Technology Corp. - * Contact: carrier sleuthkit 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.casemodule.datasourcesummary; - -import javax.swing.JTabbedPane; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; -import org.sleuthkit.datamodel.DataSource; - -/** - * A tabbed pane showing the summary of a data source including tabs of: - * DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and - * IngestJobInfoPanel. - */ -public class DataSourceSummaryTabbedPane extends JTabbedPane { - - private final DataSourceSummaryCountsPanel countsPanel; - private final DataSourceSummaryDetailsPanel detailsPanel; - private final IngestJobInfoPanel ingestHistoryPanel; - - private DataSource dataSource = null; - - /** - * Constructs a tabbed pane showing the summary of a data source. - */ - public DataSourceSummaryTabbedPane() { - countsPanel = new DataSourceSummaryCountsPanel(); - detailsPanel = new DataSourceSummaryDetailsPanel(); - ingestHistoryPanel = new IngestJobInfoPanel(); - setTabs(); - } - - /** - * Set tabs to the details panel, counts panel, and ingest history panel. - */ - private void setTabs() { - this.removeAll(); - addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); - addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); - addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); - } - - /** - * The datasource currently used as the model in this panel. - * - * @return The datasource currently being used as the model in this panel. - */ - public DataSource getDataSource() { - return dataSource; - } - - /** - * Sets datasource to visualize in the tabbed panel. - * - * @param dataSource The datasource to use in this panel. - */ - public void setDataSource(DataSource dataSource) { - this.dataSource = dataSource; - - countsPanel.setDataSource(dataSource); - detailsPanel.setDataSource(dataSource); - - // make sure ingest history panel doesn't try to show a data source when null or case is closed - ingestHistoryPanel.setDataSource( - dataSource != null && Case.isCaseOpen() ? dataSource : null); - } -} From 8f967cb2384f56076f62551bab2d75b0569ea08d Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 27 Jul 2020 14:37:49 -0400 Subject: [PATCH 18/78] re-adding files to fix directory issue --- .../Bundle.properties-MERGED | 75 +++++++++++++++++ .../DataSourceSummaryTabbedPane.java | 83 +++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED new file mode 100644 index 0000000000..eb7affe9ee --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED @@ -0,0 +1,75 @@ +CTL_DataSourceSummaryAction=Data Source Summary +DataSourceSummaryCountsPanel.ArtifactCountsTableModel.count.header=Count +DataSourceSummaryCountsPanel.ArtifactCountsTableModel.type.header=Result Type +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.all.row=All +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.allocated.row=Allocated +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.count.header=Count +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.directory.row=Directory +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.slack.row=Slack +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.type.header=File Type +DataSourceSummaryCountsPanel.FilesByCategoryTableModel.unallocated.row=Unallocated +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.audio.row=Audio +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.count.header=Count +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.documents.row=Documents +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.executables.row=Executables +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.images.row=Images +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.type.header=File Type +DataSourceSummaryCountsPanel.FilesByMimeTypeTableModel.videos.row=Videos +DataSourceSummaryDetailsPanel.getDataSources.error.text=Failed to get the list of datasources for the current case. +DataSourceSummaryDetailsPanel.getDataSources.error.title=Load Failure +DataSourceSummaryDetailsPanel.units.bytes=\ bytes +DataSourceSummaryDetailsPanel.units.gigabytes=\ GB +DataSourceSummaryDetailsPanel.units.kilobytes=\ kB +DataSourceSummaryDetailsPanel.units.megabytes=\ MB +DataSourceSummaryDetailsPanel.units.petabytes=\ PB +DataSourceSummaryDetailsPanel.units.terabytes=\ TB +DataSourceSummaryDialog.closeButton.text=Close +DataSourceSummaryDetailsPanel.displayNameLabel.text=Display Name: +DataSourceSummaryDetailsPanel.originalNameLabel.text=Name: +DataSourceSummaryDetailsPanel.deviceIdLabel.text=Device ID: +DataSourceSummaryDetailsPanel.operatingSystemLabel.text=OS: +DataSourceSummaryDetailsPanel.dataSourceUsageLabel.text=Usage: +DataSourceSummaryDetailsPanel.timeZoneLabel.text=Time Zone: +DataSourceSummaryDetailsPanel.imageTypeLabel.text=Image Type: +DataSourceSummaryDetailsPanel.sizeLabel.text=Size: +DataSourceSummaryDetailsPanel.sectorSizeLabel.text=Sector Size: +DataSourceSummaryDetailsPanel.md5HashLabel.text=MD5: +DataSourceSummaryDetailsPanel.sha1HashLabel.text=SHA1: +DataSourceSummaryDetailsPanel.sha256HashLabel.text=SHA256: +DataSourceSummaryDetailsPanel.filePathsLabel.text=File Paths: +DataSourceSummaryDetailsPanel.displayNameValue.text= +DataSourceSummaryDetailsPanel.originalNameValue.text= +DataSourceSummaryDetailsPanel.deviceIdValue.toolTipText= +DataSourceSummaryDetailsPanel.deviceIdValue.text= +DataSourceSummaryDetailsPanel.dataSourceUsageValue.text= +DataSourceSummaryDetailsPanel.operatingSystemValue.toolTipText= +DataSourceSummaryDetailsPanel.operatingSystemValue.text= +DataSourceSummaryDetailsPanel.timeZoneValue.text= +DataSourceSummaryDetailsPanel.imageTypeValue.toolTipText= +DataSourceSummaryDetailsPanel.imageTypeValue.text= +DataSourceSummaryDetailsPanel.sizeValue.text= +DataSourceSummaryDetailsPanel.sectorSizeValue.text= +DataSourceSummaryDetailsPanel.md5HashValue.toolTipText= +DataSourceSummaryDetailsPanel.md5HashValue.text= +DataSourceSummaryDetailsPanel.sha1HashValue.text= +DataSourceSummaryDetailsPanel.sha256HashValue.text= +DataSourceSummaryDetailsPanel.filePathsTable.columnModel.title0= +DataSourceSummaryDetailsPanel.acquisitionDetailsTextArea.text= +DataSourceSummaryDetailsPanel.acquisitionDetailsLabel.text=Acquisition Details: +DataSourceSummaryDetailsPanel.unallocatedSizeLabel.text=Unallocated Space: +DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= +DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type +DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category +DataSourceSummaryCountsPanel.jLabel1.text=Results by Type +DataSourceSummaryDialog.countsTab.title=Counts +DataSourceSummaryDialog.detailsTab.title=Details +DataSourceSummaryDialog.ingestHistoryTab.title=Ingest History +DataSourceSummaryDialog.window.title=Data Sources Summary +DataSourceSummaryNode.column.dataSourceName.header=Data Source Name +DataSourceSummaryNode.column.files.header=Files +DataSourceSummaryNode.column.results.header=Results +DataSourceSummaryNode.column.status.header=Ingest Status +DataSourceSummaryNode.column.tags.header=Tags +DataSourceSummaryNode.column.type.header=Type +DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source +ViewSummaryInformationAction.name.text=View Summary Information diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java new file mode 100644 index 0000000000..d435b5e135 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java @@ -0,0 +1,83 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.casemodule.datasourcesummary; + +import javax.swing.JTabbedPane; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; +import org.sleuthkit.datamodel.DataSource; + +/** + * A tabbed pane showing the summary of a data source including tabs of: + * DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and + * IngestJobInfoPanel. + */ +public class DataSourceSummaryTabbedPane extends JTabbedPane { + + private final DataSourceSummaryCountsPanel countsPanel; + private final DataSourceSummaryDetailsPanel detailsPanel; + private final IngestJobInfoPanel ingestHistoryPanel; + + private DataSource dataSource = null; + + /** + * Constructs a tabbed pane showing the summary of a data source. + */ + public DataSourceSummaryTabbedPane() { + countsPanel = new DataSourceSummaryCountsPanel(); + detailsPanel = new DataSourceSummaryDetailsPanel(); + ingestHistoryPanel = new IngestJobInfoPanel(); + setTabs(); + } + + /** + * Set tabs to the details panel, counts panel, and ingest history panel. + */ + private void setTabs() { + this.removeAll(); + addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); + addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); + addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); + } + + /** + * The datasource currently used as the model in this panel. + * + * @return The datasource currently being used as the model in this panel. + */ + public DataSource getDataSource() { + return dataSource; + } + + /** + * Sets datasource to visualize in the tabbed panel. + * + * @param dataSource The datasource to use in this panel. + */ + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + + countsPanel.setDataSource(dataSource); + detailsPanel.setDataSource(dataSource); + + // make sure ingest history panel doesn't try to show a data source when null or case is closed + ingestHistoryPanel.setDataSource( + dataSource != null && Case.isCaseOpen() ? dataSource : null); + } +} From b248596598b848c7c9e0079f5bfafe97afc1e85f Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 27 Jul 2020 14:42:55 -0400 Subject: [PATCH 19/78] directory fix --- .../Bundle.properties-MERGED | 0 .../{datasourceSummary => datasourcesummary}/Bundle_ja.properties | 0 .../DataSourceSummaryTabbedPane.java | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename Core/src/org/sleuthkit/autopsy/casemodule/{datasourceSummary => datasourcesummary}/Bundle.properties-MERGED (100%) rename Core/src/org/sleuthkit/autopsy/casemodule/{datasourceSummary => datasourcesummary}/Bundle_ja.properties (100%) rename Core/src/org/sleuthkit/autopsy/casemodule/{datasourceSummary => datasourcesummary}/DataSourceSummaryTabbedPane.java (100%) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED similarity index 100% rename from Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle.properties-MERGED rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties similarity index 100% rename from Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/Bundle_ja.properties rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java similarity index 100% rename from Core/src/org/sleuthkit/autopsy/casemodule/datasourceSummary/DataSourceSummaryTabbedPane.java rename to Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java From 3c096cdb2d8143ee22353cd4cd03cf8d626f172e Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 27 Jul 2020 16:05:53 -0400 Subject: [PATCH 20/78] addressing codacy remarks and bug fix --- .../DataSourceSummaryTabbedPane.java | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java index d435b5e135..8ef4d62c5d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java @@ -30,9 +30,13 @@ import org.sleuthkit.datamodel.DataSource; */ public class DataSourceSummaryTabbedPane extends JTabbedPane { + private static final long serialVersionUID = 1L; private final DataSourceSummaryCountsPanel countsPanel; private final DataSourceSummaryDetailsPanel detailsPanel; - private final IngestJobInfoPanel ingestHistoryPanel; + + // ingest panel requires an open case in order to properly initialize. + // So it will be instantiated when a data source is selected. + private IngestJobInfoPanel ingestHistoryPanel = null; private DataSource dataSource = null; @@ -42,18 +46,30 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { public DataSourceSummaryTabbedPane() { countsPanel = new DataSourceSummaryCountsPanel(); detailsPanel = new DataSourceSummaryDetailsPanel(); - ingestHistoryPanel = new IngestJobInfoPanel(); - setTabs(); } /** - * Set tabs to the details panel, counts panel, and ingest history panel. + * Set tabs to the details panel, counts panel, and ingest history panel. If + * no data source or case is closed, no tabs will be shwon. + * + * @param dataSource The data source to display. */ - private void setTabs() { + private void setTabs(DataSource dataSource) { this.removeAll(); - addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); - addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); - addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); + if (dataSource != null && Case.isCaseOpen()) { + addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); + detailsPanel.setDataSource(dataSource); + + addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); + countsPanel.setDataSource(dataSource); + + if (ingestHistoryPanel == null) { + ingestHistoryPanel = new IngestJobInfoPanel(); + } + + addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); + ingestHistoryPanel.setDataSource(dataSource); + } } /** @@ -72,12 +88,6 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { */ public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; - - countsPanel.setDataSource(dataSource); - detailsPanel.setDataSource(dataSource); - - // make sure ingest history panel doesn't try to show a data source when null or case is closed - ingestHistoryPanel.setDataSource( - dataSource != null && Case.isCaseOpen() ? dataSource : null); + setTabs(dataSource); } } From 4abbbd9dcc27b1b25fe290286a801873a9c78cee Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 28 Jul 2020 10:16:00 -0400 Subject: [PATCH 21/78] Initial commit Initial commit of Chrome based browsers --- .../recentactivity/Bundle.properties-MERGED | 20 +- .../autopsy/recentactivity/Chrome.java | 180 ++++++++++-------- 2 files changed, 108 insertions(+), 92 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED index 5ea28d7eec..839ccdd039 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED @@ -5,15 +5,10 @@ ChromeCacheExtract_adding_artifacts_msg=Chrome Cache: Adding %d artifacts for an ChromeCacheExtract_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis. ChromeCacheExtract_loading_files_msg=Chrome Cache: Loading files from %s. ChromeCacheExtractor.moduleName=ChromeCacheExtractor -# {0} - module name -# {1} - row number -# {2} - table length -# {3} - cache path ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3} DataSourceUsage_AndroidMedia=Android Media Card DataSourceUsage_DJU_Drone_DAT=DJI Internal SD Card DataSourceUsage_FlashDrive=Flash Drive -# {0} - OS name DataSourceUsageAnalyzer.customVolume.label=OS Drive ({0}) DataSourceUsageAnalyzer.parentModuleName=Recent Activity Extract.indexError.message=Failed to index artifact for keyword search. @@ -77,7 +72,7 @@ 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\n\The module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web activity (sites visited, stored cookies, book marked 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 activity (sites visited, stored cookies, book marked 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 @@ -148,14 +143,14 @@ Firefox.getDlV24.errMsg.errAnalyzeFile={0}: Error while trying to analyze file:{ Firefox.getDlV24.errMsg.errParsingArtifacts={0}: Error parsing {1} Firefox web download artifacts. Progress_Message_Analyze_Registry=Analyzing Registry Files Progress_Message_Analyze_Usage=Data Sources Usage Analysis -Progress_Message_Chrome_AutoFill=Chrome Auto Fill -Progress_Message_Chrome_Bookmarks=Chrome Bookmarks +Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0} +Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Browser {0} Progress_Message_Chrome_Cache=Chrome Cache -Progress_Message_Chrome_Cookies=Chrome Cookies -Progress_Message_Chrome_Downloads=Chrome Downloads +Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0} +Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0} Progress_Message_Chrome_FormHistory=Chrome Form History -Progress_Message_Chrome_History=Chrome History -Progress_Message_Chrome_Logins=Chrome Logins +Progress_Message_Chrome_History=Chrome History Browser {0} +Progress_Message_Chrome_Logins=Chrome Logins Browser {0} Progress_Message_Edge_Bookmarks=Microsoft Edge Bookmarks Progress_Message_Edge_Cookies=Microsoft Edge Cookies Progress_Message_Edge_History=Microsoft Edge History @@ -209,7 +204,6 @@ Recently_Used_Artifacts_Winrar=Recently opened according to WinRAR MRU Registry_System_Bam=Recently Executed according to Background Activity Moderator (BAM) RegRipperFullNotFound=Full version RegRipper executable not found. RegRipperNotFound=Autopsy RegRipper executable not found. -# {0} - file name SearchEngineURLQueryAnalyzer.init.exception.msg=Unable to find {0}. SearchEngineURLQueryAnalyzer.moduleName.text=Search Engine SearchEngineURLQueryAnalyzer.engineName.none=NONE diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java index 2c7c5dfcb0..f7949bdf78 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java @@ -22,6 +22,7 @@ */ package org.sleuthkit.autopsy.recentactivity; +import com.google.common.collect.ImmutableMap; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonIOException; @@ -84,14 +85,25 @@ class Chrome extends Extract { private Content dataSource; private IngestJobContext context; + private static final Map BROWSERS_MAP = ImmutableMap.builder() + .put("Microsoft Edge", "Microsoft/Edge") +// .put("Yandex Browser", "YandexBrowser") + .put("Opera", "Opera Software") +// .put("SalamWeb", "SalamWeb") +// .put("UC Browser", "UCBrowser") +// .put("Brave", "BraveSoftware") +// .put("Google Chrome", "Chrome") + .build(); + + @Messages({ - "Progress_Message_Chrome_History=Chrome History", - "Progress_Message_Chrome_Bookmarks=Chrome Bookmarks", - "Progress_Message_Chrome_Cookies=Chrome Cookies", - "Progress_Message_Chrome_Downloads=Chrome Downloads", + "Progress_Message_Chrome_History=Chrome History Browser {0}", + "Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Browser {0}", + "Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0}", + "Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0}", "Progress_Message_Chrome_FormHistory=Chrome Form History", - "Progress_Message_Chrome_AutoFill=Chrome Auto Fill", - "Progress_Message_Chrome_Logins=Chrome Logins", + "Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0}", + "Progress_Message_Chrome_Logins=Chrome Logins Browser {0}", "Progress_Message_Chrome_Cache=Chrome Cache", }) @@ -105,55 +117,60 @@ class Chrome extends Extract { this.context = context; dataFound = false; - progressBar.progress(Bundle.Progress_Message_Chrome_History()); - this.getHistory(); - if (context.dataSourceIngestIsCancelled()) { - return; - } - - progressBar.progress(Bundle.Progress_Message_Chrome_Bookmarks()); - this.getBookmark(); - if (context.dataSourceIngestIsCancelled()) { - return; - } - - progressBar.progress(Bundle.Progress_Message_Chrome_Cookies()); - this.getCookie(); - if (context.dataSourceIngestIsCancelled()) { - return; - } - - progressBar.progress(Bundle.Progress_Message_Chrome_Logins()); - this.getLogins(); - if (context.dataSourceIngestIsCancelled()) { - return; - } - - progressBar.progress(Bundle.Progress_Message_Chrome_AutoFill()); - this.getAutofill(); - if (context.dataSourceIngestIsCancelled()) { - return; - } - - progressBar.progress(Bundle.Progress_Message_Chrome_Downloads()); - this.getDownload(); - if (context.dataSourceIngestIsCancelled()) { - return; + for (Map.Entry browser : BROWSERS_MAP.entrySet()) { + String browserName = browser.getKey(); + String browserLocation = browser.getValue(); + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_History", browserName)); + this.getHistory(browser.getKey(), browser.getValue()); + if (context.dataSourceIngestIsCancelled()) { + return; + } + + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Bookmarks", browserName)); + this.getBookmark(browser.getKey(), browser.getValue()); + if (context.dataSourceIngestIsCancelled()) { + return; + } + + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Cookies", browserName)); + this.getCookie(browser.getKey(), browser.getValue()); + if (context.dataSourceIngestIsCancelled()) { + return; + } + + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Logins", browserName)); + this.getLogins(browser.getKey(), browser.getValue()); + if (context.dataSourceIngestIsCancelled()) { + return; + } + + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_AutoFill", browserName)); + this.getAutofill(browser.getKey(), browser.getValue()); + if (context.dataSourceIngestIsCancelled()) { + return; + } + + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Downloads", browserName)); + this.getDownload(browser.getKey(), browser.getValue()); + if (context.dataSourceIngestIsCancelled()) { + return; + } } progressBar.progress(Bundle.Progress_Message_Chrome_Cache()); ChromeCacheExtractor chromeCacheExtractor = new ChromeCacheExtractor(dataSource, context, progressBar); chromeCacheExtractor.processCaches(); + } /** * Query for history databases and add artifacts */ - private void getHistory() { + private void getHistory(String browser, String browserLocation) { FileManager fileManager = currentCase.getServices().getFileManager(); List historyFiles; try { - historyFiles = fileManager.findFiles(dataSource, "History", "Chrome"); //NON-NLS + historyFiles = fileManager.findFiles(dataSource, "%History%", browserLocation); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); @@ -179,10 +196,11 @@ class Chrome extends Extract { dataFound = true; Collection bbartifacts = new ArrayList<>(); int j = 0; - while (j < historyFiles.size()) { - String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + historyFiles.get(j).getName() + j + ".db"; //NON-NLS - final AbstractFile historyFile = historyFiles.get(j++); - if (historyFile.getSize() == 0) { + while (j < allocatedHistoryFiles.size()) { + String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + allocatedHistoryFiles.get(j).getName() + j + ".db"; //NON-NLS + final AbstractFile historyFile = allocatedHistoryFiles.get(j++); + if ((historyFile.getSize() == 0) || (historyFile.getName().toLowerCase().contains("-slack")) + || (historyFile.getName().toLowerCase().contains("cache"))) { continue; } try { @@ -223,8 +241,7 @@ class Chrome extends Extract { RecentActivityExtracterModuleFactory.getModuleName(), ((result.get("title").toString() != null) ? result.get("title").toString() : ""))); //NON-NLS bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); + RecentActivityExtracterModuleFactory.getModuleName(), browser)); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, RecentActivityExtracterModuleFactory.getModuleName(), (NetworkUtils.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS @@ -245,11 +262,11 @@ class Chrome extends Extract { /** * Search for bookmark files and make artifacts. */ - private void getBookmark() { + private void getBookmark(String browser, String browserLocation) { FileManager fileManager = currentCase.getServices().getFileManager(); List bookmarkFiles; try { - bookmarkFiles = fileManager.findFiles(dataSource, "Bookmarks", "Chrome"); //NON-NLS + bookmarkFiles = fileManager.findFiles(dataSource, "%Bookmarks%", browserLocation); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); @@ -268,10 +285,10 @@ class Chrome extends Extract { while (j < bookmarkFiles.size()) { AbstractFile bookmarkFile = bookmarkFiles.get(j++); - if (bookmarkFile.getSize() == 0) { + if ((bookmarkFile.getSize() == 0) || (bookmarkFile.getName().toLowerCase().contains("-slack"))) { continue; } - String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS + String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(bookmarkFile, new File(temps), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -359,8 +376,7 @@ class Chrome extends Extract { bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, RecentActivityExtracterModuleFactory.getModuleName(), (date / 1000000) - Long.valueOf("11644473600"))); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); + RecentActivityExtracterModuleFactory.getModuleName(), browser)); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, RecentActivityExtracterModuleFactory.getModuleName(), domain)); bbart.addAttributes(bbattributes); @@ -381,12 +397,12 @@ class Chrome extends Extract { /** * Queries for cookie files and adds artifacts */ - private void getCookie() { + private void getCookie(String browser, String browserLocation) { FileManager fileManager = currentCase.getServices().getFileManager(); List cookiesFiles; try { - cookiesFiles = fileManager.findFiles(dataSource, "Cookies", "Chrome"); //NON-NLS + cookiesFiles = fileManager.findFiles(dataSource, "%Cookies%", browserLocation); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getCookie.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); @@ -404,10 +420,10 @@ class Chrome extends Extract { int j = 0; while (j < cookiesFiles.size()) { AbstractFile cookiesFile = cookiesFiles.get(j++); - if (cookiesFile.getSize() == 0) { + if ((cookiesFile.getSize() == 0) || (cookiesFile.getName().toLowerCase().contains("-slack"))) { continue; } - String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + cookiesFile.getName() + j + ".db"; //NON-NLS + String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + cookiesFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(cookiesFile, new File(temps), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -447,8 +463,7 @@ class Chrome extends Extract { RecentActivityExtracterModuleFactory.getModuleName(), ((result.get("value").toString() != null) ? result.get("value").toString() : ""))); //NON-NLS bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); + RecentActivityExtracterModuleFactory.getModuleName(), browser)); String domain = result.get("host_key").toString(); //NON-NLS domain = domain.replaceFirst("^\\.+(?!$)", ""); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, @@ -471,11 +486,11 @@ class Chrome extends Extract { /** * Queries for download files and adds artifacts */ - private void getDownload() { + private void getDownload(String browser, String browserLocation) { FileManager fileManager = currentCase.getServices().getFileManager(); List downloadFiles; try { - downloadFiles = fileManager.findFiles(dataSource, "History", "Chrome"); //NON-NLS + downloadFiles = fileManager.findFiles(dataSource, "%History%", "Chrome"); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getDownload.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); @@ -493,10 +508,12 @@ class Chrome extends Extract { int j = 0; while (j < downloadFiles.size()) { AbstractFile downloadFile = downloadFiles.get(j++); - if (downloadFile.getSize() == 0) { + if ((downloadFile.getSize() == 0) || (downloadFile.getName().toLowerCase().contains("-slack")) + || (downloadFile.getName().toLowerCase().contains("cache"))) { continue; } - String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + downloadFile.getName() + j + ".db"; //NON-NLS + + String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + downloadFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(downloadFile, new File(temps), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -552,8 +569,7 @@ class Chrome extends Extract { bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, RecentActivityExtracterModuleFactory.getModuleName(), domain)); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); + RecentActivityExtracterModuleFactory.getModuleName(), browser)); BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes); if (webDownloadArtifact != null) { @@ -561,7 +577,7 @@ class Chrome extends Extract { // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. try { - String normalizedFullPath = FilenameUtils.normalize(fullPath, true); + String normalizedFullPath = FilenameUtils.normalize(fullPath, true); for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(normalizedFullPath), FilenameUtils.getPath(normalizedFullPath))) { BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); associatedObjectArtifact.addAttribute( @@ -588,12 +604,12 @@ class Chrome extends Extract { /** * Gets user logins from Login Data sqlite database */ - private void getLogins() { + private void getLogins(String browser, String browserLocation) { FileManager fileManager = currentCase.getServices().getFileManager(); List loginDataFiles; try { - loginDataFiles = fileManager.findFiles(dataSource, "Login Data", "Chrome"); //NON-NLS + loginDataFiles = fileManager.findFiles(dataSource, "%Login Data%", browserLocation); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); @@ -611,10 +627,10 @@ class Chrome extends Extract { int j = 0; while (j < loginDataFiles.size()) { AbstractFile loginDataFile = loginDataFiles.get(j++); - if (loginDataFile.getSize() == 0) { + if ((loginDataFile.getSize() == 0) || (loginDataFile.getName().toLowerCase().contains("-slack"))) { continue; } - String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + loginDataFile.getName() + j + ".db"; //NON-NLS + String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + loginDataFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(loginDataFile, new File(temps), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -661,6 +677,9 @@ class Chrome extends Extract { RecentActivityExtracterModuleFactory.getModuleName(), ((result.get("signon_realm").toString() != null) ? result.get("signon_realm").toString() : ""))); //NON-NLS + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), browser)); + BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_SERVICE_ACCOUNT, loginDataFile, bbattributes); if (bbart != null) { bbartifacts.add(bbart); @@ -679,12 +698,12 @@ class Chrome extends Extract { * Gets and parses Autofill data from 'Web Data' database, * and creates TSK_WEB_FORM_AUTOFILL, TSK_WEB_FORM_ADDRESS artifacts */ - private void getAutofill() { + private void getAutofill(String browser, String browserLocation) { FileManager fileManager = currentCase.getServices().getFileManager(); List webDataFiles; try { - webDataFiles = fileManager.findFiles(dataSource, "Web Data", "Chrome"); //NON-NLS + webDataFiles = fileManager.findFiles(dataSource, "%Web Data%", browserLocation); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getAutofills.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); @@ -702,10 +721,10 @@ class Chrome extends Extract { int j = 0; while (j < webDataFiles.size()) { AbstractFile webDataFile = webDataFiles.get(j++); - if (webDataFile.getSize() == 0) { + if ((webDataFile.getSize() == 0) || (webDataFile.getName().toLowerCase().contains("-slack"))) { continue; } - String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + webDataFile.getName() + j + ".db"; //NON-NLS + String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + webDataFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(webDataFile, new File(tempFilePath), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -731,7 +750,7 @@ class Chrome extends Extract { boolean isSchemaV8X = Util.checkColumn("date_created", "autofill", tempFilePath); // get form autofill artifacts - bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X)); + bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X, browser)); try { // get form address atifacts getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X); @@ -757,7 +776,7 @@ class Chrome extends Extract { * * @return collection of TSK_WEB_FORM_AUTOFILL artifacts */ - private Collection getFormAutofillArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) { + private Collection getFormAutofillArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X, String browser ) { Collection bbartifacts = new ArrayList<>(); @@ -783,7 +802,7 @@ class Chrome extends Extract { RecentActivityExtracterModuleFactory.getModuleName(), (Integer.valueOf(result.get("count").toString())))); //NON-NLS - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, RecentActivityExtracterModuleFactory.getModuleName(), Long.valueOf(result.get("date_created").toString()))); //NON-NLS @@ -794,6 +813,9 @@ class Chrome extends Extract { Long.valueOf(result.get("date_last_used").toString()))); //NON-NLS } + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), browser)); + // Add an artifact BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, webDataFile, bbattributes); if (bbart != null) { @@ -886,7 +908,7 @@ class Chrome extends Extract { use_count, otherAttributes); } } - + private boolean isChromePreVersion30(String temps) { String query = "PRAGMA table_info(downloads)"; //NON-NLS List> columns = this.dbConnect(temps, query); From 5aa868b0a35b22b485d9337446a6b98568f7f53b Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 28 Jul 2020 11:47:43 -0400 Subject: [PATCH 22/78] Update ExtractRegistry.java Address comments and codacy --- .../recentactivity/ExtractRegistry.java | 85 ++++++++++--------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index 4c0e2080c5..4fcfcf9477 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -961,9 +961,9 @@ class ExtractRegistry extends Extract { while (line != null) { line = line.trim(); - if (line.matches("^bam v.*")) { + if (line.toLowerCase().matches("^bam v.*")) { parseBamKey(regAbstractFile, reader, Bundle.Registry_System_Bam()); - } else if (line.matches("^bthport v..*")) { + } else if (line.toLowerCase().matches("^bthport v..*")) { parseBlueToothDevices(regAbstractFile, reader) ; } line = reader.readLine(); @@ -993,56 +993,25 @@ class ExtractRegistry extends Extract { private void parseBlueToothDevices(AbstractFile regFile, BufferedReader reader) throws FileNotFoundException, IOException { List bbartifacts = new ArrayList<>(); String line = reader.readLine(); - // date format for plugin Tue Jun 23 10:27:54 2020 Z - SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US); - Long bthLastSeen = Long.valueOf(0); - Long bthLastConnected = Long.valueOf(0); while (!line.contains(SECTION_DIVIDER)) { line = reader.readLine(); line = line.trim(); - if (line.contains("Device Unique ID")) { + if (line.toLowerCase().contains("device unique id")) { // Columns are seperated by colons : // Data : Values // Record is 4 lines in length (Device Unique Id, Name, Last Seen, LastConnected - while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Radio Support not found")) { - // Split line on "> " which is the record delimiter between position and file - String deviceTokens[] = line.split(": "); - String deviceUniqueId = deviceTokens[1]; + while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.toLowerCase().contains("radio support not found")) { + Collection attributes = new ArrayList<>(); + addBlueToothAttribute(line, attributes, TSK_DEVICE_ID); line = reader.readLine(); - // Default device name to unknown as a device name may not exist. - String deviceName = "Unknown"; - if (line.contains("Name")) { - String nameTokens[] = line.split(": "); - deviceName = nameTokens[1]; + // Name may not exist, check for it to make sure. + if (line.toLowerCase().contains("name")) { + addBlueToothAttribute(line, attributes, TSK_NAME); line = reader.readLine(); } - String lastSeenTokens[] = line.split(": "); - String lastSeen = lastSeenTokens[1].replace(" Z", ""); + addBlueToothAttribute(line, attributes, TSK_DATETIME); line = reader.readLine(); - String lastConnectedTokens[] = line.split(": "); - String lastConnected = lastConnectedTokens[1].replace(" Z", ""); - try { - Date usedSeenDate = dateFormat.parse(lastSeen); - bthLastSeen = usedSeenDate.getTime()/1000; - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed - // we set the timestamp to 0 and continue on processing - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last Seen attribute.", lastSeen), ex); //NON-NLS - } - try { - Date usedConnectedDate = dateFormat.parse(lastConnected); - bthLastConnected = usedConnectedDate.getTime()/1000; - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed - // we set the timestamp to 0 and continue on processing - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last connected attribute.", lastSeen), ex); //NON-NLS - } - - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_DEVICE_ID, getName(), deviceUniqueId)); - attributes.add(new BlackboardAttribute(TSK_NAME, getName(), deviceName)); - attributes.add(new BlackboardAttribute(TSK_DATETIME, getName(), bthLastSeen)); - attributes.add(new BlackboardAttribute(TSK_DATETIME_ACCESSED, getName(), bthLastConnected)); + addBlueToothAttribute(line, attributes, TSK_DATETIME_ACCESSED); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING, regFile, attributes); if(bba != null) { bbartifacts.add(bba); @@ -1059,6 +1028,38 @@ class ExtractRegistry extends Extract { } } + + private void addBlueToothAttribute(String line, Collection attributes, ATTRIBUTE_TYPE attributeType) { + if(line == null) { + return; + } + + String tokens[] = line.split(": "); + if(tokens.length > 1 && !tokens[1].isEmpty()) { + String tokenString = tokens[1]; + if (attributeType.getDisplayName().toLowerCase().contains("date")) { + String dateString = tokenString.toLowerCase().replace(" z", ""); + // date format for plugin Tue Jun 23 10:27:54 2020 Z + SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US); + Long dateLong = Long.valueOf(0); + try { + Date newDate = dateFormat.parse(dateString); + dateLong = newDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last Seen attribute.", dateString), ex); //NON-NLS + } + attributes.add(new BlackboardAttribute(attributeType, getName(), dateLong)); + + + } else { + attributes.add(new BlackboardAttribute(attributeType, getName(), tokenString)); + } + } +} + + /** * Parse the output of the SAM regripper plugin to get additional Account * information From 122015f3e7d53b8964bd8ee423c7850a70630ba1 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 28 Jul 2020 15:08:15 -0400 Subject: [PATCH 23/78] Update Chrome.java Add bookmarks and history files to ignore. --- .../autopsy/recentactivity/Chrome.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java index f7949bdf78..524511a308 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java @@ -87,12 +87,12 @@ class Chrome extends Extract { private static final Map BROWSERS_MAP = ImmutableMap.builder() .put("Microsoft Edge", "Microsoft/Edge") -// .put("Yandex Browser", "YandexBrowser") + .put("Yandex Browser", "YandexBrowser") .put("Opera", "Opera Software") -// .put("SalamWeb", "SalamWeb") -// .put("UC Browser", "UCBrowser") -// .put("Brave", "BraveSoftware") -// .put("Google Chrome", "Chrome") + .put("SalamWeb", "SalamWeb") + .put("UC Browser", "UCBrowser") + .put("Brave", "BraveSoftware") + .put("Google Chrome", "Chrome") .build(); @@ -200,7 +200,7 @@ class Chrome extends Extract { String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + allocatedHistoryFiles.get(j).getName() + j + ".db"; //NON-NLS final AbstractFile historyFile = allocatedHistoryFiles.get(j++); if ((historyFile.getSize() == 0) || (historyFile.getName().toLowerCase().contains("-slack")) - || (historyFile.getName().toLowerCase().contains("cache"))) { + || (historyFile.getName().toLowerCase().contains("cache")) || (historyFile.getName().toLowerCase().contains("media"))) { continue; } try { @@ -285,7 +285,9 @@ class Chrome extends Extract { while (j < bookmarkFiles.size()) { AbstractFile bookmarkFile = bookmarkFiles.get(j++); - if ((bookmarkFile.getSize() == 0) || (bookmarkFile.getName().toLowerCase().contains("-slack"))) { + if ((bookmarkFile.getSize() == 0) || (bookmarkFile.getName().toLowerCase().contains("-slack")) + || (bookmarkFile.getName().toLowerCase().contains("extras")) || (bookmarkFile.getName().toLowerCase().contains("log")) + || (bookmarkFile.getName().toLowerCase().contains("backup")) || (bookmarkFile.getName().toLowerCase().contains("visualized"))) { continue; } String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS @@ -490,7 +492,7 @@ class Chrome extends Extract { FileManager fileManager = currentCase.getServices().getFileManager(); List downloadFiles; try { - downloadFiles = fileManager.findFiles(dataSource, "%History%", "Chrome"); //NON-NLS + downloadFiles = fileManager.findFiles(dataSource, "%History%", browserLocation); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getDownload.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); From d028db66dd1e687590e20d56acf05932e3e82fcc Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 29 Jul 2020 14:46:52 -0400 Subject: [PATCH 24/78] Modified cvt account cache to be more efficent --- .../autopsy/communications/ContactCache.java | 92 ++++++++++++++----- 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java index 1f34b3cf62..5ee6e6a563 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java +++ b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java @@ -22,9 +22,13 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import java.beans.PropertyChangeListener; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -36,21 +40,22 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; /** - * A singleton cache of the Contact artifacts for accounts. This list of - * TSK_CONTACT artifacts for a given Account retrieved on first access and - * evicted from the ache after 10 minutes. + * A singleton cache of the Contact artifacts for accounts. The map of account + * unique ids to list of contact artifacts is stored in a LoadingCache which + * expires after 10 of non-use. * */ final class ContactCache { private static final Logger logger = Logger.getLogger(ContactCache.class.getName()); - private final LoadingCache> accountMap; - private static ContactCache instance; + + private final LoadingCache>> accountMap; /** * Returns the list of Contacts for the given Account. @@ -63,7 +68,7 @@ final class ContactCache { * @throws ExecutionException */ static synchronized List getContacts(Account account) throws ExecutionException { - return getInstance().accountMap.get(account); + return getInstance().accountMap.get("realMap").get(account.getTypeSpecificID()); } /** @@ -77,18 +82,17 @@ final class ContactCache { * Construct a new instance. */ private ContactCache() { + accountMap = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build( - new CacheLoader>() { + new CacheLoader>>() { @Override - public List load(Account key) { + public Map> load(String key) { try { - List contactList = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); - return findContactForAccount(contactList, key); - - } catch (TskCoreException ex) { - logger.log(Level.WARNING, String.format("Failed to load contacts for account %d", key.getAccountID()), ex); - } - return new ArrayList<>(); + return buildMap(); + } catch (SQLException | TskCoreException ex) { + logger.log(Level.WARNING, "Failed to build account to contact map", ex); + } + return new HashMap<>(); // Return an empty map if there is an exception to avoid NPE and continual trying. } }); @@ -117,23 +121,43 @@ final class ContactCache { return instance; } + + /** + * Builds the map of account IDs to contacts that reference them. + * + * @return A map of account IDs to contact artifacts. + * + * @throws TskCoreException + * @throws SQLException + */ + private Map> buildMap() throws TskCoreException, SQLException { + Map> acctMap = new HashMap<>(); + List accountIdList = getAccountList(); + List contactList = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); + + for(String id: accountIdList) { + acctMap.put(id, findContactForAccount(contactList, id)); + } + + return acctMap; + } /** * Returns a list of TSK_CONTACT artifacts that reference the given account. * * @param allContactList List of existing TSK_CONTACT artifacts. - * @param account Account reference. + * @param account String account unique id. * * @return A list of TSK_CONTACT artifact that reference the given account * or empty list of none were found. * * @throws TskCoreException */ - private List findContactForAccount(List allContactList, Account account) throws TskCoreException { + private List findContactForAccount(List allContactList, String accountId) throws TskCoreException { List accountContacts = new ArrayList<>(); for (BlackboardArtifact contact : allContactList) { - if (isAccountInAttributeList(contact.getAttributes(), account)) { + if (isAccountRelatedToArtifact(contact, accountId)) { accountContacts.add(contact); } } @@ -146,13 +170,14 @@ final class ContactCache { * given account. * * @param contactAttributes List of attributes. - * @param account Account object. + * @param account String account uniqueID. * * @return True if one of the attributes in the list reference the account. */ - private boolean isAccountInAttributeList(List contactAttributes, Account account) { + private boolean isAccountRelatedToArtifact(BlackboardArtifact artifact, String accountId) throws TskCoreException { + List contactAttributes = artifact.getAttributes(); for (BlackboardAttribute attribute : contactAttributes) { - if (isAccountInAttribute(attribute, account)) { + if (isAccountInAttribute(attribute, accountId)) { return true; } } @@ -167,14 +192,35 @@ final class ContactCache { * * @return True if the attribute references the account. */ - private boolean isAccountInAttribute(BlackboardAttribute attribute, Account account) { + private boolean isAccountInAttribute(BlackboardAttribute attribute, String accountId) { String typeName = attribute.getAttributeType().getTypeName(); return (typeName.startsWith("TSK_EMAIL") || typeName.startsWith("TSK_PHONE") || typeName.startsWith("TSK_NAME") || typeName.startsWith("TSK_ID")) - && attribute.getValueString().equals(account.getTypeSpecificID()); + && attribute.getValueString().equals(accountId); + } + + /** + * Gets a list of all accounts unique IDs from the db. + * + * @return A list of unique account ids or empty list if no accounts were found. + * + * @throws TskCoreException + * @throws SQLException + */ + private List getAccountList() throws TskCoreException, SQLException { + List uniqueIdList = new ArrayList<>(); + + CaseDbQuery caseDbQuery = Case.getCurrentCase().getSleuthkitCase().executeQuery("SELECT account_unique_indenifier FROM accounts"); + ResultSet resultSet = caseDbQuery.getResultSet(); + + while(resultSet.next()) { + uniqueIdList.add(resultSet.getString(1)); + } + + return uniqueIdList; } } From b14add6c25764e248a83472007fb3fcc81dae2c7 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 30 Jul 2020 10:54:51 -0400 Subject: [PATCH 25/78] beginnings of top program items --- .../datasourcesummary/Bundle.properties | 1 + .../Bundle.properties-MERGED | 4 + .../datasourcesummary/Bundle_ja.properties | 1 + .../DataSourceInfoUtilities.java | 87 +++++++++++++++---- .../DataSourceSummaryTabbedPane.java | 5 ++ .../autoingest/Bundle.properties-MERGED | 2 - 6 files changed, 82 insertions(+), 18 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties index 2cb4756460..24a312f733 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties @@ -36,3 +36,4 @@ DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category DataSourceSummaryCountsPanel.jLabel1.text=Results by Type +DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED index eb7affe9ee..4682d7d76f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED @@ -72,4 +72,8 @@ DataSourceSummaryNode.column.status.header=Ingest Status DataSourceSummaryNode.column.tags.header=Tags DataSourceSummaryNode.column.type.header=Type DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source +DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run +DataSourceSummaryUserActivityPanel_tab_title=User Activity +DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times +DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program ViewSummaryInformationAction.name.text=View Summary Information diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties index 99fd24c7ef..a4e60c4f36 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties @@ -71,3 +71,4 @@ DataSourceSummaryNode.column.tags.header=\u30bf\u30b0 DataSourceSummaryNode.column.type.header=\u30bf\u30a4\u30d7 DataSourceSummaryNode.viewDataSourceAction.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306b\u79fb\u52d5 ViewSummaryInformationAction.name.text=\u30b5\u30de\u30ea\u30fc\u60c5\u5831\u3092\u8868\u793a +DataSourceSummaryUserActivityPanel.programsRunLabel.text=\u30bf\u30a4\u30d7\u5225\u7d50\u679c diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index 662f3739f0..da991e26fc 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 Basis Technology Corp. + * Copyright 2019 - 2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -29,6 +28,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Set; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -194,6 +194,33 @@ final class DataSourceInfoUtilities { return getBaseQueryResult(query, handler, errorMessage); } + /** + * Generates a result set handler that will return a map of string to long. + * + * @param keyParam The named parameter in the result set representing the + * key. + * @param valueParam The named parameter in the result set representing the + * value. + * + * @return The result set handler to generate the map of string to long. + */ + private static ResultSetHandler> getStringLongResultSetHandler(String keyParam, String valueParam) { + ResultSetHandler> handler = (resultSet) -> { + LinkedHashMap toRet = new LinkedHashMap<>(); + while (resultSet.next()) { + try { + toRet.put(resultSet.getString(keyParam), resultSet.getLong(valueParam)); + } catch (SQLException ex) { + logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex); + } + } + + return toRet; + }; + + return handler; + } + /** * Retrieves counts for each artifact type in a data source. * @@ -216,22 +243,50 @@ final class DataSourceInfoUtilities { + " WHERE bba.data_source_obj_id =" + selectedDataSource.getId() + " GROUP BY bbt.display_name"; - ResultSetHandler> handler = (resultSet) -> { - Map toRet = new HashMap<>(); - while (resultSet.next()) { - try { - toRet.put(resultSet.getString(nameParam), resultSet.getLong(valueParam)); - } catch (SQLException ex) { - logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex); - } - } - - return toRet; - }; - String errorMessage = "Unable to get artifact type counts; returning null."; + return getBaseQueryResult(query, getStringLongResultSetHandler(nameParam, valueParam), errorMessage); + } - return getBaseQueryResult(query, handler, errorMessage); + /** + * Retrieves a list of the top programs used on the data source. Currently + * determines this based off of which prefetch results return the highest + * count. + * + * @param dataSource The data source. + * @param count The number of programs to return. + * + * @return + */ + static Map getTopPrograms(DataSource dataSource, int count) { + if (dataSource == null || count <= 0) { + return Collections.emptyMap(); + } + + String progNameParam = "prog_name"; + String runTimesParam = "run_times"; + String prefetchIdentifier = "Windows Prefetch Extractor"; + + String query = "SELECT program_artifacts." + progNameParam + ", MAX(attr.value_int32) AS " + runTimesParam + "\n" + + "FROM blackboard_artifacts bba\n" + + "INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id\n" + + "INNER JOIN (\n" + + "-- get all the different artifacts coming from prefetch\n" + + " SELECT \n" + + " bba.artifact_id as artifact_id, \n" + + " attr.value_text as " + progNameParam + "\n" + + " FROM blackboard_artifacts bba\n" + + " INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id\n" + + " WHERE bba.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() + "\n" + + " AND attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID() + "\n" + + " AND attr.source = '" + prefetchIdentifier + "'\n" + + ") program_artifacts ON bba.artifact_id = program_artifacts.artifact_id\n" + + "WHERE attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT.getTypeID() + "\n" + + "GROUP BY program_artifacts." + progNameParam + "\n" + + "ORDER BY " + runTimesParam + " DESC\n" + + "LIMIT " + count + " OFFSET 0"; + + String errorMessage = "Unable to get top program counts; returning null."; + return getBaseQueryResult(query, getStringLongResultSetHandler(progNameParam, runTimesParam), errorMessage); } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java index 8ef4d62c5d..78dc38190f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java @@ -33,6 +33,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { private static final long serialVersionUID = 1L; private final DataSourceSummaryCountsPanel countsPanel; private final DataSourceSummaryDetailsPanel detailsPanel; + private final DataSourceSummaryUserActivityPanel userActivityPanel; // ingest panel requires an open case in order to properly initialize. // So it will be instantiated when a data source is selected. @@ -46,6 +47,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { public DataSourceSummaryTabbedPane() { countsPanel = new DataSourceSummaryCountsPanel(); detailsPanel = new DataSourceSummaryDetailsPanel(); + userActivityPanel = new DataSourceSummaryUserActivityPanel(); } /** @@ -63,6 +65,9 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); countsPanel.setDataSource(dataSource); + addTab(Bundle.DataSourceSummaryUserActivityPanel_tab_title(), userActivityPanel); + userActivityPanel.setDataSource(dataSource); + if (ingestHistoryPanel == null) { ingestHistoryPanel = new IngestJobInfoPanel(); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED index 823399e0d0..56a675e256 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED @@ -205,9 +205,7 @@ DeleteCaseTask.progress.parsingManifest=Parsing manifest file {0}... DeleteCaseTask.progress.releasingManifestLock=Releasing lock on the manifest file {0}... DeleteCaseTask.progress.startMessage=Starting deletion... DeleteOrphanCaseNodesAction.progressDisplayName=Cleanup Case Znodes -# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.lblNodeCount.text=Znodes found: {0} -# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.znodesTextArea.countMessage=ZNODES FOUND: {0} DeleteOrphanCaseNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service # {0} - node path From 5c9f1a8c9f609ca12c9508a83eadf73844d61f50 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 30 Jul 2020 10:56:36 -0400 Subject: [PATCH 26/78] beginnings of top program items --- .../DataSourceSummaryUserActivityPanel.form | 80 +++++++++ .../DataSourceSummaryUserActivityPanel.java | 166 ++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form new file mode 100644 index 0000000000..02cda9a4b3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form @@ -0,0 +1,80 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java new file mode 100644 index 0000000000..f556ebc7f3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -0,0 +1,166 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.casemodule.datasourcesummary; + +import java.util.Map; +import javax.swing.JLabel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.DataSource; + +/** + * A panel to display user activity. + */ +@Messages({ + "DataSourceSummaryUserActivityPanel_tab_title=User Activity", + "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program", + "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times" +}) +public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { + // Result returned for a data model if no data found. + private static final Object[][] EMPTY_PAIRS = new Object[][]{}; + private static final int TOP_PROGS_COUNT = 10; + + private static final DefaultTableCellRenderer RIGHT_ALIGNED_RENDERER = new DefaultTableCellRenderer(); + + static { + RIGHT_ALIGNED_RENDERER.setHorizontalAlignment(JLabel.RIGHT); + } + + // column headers for artifact counts table + private static final Object[] TOP_PROGS_COLUMN_HEADERS = new Object[]{ + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header() + }; + + private DataSource dataSource; + + /** + * Creates new form DataSourceUserActivityPanel + */ + public DataSourceSummaryUserActivityPanel() { + initComponents(); + } + + /** + * The datasource currently used as the model in this panel. + * + * @return The datasource currently being used as the model in this panel. + */ + public DataSource getDataSource() { + return dataSource; + } + + /** + * Sets datasource to visualize in the panel. + * + * @param dataSource The datasource to use in this panel. + */ + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + if (dataSource == null || !Case.isCaseOpen()) { + updateTopPrograms(EMPTY_PAIRS); + } else { + updateTopPrograms(getTopProgramsModel(dataSource)); + } + } + + /** + * Updates the Top Programs Table in the gui. + * @param data The data in Object[][] form to be used by the DefaultTableModel. + */ + private void updateTopPrograms(Object[][] data) { + topProgramsTable.setModel(new DefaultTableModel(data, TOP_PROGS_COLUMN_HEADERS)); + topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(230); + topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); + this.repaint(); + } + + /** + * The counts of top programs run. + * + * @param selectedDataSource The DataSource. + * + * @return The JTable data model of counts of program runs. + */ + private static Object[][] getTopProgramsModel(DataSource selectedDataSource) { + Map artifactMapping = DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); + if (artifactMapping == null) { + return EMPTY_PAIRS; + } + + return artifactMapping.entrySet().stream() + .filter((entrySet) -> entrySet != null && entrySet.getKey() != null) + .sorted((a, b) -> -Long.compare(a.getValue(), b.getValue())) + .map((entrySet) -> new Object[]{entrySet.getKey(), entrySet.getValue()}) + .toArray(Object[][]::new); + } + + + /** + * 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") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.JLabel programsRunLabel = new javax.swing.JLabel(); + javax.swing.JScrollPane topProgramsScrollPane = new javax.swing.JScrollPane(); + topProgramsTable = new javax.swing.JTable(); + + setMinimumSize(new java.awt.Dimension(256, 300)); + + org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N + + topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(290, 187)); + + topProgramsTable.setAutoCreateRowSorter(true); + topProgramsScrollPane.setViewportView(topProgramsTable); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(programsRunLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(47, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(programsRunLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTable topProgramsTable; + // End of variables declaration//GEN-END:variables +} From 34ee2bd5c146d7423868e27f63ac6f8deabe1dbd Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 30 Jul 2020 11:15:04 -0400 Subject: [PATCH 27/78] non editable table model --- .../DataSourceSummaryCountsPanel.java | 7 ++-- .../NonEditableTableModel.java | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/NonEditableTableModel.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java index 44a108d0aa..2a1190d30b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryCountsPanel.java @@ -22,7 +22,6 @@ import java.util.Map; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.JLabel; import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableModel; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.FileTypeUtils; @@ -115,15 +114,15 @@ class DataSourceSummaryCountsPanel extends javax.swing.JPanel { * @param artifactDataModel The artifact type data model. */ private void updateCountsTableData(Object[][] mimeTypeDataModel, Object[][] fileCategoryDataModel, Object[][] artifactDataModel) { - fileCountsByMimeTypeTable.setModel(new DefaultTableModel(mimeTypeDataModel, MIME_TYPE_COLUMN_HEADERS)); + fileCountsByMimeTypeTable.setModel(new NonEditableTableModel(mimeTypeDataModel, MIME_TYPE_COLUMN_HEADERS)); fileCountsByMimeTypeTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); fileCountsByMimeTypeTable.getColumnModel().getColumn(0).setPreferredWidth(130); - fileCountsByCategoryTable.setModel(new DefaultTableModel(fileCategoryDataModel, FILE_BY_CATEGORY_COLUMN_HEADERS)); + fileCountsByCategoryTable.setModel(new NonEditableTableModel(fileCategoryDataModel, FILE_BY_CATEGORY_COLUMN_HEADERS)); fileCountsByCategoryTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); fileCountsByCategoryTable.getColumnModel().getColumn(0).setPreferredWidth(130); - artifactCountsTable.setModel(new DefaultTableModel(artifactDataModel, ARTIFACT_COUNTS_COLUMN_HEADERS)); + artifactCountsTable.setModel(new NonEditableTableModel(artifactDataModel, ARTIFACT_COUNTS_COLUMN_HEADERS)); artifactCountsTable.getColumnModel().getColumn(0).setPreferredWidth(230); artifactCountsTable.getColumnModel().getColumn(1).setCellRenderer(rightAlignedRenderer); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/NonEditableTableModel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/NonEditableTableModel.java new file mode 100644 index 0000000000..0550e8f778 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/NonEditableTableModel.java @@ -0,0 +1,36 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.casemodule.datasourcesummary; + +import javax.swing.table.DefaultTableModel; + +/** + * A Table model where cells are not editable. + */ +class NonEditableTableModel extends DefaultTableModel { + + NonEditableTableModel(Object[][] data, Object[] columnNames) { + super(data, columnNames); + } + + @Override + public boolean isCellEditable(int row, int column) { + return false; + } +} From 11fc7e24d2d0b8151111420190eeeeca8ea1074b Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 30 Jul 2020 11:46:44 -0400 Subject: [PATCH 28/78] worked through some table editing issues --- .../DataSourceSummaryUserActivityPanel.form | 3 --- .../DataSourceSummaryUserActivityPanel.java | 5 ++--- .../communications/relationships/Bundle.properties-MERGED | 2 +- .../core.jar/org/netbeans/core/startup/Bundle.properties | 2 +- .../org/netbeans/core/windows/view/ui/Bundle.properties | 2 +- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form index 02cda9a4b3..0ad3ca0349 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form @@ -70,9 +70,6 @@ - - - diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index f556ebc7f3..696e6e904b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -58,6 +58,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { */ public DataSourceSummaryUserActivityPanel() { initComponents(); + topProgramsTable.getTableHeader().setReorderingAllowed(false); } /** @@ -88,7 +89,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { * @param data The data in Object[][] form to be used by the DefaultTableModel. */ private void updateTopPrograms(Object[][] data) { - topProgramsTable.setModel(new DefaultTableModel(data, TOP_PROGS_COLUMN_HEADERS)); + topProgramsTable.setModel(new NonEditableTableModel(data, TOP_PROGS_COLUMN_HEADERS)); topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(230); topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); this.repaint(); @@ -133,8 +134,6 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(290, 187)); - - topProgramsTable.setAutoCreateRowSorter(true); topProgramsScrollPane.setViewportView(topProgramsTable); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED index 6778d8dc53..616408978d 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED @@ -73,5 +73,5 @@ SummaryViewer.referencesLabel.text=Communication References: SummaryViewer.referencesDataLabel.text= SummaryViewer.contactsLabel.text=Book Entries: SummaryViewer.accountCountry.text= -SummaryViewer.fileRefPane.border.title=File Referernce(s) in Current Case +SummaryViewer.fileRefPane.border.title=File References in Current Case SummaryViewer.selectAccountFileRefLabel.text= diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED index 56a675e256..823399e0d0 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED @@ -205,7 +205,9 @@ DeleteCaseTask.progress.parsingManifest=Parsing manifest file {0}... DeleteCaseTask.progress.releasingManifestLock=Releasing lock on the manifest file {0}... DeleteCaseTask.progress.startMessage=Starting deletion... DeleteOrphanCaseNodesAction.progressDisplayName=Cleanup Case Znodes +# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.lblNodeCount.text=Znodes found: {0} +# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.znodesTextArea.countMessage=ZNODES FOUND: {0} DeleteOrphanCaseNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service # {0} - node path diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index 333baeeca2..35138509d8 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Thu, 30 Jul 2020 11:24:48 -0400 +#Wed, 08 Jul 2020 15:15:46 -0400 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 044fad190f..cf36e85b33 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Thu, 30 Jul 2020 11:24:48 -0400 +#Wed, 08 Jul 2020 15:15:46 -0400 CTL_MainWindow_Title=Autopsy 4.16.0 CTL_MainWindow_Title_No_Project=Autopsy 4.16.0 From a0898fddbea1b2b389869a3a4c9ba0beaa28cab5 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 30 Jul 2020 16:00:35 -0400 Subject: [PATCH 31/78] beginnings of photorec extension exclusions --- .../modules/photoreccarver/Bundle.properties | 5 + .../PhotoRecCarverFileIngestModule.java | 52 ++++++- .../PhotoRecCarverIngestJobSettings.java | 113 ++++++++++++-- .../PhotoRecCarverIngestJobSettingsPanel.form | 142 +++++++++++++++++- .../PhotoRecCarverIngestJobSettingsPanel.java | 134 +++++++++++++++-- 5 files changed, 409 insertions(+), 37 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties index 278caa666b..629113188e 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties @@ -19,3 +19,8 @@ PhotoRecIngestModule.error.msg=Error processing {0} with PhotoRec carver. PhotoRecIngestModule.complete.numberOfErrors=Number of Errors while Carving: PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec Settings PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text=Keep corrupted files +PhotoRecCarverIngestJobSettingsPanel.includeExcludeCheckbox.text=Include or Exclude File Extensions +PhotoRecCarverIngestJobSettingsPanel.excludeRadioButton.text=Exclude +PhotoRecCarverIngestJobSettingsPanel.includeExcludeExtensionsLabel.text=Extensions to exclude / include (a comma-separated list) +PhotoRecCarverIngestJobSettingsPanel.extensionListTextfield.text= +PhotoRecCarverIngestJobSettingsPanel.includeRadioButton.text=Include diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java index 4b2f8eddfa..3f89d84801 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java @@ -29,6 +29,7 @@ import java.nio.file.Paths; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -79,6 +80,9 @@ import org.sleuthkit.datamodel.TskData; final class PhotoRecCarverFileIngestModule implements FileIngestModule { static final boolean DEFAULT_CONFIG_KEEP_CORRUPTED_FILES = false; + static final boolean DEFAULT_CONFIG_FILE_OPT_OPTIONS = false; + static final boolean DEFAULT_CONFIG_INCLUDE_ELSE_EXCLUDE = false; + private static final String PHOTOREC_DIRECTORY = "photorec_exec"; //NON-NLS private static final String PHOTOREC_SUBDIRECTORY = "bin"; //NON-NLS @@ -99,9 +103,9 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { private File executableFile; private IngestServices services; private final UNCPathUtilities uncPathUtilities = new UNCPathUtilities(); + private final PhotoRecCarverIngestJobSettings settings; private long jobId; - private final boolean keepCorruptedFiles; private static class IngestJobTotals { private final AtomicLong totalItemsRecovered = new AtomicLong(0); @@ -115,7 +119,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { * @param settings Ingest job settings used to configure the module. */ PhotoRecCarverFileIngestModule(PhotoRecCarverIngestJobSettings settings) { - keepCorruptedFiles = settings.isKeepCorruptedFiles(); + this.settings = settings; } private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) { @@ -177,6 +181,42 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { } } } + + + + private String getPhotorecOptions() { + List toRet = new ArrayList(); + + if (settings.hasFileOptOption()) { + String enable = "enable"; + String disable = "disable"; + + // if we are including file extensions, then we are excluding + // everything else and vice-versa. + String everythingEnable = settings.isIncludeElseExclude() ? + disable : enable; + + toRet.addAll(Arrays.asList("everything", everythingEnable)); + + final String itemEnable = settings.isIncludeElseExclude() ? + enable : disable; + + settings.getIncludeExcludeExtensions().forEach((extension) -> { + toRet.addAll(Arrays.asList(extension, itemEnable)); + }); + } + + if (settings.isKeepCorruptedFiles()) { + toRet.add("keep_corrupted_file"); + } + + if (toRet.size() > 0) { + toRet.add(0, "options"); + } + + toRet.add("search"); + return String.join(",", toRet); + } /** * @inheritDoc @@ -242,11 +282,9 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { outputDirPath.toAbsolutePath().toString() + File.separator + PHOTOREC_RESULTS_BASE, "/cmd", // NON-NLS tempFilePath.toFile().toString()); - if (keepCorruptedFiles) { - processAndSettings.command().add("options,keep_corrupted_file,search"); // NON-NLS - } else { - processAndSettings.command().add("search"); // NON-NLS - } + + String photorecOptions = getPhotorecOptions(); + processAndSettings.command().add(photorecOptions); // Add environment variable to force PhotoRec to run with the same permissions Autopsy uses processAndSettings.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java index 93de74f1d6..d87984fc94 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java @@ -18,7 +18,10 @@ */ package org.sleuthkit.autopsy.modules.photoreccarver; +import java.util.ArrayList; +import java.util.Collections; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; +import java.util.List; /** * Ingest job settings for the PhotoRec Carver module. @@ -26,45 +29,133 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; final class PhotoRecCarverIngestJobSettings implements IngestModuleIngestJobSettings { private static final long serialVersionUID = 1L; - + private boolean keepCorruptedFiles; - + private List includeExcludeExtensions; + private boolean fileOptOption; + private boolean includeElseExclude; + /** * Instantiate the ingest job settings with default values. */ PhotoRecCarverIngestJobSettings() { - this.keepCorruptedFiles = PhotoRecCarverFileIngestModule.DEFAULT_CONFIG_KEEP_CORRUPTED_FILES; + this(PhotoRecCarverFileIngestModule.DEFAULT_CONFIG_KEEP_CORRUPTED_FILES, + PhotoRecCarverFileIngestModule.DEFAULT_CONFIG_FILE_OPT_OPTIONS, + PhotoRecCarverFileIngestModule.DEFAULT_CONFIG_INCLUDE_ELSE_EXCLUDE, + null); } - + /** - * Instantiate the ingest job settings. + * Sets the photo rec settings. * - * @param keepCorruptedFiles Keep corrupted files. + * @param keepCorruptedFiles Whether or not to keep corrupted files. + * @param fileOptOption Whether or not the file opt options + * should be enabled (whether or not to + * include/exclude file extensions). + * @param includeElseExclude If file opt options is enabled, whether + * to include only the extensions listed or + * exclude extensions from output. + * @param includeExcludeExtensions The extensions to include or exclude + * (i.e. jpg, gif) */ - PhotoRecCarverIngestJobSettings(boolean keepCorruptedFiles) { + public PhotoRecCarverIngestJobSettings(boolean keepCorruptedFiles, boolean fileOptOption, boolean includeElseExclude, List includeExcludeExtensions) { this.keepCorruptedFiles = keepCorruptedFiles; + this.fileOptOption = fileOptOption; + this.includeElseExclude = includeElseExclude; + setIncludeExcludeExtensions(includeExcludeExtensions); } @Override public long getVersionNumber() { return serialVersionUID; } - + /** * Are corrupted files being kept? - * + * * @return True if keeping corrupted files; otherwise false. */ boolean isKeepCorruptedFiles() { return keepCorruptedFiles; } - + /** * Keep or disgard corrupted files. - * + * * @param keepCorruptedFiles Are corrupted files being kept? */ void setKeepCorruptedFiles(boolean keepCorruptedFiles) { this.keepCorruptedFiles = keepCorruptedFiles; } + + /** + * Gets extension names (i.e. jpg, exe) to include or exclude from photorec + * carving. + * + * @return The extension names. + */ + public List getIncludeExcludeExtensions() { + return Collections.unmodifiableList(includeExcludeExtensions); + } + + /** + * Sets extension names (i.e. jpg, exe) to include or exclude from photorec + * carving. + * + * @param includeExcludeExtensions The extension names. + */ + public void setIncludeExcludeExtensions(List includeExcludeExtensions) { + this.includeExcludeExtensions = new ArrayList<>(); + if (includeExcludeExtensions != null) { + this.includeExcludeExtensions.addAll(includeExcludeExtensions); + } + } + + /** + * Returns whether or not the fileopt option (and subsequent file extension + * filtering) should be enabled. + * + * @return Whether or not the fileopt option (and subsequent file extension + * filtering) should be enabled. + */ + public boolean hasFileOptOption() { + return fileOptOption; + } + + /** + * Returns whether or not the fileopt option (and subsequent file extension + * filtering) should be enabled. + * + * @param fileOptOption Whether or not the fileopt option (and subsequent + * file extension filtering) should be enabled. + */ + public void setFileOptOption(boolean fileOptOption) { + this.fileOptOption = fileOptOption; + } + + /** + * If the hasFileOptOption is true, this determines whether + * includeExcludeExtensions will be included in the results (excluding all + * others) or includeExcludeExtensions will be excluded from results + * (including all others). + * + * @return Whether to include or exclude includeExcludeExtensions. + */ + public boolean isIncludeElseExclude() { + return includeElseExclude; + } + + /** + * Sets whether or not to include or exclude files. If the hasFileOptOption + * is true, this determines whether includeExcludeExtensions will be + * included in the results (excluding all others) or + * includeExcludeExtensions will be excluded from results (including all + * others). + * + * @param includeElseExclude Whether to include or exclude + * includeExcludeExtensions. + */ + public void setIncludeElseExclude(boolean includeElseExclude) { + this.includeElseExclude = includeElseExclude; + } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form index 9e0283e7f6..26e6f8763c 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form @@ -1,6 +1,15 @@
+ + + + + + + + + @@ -16,16 +25,27 @@ - - + - - - + + + + + + + + + + + + + + + + - - + @@ -36,7 +56,11 @@ - + + + + + @@ -60,6 +84,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java index 2f8d8f10ae..7b70511b6c 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java @@ -18,6 +18,11 @@ */ package org.sleuthkit.autopsy.modules.photoreccarver; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang.StringUtils; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; @@ -26,7 +31,8 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; */ @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel { - + private static final String EXTENSION_LIST_SEPARATOR = ","; + /** * Instantiate the ingest job settings panel. * @@ -43,13 +49,42 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe * @param settings The ingest job settings. */ private void customizeComponents(PhotoRecCarverIngestJobSettings settings) { + includeExcludeCheckbox.setSelected(settings.hasFileOptOption()); + extensionListTextfield.setText(String.join(EXTENSION_LIST_SEPARATOR, settings.getIncludeExcludeExtensions())); + includeRadioButton.setSelected(!settings.isIncludeElseExclude()); + excludeRadioButton.setSelected(!settings.isIncludeElseExclude()); keepCorruptedFilesCheckbox.setSelected(settings.isKeepCorruptedFiles()); + setIncludePanelEnabled(); + } + + private void setIncludePanelEnabled() { + setIncludePanelEnabled(includeExcludeCheckbox.isSelected()); + } + + private void setIncludePanelEnabled(boolean enabled) { + extensionListTextfield.setEnabled(enabled); + includeRadioButton.setEnabled(enabled); + excludeRadioButton.setEnabled(enabled); } @Override public IngestModuleIngestJobSettings getSettings() { - return new PhotoRecCarverIngestJobSettings( - keepCorruptedFilesCheckbox.isSelected()); + return new PhotoRecCarverIngestJobSettings( + keepCorruptedFilesCheckbox.isSelected(), + includeExcludeCheckbox.isSelected(), + includeRadioButton.isSelected(), + getExtensions(extensionListTextfield.getText())); + } + + private List getExtensions(String combinedList) { + if (StringUtils.isBlank(combinedList)) { + return Collections.emptyList(); + } + + return Stream.of(combinedList.split(EXTENSION_LIST_SEPARATOR)) + .map(ext -> ext.trim().replaceAll(EXTENSION_LIST_SEPARATOR, "")) + .filter(ext -> StringUtils.isNotBlank(ext)) + .collect(Collectors.toList()); } /** @@ -61,26 +96,91 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe // //GEN-BEGIN:initComponents private void initComponents() { + includeExcludeButtonGroup = new javax.swing.ButtonGroup(); keepCorruptedFilesCheckbox = new javax.swing.JCheckBox(); - detectionSettingsLabel = new javax.swing.JLabel(); + javax.swing.JLabel detectionSettingsLabel = new javax.swing.JLabel(); + includeExcludeCheckbox = new javax.swing.JCheckBox(); + javax.swing.JPanel includeExcludeParentPanel = new javax.swing.JPanel(); + includeRadioButton = new javax.swing.JRadioButton(); + excludeRadioButton = new javax.swing.JRadioButton(); + javax.swing.JLabel includeExcludeExtensionsLabel = new javax.swing.JLabel(); + extensionListTextfield = new javax.swing.JTextField(); + + setPreferredSize(null); org.openide.awt.Mnemonics.setLocalizedText(keepCorruptedFilesCheckbox, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text")); // NOI18N detectionSettingsLabel.setFont(detectionSettingsLabel.getFont().deriveFont(detectionSettingsLabel.getFont().getStyle() | java.awt.Font.BOLD)); org.openide.awt.Mnemonics.setLocalizedText(detectionSettingsLabel, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(includeExcludeCheckbox, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.includeExcludeCheckbox.text")); // NOI18N + includeExcludeCheckbox.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + includeExcludeCheckboxActionPerformed(evt); + } + }); + + includeExcludeParentPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder()); + + includeExcludeButtonGroup.add(includeRadioButton); + includeRadioButton.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(includeRadioButton, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.includeRadioButton.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(excludeRadioButton, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.excludeRadioButton.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(includeExcludeExtensionsLabel, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.includeExcludeExtensionsLabel.text")); // NOI18N + + extensionListTextfield.setText(org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.extensionListTextfield.text")); // NOI18N + + javax.swing.GroupLayout includeExcludeParentPanelLayout = new javax.swing.GroupLayout(includeExcludeParentPanel); + includeExcludeParentPanel.setLayout(includeExcludeParentPanelLayout); + includeExcludeParentPanelLayout.setHorizontalGroup( + includeExcludeParentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(includeExcludeParentPanelLayout.createSequentialGroup() + .addContainerGap() + .addGroup(includeExcludeParentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(includeExcludeParentPanelLayout.createSequentialGroup() + .addGroup(includeExcludeParentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(includeRadioButton) + .addComponent(excludeRadioButton) + .addComponent(includeExcludeExtensionsLabel)) + .addGap(0, 0, Short.MAX_VALUE)) + .addComponent(extensionListTextfield, javax.swing.GroupLayout.Alignment.TRAILING)) + .addContainerGap()) + ); + includeExcludeParentPanelLayout.setVerticalGroup( + includeExcludeParentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(includeExcludeParentPanelLayout.createSequentialGroup() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(includeRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(excludeRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(includeExcludeExtensionsLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(extensionListTextfield, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) + ); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addGap(10, 10, 10) - .addComponent(keepCorruptedFilesCheckbox)) - .addComponent(detectionSettingsLabel)) - .addContainerGap(159, Short.MAX_VALUE)) + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(detectionSettingsLabel) + .addGroup(layout.createSequentialGroup() + .addGap(10, 10, 10) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(includeExcludeCheckbox) + .addComponent(keepCorruptedFilesCheckbox))))) + .addGroup(layout.createSequentialGroup() + .addGap(35, 35, 35) + .addComponent(includeExcludeParentPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap(190, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -89,12 +189,24 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe .addComponent(detectionSettingsLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(keepCorruptedFilesCheckbox) - .addContainerGap(145, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(includeExcludeCheckbox) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(includeExcludeParentPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(26, Short.MAX_VALUE)) ); }// //GEN-END:initComponents + private void includeExcludeCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_includeExcludeCheckboxActionPerformed + setIncludePanelEnabled(); + }//GEN-LAST:event_includeExcludeCheckboxActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JLabel detectionSettingsLabel; + private javax.swing.JRadioButton excludeRadioButton; + private javax.swing.JTextField extensionListTextfield; + private javax.swing.ButtonGroup includeExcludeButtonGroup; + private javax.swing.JCheckBox includeExcludeCheckbox; + private javax.swing.JRadioButton includeRadioButton; private javax.swing.JCheckBox keepCorruptedFilesCheckbox; // End of variables declaration//GEN-END:variables } From f574233895e9d4648b3199349b33168d585a14b7 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 31 Jul 2020 08:19:00 -0400 Subject: [PATCH 32/78] some fixes --- .../modules/photoreccarver/Bundle.properties | 2 +- .../photoreccarver/Bundle.properties-MERGED | 5 + .../PhotoRecCarverFileIngestModule.java | 106 +++++++++--------- .../PhotoRecCarverIngestJobSettings.java | 4 +- .../PhotoRecCarverIngestJobSettingsPanel.form | 20 ++-- .../PhotoRecCarverIngestJobSettingsPanel.java | 17 +-- 6 files changed, 79 insertions(+), 75 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties index 629113188e..a700ac239b 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties @@ -21,6 +21,6 @@ PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec Settin PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text=Keep corrupted files PhotoRecCarverIngestJobSettingsPanel.includeExcludeCheckbox.text=Include or Exclude File Extensions PhotoRecCarverIngestJobSettingsPanel.excludeRadioButton.text=Exclude -PhotoRecCarverIngestJobSettingsPanel.includeExcludeExtensionsLabel.text=Extensions to exclude / include (a comma-separated list) PhotoRecCarverIngestJobSettingsPanel.extensionListTextfield.text= PhotoRecCarverIngestJobSettingsPanel.includeRadioButton.text=Include +PhotoRecCarverIngestJobSettingsPanel.extensionListLabel.text=Extensions to exclude / include (a comma-separated list) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED index 87dacfc16c..eb14e0af89 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED @@ -26,5 +26,10 @@ PhotoRecIngestModule.error.msg=Error processing {0} with PhotoRec carver. PhotoRecIngestModule.complete.numberOfErrors=Number of Errors while Carving: PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec Settings PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text=Keep corrupted files +PhotoRecCarverIngestJobSettingsPanel.includeExcludeCheckbox.text=Include or Exclude File Extensions +PhotoRecCarverIngestJobSettingsPanel.excludeRadioButton.text=Exclude +PhotoRecCarverIngestJobSettingsPanel.extensionListTextfield.text= +PhotoRecCarverIngestJobSettingsPanel.includeRadioButton.text=Include +PhotoRecCarverIngestJobSettingsPanel.extensionListLabel.text=Extensions to exclude / include (a comma-separated list) unallocatedSpaceProcessingSettingsError.message=The selected file ingest filter ignores unallocated space. This module carves unallocated space. Please choose a filter which does not ignore unallocated space or disable this module. unsupportedOS.message=PhotoRec module is supported on Windows platforms only. diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java index 3f89d84801..abeb9199ba 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java @@ -82,8 +82,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { static final boolean DEFAULT_CONFIG_KEEP_CORRUPTED_FILES = false; static final boolean DEFAULT_CONFIG_FILE_OPT_OPTIONS = false; static final boolean DEFAULT_CONFIG_INCLUDE_ELSE_EXCLUDE = false; - - + private static final String PHOTOREC_DIRECTORY = "photorec_exec"; //NON-NLS private static final String PHOTOREC_SUBDIRECTORY = "bin"; //NON-NLS private static final String PHOTOREC_EXECUTABLE = "photorec_win.exe"; //NON-NLS @@ -103,23 +102,65 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { private File executableFile; private IngestServices services; private final UNCPathUtilities uncPathUtilities = new UNCPathUtilities(); - private final PhotoRecCarverIngestJobSettings settings; + private final String optionsString; private long jobId; - private static class IngestJobTotals { + private final AtomicLong totalItemsRecovered = new AtomicLong(0); private final AtomicLong totalItemsWithErrors = new AtomicLong(0); private final AtomicLong totalWritetime = new AtomicLong(0); private final AtomicLong totalParsetime = new AtomicLong(0); } + /** * Create a PhotoRec Carver ingest module instance. - * + * * @param settings Ingest job settings used to configure the module. */ PhotoRecCarverFileIngestModule(PhotoRecCarverIngestJobSettings settings) { - this.settings = settings; + this.optionsString = getPhotorecOptions(settings); + } + + /** + * Creates a photorec command line options string based on the settings. + * + * @param settings The settings. + * + * @return The options string to be provided to Photorec on the command + * line. + */ + private String getPhotorecOptions(PhotoRecCarverIngestJobSettings settings) { + List toRet = new ArrayList(); + + if (settings.isKeepCorruptedFiles()) { + toRet.addAll(Arrays.asList("options", "keep_corrupted_file")); + } + + if (settings.hasFileOptOption()) { + // add the file opt menu item + toRet.add("fileopt"); + + String enable = "enable"; + String disable = "disable"; + + // if we are including file extensions, then we are excluding + // everything else and vice-versa. + String everythingEnable = settings.isIncludeElseExclude() + ? disable : enable; + + toRet.addAll(Arrays.asList("everything", everythingEnable)); + + final String itemEnable = settings.isIncludeElseExclude() + ? enable : disable; + + settings.getIncludeExcludeExtensions().forEach((extension) -> { + toRet.addAll(Arrays.asList(extension, itemEnable)); + }); + } + + toRet.add("search"); + return String.join(",", toRet); } private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) { @@ -156,7 +197,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { this.rootOutputDirPath = createModuleOutputDirectoryForCase(); //Set photorec executable directory based on operating system. - executableFile = locateExecutable(); + executableFile = locateExecutable(); if (PhotoRecCarverFileIngestModule.refCounter.incrementAndGet(this.jobId) == 1) { try { @@ -181,42 +222,6 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { } } } - - - - private String getPhotorecOptions() { - List toRet = new ArrayList(); - - if (settings.hasFileOptOption()) { - String enable = "enable"; - String disable = "disable"; - - // if we are including file extensions, then we are excluding - // everything else and vice-versa. - String everythingEnable = settings.isIncludeElseExclude() ? - disable : enable; - - toRet.addAll(Arrays.asList("everything", everythingEnable)); - - final String itemEnable = settings.isIncludeElseExclude() ? - enable : disable; - - settings.getIncludeExcludeExtensions().forEach((extension) -> { - toRet.addAll(Arrays.asList(extension, itemEnable)); - }); - } - - if (settings.isKeepCorruptedFiles()) { - toRet.add("keep_corrupted_file"); - } - - if (toRet.size() > 0) { - toRet.add(0, "options"); - } - - toRet.add("search"); - return String.join(",", toRet); - } /** * @inheritDoc @@ -282,10 +287,9 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { outputDirPath.toAbsolutePath().toString() + File.separator + PHOTOREC_RESULTS_BASE, "/cmd", // NON-NLS tempFilePath.toFile().toString()); - - String photorecOptions = getPhotorecOptions(); - processAndSettings.command().add(photorecOptions); - + + processAndSettings.command().add(this.optionsString); + // Add environment variable to force PhotoRec to run with the same permissions Autopsy uses processAndSettings.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS processAndSettings.redirectErrorStream(true); @@ -493,7 +497,6 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { return path; } - /** * Finds and returns the path to the executable, if able. * @@ -515,9 +518,9 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { File usrLocalBin = new File("/usr/local/bin/photorec"); if (usrBin.canExecute() && usrBin.exists() && !usrBin.isDirectory()) { photorec_linux_directory = "/usr/bin"; - }else if(usrLocalBin.canExecute() && usrLocalBin.exists() && !usrLocalBin.isDirectory()){ + } else if (usrLocalBin.canExecute() && usrLocalBin.exists() && !usrLocalBin.isDirectory()) { photorec_linux_directory = "/usr/local/bin"; - }else{ + } else { throw new IngestModule.IngestModuleException("Photorec not found"); } execName = Paths.get(photorec_linux_directory, PHOTOREC_LINUX_EXECUTABLE); @@ -527,8 +530,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { if (null == exeFile) { throw new IngestModule.IngestModuleException(Bundle.missingExecutable_message()); } - - + if (!exeFile.canExecute()) { throw new IngestModule.IngestModuleException(Bundle.cannotRunExecutable_message()); } diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java index d87984fc94..6ef3035be3 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java @@ -95,7 +95,9 @@ final class PhotoRecCarverIngestJobSettings implements IngestModuleIngestJobSett * @return The extension names. */ public List getIncludeExcludeExtensions() { - return Collections.unmodifiableList(includeExcludeExtensions); + return includeExcludeExtensions == null ? + Collections.emptyList() : + Collections.unmodifiableList(includeExcludeExtensions); } /** diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form index 26e6f8763c..4ce6bc15e8 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form @@ -5,11 +5,6 @@ - - - - - @@ -122,7 +117,7 @@ - + @@ -140,7 +135,7 @@ - + @@ -162,21 +157,20 @@ + + + - + - + - - - - diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java index 7b70511b6c..137e5195e6 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2018 Basis Technology Corp. + * Copyright 2018-2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -62,9 +62,10 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe } private void setIncludePanelEnabled(boolean enabled) { - extensionListTextfield.setEnabled(enabled); includeRadioButton.setEnabled(enabled); excludeRadioButton.setEnabled(enabled); + extensionListLabel.setEnabled(enabled); + extensionListTextfield.setEnabled(enabled); } @Override @@ -103,11 +104,9 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe javax.swing.JPanel includeExcludeParentPanel = new javax.swing.JPanel(); includeRadioButton = new javax.swing.JRadioButton(); excludeRadioButton = new javax.swing.JRadioButton(); - javax.swing.JLabel includeExcludeExtensionsLabel = new javax.swing.JLabel(); + extensionListLabel = new javax.swing.JLabel(); extensionListTextfield = new javax.swing.JTextField(); - setPreferredSize(null); - org.openide.awt.Mnemonics.setLocalizedText(keepCorruptedFilesCheckbox, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text")); // NOI18N detectionSettingsLabel.setFont(detectionSettingsLabel.getFont().deriveFont(detectionSettingsLabel.getFont().getStyle() | java.awt.Font.BOLD)); @@ -126,9 +125,10 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe includeRadioButton.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(includeRadioButton, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.includeRadioButton.text")); // NOI18N + includeExcludeButtonGroup.add(excludeRadioButton); org.openide.awt.Mnemonics.setLocalizedText(excludeRadioButton, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.excludeRadioButton.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(includeExcludeExtensionsLabel, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.includeExcludeExtensionsLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(extensionListLabel, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.extensionListLabel.text")); // NOI18N extensionListTextfield.setText(org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.extensionListTextfield.text")); // NOI18N @@ -143,7 +143,7 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe .addGroup(includeExcludeParentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(includeRadioButton) .addComponent(excludeRadioButton) - .addComponent(includeExcludeExtensionsLabel)) + .addComponent(extensionListLabel)) .addGap(0, 0, Short.MAX_VALUE)) .addComponent(extensionListTextfield, javax.swing.GroupLayout.Alignment.TRAILING)) .addContainerGap()) @@ -156,7 +156,7 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(excludeRadioButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(includeExcludeExtensionsLabel) + .addComponent(extensionListLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(extensionListTextfield, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) @@ -203,6 +203,7 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JRadioButton excludeRadioButton; + private javax.swing.JLabel extensionListLabel; private javax.swing.JTextField extensionListTextfield; private javax.swing.ButtonGroup includeExcludeButtonGroup; private javax.swing.JCheckBox includeExcludeCheckbox; From dcc134939ec1c65ccb7383c843577248d344ccb6 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Fri, 31 Jul 2020 16:26:20 -0400 Subject: [PATCH 33/78] tab updates and case updates in IngestJobInfoPanel --- .../casemodule/IngestJobInfoPanel.java | 26 ++++++++++++++++--- .../DataSourceSummaryTabbedPane.java | 15 +++++++---- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java index b1e6d83482..6381acd32d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java @@ -31,7 +31,9 @@ import javax.swing.JOptionPane; import javax.swing.event.ListSelectionEvent; import javax.swing.table.AbstractTableModel; import org.openide.util.NbBundle.Messages; +import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.IngestJobInfo; import org.sleuthkit.datamodel.IngestModuleInfo; @@ -47,6 +49,8 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(IngestJobInfoPanel.class.getName()); private static final Set INGEST_JOB_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestJobEvent.STARTED, IngestManager.IngestJobEvent.CANCELLED, IngestManager.IngestJobEvent.COMPLETED); + private static final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE); + private List ingestJobs; private final List ingestJobsForSelectedDataSource = new ArrayList<>(); private IngestJobTableModel ingestJobTableModel = new IngestJobTableModel(); @@ -79,6 +83,16 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { refresh(); } }); + + Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, (PropertyChangeEvent evt) -> { + if (!(evt instanceof AutopsyEvent) || (((AutopsyEvent) evt).getSourceType() != AutopsyEvent.SourceType.LOCAL)) { + return; + } + + if (CURRENT_CASE == Case.Events.valueOf(evt.getPropertyName())) { + refresh(); + } + }); } /** @@ -110,9 +124,15 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { */ private void refresh() { try { - SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); - this.ingestJobs = skCase.getIngestJobs(); - setDataSource(selectedDataSource); + if (Case.isCaseOpen()) { + SleuthkitCase skCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + this.ingestJobs = skCase.getIngestJobs(); + setDataSource(selectedDataSource); + } else { + this.ingestJobs = new ArrayList<>(); + setDataSource(null); + } + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Failed to load ingest jobs.", ex); JOptionPane.showMessageDialog(this, Bundle.IngestJobInfoPanel_loadIngestJob_error_text(), Bundle.IngestJobInfoPanel_loadIngestJob_error_title(), JOptionPane.ERROR_MESSAGE); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java index 8ef4d62c5d..6375bffd28 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java @@ -31,6 +31,8 @@ import org.sleuthkit.datamodel.DataSource; public class DataSourceSummaryTabbedPane extends JTabbedPane { private static final long serialVersionUID = 1L; + private static final int TAB_COUNT = 3; + private final DataSourceSummaryCountsPanel countsPanel; private final DataSourceSummaryDetailsPanel detailsPanel; @@ -55,20 +57,23 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { * @param dataSource The data source to display. */ private void setTabs(DataSource dataSource) { - this.removeAll(); if (dataSource != null && Case.isCaseOpen()) { - addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); detailsPanel.setDataSource(dataSource); - - addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); countsPanel.setDataSource(dataSource); if (ingestHistoryPanel == null) { ingestHistoryPanel = new IngestJobInfoPanel(); } - addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); ingestHistoryPanel.setDataSource(dataSource); + + // initialize tabs if they have not been initialized properly. + if (getTabCount() != TAB_COUNT) { + removeAll(); + addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); + addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); + addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); + } } } From ef1a5840dda79b12522362d20bb9cbbb8f0f7756 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 3 Aug 2020 08:49:58 -0400 Subject: [PATCH 34/78] fix for ingest job info panel and tabs --- .../DataSourceSummaryTabbedPane.java | 44 +++++-------------- .../relationships/Bundle.properties-MERGED | 2 +- .../autoingest/Bundle.properties-MERGED | 2 - .../netbeans/core/startup/Bundle.properties | 2 +- .../core/windows/view/ui/Bundle.properties | 2 +- 5 files changed, 13 insertions(+), 39 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java index 6375bffd28..e1d15a6b0d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import javax.swing.JTabbedPane; -import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; import org.sleuthkit.datamodel.DataSource; @@ -31,14 +30,10 @@ import org.sleuthkit.datamodel.DataSource; public class DataSourceSummaryTabbedPane extends JTabbedPane { private static final long serialVersionUID = 1L; - private static final int TAB_COUNT = 3; private final DataSourceSummaryCountsPanel countsPanel; private final DataSourceSummaryDetailsPanel detailsPanel; - - // ingest panel requires an open case in order to properly initialize. - // So it will be instantiated when a data source is selected. - private IngestJobInfoPanel ingestHistoryPanel = null; + private final IngestJobInfoPanel ingestHistoryPanel; private DataSource dataSource = null; @@ -48,33 +43,11 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { public DataSourceSummaryTabbedPane() { countsPanel = new DataSourceSummaryCountsPanel(); detailsPanel = new DataSourceSummaryDetailsPanel(); - } - - /** - * Set tabs to the details panel, counts panel, and ingest history panel. If - * no data source or case is closed, no tabs will be shwon. - * - * @param dataSource The data source to display. - */ - private void setTabs(DataSource dataSource) { - if (dataSource != null && Case.isCaseOpen()) { - detailsPanel.setDataSource(dataSource); - countsPanel.setDataSource(dataSource); - - if (ingestHistoryPanel == null) { - ingestHistoryPanel = new IngestJobInfoPanel(); - } - - ingestHistoryPanel.setDataSource(dataSource); - - // initialize tabs if they have not been initialized properly. - if (getTabCount() != TAB_COUNT) { - removeAll(); - addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); - addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); - addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); - } - } + ingestHistoryPanel = new IngestJobInfoPanel(); + + addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); + addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); + addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); } /** @@ -93,6 +66,9 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { */ public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; - setTabs(dataSource); + + detailsPanel.setDataSource(dataSource); + countsPanel.setDataSource(dataSource); + ingestHistoryPanel.setDataSource(dataSource); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED index 6778d8dc53..616408978d 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED @@ -73,5 +73,5 @@ SummaryViewer.referencesLabel.text=Communication References: SummaryViewer.referencesDataLabel.text= SummaryViewer.contactsLabel.text=Book Entries: SummaryViewer.accountCountry.text= -SummaryViewer.fileRefPane.border.title=File Referernce(s) in Current Case +SummaryViewer.fileRefPane.border.title=File References in Current Case SummaryViewer.selectAccountFileRefLabel.text= diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED index 56a675e256..823399e0d0 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED @@ -205,7 +205,9 @@ DeleteCaseTask.progress.parsingManifest=Parsing manifest file {0}... DeleteCaseTask.progress.releasingManifestLock=Releasing lock on the manifest file {0}... DeleteCaseTask.progress.startMessage=Starting deletion... DeleteOrphanCaseNodesAction.progressDisplayName=Cleanup Case Znodes +# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.lblNodeCount.text=Znodes found: {0} +# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.znodesTextArea.countMessage=ZNODES FOUND: {0} DeleteOrphanCaseNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service # {0} - node path diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index c2167700fd..35138509d8 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Mon, 03 Aug 2020 08:23:57 -0400 +#Wed, 08 Jul 2020 15:15:46 -0400 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 9d11d2bf7a..cf36e85b33 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Mon, 03 Aug 2020 08:23:57 -0400 +#Wed, 08 Jul 2020 15:15:46 -0400 CTL_MainWindow_Title=Autopsy 4.16.0 CTL_MainWindow_Title_No_Project=Autopsy 4.16.0 From 4ae8405c89ddad762cd38ad03944815ff4734979 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Mon, 3 Aug 2020 10:28:32 -0400 Subject: [PATCH 36/78] updated look and feel with url --- .../modules/photoreccarver/Bundle.properties | 11 +- .../photoreccarver/Bundle.properties-MERGED | 11 +- .../PhotoRecCarverIngestJobSettingsPanel.form | 78 +++++----- .../PhotoRecCarverIngestJobSettingsPanel.java | 139 +++++++++++++----- 4 files changed, 159 insertions(+), 80 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties index a700ac239b..39cc41e1f4 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties @@ -19,8 +19,11 @@ PhotoRecIngestModule.error.msg=Error processing {0} with PhotoRec carver. PhotoRecIngestModule.complete.numberOfErrors=Number of Errors while Carving: PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec Settings PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text=Keep corrupted files -PhotoRecCarverIngestJobSettingsPanel.includeExcludeCheckbox.text=Include or Exclude File Extensions -PhotoRecCarverIngestJobSettingsPanel.excludeRadioButton.text=Exclude +PhotoRecCarverIngestJobSettingsPanel.includeExcludeCheckbox.text=Focus on certain file types +PhotoRecCarverIngestJobSettingsPanel.fullListOfTypesHyperlink.text=https://www.cgsecurity.org/wiki/\nFile_Formats_Recovered_By_PhotoRec +PhotoRecCarverIngestJobSettingsPanel.fullListOfTypesLabel.text=Full List of Types: +PhotoRecCarverIngestJobSettingsPanel.exampleLabel.text=Example: jpg,png,zip PhotoRecCarverIngestJobSettingsPanel.extensionListTextfield.text= -PhotoRecCarverIngestJobSettingsPanel.includeRadioButton.text=Include -PhotoRecCarverIngestJobSettingsPanel.extensionListLabel.text=Extensions to exclude / include (a comma-separated list) +PhotoRecCarverIngestJobSettingsPanel.extensionListLabel.text=Types (comma separated list of extensions) +PhotoRecCarverIngestJobSettingsPanel.excludeRadioButton.text=Ignore the specified types +PhotoRecCarverIngestJobSettingsPanel.includeRadioButton.text=Carve only the specified types diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED index eb14e0af89..f1725a18fa 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED @@ -26,10 +26,13 @@ PhotoRecIngestModule.error.msg=Error processing {0} with PhotoRec carver. PhotoRecIngestModule.complete.numberOfErrors=Number of Errors while Carving: PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec Settings PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text=Keep corrupted files -PhotoRecCarverIngestJobSettingsPanel.includeExcludeCheckbox.text=Include or Exclude File Extensions -PhotoRecCarverIngestJobSettingsPanel.excludeRadioButton.text=Exclude +PhotoRecCarverIngestJobSettingsPanel.includeExcludeCheckbox.text=Focus on certain file types +PhotoRecCarverIngestJobSettingsPanel.fullListOfTypesHyperlink.text=https://www.cgsecurity.org/wiki/\nFile_Formats_Recovered_By_PhotoRec +PhotoRecCarverIngestJobSettingsPanel.fullListOfTypesLabel.text=Full List of Types: +PhotoRecCarverIngestJobSettingsPanel.exampleLabel.text=Example: jpg,png,zip PhotoRecCarverIngestJobSettingsPanel.extensionListTextfield.text= -PhotoRecCarverIngestJobSettingsPanel.includeRadioButton.text=Include -PhotoRecCarverIngestJobSettingsPanel.extensionListLabel.text=Extensions to exclude / include (a comma-separated list) +PhotoRecCarverIngestJobSettingsPanel.extensionListLabel.text=Types (comma separated list of extensions) +PhotoRecCarverIngestJobSettingsPanel.excludeRadioButton.text=Ignore the specified types +PhotoRecCarverIngestJobSettingsPanel.includeRadioButton.text=Carve only the specified types unallocatedSpaceProcessingSettingsError.message=The selected file ingest filter ignores unallocated space. This module carves unallocated space. Please choose a filter which does not ignore unallocated space or disable this module. unsupportedOS.message=PhotoRec module is supported on Windows platforms only. diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form index 4ce6bc15e8..0e51274e04 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form @@ -21,26 +21,13 @@ + - - - - - - - - - - - - - - - - - + + + + - @@ -49,13 +36,12 @@ - + - + - @@ -113,32 +99,35 @@ - - - - - - - - - + + + + + + + - + - - + - + + + + + + + @@ -179,6 +168,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java index 137e5195e6..84511b0a52 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java @@ -18,21 +18,34 @@ */ package org.sleuthkit.autopsy.modules.photoreccarver; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Desktop; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Collections; import java.util.List; +import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.lang.StringUtils; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; +import org.sleuthkit.autopsy.coreutils.Logger; /** * Ingest job settings panel for the Encryption Detection module. */ @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel { + + private static final Logger logger = Logger.getLogger(PhotoRecCarverIngestJobSettingsPanel.class.getName()); private static final String EXTENSION_LIST_SEPARATOR = ","; - + private static final String PHOTOREC_TYPES_URL = "https://www.cgsecurity.org/wiki/File_Formats_Recovered_By_PhotoRec"; + /** * Instantiate the ingest job settings panel. * @@ -54,36 +67,78 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe includeRadioButton.setSelected(!settings.isIncludeElseExclude()); excludeRadioButton.setSelected(!settings.isIncludeElseExclude()); keepCorruptedFilesCheckbox.setSelected(settings.isKeepCorruptedFiles()); + setupTypesHyperlink(); setIncludePanelEnabled(); } - + + /** + * Sets up a clickable hyperlink for the different supported types for + * extensions. + */ + private void setupTypesHyperlink() { + // taken from https://www.codejava.net/java-se/swing/how-to-create-hyperlink-with-jlabel-in-java-swing + this.fullListOfTypesHyperlink.setForeground(Color.BLUE.darker()); + this.fullListOfTypesHyperlink.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); + this.fullListOfTypesHyperlink.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + try { + Desktop.getDesktop().browse(new URI(PHOTOREC_TYPES_URL)); + } catch (IOException | URISyntaxException ex) { + logger.log(Level.WARNING, "There was an error going to types huperlink: " + PHOTOREC_TYPES_URL, ex); + } + } + }); + + } + + /** + * Whether or not the file type inclusion/exclusion panel should be enabled + * based on whether or not the includeExcludeCheckbox is checked. + */ private void setIncludePanelEnabled() { setIncludePanelEnabled(includeExcludeCheckbox.isSelected()); } - + + /** + * Sets components in the inclusion/exclusion panel to the specified enabled + * state. + * + * @param enabled Whether or not to enable components. + */ private void setIncludePanelEnabled(boolean enabled) { includeRadioButton.setEnabled(enabled); excludeRadioButton.setEnabled(enabled); extensionListLabel.setEnabled(enabled); extensionListTextfield.setEnabled(enabled); + exampleLabel.setEnabled(enabled); + fullListOfTypesLabel.setEnabled(enabled); } @Override public IngestModuleIngestJobSettings getSettings() { - return new PhotoRecCarverIngestJobSettings( - keepCorruptedFilesCheckbox.isSelected(), - includeExcludeCheckbox.isSelected(), - includeRadioButton.isSelected(), - getExtensions(extensionListTextfield.getText())); + return new PhotoRecCarverIngestJobSettings( + keepCorruptedFilesCheckbox.isSelected(), + includeExcludeCheckbox.isSelected(), + includeRadioButton.isSelected(), + getExtensions(extensionListTextfield.getText())); } - + + /** + * Determines a list of extensions to pass as parameters to photorec based + * on the specified input. Splits on separator and trims. + * + * @param combinedList The comma-separated list. + * + * @return The list of strings to use with photorec. + */ private List getExtensions(String combinedList) { if (StringUtils.isBlank(combinedList)) { return Collections.emptyList(); } - + return Stream.of(combinedList.split(EXTENSION_LIST_SEPARATOR)) - .map(ext -> ext.trim().replaceAll(EXTENSION_LIST_SEPARATOR, "")) + .map(ext -> ext.trim()) .filter(ext -> StringUtils.isNotBlank(ext)) .collect(Collectors.toList()); } @@ -106,6 +161,9 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe excludeRadioButton = new javax.swing.JRadioButton(); extensionListLabel = new javax.swing.JLabel(); extensionListTextfield = new javax.swing.JTextField(); + exampleLabel = new javax.swing.JLabel(); + fullListOfTypesLabel = new javax.swing.JLabel(); + fullListOfTypesHyperlink = new javax.swing.JLabel(); org.openide.awt.Mnemonics.setLocalizedText(keepCorruptedFilesCheckbox, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text")); // NOI18N @@ -132,6 +190,12 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe extensionListTextfield.setText(org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.extensionListTextfield.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(exampleLabel, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.exampleLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(fullListOfTypesLabel, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.fullListOfTypesLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(fullListOfTypesHyperlink, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.fullListOfTypesHyperlink.text")); // NOI18N + javax.swing.GroupLayout includeExcludeParentPanelLayout = new javax.swing.GroupLayout(includeExcludeParentPanel); includeExcludeParentPanel.setLayout(includeExcludeParentPanelLayout); includeExcludeParentPanelLayout.setHorizontalGroup( @@ -139,26 +203,31 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe .addGroup(includeExcludeParentPanelLayout.createSequentialGroup() .addContainerGap() .addGroup(includeExcludeParentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(includeExcludeParentPanelLayout.createSequentialGroup() - .addGroup(includeExcludeParentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(includeRadioButton) - .addComponent(excludeRadioButton) - .addComponent(extensionListLabel)) - .addGap(0, 0, Short.MAX_VALUE)) - .addComponent(extensionListTextfield, javax.swing.GroupLayout.Alignment.TRAILING)) - .addContainerGap()) + .addComponent(fullListOfTypesLabel) + .addComponent(exampleLabel) + .addComponent(extensionListLabel) + .addComponent(extensionListTextfield, javax.swing.GroupLayout.PREFERRED_SIZE, 258, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(fullListOfTypesHyperlink, javax.swing.GroupLayout.PREFERRED_SIZE, 250, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(includeRadioButton) + .addComponent(excludeRadioButton)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); includeExcludeParentPanelLayout.setVerticalGroup( includeExcludeParentPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(includeExcludeParentPanelLayout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(includeRadioButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGap(0, 0, 0) .addComponent(excludeRadioButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(extensionListLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(extensionListTextfield, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(exampleLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(fullListOfTypesLabel) + .addGap(0, 0, 0) + .addComponent(fullListOfTypesHyperlink, javax.swing.GroupLayout.DEFAULT_SIZE, 36, Short.MAX_VALUE) .addContainerGap()) ); @@ -167,33 +236,24 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() + .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(detectionSettingsLabel) - .addGroup(layout.createSequentialGroup() - .addGap(10, 10, 10) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(includeExcludeCheckbox) - .addComponent(keepCorruptedFilesCheckbox))))) - .addGroup(layout.createSequentialGroup() - .addGap(35, 35, 35) - .addComponent(includeExcludeParentPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addContainerGap(190, Short.MAX_VALUE)) + .addComponent(includeExcludeParentPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(detectionSettingsLabel) + .addComponent(keepCorruptedFilesCheckbox) + .addComponent(includeExcludeCheckbox))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(detectionSettingsLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGap(0, 2, 2) .addComponent(keepCorruptedFilesCheckbox) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGap(0, 0, 0) .addComponent(includeExcludeCheckbox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(includeExcludeParentPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(26, Short.MAX_VALUE)) + .addComponent(includeExcludeParentPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) ); }// //GEN-END:initComponents @@ -202,9 +262,12 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe }//GEN-LAST:event_includeExcludeCheckboxActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel exampleLabel; private javax.swing.JRadioButton excludeRadioButton; private javax.swing.JLabel extensionListLabel; private javax.swing.JTextField extensionListTextfield; + private javax.swing.JLabel fullListOfTypesHyperlink; + private javax.swing.JLabel fullListOfTypesLabel; private javax.swing.ButtonGroup includeExcludeButtonGroup; private javax.swing.JCheckBox includeExcludeCheckbox; private javax.swing.JRadioButton includeRadioButton; From 4f0ceb4e7783852f204b996b3d7f39a19bb12d95 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 3 Aug 2020 10:37:57 -0400 Subject: [PATCH 37/78] inital pass --- .../AccountDeviceInstanceNode.java | 25 +++++++++++++++++-- .../relationships/Bundle.properties-MERGED | 2 +- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index 4a50969ef3..24d190bf2d 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -35,8 +35,12 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.AccountDeviceInstance; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; +import org.sleuthkit.datamodel.TskCoreException; /** * Node to represent an Account Device Instance in the CVT @@ -49,6 +53,8 @@ final class AccountDeviceInstanceNode extends AbstractNode { private final CommunicationsManager commsManager; private final Account account; + private static final BlackboardAttribute.Type NAME_ATTRIBUTE = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(TSK_NAME.getTypeID())); + AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) { super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager)); this.accountDeviceInstanceKey = accountDeviceInstanceKey; @@ -122,15 +128,17 @@ final class AccountDeviceInstanceNode extends AbstractNode { @Override public String getShortDescription() { List personaList; + List contactArtifactList; try { personaList = CVTPersonaCache.getPersonaAccounts(account); + contactArtifactList = ContactCache.getContacts(account); } catch (ExecutionException ex) { logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex); return getDisplayName(); } String personaName; - if (!personaList.isEmpty()) { + if (personaList != null && !personaList.isEmpty()) { personaName = personaList.get(0).getPersona().getName(); if (personaList.size() > 1) { personaName += Bundle.AccountInstanceNode_Tooltip_suffix(Integer.toString(personaList.size())); @@ -139,6 +147,19 @@ final class AccountDeviceInstanceNode extends AbstractNode { personaName = "None"; } - return Bundle.AccountInstanceNode_Tooltip_Template(getDisplayName(), personaName); + String contactName = getDisplayName(); + if (contactArtifactList != null && !contactArtifactList.isEmpty()) { + try { + BlackboardArtifact contactArtifact = contactArtifactList.get(0); + BlackboardAttribute attribute = contactArtifact.getAttribute(NAME_ATTRIBUTE); + if (attribute != null) { + contactName = attribute.getValueString(); + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Failed to retrive name attribute from contact artifact.", ex); + } + } + + return Bundle.AccountInstanceNode_Tooltip_Template(contactName, personaName); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED index 6778d8dc53..616408978d 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED @@ -73,5 +73,5 @@ SummaryViewer.referencesLabel.text=Communication References: SummaryViewer.referencesDataLabel.text= SummaryViewer.contactsLabel.text=Book Entries: SummaryViewer.accountCountry.text= -SummaryViewer.fileRefPane.border.title=File Referernce(s) in Current Case +SummaryViewer.fileRefPane.border.title=File References in Current Case SummaryViewer.selectAccountFileRefLabel.text= diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageViewer.java b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageViewer.java index fc4e1ec1b4..f9989c5f55 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageViewer.java +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/MessageViewer.java @@ -38,6 +38,8 @@ import javax.swing.JPanel; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import static javax.swing.SwingUtilities.isDescendingFrom; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; import org.netbeans.swing.outline.DefaultOutlineModel; import org.netbeans.swing.outline.Outline; import org.openide.explorer.ExplorerManager; @@ -50,6 +52,7 @@ import org.openide.nodes.Node.PropertySet; import org.openide.util.Lookup; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.communications.ModifiableProxyLookup; +import org.sleuthkit.autopsy.communications.Utils; import org.sleuthkit.autopsy.coreutils.Logger; /** @@ -115,6 +118,13 @@ final class MessageViewer extends JPanel implements RelationshipsViewer { showSelectedThread(); } }); + + rootTablePane.getOutlineView().getOutline().getModel().addTableModelListener(new TableModelListener() { + @Override + public void tableChanged(TableModelEvent e) { + Utils.setColumnWidths(rootTablePane.getOutlineView().getOutline()); + } + }); threadMessagesPanel.setChildFactory(threadMessageNodeFactory); From 8cd7594afaa374bb1fd4facfac470c8a34f08dad Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 4 Aug 2020 16:30:16 -0400 Subject: [PATCH 50/78] updates for extension validation --- .../modules/photoreccarver/Bundle.properties | 1 - .../photoreccarver/Bundle.properties-MERGED | 4 +- .../PhotoRecCarverIngestJobSettingsPanel.form | 55 ++++---- .../PhotoRecCarverIngestJobSettingsPanel.java | 117 ++++++++++++++---- thirdparty/photorec_exec/README.txt | 4 +- 5 files changed, 134 insertions(+), 47 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties index 39cc41e1f4..3a3478ab94 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties @@ -20,7 +20,6 @@ PhotoRecIngestModule.complete.numberOfErrors=Number of Errors while Carving: PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec Settings PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text=Keep corrupted files PhotoRecCarverIngestJobSettingsPanel.includeExcludeCheckbox.text=Focus on certain file types -PhotoRecCarverIngestJobSettingsPanel.fullListOfTypesHyperlink.text=https://www.cgsecurity.org/wiki/\nFile_Formats_Recovered_By_PhotoRec PhotoRecCarverIngestJobSettingsPanel.fullListOfTypesLabel.text=Full List of Types: PhotoRecCarverIngestJobSettingsPanel.exampleLabel.text=Example: jpg,png,zip PhotoRecCarverIngestJobSettingsPanel.extensionListTextfield.text= diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED index f1725a18fa..3544b97d36 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED @@ -8,6 +8,9 @@ OpenIDE-Module-Long-Description=PhotoRec Carver ingest module. \n\n Carves unall OpenIDE-Module-Short-Description=Carves unallocated space and feeds carved files back into the system for processing. moduleDisplayName.text=PhotoRec Carver moduleDescription.text=Runs PhotoRec carver against unallocated space in the data source. +# {0} - extensions +PhotoRecCarverIngestJobSettingsPanel_getSettings_invalidExtensions_description=The following extensions are invalid and were removed: {0} +PhotoRecCarverIngestJobSettingsPanel_getSettings_invalidExtensions_title=Invalid Extensions PhotoRecIngestModule.nonHostnameUNCPathUsed=PhotoRec cannot operate with a UNC path containing IP addresses. PhotoRecIngestModule.PermissionsNotSufficient=Insufficient permissions accessing PhotoRecIngestModule.PermissionsNotSufficientSeeReference=See 'Shared Drive Authentication' in Autopsy help. @@ -27,7 +30,6 @@ PhotoRecIngestModule.complete.numberOfErrors=Number of Errors while Carving: PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec Settings PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text=Keep corrupted files PhotoRecCarverIngestJobSettingsPanel.includeExcludeCheckbox.text=Focus on certain file types -PhotoRecCarverIngestJobSettingsPanel.fullListOfTypesHyperlink.text=https://www.cgsecurity.org/wiki/\nFile_Formats_Recovered_By_PhotoRec PhotoRecCarverIngestJobSettingsPanel.fullListOfTypesLabel.text=Full List of Types: PhotoRecCarverIngestJobSettingsPanel.exampleLabel.text=Example: jpg,png,zip PhotoRecCarverIngestJobSettingsPanel.extensionListTextfield.text= diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form index f067266970..ca456614c7 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form @@ -20,25 +20,30 @@ - - + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + @@ -64,8 +69,7 @@ - - + @@ -137,8 +141,17 @@ - - + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java index 75986ba67f..b62218e17f 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java @@ -28,10 +28,13 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.swing.JOptionPane; import org.apache.commons.lang.StringUtils; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; import org.sleuthkit.autopsy.coreutils.Logger; @@ -44,7 +47,7 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe private static final Logger logger = Logger.getLogger(PhotoRecCarverIngestJobSettingsPanel.class.getName()); private static final String EXTENSION_LIST_SEPARATOR = ","; - private static final String PHOTOREC_TYPES_URL = "https://www.cgsecurity.org/wiki/File_Formats_Recovered_By_PhotoRec"; + private static final String PHOTOREC_TYPES_URL = "http://sleuthkit.org/autopsy/docs/user-docs/latest/photorec_carver_page.html"; /** * Instantiate the ingest job settings panel. @@ -116,12 +119,72 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe } @Override + @Messages({ + "PhotoRecCarverIngestJobSettingsPanel_getSettings_invalidExtensions_title=Invalid Extensions", + "# {0} - extensions", + "PhotoRecCarverIngestJobSettingsPanel_getSettings_invalidExtensions_description=The following extensions are invalid and were removed: {0}" + }) public IngestModuleIngestJobSettings getSettings() { + + return new PhotoRecCarverIngestJobSettings( keepCorruptedFilesCheckbox.isSelected(), includeExcludeCheckbox.isSelected(), includeRadioButton.isSelected(), - getExtensions(extensionListTextfield.getText())); + getAndUpdateExtensions() + ); + } + + + private List getAndUpdateExtensions() { + PhotoRecExtensions extensions = getExtensions(extensionListTextfield.getText()); + + if (extensions.getInvalidExtensions().size() > 0) { + JOptionPane.showMessageDialog( + this, + String.format("%s", + Bundle.PhotoRecCarverIngestJobSettingsPanel_getSettings_invalidExtensions_description( + String.join(",", extensions.getInvalidExtensions()))), + Bundle.PhotoRecCarverIngestJobSettingsPanel_getSettings_invalidExtensions_title(), + JOptionPane.ERROR_MESSAGE); + } + + + extensionListTextfield.setText(String.join(EXTENSION_LIST_SEPARATOR, extensions.getValidExtensions())); + return extensions.getValidExtensions(); + } + + /** + * An object defining valid and invalid photorec extensions as provided by the user. + */ + private static class PhotoRecExtensions { + + private final List validExtensions; + private final List invalidExtensions; + + /** + * Main constructor. + * @param validExtensions A list of strings representing the valid extensions. + * @param invalidExtensions A list of invalid extensions. + */ + PhotoRecExtensions(List validExtensions, List invalidExtensions) { + this.validExtensions = validExtensions == null ? Collections.emptyList() : Collections.unmodifiableList(validExtensions); + this.invalidExtensions = invalidExtensions == null ? Collections.emptyList() : Collections.unmodifiableList(invalidExtensions); + } + + /** + * @return The valid extensions. + */ + List getValidExtensions() { + return validExtensions; + } + + /** + * @return The invalid extensions. + */ + List getInvalidExtensions() { + return invalidExtensions; + } } /** @@ -132,15 +195,18 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe * * @return The list of strings to use with photorec. */ - private List getExtensions(String combinedList) { + private PhotoRecExtensions getExtensions(String combinedList) { if (StringUtils.isBlank(combinedList)) { - return Collections.emptyList(); + return new PhotoRecExtensions(null, null); } - return Stream.of(combinedList.split(EXTENSION_LIST_SEPARATOR)) + Map> extensions = Stream.of(combinedList.split(EXTENSION_LIST_SEPARATOR)) .map(ext -> ext.trim()) .filter(ext -> StringUtils.isNotBlank(ext)) - .collect(Collectors.toList()); + .sorted((a, b) -> a.toLowerCase().compareTo(b.toLowerCase())) + .collect(Collectors.partitioningBy(PhotoRecCarverFileOptExtensions::isValidExtension)); + + return new PhotoRecExtensions(extensions.get(true), extensions.get(false)); } /** @@ -185,7 +251,10 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe org.openide.awt.Mnemonics.setLocalizedText(extensionListLabel, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.extensionListLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(fullListOfTypesHyperlink, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.fullListOfTypesHyperlink.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(fullListOfTypesHyperlink, String.format("%s", PHOTOREC_TYPES_URL)); + fullListOfTypesHyperlink.setMaximumSize(new java.awt.Dimension(240, 50)); + fullListOfTypesHyperlink.setMinimumSize(new java.awt.Dimension(240, 50)); + fullListOfTypesHyperlink.setPreferredSize(new java.awt.Dimension(240, 50)); extensionListTextfield.setText(org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.extensionListTextfield.text")); // NOI18N @@ -198,21 +267,24 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(detectionSettingsLabel) - .addComponent(keepCorruptedFilesCheckbox) - .addComponent(includeExcludeCheckbox))) - .addGroup(layout.createSequentialGroup() - .addGap(31, 31, 31) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(includeRadioButton) - .addComponent(excludeRadioButton) - .addComponent(exampleLabel) - .addComponent(extensionListTextfield, javax.swing.GroupLayout.PREFERRED_SIZE, 258, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(fullListOfTypesLabel) - .addComponent(fullListOfTypesHyperlink, javax.swing.GroupLayout.PREFERRED_SIZE, 250, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(extensionListLabel))) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(detectionSettingsLabel) + .addComponent(keepCorruptedFilesCheckbox) + .addComponent(includeExcludeCheckbox))) + .addGroup(layout.createSequentialGroup() + .addGap(31, 31, 31) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(includeRadioButton) + .addComponent(excludeRadioButton) + .addComponent(exampleLabel) + .addComponent(extensionListTextfield, javax.swing.GroupLayout.PREFERRED_SIZE, 258, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(fullListOfTypesLabel) + .addComponent(extensionListLabel) + .addComponent(fullListOfTypesHyperlink, javax.swing.GroupLayout.PREFERRED_SIZE, 238, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -236,8 +308,7 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(fullListOfTypesLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(fullListOfTypesHyperlink) - .addContainerGap()) + .addComponent(fullListOfTypesHyperlink, javax.swing.GroupLayout.PREFERRED_SIZE, 51, javax.swing.GroupLayout.PREFERRED_SIZE)) ); }// //GEN-END:initComponents diff --git a/thirdparty/photorec_exec/README.txt b/thirdparty/photorec_exec/README.txt index 379000fcde..b65f25d7bd 100644 --- a/thirdparty/photorec_exec/README.txt +++ b/thirdparty/photorec_exec/README.txt @@ -1,4 +1,6 @@ The 'bin' folder is the version used when running the PhotoRec ingest module. It is also the 32-bit version. When the 64-bit version of the installer is created, the photorec_exec/64-bit/bin folder is placed at photorec_exec/bin. When the 32-bit version of the installer is created, the photorec_exec/64-bit folder is deleted. -See 'build-windows-installer.xml' for more details. \ No newline at end of file +See 'build-windows-installer.xml' for more details. + +Extensions for PhotoRec need to be placed in the PhotoRecCarverFileOptExtensions class so that only valid extensions will be used with PhotoRec. It can be generated through PhotoRec by launching photorec_win with no arguments, go to "Proceed", go to "File Opt" and press 'b'. This should generate a photorec.cfg file in the current working directory with a list of all the extensions. \ No newline at end of file From ea1246475d7b9c4b93db2f3d851e8234f5e82a77 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Tue, 4 Aug 2020 16:30:30 -0400 Subject: [PATCH 51/78] updates for extension validation --- .../PhotoRecCarverFileOptExtensions.java | 381 ++++++++++++++++++ 1 file changed, 381 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileOptExtensions.java diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileOptExtensions.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileOptExtensions.java new file mode 100644 index 0000000000..ada33e1242 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileOptExtensions.java @@ -0,0 +1,381 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit 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.modules.photoreccarver; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Specifies what extensions can be used for the "fileopt" option of photorec, + * which allows certain extensions to be included or excluded. + */ +final class PhotoRecCarverFileOptExtensions { + + /** + * Returns true if the extension provided in the string can be used with + * photorec. + * + * @param extension The extension (i.e. 'exe'). + * + * @return True if the extension can be used with photorec. + */ + public static boolean isValidExtension(String extension) { + return KNOWN_EXTENSIONS.contains(extension); + } + + // This was generated by photorec. It can be generated by photorec by + // launching photorec_win with no arguments, go to "Proceed", go to + // "File Opt" and press 'b'. This should generate a photorec.cfg file in + // the working directory with all the extensions. + private static final Set KNOWN_EXTENSIONS = new HashSet(Arrays.asList( + "1cd", + "3dm", + "7z", + "DB", + "a", + "abr", + "acb", + "accdb", + "ace", + "ab", + "ado", + "afdesign", + "ahn", + "aif", + "all", + "als", + "amd", + "amr", + "apa", + "ape", + "apple", + "ari", + "arj", + "asf", + "asl", + "asm", + "atd", + "au", + "axp", + "axx", + "bac", + "bdm", + "db", + "bim", + "bin", + "binvox", + "bkf", + "blend", + "bmp", + "bpg", + "bvr", + "bz2", + "c4d", + "cab", + "caf", + "cam", + "catdrawing", + "cdt", + "che", + "chm", + "class", + "comicdoc", + "cp_", + "cow", + "cpi", + "crw", + "csh", + "ctg", + "cwk", + "d2s", + "dad", + "dar", + "dat", + "dbf", + "dbn", + "dcm", + "ddf", + "dex", + "diskimage", + "fat", + "djv", + "dmp", + "drw", + "doc", + "dpx", + "ds2", + "DS_Store", + "dsc", + "dss", + "dst", + "dta", + "dump", + "dv", + "dvi", + "dvr", + "dwg", + "dxf", + "e01", + "eCryptfs", + "edb", + "elf", + "emf", + "ess", + "evt", + "evtx", + "exe", + "exs", + "ext", + "ext", + "fat", + "fbf", + "fbk", + "fcp", + "fcs", + "fdb", + "fds", + "fh10", + "fh5", + "sparseimage", + "fits", + "fit", + "flac", + "flp", + "flv", + "fm", + "fob", + "fos", + "fp5", + "fp7", + "freeway", + "frm", + "fs", + "fwd", + "gam", + "gct", + "gho", + "gi", + "gif", + "gm*", + "gp2", + "gp5", + "gpg", + "gpx", + "gsm", + "gz", + "hdf", + "hdr", + "hds", + "hfsp", + "hm", + "hr9", + "http", + "ibd", + "icc", + "icns", + "ico", + "idx", + "ifo", + "imb", + "indd", + "info", + "iso", + "it", + "itu", + "jks", + "jpg", + "jsonlz4", + "kdb", + "kdbx", + "key", + "ldf", + "lit", + "logic", + "lnk", + "lso", + "luks", + "lxo", + "lzh", + "lzo", + "m2ts", + "mat", + "max", + "mb", + "mcd", + "mdb", + "mdf", + "mfa", + "mfg", + "mft", + "mid", + "mig", + "mk5", + "mkv", + "mlv", + "mobi", + "mov/mdat", + "mov", + "mp3", + "mpg", + "mpl", + "mrw", + "msa", + "mus", + "myo", + "MYI", + "mxf", + "nd2", + "nds", + "nes", + "njx", + "nk2", + "nsf", + "oci", + "ogg", + "one", + "orf", + "paf", + "pap", + "par2", + "pcap", + "pcb", + "pct", + "pcx", + "pdb", + "pdf", + "pds", + "pf", + "pfx", + "dump", + "plist", + "plr", + "plt", + "png", + "pnm", + "prc", + "prd", + "prt", + "ps", + "psb", + "psd", + "psf", + "psp", + "pst", + "ptb", + "ptf", + "pyc", + "pzf", + "pzh", + "qbb", + "qdf", + "qkt", + "qxd", + "r3d", + "ra", + "raf", + "rar", + "raw", + "rdc", + "reg", + "res", + "rfp", + "riff", + "rlv", + "rm", + "rns", + "rpm", + "rw2", + "rx2", + "save", + "ses", + "sgcta", + "shn", + "sib", + "sit", + "skd", + "skp", + "snag", + "snz", + "sp3", + "spe", + "spf", + "sav", + "sqlite", + "sqm", + "steuer2014", + "stl", + "studio", + "sit", + "swf", + "tar", + "tax", + "tg", + "tib", + "tif", + "TiVo", + "torrent", + "tph", + "tpl", + "ts", + "ttf", + "tx?", + "txt", + "tz", + "v2i", + "vault", + "vdj", + "vfb", + "vdi", + "veg", + "vib", + "vmdk", + "vmg", + "wallet", + "wdp", + "wee", + "wim", + "win", + "wks", + "wld", + "wmf", + "wnk", + "woff", + "wpb", + "wpd", + "wtv", + "wv", + "x3f", + "x3i", + "x4a", + "xar", + "xcf", + "xfi", + "xfs", + "xm", + "xml", + "xsv", + "xpt", + "xv", + "xz", + "z2d", + "zcode", + "zip", + "zpr")); +} From e306dde9c5932e029260da99fade4f6b0d139d08 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 5 Aug 2020 07:48:13 -0400 Subject: [PATCH 52/78] fix for link wrap --- .../PhotoRecCarverIngestJobSettingsPanel.form | 39 ++++++++++--------- .../PhotoRecCarverIngestJobSettingsPanel.java | 24 +++++++----- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form index ca456614c7..88a1c77e44 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form @@ -5,6 +5,11 @@ + + + + + @@ -39,7 +44,7 @@ - + @@ -69,7 +74,8 @@ - + + @@ -139,22 +145,6 @@ - - - - - - - - - - - - - - - - @@ -173,5 +163,18 @@ + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java index b62218e17f..da028bc250 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java @@ -226,9 +226,11 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe exampleLabel = new javax.swing.JLabel(); fullListOfTypesLabel = new javax.swing.JLabel(); extensionListLabel = new javax.swing.JLabel(); - fullListOfTypesHyperlink = new javax.swing.JLabel(); extensionListTextfield = new javax.swing.JTextField(); includeRadioButton = new javax.swing.JRadioButton(); + fullListOfTypesHyperlink = new javax.swing.JTextArea(); + + setPreferredSize(null); org.openide.awt.Mnemonics.setLocalizedText(keepCorruptedFilesCheckbox, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text")); // NOI18N @@ -251,17 +253,20 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe org.openide.awt.Mnemonics.setLocalizedText(extensionListLabel, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.extensionListLabel.text")); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(fullListOfTypesHyperlink, String.format("%s", PHOTOREC_TYPES_URL)); - fullListOfTypesHyperlink.setMaximumSize(new java.awt.Dimension(240, 50)); - fullListOfTypesHyperlink.setMinimumSize(new java.awt.Dimension(240, 50)); - fullListOfTypesHyperlink.setPreferredSize(new java.awt.Dimension(240, 50)); - extensionListTextfield.setText(org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.extensionListTextfield.text")); // NOI18N includeExcludeButtonGroup.add(includeRadioButton); includeRadioButton.setSelected(true); org.openide.awt.Mnemonics.setLocalizedText(includeRadioButton, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.includeRadioButton.text")); // NOI18N + fullListOfTypesHyperlink.setEditable(false); + fullListOfTypesHyperlink.setColumns(20); + fullListOfTypesHyperlink.setLineWrap(true); + fullListOfTypesHyperlink.setRows(5); + fullListOfTypesHyperlink.setText(PHOTOREC_TYPES_URL); + fullListOfTypesHyperlink.setFocusable(false); + fullListOfTypesHyperlink.setOpaque(false); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -283,7 +288,7 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe .addComponent(extensionListTextfield, javax.swing.GroupLayout.PREFERRED_SIZE, 258, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(fullListOfTypesLabel) .addComponent(extensionListLabel) - .addComponent(fullListOfTypesHyperlink, javax.swing.GroupLayout.PREFERRED_SIZE, 238, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addComponent(fullListOfTypesHyperlink, javax.swing.GroupLayout.PREFERRED_SIZE, 247, javax.swing.GroupLayout.PREFERRED_SIZE)))) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); layout.setVerticalGroup( @@ -308,7 +313,8 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(fullListOfTypesLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(fullListOfTypesHyperlink, javax.swing.GroupLayout.PREFERRED_SIZE, 51, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(fullListOfTypesHyperlink, javax.swing.GroupLayout.PREFERRED_SIZE, 36, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) ); }// //GEN-END:initComponents @@ -321,7 +327,7 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe private javax.swing.JRadioButton excludeRadioButton; private javax.swing.JLabel extensionListLabel; private javax.swing.JTextField extensionListTextfield; - private javax.swing.JLabel fullListOfTypesHyperlink; + private javax.swing.JTextArea fullListOfTypesHyperlink; private javax.swing.JLabel fullListOfTypesLabel; private javax.swing.ButtonGroup includeExcludeButtonGroup; private javax.swing.JCheckBox includeExcludeCheckbox; From 5ca4dc6022e5d7ca8c9a8263a7b6c251f2c92b13 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 5 Aug 2020 15:12:22 -0400 Subject: [PATCH 53/78] Updated based on review comments --- .../autopsy/communications/ContactCache.java | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java index 54c5b54b82..2b0cc8f678 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java +++ b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.communications; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.sql.SQLException; import java.util.ArrayList; @@ -44,7 +45,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * A singleton cache of the Contact artifacts for accounts. The map of account * unique ids to list of contact artifacts is stored in a LoadingCache which - * expires after 10 of non-use. + * expires after 10 of non-use. * */ final class ContactCache { @@ -52,7 +53,7 @@ final class ContactCache { private static final Logger logger = Logger.getLogger(ContactCache.class.getName()); private static ContactCache instance; - + private final LoadingCache>> accountMap; /** @@ -80,16 +81,16 @@ final class ContactCache { * Construct a new instance. */ private ContactCache() { - + accountMap = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build( new CacheLoader>>() { @Override public Map> load(String key) { try { - return buildMap(); + return buildMap(); } catch (SQLException | TskCoreException ex) { logger.log(Level.WARNING, "Failed to build account to contact map", ex); - } + } return new HashMap<>(); // Return an empty map if there is an exception to avoid NPE and continual trying. } }); @@ -104,6 +105,12 @@ final class ContactCache { } }; + Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), (PropertyChangeEvent event) -> { + if (event.getNewValue() == null) { + invalidateCache(); + } + }); + IngestManager.getInstance().addIngestModuleEventListener(EnumSet.of(DATA_ADDED), ingestListener); } @@ -119,42 +126,41 @@ final class ContactCache { return instance; } - + /** * Builds the map of account IDs to contacts that reference them. - * + * * @return A map of account IDs to contact artifacts. - * + * * @throws TskCoreException - * @throws SQLException + * @throws SQLException */ private Map> buildMap() throws TskCoreException, SQLException { Map> acctMap = new HashMap<>(); List contactList = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); - for(BlackboardArtifact contactArtifact: contactList) { + for (BlackboardArtifact contactArtifact : contactList) { List contactAttributes = contactArtifact.getAttributes(); - for(BlackboardAttribute attribute: contactAttributes) { + for (BlackboardAttribute attribute : contactAttributes) { String typeName = attribute.getAttributeType().getTypeName(); - - if(typeName.startsWith("TSK_EMAIL") - || typeName.startsWith("TSK_PHONE") - || typeName.startsWith("TSK_NAME") - || typeName.startsWith("TSK_ID")) { + + if (typeName.startsWith("TSK_EMAIL") + || typeName.startsWith("TSK_PHONE") + || typeName.startsWith("TSK_NAME") + || typeName.startsWith("TSK_ID")) { String accountID = attribute.getValueString(); - List artifactList = acctMap.get(accountID); - if(artifactList == null) { - artifactList = new ArrayList<>(); - acctMap.put(accountID, artifactList); - } - if(!artifactList.contains(contactArtifact)) { + List artifactList = acctMap.getOrDefault(accountID, new ArrayList<>()); + + acctMap.put(accountID, artifactList); + + if (!artifactList.contains(contactArtifact)) { artifactList.add(contactArtifact); } } } - + } - + return acctMap; } } From b792ec8145ab124c57b9e0251f8b639ddadad301 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Wed, 5 Aug 2020 16:18:19 -0400 Subject: [PATCH 54/78] error on startUp --- .../PhotoRecCarverFileIngestModule.java | 18 +++++ .../PhotoRecCarverIngestJobSettingsPanel.form | 5 -- .../PhotoRecCarverIngestJobSettingsPanel.java | 69 ++----------------- 3 files changed, 24 insertions(+), 68 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java index abeb9199ba..24b495f05c 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java @@ -37,6 +37,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; +import java.util.stream.Collectors; import org.openide.modules.InstalledFileLocator; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; @@ -102,6 +103,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { private File executableFile; private IngestServices services; private final UNCPathUtilities uncPathUtilities = new UNCPathUtilities(); + private final PhotoRecCarverIngestJobSettings settings; private final String optionsString; private long jobId; @@ -119,6 +121,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { * @param settings Ingest job settings used to configure the module. */ PhotoRecCarverFileIngestModule(PhotoRecCarverIngestJobSettings settings) { + this.settings = settings; this.optionsString = getPhotorecOptions(settings); } @@ -181,7 +184,22 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { * @inheritDoc */ @Override + @NbBundle.Messages({ + "# {0} - extensions", + "PhotoRecCarverFileIngestModule_getSettings_invalidExtensions_description=The following extensions are invalid: {0}" + }) public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException { + // validate settings + List invalidExtensions = this.settings.getIncludeExcludeExtensions().stream() + .filter((ext) -> !PhotoRecCarverFileOptExtensions.isValidExtension(ext)) + .collect(Collectors.toList()); + + if (invalidExtensions.size() > 0) { + throw new IngestModule.IngestModuleException( + Bundle.PhotoRecCarverFileIngestModule_getSettings_invalidExtensions_description( + String.join(",", invalidExtensions))); + } + this.context = context; this.services = IngestServices.getInstance(); this.jobId = this.context.getJobId(); diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form index 88a1c77e44..b84cb64e56 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form @@ -5,11 +5,6 @@ - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java index da028bc250..a3073d031e 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java @@ -119,11 +119,6 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe } @Override - @Messages({ - "PhotoRecCarverIngestJobSettingsPanel_getSettings_invalidExtensions_title=Invalid Extensions", - "# {0} - extensions", - "PhotoRecCarverIngestJobSettingsPanel_getSettings_invalidExtensions_description=The following extensions are invalid and were removed: {0}" - }) public IngestModuleIngestJobSettings getSettings() { @@ -131,82 +126,30 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe keepCorruptedFilesCheckbox.isSelected(), includeExcludeCheckbox.isSelected(), includeRadioButton.isSelected(), - getAndUpdateExtensions() + getExtensions(extensionListTextfield.getText()) ); } - - private List getAndUpdateExtensions() { - PhotoRecExtensions extensions = getExtensions(extensionListTextfield.getText()); - - if (extensions.getInvalidExtensions().size() > 0) { - JOptionPane.showMessageDialog( - this, - String.format("%s", - Bundle.PhotoRecCarverIngestJobSettingsPanel_getSettings_invalidExtensions_description( - String.join(",", extensions.getInvalidExtensions()))), - Bundle.PhotoRecCarverIngestJobSettingsPanel_getSettings_invalidExtensions_title(), - JOptionPane.ERROR_MESSAGE); - } - - - extensionListTextfield.setText(String.join(EXTENSION_LIST_SEPARATOR, extensions.getValidExtensions())); - return extensions.getValidExtensions(); - } - /** - * An object defining valid and invalid photorec extensions as provided by the user. - */ - private static class PhotoRecExtensions { - - private final List validExtensions; - private final List invalidExtensions; - - /** - * Main constructor. - * @param validExtensions A list of strings representing the valid extensions. - * @param invalidExtensions A list of invalid extensions. - */ - PhotoRecExtensions(List validExtensions, List invalidExtensions) { - this.validExtensions = validExtensions == null ? Collections.emptyList() : Collections.unmodifiableList(validExtensions); - this.invalidExtensions = invalidExtensions == null ? Collections.emptyList() : Collections.unmodifiableList(invalidExtensions); - } - - /** - * @return The valid extensions. - */ - List getValidExtensions() { - return validExtensions; - } - - /** - * @return The invalid extensions. - */ - List getInvalidExtensions() { - return invalidExtensions; - } - } /** * Determines a list of extensions to pass as parameters to photorec based - * on the specified input. Splits on separator and trims. + * on the specified input. * * @param combinedList The comma-separated list. * * @return The list of strings to use with photorec. */ - private PhotoRecExtensions getExtensions(String combinedList) { + private List getExtensions(String combinedList) { if (StringUtils.isBlank(combinedList)) { - return new PhotoRecExtensions(null, null); + return Collections.emptyList(); } - Map> extensions = Stream.of(combinedList.split(EXTENSION_LIST_SEPARATOR)) + return Stream.of(combinedList.split(EXTENSION_LIST_SEPARATOR)) .map(ext -> ext.trim()) .filter(ext -> StringUtils.isNotBlank(ext)) .sorted((a, b) -> a.toLowerCase().compareTo(b.toLowerCase())) - .collect(Collectors.partitioningBy(PhotoRecCarverFileOptExtensions::isValidExtension)); - - return new PhotoRecExtensions(extensions.get(true), extensions.get(false)); + .collect(Collectors.toList()); } /** From adbaf3f70a9abbaaaaa2dd86215596c4bbaff4ea Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 07:34:23 -0400 Subject: [PATCH 55/78] some refactoring --- .../DataSourceSummaryDialog.java | 5 +--- .../DataSourceSummaryTabbedPane.java | 27 ++++++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java index e1197fa83a..9cd7549c06 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryDialog.java @@ -49,10 +49,7 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser * datasource. */ @Messages({ - "DataSourceSummaryDialog.window.title=Data Sources Summary", - "DataSourceSummaryDialog.countsTab.title=Counts", - "DataSourceSummaryDialog.detailsTab.title=Details", - "DataSourceSummaryDialog.ingestHistoryTab.title=Ingest History" + "DataSourceSummaryDialog.window.title=Data Sources Summary" }) DataSourceSummaryDialog(Frame owner) { super(owner, Bundle.DataSourceSummaryDialog_window_title(), true); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java index e1d15a6b0d..c112517acc 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import javax.swing.JTabbedPane; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; import org.sleuthkit.datamodel.DataSource; @@ -27,13 +28,20 @@ import org.sleuthkit.datamodel.DataSource; * DataSourceSummaryCountsPanel, DataSourceSummaryDetailsPanel, and * IngestJobInfoPanel. */ +@Messages({ + "DataSourceSummaryTabbedPane_countsTab_title=Counts", + "DataSourceSummaryTabbedPane_detailsTab_title=Details", + "DataSourceSummaryTabbedPane_userActivityTab_title=User Activity", + "DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History" +}) public class DataSourceSummaryTabbedPane extends JTabbedPane { private static final long serialVersionUID = 1L; - private final DataSourceSummaryCountsPanel countsPanel; - private final DataSourceSummaryDetailsPanel detailsPanel; - private final IngestJobInfoPanel ingestHistoryPanel; + private final DataSourceSummaryCountsPanel countsPanel = new DataSourceSummaryCountsPanel(); + private final DataSourceSummaryDetailsPanel detailsPanel = new DataSourceSummaryDetailsPanel(); + private final DataSourceSummaryUserActivityPanel userActivityPanel = new DataSourceSummaryUserActivityPanel(); + private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel(); private DataSource dataSource = null; @@ -41,13 +49,11 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { * Constructs a tabbed pane showing the summary of a data source. */ public DataSourceSummaryTabbedPane() { - countsPanel = new DataSourceSummaryCountsPanel(); - detailsPanel = new DataSourceSummaryDetailsPanel(); - ingestHistoryPanel = new IngestJobInfoPanel(); - - addTab(Bundle.DataSourceSummaryDialog_detailsTab_title(), detailsPanel); - addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); - addTab(Bundle.DataSourceSummaryDialog_ingestHistoryTab_title(), ingestHistoryPanel); + + addTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), detailsPanel); + addTab(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), countsPanel); + addTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), userActivityPanel); + addTab(Bundle.DataSourceSummaryTabbedPane_ingestHistoryTab_title(), ingestHistoryPanel); } /** @@ -69,6 +75,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { detailsPanel.setDataSource(dataSource); countsPanel.setDataSource(dataSource); + userActivityPanel.setDataSource(dataSource); ingestHistoryPanel.setDataSource(dataSource); } } From baeb551a545d5569eb07ac784bcfc644f45bed30 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 07:36:59 -0400 Subject: [PATCH 56/78] fixes for codacy --- .../photoreccarver/PhotoRecCarverFileIngestModule.java | 2 +- .../photoreccarver/PhotoRecCarverFileOptExtensions.java | 5 ++++- .../photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java | 3 --- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java index 24b495f05c..53055f3451 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java @@ -194,7 +194,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { .filter((ext) -> !PhotoRecCarverFileOptExtensions.isValidExtension(ext)) .collect(Collectors.toList()); - if (invalidExtensions.size() > 0) { + if (!invalidExtensions.isEmpty()) { throw new IngestModule.IngestModuleException( Bundle.PhotoRecCarverFileIngestModule_getSettings_invalidExtensions_description( String.join(",", invalidExtensions))); diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileOptExtensions.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileOptExtensions.java index ada33e1242..9072a3060b 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileOptExtensions.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileOptExtensions.java @@ -36,7 +36,7 @@ final class PhotoRecCarverFileOptExtensions { * * @return True if the extension can be used with photorec. */ - public static boolean isValidExtension(String extension) { + static boolean isValidExtension(String extension) { return KNOWN_EXTENSIONS.contains(extension); } @@ -378,4 +378,7 @@ final class PhotoRecCarverFileOptExtensions { "zcode", "zip", "zpr")); + + private PhotoRecCarverFileOptExtensions() { + } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java index a3073d031e..5d91124ff9 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java @@ -28,13 +28,10 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.logging.Level; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.swing.JOptionPane; import org.apache.commons.lang.StringUtils; -import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; import org.sleuthkit.autopsy.coreutils.Logger; From b039b9e9e7c4718412c200b985a740ebb57339f0 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 07:54:22 -0400 Subject: [PATCH 57/78] validation fix --- .../photoreccarver/Bundle.properties-MERGED | 3 +-- .../PhotoRecCarverFileIngestModule.java | 23 +++++++++++-------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED index 3544b97d36..a5a0625cdc 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED @@ -9,8 +9,7 @@ OpenIDE-Module-Short-Description=Carves unallocated space and feeds carved files moduleDisplayName.text=PhotoRec Carver moduleDescription.text=Runs PhotoRec carver against unallocated space in the data source. # {0} - extensions -PhotoRecCarverIngestJobSettingsPanel_getSettings_invalidExtensions_description=The following extensions are invalid and were removed: {0} -PhotoRecCarverIngestJobSettingsPanel_getSettings_invalidExtensions_title=Invalid Extensions +PhotoRecCarverFileIngestModule_getSettings_invalidExtensions_description=The following extensions are invalid: {0} PhotoRecIngestModule.nonHostnameUNCPathUsed=PhotoRec cannot operate with a UNC path containing IP addresses. PhotoRecIngestModule.PermissionsNotSufficient=Insufficient permissions accessing PhotoRecIngestModule.PermissionsNotSufficientSeeReference=See 'Shared Drive Authentication' in Autopsy help. diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java index 53055f3451..4ff59ef0d6 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java @@ -104,7 +104,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { private IngestServices services; private final UNCPathUtilities uncPathUtilities = new UNCPathUtilities(); private final PhotoRecCarverIngestJobSettings settings; - private final String optionsString; + private String optionsString; private long jobId; private static class IngestJobTotals { @@ -122,7 +122,6 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { */ PhotoRecCarverFileIngestModule(PhotoRecCarverIngestJobSettings settings) { this.settings = settings; - this.optionsString = getPhotorecOptions(settings); } /** @@ -190,16 +189,20 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { }) public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException { // validate settings - List invalidExtensions = this.settings.getIncludeExcludeExtensions().stream() - .filter((ext) -> !PhotoRecCarverFileOptExtensions.isValidExtension(ext)) - .collect(Collectors.toList()); - - if (!invalidExtensions.isEmpty()) { - throw new IngestModule.IngestModuleException( - Bundle.PhotoRecCarverFileIngestModule_getSettings_invalidExtensions_description( - String.join(",", invalidExtensions))); + if (this.settings.hasFileOptOption()) { + List invalidExtensions = this.settings.getIncludeExcludeExtensions().stream() + .filter((ext) -> !PhotoRecCarverFileOptExtensions.isValidExtension(ext)) + .collect(Collectors.toList()); + + if (!invalidExtensions.isEmpty()) { + throw new IngestModule.IngestModuleException( + Bundle.PhotoRecCarverFileIngestModule_getSettings_invalidExtensions_description( + String.join(",", invalidExtensions))); + } } + this.optionsString = getPhotorecOptions(this.settings); + this.context = context; this.services = IngestServices.getInstance(); this.jobId = this.context.getJobId(); From b637d4d2c1c5ea2ce96d36b105c768c52e013d9f Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 09:52:06 -0400 Subject: [PATCH 58/78] updating for handling path --- .../DataSourceInfoUtilities.java | 129 +++++++++++++++--- .../DataSourceSummaryUserActivityPanel.java | 80 ++++++++--- 2 files changed, 170 insertions(+), 39 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index 511d1a62a2..7ca82753b0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -30,6 +32,10 @@ import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Set; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import org.apache.commons.lang3.tuple.Pair; +import java.util.function.Function; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -247,6 +253,31 @@ final class DataSourceInfoUtilities { return getBaseQueryResult(query, getStringLongResultSetHandler(nameParam, valueParam), errorMessage); } + static class TopProgramsResult { + + private final String programName; + private final String programPath; + private final long runTimes; + + public TopProgramsResult(String programName, String programPath, long runTimes) { + this.programName = programName; + this.programPath = programPath; + this.runTimes = runTimes; + } + + public String getProgramName() { + return programName; + } + + public String getProgramPath() { + return programPath; + } + + public long getRunTimes() { + return runTimes; + } + } + /** * Retrieves a list of the top programs used on the data source. Currently * determines this based off of which prefetch results return the highest @@ -257,36 +288,94 @@ final class DataSourceInfoUtilities { * * @return */ - static Map getTopPrograms(DataSource dataSource, int count) { + static List getTopPrograms(DataSource dataSource, int count) { if (dataSource == null || count <= 0) { - return Collections.emptyMap(); + return Collections.emptyList(); } - String progNameParam = "prog_name"; - String runTimesParam = "run_times"; - String prefetchIdentifier = "Windows Prefetch Extractor"; + final String progNameParam = "prog_name"; + final String progPathParam = "prog_path"; + final String runTimesParam = "run_times"; + final String prefetchIdentifier = "Windows Prefetch Extractor"; - String query = "SELECT program_artifacts." + progNameParam + ", MAX(attr.value_int32) AS " + runTimesParam + "\n" + final String query = "SELECT\n" + + " prog_name_query." + progNameParam + ",\n" + + " prog_path_query." + progPathParam + ",\n" + + " MAX(attr.value_int32) AS " + runTimesParam + "\n" + "FROM blackboard_artifacts bba\n" + "INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id\n" + "INNER JOIN (\n" - + "-- get all the different artifacts coming from prefetch\n" - + " SELECT \n" - + " bba.artifact_id as artifact_id, \n" - + " attr.value_text as " + progNameParam + "\n" - + " FROM blackboard_artifacts bba\n" - + " INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id\n" - + " WHERE bba.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() + "\n" - + " AND attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID() + "\n" - + " AND attr.source = '" + prefetchIdentifier + "'\n" - + ") program_artifacts ON bba.artifact_id = program_artifacts.artifact_id\n" + + " SELECT \n" + + " bba1.artifact_id AS artifact_id,\n" + + " attr1.value_text AS " + progNameParam + "\n" + + " FROM blackboard_artifacts bba1\n" + + " INNER JOIN blackboard_attributes attr1 ON bba1.artifact_id = attr1.artifact_id\n" + + " WHERE bba1.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() + "\n" + + " AND attr1.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID() + "\n" + + ") prog_name_query ON bba.artifact_id = prog_name_query.artifact_id\n" + + "LEFT JOIN (\n" + + " SELECT \n" + + " bba2.artifact_id AS artifact_id,\n" + + " attr2.value_text AS " + progPathParam + "\n" + + " FROM blackboard_artifacts bba2\n" + + " INNER JOIN blackboard_attributes attr2 ON bba2.artifact_id = attr2.artifact_id\n" + + " WHERE bba2.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() + "\n" + + " AND attr2.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH.getTypeID() + "\n" + + ") prog_path_query ON bba.artifact_id = prog_path_query.artifact_id\n" + "WHERE attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT.getTypeID() + "\n" - + "GROUP BY program_artifacts." + progNameParam + "\n" - + "ORDER BY " + runTimesParam + " DESC\n" + + "AND bba.data_source_obj_id = " + dataSource.getId() + "\n" + + "AND attr.source = '" + prefetchIdentifier + "'\n" + + "GROUP BY prog_name_query." + progNameParam + ", prog_path_query." + progPathParam + "\n" + + "ORDER BY MAX(attr.value_int32) DESC\n" + "LIMIT " + count + " OFFSET 0"; - String errorMessage = "Unable to get top program counts; returning null."; - return getBaseQueryResult(query, getStringLongResultSetHandler(progNameParam, runTimesParam), errorMessage); + final String errorMessage = "Unable to get top program results; returning null."; + + ResultSetHandler> handler = (resultSet) -> { + List progResults = new ArrayList<>(); + + while (resultSet.next()) { + try { + progResults.add(new TopProgramsResult( + resultSet.getString(progNameParam), + resultSet.getString(progPathParam), + resultSet.getLong(runTimesParam))); + } catch (SQLException ex) { + logger.log(Level.WARNING, "Failed to get a top program result from the result set.", ex); + } + } + + return progResults; + }; + + return getBaseQueryResult(query, handler, errorMessage); + } + + private static List>> SHORT_FOLDER_MATCHERS = Arrays.asList( + // Windows if in /Windows + Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?windows", Pattern.CASE_INSENSITIVE), (match) -> "Windows"), + // program name if /Program Files/program or /Program Files (x86)/program + Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?Program Files( \\(x86\\))?[\\\\\\/](?.+?)[\\\\\\/]", + Pattern.CASE_INSENSITIVE), (match) -> match.group("programName")), + // match for an AppData folder + Pair.of(Pattern.compile("(^|[\\\\\\/])AppData([\\\\\\/]|$)"), (match) -> "AppData") + ); + + static String getShortFolderName(String path) { + if (path == null) { + return ""; + } + + for (Pair> matchEntry : SHORT_FOLDER_MATCHERS) { + Pattern p = matchEntry.getLeft(); + Function resultsProvider = matchEntry.getRight(); + Matcher match = p.matcher(path); + if (match.find()) { + return resultsProvider.apply(match); + } + } + + return ""; } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index 696e6e904b..1b0abc05fa 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -18,10 +18,13 @@ */ package org.sleuthkit.autopsy.casemodule.datasourcesummary; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Map; import javax.swing.JLabel; +import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableModel; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.DataSource; @@ -32,6 +35,7 @@ import org.sleuthkit.datamodel.DataSource; @Messages({ "DataSourceSummaryUserActivityPanel_tab_title=User Activity", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program", + "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder", "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times" }) public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { @@ -45,12 +49,6 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { RIGHT_ALIGNED_RENDERER.setHorizontalAlignment(JLabel.RIGHT); } - // column headers for artifact counts table - private static final Object[] TOP_PROGS_COLUMN_HEADERS = new Object[]{ - Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), - Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header() - }; - private DataSource dataSource; /** @@ -78,7 +76,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; if (dataSource == null || !Case.isCaseOpen()) { - updateTopPrograms(EMPTY_PAIRS); + updateTopPrograms(new TopProgramsModel(null)); } else { updateTopPrograms(getTopProgramsModel(dataSource)); } @@ -88,8 +86,8 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { * Updates the Top Programs Table in the gui. * @param data The data in Object[][] form to be used by the DefaultTableModel. */ - private void updateTopPrograms(Object[][] data) { - topProgramsTable.setModel(new NonEditableTableModel(data, TOP_PROGS_COLUMN_HEADERS)); + private void updateTopPrograms(TopProgramsModel model) { + topProgramsTable.setModel(model); topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(230); topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); this.repaint(); @@ -102,17 +100,61 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { * * @return The JTable data model of counts of program runs. */ - private static Object[][] getTopProgramsModel(DataSource selectedDataSource) { - Map artifactMapping = DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); - if (artifactMapping == null) { - return EMPTY_PAIRS; + private static TopProgramsModel getTopProgramsModel(DataSource selectedDataSource) { + List topProgramList = + DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); + + if (topProgramList == null) { + return new TopProgramsModel(null); + } else { + return new TopProgramsModel(topProgramList); + } + } + + private static class TopProgramsModel extends AbstractTableModel { + // column headers for artifact counts table + private static final String[] TOP_PROGS_COLUMN_HEADERS = new String[]{ + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(), + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header() + }; + + private final List programResults; + + public TopProgramsModel(List programResults) { + this.programResults = programResults == null ? new ArrayList<>() : Collections.unmodifiableList(programResults); } - return artifactMapping.entrySet().stream() - .filter((entrySet) -> entrySet != null && entrySet.getKey() != null) - .sorted((a, b) -> -Long.compare(a.getValue(), b.getValue())) - .map((entrySet) -> new Object[]{entrySet.getKey(), entrySet.getValue()}) - .toArray(Object[][]::new); + @Override + public String getColumnName(int column) { + return column < 0 || column >= TOP_PROGS_COLUMN_HEADERS.length ? null : TOP_PROGS_COLUMN_HEADERS[column]; + } + + @Override + public int getRowCount() { + return programResults.size(); + } + + @Override + public int getColumnCount() { + return TOP_PROGS_COLUMN_HEADERS.length; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + if (rowIndex < 0 || rowIndex >= programResults.size()) { + return null; + } + + DataSourceInfoUtilities.TopProgramsResult result = programResults.get(rowIndex); + switch (columnIndex) { + case 0: return result.getProgramName(); + case 1: return DataSourceInfoUtilities.getShortFolderName(result.getProgramPath()); + case 2: return result.getRunTimes(); + default: return null; + } + } + } From 4d5e2362e75355129a59c750fbd8f63b7c9798e5 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 10:17:19 -0400 Subject: [PATCH 59/78] updated to 'hyperlink' and error handling updated --- .../modules/photoreccarver/Bundle.properties-MERGED | 3 ++- .../photoreccarver/PhotoRecCarverFileIngestModule.java | 10 ++++++++-- .../PhotoRecCarverIngestJobSettingsPanel.java | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED index a5a0625cdc..c107f8f0fb 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties-MERGED @@ -9,7 +9,8 @@ OpenIDE-Module-Short-Description=Carves unallocated space and feeds carved files moduleDisplayName.text=PhotoRec Carver moduleDescription.text=Runs PhotoRec carver against unallocated space in the data source. # {0} - extensions -PhotoRecCarverFileIngestModule_getSettings_invalidExtensions_description=The following extensions are invalid: {0} +PhotoRecCarverFileIngestModule_startUp_invalidExtensions_description=The following extensions are invalid: {0} +PhotoRecCarverFileIngestModule_startUp_noExtensionsProvided_description=No extensions provided for PhotoRec to carve. PhotoRecIngestModule.nonHostnameUNCPathUsed=PhotoRec cannot operate with a UNC path containing IP addresses. PhotoRecIngestModule.PermissionsNotSufficient=Insufficient permissions accessing PhotoRecIngestModule.PermissionsNotSufficientSeeReference=See 'Shared Drive Authentication' in Autopsy help. diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java index 4ff59ef0d6..eb8729c8c4 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java @@ -185,18 +185,24 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { @Override @NbBundle.Messages({ "# {0} - extensions", - "PhotoRecCarverFileIngestModule_getSettings_invalidExtensions_description=The following extensions are invalid: {0}" + "PhotoRecCarverFileIngestModule_startUp_invalidExtensions_description=The following extensions are invalid: {0}", + "PhotoRecCarverFileIngestModule_startUp_noExtensionsProvided_description=No extensions provided for PhotoRec to carve." }) public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException { // validate settings if (this.settings.hasFileOptOption()) { + if (this.settings.getIncludeExcludeExtensions().isEmpty() && this.settings.isIncludeElseExclude()) { + throw new IngestModule.IngestModuleException( + Bundle.PhotoRecCarverFileIngestModule_startUp_noExtensionsProvided_description()); + } + List invalidExtensions = this.settings.getIncludeExcludeExtensions().stream() .filter((ext) -> !PhotoRecCarverFileOptExtensions.isValidExtension(ext)) .collect(Collectors.toList()); if (!invalidExtensions.isEmpty()) { throw new IngestModule.IngestModuleException( - Bundle.PhotoRecCarverFileIngestModule_getSettings_invalidExtensions_description( + Bundle.PhotoRecCarverFileIngestModule_startUp_invalidExtensions_description( String.join(",", invalidExtensions))); } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java index 5d91124ff9..a0f72cd31c 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java @@ -85,7 +85,7 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe try { Desktop.getDesktop().browse(new URI(PHOTOREC_TYPES_URL)); } catch (IOException | URISyntaxException ex) { - logger.log(Level.WARNING, "There was an error going to types huperlink: " + PHOTOREC_TYPES_URL, ex); + logger.log(Level.WARNING, "There was an error going to types hyperlink: " + PHOTOREC_TYPES_URL, ex); } } }); From d79fa1f2a04321461458e753e14e0a935c4cdcb0 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 11:04:27 -0400 Subject: [PATCH 60/78] working through top programs data model --- .../Bundle.properties-MERGED | 8 +- .../DataSourceSummaryUserActivityPanel.java | 96 ++++++++++++++----- 2 files changed, 76 insertions(+), 28 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED index 4682d7d76f..40e5bf2386 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED @@ -61,9 +61,6 @@ DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category DataSourceSummaryCountsPanel.jLabel1.text=Results by Type -DataSourceSummaryDialog.countsTab.title=Counts -DataSourceSummaryDialog.detailsTab.title=Details -DataSourceSummaryDialog.ingestHistoryTab.title=Ingest History DataSourceSummaryDialog.window.title=Data Sources Summary DataSourceSummaryNode.column.dataSourceName.header=Data Source Name DataSourceSummaryNode.column.files.header=Files @@ -72,8 +69,13 @@ DataSourceSummaryNode.column.status.header=Ingest Status DataSourceSummaryNode.column.tags.header=Tags DataSourceSummaryNode.column.type.header=Type DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source +DataSourceSummaryTabbedPane_countsTab_title=Counts +DataSourceSummaryTabbedPane_detailsTab_title=Details +DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History +DataSourceSummaryTabbedPane_userActivityTab_title=User Activity DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run DataSourceSummaryUserActivityPanel_tab_title=User Activity DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times +DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program ViewSummaryInformationAction.name.text=View Summary Information diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index 1b0abc05fa..7b00d8f527 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -18,13 +18,15 @@ */ package org.sleuthkit.autopsy.casemodule.datasourcesummary; +import java.awt.Component; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; import javax.swing.JLabel; +import javax.swing.JTable; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.datamodel.DataSource; @@ -39,18 +41,16 @@ import org.sleuthkit.datamodel.DataSource; "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times" }) public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { - // Result returned for a data model if no data found. - private static final Object[][] EMPTY_PAIRS = new Object[][]{}; + private static final int TOP_PROGS_COUNT = 10; - private static final DefaultTableCellRenderer RIGHT_ALIGNED_RENDERER = new DefaultTableCellRenderer(); - + static { RIGHT_ALIGNED_RENDERER.setHorizontalAlignment(JLabel.RIGHT); } - + private DataSource dataSource; - + /** * Creates new form DataSourceUserActivityPanel */ @@ -81,18 +81,21 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { updateTopPrograms(getTopProgramsModel(dataSource)); } } - + /** * Updates the Top Programs Table in the gui. - * @param data The data in Object[][] form to be used by the DefaultTableModel. + * + * @param data The data in Object[][] form to be used by the + * DefaultTableModel. */ private void updateTopPrograms(TopProgramsModel model) { topProgramsTable.setModel(model); topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(230); - topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); + topProgramsTable.getColumnModel().getColumn(0).setCellRenderer(PATH_CELL_RENDERER); + topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); this.repaint(); } - + /** * The counts of top programs run. * @@ -101,24 +104,64 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { * @return The JTable data model of counts of program runs. */ private static TopProgramsModel getTopProgramsModel(DataSource selectedDataSource) { - List topProgramList = - DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); - + List topProgramList + = DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); + if (topProgramList == null) { return new TopProgramsModel(null); } else { return new TopProgramsModel(topProgramList); } } - + + private static class ProgramNameCellValue { + + private final String programName; + private final String programPath; + + public ProgramNameCellValue(String programName, String programPath) { + this.programName = programName; + this.programPath = programPath; + } + + @Override + public String toString() { + return programName; + } + + String getProgramName() { + return programName; + } + + String getProgramPath() { + return programPath; + } + } + + private static TableCellRenderer PATH_CELL_RENDERER = new DefaultTableCellRenderer() { + + public Component getTableCellRendererComponent( + JTable table, Object value, + boolean isSelected, boolean hasFocus, + int row, int column) { + JLabel c = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (value instanceof ProgramNameCellValue) { + ProgramNameCellValue cellValue = (ProgramNameCellValue) value; + c.setToolTipText(cellValue.getProgramPath()); + } + return c; + } + }; + private static class TopProgramsModel extends AbstractTableModel { + // column headers for artifact counts table private static final String[] TOP_PROGS_COLUMN_HEADERS = new String[]{ Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header(), Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header() }; - + private final List programResults; public TopProgramsModel(List programResults) { @@ -129,7 +172,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { public String getColumnName(int column) { return column < 0 || column >= TOP_PROGS_COLUMN_HEADERS.length ? null : TOP_PROGS_COLUMN_HEADERS[column]; } - + @Override public int getRowCount() { return programResults.size(); @@ -145,19 +188,22 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { if (rowIndex < 0 || rowIndex >= programResults.size()) { return null; } - + DataSourceInfoUtilities.TopProgramsResult result = programResults.get(rowIndex); switch (columnIndex) { - case 0: return result.getProgramName(); - case 1: return DataSourceInfoUtilities.getShortFolderName(result.getProgramPath()); - case 2: return result.getRunTimes(); - default: return null; + case 0: + return new ProgramNameCellValue(result.getProgramName(), result.getProgramPath()); + case 1: + return DataSourceInfoUtilities.getShortFolderName(result.getProgramPath()); + case 2: + return result.getRunTimes(); + default: + return null; } } - + } - - + /** * 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 From b700d17011697371c4db827140c967a8fb94502f Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 12:05:25 -0400 Subject: [PATCH 61/78] now with fixed paths and tooltips --- .../datasourcesummary/DataSourceInfoUtilities.java | 10 +++++++--- .../DataSourceSummaryUserActivityPanel.form | 11 +++++++---- .../DataSourceSummaryUserActivityPanel.java | 12 +++++++----- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index 7ca82753b0..92f0723af3 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -353,12 +353,16 @@ final class DataSourceInfoUtilities { private static List>> SHORT_FOLDER_MATCHERS = Arrays.asList( // Windows if in /Windows - Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?windows", Pattern.CASE_INSENSITIVE), (match) -> "Windows"), + Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?windows", Pattern.CASE_INSENSITIVE), + (match) -> "Windows"), + // program name if /Program Files/program or /Program Files (x86)/program - Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?Program Files( \\(x86\\))?[\\\\\\/](?.+?)[\\\\\\/]", + Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?Program Files( \\(x86\\))?[\\\\\\/](?.+?)([\\\\\\/]|$)", Pattern.CASE_INSENSITIVE), (match) -> match.group("programName")), + // match for an AppData folder - Pair.of(Pattern.compile("(^|[\\\\\\/])AppData([\\\\\\/]|$)"), (match) -> "AppData") + Pair.of(Pattern.compile("(^|[\\\\\\/])AppData([\\\\\\/]|$)", Pattern.CASE_INSENSITIVE), + (match) -> "AppData") ); static String getShortFolderName(String path) { diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form index 0ad3ca0349..08a49c17e4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form @@ -3,7 +3,10 @@
- + + + + @@ -25,9 +28,9 @@ - + - + @@ -58,7 +61,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index 7b00d8f527..e6befe9e9c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -92,7 +92,8 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { topProgramsTable.setModel(model); topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(230); topProgramsTable.getColumnModel().getColumn(0).setCellRenderer(PATH_CELL_RENDERER); - topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); + topProgramsTable.getColumnModel().getColumn(1).setPreferredWidth(150); + topProgramsTable.getColumnModel().getColumn(2).setCellRenderer(RIGHT_ALIGNED_RENDERER); this.repaint(); } @@ -217,11 +218,12 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { javax.swing.JScrollPane topProgramsScrollPane = new javax.swing.JScrollPane(); topProgramsTable = new javax.swing.JTable(); - setMinimumSize(new java.awt.Dimension(256, 300)); + setMinimumSize(null); + setPreferredSize(null); org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N - topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(290, 187)); + topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(460, 187)); topProgramsScrollPane.setViewportView(topProgramsTable); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); @@ -232,8 +234,8 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(programsRunLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(47, Short.MAX_VALUE)) + .addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 460, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(128, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) From f7985fbb0a7b422506a3975e0c3f8a2e362c4416 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 12:08:36 -0400 Subject: [PATCH 62/78] formatting --- .../datasourcesummary/DataSourceInfoUtilities.java | 12 +++++------- .../DataSourceSummaryTabbedPane.java | 12 ++++++------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index 92f0723af3..688bcb86c4 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -353,15 +353,13 @@ final class DataSourceInfoUtilities { private static List>> SHORT_FOLDER_MATCHERS = Arrays.asList( // Windows if in /Windows - Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?windows", Pattern.CASE_INSENSITIVE), + Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?windows", Pattern.CASE_INSENSITIVE), (match) -> "Windows"), - // program name if /Program Files/program or /Program Files (x86)/program - Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?Program Files( \\(x86\\))?[\\\\\\/](?.+?)([\\\\\\/]|$)", + Pair.of(Pattern.compile("^([A-Z]:)?[\\\\\\/]?Program Files( \\(x86\\))?[\\\\\\/](?.+?)([\\\\\\/]|$)", Pattern.CASE_INSENSITIVE), (match) -> match.group("programName")), - // match for an AppData folder - Pair.of(Pattern.compile("(^|[\\\\\\/])AppData([\\\\\\/]|$)", Pattern.CASE_INSENSITIVE), + Pair.of(Pattern.compile("(^|[\\\\\\/])AppData([\\\\\\/]|$)", Pattern.CASE_INSENSITIVE), (match) -> "AppData") ); @@ -369,7 +367,7 @@ final class DataSourceInfoUtilities { if (path == null) { return ""; } - + for (Pair> matchEntry : SHORT_FOLDER_MATCHERS) { Pattern p = matchEntry.getLeft(); Function resultsProvider = matchEntry.getRight(); @@ -378,7 +376,7 @@ final class DataSourceInfoUtilities { return resultsProvider.apply(match); } } - + return ""; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java index c112517acc..dddc4ee123 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java @@ -29,15 +29,15 @@ import org.sleuthkit.datamodel.DataSource; * IngestJobInfoPanel. */ @Messages({ - "DataSourceSummaryTabbedPane_countsTab_title=Counts", - "DataSourceSummaryTabbedPane_detailsTab_title=Details", - "DataSourceSummaryTabbedPane_userActivityTab_title=User Activity", - "DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History" + "DataSourceSummaryTabbedPane_countsTab_title=Counts", + "DataSourceSummaryTabbedPane_detailsTab_title=Details", + "DataSourceSummaryTabbedPane_userActivityTab_title=User Activity", + "DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History" }) public class DataSourceSummaryTabbedPane extends JTabbedPane { private static final long serialVersionUID = 1L; - + private final DataSourceSummaryCountsPanel countsPanel = new DataSourceSummaryCountsPanel(); private final DataSourceSummaryDetailsPanel detailsPanel = new DataSourceSummaryDetailsPanel(); private final DataSourceSummaryUserActivityPanel userActivityPanel = new DataSourceSummaryUserActivityPanel(); @@ -49,7 +49,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { * Constructs a tabbed pane showing the summary of a data source. */ public DataSourceSummaryTabbedPane() { - + addTab(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), detailsPanel); addTab(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), countsPanel); addTab(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), userActivityPanel); From 5ca2fa1931b45b9d1b3edbe664a0e1884ded389c Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 13:31:08 -0400 Subject: [PATCH 63/78] update to enum --- .../PhotoRecCarverFileIngestModule.java | 24 ++-- .../PhotoRecCarverIngestJobSettings.java | 106 ++++++++---------- .../PhotoRecCarverIngestJobSettingsPanel.java | 25 +++-- 3 files changed, 79 insertions(+), 76 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java index eb8729c8c4..c5e6c496ad 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java @@ -81,7 +81,9 @@ import org.sleuthkit.datamodel.TskData; final class PhotoRecCarverFileIngestModule implements FileIngestModule { static final boolean DEFAULT_CONFIG_KEEP_CORRUPTED_FILES = false; - static final boolean DEFAULT_CONFIG_FILE_OPT_OPTIONS = false; + static final PhotoRecCarverIngestJobSettings.ExtensionFilterOption DEFAULT_CONFIG_EXTENSION_FILTER = + PhotoRecCarverIngestJobSettings.ExtensionFilterOption.NO_FILTER; + static final boolean DEFAULT_CONFIG_INCLUDE_ELSE_EXCLUDE = false; private static final String PHOTOREC_DIRECTORY = "photorec_exec"; //NON-NLS @@ -139,7 +141,9 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { toRet.addAll(Arrays.asList("options", "keep_corrupted_file")); } - if (settings.hasFileOptOption()) { + if (settings.getExtensionFilterOption() != + PhotoRecCarverIngestJobSettings.ExtensionFilterOption.NO_FILTER) { + // add the file opt menu item toRet.add("fileopt"); @@ -148,15 +152,17 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { // if we are including file extensions, then we are excluding // everything else and vice-versa. - String everythingEnable = settings.isIncludeElseExclude() + String everythingEnable = settings.getExtensionFilterOption() == + PhotoRecCarverIngestJobSettings.ExtensionFilterOption.INCLUDE ? disable : enable; toRet.addAll(Arrays.asList("everything", everythingEnable)); - final String itemEnable = settings.isIncludeElseExclude() + final String itemEnable = settings.getExtensionFilterOption() == + PhotoRecCarverIngestJobSettings.ExtensionFilterOption.INCLUDE ? enable : disable; - settings.getIncludeExcludeExtensions().forEach((extension) -> { + settings.getExtensions().forEach((extension) -> { toRet.addAll(Arrays.asList(extension, itemEnable)); }); } @@ -190,13 +196,15 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { }) public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException { // validate settings - if (this.settings.hasFileOptOption()) { - if (this.settings.getIncludeExcludeExtensions().isEmpty() && this.settings.isIncludeElseExclude()) { + if (this.settings.getExtensionFilterOption() != PhotoRecCarverIngestJobSettings.ExtensionFilterOption.NO_FILTER) { + if (this.settings.getExtensions().isEmpty() && + this.settings.getExtensionFilterOption() == PhotoRecCarverIngestJobSettings.ExtensionFilterOption.INCLUDE) { + throw new IngestModule.IngestModuleException( Bundle.PhotoRecCarverFileIngestModule_startUp_noExtensionsProvided_description()); } - List invalidExtensions = this.settings.getIncludeExcludeExtensions().stream() + List invalidExtensions = this.settings.getExtensions().stream() .filter((ext) -> !PhotoRecCarverFileOptExtensions.isValidExtension(ext)) .collect(Collectors.toList()); diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java index 40e929cb75..38c2d49082 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java @@ -28,20 +28,37 @@ import java.util.List; */ final class PhotoRecCarverIngestJobSettings implements IngestModuleIngestJobSettings { + /** + * What kind of filtering should occur for the extension list. + */ + static enum ExtensionFilterOption { + /** + * The file extensions should be included (and others should be + * filtered). + */ + INCLUDE, + /** + * The extensions should be excluded from the results list. + */ + EXCLUDE, + /** + * No extension filtering should take place. + */ + NO_FILTER + }; + private static final long serialVersionUID = 1L; private boolean keepCorruptedFiles; - private List includeExcludeExtensions; - private boolean fileOptOption; - private boolean includeElseExclude; + private List extensions; + private ExtensionFilterOption extensionFilterOption; /** * Instantiate the ingest job settings with default values. */ PhotoRecCarverIngestJobSettings() { this(PhotoRecCarverFileIngestModule.DEFAULT_CONFIG_KEEP_CORRUPTED_FILES, - PhotoRecCarverFileIngestModule.DEFAULT_CONFIG_FILE_OPT_OPTIONS, - PhotoRecCarverFileIngestModule.DEFAULT_CONFIG_INCLUDE_ELSE_EXCLUDE, + PhotoRecCarverFileIngestModule.DEFAULT_CONFIG_EXTENSION_FILTER, null); } @@ -50,19 +67,16 @@ final class PhotoRecCarverIngestJobSettings implements IngestModuleIngestJobSett * * @param keepCorruptedFiles Whether or not to keep corrupted files. * @param fileOptOption Whether or not the file opt options - * should be enabled (whether or not to - * include/exclude file extensions). - * @param includeElseExclude If file opt options is enabled, whether - * to include only the extensions listed or - * exclude extensions from output. + * @param extensionFilterOption How the includeExcludeExtensions should + * be filtered. * @param includeExcludeExtensions The extensions to include or exclude * (i.e. jpg, gif) */ - PhotoRecCarverIngestJobSettings(boolean keepCorruptedFiles, boolean fileOptOption, boolean includeElseExclude, List includeExcludeExtensions) { + PhotoRecCarverIngestJobSettings(boolean keepCorruptedFiles, ExtensionFilterOption extensionFilterOption, List includeExcludeExtensions) { this.keepCorruptedFiles = keepCorruptedFiles; - this.fileOptOption = fileOptOption; - this.includeElseExclude = includeElseExclude; - setIncludeExcludeExtensions(includeExcludeExtensions); + setExtensionFilterOption(extensionFilterOption); + setExtensions(includeExcludeExtensions); + } @Override @@ -94,10 +108,10 @@ final class PhotoRecCarverIngestJobSettings implements IngestModuleIngestJobSett * * @return The extension names. */ - List getIncludeExcludeExtensions() { - return includeExcludeExtensions == null + List getExtensions() { + return extensions == null ? Collections.emptyList() - : Collections.unmodifiableList(includeExcludeExtensions); + : Collections.unmodifiableList(extensions); } /** @@ -106,58 +120,30 @@ final class PhotoRecCarverIngestJobSettings implements IngestModuleIngestJobSett * * @param includeExcludeExtensions The extension names. */ - void setIncludeExcludeExtensions(List includeExcludeExtensions) { - this.includeExcludeExtensions = new ArrayList<>(); + void setExtensions(List includeExcludeExtensions) { + this.extensions = new ArrayList<>(); if (includeExcludeExtensions != null) { - this.includeExcludeExtensions.addAll(includeExcludeExtensions); + this.extensions.addAll(includeExcludeExtensions); } } /** - * Returns whether or not the fileopt option (and subsequent file extension - * filtering) should be enabled. - * - * @return Whether or not the fileopt option (and subsequent file extension - * filtering) should be enabled. + * How extension filtering should be handled. + * @return How extension filtering should be handled. */ - boolean hasFileOptOption() { - return fileOptOption; + ExtensionFilterOption getExtensionFilterOption() { + return (this.extensionFilterOption == null) ? + ExtensionFilterOption.NO_FILTER : + extensionFilterOption; } /** - * Returns whether or not the fileopt option (and subsequent file extension - * filtering) should be enabled. - * - * @param fileOptOption Whether or not the fileopt option (and subsequent - * file extension filtering) should be enabled. + * Sets how extension filtering should be handled. + * @param extensionFilterOption How extension filtering should be handled. */ - void setFileOptOption(boolean fileOptOption) { - this.fileOptOption = fileOptOption; - } - - /** - * If the hasFileOptOption is true, this determines whether - * includeExcludeExtensions will be included in the results (excluding all - * others) or includeExcludeExtensions will be excluded from results - * (including all others). - * - * @return Whether to include or exclude includeExcludeExtensions. - */ - boolean isIncludeElseExclude() { - return includeElseExclude; - } - - /** - * Sets whether or not to include or exclude files. If the hasFileOptOption - * is true, this determines whether includeExcludeExtensions will be - * included in the results (excluding all others) or - * includeExcludeExtensions will be excluded from results (including all - * others). - * - * @param includeElseExclude Whether to include or exclude - * includeExcludeExtensions. - */ - void setIncludeElseExclude(boolean includeElseExclude) { - this.includeElseExclude = includeElseExclude; + void setExtensionFilterOption(ExtensionFilterOption extensionFilterOption) { + this.extensionFilterOption = (extensionFilterOption == null) ? + ExtensionFilterOption.NO_FILTER : + extensionFilterOption; } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java index a0f72cd31c..1efafafd24 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java @@ -62,10 +62,10 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe * @param settings The ingest job settings. */ private void customizeComponents(PhotoRecCarverIngestJobSettings settings) { - includeExcludeCheckbox.setSelected(settings.hasFileOptOption()); - extensionListTextfield.setText(String.join(EXTENSION_LIST_SEPARATOR, settings.getIncludeExcludeExtensions())); - includeRadioButton.setSelected(!settings.isIncludeElseExclude()); - excludeRadioButton.setSelected(!settings.isIncludeElseExclude()); + includeExcludeCheckbox.setSelected(settings.getExtensionFilterOption() != PhotoRecCarverIngestJobSettings.ExtensionFilterOption.NO_FILTER); + extensionListTextfield.setText(String.join(EXTENSION_LIST_SEPARATOR, settings.getExtensions())); + includeRadioButton.setSelected(settings.getExtensionFilterOption() == PhotoRecCarverIngestJobSettings.ExtensionFilterOption.INCLUDE); + excludeRadioButton.setSelected(settings.getExtensionFilterOption() == PhotoRecCarverIngestJobSettings.ExtensionFilterOption.EXCLUDE); keepCorruptedFilesCheckbox.setSelected(settings.isKeepCorruptedFiles()); setupTypesHyperlink(); setIncludePanelEnabled(); @@ -117,12 +117,21 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe @Override public IngestModuleIngestJobSettings getSettings() { - - + PhotoRecCarverIngestJobSettings.ExtensionFilterOption filterOption = + PhotoRecCarverIngestJobSettings.ExtensionFilterOption.NO_FILTER; + + if (includeExcludeCheckbox.isSelected()) { + if (includeRadioButton.isSelected()) { + filterOption = PhotoRecCarverIngestJobSettings.ExtensionFilterOption.INCLUDE; + } else { + filterOption = PhotoRecCarverIngestJobSettings.ExtensionFilterOption.EXCLUDE; + } + } + + return new PhotoRecCarverIngestJobSettings( keepCorruptedFilesCheckbox.isSelected(), - includeExcludeCheckbox.isSelected(), - includeRadioButton.isSelected(), + filterOption, getExtensions(extensionListTextfield.getText()) ); } From 658b603dd08b9598c92a0b87246c6ae585211e96 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 14:07:44 -0400 Subject: [PATCH 64/78] removes empty values --- .../localization_scripts/updatepropsscript.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/release_scripts/localization_scripts/updatepropsscript.py b/release_scripts/localization_scripts/updatepropsscript.py index 3d8489af82..8ba02d4664 100644 --- a/release_scripts/localization_scripts/updatepropsscript.py +++ b/release_scripts/localization_scripts/updatepropsscript.py @@ -131,7 +131,8 @@ def get_prop_entries(rows: List[List[str]], should_delete_converter: Callable[[List[str]], bool] = None, path_converter: Callable[[str], str] = None) -> Iterator[PropEntry]: - """Parses PropEntry objects from rows of values in a csv. + """Parses PropEntry objects from rows of values in a csv. Any items that have an empty string value will be + ignored. Args: rows (List[List[str]]): The csv file rows to parse. @@ -146,9 +147,11 @@ def get_prop_entries(rows: List[List[str]], Returns: List[PropEntry]: The generated prop entry objects. """ - return map(lambda row: get_prop_entry( - row, path_idx, key_idx, value_idx, should_delete_converter, path_converter), - rows) + propentry_iter = map(lambda row: get_prop_entry(row, path_idx, key_idx, value_idx, should_delete_converter, + path_converter), rows) + + # filter rows that have no value + return filter(lambda entry: entry and entry.value.strip(), propentry_iter) def get_should_deleted(row_items: List[str], requested_idx: int) -> bool: From b7ca09d64706ba69fdce638094b78bfc48058fe2 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 6 Aug 2020 15:00:39 -0400 Subject: [PATCH 65/78] address codacy remark --- .../modules/photoreccarver/PhotoRecCarverIngestJobSettings.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java index 38c2d49082..9ee56581f0 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java @@ -31,7 +31,7 @@ final class PhotoRecCarverIngestJobSettings implements IngestModuleIngestJobSett /** * What kind of filtering should occur for the extension list. */ - static enum ExtensionFilterOption { + enum ExtensionFilterOption { /** * The file extensions should be included (and others should be * filtered). From de3306076f1aa86427deea2f89b5c7a11c6b326e Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 6 Aug 2020 16:47:21 -0400 Subject: [PATCH 66/78] Added tool tips to hash, keyword and interesting items ingest panel --- .../relationships/Bundle.properties-MERGED | 2 +- .../HashLookupModuleSettingsPanel.java | 22 ++++++++++++++++++ ...FilesIdentifierIngestJobSettingsPanel.java | 22 ++++++++++++++++++ .../keywordsearch/Bundle.properties-MERGED | 2 +- .../KeywordSearchJobSettingsPanel.java | 23 +++++++++++++++++++ 5 files changed, 69 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED index 6778d8dc53..616408978d 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED @@ -73,5 +73,5 @@ SummaryViewer.referencesLabel.text=Communication References: SummaryViewer.referencesDataLabel.text= SummaryViewer.contactsLabel.text=Book Entries: SummaryViewer.accountCountry.text= -SummaryViewer.fileRefPane.border.title=File Referernce(s) in Current Case +SummaryViewer.fileRefPane.border.title=File References in Current Case SummaryViewer.selectAccountFileRefLabel.text=