From 8e48ac03e68d33d73b4f9d7967a8f493b1891e3a Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Thu, 21 Nov 2019 13:14:25 -0500 Subject: [PATCH 01/13] 5780: Context content viewer to show source of message attachments and web downloaded files. - This includes replacing the TSK_DOWNLOAD_SOURCE artifact with TSK_ASSOCIATED_OBJECT artifact, in the RecentActivity module. --- .../autopsy/contentviewers/ContextViewer.form | 95 +++++ .../autopsy/contentviewers/ContextViewer.java | 357 ++++++++++++++++++ .../autopsy/contentviewers/Metadata.java | 21 +- .../autopsy/datamodel/ExtractedContent.java | 4 +- .../autopsy/recentactivity/Chrome.java | 32 +- .../recentactivity/ChromeCacheExtractor.java | 38 +- .../autopsy/recentactivity/ExtractSafari.java | 17 +- .../recentactivity/ExtractZoneIdentifier.java | 63 ++-- .../autopsy/recentactivity/Firefox.java | 63 ++-- 9 files changed, 579 insertions(+), 111 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form create mode 100644 Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form new file mode 100644 index 0000000000..c7f826dc9e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form @@ -0,0 +1,95 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java new file mode 100644 index 0000000000..3c2e4e526f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java @@ -0,0 +1,357 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 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.contentviewers; + +import java.awt.Component; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import org.apache.commons.lang.StringUtils; +import org.openide.nodes.Node; +import org.openide.util.NbBundle; +import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * Displays additional context for the selected file, such as its source, and + * usage, if known. + * + */ +@ServiceProvider(service = DataContentViewer.class, position = 7) +@NbBundle.Messages({ + "ContextViewer.title=Context Viewer", + "ContextViewer.toolTip=Displays context for selected file." +}) +public final class ContextViewer extends javax.swing.JPanel implements DataContentViewer { + + private static final long serialVersionUID = 1L; + private static final Logger logger = Logger.getLogger(ContextViewer.class.getName()); + + // defines a list of artifacts that provide context for a file + private static final List SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>(); + + static { + SOURCE_CONTEXT_ARTIFACTS.add(TSK_ASSOCIATED_OBJECT); + } + + private BlackboardArtifact sourceContextArtifact; + + /** + * Creates new form ContextViewer + */ + public ContextViewer() { + + initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jSourceGoToResultButton = new javax.swing.JButton(); + jLabel1 = new javax.swing.JLabel(); + jSourceNameLabel = new javax.swing.JLabel(); + jSourceTextLabel = new javax.swing.JLabel(); + + org.openide.awt.Mnemonics.setLocalizedText(jSourceGoToResultButton, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceGoToResultButton.text")); // NOI18N + jSourceGoToResultButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jSourceGoToResultButtonActionPerformed(evt); + } + }); + + jLabel1.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jLabel1.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jSourceNameLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceNameLabel.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(jSourceTextLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceTextLabel.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel1) + .addGroup(layout.createSequentialGroup() + .addGap(6, 6, 6) + .addComponent(jSourceNameLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jSourceTextLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 192, Short.MAX_VALUE))) + .addGap(36, 36, 36)) + .addGroup(layout.createSequentialGroup() + .addComponent(jSourceGoToResultButton) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jSourceNameLabel) + .addComponent(jSourceTextLabel)) + .addGap(18, 18, 18) + .addComponent(jSourceGoToResultButton) + .addGap(0, 203, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + private void jSourceGoToResultButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jSourceGoToResultButtonActionPerformed + + final DirectoryTreeTopComponent dtc = DirectoryTreeTopComponent.findInstance(); + + // Navigate to the source context artifact. + if (sourceContextArtifact != null) { + dtc.viewArtifact(sourceContextArtifact); + } + + }//GEN-LAST:event_jSourceGoToResultButtonActionPerformed + + @Override + public void setNode(Node selectedNode) { + if ((selectedNode == null) || (!isSupported(selectedNode))) { + resetComponent(); + return; + } + + AbstractFile file = selectedNode.getLookup().lookup(AbstractFile.class); + try { + populateSourceContextData(file); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.SEVERE, "Exception displaying context for file {0}", file); //NON-NLS + } + } + + @Override + public String getTitle() { + return Bundle.ContextViewer_title(); + } + + @Override + public String getToolTip() { + return Bundle.ContextViewer_toolTip(); + } + + @Override + public DataContentViewer createInstance() { + return new ContextViewer(); + } + + @Override + public Component getComponent() { + return this; + } + + @Override + public void resetComponent() { + setSourceName(""); + setSourceText(""); + } + + @Override + public boolean isSupported(Node node) { + + // check if the node has an abstract file and the file has any context defining artifacts. + if (node.getLookup().lookup(AbstractFile.class) != null) { + AbstractFile abstractFile = node.getLookup().lookup(AbstractFile.class); + for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SOURCE_CONTEXT_ARTIFACTS) { + List artifactsList; + try { + artifactsList = abstractFile.getArtifacts(artifactType); + if (!artifactsList.isEmpty()) { + return true; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Exception while looking up context artifacts for file {0}", abstractFile); //NON-NLS + } + } + + } + + return false; + } + + @Override + public int isPreferred(Node node) { + return 1; + } + + private void populateSourceContextData(AbstractFile sourceFile) throws NoCurrentCaseException, TskCoreException { + + SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); + + // Check for all context artifacts + boolean foundASource = false; + for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SOURCE_CONTEXT_ARTIFACTS) { + List artifactsList = tskCase.getBlackboardArtifacts(artifactType, sourceFile.getId()); + if (!artifactsList.isEmpty()) { + foundASource = true; + } + for (BlackboardArtifact contextArtifact : artifactsList) { + addSourceEntry(contextArtifact); + } + } + if (foundASource == false) { + setSourceName("Unknown"); + showSourceText(false); + } + } + + @NbBundle.Messages({ + "ContextViewer.attachmentSource=Attached to: ", + "ContextViewer.downloadSource=Downloaded from: " + }) + private void addSourceEntry(BlackboardArtifact artifact) throws TskCoreException { + + if (BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID() == artifact.getArtifactTypeID()) { + BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); + if (associatedArtifactAttribute != null) { + long artifactId = associatedArtifactAttribute.getValueLong(); + BlackboardArtifact associatedArtifact = artifact.getSleuthkitCase().getBlackboardArtifact(artifactId); + + //save the artifact id for "Go to Result" button + sourceContextArtifact = associatedArtifact; + + if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID() + || BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == associatedArtifact.getArtifactTypeID()) { + + setSourceName(Bundle.ContextViewer_attachmentSource()); + setSourceText(msgArtifactToAbbreiviatedString(associatedArtifact)); + + } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID() + || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == associatedArtifact.getArtifactTypeID()) { + + setSourceName(Bundle.ContextViewer_downloadSource()); + setSourceText(webDownloadArtifactToString(associatedArtifact)); + } + } + } + + } + + private void setSourceName(String nameLabel) { + jSourceNameLabel.setText(nameLabel); + } + + private void setSourceText(String text) { + jSourceTextLabel.setText(text); + showSourceText(true); + } + + private void showSourceText(boolean isVisible) { + jSourceTextLabel.setVisible(isVisible); + } + + private String webDownloadArtifactToString(BlackboardArtifact artifact) throws TskCoreException { + StringBuilder sb = new StringBuilder(1024); + Map attributesMap = getAttributesMap(artifact); + + if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == artifact.getArtifactTypeID() + || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == artifact.getArtifactTypeID()) { + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, attributesMap, "URL"); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, attributesMap, "On"); + } + return sb.toString(); + } + + private String msgArtifactToAbbreiviatedString(BlackboardArtifact artifact) throws TskCoreException { + + StringBuilder sb = new StringBuilder(1024); + Map attributesMap = getAttributesMap(artifact); + + if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == artifact.getArtifactTypeID()) { + sb.append("Message "); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, attributesMap, "From"); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, attributesMap, "To"); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, attributesMap, "On"); + } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == artifact.getArtifactTypeID()) { + sb.append("Email "); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM, attributesMap, "From"); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO, attributesMap, "To"); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT, attributesMap, "On"); + } + return sb.toString(); + } + + private void appendAttributeString(StringBuilder sb, BlackboardAttribute.ATTRIBUTE_TYPE attribType, + Map attributesMap, String prependStr) { + + BlackboardAttribute attribute = attributesMap.get(attribType); + if (attribute != null) { + String attrVal = attribute.getDisplayString(); + if (!StringUtils.isEmpty(attrVal)) { + if (!StringUtils.isEmpty(prependStr)) { + sb.append(prependStr).append(" "); + } + sb.append(StringUtils.abbreviate(attrVal, 200)).append(" "); + } + } + } + + private String getAttribNameValue(BlackboardAttribute.ATTRIBUTE_TYPE attribType, Map attributesMap) { + BlackboardAttribute attribute = attributesMap.get(attribType); + if (attribute != null) { + return String.format("%s : %s", attribType.getDisplayName(), attribute.getDisplayString()); + } + return null; + } + + private Map getAttributesMap(BlackboardArtifact artifact) throws TskCoreException { + Map attributeMap = new HashMap<>(); + + List attributeList = artifact.getAttributes(); + for (BlackboardAttribute attribute : attributeList) { + BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID()); + attributeMap.put(type, attribute); + } + + return attributeMap; + } + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel jLabel1; + private javax.swing.JButton jSourceGoToResultButton; + private javax.swing.JLabel jSourceNameLabel; + private javax.swing.JLabel jSourceTextLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java index 126299f120..5f6c3eac9b 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java @@ -191,12 +191,21 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer { } try { - List sourceArtifacts = file.getArtifacts(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - if (!sourceArtifacts.isEmpty()) { - BlackboardArtifact artifact = sourceArtifacts.get(0); - BlackboardAttribute urlAttr = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_URL)); - if (urlAttr != null) { - addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.downloadSource"), urlAttr.getValueString()); + List associatedObjectArtifacts = file.getArtifacts(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + if (!associatedObjectArtifacts.isEmpty()) { + BlackboardArtifact artifact = associatedObjectArtifacts.get(0); + BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); + if (associatedArtifactAttribute != null) { + long artifactId = associatedArtifactAttribute.getValueLong(); + BlackboardArtifact associatedArtifact = artifact.getSleuthkitCase().getBlackboardArtifact(artifactId); + if (associatedArtifact != null && + ((associatedArtifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()) || + (associatedArtifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID())) ) { + BlackboardAttribute urlAttr = associatedArtifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_URL)); + if (urlAttr != null) { + addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.downloadSource"), urlAttr.getValueString()); + } + } } } } catch (TskCoreException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index e52e50c31c..ada8d91b36 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -238,8 +238,8 @@ public class ExtractedContent implements AutopsyVisitableItem { doNotShow.add(new BlackboardArtifact.Type(TSK_INTERESTING_ARTIFACT_HIT)); doNotShow.add(new BlackboardArtifact.Type(TSK_ACCOUNT)); doNotShow.add(new BlackboardArtifact.Type(TSK_DATA_SOURCE_USAGE)); - doNotShow.add(new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE)); - doNotShow.add(new BlackboardArtifact.Type(TSK_ASSOCIATED_OBJECT)); + //doNotShow.add(new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE)); + //doNotShow.add(new BlackboardArtifact.Type(TSK_ASSOCIATED_OBJECT)); } private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java index f75132c610..49ac9911fe 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java @@ -554,22 +554,24 @@ class Chrome extends Extract { RecentActivityExtracterModuleFactory.getModuleName(), NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); - BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes); - if (bbart != null) { - bbartifacts.add(bbart); - } - - // find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it.. - try { - for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(fullPath), FilenameUtils.getPath(fullPath))) { - BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - downloadSourceArt.addAttributes(createDownloadSourceAttributes(result.get("url").toString())); - - bbartifacts.add(downloadSourceArt); - break; + BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes); + if (webDownloadArtifact != null) { + bbartifacts.add(webDownloadArtifact); + + // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. + try { + for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(fullPath), FilenameUtils.getPath(fullPath))) { + BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + associatedObjectArtifact.addAttribute( + new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, + RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID())); + + bbartifacts.add(associatedObjectArtifact); + break; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'", fullPath), ex); //NON-NLS } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Error creating download source artifact for file '%s'", fullPath), ex); //NON-NLS } } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java index efaf8934b2..4e1b5c3931 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ChromeCacheExtractor.java @@ -380,12 +380,12 @@ final class ChromeCacheExtractor { * Extracts the files if needed and adds as derived files, creates artifacts * * @param cacheEntryAddress cache entry address - * @param sourceArtifacts any source artifacts created are added to this collection + * @param associatedObjectArtifacts any associated object artifacts created are added to this collection * @param webCacheArtifacts any web cache artifacts created are added to this collection * * @return Optional derived file, is a derived file is added for the given entry */ - private List processCacheEntry(CacheAddress cacheEntryAddress, Collection sourceArtifacts, Collection webCacheArtifacts ) throws TskCoreException, IngestModuleException { + private List processCacheEntry(CacheAddress cacheEntryAddress, Collection associatedObjectArtifacts, Collection webCacheArtifacts ) throws TskCoreException, IngestModuleException { List derivedFiles = new ArrayList<>(); @@ -437,10 +437,6 @@ final class ChromeCacheExtractor { moduleName, cacheEntry.getHTTPHeaders()); - Collection sourceArtifactAttributes = new ArrayList<>(); - sourceArtifactAttributes.add(urlAttr); - sourceArtifactAttributes.add(createTimeAttr); - Collection webCacheAttributes = new ArrayList<>(); webCacheAttributes.add(urlAttr); webCacheAttributes.add(createTimeAttr); @@ -450,12 +446,7 @@ final class ChromeCacheExtractor { // add artifacts to the f_XXX file if (dataSegment.isInExternalFile() ) { try { - BlackboardArtifact sourceArtifact = cachedFileAbstractFile.get().newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - if (sourceArtifact != null) { - sourceArtifact.addAttributes(sourceArtifactAttributes); - sourceArtifacts.add(sourceArtifact); - } - + BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE); if (webCacheArtifact != null) { webCacheArtifact.addAttributes(webCacheAttributes); @@ -469,6 +460,14 @@ final class ChromeCacheExtractor { moduleName, cachedFileAbstractFile.get().getId())); webCacheArtifacts.add(webCacheArtifact); + + BlackboardArtifact associatedObjectArtifact = cachedFileAbstractFile.get().newArtifact(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + if (associatedObjectArtifact != null) { + associatedObjectArtifact.addAttribute( + new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, + moduleName, webCacheArtifact.getArtifactID())); + associatedObjectArtifacts.add(associatedObjectArtifact); + } } if (isBrotliCompressed) { @@ -497,12 +496,7 @@ final class ChromeCacheExtractor { "", TskData.EncodingType.NONE); - BlackboardArtifact sourceArtifact = derivedFile.newArtifact(ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - if (sourceArtifact != null) { - sourceArtifact.addAttributes(sourceArtifactAttributes); - sourceArtifacts.add(sourceArtifact); - } - + BlackboardArtifact webCacheArtifact = cacheEntryFile.get().getAbstractFile().newArtifact(ARTIFACT_TYPE.TSK_WEB_CACHE); if (webCacheArtifact != null) { webCacheArtifact.addAttributes(webCacheAttributes); @@ -516,6 +510,14 @@ final class ChromeCacheExtractor { moduleName, derivedFile.getId())); webCacheArtifacts.add(webCacheArtifact); + + BlackboardArtifact associatedObjectArtifact = derivedFile.newArtifact(ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + if (associatedObjectArtifact != null) { + associatedObjectArtifact.addAttribute( + new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, + moduleName, webCacheArtifact.getArtifactID())); + associatedObjectArtifacts.add(associatedObjectArtifact); + } } if (isBrotliCompressed) { diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java index 704826047f..c11e4fde22 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractSafari.java @@ -50,6 +50,7 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.autopsy.recentactivity.BinaryCookieReader.Cookie; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; import org.xml.sax.SAXException; @@ -637,15 +638,17 @@ final class ExtractSafari extends Extract { time = date.getDate().getTime(); } - BlackboardArtifact bbart = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD); - bbart.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName())); - bbartifacts.add(bbart); + BlackboardArtifact webDownloadArtifact = origFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD); + webDownloadArtifact.addAttributes(this.createDownloadAttributes(path, pathID, url, time, NetworkUtils.extractDomain(url), getName())); + bbartifacts.add(webDownloadArtifact); - // find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it. + // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(path), FilenameUtils.getPath(path))) { - BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - downloadSourceArt.addAttributes(createDownloadSourceAttributes(url)); - bbartifacts.add(downloadSourceArt); + BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + associatedObjectArtifact.addAttribute( + new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, + RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID())); + bbartifacts.add(associatedObjectArtifact); break; } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java index 250c55fa6e..51e3a8eff9 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java @@ -36,9 +36,11 @@ import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD; import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID; @@ -94,7 +96,7 @@ final class ExtractZoneIdentifier extends Extract { return; } - Collection sourceArtifacts = new ArrayList<>(); + Collection associatedObjectArtifacts = new ArrayList<>(); Collection downloadArtifacts = new ArrayList<>(); for (AbstractFile zoneFile : zoneFiles) { @@ -104,7 +106,7 @@ final class ExtractZoneIdentifier extends Extract { } try { - processZoneFile(context, dataSource, zoneFile, sourceArtifacts, downloadArtifacts, knownPathIDs); + processZoneFile(context, dataSource, zoneFile, associatedObjectArtifacts, downloadArtifacts, knownPathIDs); } catch (TskCoreException ex) { addErrorMessage(Bundle.ExtractZone_process_errMsg()); String message = String.format("Failed to process zone identifier file %s", zoneFile.getName()); //NON-NLS @@ -112,23 +114,23 @@ final class ExtractZoneIdentifier extends Extract { } } - postArtifacts(sourceArtifacts); + postArtifacts(associatedObjectArtifacts); postArtifacts(downloadArtifacts); } /** * Process a single Zone Identifier file. * - * @param context IngetJobContext - * @param dataSource Content - * @param zoneFile Zone Indentifier file - * @param sourceArtifacts List for TSK_DOWNLOAD_SOURCE artifacts - * @param downloadArtifacts List for TSK_WEB_DOWNLOAD aritfacts - * + * @param context IngetJobContext + * @param dataSource Content + * @param zoneFile Zone Indentifier file + * @param associatedObjectArtifacts List for TSK_ASSOCIATED_OBJECT artifacts + * @param downloadArtifacts List for TSK_WEB_DOWNLOAD artifacts + * * @throws TskCoreException */ private void processZoneFile(IngestJobContext context, Content dataSource, - AbstractFile zoneFile, Collection sourceArtifacts, + AbstractFile zoneFile, Collection associatedObjectArtifacts, Collection downloadArtifacts, Set knownPathIDs) throws TskCoreException { @@ -155,16 +157,16 @@ final class ExtractZoneIdentifier extends Extract { BlackboardArtifact downloadBba = createDownloadArtifact(zoneFile, zoneInfo); if (downloadBba != null) { downloadArtifacts.add(downloadBba); + // create a TSK_ASSOCIATED_OBJECT for the downloaded file, associating it with the TSK_WEB_DOWNLOAD artifact. + if (downloadFile.getArtifactsCount(TSK_ASSOCIATED_OBJECT) == 0) { + BlackboardArtifact associatedObjectBba = createAssociatedObjectArtifact(downloadFile, downloadBba); + if (associatedObjectBba != null) { + associatedObjectArtifacts.add(associatedObjectBba); + } + } } } - // check if download has a child TSK_DOWNLOAD_SOURCE artifact, if not create one - if (downloadFile.getArtifactsCount(TSK_DOWNLOAD_SOURCE) == 0) { - BlackboardArtifact sourceBba = createDownloadSourceArtifact(downloadFile, zoneInfo); - if (sourceBba != null) { - sourceArtifacts.add(sourceBba); - } - } } } @@ -203,33 +205,26 @@ final class ExtractZoneIdentifier extends Extract { } /** - * Create a Download Source Artifact for the given ZoneIdentifierInfo + * Create a Associated Object Artifact for the given ZoneIdentifierInfo * object. * * @param downloadFile AbstractFile representing the file downloaded, not - * the zone identifier file. - * @param zoneInfo Zone identifier file wrapper object + * the zone identifier file. + * @param downloadBba TSK_WEB_DOWNLOAD artifact to associate with. * - * @return TSK_DOWNLOAD_SOURCE object for given parameters + * @return TSK_ASSOCIATED_OBJECT artifact. */ - private BlackboardArtifact createDownloadSourceArtifact(AbstractFile downloadFile, ZoneIdentifierInfo zoneInfo) { + private BlackboardArtifact createAssociatedObjectArtifact(AbstractFile downloadFile, BlackboardArtifact downloadBba) { Collection bbattributes = new ArrayList<>(); bbattributes.addAll(Arrays.asList( - new BlackboardAttribute(TSK_URL, - RecentActivityExtracterModuleFactory.getModuleName(), - StringUtils.defaultString(zoneInfo.getURL(), "")), - - new BlackboardAttribute(TSK_DOMAIN, - RecentActivityExtracterModuleFactory.getModuleName(), - (zoneInfo.getURL() != null) ? NetworkUtils.extractDomain(zoneInfo.getURL()) : ""), - - new BlackboardAttribute(TSK_LOCATION, - RecentActivityExtracterModuleFactory.getModuleName(), - StringUtils.defaultString(zoneInfo.getZoneIdAsString(), "")))); //NON-NLS + new BlackboardAttribute(TSK_ASSOCIATED_ARTIFACT, + RecentActivityExtracterModuleFactory.getModuleName(), + downloadBba.getArtifactID()) + )); - return createArtifactWithAttributes(TSK_DOWNLOAD_SOURCE, downloadFile, bbattributes); + return createArtifactWithAttributes(TSK_ASSOCIATED_OBJECT, downloadFile, bbattributes); } /** diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java index dd4e70ee06..7fddeb0aa8 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Firefox.java @@ -544,22 +544,25 @@ class Firefox extends Extract { domain)); //NON-NLS } - BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); - if (bbart != null) { - bbartifacts.add(bbart); - } - - // find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it. - try { - for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) { - BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - downloadSourceArt.addAttributes(createDownloadSourceAttributes(source)); - bbartifacts.add(downloadSourceArt); - break; + BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); + if (webDownloadArtifact != null) { + bbartifacts.add(webDownloadArtifact); + + // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. + try { + for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) { + BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + associatedObjectArtifact.addAttribute( + new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, + RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID())); + + bbartifacts.add(associatedObjectArtifact); + break; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'", + downloadedFilePath), ex); //NON-NLS } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Error creating download source artifact for file '%s'", - downloadedFilePath), ex); //NON-NLS } } if (errors > 0) { @@ -681,22 +684,24 @@ class Firefox extends Extract { RecentActivityExtracterModuleFactory.getModuleName(), domain)); //NON-NLS } - BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); - if (bbart != null) { - bbartifacts.add(bbart); - } + BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes); + if (webDownloadArtifact != null) { + bbartifacts.add(webDownloadArtifact); - // find the downloaded file and create a TSK_DOWNLOAD_SOURCE for it. - try { - for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) { - BlackboardArtifact downloadSourceArt = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE); - downloadSourceArt.addAttributes(createDownloadSourceAttributes(url)); - bbartifacts.add(downloadSourceArt); - break; + // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. + try { + for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) { + BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + associatedObjectArtifact.addAttribute( + new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, + RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID())); + bbartifacts.add(associatedObjectArtifact); + break; + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error creating associated object artifact for file '%s'", + downloadedFilePath), ex); //NON-NLS } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, String.format("Error creating download source artifact for file '%s'", - downloadedFilePath), ex); //NON-NLS } } if (errors > 0) { From 2ef14e2554d2aed692905d913b017f5732f0c943 Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Thu, 21 Nov 2019 21:50:52 -0500 Subject: [PATCH 02/13] Addressed Codacy comments in previous commit, added more documentation. --- .../autopsy/contentviewers/Bundle.properties | 4 + .../autopsy/contentviewers/ContextViewer.java | 120 ++++++++++++++---- .../autopsy/contentviewers/Metadata.java | 29 +++-- .../autopsy/datamodel/ExtractedContent.java | 4 +- .../recentactivity/ExtractZoneIdentifier.java | 5 - 5 files changed, 122 insertions(+), 40 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index b3a384ce48..f1e154fb40 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -93,3 +93,7 @@ MediaPlayerPanel.playButton.text=\u25ba MediaPlayerPanel.infoLabel.text=No Errors MediaPlayerPanel.VolumeIcon.text=Volume MediaPlayerPanel.playBackSpeedLabel.text=Speed: +ContextViewer.jSourceGoToResultButton.text=Go to Result +ContextViewer.jLabel1.text=Source +ContextViewer.jSourceNameLabel.text=jSourceNameLabel +ContextViewer.jSourceTextLabel.text=jLabel2 \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java index 3c2e4e526f..ef3b5025c7 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java @@ -215,6 +215,15 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte return 1; } + /** + * Looks for context providing artifacts for the given file and populates + * the source context. + * + * @param sourceFile File for which to show the context. + * + * @throws NoCurrentCaseException + * @throws TskCoreException + */ private void populateSourceContextData(AbstractFile sourceFile) throws NoCurrentCaseException, TskCoreException { SleuthkitCase tskCase = Case.getCurrentCaseThrows().getSleuthkitCase(); @@ -240,38 +249,68 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte "ContextViewer.attachmentSource=Attached to: ", "ContextViewer.downloadSource=Downloaded from: " }) - private void addSourceEntry(BlackboardArtifact artifact) throws TskCoreException { + /** + * Adds a source context entry for the selected file based on the given context + * providing artifact. + * + * @param artifact Artifact that may provide context. + * + * @throws NoCurrentCaseException + * @throws TskCoreException + */ + private void addSourceEntry(BlackboardArtifact artifact) throws TskCoreException { if (BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT.getTypeID() == artifact.getArtifactTypeID()) { BlackboardAttribute associatedArtifactAttribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); if (associatedArtifactAttribute != null) { long artifactId = associatedArtifactAttribute.getValueLong(); BlackboardArtifact associatedArtifact = artifact.getSleuthkitCase().getBlackboardArtifact(artifactId); - //save the artifact id for "Go to Result" button + //save the artifact for "Go to Result" button sourceContextArtifact = associatedArtifact; - if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID() - || BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == associatedArtifact.getArtifactTypeID()) { - - setSourceName(Bundle.ContextViewer_attachmentSource()); - setSourceText(msgArtifactToAbbreiviatedString(associatedArtifact)); - - } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID() - || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == associatedArtifact.getArtifactTypeID()) { - - setSourceName(Bundle.ContextViewer_downloadSource()); - setSourceText(webDownloadArtifactToString(associatedArtifact)); - } + setSourceFields(associatedArtifact); } } - } + /** + * Sets the source label and text fields based on the given associated + * artifact. + * + * @param associatedArtifact - associated artifact + * + * @throws TskCoreException + */ + private void setSourceFields(BlackboardArtifact associatedArtifact) throws TskCoreException { + if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID() + || BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == associatedArtifact.getArtifactTypeID()) { + + setSourceName(Bundle.ContextViewer_attachmentSource()); + setSourceText(msgArtifactToAbbreiviatedString(associatedArtifact)); + + } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID() + || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == associatedArtifact.getArtifactTypeID()) { + + setSourceName(Bundle.ContextViewer_downloadSource()); + setSourceText(webDownloadArtifactToString(associatedArtifact)); + } + } + + /** + * Sets the source label string. + * + * @param nameLabel String value for source label. + */ private void setSourceName(String nameLabel) { jSourceNameLabel.setText(nameLabel); } + /** + * Sets the source text string. + * + * @param nameLabel String value for source text. + */ private void setSourceText(String text) { jSourceTextLabel.setText(text); showSourceText(true); @@ -281,6 +320,16 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte jSourceTextLabel.setVisible(isVisible); } + /** + * Returns a display string with download source URL from the given + * artifact. + * + * @param artifact artifact to get download source URL from. + * + * @return Display string with download URL and date/time. + * + * @throws TskCoreException + */ private String webDownloadArtifactToString(BlackboardArtifact artifact) throws TskCoreException { StringBuilder sb = new StringBuilder(1024); Map attributesMap = getAttributesMap(artifact); @@ -293,6 +342,15 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte return sb.toString(); } + /** + * Returns a abbreviated display string for a message artifact. + * + * @param artifact artifact to get download source URL from. + * + * @return Display string for message artifact. + * + * @throws TskCoreException + */ private String msgArtifactToAbbreiviatedString(BlackboardArtifact artifact) throws TskCoreException { StringBuilder sb = new StringBuilder(1024); @@ -312,6 +370,16 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte return sb.toString(); } + /** + * Looks up specified attribute in the given map and, if found, appends its + * value to the given string builder. + * + * @param sb String builder to append to. + * @param attribType Attribute type to look for. + * @param attributesMap Attributes map. + * @param prependStr Optional string that is prepended before the attribute + * value. + */ private void appendAttributeString(StringBuilder sb, BlackboardAttribute.ATTRIBUTE_TYPE attribType, Map attributesMap, String prependStr) { @@ -320,21 +388,23 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte String attrVal = attribute.getDisplayString(); if (!StringUtils.isEmpty(attrVal)) { if (!StringUtils.isEmpty(prependStr)) { - sb.append(prependStr).append(" "); + sb.append(prependStr).append(' '); } - sb.append(StringUtils.abbreviate(attrVal, 200)).append(" "); + sb.append(StringUtils.abbreviate(attrVal, 200)).append(' '); } } } - private String getAttribNameValue(BlackboardAttribute.ATTRIBUTE_TYPE attribType, Map attributesMap) { - BlackboardAttribute attribute = attributesMap.get(attribType); - if (attribute != null) { - return String.format("%s : %s", attribType.getDisplayName(), attribute.getDisplayString()); - } - return null; - } - + /** + * Gets all attributes for the given artifact, and returns a map of + * attributes keyed by attribute type. + * + * @param artifact Artifact for which to get the attributes. + * + * @return Map of attribute type and value. + * + * @throws TskCoreException + */ private Map getAttributesMap(BlackboardArtifact artifact) throws TskCoreException { Map attributeMap = new HashMap<>(); diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java index 5f6c3eac9b..796386a5b3 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java @@ -198,14 +198,7 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer { if (associatedArtifactAttribute != null) { long artifactId = associatedArtifactAttribute.getValueLong(); BlackboardArtifact associatedArtifact = artifact.getSleuthkitCase().getBlackboardArtifact(artifactId); - if (associatedArtifact != null && - ((associatedArtifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()) || - (associatedArtifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID())) ) { - BlackboardAttribute urlAttr = associatedArtifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_URL)); - if (urlAttr != null) { - addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.downloadSource"), urlAttr.getValueString()); - } - } + addDownloadSourceRow(sb, associatedArtifact); } } } catch (TskCoreException ex) { @@ -301,6 +294,26 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer { this.setCursor(null); } + /** + * Adds a row for download source from the given associated artifact, + * if the associated artifacts specifies a source. + * + * @param sb string builder. + * @param associatedArtifact + * + * @throws TskCoreException if there is an error + */ + private void addDownloadSourceRow(StringBuilder sb, BlackboardArtifact associatedArtifact ) throws TskCoreException { + if (associatedArtifact != null && + ((associatedArtifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()) || + (associatedArtifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID())) ) { + BlackboardAttribute urlAttr = associatedArtifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_URL)); + if (urlAttr != null) { + addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.downloadSource"), urlAttr.getValueString()); + } + } + } + /** * Add the acquisition details to the results (if applicable) * diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index ada8d91b36..e52e50c31c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -238,8 +238,8 @@ public class ExtractedContent implements AutopsyVisitableItem { doNotShow.add(new BlackboardArtifact.Type(TSK_INTERESTING_ARTIFACT_HIT)); doNotShow.add(new BlackboardArtifact.Type(TSK_ACCOUNT)); doNotShow.add(new BlackboardArtifact.Type(TSK_DATA_SOURCE_USAGE)); - //doNotShow.add(new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE)); - //doNotShow.add(new BlackboardArtifact.Type(TSK_ASSOCIATED_OBJECT)); + doNotShow.add(new BlackboardArtifact.Type(TSK_DOWNLOAD_SOURCE)); + doNotShow.add(new BlackboardArtifact.Type(TSK_ASSOCIATED_OBJECT)); } private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java index 51e3a8eff9..5062806133 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractZoneIdentifier.java @@ -28,7 +28,6 @@ import java.util.List; import java.util.Properties; import java.util.Set; import java.util.logging.Level; -import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.NetworkUtils; @@ -37,14 +36,10 @@ import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; -import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_DOWNLOAD_SOURCE; import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD; import org.sleuthkit.datamodel.BlackboardAttribute; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.TskCoreException; From b737ab86b235938705eccee1125ca7232b5e07ee Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Fri, 22 Nov 2019 10:25:21 -0500 Subject: [PATCH 03/13] Update PlasoIngestModule.java Move code to check if datasource is image to process instead of starup and fail gracefully if it is not. --- .../modules/plaso/PlasoIngestModule.java | 203 +++++++++--------- 1 file changed, 105 insertions(+), 98 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java index c9fad6c1bc..e9ace4fde5 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java @@ -85,7 +85,7 @@ public class PlasoIngestModule implements DataSourceIngestModule { private static final int LOG2TIMELINE_WORKERS = 2; private static final long TERMINATION_CHECK_INTERVAL = 5; private static final TimeUnit TERMINATION_CHECK_INTERVAL_UNITS = TimeUnit.SECONDS; - + private File log2TimeLineExecutable; private File psortExecutable; @@ -103,8 +103,7 @@ public class PlasoIngestModule implements DataSourceIngestModule { @NbBundle.Messages({ "PlasoIngestModule.executable.not.found=Plaso Executable Not Found.", - "PlasoIngestModule.requires.windows=Plaso module requires windows.", - "PlasoIngestModule.dataSource.not.an.image=Datasource is not an Image."}) + "PlasoIngestModule.requires.windows=Plaso module requires windows."}) @Override public void startUp(IngestJobContext context) throws IngestModuleException { this.context = context; @@ -121,11 +120,6 @@ public class PlasoIngestModule implements DataSourceIngestModule { throw new IngestModuleException(Bundle.PlasoIngestModule_executable_not_found(), exception); } - Content dataSource = context.getDataSource(); - if (!(dataSource instanceof Image)) { - throw new IngestModuleException(Bundle.PlasoIngestModule_dataSource_not_an_image()); - } - image = (Image) dataSource; } @NbBundle.Messages({ @@ -139,79 +133,89 @@ public class PlasoIngestModule implements DataSourceIngestModule { "PlasoIngestModule.bad.imageFile=Cannot find image file name and path", "PlasoIngestModule.completed=Plaso Processing Completed", "PlasoIngestModule.has.run=Plaso Plugin has been run.", - "PlasoIngestModule.psort.fail=Plaso returned an error when sorting events. Results are not complete."}) + "PlasoIngestModule.psort.fail=Plaso returned an error when sorting events. Results are not complete.", + "PlasoIngestModule.dataSource.not.an.image=Datasource is not an Image."}) @Override public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) { - assert dataSource.equals(image); - statusHelper.switchToDeterminate(100); - currentCase = Case.getCurrentCase(); - fileManager = currentCase.getServices().getFileManager(); + if (!(dataSource instanceof Image)) { + IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, + Bundle.PlasoIngestModule_has_run(), + Bundle.PlasoIngestModule_dataSource_not_an_image()); + IngestServices.getInstance().postMessage(message); + return ProcessResult.OK; + } else { + image = (Image) dataSource; - String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS - Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), PLASO, currentTime); - try { - Files.createDirectories(moduleOutputPath); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Error creating Plaso module output directory.", ex); //NON-NLS - return ProcessResult.ERROR; - } + statusHelper.switchToDeterminate(100); + currentCase = Case.getCurrentCase(); + fileManager = currentCase.getServices().getFileManager(); - // Run log2timeline - logger.log(Level.INFO, "Starting Plaso Run.");//NON-NLS - statusHelper.progress(Bundle.PlasoIngestModule_starting_log2timeline(), 0); - ProcessBuilder log2TimeLineCommand = buildLog2TimeLineCommand(moduleOutputPath, image); - try { - Process log2TimeLineProcess = log2TimeLineCommand.start(); - try (BufferedReader log2TimeLineOutpout = new BufferedReader(new InputStreamReader(log2TimeLineProcess.getInputStream()))) { - L2TStatusProcessor statusReader = new L2TStatusProcessor(log2TimeLineOutpout, statusHelper, moduleOutputPath); - new Thread(statusReader, "log2timeline status reader").start(); //NON-NLS - ExecUtil.waitForTermination(LOG2TIMELINE_EXECUTABLE, log2TimeLineProcess, TERMINATION_CHECK_INTERVAL, TERMINATION_CHECK_INTERVAL_UNITS, new DataSourceIngestModuleProcessTerminator(context)); - statusReader.cancel(); - } - - if (context.dataSourceIngestIsCancelled()) { - logger.log(Level.INFO, "Log2timeline run was canceled"); //NON-NLS - return ProcessResult.OK; - } - if (Files.notExists(moduleOutputPath.resolve(PLASO))) { - logger.log(Level.WARNING, "Error running log2timeline: there was no storage file."); //NON-NLS + String currentTime = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss z", Locale.US).format(System.currentTimeMillis());//NON-NLS + Path moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), PLASO, currentTime); + try { + Files.createDirectories(moduleOutputPath); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Error creating Plaso module output directory.", ex); //NON-NLS return ProcessResult.ERROR; } - // sort the output - statusHelper.progress(Bundle.PlasoIngestModule_running_psort(), 33); - ProcessBuilder psortCommand = buildPsortCommand(moduleOutputPath); - int result = ExecUtil.execute(psortCommand, new DataSourceIngestModuleProcessTerminator(context)); - if (result != 0) { - logger.log(Level.SEVERE, String.format("Error running Psort, error code returned %d", result)); //NON-NLS - MessageNotifyUtil.Notify.error(MODULE_NAME, Bundle.PlasoIngestModule_psort_fail()); - return ProcessResult.ERROR; - } + // Run log2timeline + logger.log(Level.INFO, "Starting Plaso Run.");//NON-NLS + statusHelper.progress(Bundle.PlasoIngestModule_starting_log2timeline(), 0); + ProcessBuilder log2TimeLineCommand = buildLog2TimeLineCommand(moduleOutputPath, image); + try { + Process log2TimeLineProcess = log2TimeLineCommand.start(); + try (BufferedReader log2TimeLineOutpout = new BufferedReader(new InputStreamReader(log2TimeLineProcess.getInputStream()))) { + L2TStatusProcessor statusReader = new L2TStatusProcessor(log2TimeLineOutpout, statusHelper, moduleOutputPath); + new Thread(statusReader, "log2timeline status reader").start(); //NON-NLS + ExecUtil.waitForTermination(LOG2TIMELINE_EXECUTABLE, log2TimeLineProcess, TERMINATION_CHECK_INTERVAL, TERMINATION_CHECK_INTERVAL_UNITS, new DataSourceIngestModuleProcessTerminator(context)); + statusReader.cancel(); + } - if (context.dataSourceIngestIsCancelled()) { - logger.log(Level.INFO, "psort run was canceled"); //NON-NLS - return ProcessResult.OK; - } - Path plasoFile = moduleOutputPath.resolve("plasodb.db3"); //NON-NLS - if (Files.notExists(plasoFile)) { - logger.log(Level.SEVERE, "Error running Psort: there was no sqlite db file."); //NON-NLS + if (context.dataSourceIngestIsCancelled()) { + logger.log(Level.INFO, "Log2timeline run was canceled"); //NON-NLS + return ProcessResult.OK; + } + if (Files.notExists(moduleOutputPath.resolve(PLASO))) { + logger.log(Level.WARNING, "Error running log2timeline: there was no storage file."); //NON-NLS + return ProcessResult.ERROR; + } + + // sort the output + statusHelper.progress(Bundle.PlasoIngestModule_running_psort(), 33); + ProcessBuilder psortCommand = buildPsortCommand(moduleOutputPath); + int result = ExecUtil.execute(psortCommand, new DataSourceIngestModuleProcessTerminator(context)); + if (result != 0) { + logger.log(Level.SEVERE, String.format("Error running Psort, error code returned %d", result)); //NON-NLS + MessageNotifyUtil.Notify.error(MODULE_NAME, Bundle.PlasoIngestModule_psort_fail()); + return ProcessResult.ERROR; + } + + if (context.dataSourceIngestIsCancelled()) { + logger.log(Level.INFO, "psort run was canceled"); //NON-NLS + return ProcessResult.OK; + } + Path plasoFile = moduleOutputPath.resolve("plasodb.db3"); //NON-NLS + if (Files.notExists(plasoFile)) { + logger.log(Level.SEVERE, "Error running Psort: there was no sqlite db file."); //NON-NLS + return ProcessResult.ERROR; + } + + // parse the output and make artifacts + createPlasoArtifacts(plasoFile.toString(), statusHelper); + + } catch (IOException ex) { + logger.log(Level.SEVERE, "Error running Plaso.", ex);//NON-NLS return ProcessResult.ERROR; } - // parse the output and make artifacts - createPlasoArtifacts(plasoFile.toString(), statusHelper); - - } catch (IOException ex) { - logger.log(Level.SEVERE, "Error running Plaso.", ex);//NON-NLS - return ProcessResult.ERROR; + IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, + Bundle.PlasoIngestModule_has_run(), + Bundle.PlasoIngestModule_completed()); + IngestServices.getInstance().postMessage(message); + return ProcessResult.OK; } - - IngestMessage message = IngestMessage.createMessage(IngestMessage.MessageType.DATA, - Bundle.PlasoIngestModule_has_run(), - Bundle.PlasoIngestModule_completed()); - IngestServices.getInstance().postMessage(message); - return ProcessResult.OK; } private ProcessBuilder buildLog2TimeLineCommand(Path moduleOutputPath, Image image) { @@ -240,8 +244,10 @@ public class PlasoIngestModule implements DataSourceIngestModule { static private ProcessBuilder buildProcessWithRunAsInvoker(String... commandLine) { ProcessBuilder processBuilder = new ProcessBuilder(commandLine); - /* Add an environment variable to force log2timeline/psort to run with - * the same permissions Autopsy uses. */ + /* + * Add an environment variable to force log2timeline/psort to run with + * the same permissions Autopsy uses. + */ processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS return processBuilder; } @@ -277,31 +283,30 @@ public class PlasoIngestModule implements DataSourceIngestModule { "PlasoIngestModule.create.artifacts.cancelled=Cancelled Plaso Artifact Creation ", "# {0} - file that events are from", "PlasoIngestModule.artifact.progress=Adding events to case: {0}", - "PlasoIngestModule.info.empty.database=Plaso database was empty.", - }) + "PlasoIngestModule.info.empty.database=Plaso database was empty.",}) private void createPlasoArtifacts(String plasoDb, DataSourceIngestModuleProgress statusHelper) { Blackboard blackboard = currentCase.getSleuthkitCase().getBlackboard(); String sqlStatement = "SELECT substr(filename,1) AS filename, " - + " strftime('%s', datetime) AS epoch_date, " - + " description, " - + " source, " - + " type, " - + " sourcetype " - + " FROM log2timeline " - + " WHERE source NOT IN ('FILE', " - + " 'WEBHIST') " // bad dates and duplicates with what we have. - + " AND sourcetype NOT IN ('UNKNOWN', " - + " 'PE Import Time');"; // lots of bad dates //NON-NLS + + " strftime('%s', datetime) AS epoch_date, " + + " description, " + + " source, " + + " type, " + + " sourcetype " + + " FROM log2timeline " + + " WHERE source NOT IN ('FILE', " + + " 'WEBHIST') " // bad dates and duplicates with what we have. + + " AND sourcetype NOT IN ('UNKNOWN', " + + " 'PE Import Time');"; // lots of bad dates //NON-NLS try (SQLiteDBConnect tempdbconnect = new SQLiteDBConnect("org.sqlite.JDBC", "jdbc:sqlite:" + plasoDb); //NON-NLS ResultSet resultSet = tempdbconnect.executeQry(sqlStatement)) { - + boolean dbHasData = false; - + while (resultSet.next()) { dbHasData = true; - + if (context.dataSourceIngestIsCancelled()) { logger.log(Level.INFO, "Cancelled Plaso Artifact Creation."); //NON-NLS return; @@ -314,20 +319,20 @@ public class PlasoIngestModule implements DataSourceIngestModule { logger.log(Level.INFO, "File {0} from Plaso output not found in case. Associating it with the data source instead.", currentFileName);//NON-NLS resolvedFile = image; } - + String description = resultSet.getString("description"); TimelineEventType eventType = findEventSubtype(currentFileName, resultSet); - + // If the description is empty use the event type display name // as the description. - if ( description == null || description.isEmpty() ) { + if (description == null || description.isEmpty()) { if (eventType != TimelineEventType.OTHER) { description = eventType.getDisplayName(); } else { continue; } } - + Collection bbattributes = Arrays.asList( new BlackboardAttribute( TSK_DATETIME, MODULE_NAME, @@ -338,14 +343,16 @@ public class PlasoIngestModule implements DataSourceIngestModule { new BlackboardAttribute( TSK_TL_EVENT_TYPE, MODULE_NAME, eventType.getTypeID())); - + try { BlackboardArtifact bbart = resolvedFile.newArtifact(TSK_TL_EVENT); bbart.addAttributes(bbattributes); try { - /* Post the artifact which will index the artifact for + /* + * Post the artifact which will index the artifact for * keyword search, and fire an event to notify UI of - * this new artifact */ + * this new artifact + */ blackboard.postArtifact(bbart, MODULE_NAME); } catch (BlackboardException ex) { logger.log(Level.SEVERE, "Error Posting Artifact.", ex);//NON-NLS @@ -354,12 +361,12 @@ public class PlasoIngestModule implements DataSourceIngestModule { logger.log(Level.SEVERE, "Exception Adding Artifact.", ex);//NON-NLS } } - + // Check if there is data the db - if( !dbHasData ) { + if (!dbHasData) { logger.log(Level.INFO, String.format("PlasoDB was empty: %s", plasoDb)); MessageNotifyUtil.Notify.info(MODULE_NAME, Bundle.PlasoIngestModule_info_empty_database()); - } + } } catch (SQLException ex) { logger.log(Level.SEVERE, "Error while trying to read into a sqlite db.", ex);//NON-NLS } @@ -377,8 +384,8 @@ public class PlasoIngestModule implements DataSourceIngestModule { // check the cached file //TODO: would we reduce 'cache misses' if we retrieved the events sorted by file? Is that overhead worth it? if (previousFile != null - && previousFile.getName().equalsIgnoreCase(fileName) - && previousFile.getParentPath().equalsIgnoreCase(filePath)) { + && previousFile.getName().equalsIgnoreCase(fileName) + && previousFile.getParentPath().equalsIgnoreCase(filePath)) { return previousFile; } @@ -416,7 +423,7 @@ public class PlasoIngestModule implements DataSourceIngestModule { switch (row.getString("source")) { case "WEBHIST": //These shouldn't actually be present, but keeping the logic just in case... if (fileName.toLowerCase().contains(COOKIE) - || row.getString("type").toLowerCase().contains(COOKIE)) {//NON-NLS + || row.getString("type").toLowerCase().contains(COOKIE)) {//NON-NLS return TimelineEventType.WEB_COOKIE; } else { From 5ae4d23de998655f9ed1a4f8749dd64cb7be4c7f Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 26 Nov 2019 13:02:21 -0500 Subject: [PATCH 04/13] Added loading progress bar to geolocation window --- .../autopsy/geolocation/CheckBoxJList.java | 3 +- .../geolocation/CheckBoxListPanel.form | 8 -- .../geolocation/CheckBoxListPanel.java | 14 ++- .../autopsy/geolocation/GeoFilterPanel.form | 4 - .../autopsy/geolocation/GeoFilterPanel.java | 17 ++- .../geolocation/GeolocationTopComponent.java | 118 +++++++++++++----- .../autopsy/geolocation/MapPanel.form | 17 ++- .../autopsy/geolocation/MapPanel.java | 32 ++++- 8 files changed, 162 insertions(+), 51 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxJList.java b/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxJList.java index 52276b6046..2f4247f3e6 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxJList.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxJList.java @@ -68,7 +68,7 @@ final class CheckBoxJList extends JLis CheckBoxJList() { initalize(); } - + /** * Do all of the UI initialization. */ @@ -103,6 +103,7 @@ final class CheckBoxJList extends JLis setBackground(list.getBackground()); setSelected(value.isChecked()); setText(value.getDisplayName()); + setEnabled(list.isEnabled()); return this; } } diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.form b/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.form index 112b734f1e..75fb1d4c62 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.form +++ b/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.form @@ -37,10 +37,6 @@ - - - - @@ -56,10 +52,6 @@ - - - - diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.java index 37dea163f5..ff3fd5b4a4 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/CheckBoxListPanel.java @@ -61,6 +61,14 @@ final class CheckBoxListPanel extends javax.swing.JPanel { void clearList() { model.removeAllElements(); } + + @Override + public void setEnabled(boolean enabled) { + checkboxList.setEnabled(enabled); + checkButton.setEnabled(enabled); + uncheckButton.setEnabled(enabled); + checkboxList.setEnabled(enabled); + } /** * Returns a list of all of the selected elements. @@ -126,8 +134,8 @@ final class CheckBoxListPanel extends javax.swing.JPanel { java.awt.GridBagConstraints gridBagConstraints; titleLabel = new javax.swing.JLabel(); - javax.swing.JButton uncheckButton = new javax.swing.JButton(); - javax.swing.JButton checkButton = new javax.swing.JButton(); + uncheckButton = new javax.swing.JButton(); + checkButton = new javax.swing.JButton(); scrollPane = new javax.swing.JScrollPane(); setLayout(new java.awt.GridBagLayout()); @@ -186,8 +194,10 @@ final class CheckBoxListPanel extends javax.swing.JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton checkButton; private javax.swing.JScrollPane scrollPane; private javax.swing.JLabel titleLabel; + private javax.swing.JButton uncheckButton; // End of variables declaration//GEN-END:variables /** diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.form b/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.form index 79a852b4d4..1b80568453 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.form +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.form @@ -115,10 +115,6 @@ - - - - diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.java index 9e009d1245..fe20a9bceb 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeoFilterPanel.java @@ -74,6 +74,20 @@ class GeoFilterPanel extends javax.swing.JPanel { add(checkboxPanel, gridBagConstraints); } + @Override + public void setEnabled(boolean enabled) { + applyButton.setEnabled(enabled); + mostRecentButton.setEnabled(enabled); + allButton.setEnabled(enabled); + showWaypointsWOTSCheckBox.setEnabled(enabled && mostRecentButton.isSelected()); + checkboxPanel.setEnabled(enabled); + daysLabel.setEnabled(enabled); + daysSpinner.setEnabled(enabled); + } + + /** + * Update the data source list with the current data sources + */ void updateDataSourceList() { try { initCheckboxList(); @@ -155,7 +169,7 @@ class GeoFilterPanel extends javax.swing.JPanel { mostRecentButton = new javax.swing.JRadioButton(); showWaypointsWOTSCheckBox = new javax.swing.JCheckBox(); daysSpinner = new javax.swing.JSpinner(numberModel); - javax.swing.JLabel daysLabel = new javax.swing.JLabel(); + daysLabel = new javax.swing.JLabel(); javax.swing.JPanel buttonPanel = new javax.swing.JPanel(); applyButton = new javax.swing.JButton(); javax.swing.JLabel optionsLabel = new javax.swing.JLabel(); @@ -272,6 +286,7 @@ class GeoFilterPanel extends javax.swing.JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JRadioButton allButton; private javax.swing.JButton applyButton; + private javax.swing.JLabel daysLabel; private javax.swing.JSpinner daysSpinner; private javax.swing.JRadioButton mostRecentButton; private javax.swing.JCheckBox showWaypointsWOTSCheckBox; diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java index a653979bb6..8a7967583d 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java @@ -22,12 +22,15 @@ import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.logging.Level; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; +import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.openide.windows.RetainLocation; import org.openide.windows.TopComponent; @@ -171,6 +174,7 @@ public final class GeolocationTopComponent extends TopComponent { logger.log(Level.SEVERE, ex.getMessage(), ex); return; // Doen't set the waypoints. } + mapPanel.setWaypoints(new ArrayList<>()); updateWaypoints(); } @@ -213,36 +217,12 @@ public final class GeolocationTopComponent extends TopComponent { JOptionPane.INFORMATION_MESSAGE); return; } - - SwingUtilities.invokeLater(new Runnable() { - public void run() { - Case currentCase = Case.getCurrentCase(); - try { - WaypointBuilder.getAllWaypoints(currentCase.getSleuthkitCase(), filters.getDataSources(), filters.showAllWaypoints(), filters.getMostRecentNumDays(), filters.showWaypointsWithoutTimeStamp(), new WaypointFilterQueryCallBack() { - @Override - public void process(List waypoints) { - // If the list is empty, tell the user and do not change - // the visible waypoints. - if (waypoints == null || waypoints.isEmpty()) { - JOptionPane.showMessageDialog(GeolocationTopComponent.this, - Bundle.GeoTopComponent_no_waypoints_returned_Title(), - Bundle.GeoTopComponent_no_waypoints_returned_mgs(), - JOptionPane.INFORMATION_MESSAGE); - - return; - } - mapPanel.setWaypoints(MapWaypoint.getWaypoints(waypoints)); - } - }); - } catch (GeoLocationDataException ex) { - logger.log(Level.SEVERE, "Failed to filter waypoints.", ex); - JOptionPane.showMessageDialog(GeolocationTopComponent.this, - Bundle.GeoTopComponent_filter_exception_Title(), - Bundle.GeoTopComponent_filter_exception_msg(), - JOptionPane.ERROR_MESSAGE); - } - } - }); + + mapPanel.setWaypointLoading(true); + geoFilterPanel.setEnabled(false); + + Thread thread = new Thread(new WaypointRunner(filters)); + thread.start(); } /** @@ -269,4 +249,82 @@ public final class GeolocationTopComponent extends TopComponent { private org.sleuthkit.autopsy.geolocation.HidingPane filterPane; private org.sleuthkit.autopsy.geolocation.MapPanel mapPanel; // End of variables declaration//GEN-END:variables + + /** + * A runnable class for getting waypoints based on the current filters. + */ + private class WaypointRunner implements Runnable { + + private final GeoFilter filters; + + /** + * Constructs the Waypoint Runner + * + * @param filters + */ + WaypointRunner(GeoFilter filters) { + this.filters = filters; + } + + @Override + public void run() { + Case currentCase = Case.getCurrentCase(); + try { + try { + Thread.sleep(5000); + } catch (InterruptedException ex) { + Exceptions.printStackTrace(ex); + } + + WaypointBuilder.getAllWaypoints(currentCase.getSleuthkitCase(), + filters.getDataSources(), + filters.showAllWaypoints(), + filters.getMostRecentNumDays(), + filters.showWaypointsWithoutTimeStamp(), + new WaypointCallBack()); + + } catch (GeoLocationDataException ex) { + logger.log(Level.SEVERE, "Failed to filter waypoints.", ex); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + JOptionPane.showMessageDialog(GeolocationTopComponent.this, + Bundle.GeoTopComponent_filter_exception_Title(), + Bundle.GeoTopComponent_filter_exception_msg(), + JOptionPane.ERROR_MESSAGE); + } + }); + } + } + + } + + /** + * Callback for getting waypoints. + */ + private class WaypointCallBack implements WaypointFilterQueryCallBack { + + @Override + public void process(List waypoints) { + // Make sure that the waypoints are added to the map panel in + // the correct thread. + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + // If the list is empty, tell the user and do not change + // the visible waypoints. + if (waypoints == null || waypoints.isEmpty()) { + JOptionPane.showMessageDialog(GeolocationTopComponent.this, + Bundle.GeoTopComponent_no_waypoints_returned_Title(), + Bundle.GeoTopComponent_no_waypoints_returned_mgs(), + JOptionPane.INFORMATION_MESSAGE); + + return; + } + mapPanel.setWaypoints(MapWaypoint.getWaypoints(waypoints)); + geoFilterPanel.setEnabled(true); + } + }); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form index 454008c062..46a6e7d308 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form @@ -98,7 +98,7 @@ - + @@ -107,8 +107,19 @@ - - + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java index fb5cdc23e5..ff96b5c443 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java @@ -178,6 +178,17 @@ final public class MapPanel extends javax.swing.JPanel { mapViewer.setOverlayPainter(waypointPainter); } + + /** + * Show or hide the waypoint loading progress bar. + * + * @param loading + */ + void setWaypointLoading(boolean loading) { + progressBar.setEnabled(true); + progressBar.setVisible(loading); + progressBar.setString("Loading Waypoints"); + } /** * Setup the zoom slider based on the current tileFactory. @@ -265,6 +276,7 @@ final public class MapPanel extends javax.swing.JPanel { } mapViewer.repaint(); + setWaypointLoading(false); } /** @@ -486,6 +498,7 @@ final public class MapPanel extends javax.swing.JPanel { zoomSlider = new javax.swing.JSlider(); infoPanel = new javax.swing.JPanel(); cordLabel = new javax.swing.JLabel(); + progressBar = new javax.swing.JProgressBar(); setFocusable(false); setLayout(new java.awt.BorderLayout()); @@ -553,10 +566,24 @@ final public class MapPanel extends javax.swing.JPanel { add(mapViewer, java.awt.BorderLayout.CENTER); - infoPanel.setLayout(new java.awt.BorderLayout()); + infoPanel.setLayout(new java.awt.GridBagLayout()); org.openide.awt.Mnemonics.setLocalizedText(cordLabel, org.openide.util.NbBundle.getMessage(MapPanel.class, "MapPanel.cordLabel.text")); // NOI18N - infoPanel.add(cordLabel, java.awt.BorderLayout.EAST); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 5); + infoPanel.add(cordLabel, gridBagConstraints); + + progressBar.setIndeterminate(true); + progressBar.setStringPainted(true); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + infoPanel.add(progressBar, gridBagConstraints); add(infoPanel, java.awt.BorderLayout.SOUTH); }// //GEN-END:initComponents @@ -594,6 +621,7 @@ final public class MapPanel extends javax.swing.JPanel { private javax.swing.JLabel cordLabel; private javax.swing.JPanel infoPanel; private org.jxmapviewer.JXMapViewer mapViewer; + private javax.swing.JProgressBar progressBar; private javax.swing.JPanel zoomPanel; private javax.swing.JSlider zoomSlider; // End of variables declaration//GEN-END:variables From f8639837eff473f512d214e26f5f3bf8a831f0cd Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 26 Nov 2019 13:59:32 -0500 Subject: [PATCH 05/13] Moved the progress bar --- Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form | 2 +- Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form index 46a6e7d308..5158982822 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.form @@ -108,7 +108,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java index ff96b5c443..03ec22cf2b 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/MapPanel.java @@ -572,9 +572,9 @@ final public class MapPanel extends javax.swing.JPanel { gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; - gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; - gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 5); + gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 5); infoPanel.add(cordLabel, gridBagConstraints); progressBar.setIndeterminate(true); From 64f8609fe7ec7c9db0a8112a75014659e415024b Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 26 Nov 2019 14:09:40 -0500 Subject: [PATCH 06/13] Cleaned up imports and removed test code --- .../autopsy/geolocation/GeolocationTopComponent.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java index 8a7967583d..1bf0aaabbf 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/GeolocationTopComponent.java @@ -23,14 +23,12 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeListener; import java.util.ArrayList; -import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.logging.Level; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; -import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.openide.windows.RetainLocation; import org.openide.windows.TopComponent; @@ -270,12 +268,6 @@ public final class GeolocationTopComponent extends TopComponent { public void run() { Case currentCase = Case.getCurrentCase(); try { - try { - Thread.sleep(5000); - } catch (InterruptedException ex) { - Exceptions.printStackTrace(ex); - } - WaypointBuilder.getAllWaypoints(currentCase.getSleuthkitCase(), filters.getDataSources(), filters.showAllWaypoints(), From 80610becfa8a133f6650e9b7f7f2ba83bffb8ad9 Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Mon, 2 Dec 2019 11:05:31 -0500 Subject: [PATCH 07/13] 5770: add attachments to WhatsApp messages. --- InternalPythonModules/android/whatsapp.py | 51 ++++++++++++++++------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/InternalPythonModules/android/whatsapp.py b/InternalPythonModules/android/whatsapp.py index 438784f3e2..a91c21375b 100644 --- a/InternalPythonModules/android/whatsapp.py +++ b/InternalPythonModules/android/whatsapp.py @@ -45,6 +45,9 @@ from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection +from org.sleuthkit.datamodel.blackboardutils import FileAttachment +from org.sleuthkit.datamodel.blackboardutils import URLAttachment +from org.sleuthkit.datamodel.blackboardutils import MessageAttachments from TskMessagesParser import TskMessagesParser from TskContactsParser import TskContactsParser from TskCallLogsParser import TskCallLogsParser @@ -151,7 +154,7 @@ class WhatsAppAnalyzer(general.AndroidComponentAnalyzer): current_case.getSleuthkitCase(), self._PARSER_NAME, calllog_and_message_db.getDBFile(), Account.Type.WHATSAPP) self.parse_calllogs(calllog_and_message_db, helper) - self.parse_messages(dataSource, calllog_and_message_db, helper) + self.parse_messages(dataSource, calllog_and_message_db, helper, current_case) except NoCurrentCaseException as ex: #If there is no current case, bail out immediately. @@ -227,24 +230,32 @@ class WhatsAppAnalyzer(general.AndroidComponentAnalyzer): "Error posting calllog artifact to the blackboard.", ex) self._logger.log(Level.WARNING, traceback.format_exc()) - def parse_messages(self, dataSource, messages_db, helper): + def parse_messages(self, dataSource, messages_db, helper, current_case): try: messages_db.attachDatabase(dataSource, "wa.db", messages_db.getDBFile().getParentPath(), "wadb") messages_parser = WhatsAppMessagesParser(messages_db) while messages_parser.next(): - helper.addMessage( - messages_parser.get_message_type(), - messages_parser.get_message_direction(), - messages_parser.get_phone_number_from(), - messages_parser.get_phone_number_to(), - messages_parser.get_message_date_time(), - messages_parser.get_message_read_status(), - messages_parser.get_message_subject(), - messages_parser.get_message_text(), - messages_parser.get_thread_id() - ) + message_artifact = helper.addMessage( + messages_parser.get_message_type(), + messages_parser.get_message_direction(), + messages_parser.get_phone_number_from(), + messages_parser.get_phone_number_to(), + messages_parser.get_message_date_time(), + messages_parser.get_message_read_status(), + messages_parser.get_message_subject(), + messages_parser.get_message_text(), + messages_parser.get_thread_id() + ) + + # add attachments, if any + if (messages_parser.get_url_attachment() is not None): + url_attachments = ArrayList() + url_attachments.add(URLAttachment(messages_parser.get_url_attachment())) + message_attachments = MessageAttachments([], url_attachments) + helper.addAttachments(message_artifact, message_attachments) + messages_parser.close() except SQLException as ex: self._logger.log(Level.WARNING, "Error querying the whatsapp database for contacts.", ex) @@ -502,9 +513,6 @@ class WhatsAppMessagesParser(TskMessagesParser): message = self.result_set.getString("content") if message is None: message = super(WhatsAppMessagesParser, self).get_message_text() - attachment = self.result_set.getString("attachment") - if attachment is not None: - return general.appendAttachmentList(message, [attachment]) return message def get_thread_id(self): @@ -512,3 +520,14 @@ class WhatsAppMessagesParser(TskMessagesParser): if group is not None: return self.result_set.getString("id") return super(WhatsAppMessagesParser, self).get_thread_id() + + + def get_url_attachment(self): + attachment = self.result_set.getString("attachment") + if (attachment is None): + return None + elif (str(attachment).startswith("http:") or str(attachment).startswith("https:") ): + return attachment + else: + return None + From 1275119c6b82c4a5f27dc36d13213a719a7a3d23 Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Mon, 2 Dec 2019 15:49:38 -0500 Subject: [PATCH 08/13] 5767: Add attachments to Skype messages. --- InternalPythonModules/android/skype.py | 55 +++++++++++++++++--------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/InternalPythonModules/android/skype.py b/InternalPythonModules/android/skype.py index d8b79ac7fe..12cfa0f53e 100644 --- a/InternalPythonModules/android/skype.py +++ b/InternalPythonModules/android/skype.py @@ -46,6 +46,8 @@ from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection +from org.sleuthkit.datamodel.blackboardutils import FileAttachment +from org.sleuthkit.datamodel.blackboardutils import MessageAttachments from TskMessagesParser import TskMessagesParser from TskContactsParser import TskContactsParser from TskCallLogsParser import TskCallLogsParser @@ -137,7 +139,7 @@ class SkypeAnalyzer(general.AndroidComponentAnalyzer): ) self.parse_contacts(skype_db, helper) self.parse_calllogs(skype_db, helper) - self.parse_messages(skype_db, helper) + self.parse_messages(skype_db, helper, current_case) except NoCurrentCaseException as ex: self._logger.log(Level.WARNING, "No case currently open.", ex) self._logger.log(Level.WARNING, traceback.format_exc()) @@ -209,23 +211,30 @@ class SkypeAnalyzer(general.AndroidComponentAnalyzer): "Failed to post call log artifact to the blackboard", ex) self._logger.log(Level.WARNING, traceback.format_exc()) - def parse_messages(self, skype_db, helper): + def parse_messages(self, skype_db, helper, current_case): #Query for messages and iterate row by row adding #each message artifact try: messages_parser = SkypeMessagesParser(skype_db) while messages_parser.next(): - helper.addMessage( - messages_parser.get_message_type(), - messages_parser.get_message_direction(), - messages_parser.get_phone_number_from(), - messages_parser.get_phone_number_to(), - messages_parser.get_message_date_time(), - messages_parser.get_message_read_status(), - messages_parser.get_message_subject(), - messages_parser.get_message_text(), - messages_parser.get_thread_id() - ) + message_artifact = helper.addMessage( + messages_parser.get_message_type(), + messages_parser.get_message_direction(), + messages_parser.get_phone_number_from(), + messages_parser.get_phone_number_to(), + messages_parser.get_message_date_time(), + messages_parser.get_message_read_status(), + messages_parser.get_message_subject(), + messages_parser.get_message_text(), + messages_parser.get_thread_id() + ) + + if (messages_parser.get_file_attachment() is not None): + file_attachments = ArrayList() + file_attachments.add(FileAttachment(current_case.getSleuthkitCase(), skype_db.getDBFile().getDataSource(), messages_parser.get_file_attachment())) + message_attachments = MessageAttachments(file_attachments, []) + helper.addAttachments(message_artifact, message_attachments) + messages_parser.close() except SQLException as ex: #Error parsing Skype db @@ -425,12 +434,12 @@ class SkypeMessagesParser(TskMessagesParser): content = self.result_set.getString("content") if content is not None: - file_path = self.result_set.getString("device_gallery_path") - - #if a file name and file path are associated with a message, append it - if file_path is not None: - return general.appendAttachmentList(content, [file_path]) - +## file_path = self.result_set.getString("device_gallery_path") +## +## #if a file name and file path are associated with a message, append it +## if file_path is not None: +## return general.appendAttachmentList(content, [file_path]) +## return content return super(SkypeMessagesParser, self).get_message_text() @@ -440,3 +449,11 @@ class SkypeMessagesParser(TskMessagesParser): if group_ids is not None: return self.result_set.getString("conversation_id") return super(SkypeMessagesParser, self).get_thread_id() + + + def get_file_attachment(self): + if (self.result_set.getString("device_gallery_path") is None): + return None + else: + return self.result_set.getString("device_gallery_path") + From 46e9051d70c08fd5b5acd443b52d2f588adc7d7b Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Mon, 2 Dec 2019 19:01:48 -0500 Subject: [PATCH 09/13] Update PlasoIngestModule.java Change ingest message that is displayed --- .../sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java index e9ace4fde5..d359332f84 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/plaso/PlasoIngestModule.java @@ -132,9 +132,9 @@ public class PlasoIngestModule implements DataSourceIngestModule { "PlasoIngestModule.psort.cancelled=psort run was canceled", "PlasoIngestModule.bad.imageFile=Cannot find image file name and path", "PlasoIngestModule.completed=Plaso Processing Completed", - "PlasoIngestModule.has.run=Plaso Plugin has been run.", + "PlasoIngestModule.has.run=Plaso", "PlasoIngestModule.psort.fail=Plaso returned an error when sorting events. Results are not complete.", - "PlasoIngestModule.dataSource.not.an.image=Datasource is not an Image."}) + "PlasoIngestModule.dataSource.not.an.image=Skipping non-disk image datasource"}) @Override public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress statusHelper) { From fdffe1cfeeeb22c8188cb2d60a719b365c281318 Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Tue, 3 Dec 2019 09:33:32 -0500 Subject: [PATCH 10/13] Addressed review comments. --- .../autopsy/contentviewers/Bundle.properties | 4 +- .../autopsy/contentviewers/ContextViewer.form | 8 +- .../autopsy/contentviewers/ContextViewer.java | 83 +++++++++++-------- 3 files changed, 55 insertions(+), 40 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties index f1e154fb40..f8119b88da 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties @@ -94,6 +94,6 @@ MediaPlayerPanel.infoLabel.text=No Errors MediaPlayerPanel.VolumeIcon.text=Volume MediaPlayerPanel.playBackSpeedLabel.text=Speed: ContextViewer.jSourceGoToResultButton.text=Go to Result -ContextViewer.jLabel1.text=Source ContextViewer.jSourceNameLabel.text=jSourceNameLabel -ContextViewer.jSourceTextLabel.text=jLabel2 \ No newline at end of file +ContextViewer.jSourceTextLabel.text=jLabel2 +ContextViewer.jSourceLabel.text=Source diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form index c7f826dc9e..c9c65f23a3 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form @@ -21,7 +21,7 @@ - + @@ -43,7 +43,7 @@ - + @@ -67,13 +67,13 @@ - + - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java index ef3b5025c7..67031e92bd 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java @@ -46,14 +46,12 @@ import org.sleuthkit.datamodel.TskCoreException; * */ @ServiceProvider(service = DataContentViewer.class, position = 7) -@NbBundle.Messages({ - "ContextViewer.title=Context Viewer", - "ContextViewer.toolTip=Displays context for selected file." -}) public final class ContextViewer extends javax.swing.JPanel implements DataContentViewer { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(ContextViewer.class.getName()); + private static final int ARTIFACT_STR_MAX_LEN = 1024; + private static final int ATTRIBUTE_STR_MAX_LEN = 200; // defines a list of artifacts that provide context for a file private static final List SOURCE_CONTEXT_ARTIFACTS = new ArrayList<>(); @@ -82,7 +80,7 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte private void initComponents() { jSourceGoToResultButton = new javax.swing.JButton(); - jLabel1 = new javax.swing.JLabel(); + jSourceLabel = new javax.swing.JLabel(); jSourceNameLabel = new javax.swing.JLabel(); jSourceTextLabel = new javax.swing.JLabel(); @@ -93,8 +91,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte } }); - jLabel1.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N - org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jLabel1.text")); // NOI18N + jSourceLabel.setFont(new java.awt.Font("Dialog", 1, 14)); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(jSourceLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jSourceNameLabel, org.openide.util.NbBundle.getMessage(ContextViewer.class, "ContextViewer.jSourceNameLabel.text")); // NOI18N @@ -109,7 +107,7 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jLabel1) + .addComponent(jSourceLabel) .addGroup(layout.createSequentialGroup() .addGap(6, 6, 6) .addComponent(jSourceNameLabel) @@ -124,7 +122,7 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addComponent(jLabel1) + .addComponent(jSourceLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(jSourceNameLabel) @@ -157,10 +155,15 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte try { populateSourceContextData(file); } catch (NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.SEVERE, "Exception displaying context for file {0}", file); //NON-NLS + logger.log(Level.SEVERE, String.format("Exception displaying context for file %s", file.getName()), ex); //NON-NLS } } + @NbBundle.Messages({ + "ContextViewer.title=Context Viewer", + "ContextViewer.toolTip=Displays context for selected file." + }) + @Override public String getTitle() { return Bundle.ContextViewer_title(); @@ -201,7 +204,7 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte return true; } } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Exception while looking up context artifacts for file {0}", abstractFile); //NON-NLS + logger.log(Level.SEVERE, String.format("Exception while looking up context artifacts for file %s", abstractFile), ex); //NON-NLS } } @@ -212,6 +215,7 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte @Override public int isPreferred(Node node) { + // this is a low preference viewer. return 1; } @@ -232,9 +236,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte boolean foundASource = false; for (BlackboardArtifact.ARTIFACT_TYPE artifactType : SOURCE_CONTEXT_ARTIFACTS) { List artifactsList = tskCase.getBlackboardArtifacts(artifactType, sourceFile.getId()); - if (!artifactsList.isEmpty()) { - foundASource = true; - } + + foundASource = !artifactsList.isEmpty(); for (BlackboardArtifact contextArtifact : artifactsList) { addSourceEntry(contextArtifact); } @@ -245,10 +248,7 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte } } - @NbBundle.Messages({ - "ContextViewer.attachmentSource=Attached to: ", - "ContextViewer.downloadSource=Downloaded from: " - }) + /** * Adds a source context entry for the selected file based on the given context @@ -282,12 +282,16 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte * * @throws TskCoreException */ + @NbBundle.Messages({ + "ContextViewer.attachmentSource=Attached to: ", + "ContextViewer.downloadSource=Downloaded from: " + }) private void setSourceFields(BlackboardArtifact associatedArtifact) throws TskCoreException { if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == associatedArtifact.getArtifactTypeID() || BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == associatedArtifact.getArtifactTypeID()) { setSourceName(Bundle.ContextViewer_attachmentSource()); - setSourceText(msgArtifactToAbbreiviatedString(associatedArtifact)); + setSourceText(msgArtifactToAbbreviatedString(associatedArtifact)); } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == associatedArtifact.getArtifactTypeID() || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == associatedArtifact.getArtifactTypeID()) { @@ -330,14 +334,18 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte * * @throws TskCoreException */ + @NbBundle.Messages({ + "ContextViewer.downloadURL=URL", + "ContextViewer.downloadedOn=On" + }) private String webDownloadArtifactToString(BlackboardArtifact artifact) throws TskCoreException { - StringBuilder sb = new StringBuilder(1024); + StringBuilder sb = new StringBuilder(ARTIFACT_STR_MAX_LEN); Map attributesMap = getAttributesMap(artifact); if (BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() == artifact.getArtifactTypeID() || BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID() == artifact.getArtifactTypeID()) { - appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, attributesMap, "URL"); - appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, attributesMap, "On"); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_URL, attributesMap, Bundle.ContextViewer_downloadURL()); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, attributesMap, Bundle.ContextViewer_downloadedOn()); } return sb.toString(); } @@ -351,21 +359,28 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte * * @throws TskCoreException */ - private String msgArtifactToAbbreiviatedString(BlackboardArtifact artifact) throws TskCoreException { + @NbBundle.Messages({ + "ContextViewer.message=Message", + "ContextViewer.email=Email", + "ContextViewer.messageFrom=From", + "ContextViewer.messageTo=From", + "ContextViewer.messageOn=On", + }) + private String msgArtifactToAbbreviatedString(BlackboardArtifact artifact) throws TskCoreException { - StringBuilder sb = new StringBuilder(1024); + StringBuilder sb = new StringBuilder(ARTIFACT_STR_MAX_LEN); Map attributesMap = getAttributesMap(artifact); if (BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() == artifact.getArtifactTypeID()) { - sb.append("Message "); - appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, attributesMap, "From"); - appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, attributesMap, "To"); - appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, attributesMap, "On"); + sb.append(Bundle.ContextViewer_message()).append(' '); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, attributesMap, Bundle.ContextViewer_messageFrom()); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, attributesMap, Bundle.ContextViewer_messageTo()); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, attributesMap, Bundle.ContextViewer_messageOn()); } else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() == artifact.getArtifactTypeID()) { - sb.append("Email "); - appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM, attributesMap, "From"); - appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO, attributesMap, "To"); - appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT, attributesMap, "On"); + sb.append(Bundle.ContextViewer_email()).append(' '); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM, attributesMap, Bundle.ContextViewer_messageFrom()); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO, attributesMap, Bundle.ContextViewer_messageTo()); + appendAttributeString(sb, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_SENT, attributesMap, Bundle.ContextViewer_messageOn()); } return sb.toString(); } @@ -390,7 +405,7 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte if (!StringUtils.isEmpty(prependStr)) { sb.append(prependStr).append(' '); } - sb.append(StringUtils.abbreviate(attrVal, 200)).append(' '); + sb.append(StringUtils.abbreviate(attrVal, ATTRIBUTE_STR_MAX_LEN)).append(' '); } } } @@ -419,8 +434,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JLabel jLabel1; private javax.swing.JButton jSourceGoToResultButton; + private javax.swing.JLabel jSourceLabel; private javax.swing.JLabel jSourceNameLabel; private javax.swing.JLabel jSourceTextLabel; // End of variables declaration//GEN-END:variables From cb0698705769f41501040715925e3c3699e556e7 Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Tue, 3 Dec 2019 11:35:25 -0500 Subject: [PATCH 11/13] Deleted commented out code. --- InternalPythonModules/android/skype.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/InternalPythonModules/android/skype.py b/InternalPythonModules/android/skype.py index 12cfa0f53e..721fc92421 100644 --- a/InternalPythonModules/android/skype.py +++ b/InternalPythonModules/android/skype.py @@ -434,12 +434,6 @@ class SkypeMessagesParser(TskMessagesParser): content = self.result_set.getString("content") if content is not None: -## file_path = self.result_set.getString("device_gallery_path") -## -## #if a file name and file path are associated with a message, append it -## if file_path is not None: -## return general.appendAttachmentList(content, [file_path]) -## return content return super(SkypeMessagesParser, self).get_message_text() From 11c0848d452a4ec6d5bd5c18ed8aa7fe0a0af678 Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Wed, 4 Dec 2019 11:08:08 -0500 Subject: [PATCH 12/13] Address Codacy comments. --- .../contentviewers/Bundle.properties-MERGED | 15 +++++++++++++++ .../autopsy/contentviewers/ContextViewer.form | 8 ++++++++ .../autopsy/contentviewers/ContextViewer.java | 6 ++---- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED index 2d9df0ae33..ceec760a69 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Bundle.properties-MERGED @@ -10,6 +10,17 @@ AnnotationsContentViewer.title=Annotations AnnotationsContentViewer.toolTip=Displays tags and comments associated with the selected content. ApplicationContentViewer.title=Application ApplicationContentViewer.toolTip=Displays file contents. +ContextViewer.attachmentSource=Attached to: +ContextViewer.downloadedOn=On +ContextViewer.downloadSource=Downloaded from: +ContextViewer.downloadURL=URL +ContextViewer.email=Email +ContextViewer.message=Message +ContextViewer.messageFrom=From +ContextViewer.messageOn=On +ContextViewer.messageTo=From +ContextViewer.title=Context Viewer +ContextViewer.toolTip=Displays context for selected file. FXVideoPanel.pauseButton.infoLabel.playbackErr=Unable to play video. FXVideoPanel.progress.bufferingCancelled=media buffering was canceled FXVideoPanel.progress.bufferingInterrupted=media buffering was interrupted @@ -164,6 +175,10 @@ MediaPlayerPanel.playButton.text=\u25ba MediaPlayerPanel.infoLabel.text=No Errors MediaPlayerPanel.VolumeIcon.text=Volume MediaPlayerPanel.playBackSpeedLabel.text=Speed: +ContextViewer.jSourceGoToResultButton.text=Go to Result +ContextViewer.jSourceNameLabel.text=jSourceNameLabel +ContextViewer.jSourceTextLabel.text=jLabel2 +ContextViewer.jSourceLabel.text=Source # {0} - tableName SQLiteViewer.readTable.errorText=Error getting rows for table: {0} # {0} - tableName diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form index c9c65f23a3..2546ea406a 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.form @@ -66,6 +66,10 @@ + + + + @@ -76,6 +80,10 @@ + + + + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java index 67031e92bd..41a399be52 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/ContextViewer.java @@ -79,8 +79,8 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte // //GEN-BEGIN:initComponents private void initComponents() { - jSourceGoToResultButton = new javax.swing.JButton(); - jSourceLabel = new javax.swing.JLabel(); + javax.swing.JButton jSourceGoToResultButton = new javax.swing.JButton(); + javax.swing.JLabel jSourceLabel = new javax.swing.JLabel(); jSourceNameLabel = new javax.swing.JLabel(); jSourceTextLabel = new javax.swing.JLabel(); @@ -434,8 +434,6 @@ public final class ContextViewer extends javax.swing.JPanel implements DataConte // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton jSourceGoToResultButton; - private javax.swing.JLabel jSourceLabel; private javax.swing.JLabel jSourceNameLabel; private javax.swing.JLabel jSourceTextLabel; // End of variables declaration//GEN-END:variables From ed1c2ad31cb76f343dc4c0340b868b00c7ecf9e4 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Wed, 4 Dec 2019 14:25:46 -0500 Subject: [PATCH 13/13] Implemented and tested a more robust report type parsing --- .../xry/XRYFileReader.java | 66 +++++++++++++++++-- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileReader.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileReader.java index bc3c04259b..726c86f170 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileReader.java +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYFileReader.java @@ -52,7 +52,11 @@ final class XRYFileReader implements AutoCloseable { //Assume UTF_16LE private static final Charset CHARSET = StandardCharsets.UTF_16LE; - //Assume all XRY reports have the type on the 3rd line. + //Assume the header begins with 'xry export'. + private static final String START_OF_HEADER = "xry export"; + + //Assume all XRY reports have the type on the 3rd line + //relative to the start of the header. private static final int LINE_WITH_REPORT_TYPE = 3; //Assume all headers are 5 lines in length. @@ -91,8 +95,12 @@ final class XRYFileReader implements AutoCloseable { reader = Files.newBufferedReader(xryFile, CHARSET); xryFilePath = xryFile; - //Advance the reader to the start of the first XRY entity. - for (int i = 0; i < HEADER_LENGTH_IN_LINES; i++) { + //Advance the reader to the start of the header. + advanceToHeader(reader); + + //Advance the reader past the header to the start + //of the first XRY entity. + for (int i = 1; i < HEADER_LENGTH_IN_LINES; i++) { reader.readLine(); } @@ -298,8 +306,11 @@ final class XRYFileReader implements AutoCloseable { */ private static Optional getType(Path file) throws IOException { try (BufferedReader reader = Files.newBufferedReader(file, CHARSET)) { + //Header may not start at the beginning of the file. + advanceToHeader(reader); + //Advance the reader to the line before the report type. - for (int i = 0; i < LINE_WITH_REPORT_TYPE - 1; i++) { + for (int i = 1; i < LINE_WITH_REPORT_TYPE - 1; i++) { reader.readLine(); } @@ -310,4 +321,51 @@ final class XRYFileReader implements AutoCloseable { return Optional.empty(); } } + + /** + * Advances the reader to the start of the header. The XRY Export header may + * not be the first n lines of the file. It may be preceded by new lines or + * white space. + * + * This function will consume the first line of the header, which will be + * 'XRY Export'. + * + * @param reader BufferedReader pointing to the xry file + * @throws IOException if an I/O error occurs + */ + private static void advanceToHeader(BufferedReader reader) throws IOException { + String line; + if((line = reader.readLine()) == null) { + return; + } + + String normalizedLine = line.trim().toLowerCase(); + if (normalizedLine.equals(START_OF_HEADER)) { + return; + } + + /** + * The first line may have 0xFFFE BOM prepended to it, which will cause + * the equality check to fail. This bit a logic will try to remove those + * bytes and attempt another check. + */ + byte[] normalizedBytes = normalizedLine.getBytes(CHARSET); + if (normalizedBytes.length > 2) { + normalizedLine = new String(normalizedBytes, 2, + normalizedBytes.length - 2, CHARSET); + if (normalizedLine.equals(START_OF_HEADER)) { + return; + } + } + + /** + * All other lines will need to match completely. + */ + while ((line = reader.readLine()) != null) { + normalizedLine = line.trim().toLowerCase(); + if (normalizedLine.equals(START_OF_HEADER)) { + return; + } + } + } }