diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoSettings.java index b7ec071f27390e9db817a84a14751cf7f1c051ae..d24b6245a6f8f8c8c546df5325d1f6c21c0f3f39 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoSettings.java @@ -44,6 +44,8 @@ public static CentralRepoSettings getInstance() { private static final String DEFAULT_DB_PARENT_PATH = Paths.get(CENTRAL_REPO_BASE_PATH, "LocalDatabase").toString(); private static final String DEFAULT_DB_NAME = "central_repository.db"; + + // NOTE: if this changes, an equivalent fix will be needed in CentralRepoDatamodelTest for the String PROPERTIES_FILE private static final String MODULE_SETTINGS_KEY = Paths.get( Paths.get(PlatformUtil.getUserConfigDirectory()).relativize(Paths.get(PlatformUtil.getModuleConfigDirectory())).toString(), CENTRAL_REPOSITORY_FOLDER, diff --git a/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineIngestManager.java b/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineIngestManager.java index 39aea28933fe877ea2140ee6183a800b49ecd856..ffc31fff1c0889b442abc380dbc6a3e1b79ef30e 100755 --- a/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineIngestManager.java +++ b/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineIngestManager.java @@ -56,6 +56,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleError; import org.sleuthkit.autopsy.ingest.IngestProfiles; import org.sleuthkit.autopsy.ingest.IngestProfiles.IngestProfile; +import org.sleuthkit.autopsy.ingest.profile.IngestProfilePaths; import org.sleuthkit.autopsy.modules.interestingitems.FilesSet; import org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager; import org.sleuthkit.autopsy.report.infrastructure.ReportGenerator; @@ -75,9 +76,9 @@ public class CommandLineIngestManager extends CommandLineManager { private Case caseForJob = null; private AutoIngestDataSource dataSource = null; - static int CL_SUCCESS = 0; - static int CL_RUN_FAILURE = -1; - static int CL_PROCESS_FAILURE = 1; + static final int CL_SUCCESS = 0; + static final int CL_RUN_FAILURE = -1; + static final int CL_PROCESS_FAILURE = -2; public CommandLineIngestManager() { } @@ -260,7 +261,7 @@ public void run() { // run ingest String ingestProfile = inputs.get(CommandLineCommand.InputType.INGEST_PROFILE_NAME.name()); analyze(dataSource, ingestProfile); - } catch (InterruptedException | CaseActionException ex) { + } catch (InterruptedException | CaseActionException | AnalysisStartupException ex) { String dataSourcePath = command.getInputs().get(CommandLineCommand.InputType.DATA_SOURCE_PATH.name()); LOGGER.log(Level.SEVERE, "Error running ingest on data source " + dataSourcePath, ex); System.out.println("Error running ingest on data source " + dataSourcePath); @@ -520,7 +521,7 @@ private void analyze(AutoIngestDataSource dataSource, String ingestProfileName) // unable to find the user specified profile LOGGER.log(Level.SEVERE, "Unable to find ingest profile: {0}. Ingest cancelled!", ingestProfileName); System.out.println("Unable to find ingest profile: " + ingestProfileName + ". Ingest cancelled!"); - return; + throw new AnalysisStartupException("Unable to find ingest profile: " + ingestProfileName + ". Ingest cancelled!"); } // get FileSet filter associated with this profile @@ -529,7 +530,7 @@ private void analyze(AutoIngestDataSource dataSource, String ingestProfileName) // unable to find the user specified profile LOGGER.log(Level.SEVERE, "Unable to find file filter {0} for ingest profile: {1}. Ingest cancelled!", new Object[]{selectedProfile.getFileIngestFilter(), ingestProfileName}); System.out.println("Unable to find file filter " + selectedProfile.getFileIngestFilter() + " for ingest profile: " + ingestProfileName + ". Ingest cancelled!"); - return; + throw new AnalysisStartupException("Unable to find file filter " + selectedProfile.getFileIngestFilter() + " for ingest profile: " + ingestProfileName + ". Ingest cancelled!"); } } @@ -543,7 +544,7 @@ private void analyze(AutoIngestDataSource dataSource, String ingestProfileName) ingestJobSettings = new IngestJobSettings(UserPreferences.getCommandLineModeIngestModuleContextString()); } else { // load the custom ingest - ingestJobSettings = new IngestJobSettings(selectedProfile.toString()); + ingestJobSettings = new IngestJobSettings(IngestProfilePaths.getInstance().getIngestProfilePrefix() + selectedProfile.toString()); ingestJobSettings.setFileFilter(selectedFileSet); } diff --git a/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineOptionProcessor.java b/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineOptionProcessor.java index 0bb42295a042d2000d5137ea72914c5081aaf44b..921c2e0a6590ee84850f3c42fff2aadcdbcf4735 100755 --- a/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineOptionProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineOptionProcessor.java @@ -271,6 +271,7 @@ protected void process(Env env, Map<Option, String[]> values) throws CommandExce newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir); newCommand.addInputValue(CommandLineCommand.InputType.DATA_SOURCE_ID.name(), dataSourceId); newCommand.addInputValue(CommandLineCommand.InputType.INGEST_PROFILE_NAME.name(), ingestProfile); + newCommand.addInputValue(CommandLineCommand.InputType.DATA_SOURCE_PATH.name(), dataSourcePath); commands.add(newCommand); runFromCommandLine(true); } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java index 85052e0aedef6f4ba381a0478454a2dda701e160..a6b5f897dd1e1649721383a3e63fb093788364ae 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java @@ -199,7 +199,7 @@ public IngestJobSettings(String executionContext, IngestType ingestType, Collect public Path getSavedModuleSettingsFolder() { return getSavedModuleSettingsFolder(executionContext); } - + /** * Saves these ingest job settings. */ diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestProfiles.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestProfiles.java index a821f418de4a05166e38c9765e7c92f99f23c64f..2bca1c810c197fa43008675e7fd5d2bcc23b72e4 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestProfiles.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestProfiles.java @@ -202,15 +202,24 @@ public String getFileIngestFilter() { * @param selectedProfile */ synchronized static void deleteProfile(IngestProfile selectedProfile) { + deleteProfile(selectedProfile.getName()); + } + + /** + * Deletes all of the files which are currently storing a profile. + * + * @param profile name + */ + synchronized static void deleteProfile(String profileName) { try { - File rootSettingsFile = getRootSettingsFile(selectedProfile.getName()); - File settingsDirectory = getSettingsDirectory(selectedProfile.getName()); + File rootSettingsFile = getRootSettingsFile(profileName); + File settingsDirectory = getSettingsDirectory(profileName); Files.deleteIfExists(rootSettingsFile.toPath()); FileUtils.deleteDirectory(settingsDirectory); } catch (IOException ex) { - logger.log(Level.WARNING, "Error deleting directory for profile " + selectedProfile.getName(), ex); + logger.log(Level.WARNING, "Error deleting directory for profile " + profileName, ex); } - } + } /** * Renames the files and directories associated with a profile diff --git a/Core/src/org/sleuthkit/autopsy/ingest/ProfilePanel.java b/Core/src/org/sleuthkit/autopsy/ingest/ProfilePanel.java index 826ca6c2c94577ce889a6a4e4bdb415f2d8d429a..3f00cc048b58ddce555e81905e253182c8787780 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/ProfilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/ProfilePanel.java @@ -85,7 +85,15 @@ String getProfileDesc() { IngestJobSettings getSettings() { return ingestSettingsPanel.getSettings(); } - + + String getIngestProfileName() { + if (profile != null) { + return profile.getName(); + } else { + return NEW_PROFILE_NAME; + } + } + /** * 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 diff --git a/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java index b341b287525fbff54b7120f669c2d70757e722d2..a28a5e6aa8f5a63a9c1c667d15693e101fcc308f 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/ProfileSettingsPanel.java @@ -391,7 +391,7 @@ private void editProfileButtonActionPerformed(java.awt.event.ActionEvent evt) {/ */ private void doProfileDialog(IngestProfile selectedProfile) { // Create a files set defintion panel. - final AdvancedConfigurationDialog dialog = new AdvancedConfigurationDialog(true); + final AdvancedConfigurationDialog dialog = new AdvancedConfigurationDialog(true); this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); //start wait cursor for ingest job settings construction if (selectedProfile != null) { @@ -430,6 +430,15 @@ private void doProfileDialog(IngestProfile selectedProfile) { } panel.saveSettings(); load(); + } else if (option == JOptionPane.CANCEL_OPTION) { + if (selectedProfile == null) { + // for new profiles, if user canlessed a profile create/edit then delete the temp empty profile that was created. + // Otherwise it will remain in "config/ModuleSettings/IngestSettings" and then will get loaded + // next time we open ProfilePanel(), causing an NPE (JIRA-8404). This only needs to be done when creating + // a new ingest profile. If user cancelled editing of an existing profile, then we should not delete + // that profile. + IngestProfile.deleteProfile(panel.getIngestProfileName()); + } } } @@ -479,7 +488,12 @@ public void valueChanged(ListSelectionEvent e) { for (FilesSet fSet : FilesSetsManager.getStandardFileIngestFilters()) { fileIngestFilters.put(fSet.getName(), fSet); } - filterDescArea.setText(fileIngestFilters.get(selectedProfile.getFileIngestFilter()).getDescription()); + String selectedFilter = selectedProfile.getFileIngestFilter(); + if (selectedFilter == null) { + filterDescArea.setText(NbBundle.getMessage(ProfileSettingsPanel.class, "ProfileSettingsPanel.messages.filterLoadFailed")); + } else { + filterDescArea.setText(fileIngestFilters.get(selectedFilter).getDescription()); + } } catch (FilesSetsManager.FilesSetsManagerException ex) { filterDescArea.setText(NbBundle.getMessage(ProfileSettingsPanel.class, "ProfileSettingsPanel.messages.filterLoadFailed")); } diff --git a/Core/src/org/sleuthkit/autopsy/python/JythonModuleLoader.java b/Core/src/org/sleuthkit/autopsy/python/JythonModuleLoader.java index dab8969c94497372c61737fd27851552cd405090..beb3e712070733d0b0843cb0bac785d262d911d2 100644 --- a/Core/src/org/sleuthkit/autopsy/python/JythonModuleLoader.java +++ b/Core/src/org/sleuthkit/autopsy/python/JythonModuleLoader.java @@ -43,6 +43,7 @@ import org.sleuthkit.autopsy.report.GeneralReportModule; import java.io.BufferedReader; import java.io.FileReader; +import java.util.Comparator; /** * Finds and loads Autopsy modules written using the Jython variant of the @@ -124,6 +125,8 @@ private static <T> List<T> getInterfaceImplementations(LineFilter filter, Class< } } } + + Collections.sort(objects, Comparator.comparing((T obj) -> obj.getClass().getSimpleName(), (s1, s2) -> s1.compareToIgnoreCase(s2))); return objects; } diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportGenerationException.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportGenerationException.java new file mode 100755 index 0000000000000000000000000000000000000000..4002abc1e10c751a8a60c26104e3d91a7c2b8200 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportGenerationException.java @@ -0,0 +1,47 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2022 Basis Technology Corp. + * Contact: carrier <at> sleuthkit <dot> org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.report.infrastructure; + +/** + * Instances of this exception class are thrown when there is an error while + * generating a report. + */ +public final class ReportGenerationException extends Exception { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a new exception with the specified message. + * + * @param message The message. + */ + public ReportGenerationException(String message) { + super(message); + } + + /** + * Constructs a new exception with the specified message and cause. + * + * @param message The message. + * @param cause The cause. + */ + public ReportGenerationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportGenerator.java index 3894af6e2e7c1ca61a15ba8c1d1750d2b0ff7446..12b2a1e4bc583962f277082ee0245eb2ebe75519 100644 --- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportGenerator.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2020 Basis Technology Corp. + * Copyright 2013-2022 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -107,7 +107,7 @@ public ReportGenerator(String configName, ReportProgressIndicator progressIndica this.reportGenerationPanel = null; this.configName = configName; } - + /** * Constructs a report generator that generates one or more reports by * running user-selected report modules and uses a report generation panel @@ -121,12 +121,15 @@ public ReportGenerator(String configName, ReportProgressIndicator progressIndica this.progressIndicator = panel.getProgressPanel(); this.configName = configName; } - + /** - * Generates the reports specified by the reporting configuration passed - * in via the constructor. Does lookup of all existing report modules. + * Generates the reports specified by the reporting configuration passed in + * via the constructor. Does lookup of all existing report modules. + * + * @throws ReportGenerationException if an error occurred while generating + * the report. */ - public void generateReports() { + public void generateReports() throws ReportGenerationException { // load all report modules Map<String, ReportModule> modules = new HashMap<>(); for (TableReportModule module : ReportModuleLoader.getTableReportModules()) { @@ -143,38 +146,51 @@ public void generateReports() { // special case for PortableCaseReportModule modules.put(FactoryClassNameNormalizer.normalize(PortableCaseReportModule.class.getCanonicalName()), new PortableCaseReportModule()); - + generateReports(modules); } + @NbBundle.Messages({ + "ReportGenerator.error.noReportModules=No report modules found", + "# {0} - report configuration name", "ReportGenerator.error.unableToLoadConfig=Unable to load reporting configuration {0}.", + "# {0} - report module name", "ReportGenerator.error.moduleNotFound=Report module {0} not found", + "# {0} - report module name", "ReportGenerator.error.noTableReportSettings=No table report settings for report module {0}", + "# {0} - report module name", "ReportGenerator.error.noFileReportSettings=No file report settings for report module {0}", + "# {0} - report module name", "ReportGenerator.error.invalidSettings=Invalid settings for report module {0}", + "# {0} - report module name", "ReportGenerator.error.unsupportedType=Report module {0} has unsupported report module type", + "# {0} - report module name", "ReportGenerator.error.exception=Exception while running report module {0}"}) /** * Generates the reports specified by the reporting configuration passed in - * via the constructor. - * - * @param modules Map of report module objects to use. This is useful when we want to - * re-use the module instances or limit which reports are generated. + * via the constructor. + * + * @param modules Map of report module objects to use. This is useful when + * we want to re-use the module instances or limit which + * reports are generated. + * + * @throws ReportGenerationException if an error occurred while generating + * the report. */ - public void generateReports(Map<String, ReportModule> modules) { - + public void generateReports(Map<String, ReportModule> modules) throws ReportGenerationException { + if (modules == null || modules.isEmpty()) { - logger.log(Level.SEVERE, "No report modules found"); - progressIndicator.updateStatusLabel("No report modules found. Exiting"); - return; + logger.log(Level.SEVERE, Bundle.ReportGenerator_error_noReportModules()); + progressIndicator.updateStatusLabel(Bundle.ReportGenerator_error_noReportModules()); + throw new ReportGenerationException(Bundle.ReportGenerator_error_noReportModules()); } - + ReportingConfig config = null; try { config = ReportingConfigLoader.loadConfig(configName); } catch (ReportConfigException ex) { - logger.log(Level.SEVERE, "Unable to load reporting configuration " + configName + ". Exiting", ex); - progressIndicator.updateStatusLabel("Unable to load reporting configuration " + configName + ". Exiting"); - return; + logger.log(Level.SEVERE, Bundle.ReportGenerator_error_unableToLoadConfig(configName), ex); + progressIndicator.updateStatusLabel(Bundle.ReportGenerator_error_unableToLoadConfig(configName)); + throw new ReportGenerationException(Bundle.ReportGenerator_error_unableToLoadConfig(configName)); } if (config == null) { - logger.log(Level.SEVERE, "Unable to load reporting configuration {0}. Exiting", configName); - progressIndicator.updateStatusLabel("Unable to load reporting configuration " + configName + ". Exiting"); - return; + logger.log(Level.SEVERE, Bundle.ReportGenerator_error_unableToLoadConfig(configName)); + progressIndicator.updateStatusLabel(Bundle.ReportGenerator_error_unableToLoadConfig(configName)); + throw new ReportGenerationException(Bundle.ReportGenerator_error_unableToLoadConfig(configName)); } try { @@ -189,8 +205,8 @@ public void generateReports(Map<String, ReportModule> modules) { String moduleName = entry.getKey(); ReportModule module = modules.get(moduleName); if (module == null) { - logger.log(Level.SEVERE, "Report module {0} not found", moduleName); - progressIndicator.updateStatusLabel("Report module " + moduleName + " not found"); + logger.log(Level.SEVERE, Bundle.ReportGenerator_error_moduleNotFound(moduleName)); + progressIndicator.updateStatusLabel(Bundle.ReportGenerator_error_moduleNotFound(moduleName)); continue; } @@ -216,8 +232,8 @@ public void generateReports(Map<String, ReportModule> modules) { // get table report settings TableReportSettings tableSettings = config.getTableReportSettings(); if (tableSettings == null) { - logger.log(Level.SEVERE, "No table report settings for report module {0}", moduleName); - progressIndicator.updateStatusLabel("No table report settings for report module " + moduleName); + logger.log(Level.SEVERE, Bundle.ReportGenerator_error_noTableReportSettings(moduleName)); + progressIndicator.updateStatusLabel(Bundle.ReportGenerator_error_noTableReportSettings(moduleName)); continue; } @@ -228,8 +244,8 @@ public void generateReports(Map<String, ReportModule> modules) { // get file report settings FileReportSettings fileSettings = config.getFileReportSettings(); if (fileSettings == null) { - logger.log(Level.SEVERE, "No file report settings for report module {0}", moduleName); - progressIndicator.updateStatusLabel("No file report settings for report module " + moduleName); + logger.log(Level.SEVERE, Bundle.ReportGenerator_error_noFileReportSettings(moduleName)); + progressIndicator.updateStatusLabel(Bundle.ReportGenerator_error_noFileReportSettings(moduleName)); continue; } @@ -240,20 +256,20 @@ public void generateReports(Map<String, ReportModule> modules) { if (settings instanceof NoReportModuleSettings) { settings = new PortableCaseReportModuleSettings(); } else if (!(settings instanceof PortableCaseReportModuleSettings)) { - logger.log(Level.SEVERE, "Invalid settings for report module {0}", moduleName); - progressIndicator.updateStatusLabel("Invalid settings for report module " + moduleName); + logger.log(Level.SEVERE, Bundle.ReportGenerator_error_invalidSettings(moduleName)); + progressIndicator.updateStatusLabel(Bundle.ReportGenerator_error_invalidSettings(moduleName)); continue; } generatePortableCaseReport((PortableCaseReportModule) module, (PortableCaseReportModuleSettings) settings); } else { - logger.log(Level.SEVERE, "Report module {0} has unsupported report module type", moduleName); - progressIndicator.updateStatusLabel("Report module " + moduleName + " has unsupported report module type"); + logger.log(Level.SEVERE, Bundle.ReportGenerator_error_unsupportedType(moduleName)); + progressIndicator.updateStatusLabel(Bundle.ReportGenerator_error_unsupportedType(moduleName)); } } catch (IOException e) { - logger.log(Level.SEVERE, "Exception while running report module {0}: {1}", new Object[]{moduleName, e.getMessage()}); - progressIndicator.updateStatusLabel("Exception while running report module " + moduleName); + logger.log(Level.SEVERE, Bundle.ReportGenerator_error_exception(moduleName)); + progressIndicator.updateStatusLabel(Bundle.ReportGenerator_error_exception(moduleName)); } } } finally { @@ -321,15 +337,15 @@ private void generateTableReport(TableReportModule tableReport, TableReportSetti TableReportGenerator generator = new TableReportGenerator(tableReportSettings, progressIndicator, tableReport); generator.execute(); tableReport.endReport(); - + // finish progress, wrap up errorList = generator.getErrorList(); - + // if error list is empty, the operation has completed successfully. If not there is an error - ReportProgressPanel.ReportStatus finalStatus = (errorList == null || errorList.isEmpty()) ? - ReportProgressPanel.ReportStatus.COMPLETE : - ReportProgressPanel.ReportStatus.ERROR; - + ReportProgressPanel.ReportStatus finalStatus = (errorList == null || errorList.isEmpty()) + ? ReportProgressPanel.ReportStatus.COMPLETE + : ReportProgressPanel.ReportStatus.ERROR; + progressIndicator.complete(finalStatus); } } @@ -368,10 +384,10 @@ private void generateFileListReport(FileReportModule fileReportModule, FileRepor int i = 0; // Add files to report. for (AbstractFile file : files) { - if(shouldFilterFromReport(file, fileReportSettings)) { + if (shouldFilterFromReport(file, fileReportSettings)) { continue; } - + // Check to see if any reports have been cancelled. if (progressIndicator.getStatus() == ReportStatus.CANCELED) { return; @@ -393,9 +409,9 @@ private void generateFileListReport(FileReportModule fileReportModule, FileRepor progressIndicator.complete(ReportStatus.COMPLETE); } } - + private boolean shouldFilterFromReport(AbstractFile file, FileReportSettings fileReportSettings) { - if(fileReportSettings.getSelectedDataSources() == null) { + if (fileReportSettings.getSelectedDataSources() == null) { return false; } // Filter if the data source id is not in the list to process diff --git a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java index f2c419eee7fad77078220970d0e00111b88b1400..c15a5ceef90bdbd46471b4e713bf1f0b2b496278 100644 --- a/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java +++ b/Core/src/org/sleuthkit/autopsy/report/infrastructure/ReportWizardAction.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2012-2020 Basis Technology Corp. + * Copyright 2012-2022 Basis Technology Corp. * * Copyright 2012 42six Solutions. * Contact: aebadirad <at> 42six <dot> com @@ -108,7 +108,12 @@ public static void doReportWizard(String configName, boolean displayCaseSpecific Map<String, ReportModule> modules = (Map<String, ReportModule>) wiz.getProperty("modules"); ReportGenerator generator = new ReportGenerator(configName, panel); //NON-NLS ReportWorker worker = new ReportWorker(() -> { - generator.generateReports(modules); + try { + generator.generateReports(modules); + } catch (ReportGenerationException ex) { + // do nothing. the error message will be logged and + // displayed by the progress panel. + } }); worker.execute(); generator.displayProgressPanel(); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java index d4b188d7db59d212f58637712f19a1aa4a53bf27..c2c122f1fab940122f378c88cbef2e7b8baae509 100755 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java @@ -43,7 +43,6 @@ import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.centralrepository.CentralRepoSettings; import org.sleuthkit.autopsy.coreutils.FileUtil; /** @@ -57,7 +56,10 @@ */ public class CentralRepoDatamodelTest extends TestCase { - private static final String PROPERTIES_FILE = CentralRepoSettings.getInstance().getModuleSettingsKey(); + // Classloader for qa functional tests is having trouble with loading NbBundle. + // Path is hard-coded to avoid that issue instead of using + // CentralRepoSettings.getInstance().getModuleSettingsKey() + private static final String PROPERTIES_FILE = "ModuleConfig/CentralRepository/CentralRepository"; private static final String CR_DB_NAME = "testcentralrepo.db"; private static final Path testDirectory = Paths.get(System.getProperty("java.io.tmpdir"), "CentralRepoDatamodelTest"); diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties index 602dc5b0a07aa4352f7a1374e828a45a07495219..b9a385e3b8b3c62f4a5658e59e810202e58dda81 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties @@ -3,6 +3,14 @@ OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\n The module ex OpenIDE-Module-Name=RecentActivity OpenIDE-Module-Short-Description=Recent Activity finder ingest module Chrome.moduleName=Chromium Analyzer +Chrome.getLocalState.errMsg.errGettingFiles=Error when trying to get Local State file. +Chrome.getLocalState.errMsg.couldntFindAnyFiles=Could not find any allocated Chrome Local State files. +Chrome.getLocalState.errMsg.errAnalyzingFile={0}: Error while trying to analyze file:{1} +Chrome.getLocalState.displayName=Chromium Profiles +Chrome.getExtensions.errMsg.errGettingFiles=Error when trying to get Secure Preferences file. +Chrome.getExtensions.errMsg.couldntFindAnyFiles=Could not find any allocated Chrome Secure Preferences file. +Chrome.getExtensions.errMsg.errAnalyzingFile={0}: Error while trying to analyze file:{1} +Chrome.getExtensions.displayName=Chromium Extensions Chrome.getHistory.errMsg.errGettingFiles=Error when trying to get Chrome history files. Chrome.getHistory.errMsg.couldntFindAnyFiles=Could not find any allocated Chrome history files. Chrome.getHistory.errMsg.errAnalyzingFile={0}: Error while trying to analyze file:{1} @@ -15,6 +23,10 @@ Chrome.getCookie.errMsg.errGettingFiles=Error when trying to get Chrome history Chrome.getCookie.errMsg.errAnalyzeFile={0}: Error while trying to analyze file:{1} Chrome.getDownload.errMsg.errGettingFiles=Error when trying to get Chrome history files. Chrome.getDownload.errMsg.errAnalyzeFiles1={0}: Error while trying to analyze file:{1} +Chrome.getFavicon.errMsg.errGettingFiles=Error when trying to get Chrome favicon files. +Chrome.getFavicon.errMsg.errAnalyzeFiles1={0}: Error while trying to analyze file:{1} +Chrome.getFavicon.errMsg.errCreateArtifact=Error creating Favicon artifact +Chrome.getFavicon.displayName=Favicon Chrome.getLogin.errMsg.errGettingFiles=Error when trying to get Chrome history files. Chrome.getLogin.errMsg.errAnalyzingFiles={0}: Error while trying to analyze file:{1} Chrome.getAutofill.errMsg.errGettingFiles=Error when trying to get Chrome Web Data files. diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED index c73bf456d5b0d22afe493e3b1ebb70f39b322c03..43b84b5fe6d5b7c1862b1d49dbb8ecd90b1e641d 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED @@ -12,7 +12,6 @@ ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries 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.displayName=Data Source Usage Analyzer DefaultPriorityDomainCategorizer_searchEngineCategory=Search Engine @@ -25,7 +24,7 @@ ExtractEdge_process_errMsg_errGettingWebCacheFiles=Error trying to retrieving Ed ExtractEdge_process_errMsg_spartanFail=Failure processing Microsoft Edge spartan.edb file ExtractEdge_process_errMsg_unableFindESEViewer=Unable to find ESEDatabaseViewer ExtractEdge_process_errMsg_webcacheFail=Failure processing Microsoft Edge WebCacheV01.dat file -# {0} - sub module name +ExtractFavicon_Display_Name=Favicon ExtractIE_executePasco_errMsg_errorRunningPasco={0}: Error analyzing Internet Explorer web history ExtractOs.androidOs.label=Android ExtractOs.androidVolume.label=OS Drive (Android) @@ -58,7 +57,6 @@ ExtractOs.windowsVolume.label=OS Drive (Windows) ExtractOs.yellowDogLinuxOs.label=Linux (Yellow Dog) ExtractOs.yellowDogLinuxVolume.label=OS Drive (Linux Yellow Dog) ExtractOS_progressMessage=Checking for OS -# {0} - sub module name ExtractPrefetch_errMsg_prefetchParsingFailed={0}: Error analyzing prefetch files ExtractPrefetch_module_name=Windows Prefetch Analyzer ExtractRecycleBin_module_name=Recycle Bin Analyzer @@ -93,6 +91,14 @@ OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\n The module ex OpenIDE-Module-Name=RecentActivity OpenIDE-Module-Short-Description=Recent Activity finder ingest module Chrome.moduleName=Chromium Analyzer +Chrome.getLocalState.errMsg.errGettingFiles=Error when trying to get Local State file. +Chrome.getLocalState.errMsg.couldntFindAnyFiles=Could not find any allocated Chrome Local State files. +Chrome.getLocalState.errMsg.errAnalyzingFile={0}: Error while trying to analyze file:{1} +Chrome.getLocalState.displayName=Chromium Profiles +Chrome.getExtensions.errMsg.errGettingFiles=Error when trying to get Secure Preferences file. +Chrome.getExtensions.errMsg.couldntFindAnyFiles=Could not find any allocated Chrome Secure Preferences file. +Chrome.getExtensions.errMsg.errAnalyzingFile={0}: Error while trying to analyze file:{1} +Chrome.getExtensions.displayName=Chromium Extensions Chrome.getHistory.errMsg.errGettingFiles=Error when trying to get Chrome history files. Chrome.getHistory.errMsg.couldntFindAnyFiles=Could not find any allocated Chrome history files. Chrome.getHistory.errMsg.errAnalyzingFile={0}: Error while trying to analyze file:{1} @@ -105,6 +111,10 @@ Chrome.getCookie.errMsg.errGettingFiles=Error when trying to get Chrome history Chrome.getCookie.errMsg.errAnalyzeFile={0}: Error while trying to analyze file:{1} Chrome.getDownload.errMsg.errGettingFiles=Error when trying to get Chrome history files. Chrome.getDownload.errMsg.errAnalyzeFiles1={0}: Error while trying to analyze file:{1} +Chrome.getFavicon.errMsg.errGettingFiles=Error when trying to get Chrome favicon files. +Chrome.getFavicon.errMsg.errAnalyzeFiles1={0}: Error while trying to analyze file:{1} +Chrome.getFavicon.errMsg.errCreateArtifact=Error creating Favicon artifact +Chrome.getFavicon.displayName=Favicon Chrome.getLogin.errMsg.errGettingFiles=Error when trying to get Chrome history files. Chrome.getLogin.errMsg.errAnalyzingFiles={0}: Error while trying to analyze file:{1} Chrome.getAutofill.errMsg.errGettingFiles=Error when trying to get Chrome Web Data files. @@ -166,11 +176,14 @@ Progress_Message_Chrome_Cache=Chrome Cache Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0} # {0} - browserName Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0} +Progress_Message_Chrome_Extensions=Chrome Extensions {0} +Progress_Message_Chrome_Favicons=Chrome Downloads Favicons {0} Progress_Message_Chrome_FormHistory=Chrome Form History # {0} - browserName Progress_Message_Chrome_History=Chrome History Browser {0} # {0} - browserName Progress_Message_Chrome_Logins=Chrome Logins Browser {0} +Progress_Message_Chrome_Profiles=Chrome Profiles {0} Progress_Message_Edge_Bookmarks=Microsoft Edge Bookmarks Progress_Message_Edge_Cookies=Microsoft Edge Cookies Progress_Message_Edge_History=Microsoft Edge History @@ -225,7 +238,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 Query Analyzer SearchEngineURLQueryAnalyzer.engineName.none=NONE diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chromium.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chromium.java index 1d22f930b9394708b90225aa554f76241361dd7f..41eefc21f8ad572d3cb315191bfc786b323d7210 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chromium.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chromium.java @@ -42,6 +42,7 @@ import java.util.HashMap; import java.util.ArrayList; import java.util.Arrays; +import java.util.Set; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle.Messages; @@ -86,13 +87,24 @@ class Chromium extends Extract { private static final String WEBFORM_ADDRESS_QUERY_V8X = "SELECT first_name, middle_name, last_name, full_name, street_address, city, state, zipcode, country_code, number, email, date_modified, use_date, use_count" + " FROM autofill_profiles, autofill_profile_names, autofill_profile_emails, autofill_profile_phones" + " WHERE autofill_profiles.guid = autofill_profile_names.guid AND autofill_profiles.guid = autofill_profile_emails.guid AND autofill_profiles.guid = autofill_profile_phones.guid"; + private static final String FAVICON_QUERY = "SELECT page_url, last_updated, last_requested FROM icon_mapping, favicon_bitmaps " + + " WHERE icon_mapping.icon_id = favicon_bitmaps.icon_id"; + private static final String LOCALSTATE_FILE_NAME = "Local State"; + private static final String EXTENSIONS_FILE_NAME = "Secure Preferences"; private static final String HISTORY_FILE_NAME = "History"; private static final String BOOKMARK_FILE_NAME = "Bookmarks"; private static final String COOKIE_FILE_NAME = "Cookies"; private static final String LOGIN_DATA_FILE_NAME = "Login Data"; private static final String WEB_DATA_FILE_NAME = "Web Data"; + private static final String FAVICON_DATA_FILE_NAME = "Favicons"; private static final String UC_BROWSER_NAME = "UC Browser"; + private static final String OPERA_BROWSER_NAME = "Opera"; private static final String ENCRYPTED_FIELD_MESSAGE = "The data was encrypted."; + private static final String GOOGLE_PROFILE_NAME = "Profile"; + private static final String GOOGLE_PROFILE = "Google Chrome "; + private static final String FAVICON_ARTIFACT_NAME = "TSK_FAVICON"; //NON-NLS + private static final String LOCAL_STATE_ARTIFACT_NAME = "TSK_LOCAL_STATE"; //NON-NLS + private static final String EXTENSIONS_ARTIFACT_NAME = "TSK_CHROME_EXTENSIONS"; //NON-NLS private Boolean databaseEncrypted = false; private Boolean fieldEncrypted = false; @@ -101,14 +113,17 @@ class Chromium extends Extract { private Content dataSource; private final IngestJobContext context; + private Map<String, String> userProfiles; + private Map<String, String> browserLocations; + private static final Map<String, String> BROWSERS_MAP = ImmutableMap.<String, String>builder() - .put("Microsoft Edge", "Microsoft/Edge/User Data/Default") - .put("Yandex", "YandexBrowser/User Data/Default") + .put("Microsoft Edge", "Microsoft/Edge/User Data") + .put("Yandex", "YandexBrowser/User Data") .put("Opera", "Opera Software/Opera Stable") - .put("SalamWeb", "SalamWeb/User Data/Default") - .put("UC Browser", "UCBrowser/User Data%/Default") - .put("Brave", "BraveSoftware/Brave-Browser/User Data/Default") - .put("Google Chrome", "Chrome/User Data/Default") + .put("SalamWeb", "SalamWeb/User Data") + .put("UC Browser", "UCBrowser/User Data%") + .put("Brave", "BraveSoftware/Brave-Browser/User Data") + .put("Google Chrome", "Chrome/User Data") .build(); @Messages({"# {0} - browserName", @@ -119,6 +134,9 @@ class Chromium extends Extract { "Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0}", "# {0} - browserName", "Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0}", + "Progress_Message_Chrome_Profiles=Chrome Profiles {0}", + "Progress_Message_Chrome_Extensions=Chrome Extensions {0}", + "Progress_Message_Chrome_Favicons=Chrome Downloads Favicons {0}", "Progress_Message_Chrome_FormHistory=Chrome Form History", "# {0} - browserName", "Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0}", @@ -137,40 +155,62 @@ public void process(Content dataSource, DataSourceIngestModuleProgress progressB dataFound = false; long ingestJobId = context.getJobId(); + userProfiles = new HashMap<>(); + browserLocations = new HashMap<>(); for (Map.Entry<String, String> browser : BROWSERS_MAP.entrySet()) { - String browserName = browser.getKey(); + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Profiles", browser.getKey())); + getProfiles(browser.getKey(), browser.getValue(), ingestJobId); + if (context.dataSourceIngestIsCancelled()) { + return; + } + } + for (Map.Entry<String, String> profile : userProfiles.entrySet()) { + String browserLocation = profile.getKey(); + String browserName = browserLocations.get(browserLocation); + String userName = profile.getValue(); + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Extensions", browserName)); + this.getExtensions(browserName, browserLocation, userName, ingestJobId); + if (context.dataSourceIngestIsCancelled()) { + return; + } progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_History", browserName)); - this.getHistory(browser.getKey(), browser.getValue(), ingestJobId); + this.getHistory(browserName, browserLocation, userName, ingestJobId); if (context.dataSourceIngestIsCancelled()) { return; } progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Bookmarks", browserName)); - this.getBookmark(browser.getKey(), browser.getValue(), ingestJobId); + this.getBookmark(browserName, browserLocation, userName, ingestJobId); if (context.dataSourceIngestIsCancelled()) { return; } progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Cookies", browserName)); - this.getCookie(browser.getKey(), browser.getValue(), ingestJobId); + this.getCookie(browserName, browserLocation, userName, ingestJobId); if (context.dataSourceIngestIsCancelled()) { return; } progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Logins", browserName)); - this.getLogins(browser.getKey(), browser.getValue(), ingestJobId); + this.getLogins(browserName, browserLocation, userName, ingestJobId); if (context.dataSourceIngestIsCancelled()) { return; } progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_AutoFill", browserName)); - this.getAutofill(browser.getKey(), browser.getValue(), ingestJobId); + this.getAutofill(browserName, browserLocation, userName, ingestJobId); if (context.dataSourceIngestIsCancelled()) { return; } progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Downloads", browserName)); - this.getDownload(browser.getKey(), browser.getValue(), ingestJobId); + this.getDownload(browserName, browserLocation, userName, ingestJobId); + if (context.dataSourceIngestIsCancelled()) { + return; + } + + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Favicons", browserName)); + this.getFavicons(browserName, browserLocation, userName, ingestJobId); if (context.dataSourceIngestIsCancelled()) { return; } @@ -181,6 +221,425 @@ public void process(Content dataSource, DataSourceIngestModuleProgress progressB chromeCacheExtractor.processCaches(); } + /** + * Query for profiles and add artifacts + * + * @param browser + * @param browserLocation + * @param ingestJobId The ingest job id. + */ + private void getProfiles(String browser, String browserLocation, long ingestJobId) { + FileManager fileManager = currentCase.getServices().getFileManager(); + String browserName = browser; + List<AbstractFile> localStateFiles; + String localStateName = LOCALSTATE_FILE_NAME; + if (browserName.equals(UC_BROWSER_NAME)) { + localStateName = LOCALSTATE_FILE_NAME + "%"; + } + try { + localStateFiles = fileManager.findFiles(dataSource, localStateName, browserLocation); //NON-NLS + } catch (TskCoreException ex) { + String msg = NbBundle.getMessage(this.getClass(), "Chrome.getLocalState.errMsg.errGettingFiles"); + logger.log(Level.SEVERE, msg, ex); + this.addErrorMessage(this.getDisplayName() + ": " + msg); + return; + } + + // get only the allocated ones, for now + List<AbstractFile> allocatedLocalStateFiles = new ArrayList<>(); + for (AbstractFile localStateFile : localStateFiles) { + if (localStateFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) { + allocatedLocalStateFiles.add(localStateFile); + } + } + + // log a message if we don't have any allocated Local State files + if (allocatedLocalStateFiles.isEmpty()) { + String msg = NbBundle.getMessage(this.getClass(), "Chrome.getLocalState.errMsg.couldntFindAnyFiles"); + logger.log(Level.INFO, msg); + return; + } + + dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); + int j = 0; + while (j < allocatedLocalStateFiles.size()) { + if (browser.contains(GOOGLE_PROFILE_NAME)) { + String parentPath = FilenameUtils.normalizeNoEndSeparator(allocatedLocalStateFiles.get(j).getParentPath()); + browserName = GOOGLE_PROFILE + " " + FilenameUtils.getBaseName(parentPath); + } + String temps = RAImageIngestModule.getRATempPath(currentCase, browserName, ingestJobId) + File.separator + allocatedLocalStateFiles.get(j).getName() + j; //NON-NLS + final AbstractFile localStateFile = allocatedLocalStateFiles.get(j++); + if ((localStateFile.getSize() == 0) || (localStateFile.getName().toLowerCase().contains("-slack")) + || (localStateFile.getName().toLowerCase().contains("cache")) || (localStateFile.getName().toLowerCase().contains("media")) + || (localStateFile.getName().toLowerCase().contains("index"))) { + continue; + } + try { + ContentUtils.writeToFile(localStateFile, new File(temps), context::dataSourceIngestIsCancelled); + } catch (ReadContentInputStreamException ex) { + logger.log(Level.WARNING, String.format("Error reading Chrome web Local State artifacts file '%s' (id=%d).", + localStateFile.getName(), localStateFile.getId()), ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getLocalState.errMsg.errAnalyzingFile", + this.getDisplayName(), localStateFile.getName())); + continue; + } catch (IOException ex) { + logger.log(Level.SEVERE, String.format("Error writing temp file '%s' for Chrome Local State artifacts file '%s' (id=%d).", + temps, localStateFile.getName(), localStateFile.getId()), ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getLocalState.errMsg.errAnalyzingFile", + this.getDisplayName(), localStateFile.getName())); + continue; + } + + if (context.dataSourceIngestIsCancelled()) { + break; + } + + FileReader tempReader; + try { + tempReader = new FileReader(temps); + } catch (FileNotFoundException ex) { + logger.log(Level.WARNING, "Error while trying to read into the LocalState file.", ex); //NON-NLS + continue; + } + + JsonElement jsonElement; + JsonObject jElement, jProfile, jInfoCache; + + try { + jsonElement = JsonParser.parseReader(tempReader); + jElement = jsonElement.getAsJsonObject(); + if (jElement.has("profile")) { + jProfile = jElement.get("profile").getAsJsonObject(); //NON-NLS + jInfoCache = jProfile.get("info_cache").getAsJsonObject(); + } else { + continue; + } + } catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) { + logger.log(Level.WARNING, "Error parsing Json from LocalState.", ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getlocalState.errMsg.errAnalyzingFile", + this.getDisplayName(), localStateFile.getName())); + continue; + } + + BlackboardArtifact.Type localStateArtifactType; + + try { + localStateArtifactType = createArtifactType(LOCAL_STATE_ARTIFACT_NAME, NbBundle.getMessage(this.getClass(), "Chrome.getLocalState.displayName")); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error creating artifact type for LocalState."), ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getfavicon.errMsg.errCreateArtifact")); + continue; + + } + Set<String> profileNames = jInfoCache.keySet(); + for (String profileName : profileNames) { + JsonElement result = jInfoCache.get(profileName); + JsonObject profile = result.getAsJsonObject(); + if (profile == null) { + continue; + } + JsonElement gaiaIdEl = profile.get("gaia_id"); //NON-NLS + String gaiaId; + if (gaiaIdEl != null) { + gaiaId = gaiaIdEl.getAsString(); + } else { + gaiaId = ""; + } + String hostedDomain; + JsonElement hostedDomainEl = profile.get("hosted_domain"); //NON-NLS + if (hostedDomainEl != null) { + hostedDomain = hostedDomainEl.getAsString(); + } else { + hostedDomain= ""; + } + String shortcutName; + JsonElement shortcutNameEl = profile.get("shortcut_name"); //NON-NLS + if (shortcutNameEl != null) { + shortcutName = shortcutNameEl.getAsString(); + } else { + shortcutName = ""; + } + String name; + JsonElement nameEl = profile.get("name"); //NON-NLS + if (nameEl != null) { + name = nameEl.getAsString(); + } else { + name= ""; + } + String userName; + JsonElement userNameEl = profile.get("user_name"); //NON-NLS + if (userNameEl != null) { + userName = userNameEl.getAsString(); + } else { + userName = ""; + } + + if (userName.contains("")) { + userProfiles.put(browserLocation + "/" + profileName, name); + browserLocations.put(browserLocation + "/" + profileName, browser); + } else { + userProfiles.put(browserLocation + "/" + profileName, userName); + browserLocations.put(browserLocation + "/" + profileName, browser); + } + + Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, + RecentActivityExtracterModuleFactory.getModuleName(), profileName)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID, + RecentActivityExtracterModuleFactory.getModuleName(), gaiaId)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, + RecentActivityExtracterModuleFactory.getModuleName(), hostedDomain)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SHORTCUT, + RecentActivityExtracterModuleFactory.getModuleName(), shortcutName)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), name)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), userName)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), browserName)); + + try { + bbartifacts.add(createArtifactWithAttributes(localStateArtifactType, localStateFile, bbattributes)); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Failed to create bookmark artifact for file (%d)", localStateFile.getId()), ex); + } + + } + + if (!context.dataSourceIngestIsCancelled()) { + postArtifacts(bbartifacts); + } + bbartifacts.clear(); + + } + // Check if Default, Guest Profile and System Profile are in the usersProfiles, if they are not then add them + if (!userProfiles.containsKey("Default")) { + userProfiles.put(browserLocation + "/" + "Default", "Default"); + browserLocations.put(browserLocation + "/" + "Default", browser); + } + if (!userProfiles.containsKey("Guest Profile")) { + userProfiles.put(browserLocation + "/" + "Guest Profile", "Guest"); + browserLocations.put(browserLocation + "/" + "Guest Profile", browser); + } + if (!userProfiles.containsKey("System Profile")) { + userProfiles.put(browserLocation + "/" + "System Profile", "System"); + browserLocations.put(browserLocation + "/" + "System Profile", browser); + } + } + + /** + * Query for Extensions and add artifacts + * + * @param browser + * @param browserLocation + * @param ingestJobId The ingest job id. + */ + private void getExtensions(String browser, String browserLocation, String userName, long ingestJobId) { + FileManager fileManager = currentCase.getServices().getFileManager(); + String browserName = browser; + List<AbstractFile> extensionFiles; + String extensionsName = EXTENSIONS_FILE_NAME; + if (browserName.equals(UC_BROWSER_NAME)) { + extensionsName = EXTENSIONS_FILE_NAME + "%"; + } + try { + // Local State file is found in the directory about the browserLocation, that is why it is being removed + extensionFiles = fileManager.findFiles(dataSource, extensionsName, browserLocation); //NON-NLS + } catch (TskCoreException ex) { + String msg = NbBundle.getMessage(this.getClass(), "Chrome.getExtensions.errMsg.errGettingFiles"); + logger.log(Level.SEVERE, msg, ex); + this.addErrorMessage(this.getDisplayName() + ": " + msg); + return; + } + + // get only the allocated ones, for now + List<AbstractFile> allocatedExtensionsFiles = new ArrayList<>(); + for (AbstractFile extensionFile : extensionFiles) { + if (extensionFile.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC)) { + allocatedExtensionsFiles.add(extensionFile); + } + } + + // log a message if we don't have any allocated Local State files + if (allocatedExtensionsFiles.isEmpty()) { + String msg = NbBundle.getMessage(this.getClass(), "Chrome.getExtensions.errMsg.couldntFindAnyFiles"); + logger.log(Level.INFO, msg); + return; + } + + dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); + int j = 0; + while (j < allocatedExtensionsFiles.size()) { + if (browser.contains(GOOGLE_PROFILE_NAME)) { + String parentPath = FilenameUtils.normalizeNoEndSeparator(allocatedExtensionsFiles.get(j).getParentPath()); + browserName = GOOGLE_PROFILE + " " + FilenameUtils.getBaseName(parentPath); + } + String temps = RAImageIngestModule.getRATempPath(currentCase, browserName, ingestJobId) + File.separator + allocatedExtensionsFiles.get(j).getName() + j; //NON-NLS + final AbstractFile extensionFile = allocatedExtensionsFiles.get(j++); + if ((extensionFile.getSize() == 0) || (extensionFile.getName().toLowerCase().contains("-slack")) + || (extensionFile.getName().toLowerCase().contains("cache")) || (extensionFile.getName().toLowerCase().contains("media")) + || (extensionFile.getName().toLowerCase().contains("index"))) { + continue; + } + try { + ContentUtils.writeToFile(extensionFile, new File(temps), context::dataSourceIngestIsCancelled); + } catch (ReadContentInputStreamException ex) { + logger.log(Level.WARNING, String.format("Error reading Chrome web extension artifacts file '%s' (id=%d).", + extensionFile.getName(), extensionFile.getId()), ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getExtensions.errMsg.errAnalyzingFile", + this.getDisplayName(), extensionFile.getName())); + continue; + } catch (IOException ex) { + logger.log(Level.SEVERE, String.format("Error writing temp file '%s' for Chrome Extensions artifacts file '%s' (id=%d).", + temps, extensionFile.getName(), extensionFile.getId()), ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getExtensions.errMsg.errAnalyzingFile", + this.getDisplayName(), extensionFile.getName())); + continue; + } + + if (context.dataSourceIngestIsCancelled()) { + break; + } + + FileReader tempReader; + try { + tempReader = new FileReader(temps); + } catch (FileNotFoundException ex) { + logger.log(Level.WARNING, "Error while trying to read into the Secure Preferences file.", ex); //NON-NLS + continue; + } + + BlackboardArtifact.Type localStateArtifactType; + + try { + localStateArtifactType = createArtifactType(EXTENSIONS_ARTIFACT_NAME, NbBundle.getMessage(this.getClass(), "Chrome.getExtensions.displayName")); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error creating artifact type for Secure Preferences."), ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getExtensions.errMsg.errCreateArtifact")); + continue; + } + + String profileName = FilenameUtils.getBaseName(StringUtils.chop(extensionFile.getParentPath())); + + JsonElement jsonElement; + JsonObject jElement, jExtensions, jSettings; + + try { + jsonElement = JsonParser.parseReader(tempReader); + jElement = jsonElement.getAsJsonObject(); + if (jElement.has("extensions")) { + logger.log(Level.WARNING, String.format("Processing Secure Preferences from %s", extensionFile.getParentPath())); + jExtensions = jElement.get("extensions").getAsJsonObject(); //NON-NLS + if (!browserName.equals(OPERA_BROWSER_NAME)) { + jSettings = jExtensions.get("settings").getAsJsonObject(); + } else { + jSettings = jExtensions.get("opsettings").getAsJsonObject(); + } + } else { + continue; + } + } catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) { + logger.log(Level.WARNING, "Error parsing Json from Secure Preferences.", ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getExtensoins.errMsg.errAnalyzingFile", + this.getDisplayName(), extensionFile.getName())); + continue; + } + + Set<String> extensions = jSettings.keySet(); + for (String extension : extensions) { + JsonElement result = jSettings.get(extension); + JsonObject ext = result.getAsJsonObject(); + if (ext == null) { + continue; + } + JsonElement flagEl = ext.get("state"); //NON-NLS + String flag; + if (flagEl != null) { + if (flagEl.getAsInt() == 1) { + flag = "Enabled"; + } else { + flag = "Disabled"; + } + } else { + flag = ""; + } + String apiGrantedPermissions = ""; + if (ext.has("active_permissions")) { + JsonObject permissions = ext.get("active_permissions").getAsJsonObject(); + JsonArray apiPermissions = permissions.get("api").getAsJsonArray(); + for (JsonElement apiPermission : apiPermissions) { + String apigrantEl = apiPermission.getAsString(); + if (apigrantEl != null) { + apiGrantedPermissions = apiGrantedPermissions + ", " + apigrantEl; + } else { + apiGrantedPermissions = apiGrantedPermissions + ""; + } + } + } + String version; + String description; + String extName; + if (ext.has("manifest")) { + JsonObject manifest = ext.get("manifest").getAsJsonObject(); + JsonElement descriptionEl = manifest.get("description"); + if (descriptionEl != null) { + description = descriptionEl.getAsString(); + } else { + description = ""; + } + JsonElement versionEl = manifest.get("version"); + if (versionEl != null) { + version = versionEl.getAsString(); + } else { + version = ""; + } + JsonElement extNameEl = manifest.get("name"); + if (extNameEl != null) { + extName = extNameEl.getAsString(); + } else { + extName = ""; + } + } else { + version = ""; + description = ""; + extName = ""; + } + Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ID, + RecentActivityExtracterModuleFactory.getModuleName(), extension)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), extName)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DESCRIPTION, + RecentActivityExtracterModuleFactory.getModuleName(), description)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VERSION, + RecentActivityExtracterModuleFactory.getModuleName(), version)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_FLAG, + RecentActivityExtracterModuleFactory.getModuleName(), flag)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PERMISSIONS, + RecentActivityExtracterModuleFactory.getModuleName(), apiGrantedPermissions.replaceFirst(", ", ""))); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), userName)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), browserName)); + + try { + bbartifacts.add(createArtifactWithAttributes(localStateArtifactType, extensionFile, bbattributes)); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Failed to create Extension artifact for file (%d)", extensionFile.getId()), ex); + } + + } + + if (!context.dataSourceIngestIsCancelled()) { + postArtifacts(bbartifacts); + } + bbartifacts.clear(); + + } + } + /** * Query for history databases and add artifacts * @@ -188,11 +647,12 @@ public void process(Content dataSource, DataSourceIngestModuleProgress progressB * @param browserLocation * @param ingestJobId The ingest job id. */ - private void getHistory(String browser, String browserLocation, long ingestJobId) { + private void getHistory(String browser, String browserLocation, String userName, long ingestJobId) { FileManager fileManager = currentCase.getServices().getFileManager(); + String browserName = browser; List<AbstractFile> historyFiles; String historyFileName = HISTORY_FILE_NAME; - if (browser.equals(UC_BROWSER_NAME)) { + if (browserName.equals(UC_BROWSER_NAME)) { historyFileName = HISTORY_FILE_NAME + "%"; } try { @@ -223,7 +683,11 @@ private void getHistory(String browser, String browserLocation, long ingestJobId Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; while (j < allocatedHistoryFiles.size()) { - String temps = RAImageIngestModule.getRATempPath(currentCase, browser, ingestJobId) + File.separator + allocatedHistoryFiles.get(j).getName() + j + ".db"; //NON-NLS + if (browser.contains(GOOGLE_PROFILE_NAME)) { + String parentPath = FilenameUtils.normalizeNoEndSeparator(allocatedHistoryFiles.get(j).getParentPath()); + browserName = GOOGLE_PROFILE + " " + FilenameUtils.getBaseName(parentPath); + } + String temps = RAImageIngestModule.getRATempPath(currentCase, browserName, ingestJobId) + 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("media")) @@ -263,9 +727,9 @@ private void getHistory(String browser, String browserLocation, long ingestJobId (Long.valueOf(result.get("last_visit_time").toString()) / 1000000) - Long.valueOf("11644473600"), result.get("from_visit") == null ? "" : result.get("from_visit").toString(), result.get("title") == null ? "" : result.get("title").toString(), - browser, + browserName, extractedDomain, - ""); + userName); bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_HISTORY, historyFile, bbattributes)); } catch (TskCoreException ex) { @@ -287,11 +751,12 @@ private void getHistory(String browser, String browserLocation, long ingestJobId * @param browserLocation * @param ingestJobId The ingest job id. */ - private void getBookmark(String browser, String browserLocation, long ingestJobId) { + private void getBookmark(String browser, String browserLocation, String userName, long ingestJobId) { FileManager fileManager = currentCase.getServices().getFileManager(); List<AbstractFile> bookmarkFiles; + String browserName = browser; String bookmarkFileName = BOOKMARK_FILE_NAME; - if (browser.equals(UC_BROWSER_NAME)) { + if (browserName.equals(UC_BROWSER_NAME)) { bookmarkFileName = BOOKMARK_FILE_NAME + "%"; } try { @@ -312,6 +777,11 @@ private void getBookmark(String browser, String browserLocation, long ingestJobI Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; while (j < bookmarkFiles.size()) { + if (browser.contains(GOOGLE_PROFILE_NAME)) { + String parentPath = FilenameUtils.normalizeNoEndSeparator(bookmarkFiles.get(j).getParentPath()); + browserName = GOOGLE_PROFILE + " " + FilenameUtils.getBaseName(parentPath); + } + AbstractFile bookmarkFile = bookmarkFiles.get(j++); if ((bookmarkFile.getSize() == 0) || (bookmarkFile.getName().toLowerCase().contains("-slack")) || (bookmarkFile.getName().toLowerCase().contains("extras")) || (bookmarkFile.getName().toLowerCase().contains("log")) @@ -319,7 +789,7 @@ private void getBookmark(String browser, String browserLocation, long ingestJobI || (bookmarkFile.getName().toLowerCase().contains("bak")) || (bookmarkFile.getParentPath().toLowerCase().contains("backup"))) { continue; } - String temps = RAImageIngestModule.getRATempPath(currentCase, browser, ingestJobId) + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS + String temps = RAImageIngestModule.getRATempPath(currentCase, browserName, ingestJobId) + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(bookmarkFile, new File(temps), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -352,15 +822,13 @@ private void getBookmark(String browser, String browserLocation, long ingestJobI } JsonElement jsonElement; - JsonObject jElement, jRoot, jBookmark; - JsonArray jBookmarkArray; + JsonObject jElement, jRoot; try { jsonElement = JsonParser.parseReader(tempReader); jElement = jsonElement.getAsJsonObject(); jRoot = jElement.get("roots").getAsJsonObject(); //NON-NLS - jBookmark = jRoot.get("bookmark_bar").getAsJsonObject(); //NON-NLS - jBookmarkArray = jBookmark.getAsJsonArray("children"); //NON-NLS + Set<String> bookmarkKeys = jRoot.keySet(); } catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) { logger.log(Level.WARNING, "Error parsing Json from Chrome Bookmark.", ex); //NON-NLS this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errAnalyzingFile3", @@ -368,54 +836,64 @@ private void getBookmark(String browser, String browserLocation, long ingestJobI continue; } - for (JsonElement result : jBookmarkArray) { - JsonObject address = result.getAsJsonObject(); - if (address == null) { - continue; - } - JsonElement urlEl = address.get("url"); //NON-NLS - String url; - if (urlEl != null) { - url = urlEl.getAsString(); - } else { - url = ""; - } - String name; - JsonElement nameEl = address.get("name"); //NON-NLS - if (nameEl != null) { - name = nameEl.getAsString(); - } else { - name = ""; - } - Long date; - JsonElement dateEl = address.get("date_added"); //NON-NLS - if (dateEl != null) { - date = dateEl.getAsLong(); - } else { - date = Long.valueOf(0); - } - String domain = NetworkUtils.extractDomain(url); - Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); - //TODO Revisit usage of deprecated constructor as per TSK-583 - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, - RecentActivityExtracterModuleFactory.getModuleName(), url)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE, - RecentActivityExtracterModuleFactory.getModuleName(), name)); - 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(), browser)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, - RecentActivityExtracterModuleFactory.getModuleName(), domain)); + Set<String> bookmarkKeys = jRoot.keySet(); + for (String bookmarkKey : bookmarkKeys) { + JsonObject jBookmark = jRoot.get(bookmarkKey).getAsJsonObject(); //NON-NLS + JsonArray jBookmarkArray = jBookmark.getAsJsonArray("children"); //NON-NLS + for (JsonElement result : jBookmarkArray) { + JsonObject address = result.getAsJsonObject(); + if (address == null) { + continue; + } + JsonElement urlEl = address.get("url"); //NON-NLS + String url; + if (urlEl != null) { + url = urlEl.getAsString(); + } else { + url = ""; + } + String name; + JsonElement nameEl = address.get("name"); //NON-NLS + if (nameEl != null) { + name = nameEl.getAsString(); + } else { + name = ""; + } + Long date; + JsonElement dateEl = address.get("date_added"); //NON-NLS + if (dateEl != null) { + date = dateEl.getAsLong(); + } else { + date = Long.valueOf(0); + } + String domain = NetworkUtils.extractDomain(url); + Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); + //TODO Revisit usage of deprecated constructor as per TSK-583 + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, + RecentActivityExtracterModuleFactory.getModuleName(), url)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE, + RecentActivityExtracterModuleFactory.getModuleName(), name)); + 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(), browserName)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, + RecentActivityExtracterModuleFactory.getModuleName(), domain)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), userName)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, + RecentActivityExtracterModuleFactory.getModuleName(), bookmarkKey)); + + + try { + bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_BOOKMARK, bookmarkFile, bbattributes)); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Failed to create bookmark artifact for file (%d)", bookmarkFile.getId()), ex); + } - try { - bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_BOOKMARK, bookmarkFile, bbattributes)); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Failed to create bookmark artifact for file (%d)", bookmarkFile.getId()), ex); } - } - + if (!context.dataSourceIngestIsCancelled()) { postArtifacts(bbartifacts); } @@ -431,12 +909,13 @@ private void getBookmark(String browser, String browserLocation, long ingestJobI * @param browserLocation * @param ingestJobId The ingest job id. */ - private void getCookie(String browser, String browserLocation, long ingestJobId) { + private void getCookie(String browser, String browserLocation, String userName, long ingestJobId) { FileManager fileManager = currentCase.getServices().getFileManager(); List<AbstractFile> cookiesFiles; + String browserName = browser; String cookieFileName = COOKIE_FILE_NAME; - if (browser.equals(UC_BROWSER_NAME)) { + if (browserName.equals(UC_BROWSER_NAME)) { // Wildcard on front and back of Cookies are there for Cookie files that start with something else // ie: UC browser has "Extension Cookies.9" as well as Cookies.9 cookieFileName = "%" + COOKIE_FILE_NAME + "%"; @@ -459,11 +938,16 @@ private void getCookie(String browser, String browserLocation, long ingestJobId) Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; while (j < cookiesFiles.size()) { + if (browser.contains(GOOGLE_PROFILE_NAME)) { + String parentPath = FilenameUtils.normalizeNoEndSeparator(cookiesFiles.get(j).getParentPath()); + browserName = GOOGLE_PROFILE + FilenameUtils.getBaseName(parentPath); + } + AbstractFile cookiesFile = cookiesFiles.get(j++); if ((cookiesFile.getSize() == 0) || (cookiesFile.getName().toLowerCase().contains("-slack"))) { continue; } - String temps = RAImageIngestModule.getRATempPath(currentCase, browser, ingestJobId) + File.separator + cookiesFile.getName() + j + ".db"; //NON-NLS + String temps = RAImageIngestModule.getRATempPath(currentCase, browserName, ingestJobId) + File.separator + cookiesFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(cookiesFile, new File(temps), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -503,11 +987,13 @@ private void getCookie(String browser, String browserLocation, long ingestJobId) RecentActivityExtracterModuleFactory.getModuleName(), ((result.get("value").toString() != null) ? result.get("value").toString() : ""))); //NON-NLS bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), browser)); + RecentActivityExtracterModuleFactory.getModuleName(), browserName)); String domain = result.get("host_key").toString(); //NON-NLS domain = domain.replaceFirst("^\\.+(?!$)", ""); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, RecentActivityExtracterModuleFactory.getModuleName(), domain)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), userName)); try { bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_COOKIE, cookiesFile, bbattributes)); @@ -531,11 +1017,12 @@ private void getCookie(String browser, String browserLocation, long ingestJobId) * @param browserLocation * @param ingestJobId The ingest job id. */ - private void getDownload(String browser, String browserLocation, long ingestJobId) { + private void getDownload(String browser, String browserLocation, String userName, long ingestJobId) { FileManager fileManager = currentCase.getServices().getFileManager(); List<AbstractFile> downloadFiles; + String browserName = browser; String historyFileName = HISTORY_FILE_NAME; - if (browser.equals(UC_BROWSER_NAME)) { + if (browserName.equals(UC_BROWSER_NAME)) { historyFileName = HISTORY_FILE_NAME + "%"; } try { @@ -556,13 +1043,18 @@ private void getDownload(String browser, String browserLocation, long ingestJobI Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; while (j < downloadFiles.size()) { + if (browser.contains(GOOGLE_PROFILE_NAME)) { + String parentPath = FilenameUtils.normalizeNoEndSeparator(downloadFiles.get(j).getParentPath()); + browserName = GOOGLE_PROFILE + FilenameUtils.getBaseName(parentPath); + } + AbstractFile downloadFile = downloadFiles.get(j++); if ((downloadFile.getSize() == 0) || (downloadFile.getName().toLowerCase().contains("-slack")) || (downloadFile.getName().toLowerCase().contains("cache")) || (downloadFile.getName().toLowerCase().contains("index"))) { continue; } - String temps = RAImageIngestModule.getRATempPath(currentCase, browser, ingestJobId) + File.separator + downloadFile.getName() + j + ".db"; //NON-NLS + String temps = RAImageIngestModule.getRATempPath(currentCase, browserName, ingestJobId) + File.separator + downloadFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(downloadFile, new File(temps), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -617,8 +1109,10 @@ private void getDownload(String browser, String browserLocation, long ingestJobI String domain = NetworkUtils.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : ""); //NON-NLS bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, RecentActivityExtracterModuleFactory.getModuleName(), domain)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), userName)); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), browser)); + RecentActivityExtracterModuleFactory.getModuleName(), browserName)); // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. try { @@ -642,6 +1136,118 @@ private void getDownload(String browser, String browserLocation, long ingestJobI } } + /** + * Queries the Favicons table and adds artifacts + * + * @param browser + * @param browserLocation + * @param ingestJobId The ingest job id. + */ + private void getFavicons(String browser, String browserLocation, String userName, long ingestJobId) { + FileManager fileManager = currentCase.getServices().getFileManager(); + List<AbstractFile> faviconFiles; + String browserName = browser; + try { + faviconFiles = fileManager.findFiles(dataSource, FAVICON_DATA_FILE_NAME, browserLocation); //NON-NLS + } catch (TskCoreException ex) { + String msg = NbBundle.getMessage(this.getClass(), "Chrome.getFavicon.errMsg.errGettingFiles"); + logger.log(Level.SEVERE, msg, ex); + this.addErrorMessage(this.getDisplayName() + ": " + msg); + return; + } + + if (faviconFiles.isEmpty()) { + logger.log(Level.INFO, "Didn't find any Chrome favicon files."); //NON-NLS + return; + } + + dataFound = true; + Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); + int j = 0; + while (j < faviconFiles.size()) { + if (browser.contains(GOOGLE_PROFILE_NAME)) { + String parentPath = FilenameUtils.normalizeNoEndSeparator(faviconFiles.get(j).getParentPath()); + browserName = GOOGLE_PROFILE + FilenameUtils.getBaseName(parentPath); + } + AbstractFile faviconFile = faviconFiles.get(j++); + if ((faviconFile.getSize() == 0) || (faviconFile.getName().toLowerCase().contains("-slack")) + || (faviconFile.getName().toLowerCase().contains("cache")) || (faviconFile.getName().toLowerCase().contains("index"))) { + continue; + } + + String temps = RAImageIngestModule.getRATempPath(currentCase, browserName, ingestJobId) + File.separator + faviconFile.getName() + j + ".db"; //NON-NLS + try { + ContentUtils.writeToFile(faviconFile, new File(temps), context::dataSourceIngestIsCancelled); + } catch (ReadContentInputStreamException ex) { + logger.log(Level.WARNING, String.format("Error reading Chrome favicons artifacts file '%s' (id=%d).", + faviconFile.getName(), faviconFile.getId()), ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getFavicon.errMsg.errAnalyzeFiles1", + this.getDisplayName(), faviconFile.getName())); + continue; + } catch (IOException ex) { + logger.log(Level.SEVERE, String.format("Error writing temp sqlite db file '%s' for Chrome favicon artifacts file '%s' (id=%d).", + temps, faviconFile.getName(), faviconFile.getId()), ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getfavicon.errMsg.errAnalyzeFiles1", + this.getDisplayName(), faviconFile.getName())); + continue; + } + File dbFile = new File(temps); + if (context.dataSourceIngestIsCancelled()) { + dbFile.delete(); + break; + } + + BlackboardArtifact.Type faviconArtifactType; + + try { + faviconArtifactType = createArtifactType(FAVICON_ARTIFACT_NAME, NbBundle.getMessage(this.getClass(), "Chrome.getFavicon.displayName")); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error creating artifact type for Chrome favicon."), ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "Chrome.getfavicon.errMsg.errCreateArtifact")); + continue; + + } + + List<HashMap<String, Object>> tempList; + + tempList = this.querySQLiteDb(temps, FAVICON_QUERY); + + logger.log(Level.INFO, "{0}- Now getting favicons from {1} with {2} artifacts identified.", new Object[]{getDisplayName(), temps, tempList.size()}); //NON-NLS + for (HashMap<String, Object> result : tempList) { + Collection<BlackboardAttribute> bbattributes = new ArrayList<>(); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL, + RecentActivityExtracterModuleFactory.getModuleName(), + ((result.get("page_url").toString() != null) ? result.get("page_url").toString() : ""))); //NON-NLS + Long updatedTime = (Long.valueOf(result.get("last_updated").toString()) / 1000000) - Long.valueOf("11644473600"); //NON-NLS + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED, + RecentActivityExtracterModuleFactory.getModuleName(), updatedTime)); + Long requestedTime = (Long.valueOf(result.get("last_requested").toString()) / 1000000) - Long.valueOf("11644473600"); //NON-NLS + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, + RecentActivityExtracterModuleFactory.getModuleName(), requestedTime)); + String domain = NetworkUtils.extractDomain((result.get("page_url").toString() != null) ? result.get("page_url").toString() : ""); //NON-NLS + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, + RecentActivityExtracterModuleFactory.getModuleName(), domain)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), userName)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), browserName)); + + try { + bbartifacts.add(createArtifactWithAttributes(faviconArtifactType, faviconFile, bbattributes)); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Failed to create cookie artifact for file (%d)", faviconFile.getId()), ex); + } + + } + + dbFile.delete(); + } + + if (!bbartifacts.isEmpty() && !context.dataSourceIngestIsCancelled()) { + postArtifacts(bbartifacts); + } + } + /** * Gets user logins from Login Data sqlite database * @@ -649,12 +1255,13 @@ private void getDownload(String browser, String browserLocation, long ingestJobI * @param browserLocation * @param ingestJobId The ingest job id. */ - private void getLogins(String browser, String browserLocation, long ingestJobId) { + private void getLogins(String browser, String browserLocation, String userName, long ingestJobId) { FileManager fileManager = currentCase.getServices().getFileManager(); List<AbstractFile> loginDataFiles; + String browserName = browser; String loginDataFileName = LOGIN_DATA_FILE_NAME; - if (browser.equals(UC_BROWSER_NAME)) { + if (browserName.equals(UC_BROWSER_NAME)) { loginDataFileName = LOGIN_DATA_FILE_NAME + "%"; } @@ -676,11 +1283,15 @@ private void getLogins(String browser, String browserLocation, long ingestJobId) Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; while (j < loginDataFiles.size()) { + if (browser.contains(GOOGLE_PROFILE_NAME)) { + String parentPath = FilenameUtils.normalizeNoEndSeparator(loginDataFiles.get(j).getParentPath()); + browserName = GOOGLE_PROFILE + FilenameUtils.getBaseName(parentPath); + } AbstractFile loginDataFile = loginDataFiles.get(j++); if ((loginDataFile.getSize() == 0) || (loginDataFile.getName().toLowerCase().contains("-slack"))) { continue; } - String temps = RAImageIngestModule.getRATempPath(currentCase, browser, ingestJobId) + File.separator + loginDataFile.getName() + j + ".db"; //NON-NLS + String temps = RAImageIngestModule.getRATempPath(currentCase, browserName, ingestJobId) + File.separator + loginDataFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(loginDataFile, new File(temps), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -731,7 +1342,10 @@ private void getLogins(String browser, String browserLocation, long ingestJobId) result.containsKey("signon_realm") ? NetworkUtils.extractDomain(result.get("signon_realm").toString()) : "")); //NON-NLS bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), browser)); + RecentActivityExtracterModuleFactory.getModuleName(), browserName)); + + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), userName)); try { bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_SERVICE_ACCOUNT, loginDataFile, bbattributes)); @@ -756,12 +1370,13 @@ private void getLogins(String browser, String browserLocation, long ingestJobId) * @param browserLocation * @param ingestJobId The ingest job id. */ - private void getAutofill(String browser, String browserLocation, long ingestJobId) { + private void getAutofill(String browser, String browserLocation, String userName, long ingestJobId) { FileManager fileManager = currentCase.getServices().getFileManager(); List<AbstractFile> webDataFiles; + String browserName = browser; String webDataFileName = WEB_DATA_FILE_NAME; - if (browser.equals(UC_BROWSER_NAME)) { + if (browserName.equals(UC_BROWSER_NAME)) { webDataFileName = WEB_DATA_FILE_NAME + "%"; } @@ -783,12 +1398,16 @@ private void getAutofill(String browser, String browserLocation, long ingestJobI Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); int j = 0; while (j < webDataFiles.size()) { + if (browser.contains(GOOGLE_PROFILE_NAME)) { + String parentPath = FilenameUtils.normalizeNoEndSeparator(webDataFiles.get(j).getParentPath()); + browserName = GOOGLE_PROFILE + FilenameUtils.getBaseName(parentPath); + } databaseEncrypted = false; AbstractFile webDataFile = webDataFiles.get(j++); if ((webDataFile.getSize() == 0) || (webDataFile.getName().toLowerCase().contains("-slack"))) { continue; } - String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, browser, ingestJobId) + File.separator + webDataFile.getName() + j + ".db"; //NON-NLS + String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, browserName, ingestJobId) + File.separator + webDataFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(webDataFile, new File(tempFilePath), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -814,12 +1433,12 @@ private void getAutofill(String browser, String browserLocation, long ingestJobI boolean isSchemaV8X = Util.checkColumn("date_created", "autofill", tempFilePath); // get form autofill artifacts - bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X, browser)); + bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X, userName, browserName)); try { // get form address atifacts getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X); if (databaseEncrypted) { - String comment = String.format("%s Autofill Database Encryption Detected", browser); + String comment = String.format("%s Autofill Database Encryption Detected", browserName); Collection<BlackboardAttribute> bbattributes = Arrays.asList( new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, RecentActivityExtracterModuleFactory.getModuleName(), comment)); @@ -852,7 +1471,7 @@ private void getAutofill(String browser, String browserLocation, long ingestJobI * * @return collection of TSK_WEB_FORM_AUTOFILL artifacts */ - private Collection<BlackboardArtifact> getFormAutofillArtifacts(AbstractFile webDataFile, String dbFilePath, boolean isSchemaV8X, String browser) { + private Collection<BlackboardArtifact> getFormAutofillArtifacts(AbstractFile webDataFile, String dbFilePath, boolean isSchemaV8X, String userName, String browser) { Collection<BlackboardArtifact> bbartifacts = new ArrayList<>(); @@ -890,6 +1509,8 @@ private Collection<BlackboardArtifact> getFormAutofillArtifacts(AbstractFile web Long.valueOf(result.get("date_last_used").toString()))); //NON-NLS } + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), userName)); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, RecentActivityExtracterModuleFactory.getModuleName(), browser)); if (fieldEncrypted) { @@ -1031,4 +1652,24 @@ private boolean isChromePreVersion30(String temps) { return false; } + + @Messages({ + "ExtractFavicon_Display_Name=Favicon" + }) + /** + * Create custom artifact type. + * + * @return the BlackboardArtifact.type of the artifact created + * @throws TskCoreException + */ + private BlackboardArtifact.Type createArtifactType(String artifactName, String displayName) throws TskCoreException { + BlackboardArtifact.Type faviconArtifactType; + try { + faviconArtifactType = tskCase.getBlackboard().getOrAddArtifactType(artifactName, displayName); //NON-NLS + } catch (Blackboard.BlackboardException ex) { + throw new TskCoreException(String.format("An exception was thrown while defining artifact type %s", artifactName), ex); + } + return faviconArtifactType; + } + }