diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoSettings.java b/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoSettings.java index b7ec071f27..d24b6245a6 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoSettings.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/CentralRepoSettings.java @@ -44,6 +44,8 @@ public class CentralRepoSettings { 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 d909bd3f6c..ffc31fff1c 100755 --- a/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineIngestManager.java +++ b/Core/src/org/sleuthkit/autopsy/commandlineingest/CommandLineIngestManager.java @@ -76,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() { } diff --git a/Core/src/org/sleuthkit/autopsy/python/JythonModuleLoader.java b/Core/src/org/sleuthkit/autopsy/python/JythonModuleLoader.java index dab8969c94..beb3e71207 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.ingest.IngestModuleFactory; 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 @@ public final class JythonModuleLoader { } } } + + 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 0000000000..4002abc1e1 --- /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 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.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 3894af6e2e..12b2a1e4bc 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 sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -107,7 +107,7 @@ public class ReportGenerator { 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 class ReportGenerator { 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 modules = new HashMap<>(); for (TableReportModule module : ReportModuleLoader.getTableReportModules()) { @@ -143,38 +146,51 @@ public class ReportGenerator { // 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 modules) { - + public void generateReports(Map 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 class ReportGenerator { 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 class ReportGenerator { // 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 class ReportGenerator { // 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 class ReportGenerator { 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 @@ public class ReportGenerator { 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 @@ public class ReportGenerator { 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 @@ public class ReportGenerator { 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 f2c419eee7..c15a5ceef9 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 42six com @@ -108,7 +108,12 @@ public final class ReportWizardAction extends CallableSystemAction implements Pr Map modules = (Map) 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 d4b188d7db..c2c122f1fa 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.casemodule.CaseDetails; 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 @@ import org.sleuthkit.autopsy.coreutils.FileUtil; */ 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 2388a93446..b9a385e3b8 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} @@ -18,6 +26,7 @@ Chrome.getDownload.errMsg.errAnalyzeFiles1={0}: Error while trying to analyze fi 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 00df8b89d8..43b84b5fe6 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED @@ -4,6 +4,10 @@ cannotParseXml=Unable to parse XML file: ChromeCacheExtract_adding_artifacts_msg=Chrome Cache: Adding %d artifacts for analysis. ChromeCacheExtract_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis. ChromeCacheExtract_loading_files_msg=Chrome Cache: Loading files from %s. +# {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 @@ -87,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} @@ -102,6 +114,7 @@ Chrome.getDownload.errMsg.errAnalyzeFiles1={0}: Error while trying to analyze fi 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. @@ -163,12 +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 diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chromium.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chromium.java index 2133633891..41eefc21f8 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.Map; 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; @@ -88,6 +89,8 @@ class Chromium extends Extract { + " 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"; @@ -95,10 +98,13 @@ class Chromium extends Extract { 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; @@ -107,16 +113,17 @@ class Chromium extends Extract { private Content dataSource; private final IngestJobContext context; + private Map userProfiles; + private Map browserLocations; + private static final Map BROWSERS_MAP = ImmutableMap.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("Google Chrome Profile", "Chrome/User Data/Profile %") - .put("Google Chrome System Profile", "Chrome/User Data/System Profile") + .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", @@ -127,6 +134,8 @@ 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", @@ -146,46 +155,62 @@ class Chromium extends Extract { dataFound = false; long ingestJobId = context.getJobId(); + userProfiles = new HashMap<>(); + browserLocations = new HashMap<>(); for (Map.Entry 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 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(browser.getKey(), browser.getValue(), ingestJobId); + this.getFavicons(browserName, browserLocation, userName, ingestJobId); if (context.dataSourceIngestIsCancelled()) { return; } @@ -196,6 +221,425 @@ class Chromium extends Extract { 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 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 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 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 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 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 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 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 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 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 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 * @@ -203,7 +647,7 @@ class Chromium extends Extract { * @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 historyFiles; @@ -285,7 +729,7 @@ class Chromium extends Extract { result.get("title") == null ? "" : result.get("title").toString(), browserName, extractedDomain, - ""); + userName); bbartifacts.add(createArtifactWithAttributes(BlackboardArtifact.Type.TSK_WEB_HISTORY, historyFile, bbattributes)); } catch (TskCoreException ex) { @@ -307,7 +751,7 @@ class Chromium extends Extract { * @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 bookmarkFiles; String browserName = browser; @@ -378,15 +822,13 @@ class Chromium extends Extract { } 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 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", @@ -394,54 +836,64 @@ class Chromium extends Extract { 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 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)); + Set 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 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); } @@ -457,7 +909,7 @@ class Chromium extends Extract { * @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 cookiesFiles; @@ -540,6 +992,8 @@ class Chromium extends Extract { 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)); @@ -563,7 +1017,7 @@ class Chromium extends Extract { * @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 downloadFiles; String browserName = browser; @@ -655,6 +1109,8 @@ class Chromium extends Extract { 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(), browserName)); @@ -687,7 +1143,7 @@ class Chromium extends Extract { * @param browserLocation * @param ingestJobId The ingest job id. */ - private void getFavicons(String browser, String browserLocation, long ingestJobId) { + private void getFavicons(String browser, String browserLocation, String userName, long ingestJobId) { FileManager fileManager = currentCase.getServices().getFileManager(); List faviconFiles; String browserName = browser; @@ -744,7 +1200,7 @@ class Chromium extends Extract { BlackboardArtifact.Type faviconArtifactType; try { - faviconArtifactType = createFaviconArtifactType(); + 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")); @@ -771,6 +1227,8 @@ class Chromium extends Extract { 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)); @@ -797,7 +1255,7 @@ class Chromium extends Extract { * @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 loginDataFiles; @@ -886,6 +1344,9 @@ class Chromium extends Extract { bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, 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)); } catch (TskCoreException ex) { @@ -909,7 +1370,7 @@ class Chromium extends Extract { * @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 webDataFiles; @@ -972,7 +1433,7 @@ class Chromium extends Extract { boolean isSchemaV8X = Util.checkColumn("date_created", "autofill", tempFilePath); // get form autofill artifacts - bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X, browserName)); + bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X, userName, browserName)); try { // get form address atifacts getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X); @@ -1010,7 +1471,7 @@ class Chromium extends Extract { * * @return collection of TSK_WEB_FORM_AUTOFILL artifacts */ - private Collection getFormAutofillArtifacts(AbstractFile webDataFile, String dbFilePath, boolean isSchemaV8X, String browser) { + private Collection getFormAutofillArtifacts(AbstractFile webDataFile, String dbFilePath, boolean isSchemaV8X, String userName, String browser) { Collection bbartifacts = new ArrayList<>(); @@ -1048,6 +1509,8 @@ class Chromium extends Extract { 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) { @@ -1194,17 +1657,17 @@ class Chromium extends Extract { "ExtractFavicon_Display_Name=Favicon" }) /** - * Create TSK_FAVICON artifact type. + * Create custom artifact type. * * @return the BlackboardArtifact.type of the artifact created * @throws TskCoreException */ - private BlackboardArtifact.Type createFaviconArtifactType() throws TskCoreException { + private BlackboardArtifact.Type createArtifactType(String artifactName, String displayName) throws TskCoreException { BlackboardArtifact.Type faviconArtifactType; try { - faviconArtifactType = tskCase.getBlackboard().getOrAddArtifactType(FAVICON_ARTIFACT_NAME, Bundle.ExtractFavicon_Display_Name()); //NON-NLS + 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", FAVICON_ARTIFACT_NAME), ex); + throw new TskCoreException(String.format("An exception was thrown while defining artifact type %s", artifactName), ex); } return faviconArtifactType; }