Merge remote-tracking branch 'upstream/develop' into version-number-updates

This commit is contained in:
rcordovano 2021-01-19 09:51:47 -05:00
commit 5af07ed9d2
122 changed files with 1939 additions and 1133 deletions

View File

@ -52,8 +52,8 @@ Case.exceptionMessage.failedToReadMetadata=Failed to read case metadata:\n{0}.
Case.exceptionMessage.metadataUpdateError=Failed to update case metadata
# {0} - exception message
Case.exceptionMessage.unsupportedSchemaVersionMessage=Unsupported case database schema version:\n{0}.
Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a exclusive lock on the case.
Case.lockingException.couldNotAcquireSharedLock=Failed to get an shared lock on the case.
Case.lockingException.couldNotAcquireExclusiveLock=Failed to get an exclusive lock on the case.
Case.lockingException.couldNotAcquireSharedLock=Failed to get a shared lock on the case.
Case.open.exception.multiUserCaseNotEnabled=Cannot open a multi-user case if multi-user cases are not enabled. See Tools, Options, Multi-User.
# {0} - image
Case.openFileSystems.openingImage=Opening all filesystems for image: {0}...

View File

@ -2828,8 +2828,8 @@ public class Case {
* @throws CaseActionException If the lock cannot be acquired.
*/
@Messages({
"Case.lockingException.couldNotAcquireSharedLock=Failed to get an shared lock on the case.",
"Case.lockingException.couldNotAcquireExclusiveLock=Failed to get a exclusive lock on the case."
"Case.lockingException.couldNotAcquireSharedLock=Failed to get a shared lock on the case.",
"Case.lockingException.couldNotAcquireExclusiveLock=Failed to get an exclusive lock on the case."
})
private void acquireCaseLock(CaseLockType lockType) throws CaseActionException {
String caseDir = metadata.getCaseDirectory();

View File

@ -39,9 +39,9 @@ GlobalSettingsPanel.askForCentralRepoDbChoice.sqliteChoice.text=Use SQLite
GlobalSettingsPanel.onMultiUserChange.disabledMu.description=The Central Repository will be reconfigured to use a local SQLite database.
GlobalSettingsPanel.onMultiUserChange.disabledMu.description2=Press Configure PostgreSQL to change to a PostgreSQL database.
GlobalSettingsPanel.onMultiUserChange.disabledMu.title=Central Repository Change Necessary
GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL database?
GlobalSettingsPanel.onMultiUserChange.enable.description2=The Central Repository stores hash values and accounts from past cases.
GlobalSettingsPanel.onMultiUserChange.enable.title=Use with Central Repository?
GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL server?
GlobalSettingsPanel.onMultiUserChange.enable.description2=Any data in an existing SQLite Central Repository will not be transferred to the new database.
GlobalSettingsPanel.onMultiUserChange.enable.title=Central Repository
GlobalSettingsPanel.testCurrentConfiguration.dbDoesNotExist.message=Database does not exist.
GlobalSettingsPanel.validationErrMsg.ingestRunning=You cannot change settings while ingest is running.
GlobalSettingsPanel.validationerrMsg.mustConfigure=Configure the database to enable this module.

View File

@ -151,9 +151,9 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i
* as of most recent change.
*/
@NbBundle.Messages({
"GlobalSettingsPanel.onMultiUserChange.enable.title=Use with Central Repository?",
"GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL database?",
"GlobalSettingsPanel.onMultiUserChange.enable.description2=The Central Repository stores hash values and accounts from past cases."
"GlobalSettingsPanel.onMultiUserChange.enable.title=Central Repository",
"GlobalSettingsPanel.onMultiUserChange.enable.description=Do you want to update the Central Repository to use this PostgreSQL server?",
"GlobalSettingsPanel.onMultiUserChange.enable.description2=Any data in an existing SQLite Central Repository will not be transferred to the new database."
})
public static void onMultiUserChange(Component parent, boolean muPreviouslySelected, boolean muCurrentlySelected) {
boolean crEnabled = CentralRepoDbUtil.allowUseOfCentralRepository();

View File

@ -82,9 +82,9 @@ final public class CVTPersonaCache {
}
/**
* Returns the list of PersonaAccounts for the given Account typeSpecificId.
* Returns the list of PersonaAccounts for the given Account.
*
* @param typeSpecificID Account typeSpecificId.
* @param account The account.
*
* @return List of PersonaAccounts for id or empty list if none were found.
*

View File

@ -11,30 +11,32 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,0,0,0,0,0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="detailsScrollPane" alignment="1" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="detailsScrollPane" max="32767" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="detailsPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="First"/>
</Constraint>
</Constraints>
<Container class="javax.swing.JScrollPane" name="detailsScrollPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 0]"/>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
</Container>
</SubComponents>
</Form>

View File

@ -37,7 +37,6 @@ import java.util.logging.Level;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import org.apache.commons.lang.StringUtils;
@ -82,6 +81,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
private final GridBagLayout gridBagLayout = new GridBagLayout();
private final GridBagConstraints gridBagConstraints = new GridBagConstraints();
private final Map<Integer, Integer[]> orderingMap = new HashMap<>();
private final javax.swing.JPanel detailsPanel = new javax.swing.JPanel();
/**
* Creates new form GeneralPurposeArtifactViewer.
@ -90,6 +90,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
public GeneralPurposeArtifactViewer() {
addOrderings();
initComponents();
gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START;
detailsPanel.setLayout(gridBagLayout);
}
@ -161,8 +162,9 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
}
updateView(artifact, attributeMap, dataSourceName, sourceFileName);
}
detailsScrollPane.setViewportView(detailsPanel);
detailsScrollPane.revalidate();
revalidate();
repaint();
}
/**
@ -172,7 +174,8 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
private void resetComponent() {
// clear the panel
detailsPanel.removeAll();
gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START;
detailsPanel.setLayout(gridBagLayout);
detailsPanel.revalidate();
gridBagConstraints.gridy = 0;
gridBagConstraints.gridx = LABEL_COLUMN;
gridBagConstraints.weighty = 0.0;
@ -183,14 +186,6 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public Component getComponent() {
// Slap a vertical scrollbar on the panel
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
public boolean isSupported(BlackboardArtifact artifact) {
return (artifact != null)
&& (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()
@ -218,22 +213,22 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
detailsPanel = new javax.swing.JPanel();
detailsScrollPane = new javax.swing.JScrollPane();
setLayout(new java.awt.BorderLayout());
detailsScrollPane.setPreferredSize(new java.awt.Dimension(300, 0));
javax.swing.GroupLayout detailsPanelLayout = new javax.swing.GroupLayout(detailsPanel);
detailsPanel.setLayout(detailsPanelLayout);
detailsPanelLayout.setHorizontalGroup(
detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(detailsScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
detailsPanelLayout.setVerticalGroup(
detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 0, Short.MAX_VALUE)
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(detailsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(0, 0, 0))
);
add(detailsPanel, java.awt.BorderLayout.PAGE_START);
}// </editor-fold>//GEN-END:initComponents
/**
@ -243,7 +238,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
* @param attributeMap The map of attributes that exist for the artifact.
* @param dataSourceName The name of the datasource that caused the creation
* of the artifact.
* @param sourceFileName The name of the file that caused the creation of
* @param sourceFilePath The path of the file that caused the creation of
* the artifact.
*/
@NbBundle.Messages({"GeneralPurposeArtifactViewer.dates.created=Created",
@ -257,7 +252,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
private void updateView(BlackboardArtifact artifact, Map<Integer, List<BlackboardAttribute>> attributeMap, String dataSourceName, String sourceFilePath) {
final Integer artifactTypeId = artifact.getArtifactTypeID();
if (!(artifactTypeId < 1 || artifactTypeId >= Integer.MAX_VALUE)) {
addDetailsHeader(artifactTypeId);
JTextPane firstTextPane = addDetailsHeader(artifactTypeId);
Integer[] orderingArray = orderingMap.get(artifactTypeId);
if (orderingArray == null) {
orderingArray = DEFAULT_ORDERING;
@ -282,7 +277,6 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
addNameValueRow(bba.getAttributeType().getDisplayName(), displayString);
} else {
addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString());
}
}
}
@ -311,7 +305,11 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
addNameValueRow(Bundle.GeneralPurposeArtifactViewer_details_file(), sourceFilePath);
// add veritcal glue at the end
addPageEndGlue();
if (firstTextPane != null) {
firstTextPane.setCaretPosition(0);
}
}
detailsPanel.revalidate();
}
/**
@ -353,7 +351,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
"GeneralPurposeArtifactViewer.details.searchHeader=Web Search",
"GeneralPurposeArtifactViewer.details.cachedHeader=Cached File",
"GeneralPurposeArtifactViewer.details.cookieHeader=Cookie Details",})
private void addDetailsHeader(int artifactTypeId) {
private JTextPane addDetailsHeader(int artifactTypeId) {
String header;
if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) {
header = Bundle.GeneralPurposeArtifactViewer_details_historyHeader();
@ -370,7 +368,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
} else {
header = Bundle.GeneralPurposeArtifactViewer_details_attrHeader();
}
addHeader(header);
return addHeader(header);
}
/**
@ -381,16 +379,21 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
* @return JLabel Heading label added.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
private JLabel addHeader(String headerString) {
private JTextPane addHeader(String headerString) {
// create label for heading
javax.swing.JLabel headingLabel = new javax.swing.JLabel();
javax.swing.JTextPane headingLabel = new javax.swing.JTextPane();
headingLabel.setOpaque(false);
headingLabel.setFocusable(false);
headingLabel.setEditable(false);
// add a blank line before the start of new section, unless it's
// the first section
if (gridBagConstraints.gridy != 0) {
gridBagConstraints.gridy++;
detailsPanel.add(new javax.swing.JLabel(" "), gridBagConstraints);
// add to panel
addToPanel(new javax.swing.JLabel(" "));
addLineEndGlue();
}
headingLabel.setFocusable(false);
}
gridBagConstraints.gridy++;
gridBagConstraints.gridx = LABEL_COLUMN;;
// let the header span all of the row
@ -401,7 +404,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
// make it large and bold
headingLabel.setFont(headingLabel.getFont().deriveFont(Font.BOLD, headingLabel.getFont().getSize() + 2));
// add to panel
detailsPanel.add(headingLabel, gridBagConstraints);
addToPanel(headingLabel);
// reset constraints to normal
gridBagConstraints.gridwidth = LABEL_WIDTH;
// add line end glue
@ -417,9 +420,9 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
* @param keyString Key name to display.
* @param valueString Value string to display.
*/
private void addNameValueRow(String keyString, String valueString) {
private JTextPane addNameValueRow(String keyString, String valueString) {
addKeyAtCol(keyString);
addValueAtCol(valueString);
return addValueAtCol(valueString);
}
/**
@ -432,7 +435,8 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
gridBagConstraints.weightx = GLUE_WEIGHT_X; // take up all the horizontal space
gridBagConstraints.fill = GridBagConstraints.BOTH;
javax.swing.Box.Filler horizontalFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(32767, 0));
detailsPanel.add(horizontalFiller, gridBagConstraints);
// add to panel
addToPanel(horizontalFiller);
// restore fill & weight
gridBagConstraints.fill = GridBagConstraints.NONE;
gridBagConstraints.weightx = TEXT_WEIGHT_X;
@ -446,7 +450,8 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
gridBagConstraints.weighty = 1.0; // take up all the vertical space
gridBagConstraints.fill = GridBagConstraints.VERTICAL;
javax.swing.Box.Filler vertFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(0, 32767));
detailsPanel.add(vertFiller, gridBagConstraints);
// add to panel
addToPanel(vertFiller);
}
/**
@ -459,16 +464,22 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
private JLabel addKeyAtCol(String keyString) {
// create label
javax.swing.JLabel keyLabel = new javax.swing.JLabel();
keyLabel.setFocusable(false);
gridBagConstraints.gridy++;
gridBagConstraints.gridx = LABEL_COLUMN;
gridBagConstraints.gridwidth = LABEL_WIDTH;
// set text
keyLabel.setText(keyString + ": ");
// add to panel
detailsPanel.add(keyLabel, gridBagConstraints);
addToPanel(keyLabel);
return keyLabel;
}
private void addToPanel(Component comp) {
detailsPanel.add(comp, gridBagConstraints);
detailsPanel.revalidate();
}
/**
* Adds a value string to the panel at specified column.
*
@ -479,6 +490,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
private JTextPane addValueAtCol(String valueString) {
// create label,
JTextPane valueField = new JTextPane();
valueField.setFocusable(false);
valueField.setEditable(false);
valueField.setOpaque(false);
gridBagConstraints.gridx = VALUE_COLUMN;
@ -488,8 +500,6 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
cloneConstraints.fill = GridBagConstraints.BOTH;
// set text
valueField.setText(valueString);
// scroll to start of text
valueField.setCaretPosition(0);
// attach a right click menu with Copy option
valueField.addMouseListener(new java.awt.event.MouseAdapter() {
@Override
@ -497,8 +507,9 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
valueLabelMouseClicked(evt, valueField);
}
});
// add label to panel
// add label to panel with cloned contraintsF
detailsPanel.add(valueField, cloneConstraints);
revalidate();
// end the line
addLineEndGlue();
return valueField;
@ -531,6 +542,6 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel detailsPanel;
private javax.swing.JScrollPane detailsScrollPane;
// End of variables declaration//GEN-END:variables
}

View File

@ -586,6 +586,7 @@ final class MessageAccountPanel extends JPanel {
// Set up each matching account. We don't know what type of account we have, so check all the types to
// find any matches.
try {
boolean showErrorMessage = true;
for (CentralRepoAccount.CentralRepoAccountType type : CentralRepository.getInstance().getAllAccountTypes()) {
try {
// Try to load any matching accounts of this type. Throws an InvalidAccountIDException if the account is the
@ -593,13 +594,15 @@ final class MessageAccountPanel extends JPanel {
CentralRepoAccount account = CentralRepository.getInstance().getAccount(type, accountContainer.getAccount().getTypeSpecificID());
if (account != null) {
personaPanel.addAccount(account, Bundle.MessageAccountPanel_account_justification(), Persona.Confidence.HIGH);
} else {
createPersonaDialog.setStartupPopupMessage(Bundle.MessageAccountPanel_id_not_found_in_cr(accountContainer.getAccount().getTypeSpecificID()));
}
showErrorMessage = false;
}
} catch (InvalidAccountIDException ex2) {
// These are expected when the account identifier doesn't match the format of the account type.
}
}
if(showErrorMessage) {
createPersonaDialog.setStartupPopupMessage(Bundle.MessageAccountPanel_id_not_found_in_cr(accountContainer.getAccount().getTypeSpecificID()));
}
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error looking up account types in the central repository", ex);
}

View File

@ -200,6 +200,7 @@ class PersonaAccountFetcher extends SwingWorker<Map<String, Collection<Persona>>
// Set up each matching account. We don't know what type of account we have, so check all the types to
// find any matches.
try {
boolean showErrorMessage = true;
for (CentralRepoAccount.CentralRepoAccountType type : CentralRepository.getInstance().getAllAccountTypes()) {
try {
// Try to load any matching accounts of this type. Throws an InvalidAccountIDException if the account is the
@ -207,16 +208,17 @@ class PersonaAccountFetcher extends SwingWorker<Map<String, Collection<Persona>>
CentralRepoAccount account = CentralRepository.getInstance().getAccount(type, personaSearcherData.getAccountIdentifer());
if (account != null) {
personaPanel.addAccount(account, Bundle.PersonaAccountFetcher_account_justification(), Persona.Confidence.HIGH);
showErrorMessage = false;
}
if((personaSearcherData.getAccountIdentifer() != null &&
!personaSearcherData.getAccountIdentifer().isEmpty()) && account == null) {
dialog.setStartupPopupMessage(Bundle.PersonaAccountFetcher_not_account_in_cr(personaSearcherData.getAccountIdentifer()));
}
} catch (InvalidAccountIDException ex2) {
// These are expected when the account identifier doesn't match the format of the account type.
}
}
if ((personaSearcherData.getAccountIdentifer() != null
&& !personaSearcherData.getAccountIdentifer().isEmpty()) && showErrorMessage) {
dialog.setStartupPopupMessage(Bundle.PersonaAccountFetcher_not_account_in_cr(personaSearcherData.getAccountIdentifer()));
}
} catch (CentralRepoException ex) {
logger.log(Level.SEVERE, "Error looking up account types in the central repository", ex);
}

View File

@ -25,6 +25,7 @@ import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.TextViewer;
import org.sleuthkit.autopsy.corecomponents.DataContentViewerUtility;
import org.sleuthkit.autopsy.datamodel.StringContent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
/**
@ -81,7 +82,7 @@ public class StringsTextViewer implements TextViewer {
return false;
}
Content content = DataContentViewerUtility.getDefaultContent(node);
return (content != null && content.getSize() > 0);
return (content != null && !(content instanceof BlackboardArtifact) && content.getSize() > 0);
}
@Override

View File

@ -84,6 +84,7 @@ public final class UserPreferences {
private static final boolean DISPLAY_TRANSLATED_NAMES_DEFAULT = true;
public static final String EXTERNAL_HEX_EDITOR_PATH = "ExternalHexEditorPath";
public static final String SOLR_MAX_JVM_SIZE = "SolrMaxJVMSize";
private static final int DEFAULT_SOLR_HEAP_SIZE_MB = 2048;
public static final String RESULTS_TABLE_PAGE_SIZE = "ResultsTablePageSize";
private static final String GEO_TILE_OPTION = "GeolocationTileOption";
private static final String GEO_OSM_TILE_ZIP_PATH = "GeolocationOsmZipPath";
@ -535,10 +536,10 @@ public final class UserPreferences {
/**
* Get the maximum JVM heap size (in MB) for the embedded Solr server.
*
* @return Saved value or default (512)
* @return Saved value or default (2 GB)
*/
public static int getMaxSolrVMSize() {
return preferences.getInt(SOLR_MAX_JVM_SIZE, 512);
return preferences.getInt(SOLR_MAX_JVM_SIZE, DEFAULT_SOLR_HEAP_SIZE_MB);
}
/**

View File

@ -82,6 +82,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private static final String CONFIG_FILE_EXTENSION = ".conf";
private static final long ONE_BILLION = 1000000000L; //used to roughly convert system memory from bytes to gigabytes
private static final int MEGA_IN_GIGA = 1024; //used to convert memory settings saved as megabytes to gigabytes
private static final int DEFAULT_SOLR_HEAP_SIZE_MB = 2048;
private static final int MIN_MEMORY_IN_GB = 2; //the enforced minimum memory in gigabytes
private static final Logger logger = Logger.getLogger(AutopsyOptionsPanel.class.getName());
private String initialMemValue = Long.toString(Runtime.getRuntime().maxMemory() / ONE_BILLION);
@ -113,7 +114,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
// The cast to int in the following is to ensure that the correct SpinnerNumberModel
// constructor is called.
solrMaxHeapSpinner.setModel(new javax.swing.SpinnerNumberModel(UserPreferences.getMaxSolrVMSize(),
512, ((int) getSystemMemoryInGB()) * MEGA_IN_GIGA, 512));
DEFAULT_SOLR_HEAP_SIZE_MB, ((int) getSystemMemoryInGB()) * MEGA_IN_GIGA, DEFAULT_SOLR_HEAP_SIZE_MB));
textFieldListener = new TextFieldListener();
agencyLogoPathField.getDocument().addDocumentListener(textFieldListener);

View File

@ -46,6 +46,7 @@ import static org.sleuthkit.autopsy.corecomponents.Bundle.*;
import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.datamodel.DataConversion;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
@ -606,7 +607,7 @@ public class DataContentViewerHex extends javax.swing.JPanel implements DataCont
return false;
}
Content content = DataContentViewerUtility.getDefaultContent(node);
return content != null && content.getSize() > 0;
return content != null && !(content instanceof BlackboardArtifact) && content.getSize() > 0;
}
@Override

View File

@ -32,9 +32,6 @@ public class DataContentViewerUtility {
* preferring to return any Content object other than a BlackboardArtifact
* object.
*
* This method was written with the needs of the hex and strings content
* viewers in mind - the algorithm is exactly what those viewers require.
*
* @param node A display Node object.
*
* @return If there are multiple Content objects associated with the Node,

View File

@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.datamodel;
import com.google.common.annotations.Beta;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.beans.PropertyChangeEvent;
@ -234,7 +235,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* @param iconPath The path to the icon for the artifact type.
*/
public BlackboardArtifactNode(BlackboardArtifact artifact, String iconPath) {
super(artifact, createLookup(artifact));
super(artifact, createLookup(artifact, false));
this.artifact = artifact;
for (Content lookupContent : this.getLookup().lookupAll(Content.class)) {
if ((lookupContent != null) && (!(lookupContent instanceof BlackboardArtifact))) {
@ -264,6 +265,49 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakListener);
}
/**
* Constructs a BlackboardArtifactNode, an AbstractNode implementation that
* can be used to represent an artifact of any type.
*
* @param artifact The artifact to represent.
* @param lookupIsAssociatedFile True if the Content lookup should be made
* for the associated file instead of the
* parent file.
*/
@Beta
public BlackboardArtifactNode(BlackboardArtifact artifact, boolean lookupIsAssociatedFile) {
super(artifact, createLookup(artifact, lookupIsAssociatedFile));
this.artifact = artifact;
try {
//The lookup for a file may or may not exist so we define the srcContent as the parent.
srcContent = artifact.getParent();
} catch (TskCoreException ex) {
logger.log(Level.WARNING, MessageFormat.format("Error getting the parent of the artifact for (artifact objID={0})", artifact.getId()), ex);
}
if (srcContent != null) {
try {
/*
* Calling this getter causes the unique path of the source
* content to be cached in the Content object. This is
* advantageous as long as this node is constructed in a
* background thread instead of a UI thread.
*/
srcContent.getUniquePath();
} catch (TskCoreException ex) {
logger.log(Level.WARNING, MessageFormat.format("Error getting the unique path of the source content (artifact objID={0})", artifact.getId()), ex);
}
} else {
throw new IllegalArgumentException(MessageFormat.format("Artifact missing source content (artifact objID={0})", artifact));
}
setName(Long.toString(artifact.getArtifactID()));
String displayName = srcContent.getName();
setDisplayName(displayName);
setShortDescription(displayName);
String iconPath = IconsUtil.getIconFilePath(artifact.getArtifactTypeID());
setIconBaseWithExtension(iconPath != null && iconPath.charAt(0) == '/' ? iconPath.substring(1) : iconPath);
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakListener);
}
/**
* Constructs a BlackboardArtifactNode, an AbstractNode implementation that
* can be used to represent an artifact of any type.
@ -284,22 +328,46 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
*/
private static Lookup createLookup(BlackboardArtifact artifact) {
final long objectID = artifact.getObjectID();
Content content = null;
try {
if (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID() || artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID()) {
content = getPathIdFile(artifact);
}
Content content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
if (content == null) {
content = contentCache.get(objectID, () -> artifact.getSleuthkitCase().getContentById(objectID));
return Lookups.fixed(artifact);
} else {
return Lookups.fixed(artifact, content);
}
} catch (ExecutionException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
content = null;
}
if (content == null) {
return Lookups.fixed(artifact);
}
}
/**
* Creates a Lookup object for this node and populates it with both the
* artifact this node represents and its source content.
*
* @param artifact The artifact this node represents.
* @param lookupIsAssociatedFile True if the Content lookup should be made
* for the associated file instead of the
* parent file.
*
* @return The Lookup.
*/
private static Lookup createLookup(BlackboardArtifact artifact, boolean lookupIsAssociatedFile) {
Content content = null;
if (lookupIsAssociatedFile) {
try {
content = getPathIdFile(artifact);
} catch (ExecutionException ex) {
logger.log(Level.SEVERE, MessageFormat.format("Error getting source content (artifact objID={0}", artifact.getId()), ex); //NON-NLS
content = null;
}
if (content == null) {
return Lookups.fixed(artifact);
} else {
return Lookups.fixed(artifact, content);
}
} else {
return Lookups.fixed(artifact, content);
return createLookup(artifact);
}
}
@ -982,7 +1050,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
}
map.put(attribute.getAttributeType().getDisplayName(), value);
} else {
switch(attribute.getAttributeType().getValueType()) {
switch (attribute.getAttributeType().getValueType()) {
case INTEGER:
map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueInt());
break;
@ -990,13 +1058,13 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueDouble());
break;
case LONG:
map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueLong());
map.put(attribute.getAttributeType().getDisplayName(), attribute.getValueLong());
break;
default:
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
map.put(attribute.getAttributeType().getDisplayName(), attribute.getDisplayString());
}
}
}
} catch (TskCoreException ex) {

View File

@ -22,6 +22,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
import java.util.logging.Level;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -46,7 +47,8 @@ class DataSourceGroupingNode extends DisplayableItemNode {
DataSourceGroupingNode(DataSource dataSource) {
super (Optional.ofNullable(createDSGroupingNodeChildren(dataSource))
.orElse(new RootContentChildren(Arrays.asList(Collections.EMPTY_LIST))));
.orElse(new RootContentChildren(Arrays.asList(Collections.EMPTY_LIST))),
Lookups.singleton(dataSource));
if (dataSource instanceof Image) {
Image image = (Image) dataSource;

View File

@ -125,6 +125,8 @@ public final class IconsUtil {
imageFile = "domain-16.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_GPS_AREA.getTypeID()) {
imageFile = "gps-area.png"; //NON-NLS
} else if (typeID == ARTIFACT_TYPE.TSK_YARA_HIT.getTypeID()) {
imageFile = "yara_16.png"; //NON-NLS
} else {
imageFile = "artifact-icon.png"; //NON-NLS
}

View File

@ -28,18 +28,21 @@ public class CityRecord extends KdTree.XYZPoint {
private final String cityName;
private final String country;
private final String state;
/**
* Main constructor.
*
* @param cityName The name of the city.
* @param state The state of the city.
* @param country The country of that city.
* @param latitude Latitude for the city.
* @param longitude Longitude for the city.
*/
CityRecord(String cityName, String country, double latitude, double longitude) {
CityRecord(String cityName, String state, String country, double latitude, double longitude) {
super(latitude, longitude);
this.cityName = cityName;
this.state = state;
this.country = country;
}
@ -50,6 +53,13 @@ public class CityRecord extends KdTree.XYZPoint {
return cityName;
}
/**
* @return The state of the city.
*/
public String getState() {
return state;
}
/**
* @return The country of that city.
*/

View File

@ -42,6 +42,7 @@ class ClosestCityMapper {
// index within a csv row of pertinent data
private static final int CITY_NAME_IDX = 0;
private static final int STATE_NAME_IDX = 7;
private static final int COUNTRY_NAME_IDX = 4;
private static final int LAT_IDX = 2;
private static final int LONG_IDX = 3;
@ -52,7 +53,7 @@ class ClosestCityMapper {
// Identifies if cities are in last, first format like "Korea, South"
private static final Pattern COUNTRY_WITH_COMMA = Pattern.compile("^\\s*([^,]*)\\s*,\\s*([^,]*)\\s*$");
private static final int MAX_IDX = Stream.of(CITY_NAME_IDX, COUNTRY_NAME_IDX, LAT_IDX, LONG_IDX)
private static final int MAX_IDX = Stream.of(CITY_NAME_IDX, STATE_NAME_IDX, COUNTRY_NAME_IDX, LAT_IDX, LONG_IDX)
.max(Integer::compare)
.get();
@ -169,12 +170,15 @@ class ClosestCityMapper {
return null;
}
// city is required
String cityName = csvRow.get(CITY_NAME_IDX);
if (StringUtils.isBlank(cityName)) {
logger.log(Level.WARNING, String.format("No city name determined for line %d.", lineNum));
return null;
}
// state and country can be optional
String stateName = csvRow.get(STATE_NAME_IDX);
String countryName = parseCountryName(csvRow.get(COUNTRY_NAME_IDX), lineNum);
Double lattitude = tryParse(csvRow.get(LAT_IDX));
@ -189,7 +193,7 @@ class ClosestCityMapper {
return null;
}
return new CityRecord(cityName, countryName, lattitude, longitude);
return new CityRecord(cityName, stateName, countryName, lattitude, longitude);
}
/**

View File

@ -309,7 +309,7 @@ public class GeolocationSummary implements DefaultArtifactUpdateGovernor {
Long mostRecent = null;
for (MapWaypoint pt : dataSourcePoints) {
CityRecord city = closestCityMapper.findClosest(new CityRecord(null, null, pt.getX(), pt.getY()));
CityRecord city = closestCityMapper.findClosest(new CityRecord(null, null, null, pt.getX(), pt.getY()));
Long curTime = pt.getTimestamp();
if (curTime != null && (mostRecent == null || curTime > mostRecent)) {
mostRecent = curTime;

View File

@ -1,145 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datasourcesummary.datamodel;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.IngestJobInfo;
import org.sleuthkit.datamodel.IngestModuleInfo;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Utilities for checking if an ingest module has been run on a datasource.
*/
@Messages({
"IngestModuleCheckUtil_recentActivityModuleName=Recent Activity",
})
public class IngestModuleCheckUtil {
public static final String RECENT_ACTIVITY_FACTORY = "org.sleuthkit.autopsy.recentactivity.RecentActivityExtracterModuleFactory";
public static final String RECENT_ACTIVITY_MODULE_NAME = Bundle.IngestModuleCheckUtil_recentActivityModuleName();
// IngestModuleInfo separator for unique_name
private static final String UNIQUE_NAME_SEPARATOR = "-";
private final SleuthkitCaseProvider caseProvider;
/**
* Main constructor.
*/
public IngestModuleCheckUtil() {
this(SleuthkitCaseProvider.DEFAULT);
}
/**
* Main constructor with external dependencies specified. This constructor
* is designed with unit testing in mind since mocked dependencies can be
* utilized.
*
* @param provider The object providing the current SleuthkitCase.
*/
public IngestModuleCheckUtil(SleuthkitCaseProvider provider) {
this.caseProvider = provider;
}
/**
* Gets the fully qualified factory from the IngestModuleInfo.
* @param info The IngestJobInfo.
* @return The fully qualified factory.
*/
private static String getFullyQualifiedFactory(IngestModuleInfo info) {
if (info == null) {
return null;
}
String qualifiedName = info.getUniqueName();
if (StringUtils.isBlank(qualifiedName)) {
return null;
}
return qualifiedName.split(UNIQUE_NAME_SEPARATOR)[0];
}
/**
* Whether or not the ingest job info contains the ingest modulename.
* @param info The IngestJobInfo.
* @param fullyQualifiedFactory The fully qualified classname of the relevant factory.
* @return True if the ingest module name is contained in the data.
*/
private static boolean hasIngestModule(IngestJobInfo info, String fullyQualifiedFactory) {
if (info == null || info.getIngestModuleInfo() == null || StringUtils.isBlank(fullyQualifiedFactory)) {
return false;
}
return info.getIngestModuleInfo().stream()
.anyMatch((moduleInfo) -> {
String thisQualifiedFactory = getFullyQualifiedFactory(moduleInfo);
return fullyQualifiedFactory.equalsIgnoreCase(thisQualifiedFactory);
});
}
/**
* Whether or not a data source has been ingested with a particular ingest module.
* @param dataSource The datasource.
* @param fullyQualifiedFactory The fully qualified classname of the relevant factory.
* @return Whether or not a data source has been ingested with a particular ingest module.
* @throws TskCoreException
* @throws SleuthkitCaseProviderException
*/
public boolean isModuleIngested(DataSource dataSource, String fullyQualifiedFactory)
throws TskCoreException, SleuthkitCaseProviderException {
if (dataSource == null) {
return false;
}
long dataSourceId = dataSource.getId();
return caseProvider.get().getIngestJobs().stream()
.anyMatch((ingestJob) -> {
return ingestJob != null
&& ingestJob.getObjectId() == dataSourceId
&& hasIngestModule(ingestJob, fullyQualifiedFactory);
});
}
/**
* Get a mapping of fully qualified factory name to display name.
* @param skCase The SleuthkitCase.
* @return The mapping of fully qualified factory name to display name.
* @throws TskCoreException
*/
public static Map<String, String> getFactoryDisplayNames(SleuthkitCase skCase) throws TskCoreException {
return skCase.getIngestJobs().stream()
.flatMap(ingestJob -> ingestJob.getIngestModuleInfo().stream())
.collect(Collectors.toMap(
(moduleInfo) -> getFullyQualifiedFactory(moduleInfo),
(moduleInfo) -> moduleInfo.getDisplayName(),
(a,b) -> a));
}
}

View File

@ -29,8 +29,6 @@ import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.IngestRunningLabel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel;
import org.sleuthkit.autopsy.modules.hashdatabase.HashLookupModuleFactory;
import org.sleuthkit.autopsy.modules.interestingitems.InterestingItemsIngestModuleFactory;
import org.sleuthkit.datamodel.DataSource;
/**
@ -46,15 +44,6 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
private static final String KEYWORD_SEARCH_MODULE_NAME = Bundle.AnalysisPanel_keywordSearchModuleName();
private static final String KEYWORD_SEARCH_FACTORY = "org.sleuthkit.autopsy.keywordsearch.KeywordSearchModuleFactory";
private static final String INTERESTING_ITEM_MODULE_NAME = new InterestingItemsIngestModuleFactory().getModuleDisplayName();
private static final String INTERESTING_ITEM_FACTORY = InterestingItemsIngestModuleFactory.class.getCanonicalName();
private static final String HASHSET_MODULE_NAME = HashLookupModuleFactory.getModuleName();
private static final String HASHSET_FACTORY = HashLookupModuleFactory.class.getCanonicalName();
/**
* Default Column definitions for each table
*/
@ -90,9 +79,8 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
keywordHitsTable,
interestingItemsTable
);
private final IngestRunningLabel ingestRunningLabel = new IngestRunningLabel();
/**
* All of the components necessary for data fetch swing workers to load data
@ -115,28 +103,26 @@ public class AnalysisPanel extends BaseDataSourceSummaryPanel {
// hashset hits loading components
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> analysisData.getHashsetCounts(dataSource),
(result) -> showResultWithModuleCheck(hashsetHitsTable, result, HASHSET_FACTORY, HASHSET_MODULE_NAME)),
(result) -> hashsetHitsTable.showDataFetchResult(result)),
// keyword hits loading components
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> analysisData.getKeywordCounts(dataSource),
(result) -> showResultWithModuleCheck(keywordHitsTable, result, KEYWORD_SEARCH_FACTORY, KEYWORD_SEARCH_MODULE_NAME)),
(result) -> keywordHitsTable.showDataFetchResult(result)),
// interesting item hits loading components
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> analysisData.getInterestingItemCounts(dataSource),
(result) -> showResultWithModuleCheck(interestingItemsTable, result, INTERESTING_ITEM_FACTORY, INTERESTING_ITEM_MODULE_NAME))
(result) -> interestingItemsTable.showDataFetchResult(result))
);
initComponents();
}
@Override
public void close() {
ingestRunningLabel.unregister();
super.close();
}
@Override
protected void fetchInformation(DataSource dataSource) {
fetchInformation(dataFetchComponents, dataSource);

View File

@ -25,7 +25,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -39,11 +38,8 @@ import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult.ResultType;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker.DataFetchComponents;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.EventUpdateHandler;
@ -73,7 +69,6 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
private static final Logger logger = Logger.getLogger(BaseDataSourceSummaryPanel.class.getName());
private final SwingWorkerSequentialExecutor executor = new SwingWorkerSequentialExecutor();
private final IngestModuleCheckUtil ingestModuleCheck = new IngestModuleCheckUtil();
private final EventUpdateHandler updateHandler;
private final List<UpdateGovernor> governors;
@ -319,8 +314,8 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
}
/**
* Given the relevant file, navigates to the file in the
* tree and closes data source summary dialog if open.
* Given the relevant file, navigates to the file in the tree and closes
* data source summary dialog if open.
*
* @param file The file.
* @return The menu item list for a go to artifact menu item.
@ -471,76 +466,4 @@ abstract class BaseDataSourceSummaryPanel extends JPanel {
fetchInformation(dataSource);
}
}
/**
* Get default message when there is a NotIngestedWithModuleException.
*
* @param moduleName The moduleName.
*
* @return Message specifying that the ingest module was not run.
*/
@Messages({
"# {0} - module name",
"BaseDataSourceSummaryPanel_defaultNotIngestMessage=The {0} ingest module has not been run on this data source."
})
protected String getDefaultNoIngestMessage(String moduleName) {
return Bundle.BaseDataSourceSummaryPanel_defaultNotIngestMessage(moduleName);
}
/**
* Utility method to return the IngestModuleCheckUtil.
*
* @return The IngestModuleCheckUtil.
*/
protected IngestModuleCheckUtil getIngestModuleCheckUtil() {
return this.ingestModuleCheck;
}
/**
* Utility method that in the event of a) there are no results and b) a
* relevant ingest module has not been run on this datasource, then a
* message indicating the unrun ingest module will be shown. Otherwise, the
* default LoadableComponent.showDataFetchResult behavior will be used.
*
* @param component The component.
* @param result The data result.
* @param factoryClass The fully qualified class name of the relevant
* factory.
* @param moduleName The name of the ingest module (i.e. 'Keyword Search').
*/
protected <T> void showResultWithModuleCheck(LoadableComponent<List<T>> component, DataFetchResult<List<T>> result, String factoryClass, String moduleName) {
Predicate<List<T>> hasResults = (lst) -> lst != null && !lst.isEmpty();
showResultWithModuleCheck(component, result, hasResults, factoryClass, moduleName);
}
/**
* Utility method that in the event of a) there are no results and b) a
* relevant ingest module has not been run on this datasource, then a
* message indicating the unrun ingest module will be shown. Otherwise, the
* default LoadableComponent.showDataFetchResult behavior will be used.
*
* @param component The component.
* @param result The data result.
* @param hasResults Given the data type, will provide whether or not the
* data contains any actual results.
* @param factoryClass The fully qualified class name of the relevant
* factory.
* @param moduleName The name of the ingest module (i.e. 'Keyword Search').
*/
protected <T> void showResultWithModuleCheck(LoadableComponent<T> component, DataFetchResult<T> result,
Predicate<T> hasResults, String factoryClass, String moduleName) {
if (result != null && result.getResultType() == ResultType.SUCCESS && !hasResults.test(result.getData())) {
try {
if (!ingestModuleCheck.isModuleIngested(getDataSource(), factoryClass)) {
component.showMessage(getDefaultNoIngestMessage(moduleName));
return;
}
} catch (TskCoreException | SleuthkitCaseProviderException ex) {
logger.log(Level.WARNING, "There was an error while checking for ingest modules for datasource.", ex);
}
}
component.showDataFetchResult(result);
}
}

View File

@ -63,8 +63,6 @@ import org.sleuthkit.datamodel.DataSource;
public class GeolocationPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
private static final String GPX_FACTORY = "org.python.proxies.GPX_Parser_Module$GPXParserFileIngestModuleFactory";
private static final String GPX_NAME = "GPX Parser";
private static final int DAYS_COUNT = 30;
private static final int MAX_COUNT = 10;
@ -114,6 +112,8 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
* @param whereUsedData The GeolocationSummary instance to use.
*/
public GeolocationPanel(GeolocationSummary whereUsedData) {
super(whereUsedData);
this.whereUsedData = whereUsedData;
// set up data acquisition methods
dataFetchComponents = Arrays.asList(
@ -145,11 +145,19 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
return null;
}
if (StringUtils.isBlank(record.getCountry())) {
return record.getCityName();
}
List<String> cityIdentifiers = Stream.of(record.getCityName(), record.getState(), record.getCountry())
.filter(StringUtils::isNotBlank)
.collect(Collectors.toList());
return String.format("%s, %s", record.getCityName(), record.getCountry());
if (cityIdentifiers.size() == 1) {
return cityIdentifiers.get(0);
} else if (cityIdentifiers.size() == 2) {
return String.format("%s, %s", cityIdentifiers.get(0), cityIdentifiers.get(1));
} else if (cityIdentifiers.size() >= 3) {
return String.format("%s, %s; %s", cityIdentifiers.get(0), cityIdentifiers.get(1), cityIdentifiers.get(2));
}
return null;
}
/**
@ -211,7 +219,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
goToGeolocation.setEnabled(true);
}
showResultWithModuleCheck(table, convertedData, GPX_FACTORY, GPX_NAME);
table.showDataFetchResult(convertedData);
}
/**
@ -225,7 +233,7 @@ public class GeolocationPanel extends BaseDataSourceSummaryPanel {
private void openGeolocationWindow(DataSource dataSource, Integer daysLimit) {
// notify dialog (if in dialog) should close.
notifyParentClose();
// set the filter
TopComponent topComponent = WindowManager.getDefault().findTopComponent(GeolocationTopComponent.class.getSimpleName());
if (topComponent instanceof GeolocationTopComponent) {

View File

@ -22,7 +22,6 @@ import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.ingestmodule.CentralRepoIngestModuleFactory;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.PastCasesSummary.PastCasesResult;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel;
@ -46,8 +45,6 @@ import org.sleuthkit.datamodel.DataSource;
public class PastCasesPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
private static final String CR_FACTORY = CentralRepoIngestModuleFactory.class.getName();
private static final String CR_NAME = CentralRepoIngestModuleFactory.getModuleName();
private static final ColumnModel<Pair<String, Long>> CASE_COL = new ColumnModel<>(
Bundle.PastCasesPanel_caseColumn_title(),
@ -84,6 +81,8 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
* Creates new form PastCasesPanel
*/
public PastCasesPanel(PastCasesSummary pastCaseData) {
super(pastCaseData);
// set up data acquisition methods
dataFetchComponents = Arrays.asList(
new DataFetchWorker.DataFetchComponents<>(
@ -101,8 +100,8 @@ public class PastCasesPanel extends BaseDataSourceSummaryPanel {
* @param result The result.
*/
private void handleResult(DataFetchResult<PastCasesResult> result) {
showResultWithModuleCheck(notableFileTable, DataFetchResult.getSubResult(result, (res) -> res.getTaggedNotable()), CR_FACTORY, CR_NAME);
showResultWithModuleCheck(sameIdTable, DataFetchResult.getSubResult(result, (res) -> res.getSameIdsResults()), CR_FACTORY, CR_NAME);
notableFileTable.showDataFetchResult(DataFetchResult.getSubResult(result, (res) -> res.getTaggedNotable()));
sameIdTable.showDataFetchResult(DataFetchResult.getSubResult(result, (res) -> res.getSameIdsResults()));
}
@Override

View File

@ -23,7 +23,6 @@ import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails;
@ -44,8 +43,6 @@ import org.sleuthkit.datamodel.DataSource;
public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
private static final long serialVersionUID = 1L;
private static final String EMAIL_PARSER_FACTORY = "org.sleuthkit.autopsy.thunderbirdparser.EmailParserModuleFactory";
private static final String EMAIL_PARSER_MODULE_NAME = Bundle.RecentFilePanel_emailParserModuleName();
private final List<JTablePanel<?>> tablePanelList = new ArrayList<>();
private final List<DataFetchWorker.DataFetchComponents<DataSource, ?>> dataFetchComponents = new ArrayList<>();
@ -167,11 +164,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
DataFetchWorker.DataFetchComponents<DataSource, List<RecentFileDetails>> worker
= new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> dataHandler.getRecentlyOpenedDocuments(dataSource, 10),
(result) -> {
showResultWithModuleCheck(pane, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
});
(result) -> pane.showDataFetchResult(result));
dataFetchComponents.add(worker);
}
@ -210,11 +203,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
DataFetchWorker.DataFetchComponents<DataSource, List<RecentDownloadDetails>> worker
= new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> dataHandler.getRecentDownloads(dataSource, 10),
(result) -> {
showResultWithModuleCheck(pane, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
});
(result) -> pane.showDataFetchResult(result));
dataFetchComponents.add(worker);
}
@ -253,7 +242,7 @@ public final class RecentFilesPanel extends BaseDataSourceSummaryPanel {
DataFetchWorker.DataFetchComponents<DataSource, List<RecentAttachmentDetails>> worker
= new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> dataHandler.getRecentAttachments(dataSource, 10),
(result) -> showResultWithModuleCheck(pane, result, EMAIL_PARSER_FACTORY, EMAIL_PARSER_MODULE_NAME)
(result) -> pane.showDataFetchResult(result)
);
dataFetchComponents.add(worker);

View File

@ -103,6 +103,8 @@ public class TimelinePanel extends BaseDataSourceSummaryPanel {
* Creates new form PastCasesPanel
*/
public TimelinePanel(TimelineSummary timelineData) {
super(timelineData);
// set up data acquisition methods
dataFetchComponents = Arrays.asList(
new DataFetchWorker.DataFetchComponents<>(

View File

@ -26,16 +26,13 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.FileTypeUtils.FileTypeCategory;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.TypesSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.ContainerSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.MimeTypeSummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException;
import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult;
@ -251,25 +248,11 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
// usage label worker
new DataFetchWorker.DataFetchComponents<>(
containerData::getDataSourceType,
(result) -> {
showResultWithModuleCheck(
usageLabel,
result,
StringUtils::isNotBlank,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
(result) -> usageLabel.showDataFetchResult(result)),
// os label worker
new DataFetchWorker.DataFetchComponents<>(
containerData::getOperatingSystems,
(result) -> {
showResultWithModuleCheck(
osLabel,
result,
StringUtils::isNotBlank,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
(result) -> osLabel.showDataFetchResult(result)),
// size label worker
new DataFetchWorker.DataFetchComponents<>(
(dataSource) -> {
@ -379,50 +362,22 @@ class TypesPanel extends BaseDataSourceSummaryPanel {
* @param result The result to be shown.
*/
private void showMimeTypeCategories(DataFetchResult<TypesPieChartData> result) {
// if result is null check for ingest module and show empty results.
if (result == null) {
showPieResultWithModuleCheck(null);
fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(null));
return;
}
// if error, show error
if (result.getResultType() == ResultType.ERROR) {
this.fileMimeTypesChart.showDataFetchResult(DataFetchResult.getErrorResult(result.getException()));
fileMimeTypesChart.showDataFetchResult(DataFetchResult.getErrorResult(result.getException()));
return;
}
TypesPieChartData data = result.getData();
if (data == null) {
// if no data, do an ingest module check with empty results
showPieResultWithModuleCheck(null);
} else if (!data.isUsefulContent()) {
// if no useful data, do an ingest module check and show data
showPieResultWithModuleCheck(data.getPieSlices());
fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(null));
} else {
// otherwise, show the data
this.fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(data.getPieSlices()));
}
}
/**
* Shows a message in the fileMimeTypesChart about the data source not being
* ingested with the file type ingest module if the data source has not been
* ingested with that module. Also shows data if present.
*
* @param items The list of items to show.
*/
private void showPieResultWithModuleCheck(List<PieChartItem> items) {
boolean hasBeenIngested = false;
try {
hasBeenIngested = this.getIngestModuleCheckUtil().isModuleIngested(getDataSource(), FILE_TYPE_FACTORY);
} catch (TskCoreException | SleuthkitCaseProviderException ex) {
logger.log(Level.WARNING, "There was an error fetching whether or not the current data source has been ingested with the file type ingest module.", ex);
}
if (hasBeenIngested) {
this.fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(items));
} else {
this.fileMimeTypesChart.showDataWithMessage(items, getDefaultNoIngestMessage(FILE_TYPE_MODULE_NAME));
fileMimeTypesChart.showDataFetchResult(DataFetchResult.getSuccessResult(data.getPieSlices()));
}
}

View File

@ -27,7 +27,6 @@ import java.util.List;
import java.util.Locale;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.IngestModuleCheckUtil;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.LastAccessedArtifact;
import org.sleuthkit.autopsy.datasourcesummary.datamodel.UserActivitySummary.TopAccountResult;
@ -287,43 +286,23 @@ public class UserActivityPanel extends BaseDataSourceSummaryPanel {
// top programs query
new DataFetchComponents<DataSource, List<TopProgramsResult>>(
(dataSource) -> userActivityData.getTopPrograms(dataSource, TOP_PROGS_COUNT),
(result) -> {
showResultWithModuleCheck(topProgramsTable, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
(result) -> topProgramsTable.showDataFetchResult(result)),
// top domains query
new DataFetchComponents<DataSource, List<TopDomainsResult>>(
(dataSource) -> userActivityData.getRecentDomains(dataSource, TOP_DOMAINS_COUNT),
(result) -> {
showResultWithModuleCheck(recentDomainsTable, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
(result) -> recentDomainsTable.showDataFetchResult(result)),
// top web searches query
new DataFetchComponents<DataSource, List<TopWebSearchResult>>(
(dataSource) -> userActivityData.getMostRecentWebSearches(dataSource, TOP_SEARCHES_COUNT),
(result) -> {
showResultWithModuleCheck(topWebSearchesTable, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
(result) -> topWebSearchesTable.showDataFetchResult(result)),
// top devices query
new DataFetchComponents<DataSource, List<TopDeviceAttachedResult>>(
(dataSource) -> userActivityData.getRecentDevices(dataSource, TOP_DEVICES_COUNT),
(result) -> {
showResultWithModuleCheck(topDevicesAttachedTable, result,
IngestModuleCheckUtil.RECENT_ACTIVITY_FACTORY,
IngestModuleCheckUtil.RECENT_ACTIVITY_MODULE_NAME);
}),
(result) -> topDevicesAttachedTable.showDataFetchResult(result)),
// top accounts query
new DataFetchComponents<DataSource, List<TopAccountResult>>(
(dataSource) -> userActivityData.getRecentAccounts(dataSource, TOP_ACCOUNTS_COUNT),
(result) -> {
showResultWithModuleCheck(topAccountsTable, result,
ANDROID_FACTORY,
ANDROID_MODULE_NAME);
})
(result) -> topAccountsTable.showDataFetchResult(result))
);
initComponents();

View File

@ -140,9 +140,10 @@ public class DiscoveryAttributes {
return new DiscoveryKeyUtils.FileTypeGroupKey(file);
}
}
/**
* Attribute for grouping/sorting by domain category (TSK_WEB_CATEGORY artifacts).
* Attribute for grouping/sorting by domain category (TSK_WEB_CATEGORY
* artifacts).
*/
static class DomainCategoryAttribute extends AttributeType {
@ -150,7 +151,7 @@ public class DiscoveryAttributes {
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
return new DiscoveryKeyUtils.DomainCategoryGroupKey(result);
}
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
@ -167,10 +168,11 @@ public class DiscoveryAttributes {
throw new DiscoveryException("Error fetching TSK_WEB_CATEGORY artifacts from the database", ex);
}
}
/**
* Loads all TSK_WEB_CATEGORY artifacts and maps the domain attribute to the category name attribute.
* Each ResultDomain is then parsed and matched against this map of values.
* Loads all TSK_WEB_CATEGORY artifacts and maps the domain attribute to
* the category name attribute. Each ResultDomain is then parsed and
* matched against this map of values.
*/
private Map<String, String> getDomainsWithWebCategories(SleuthkitCase caseDb) throws TskCoreException, InterruptedException {
Map<String, String> domainToCategory = new HashMap<>();
@ -190,7 +192,7 @@ public class DiscoveryAttributes {
}
}
return domainToCategory;
return domainToCategory;
}
}
@ -269,36 +271,36 @@ public class DiscoveryAttributes {
}
}
}
/**
* Organizes the domain instances by normalized domain value.
* This helps reduce the complexity of updating ResultDomain instances
* after the query has been executed.
*
* Example: query for notable status of google.com. Result: notable
* With this map, all domain instances that represent google.com can
* be updated after one simple lookup.
* Organizes the domain instances by normalized domain value. This helps
* reduce the complexity of updating ResultDomain instances after the query
* has been executed.
*
* Example: query for notable status of google.com. Result: notable With
* this map, all domain instances that represent google.com can be updated
* after one simple lookup.
*/
private static Map<String, List<ResultDomain>> organizeByValue(List<ResultDomain> domainsBatch, CorrelationAttributeInstance.Type attributeType) {
final Map<String, List<ResultDomain>> resultDomainTable = new HashMap<>();
for (ResultDomain domainInstance : domainsBatch) {
try {
final String domainValue = domainInstance.getDomain();
final String normalizedDomain = CorrelationAttributeNormalizer.normalize(attributeType, domainValue);
final List<ResultDomain> bucket = resultDomainTable.getOrDefault(normalizedDomain, new ArrayList<>());
bucket.add(domainInstance);
resultDomainTable.put(normalizedDomain, bucket);
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.INFO, String.format("Domain [%s] failed normalization, skipping...", domainInstance.getDomain()));
}
final Map<String, List<ResultDomain>> resultDomainTable = new HashMap<>();
for (ResultDomain domainInstance : domainsBatch) {
try {
final String domainValue = domainInstance.getDomain();
final String normalizedDomain = CorrelationAttributeNormalizer.normalize(attributeType, domainValue);
final List<ResultDomain> bucket = resultDomainTable.getOrDefault(normalizedDomain, new ArrayList<>());
bucket.add(domainInstance);
resultDomainTable.put(normalizedDomain, bucket);
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.INFO, String.format("Domain [%s] failed normalization, skipping...", domainInstance.getDomain()));
}
return resultDomainTable;
}
return resultDomainTable;
}
/**
* Helper function to create a string of comma separated values.
* Each value is wrapped in `'`. This method is used to bundle up
* a collection of values for use in a SQL WHERE IN (...) clause.
* Helper function to create a string of comma separated values. Each value
* is wrapped in `'`. This method is used to bundle up a collection of
* values for use in a SQL WHERE IN (...) clause.
*/
private static String createCSV(Set<String> values) {
StringJoiner joiner = new StringJoiner(", ");
@ -307,30 +309,30 @@ public class DiscoveryAttributes {
}
return joiner.toString();
}
/**
* Attribute for grouping/sorting by notability in the CR.
*/
static class PreviouslyNotableAttribute extends AttributeType {
static final int DOMAIN_BATCH_SIZE = 500; // Number of domains to look up at one time
@Override
public DiscoveryKeyUtils.GroupKey getGroupKey(Result result) {
return new DiscoveryKeyUtils.PreviouslyNotableGroupKey(result);
}
@Override
public void addAttributeToResults(List<Result> results, SleuthkitCase caseDb,
CentralRepository centralRepoDb) throws DiscoveryException {
if (centralRepoDb != null) {
processFilesWithCr(results, centralRepoDb);
}
}
}
private void processFilesWithCr(List<Result> results, CentralRepository centralRepo) throws DiscoveryException {
List<ResultDomain> domainsBatch = new ArrayList<>();
for (Result result : results) {
if (result.getType() == SearchData.Type.DOMAIN) {
@ -341,15 +343,15 @@ public class DiscoveryAttributes {
}
}
}
queryPreviouslyNotable(domainsBatch, centralRepo);
}
private void queryPreviouslyNotable(List<ResultDomain> domainsBatch, CentralRepository centralRepo) throws DiscoveryException {
if (domainsBatch.isEmpty()) {
return;
}
try {
final CorrelationAttributeInstance.Type attributeType = centralRepo.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID);
final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsBatch, attributeType);
@ -371,16 +373,16 @@ public class DiscoveryAttributes {
throw new DiscoveryException("Fatal exception encountered querying the CR.", ex);
}
}
private static class DomainPreviouslyNotableCallback implements InstanceTableCallback {
private final Map<String, List<ResultDomain>> domainLookup;
private SQLException sqlCause;
private DomainPreviouslyNotableCallback(Map<String, List<ResultDomain>> domainLookup) {
this.domainLookup = domainLookup;
}
@Override
public void process(ResultSet resultSet) {
try {
@ -401,7 +403,7 @@ public class DiscoveryAttributes {
*/
SQLException getCause() {
return this.sqlCause;
}
}
}
}
@ -499,12 +501,13 @@ public class DiscoveryAttributes {
final CorrelationAttributeInstance.Type attributeType = centralRepository.getCorrelationTypeById(CorrelationAttributeInstance.DOMAIN_TYPE_ID);
final Map<String, List<ResultDomain>> resultDomainTable = organizeByValue(domainsToQuery, attributeType);
final String values = createCSV(resultDomainTable.keySet());
final String tableName = CentralRepoDbUtil.correlationTypeToInstanceTableName(attributeType);
final String domainFrequencyQuery = " value AS domain_name, COUNT(*) AS frequency "
+ "FROM " + tableName + " "
+ "WHERE value IN (" + values + ") "
+ "GROUP BY value";
final String domainFrequencyQuery = " value AS domain_name, COUNT(value) AS frequency FROM"
+ "(SELECT DISTINCT case_id, value FROM "
+ tableName
+ " WHERE value IN ("
+ values
+ ")) AS foo GROUP BY value";
final DomainFrequencyCallback frequencyCallback = new DomainFrequencyCallback(resultDomainTable);
centralRepository.processSelectClause(domainFrequencyQuery, frequencyCallback);
@ -784,8 +787,8 @@ public class DiscoveryAttributes {
}
/**
* Attribute for grouping/sorting domains by number of page views.
* Page views is defined at the number of TSK_WEB_HISTORY artifacts.
* Attribute for grouping/sorting domains by number of page views. Page
* views is defined at the number of TSK_WEB_HISTORY artifacts.
*/
static class PageViewsAttribute extends AttributeType {
@ -1074,4 +1077,4 @@ public class DiscoveryAttributes {
private DiscoveryAttributes() {
// Class should not be instantiated
}
}
}

View File

@ -277,17 +277,20 @@ public final class DiscoveryEventUtils {
public static final class MiniTimelineResultEvent {
private final List<MiniTimelineResult> results = new ArrayList<>();
private final String domain;
/**
* Construct a new MiniTimelineResultEvent.
*
* @param results The list of MiniTimelineResults contained in this
* event.
* @param domain The domain the results are for.
*/
public MiniTimelineResultEvent(List<MiniTimelineResult> results) {
public MiniTimelineResultEvent(List<MiniTimelineResult> results, String domain) {
if (results != null) {
this.results.addAll(results);
}
this.domain = domain;
}
/**
@ -298,6 +301,15 @@ public final class DiscoveryEventUtils {
public List<MiniTimelineResult> getResultList() {
return Collections.unmodifiableList(results);
}
/**
* Get the domain this list of results is for.
*
* @return The domain the list of results is for.
*/
public String getDomain() {
return domain;
}
}
/**

View File

@ -201,7 +201,7 @@ public class DomainSearch {
* @throws DiscoveryException if unable to get the artifacts or the date
* attributes from an artifact.
*/
public List<MiniTimelineResult> getAllArtifactsForDomain(SleuthkitCase sleuthkitCase, String domain) throws DiscoveryException, InterruptedException {
public List<MiniTimelineResult> getAllArtifactsForDomain(SleuthkitCase sleuthkitCase, String domain) throws DiscoveryException {
List<BlackboardArtifact> artifacts = new ArrayList<>();
Map<String, List<BlackboardArtifact>> dateMap = new HashMap<>();
if (!StringUtils.isBlank(domain)) {

View File

@ -63,7 +63,7 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
// Grouping by CR Frequency, for example, will require further processing
// in order to make the correct decision. The attribute types that require
// more information implement their logic by overriding `addAttributeToResults`.
List<AttributeType> searchAttributes = new ArrayList<>();
Set<AttributeType> searchAttributes = new HashSet<>();
searchAttributes.add(key.getGroupAttributeType());
searchAttributes.addAll(key.getFileSortingMethod().getRequiredAttributes());
for (AttributeType attr : searchAttributes) {
@ -304,7 +304,7 @@ class DomainSearchCacheLoader extends CacheLoader<SearchKey, Map<GroupKey, List<
Content dataSource = skc.getContentById(dataSourceID);
resultDomains.add(new ResultDomain(domain, activityStart,
activityEnd, totalPageViews, pageViewsInLast60, filesDownloaded,
activityEnd, totalPageViews, pageViewsInLast60, filesDownloaded,
countOfKnownAccountTypes, dataSource));
}
} catch (SQLException ex) {

View File

@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.discovery.search;
import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**

View File

@ -257,60 +257,60 @@ public class ResultsSorter implements Comparator<Result> {
return compareStrings(first.getDomain().toLowerCase(), second.getDomain().toLowerCase());
};
}
/**
* Sorts domains by page view count.
*
*
* This comparator sorts results in descending order (largest -> smallest).
*/
private static Comparator<Result> getPageViewComparator() {
return (Result domain1, Result domain2) -> {
if (domain1.getType() != SearchData.Type.DOMAIN ||
domain2.getType() != SearchData.Type.DOMAIN) {
if (domain1.getType() != SearchData.Type.DOMAIN
|| domain2.getType() != SearchData.Type.DOMAIN) {
return 0;
}
ResultDomain first = (ResultDomain) domain1;
ResultDomain second = (ResultDomain) domain2;
long firstPageViews = first.getTotalPageViews();
long secondPageViews = second.getTotalPageViews();
return Long.compare(secondPageViews, firstPageViews);
};
}
/**
* Sorts result domains by last activity date time. The results will be in
* Sorts result domains by last activity date time. The results will be in
* descending order.
*/
private static Comparator<Result> getLastActivityDateTimeComparator() {
return (Result domain1, Result domain2) -> {
if (domain1.getType() != SearchData.Type.DOMAIN ||
domain2.getType() != SearchData.Type.DOMAIN) {
if (domain1.getType() != SearchData.Type.DOMAIN
|| domain2.getType() != SearchData.Type.DOMAIN) {
return 0;
}
ResultDomain first = (ResultDomain) domain1;
ResultDomain second = (ResultDomain) domain2;
long firstActivityEnd = first.getActivityEnd();
long secondActivityEnd = second.getActivityEnd();
return Long.compare(secondActivityEnd, firstActivityEnd);
};
}
/**
* Sorts result domains by most file downloads. The results will be in
* Sorts result domains by most file downloads. The results will be in
* descending order.
*/
private static Comparator<Result> getWebDownloadsComparator() {
return (Result domain1, Result domain2) -> {
if (domain1.getType() != SearchData.Type.DOMAIN ||
domain2.getType() != SearchData.Type.DOMAIN) {
if (domain1.getType() != SearchData.Type.DOMAIN
|| domain2.getType() != SearchData.Type.DOMAIN) {
return 0;
}
ResultDomain first = (ResultDomain) domain1;
ResultDomain second = (ResultDomain) domain2;
long firstFilesDownloaded = first.getFilesDownloaded();
long secondFilesDownloaded = second.getFilesDownloaded();
return Long.compare(secondFilesDownloaded, firstFilesDownloaded);
@ -388,10 +388,10 @@ public class ResultsSorter implements Comparator<Result> {
Bundle.FileSorter_SortingMethod_keywordlist_displayName()), // Sort alphabetically by list of keyword list names found
BY_FULL_PATH(new ArrayList<>(),
Bundle.FileSorter_SortingMethod_fullPath_displayName()), // Sort alphabetically by path
BY_DOMAIN_NAME(new ArrayList<>(),Bundle.FileSorter_SortingMethod_domain_displayName()),
BY_PAGE_VIEWS(new ArrayList<>(), Bundle.FileSorter_SortingMethod_pageViews_displayName()),
BY_DOWNLOADS(new ArrayList<>(), Bundle.FileSorter_SortingMethod_downloads_displayName()),
BY_LAST_ACTIVITY(new ArrayList<>(), Bundle.FileSorter_SortingMethod_activity_displayName());
BY_DOMAIN_NAME(Arrays.asList(new DiscoveryAttributes.DomainCategoryAttribute()), Bundle.FileSorter_SortingMethod_domain_displayName()),
BY_PAGE_VIEWS(Arrays.asList(new DiscoveryAttributes.DomainCategoryAttribute()), Bundle.FileSorter_SortingMethod_pageViews_displayName()),
BY_DOWNLOADS(Arrays.asList(new DiscoveryAttributes.DomainCategoryAttribute()), Bundle.FileSorter_SortingMethod_downloads_displayName()),
BY_LAST_ACTIVITY(Arrays.asList(new DiscoveryAttributes.DomainCategoryAttribute()), Bundle.FileSorter_SortingMethod_activity_displayName());
private final String displayName;
private final List<DiscoveryAttributes.AttributeType> requiredAttributes;

View File

@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.discovery.ui;
import java.awt.Component;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.datamodel.BlackboardArtifact;
@ -30,14 +29,13 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
*/
public abstract class AbstractArtifactDetailsPanel extends JPanel {
private static final long serialVersionUID = 1L;
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
public Component getComponent() {
// Slap a vertical scrollbar on the panel.
return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
return this;
}
private static final long serialVersionUID = 1L;
/**
* Called to display the contents of the given artifact.
*

View File

@ -4,7 +4,7 @@
<Properties>
<Property name="opaque" type="boolean" value="false"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 0]"/>
<Dimension value="[350, 10]"/>
</Property>
</Properties>
<AuxValues>
@ -22,12 +22,12 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" pref="400" max="32767" attributes="0"/>
<Component id="jScrollPane1" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" alignment="0" pref="0" max="32767" attributes="0"/>
<Component id="jScrollPane1" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
@ -37,9 +37,13 @@
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[350, 10]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>

View File

@ -25,6 +25,7 @@ import java.util.logging.Level;
import javax.swing.JPopupMenu;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle;
@ -32,6 +33,7 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.guiutils.SimpleTableCellRenderer;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TimeUtilities;
@ -56,6 +58,11 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
ArtifactsListPanel(BlackboardArtifact.ARTIFACT_TYPE artifactType) {
tableModel = new DomainArtifactTableModel(artifactType);
initComponents();
// add the cell renderer to all columns
TableCellRenderer renderer = new SimpleTableCellRenderer();
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
}
artifactsTable.getRowSorter().toggleSortOrder(0);
artifactsTable.getRowSorter().toggleSortOrder(0);
}
@ -94,11 +101,15 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
@Override
BlackboardArtifact getSelectedArtifact() {
int selectedIndex = artifactsTable.getSelectionModel().getLeadSelectionIndex();
if (selectedIndex < artifactsTable.getSelectionModel().getMinSelectionIndex() || artifactsTable.getSelectionModel().getMaxSelectionIndex() < 0 || selectedIndex > artifactsTable.getSelectionModel().getMaxSelectionIndex()) {
if (artifactsTable.getModel() instanceof DomainArtifactTableModel) {
int selectedIndex = artifactsTable.getSelectionModel().getLeadSelectionIndex();
if (selectedIndex < artifactsTable.getSelectionModel().getMinSelectionIndex() || artifactsTable.getSelectionModel().getMaxSelectionIndex() < 0 || selectedIndex > artifactsTable.getSelectionModel().getMaxSelectionIndex()) {
return null;
}
return tableModel.getArtifactByRow(artifactsTable.convertRowIndexToModel(selectedIndex));
} else {
return null;
}
return tableModel.getArtifactByRow(artifactsTable.convertRowIndexToModel(selectedIndex));
}
@Override
@ -124,7 +135,12 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
void addArtifacts(List<BlackboardArtifact> artifactList) {
tableModel.setContents(artifactList);
if (!artifactList.isEmpty()) {
artifactsTable.setModel(tableModel);
tableModel.setContents(artifactList);
} else {
artifactsTable.setModel(new EmptyTableModel());
}
artifactsTable.validate();
artifactsTable.repaint();
tableModel.fireTableDataChanged();
@ -152,10 +168,12 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
artifactsTable = new javax.swing.JTable();
setOpaque(false);
setPreferredSize(new java.awt.Dimension(300, 0));
setPreferredSize(new java.awt.Dimension(350, 10));
jScrollPane1.setBorder(null);
jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0));
jScrollPane1.setPreferredSize(new java.awt.Dimension(350, 10));
artifactsTable.setAutoCreateRowSorter(true);
artifactsTable.setModel(tableModel);
@ -166,11 +184,11 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 0, Short.MAX_VALUE)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
@ -357,6 +375,46 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel {
}
}
}
/**
* Table model which displays only that no results were found.
*/
private class EmptyTableModel extends AbstractTableModel {
private static final long serialVersionUID = 1L;
@Override
public int getRowCount() {
return 1;
}
@Override
public int getColumnCount() {
return 1;
}
@NbBundle.Messages({"ArtifactsListPanel.noResultsFound.text=No results found"})
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return Bundle.ArtifactsListPanel_noResultsFound_text();
}
@Override
public String getColumnName(int column) {
switch (column) {
case 0:
return Bundle.ArtifactsListPanel_dateColumn_name();
case 1:
return Bundle.ArtifactsListPanel_titleColumn_name();
case 2:
return Bundle.ArtifactsListPanel_mimeTypeColumn_name();
default:
return "";
}
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTable artifactsTable;
// End of variables declaration//GEN-END:variables

View File

@ -61,7 +61,8 @@ class ArtifactsWorker extends SwingWorker<List<BlackboardArtifact>, Void> {
return domainSearch.getArtifacts(new DomainSearchArtifactsRequest(Case.getCurrentCase().getSleuthkitCase(), domain, artifactType));
} catch (DiscoveryException ex) {
if (ex.getCause() instanceof InterruptedException) {
logger.log(Level.INFO, "MiniTimeline search was cancelled or interrupted for domain: {0}", domain);
this.cancel(true);
//ignore the exception as it was cancelled while the cache was performing its get and we support cancellation
} else {
throw ex;
}
@ -84,6 +85,6 @@ class ArtifactsWorker extends SwingWorker<List<BlackboardArtifact>, Void> {
//Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging
}
}
}
}

View File

@ -53,12 +53,11 @@ DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which
ObjectDetectedFilterPanel.text=Object Detected:
DateFilterPanel.mostRecentRadioButton.text=Only last:
DateFilterPanel.dateFilterCheckBox.text=Date Filter:
DomainSummaryPanel.activityLabel.text=
DomainSummaryPanel.pagesLabel.text=
DomainSummaryPanel.filesDownloadedLabel.text=
DomainSummaryPanel.totalVisitsLabel.text=
FileDetailsPanel.instancesList.border.title=Instances
CookieDetailsPanel.jLabel1.text=Artifact:
CookieDetailsPanel.jLabel2.text=
PreviouslyNotableFilterPanel.text_1=Include only previously notable domains
KnownAccountTypeFilterPanel.text_1=Include only domains with a known account type
KnownAccountTypeFilterPanel.text_1=Include only domains with a known account type
LoadingPanel.detailsLabel.AccessibleContext.accessibleName=detailsLabel

View File

@ -3,6 +3,7 @@ ArtifactMenuMouseAdapter_label=Extract Files
ArtifactsListPanel.dateColumn.name=Date/Time
ArtifactsListPanel.fileNameColumn.name=Name
ArtifactsListPanel.mimeTypeColumn.name=MIME Type
ArtifactsListPanel.noResultsFound.text=No results found
ArtifactsListPanel.termColumn.name=Term
ArtifactsListPanel.titleColumn.name=Title
ArtifactsListPanel.urlColumn.name=URL
@ -52,14 +53,20 @@ DocumentPanel.numberOfImages.noImages=No images
# {0} - numberOfImages
DocumentPanel.numberOfImages.text=1 of {0} images
DocumentWrapper.previewInitialValue=Preview not generated yet.
DomainDetailsPanel.miniTimelineTitle.text=Mini Timeline
DomainDetailsPanel.miniTimelineTitle.text=Timeline
# {0} - startDate
# {1} - endDate
DomainSummaryPanel.activity.text=Activity: {0} to {1}
DomainSummaryPanel.category.text=Category:
DomainSummaryPanel.downloads.text=Files downloaded:
DomainSummaryPanel.known.text=User role: Known account type(s)
DomainSummaryPanel.loadingImages.text=Loading thumbnail...
DomainSummaryPanel.no.text=No
DomainSummaryPanel.notability.text=Previously tagged as notable:
DomainSummaryPanel.pages.text=Page views in past 60 days:
DomainSummaryPanel.totalPages.text=Total page views:
DomainSummaryPanel.unknown.text=User role: Unknown
DomainSummaryPanel.yes.text=Yes
GroupsListPanel.noDomainResults.message.text=No domains were found for the selected filters.\n\nReminder:\n -The Recent Activity module must be run on each data source you want to find results in.\n -The Central Repository module must be run on each data source if you want to filter or sort by past occurrences.\n -The iOS Analyzer (iLEAPP) module must be run on each data source which contains data from an iOS device.\n
GroupsListPanel.noFileResults.message.text=No files were found for the selected filters.\n\nReminder:\n -The File Type Identification module must be run on each data source you want to find results in.\n -The Hash Lookup module must be run on each data source if you want to filter by past occurrence.\n -The Picture Analyzer module must be run on each data source if you are filtering by User Created content.
GroupsListPanel.noResults.title.text=No results found
@ -68,12 +75,16 @@ ImageThumbnailPanel.isDeleted.text=All instances of file are deleted.
# {0} - otherInstanceCount
ImageThumbnailPanel.nameLabel.more.text=\ and {0} more
InterestingItemsFilterPanel.error.text=At least one interesting file set name must be selected.
LoadingPanel.loading.text=Loading, please wait.
# {0} - resultInfo
LoadingPanel.retrieving.text=Retrieving results for {0}.
MiniTimelineArtifactListPanel.descriptionColumn.name=\ Description
MiniTimelineArtifactListPanel.typeColumn.name=Result Type
MiniTimelineArtifactListPanel.value.noValue=No value available.
MiniTimelineDateListPanel.countColumn.name=Count
MiniTimelineDateListPanel.dateColumn.name=Date
MiniTimelineDateListPanel.value.noValue=No value available.
MiniTimelinePanel.loadingPanel.details=the Timeline view
MonthAbbreviation.aprilAbbrev=Apr
MonthAbbreviation.augustAbbrev=Aug
MonthAbbreviation.decemberAbbrev=Dec
@ -156,15 +167,14 @@ DocumentFilterPanel.documentsFiltersSplitPane.border.title=Step 2: Filter which
ObjectDetectedFilterPanel.text=Object Detected:
DateFilterPanel.mostRecentRadioButton.text=Only last:
DateFilterPanel.dateFilterCheckBox.text=Date Filter:
DomainSummaryPanel.activityLabel.text=
DomainSummaryPanel.pagesLabel.text=
DomainSummaryPanel.filesDownloadedLabel.text=
DomainSummaryPanel.totalVisitsLabel.text=
FileDetailsPanel.instancesList.border.title=Instances
CookieDetailsPanel.jLabel1.text=Artifact:
CookieDetailsPanel.jLabel2.text=
PreviouslyNotableFilterPanel.text_1=Include only previously notable domains
KnownAccountTypeFilterPanel.text_1=Include only domains with a known account type
LoadingPanel.detailsLabel.AccessibleContext.accessibleName=detailsLabel
VideoThumbnailPanel.bytes.text=bytes
VideoThumbnailPanel.deleted.text=All instances of file are deleted.
VideoThumbnailPanel.gigaBytes.text=GB

View File

@ -3,7 +3,7 @@
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[500, 0]"/>
<Dimension value="[300, 0]"/>
</Property>
</Properties>
<AuxValues>

View File

@ -50,7 +50,7 @@ final class ContentViewerDetailsPanel extends AbstractArtifactDetailsPanel {
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
setPreferredSize(new java.awt.Dimension(500, 0));
setPreferredSize(new java.awt.Dimension(300, 0));
setLayout(new java.awt.BorderLayout());
}// </editor-fold>//GEN-END:initComponents
@ -59,7 +59,9 @@ final class ContentViewerDetailsPanel extends AbstractArtifactDetailsPanel {
public void setArtifact(BlackboardArtifact artifact) {
Node node = Node.EMPTY;
if (artifact != null) {
node = new BlackboardArtifactNode(artifact);
boolean useAssociatedFile = artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD.getTypeID()
|| artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_CACHE.getTypeID();
node = new BlackboardArtifactNode(artifact, useAssociatedFile);
}
contentViewer.setNode(node);
}

View File

@ -60,9 +60,11 @@ public final class DiscoveryTopComponent extends TopComponent {
private volatile static int previousDividerLocation = 250;
private final GroupListPanel groupListPanel;
private final ResultsPanel resultsPanel;
private JPanel detailsPanel = new JPanel();
private String selectedDomainTabName;
private Type searchType;
private int dividerLocation = JSplitPane.UNDEFINED_CONDITION;
private SwingAnimator animator = null;
/**
@ -83,25 +85,25 @@ public final class DiscoveryTopComponent extends TopComponent {
}
});
rightSplitPane.setTopComponent(resultsPanel);
resetBottomComponent();
rightSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)) {
if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)
&& ((animator == null || !animator.isRunning())
&& evt.getNewValue() instanceof Integer
&& evt.getOldValue() instanceof Integer
&& ((int) evt.getNewValue() + 5) < (rightSplitPane.getHeight() - rightSplitPane.getDividerSize())
&& (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue())
&& ((int) evt.getOldValue() != JSplitPane.UNDEFINED_CONDITION))) {
//Only change the saved location when it was a manual change by the user and not the animation or the window opening initially
if ((animator == null || !animator.isRunning())
&& evt.getNewValue() instanceof Integer
&& evt.getOldValue() instanceof Integer
&& ((int) evt.getNewValue() + 5) < (rightSplitPane.getHeight() - rightSplitPane.getDividerSize())
&& (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue())
&& ((int) evt.getOldValue() != JSplitPane.UNDEFINED_CONDITION)) {
previousDividerLocation = (int) evt.getNewValue();
}
previousDividerLocation = (int) evt.getNewValue();
}
}
});
rightSplitPane.setTopComponent(resultsPanel);
rightSplitPane.setBottomComponent(new JPanel());
}
);
}
/**
@ -131,7 +133,8 @@ public final class DiscoveryTopComponent extends TopComponent {
* @return The open DiscoveryTopComponent or null if it has not been opened.
*/
public static DiscoveryTopComponent getTopComponent() {
return (DiscoveryTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID);
DiscoveryTopComponent discoveryTopComp = (DiscoveryTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID);
return discoveryTopComp;
}
/**
@ -153,6 +156,11 @@ public final class DiscoveryTopComponent extends TopComponent {
DiscoveryEventUtils.getDiscoveryEventBus().register(groupListPanel);
}
private void resetBottomComponent() {
rightSplitPane.setBottomComponent(new JPanel());
rightSplitPane.setDividerLocation(JSplitPane.UNDEFINED_CONDITION);
}
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
@Override
protected void componentClosed() {
@ -162,11 +170,12 @@ public final class DiscoveryTopComponent extends TopComponent {
DiscoveryEventUtils.getDiscoveryEventBus().unregister(this);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(groupListPanel);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(resultsPanel);
DiscoveryEventUtils.getDiscoveryEventBus().unregister(rightSplitPane.getBottomComponent());
if (rightSplitPane.getBottomComponent() instanceof DomainDetailsPanel) {
selectedDomainTabName = ((DomainDetailsPanel) rightSplitPane.getBottomComponent()).getSelectedTabName();
DiscoveryEventUtils.getDiscoveryEventBus().unregister(detailsPanel);
if (detailsPanel instanceof DomainDetailsPanel) {
((DomainDetailsPanel) detailsPanel).unregister();
selectedDomainTabName = ((DomainDetailsPanel) detailsPanel).getSelectedTabName();
}
rightSplitPane.setDividerLocation(JSplitPane.UNDEFINED_CONDITION);
resetBottomComponent();
super.componentClosed();
}
@ -336,12 +345,13 @@ public final class DiscoveryTopComponent extends TopComponent {
}
selectedDomainTabName = validateLastSelectedType(searchCompleteEvent);
DomainDetailsPanel domainDetailsPanel = new DomainDetailsPanel();
rightSplitPane.setBottomComponent(domainDetailsPanel);
domainDetailsPanel.configureArtifactTabs(selectedDomainTabName);
detailsPanel = domainDetailsPanel;
} else {
rightSplitPane.setBottomComponent(new FileDetailsPanel());
detailsPanel = new FileDetailsPanel();
}
DiscoveryEventUtils.getDiscoveryEventBus().register(rightSplitPane.getBottomComponent());
rightSplitPane.setBottomComponent(detailsPanel);
DiscoveryEventUtils.getDiscoveryEventBus().register(detailsPanel);
descriptionText += searchCompleteEvent.getFilters().stream().map(AbstractFilter::getDesc).collect(Collectors.joining("; "));
progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText));
progressMessageTextArea.setCaretPosition(0);

View File

@ -1,11 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
<Properties>
<Property name="dividerLocation" type="int" value="350"/>
<Property name="resizeWeight" type="double" value="0.1"/>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
</Container>
</NonVisualComponents>
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
<Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
<Color id="Default Cursor"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
@ -23,23 +33,4 @@
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="jSplitPane1">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
</Container>
</SubComponents>
</Form>

View File

@ -22,7 +22,6 @@ import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifa
import com.google.common.eventbus.Subscribe;
import java.util.logging.Level;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
@ -64,7 +63,8 @@ final class DomainArtifactsTabPanel extends JPanel {
this.artifactType = type;
listPanel = new ArtifactsListPanel(artifactType);
listPanel.addMouseListener(new ArtifactMenuMouseAdapter(listPanel));
jSplitPane1.setLeftComponent(listPanel);
mainSplitPane.setLeftComponent(listPanel);
add(mainSplitPane);
setRightComponent();
listPanel.addSelectionListener(listener);
}
@ -91,7 +91,7 @@ final class DomainArtifactsTabPanel extends JPanel {
break;
}
if (rightPanel != null) {
jSplitPane1.setRightComponent(rightPanel.getComponent());
mainSplitPane.setRightComponent(rightPanel.getComponent());
}
}
@ -115,10 +115,14 @@ final class DomainArtifactsTabPanel extends JPanel {
this.status = status;
if (status == ArtifactRetrievalStatus.UNPOPULATED) {
listPanel.clearList();
if (rightPanel != null){
removeAll();
add(mainSplitPane);
if (rightPanel != null) {
rightPanel.setArtifact(null);
}
} else if (status == ArtifactRetrievalStatus.POPULATING) {
removeAll();
add(new LoadingPanel(artifactType.getDisplayName()));
}
}
@ -130,7 +134,7 @@ final class DomainArtifactsTabPanel extends JPanel {
*/
@Subscribe
void handleArtifactSearchResultEvent(DiscoveryEventUtils.ArtifactSearchResultEvent artifactresultEvent) {
if (artifactType == artifactresultEvent.getArtifactType()) {
if (artifactType == artifactresultEvent.getArtifactType() && status == ArtifactRetrievalStatus.POPULATING) {
SwingUtilities.invokeLater(() -> {
listPanel.removeSelectionListener(listener);
listPanel.addArtifacts(artifactresultEvent.getListOfArtifacts());
@ -138,6 +142,8 @@ final class DomainArtifactsTabPanel extends JPanel {
setEnabled(!listPanel.isEmpty());
listPanel.addSelectionListener(listener);
listPanel.selectFirst();
removeAll();
add(mainSplitPane);
revalidate();
repaint();
try {
@ -169,20 +175,19 @@ final class DomainArtifactsTabPanel extends JPanel {
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jSplitPane1 = new javax.swing.JSplitPane();
mainSplitPane = new javax.swing.JSplitPane();
mainSplitPane.setDividerLocation(350);
mainSplitPane.setResizeWeight(0.1);
setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
setMinimumSize(new java.awt.Dimension(0, 0));
setPreferredSize(new java.awt.Dimension(0, 0));
setLayout(new java.awt.BorderLayout());
jSplitPane1.setMinimumSize(new java.awt.Dimension(0, 0));
jSplitPane1.setPreferredSize(new java.awt.Dimension(0, 0));
add(jSplitPane1, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JSplitPane jSplitPane1;
private javax.swing.JSplitPane mainSplitPane;
// End of variables declaration//GEN-END:variables
/**

View File

@ -39,7 +39,6 @@ final class DomainDetailsPanel extends JPanel {
private static final long serialVersionUID = 1L;
private ArtifactsWorker singleArtifactDomainWorker;
private MiniTimelineWorker miniTimelineWorker;
private String domain;
private String selectedTabName = null;
@ -51,7 +50,9 @@ final class DomainDetailsPanel extends JPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
DomainDetailsPanel() {
initComponents();
jTabbedPane1.add(Bundle.DomainDetailsPanel_miniTimelineTitle_text(), new MiniTimelinePanel());
MiniTimelinePanel timelinePanel = new MiniTimelinePanel();
DiscoveryEventUtils.getDiscoveryEventBus().register(timelinePanel);
jTabbedPane1.add(Bundle.DomainDetailsPanel_miniTimelineTitle_text(), timelinePanel);
for (BlackboardArtifact.ARTIFACT_TYPE type : SearchData.Type.DOMAIN.getArtifactTypes()) {
jTabbedPane1.add(type.getDisplayName(), new DomainArtifactsTabPanel(type));
}
@ -63,7 +64,7 @@ final class DomainDetailsPanel extends JPanel {
*
* @param tabName The name of the tab to select initially.
*/
@NbBundle.Messages({"DomainDetailsPanel.miniTimelineTitle.text=Mini Timeline"})
@NbBundle.Messages({"DomainDetailsPanel.miniTimelineTitle.text=Timeline"})
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void configureArtifactTabs(String tabName) {
selectedTabName = tabName;
@ -104,6 +105,20 @@ final class DomainDetailsPanel extends JPanel {
}
}
/**
* Get the status of the currently selected tab.
*
* @return The loading status of the currently selected tab.
*/
DomainArtifactsTabPanel.ArtifactRetrievalStatus getCurrentTabStatus() {
if (jTabbedPane1.getSelectedComponent() instanceof MiniTimelinePanel) {
return ((MiniTimelinePanel) jTabbedPane1.getSelectedComponent()).getStatus();
} else if (jTabbedPane1.getSelectedComponent() instanceof DomainArtifactsTabPanel) {
return ((DomainArtifactsTabPanel) jTabbedPane1.getSelectedComponent()).getStatus();
}
return null;
}
/**
* Run the worker which retrieves the list of artifacts for the domain to
* populate the details area.
@ -127,14 +142,9 @@ final class DomainDetailsPanel extends JPanel {
* mini timeline view to populate.
*/
private void runMiniTimelineWorker(MiniTimelinePanel miniTimelinePanel) {
if (miniTimelineWorker != null && !miniTimelineWorker.isDone()) {
miniTimelineWorker.cancel(true);
}
if (miniTimelinePanel.getStatus() == DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED) {
DiscoveryEventUtils.getDiscoveryEventBus().register(miniTimelinePanel);
miniTimelinePanel.setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATING);
miniTimelineWorker = new MiniTimelineWorker(domain);
miniTimelineWorker.execute();
miniTimelinePanel.setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATING, domain);
new MiniTimelineWorker(domain).execute();
}
}
@ -146,20 +156,19 @@ final class DomainDetailsPanel extends JPanel {
*/
@Subscribe
void handlePopulateDomainTabsEvent(DiscoveryEventUtils.PopulateDomainTabsEvent populateEvent) {
domain = populateEvent.getDomain();
SwingUtilities.invokeLater(() -> {
resetTabsStatus();
selectTab();
Component selectedComponent = jTabbedPane1.getSelectedComponent();
if (selectedComponent instanceof DomainArtifactsTabPanel) {
runDomainWorker((DomainArtifactsTabPanel) selectedComponent);
} else if (selectedComponent instanceof MiniTimelinePanel) {
runMiniTimelineWorker((MiniTimelinePanel) selectedComponent);
}
if (StringUtils.isBlank(domain)) {
if (StringUtils.isBlank(populateEvent.getDomain())) {
resetTabsStatus();
//send fade out event
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(false));
} else {
domain = populateEvent.getDomain();
Component selectedComponent = jTabbedPane1.getSelectedComponent();
if (selectedComponent instanceof DomainArtifactsTabPanel) {
runDomainWorker((DomainArtifactsTabPanel) selectedComponent);
} else if (selectedComponent instanceof MiniTimelinePanel) {
runMiniTimelineWorker((MiniTimelinePanel) selectedComponent);
}
//send fade in event
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(true));
}
@ -176,7 +185,7 @@ final class DomainDetailsPanel extends JPanel {
if (comp instanceof DomainArtifactsTabPanel) {
((DomainArtifactsTabPanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED);
} else if (comp instanceof MiniTimelinePanel) {
((MiniTimelinePanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED);
((MiniTimelinePanel) comp).setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED, domain);
}
}
}
@ -215,4 +224,15 @@ final class DomainDetailsPanel extends JPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTabbedPane jTabbedPane1;
// End of variables declaration//GEN-END:variables
/*
* Unregister the MiniTimelinePanel from the event bus.
*/
void unregister() {
for (Component comp : jTabbedPane1.getComponents()) {
if (comp instanceof MiniTimelinePanel) {
DiscoveryEventUtils.getDiscoveryEventBus().unregister(comp);
}
}
}
}

View File

@ -24,18 +24,25 @@
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="domainNameLabel" alignment="0" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Component id="domainNameLabel" pref="539" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="47" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" max="-2" attributes="0">
<Component id="filesDownloadedLabel" linkSize="3" alignment="0" pref="300" max="32767" attributes="0"/>
<Component id="activityLabel" linkSize="3" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="domainNotabilityLabel" pref="300" max="32767" attributes="0"/>
<Component id="categoryLabel" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="4" max="-2" attributes="0"/>
</Group>
<Component id="activityLabel" alignment="0" max="32767" attributes="0"/>
<Component id="pagesLabel" max="32767" attributes="0"/>
<Component id="filesDownloadedLabel" alignment="0" max="32767" attributes="0"/>
<Component id="totalVisitsLabel" max="32767" attributes="0"/>
<Component id="pagesLabel" alignment="0" max="32767" attributes="0"/>
<Component id="totalVisitsLabel" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="numberOfImagesLabel" max="32767" attributes="0"/>
<Component id="sampleImageLabel" max="32767" attributes="0"/>
@ -51,20 +58,26 @@
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="numberOfImagesLabel" min="-2" pref="17" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<EmptySpace pref="8" max="32767" attributes="0"/>
<Component id="sampleImageLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="domainNameLabel" min="-2" pref="32" max="-2" attributes="0"/>
<Component id="domainNameLabel" min="-2" pref="35" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="32767" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="activityLabel" linkSize="4" alignment="1" min="-2" pref="15" max="-2" attributes="0"/>
<Component id="domainNotabilityLabel" linkSize="2" alignment="1" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="activityLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="11" max="-2" attributes="0"/>
<Component id="totalVisitsLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="11" max="-2" attributes="0"/>
<Component id="pagesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="filesDownloadedLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="filesDownloadedLabel" linkSize="4" pref="14" max="32767" attributes="0"/>
<Component id="categoryLabel" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="totalVisitsLabel" linkSize="2" pref="14" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pagesLabel" min="-2" pref="15" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="1" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
@ -97,11 +110,6 @@
<Component class="javax.swing.JLabel" name="numberOfImagesLabel">
</Component>
<Component class="javax.swing.JLabel" name="activityLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DomainSummaryPanel.activityLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="pagesLabel">
<Properties>
@ -118,11 +126,10 @@
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="totalVisitsLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="DomainSummaryPanel.totalVisitsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="domainNotabilityLabel">
</Component>
<Component class="javax.swing.JLabel" name="categoryLabel">
</Component>
</SubComponents>
</Form>

View File

@ -32,6 +32,7 @@ import javax.swing.ListCellRenderer;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.discovery.search.SearchData;
/**
* Class which displays a preview and details about a domain.
@ -66,6 +67,8 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
pagesLabel = new javax.swing.JLabel();
filesDownloadedLabel = new javax.swing.JLabel();
totalVisitsLabel = new javax.swing.JLabel();
domainNotabilityLabel = new javax.swing.JLabel();
categoryLabel = new javax.swing.JLabel();
setBorder(javax.swing.BorderFactory.createEtchedBorder());
@ -75,14 +78,10 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
sampleImageLabel.setMinimumSize(new java.awt.Dimension(100, 100));
sampleImageLabel.setPreferredSize(new java.awt.Dimension(100, 100));
org.openide.awt.Mnemonics.setLocalizedText(activityLabel, org.openide.util.NbBundle.getMessage(DomainSummaryPanel.class, "DomainSummaryPanel.activityLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(pagesLabel, org.openide.util.NbBundle.getMessage(DomainSummaryPanel.class, "DomainSummaryPanel.pagesLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(filesDownloadedLabel, org.openide.util.NbBundle.getMessage(DomainSummaryPanel.class, "DomainSummaryPanel.filesDownloadedLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(totalVisitsLabel, org.openide.util.NbBundle.getMessage(DomainSummaryPanel.class, "DomainSummaryPanel.totalVisitsLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
@ -90,12 +89,17 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(domainNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(domainNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 539, Short.MAX_VALUE)
.addGap(47, 47, 47))
.addComponent(activityLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addComponent(filesDownloadedLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
.addComponent(activityLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(domainNotabilityLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
.addComponent(categoryLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(4, 4, 4))
.addComponent(pagesLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(filesDownloadedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(totalVisitsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
@ -103,6 +107,9 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
.addComponent(sampleImageLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
);
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {activityLabel, filesDownloadedLabel});
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
@ -110,27 +117,38 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(numberOfImagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 17, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 8, Short.MAX_VALUE)
.addComponent(sampleImageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addComponent(domainNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(domainNameLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(activityLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(domainNotabilityLabel, javax.swing.GroupLayout.Alignment.TRAILING))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(activityLabel)
.addGap(11, 11, 11)
.addComponent(totalVisitsLabel)
.addGap(11, 11, 11)
.addComponent(pagesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(filesDownloadedLabel)
.addGap(0, 0, Short.MAX_VALUE)))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(filesDownloadedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 14, Short.MAX_VALUE)
.addComponent(categoryLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(totalVisitsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 14, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pagesLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(1, 1, 1)))
.addContainerGap())
);
layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {domainNotabilityLabel, totalVisitsLabel});
layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {activityLabel, filesDownloadedLabel});
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel activityLabel;
private javax.swing.JLabel categoryLabel;
private javax.swing.JLabel domainNameLabel;
private javax.swing.JLabel domainNotabilityLabel;
private javax.swing.JLabel filesDownloadedLabel;
private javax.swing.JLabel numberOfImagesLabel;
private javax.swing.JLabel pagesLabel;
@ -145,13 +163,27 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
"DomainSummaryPanel.pages.text=Page views in past 60 days: ",
"DomainSummaryPanel.totalPages.text=Total page views: ",
"DomainSummaryPanel.downloads.text=Files downloaded: ",
"DomainSummaryPanel.loadingImages.text=Loading thumbnail..."})
"DomainSummaryPanel.notability.text=Previously tagged as notable: ",
"DomainSummaryPanel.unknown.text=User role: Unknown",
"DomainSummaryPanel.known.text=User role: Known account type(s)",
"DomainSummaryPanel.category.text=Category: ",
"DomainSummaryPanel.loadingImages.text=Loading thumbnail...",
"DomainSummaryPanel.no.text=No",
"DomainSummaryPanel.yes.text=Yes"})
@Override
public Component getListCellRendererComponent(JList<? extends DomainWrapper> list, DomainWrapper value, int index, boolean isSelected, boolean cellHasFocus) {
domainNameLabel.setText(value.getResultDomain().getDomain());
TimeZone timeZone = ContentUtils.getTimeZone(value.getResultDomain().getDataSource());
String startDate = formatDate(value.getResultDomain().getActivityStart(), timeZone);
String endDate = formatDate(value.getResultDomain().getActivityEnd(), timeZone);
String notability = Bundle.DomainSummaryPanel_notability_text();
if (value.getResultDomain().getPreviouslyNotableInCR() == SearchData.PreviouslyNotable.PREVIOUSLY_NOTABLE) {
notability += Bundle.DomainSummaryPanel_yes_text();
} else {
notability += Bundle.DomainSummaryPanel_no_text();
}
domainNotabilityLabel.setText(notability);
categoryLabel.setText(Bundle.DomainSummaryPanel_category_text() + value.getResultDomain().getWebCategory());
activityLabel.setText(Bundle.DomainSummaryPanel_activity_text(startDate, endDate));
totalVisitsLabel.setText(Bundle.DomainSummaryPanel_totalPages_text() + value.getResultDomain().getTotalPageViews());
pagesLabel.setText(Bundle.DomainSummaryPanel_pages_text() + value.getResultDomain().getPageViewsInLast60Days());
@ -166,20 +198,21 @@ class DomainSummaryPanel extends javax.swing.JPanel implements ListCellRenderer<
setBackground(isSelected ? SELECTION_COLOR : list.getBackground());
return this;
}
/**
* Formats an epoch time in a given time zone using the following pattern
*
* MMM dd YYYY
*
* The pattern below is formatted manually to reuse the MonthAbbreviation utility.
*
* MMM dd YYYY
*
* The pattern below is formatted manually to reuse the MonthAbbreviation
* utility.
*/
private String formatDate(long epochSeconds, TimeZone timeZone) {
Instant epochSecondsAsInstant = Instant.ofEpochSecond(epochSeconds);
ZonedDateTime dateTime = ZonedDateTime.ofInstant(epochSecondsAsInstant, timeZone.toZoneId());
MonthAbbreviation currentCutOffMonth = MonthAbbreviation.fromMonthValue(dateTime.getMonthValue());
return String.format("%s %02d %04d",
currentCutOffMonth.toString(),
return String.format("%s %02d %04d",
currentCutOffMonth.toString(),
dateTime.getDayOfMonth(), dateTime.getYear());
}

View File

@ -33,6 +33,7 @@
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="domainListModel" type="code"/>
</Property>
<Property name="selectionMode" type="int" value="0"/>
<Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DomainSummaryPanel()" type="code"/>
</Property>

View File

@ -64,6 +64,7 @@ public class DomainSummaryViewer extends javax.swing.JPanel {
setLayout(new java.awt.BorderLayout());
domainList.setModel(domainListModel);
domainList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
domainList.setCellRenderer(new DomainSummaryPanel());
domainScrollPane.setViewportView(domainList);

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="loadingLabel" alignment="1" max="32767" attributes="0"/>
<Component id="detailsLabel" alignment="0" pref="390" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="loadingLabel" min="-2" pref="48" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="detailsLabel" min="-2" pref="33" max="-2" attributes="0"/>
<EmptySpace pref="30" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="loadingLabel">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font component="loadingLabel" property="font" relativeSize="true" size="4"/>
</FontInfo>
</Property>
<Property name="horizontalAlignment" type="int" value="0"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="detailsLabel">
<Properties>
<Property name="horizontalAlignment" type="int" value="0"/>
<Property name="verticalAlignment" type="int" value="1"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="verticalTextPosition" type="int" value="1"/>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/discovery/ui/Bundle.properties" key="LoadingPanel.detailsLabel.AccessibleContext.accessibleName" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,97 @@
/*
* Autopsy
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.discovery.ui;
import javax.swing.JPanel;
import org.apache.commons.lang3.StringUtils;
import org.openide.util.NbBundle;
/**
* Panel to let user know that the information is loading.
*/
class LoadingPanel extends JPanel {
private static final long serialVersionUID = 1L;
/**
* Creates new form LoadingPanel
*/
LoadingPanel() {
this(null);
}
@NbBundle.Messages({"LoadingPanel.loading.text=Loading, please wait.",
"# {0} - resultInfo",
"LoadingPanel.retrieving.text=Retrieving results for {0}."})
LoadingPanel(String details) {
initComponents();
loadingLabel.setText(Bundle.LoadingPanel_loading_text());
if (!StringUtils.isBlank(details)) {
detailsLabel.setText(Bundle.LoadingPanel_retrieving_text(details));
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
loadingLabel = new javax.swing.JLabel();
detailsLabel = new javax.swing.JLabel();
loadingLabel.setFont(loadingLabel.getFont().deriveFont(loadingLabel.getFont().getSize()+4f));
loadingLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
loadingLabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
detailsLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
detailsLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP);
detailsLabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
detailsLabel.setVerticalTextPosition(javax.swing.SwingConstants.TOP);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(loadingLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(detailsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 390, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(loadingLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(detailsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(30, Short.MAX_VALUE))
);
detailsLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(LoadingPanel.class, "LoadingPanel.detailsLabel.AccessibleContext.accessibleName")); // NOI18N
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel detailsLabel;
private javax.swing.JLabel loadingLabel;
// End of variables declaration//GEN-END:variables
}

View File

@ -25,10 +25,12 @@ import java.util.logging.Level;
import javax.swing.JPopupMenu;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.guiutils.SimpleTableCellRenderer;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.BlackboardAttribute.Type;
@ -54,6 +56,11 @@ class MiniTimelineArtifactListPanel extends AbstractArtifactListPanel {
MiniTimelineArtifactListPanel() {
tableModel = new TypeDescriptionTableModel();
initComponents();
// add the cell renderer to all columns
TableCellRenderer renderer = new SimpleTableCellRenderer();
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
artifactsTable.getColumnModel().getColumn(i).setCellRenderer(renderer);
}
artifactsTable.getRowSorter().toggleSortOrder(0);
artifactsTable.getRowSorter().toggleSortOrder(0);
}
@ -138,8 +145,8 @@ class MiniTimelineArtifactListPanel extends AbstractArtifactListPanel {
artifactsTable = new javax.swing.JTable();
setOpaque(false);
setPreferredSize(new java.awt.Dimension(300, 0));
setPreferredSize(new java.awt.Dimension(200, 10));
jScrollPane1.setPreferredSize(new java.awt.Dimension(200, 10));
jScrollPane1.setBorder(null);
jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0));

View File

@ -23,9 +23,11 @@ import java.util.List;
import javax.swing.JPanel;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.MiniTimelineResult;
import org.sleuthkit.autopsy.guiutils.SimpleTableCellRenderer;
import org.sleuthkit.datamodel.BlackboardArtifact;
/**
@ -42,6 +44,11 @@ class MiniTimelineDateListPanel extends JPanel {
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
MiniTimelineDateListPanel() {
initComponents();
// add the cell renderer to all columns
TableCellRenderer renderer = new SimpleTableCellRenderer();
for (int i = 0; i < tableModel.getColumnCount(); ++i) {
jTable1.getColumnModel().getColumn(i).setCellRenderer(renderer);
}
jTable1.getRowSorter().toggleSortOrder(0);
jTable1.getRowSorter().toggleSortOrder(0);
}
@ -139,8 +146,8 @@ class MiniTimelineDateListPanel extends JPanel {
jTable1 = new javax.swing.JTable();
setOpaque(false);
setPreferredSize(new java.awt.Dimension(200, 0));
setPreferredSize(new java.awt.Dimension(200, 10));
jScrollPane1.setPreferredSize(new java.awt.Dimension(200, 10));
jScrollPane1.setBorder(null);
jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0));

View File

@ -1,13 +1,42 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
<Properties>
<Property name="dividerLocation" type="int" value="400"/>
<Property name="resizeWeight" type="double" value="0.1"/>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="leftSplitPane">
<Properties>
<Property name="dividerLocation" type="int" value="198"/>
<Property name="resizeWeight" type="double" value="0.5"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
</Container>
</SubComponents>
</Container>
</NonVisualComponents>
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
@ -23,45 +52,4 @@
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
<Properties>
<Property name="resizeWeight" type="double" value="0.4"/>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JSplitPane" name="leftSplitPane">
<Properties>
<Property name="resizeWeight" type="double" value="0.5"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 0]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -19,12 +19,11 @@
package org.sleuthkit.autopsy.discovery.ui;
import com.google.common.eventbus.Subscribe;
import java.util.logging.Level;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifactViewer;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.discovery.search.DiscoveryEventUtils;
import org.sleuthkit.datamodel.BlackboardArtifact;
@ -32,7 +31,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact;
/**
* Panel to display the entire mini timeline feature.
*/
class MiniTimelinePanel extends javax.swing.JPanel {
final class MiniTimelinePanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
@ -40,10 +39,11 @@ class MiniTimelinePanel extends javax.swing.JPanel {
private final MiniTimelineArtifactListPanel artifactListPanel = new MiniTimelineArtifactListPanel();
private DomainArtifactsTabPanel.ArtifactRetrievalStatus status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED;
private AbstractArtifactDetailsPanel rightPanel = new GeneralPurposeArtifactViewer();
private static final Logger logger = Logger.getLogger(MiniTimelinePanel.class.getName());
private String selectedDomain = null;
private final ListSelectionListener artifactListener;
private final ListSelectionListener dateListener;
@NbBundle.Messages({"MiniTimelinePanel.loadingPanel.details=the Timeline view"})
/**
* Creates new form MiniTimelinePanel.
*/
@ -62,8 +62,8 @@ class MiniTimelinePanel extends javax.swing.JPanel {
} else {
rightPanel = new GeneralPurposeArtifactViewer();
}
rightPanel.setArtifact(artifact);
mainSplitPane.setRightComponent(rightPanel.getComponent());
rightPanel.setArtifact(artifact);
validate();
repaint();
}
@ -88,6 +88,7 @@ class MiniTimelinePanel extends javax.swing.JPanel {
leftSplitPane.setLeftComponent(dateListPanel);
leftSplitPane.setRightComponent(artifactListPanel);
mainSplitPane.setRightComponent(rightPanel.getComponent());
add(mainSplitPane);
}
/**
@ -103,17 +104,24 @@ class MiniTimelinePanel extends javax.swing.JPanel {
/**
* Manually set the status of the panel.
*
* @param status The ArtifactRetrievalStatus of the panel.
* @param status The ArtifactRetrievalStatus of the panel
* @param domain The domain the panel is currently reflecting.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.AWT)
void setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus status) {
void setStatus(DomainArtifactsTabPanel.ArtifactRetrievalStatus status, String domain) {
this.status = status;
this.selectedDomain = domain;
if (status == DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED) {
artifactListPanel.clearList();
dateListPanel.clearList();
removeAll();
add(mainSplitPane);
if (rightPanel != null) {
rightPanel.setArtifact(null);
}
} else if (status == DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATING) {
removeAll();
add(new LoadingPanel(Bundle.MiniTimelinePanel_loadingPanel_details()));
}
}
@ -126,21 +134,19 @@ class MiniTimelinePanel extends javax.swing.JPanel {
@Subscribe
void handleMiniTimelineResultEvent(DiscoveryEventUtils.MiniTimelineResultEvent miniTimelineResultEvent) {
SwingUtilities.invokeLater(() -> {
dateListPanel.removeListSelectionListener(dateListener);
artifactListPanel.removeSelectionListener(artifactListener);
dateListPanel.addArtifacts(miniTimelineResultEvent.getResultList());
status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATED;
setEnabled(!dateListPanel.isEmpty());
dateListPanel.addSelectionListener(dateListener);
artifactListPanel.addSelectionListener(artifactListener);
dateListPanel.selectFirst();
revalidate();
repaint();
try {
DiscoveryEventUtils.getDiscoveryEventBus().unregister(this);
} catch (IllegalArgumentException notRegistered) {
logger.log(Level.INFO, "Attempting to unregister mini timeline view which was not registered");
// attempting to remove a tab that was never registered
if (miniTimelineResultEvent.getDomain().equals(selectedDomain)) {
dateListPanel.removeListSelectionListener(dateListener);
artifactListPanel.removeSelectionListener(artifactListener);
dateListPanel.addArtifacts(miniTimelineResultEvent.getResultList());
status = DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATED;
setEnabled(!dateListPanel.isEmpty());
dateListPanel.addSelectionListener(dateListener);
artifactListPanel.addSelectionListener(artifactListener);
dateListPanel.selectFirst();
removeAll();
add(mainSplitPane);
revalidate();
repaint();
}
});
}
@ -157,21 +163,18 @@ class MiniTimelinePanel extends javax.swing.JPanel {
mainSplitPane = new javax.swing.JSplitPane();
leftSplitPane = new javax.swing.JSplitPane();
setMinimumSize(new java.awt.Dimension(0, 0));
setPreferredSize(new java.awt.Dimension(0, 0));
setLayout(new java.awt.BorderLayout());
mainSplitPane.setResizeWeight(0.4);
mainSplitPane.setDividerLocation(400);
mainSplitPane.setResizeWeight(0.1);
mainSplitPane.setToolTipText("");
mainSplitPane.setMinimumSize(new java.awt.Dimension(0, 0));
mainSplitPane.setPreferredSize(new java.awt.Dimension(0, 0));
leftSplitPane.setDividerLocation(198);
leftSplitPane.setResizeWeight(0.5);
leftSplitPane.setMinimumSize(new java.awt.Dimension(0, 0));
leftSplitPane.setPreferredSize(new java.awt.Dimension(300, 0));
mainSplitPane.setLeftComponent(leftSplitPane);
add(mainSplitPane, java.awt.BorderLayout.CENTER);
setMinimumSize(new java.awt.Dimension(0, 0));
setLayout(new java.awt.BorderLayout());
}// </editor-fold>//GEN-END:initComponents

View File

@ -35,7 +35,7 @@ import org.sleuthkit.autopsy.discovery.search.DomainSearch;
/**
* SwingWorker to retrieve a list of artifacts for a specified type and domain.
*/
class MiniTimelineWorker extends SwingWorker<List<MiniTimelineResult>, Void> {
class MiniTimelineWorker extends SwingWorker<Void, Void> {
private final static Logger logger = Logger.getLogger(MiniTimelineWorker.class.getName());
private final String domain;
@ -51,37 +51,35 @@ class MiniTimelineWorker extends SwingWorker<List<MiniTimelineResult>, Void> {
}
@Override
protected List<MiniTimelineResult> doInBackground() throws Exception {
protected Void doInBackground() throws Exception {
List<MiniTimelineResult> results = new ArrayList<>();
if (!StringUtils.isBlank(domain)) {
DomainSearch domainSearch = new DomainSearch();
try {
results.addAll(domainSearch.getAllArtifactsForDomain(Case.getCurrentCase().getSleuthkitCase(), domain));
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.MiniTimelineResultEvent(results, domain));
} catch (DiscoveryException ex) {
if (ex.getCause() instanceof InterruptedException) {
logger.log(Level.INFO, "MiniTimeline search was cancelled or interrupted for domain: {0}", domain);
this.cancel(true);
//ignore the exception as it was cancelled while the cache was performing its get and we support cancellation
} else {
throw ex;
}
}
}
return results;
return null;
}
@Override
protected void done() {
List<MiniTimelineResult> results = new ArrayList<>();
if (!isCancelled()) {
try {
results.addAll(get());
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.MiniTimelineResultEvent(results));
get();
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Exception while trying to get list of artifacts for Domain details for mini timeline view for domain: " + domain, ex);
} catch (CancellationException ignored) {
//Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging
}
}
}
}

View File

@ -198,8 +198,13 @@ public class Waypoint {
try {
List<BlackboardAttribute> attributeList = artifact.getAttributes();
for (BlackboardAttribute attribute : attributeList) {
BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID());
attributeMap.put(type, attribute);
try{
BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID());
attributeMap.put(type, attribute);
} catch(IllegalArgumentException ex) {
// This was thrown due to a custom attribute that geolocation
// does not currently support.
}
}
} catch (TskCoreException ex) {
throw new GeoLocationDataException("Unable to get attributes from artifact", ex);

View File

@ -1281,7 +1281,7 @@ public final class HealthMonitor implements PropertyChangeListener {
}
/**
* Get an shared lock for the health monitor database. Acquire this before
* Get a shared lock for the health monitor database. Acquire this before
* database reads or writes.
*
* @return The lock

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -155,7 +155,7 @@ public class ALeappAnalyzerIngestModule implements DataSourceIngestModule {
statusHelper.switchToDeterminate(aLeappFilesToProcess.size());
processALeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
} else {
aLeappFilesToProcess = findaLeappFilesToProcess(dataSource);
aLeappFilesToProcess = LeappFileProcessor.findLeappFilesToProcess(dataSource);
statusHelper.switchToDeterminate(aLeappFilesToProcess.size());
Integer filesProcessedCount = 0;
@ -268,40 +268,7 @@ public class ALeappAnalyzerIngestModule implements DataSourceIngestModule {
}
/**
* Find the files that will be processed by the aLeapp program
*
* @param dataSource
*
* @return List of abstract files to process.
*/
private List<AbstractFile> findaLeappFilesToProcess(Content dataSource) {
List<AbstractFile> aLeappFiles = new ArrayList<>();
FileManager fileManager = getCurrentCase().getServices().getFileManager();
// findFiles use the SQL wildcard % in the file name
try {
aLeappFiles = fileManager.findFiles(dataSource, "%", "/"); //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "No files found to process"); //NON-NLS
return aLeappFiles;
}
List<AbstractFile> aLeappFilesToProcess = new ArrayList<>();
for (AbstractFile aLeappFile : aLeappFiles) {
if (((aLeappFile.getLocalAbsPath() != null)
&& (!aLeappFile.getNameExtension().isEmpty() && (!aLeappFile.isVirtual())))
&& ((aLeappFile.getName().toLowerCase().contains(".zip") || (aLeappFile.getName().toLowerCase().contains(".tar")))
|| aLeappFile.getName().toLowerCase().contains(".tgz"))) {
aLeappFilesToProcess.add(aLeappFile);
}
}
return aLeappFilesToProcess;
}
/**
* Build the aLeapp command to run

View File

@ -155,7 +155,7 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
statusHelper.switchToDeterminate(iLeappFilesToProcess.size());
processILeappFs(dataSource, currentCase, statusHelper, tempOutputPath.toString());
} else {
iLeappFilesToProcess = findiLeappFilesToProcess(dataSource);
iLeappFilesToProcess = LeappFileProcessor.findLeappFilesToProcess(dataSource);
statusHelper.switchToDeterminate(iLeappFilesToProcess.size());
Integer filesProcessedCount = 0;
@ -268,41 +268,6 @@ public class ILeappAnalyzerIngestModule implements DataSourceIngestModule {
}
/**
* Find the files that will be processed by the iLeapp program
*
* @param dataSource
*
* @return List of abstract files to process.
*/
private List<AbstractFile> findiLeappFilesToProcess(Content dataSource) {
List<AbstractFile> iLeappFiles = new ArrayList<>();
FileManager fileManager = getCurrentCase().getServices().getFileManager();
// findFiles use the SQL wildcard % in the file name
try {
iLeappFiles = fileManager.findFiles(dataSource, "%", "/"); //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "No files found to process"); //NON-NLS
return iLeappFiles;
}
List<AbstractFile> iLeappFilesToProcess = new ArrayList<>();
for (AbstractFile iLeappFile : iLeappFiles) {
if (((iLeappFile.getLocalAbsPath() != null)
&& (!iLeappFile.getNameExtension().isEmpty() && (!iLeappFile.isVirtual())))
&& ((iLeappFile.getName().toLowerCase().contains(".zip") || (iLeappFile.getName().toLowerCase().contains(".tar")))
|| iLeappFile.getName().toLowerCase().contains(".tgz"))) {
iLeappFilesToProcess.add(iLeappFile);
}
}
return iLeappFilesToProcess;
}
/**
* Build the command to run xLeapp
* @param moduleOutputPath output path for xLeapp

View File

@ -35,18 +35,24 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import static java.util.Locale.US;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.FilenameUtils;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import static org.sleuthkit.autopsy.casemodule.Case.getCurrentCase;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
@ -69,6 +75,52 @@ import org.xml.sax.SAXException;
*/
public final class LeappFileProcessor {
/**
* Represents metadata for a particular column in a tsv file.
*/
private static class TsvColumn {
private final String attributeName;
private final String columnName;
private final boolean required;
/**
* Main constructor.
*
* @param attributeName The BlackboardAttribute name or null if not
* used.
* @param columnName The name of the column in the tsv file.
* @param required Whether or not this attribute is required to be
* present.
*/
TsvColumn(String attributeName, String columnName, boolean required) {
this.attributeName = attributeName;
this.columnName = columnName;
this.required = required;
}
/**
* @return The BlackboardAttribute name or null if not used.
*/
String getAttributeName() {
return attributeName;
}
/**
* @return The name of the column in the tsv file.
*/
String getColumnName() {
return columnName;
}
/**
* @return Whether or not this attribute is required to be present.
*/
boolean isRequired() {
return required;
}
}
private static final Logger logger = Logger.getLogger(LeappFileProcessor.class.getName());
private static final String MODULE_NAME = ILeappAnalyzerModuleFactory.getModuleName();
@ -77,7 +129,7 @@ public final class LeappFileProcessor {
private final Map<String, String> tsvFiles;
private final Map<String, String> tsvFileArtifacts;
private final Map<String, String> tsvFileArtifactComments;
private final Map<String, List<List<String>>> tsvFileAttributes;
private final Map<String, List<TsvColumn>> tsvFileAttributes;
Blackboard blkBoard;
@ -104,9 +156,7 @@ public final class LeappFileProcessor {
"LeappFileProcessor.Leapp.cancelled=Leapp run was canceled",
"LeappFileProcessor.completed=Leapp Processing Completed",
"LeappFileProcessor.error.reading.Leapp.directory=Error reading Leapp Output Directory"})
public ProcessResult processFiles(Content dataSource, Path moduleOutputPath, AbstractFile LeappFile) {
try {
List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath);
processLeappFiles(LeappTsvOutputFiles, LeappFile);
@ -123,7 +173,7 @@ public final class LeappFileProcessor {
try {
List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath);
processLeappFiles(LeappTsvOutputFiles, dataSource);
} catch (IOException | IngestModuleException ex) {
} catch (IngestModuleException ex) {
logger.log(Level.SEVERE, String.format("Error trying to process Leapp output files in directory %s. ", moduleOutputPath.toString()), ex); //NON-NLS
return ProcessResult.ERROR;
}
@ -162,7 +212,7 @@ public final class LeappFileProcessor {
* Process the Leapp files that were found that match the xml mapping file
*
* @param LeappFilesToProcess List of files to process
* @param LeappImageFile Abstract file to create artifact for
* @param LeappImageFile Abstract file to create artifact for
*
* @throws FileNotFoundException
* @throws IOException
@ -174,7 +224,7 @@ public final class LeappFileProcessor {
String fileName = FilenameUtils.getName(LeappFileName);
File LeappFile = new File(LeappFileName);
if (tsvFileAttributes.containsKey(fileName)) {
List<List<String>> attrList = tsvFileAttributes.get(fileName);
List<TsvColumn> attrList = tsvFileAttributes.get(fileName);
try {
BlackboardArtifact.Type artifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(tsvFileArtifacts.get(fileName));
@ -197,26 +247,34 @@ public final class LeappFileProcessor {
* Process the Leapp files that were found that match the xml mapping file
*
* @param LeappFilesToProcess List of files to process
* @param dataSource The data source.
* @param dataSource The data source.
*
* @throws FileNotFoundException
* @throws IOException
*/
private void processLeappFiles(List<String> LeappFilesToProcess, Content dataSource) throws FileNotFoundException, IOException, IngestModuleException {
private void processLeappFiles(List<String> LeappFilesToProcess, Content dataSource) throws IngestModuleException {
List<BlackboardArtifact> bbartifacts = new ArrayList<>();
for (String LeappFileName : LeappFilesToProcess) {
String fileName = FilenameUtils.getName(LeappFileName);
File LeappFile = new File(LeappFileName);
if (tsvFileAttributes.containsKey(fileName)) {
List<List<String>> attrList = tsvFileAttributes.get(fileName);
List<TsvColumn> attrList = tsvFileAttributes.get(fileName);
BlackboardArtifact.Type artifactType = null;
try {
BlackboardArtifact.Type artifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(tsvFileArtifacts.get(fileName));
processFile(LeappFile, attrList, fileName, artifactType, bbartifacts, dataSource);
artifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(tsvFileArtifacts.get(fileName));
} catch (TskCoreException ex) {
throw new IngestModuleException(String.format("Error getting Blackboard Artifact Type for %s", tsvFileArtifacts.get(fileName)), ex);
logger.log(Level.SEVERE, String.format("Error getting Blackboard Artifact Type for %s", tsvFileArtifacts.get(fileName)), ex);
}
if (artifactType == null) {
continue;
}
try {
processFile(LeappFile, attrList, fileName, artifactType, bbartifacts, dataSource);
} catch (TskCoreException | IOException ex) {
logger.log(Level.SEVERE, String.format("Error processing file at %s", LeappFile.toString()), ex);
}
}
@ -228,26 +286,34 @@ public final class LeappFileProcessor {
}
private void processFile(File LeappFile, List<List<String>> attrList, String fileName, BlackboardArtifact.Type artifactType,
private void processFile(File LeappFile, List<TsvColumn> attrList, String fileName, BlackboardArtifact.Type artifactType,
List<BlackboardArtifact> bbartifacts, Content dataSource) throws FileNotFoundException, IOException, IngestModuleException,
TskCoreException {
if (LeappFile == null || !LeappFile.exists() || fileName == null) {
logger.log(Level.WARNING, String.format("Leap file: %s is null or does not exist", LeappFile == null ? LeappFile.toString() : "<null>"));
return;
} else if (attrList == null || artifactType == null || dataSource == null) {
logger.log(Level.WARNING, String.format("attribute list, artifact type or dataSource not provided for %s", LeappFile == null ? LeappFile.toString() : "<null>"));
return;
}
try (BufferedReader reader = new BufferedReader(new FileReader(LeappFile))) {
String line = reader.readLine();
String header = reader.readLine();
// Check first line, if it is null then no heading so nothing to match to, close and go to next file.
if (line != null) {
Map<Integer, String> columnNumberToProcess = findColumnsToProcess(line, attrList);
line = reader.readLine();
if (header != null) {
Map<Integer, String> columnNumberToProcess = findColumnsToProcess(fileName, header, attrList);
String line = reader.readLine();
while (line != null) {
Collection<BlackboardAttribute> bbattributes = processReadLine(line, columnNumberToProcess, fileName);
if (artifactType == null) {
logger.log(Level.SEVERE, "Error trying to process Leapp output files in directory . "); //NON-NLS
}
if (!bbattributes.isEmpty() && !blkBoard.artifactExists(dataSource, BlackboardArtifact.ARTIFACT_TYPE.fromID(artifactType.getTypeID()), bbattributes)) {
BlackboardArtifact bbartifact = createArtifactWithAttributes(artifactType.getTypeID(), dataSource, bbattributes);
if (bbartifact != null) {
bbartifacts.add(bbartifact);
}
}
line = reader.readLine();
}
}
@ -258,20 +324,28 @@ public final class LeappFileProcessor {
/**
* Process the line read and create the necessary attributes for it
*
* @param line a tsv line to process that was read
* @param line a tsv line to process that was read
* @param columnNumberToProcess Which columns to process in the tsv line
* @param fileName name of file begin processed
* @param fileName name of file begin processed
*
* @return
*/
private Collection<BlackboardAttribute> processReadLine(String line, Map<Integer, String> columnNumberToProcess, String fileName) throws IngestModuleException {
if (MapUtils.isEmpty(columnNumberToProcess)) {
return Collections.emptyList();
} else if (line == null) {
logger.log(Level.WARNING, "Line is null. Returning empty list for attributes.");
return Collections.emptyList();
}
String[] columnValues;
// Check to see if the 2 values are equal, they may not be equal if there is no corresponding data in the line.
// or if the size of the line to split is not equal to the column numbers we are looking to process. This
// can happen when the last value of the tsv line has no data in it.
// If this happens then adding an empty value(s) for each columnValue where data does not exist
Integer maxColumnNumber = Collections.max(columnNumberToProcess.keySet());
if (maxColumnNumber > line.split("\\t").length) {
if ((maxColumnNumber > line.split("\\t").length) || (columnNumberToProcess.size() > line.split("\\t").length)) {
columnValues = Arrays.copyOf(line.split("\\t"), maxColumnNumber + 1);
} else {
columnValues = line.split("\\t");
@ -283,15 +357,17 @@ public final class LeappFileProcessor {
Integer columnNumber = columnToProcess.getKey();
String attributeName = columnToProcess.getValue();
try {
BlackboardAttribute.Type attributeType = Case.getCurrentCase().getSleuthkitCase().getAttributeType(attributeName.toUpperCase());
if (attributeType == null) {
break;
if (columnValues[columnNumber] != null) {
try {
BlackboardAttribute.Type attributeType = Case.getCurrentCase().getSleuthkitCase().getAttributeType(attributeName.toUpperCase());
if (attributeType == null) {
continue;
}
String attrType = attributeType.getValueType().getLabel().toUpperCase();
checkAttributeType(bbattributes, attrType, columnValues, columnNumber, attributeType, fileName);
} catch (TskCoreException ex) {
throw new IngestModuleException(String.format("Error getting Attribute type for Attribute Name %s", attributeName), ex); //NON-NLS
}
String attrType = attributeType.getValueType().getLabel().toUpperCase();
checkAttributeType(bbattributes, attrType, columnValues, columnNumber, attributeType, fileName);
} catch (TskCoreException ex) {
throw new IngestModuleException(String.format("Error getting Attribute type for Attribute Name %s", attributeName), ex); //NON-NLS
}
}
@ -303,34 +379,60 @@ public final class LeappFileProcessor {
}
private void checkAttributeType(Collection<BlackboardAttribute> bbattributes, String attrType, String[] columnValues, Integer columnNumber, BlackboardAttribute.Type attributeType,
private void checkAttributeType(Collection<BlackboardAttribute> bbattributes, String attrType, String[] columnValues, int columnNumber, BlackboardAttribute.Type attributeType,
String fileName) {
if (columnValues == null || columnNumber < 0 || columnNumber > columnValues.length || columnValues[columnNumber] == null) {
logger.log(Level.WARNING, String.format("Unable to determine column value at index %d in columnValues: %s",
columnNumber,
columnValues == null ? "<null>" : "[" + String.join(", ", columnValues) + "]"));
return;
}
String columnValue = columnValues[columnNumber];
if (attrType.matches("STRING")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValues[columnNumber]));
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValue));
} else if (attrType.matches("INTEGER")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Integer.valueOf(columnValues[columnNumber])));
try {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Integer.valueOf(columnValue)));
} catch (NumberFormatException ex) {
logger.log(Level.WARNING, String.format("Unable to format %s as an integer.", columnValue), ex);
}
} else if (attrType.matches("LONG")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Long.valueOf(columnValues[columnNumber])));
try {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Long.valueOf(columnValue)));
} catch (NumberFormatException ex) {
logger.log(Level.WARNING, String.format("Unable to format %s as an long.", columnValue), ex);
}
} else if (attrType.matches("DOUBLE")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Double.valueOf(columnValues[columnNumber])));
try {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Double.valueOf(columnValue)));
} catch (NumberFormatException ex) {
logger.log(Level.WARNING, String.format("Unable to format %s as an double.", columnValue), ex);
}
} else if (attrType.matches("BYTE")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Byte.valueOf(columnValues[columnNumber])));
try {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, Byte.valueOf(columnValue)));
} catch (NumberFormatException ex) {
logger.log(Level.WARNING, String.format("Unable to format %s as an byte.", columnValue), ex);
}
} else if (attrType.matches("DATETIME")) {
// format of data should be the same in all the data and the format is 2020-03-28 01:00:17
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-d HH:mm:ss", US);
Long dateLong = Long.valueOf(0);
try {
Date newDate = dateFormat.parse(columnValues[columnNumber]);
Date newDate = dateFormat.parse(columnValue);
dateLong = newDate.getTime() / 1000;
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, dateLong));
} catch (ParseException ex) {
// catching error and displaying date that could not be parsed
// we set the timestamp to 0 and continue on processing
logger.log(Level.WARNING, String.format("Failed to parse date/time %s for attribute type %s in file %s.", columnValues[columnNumber], attributeType.getDisplayName(), fileName)); //NON-NLS
logger.log(Level.WARNING, String.format("Failed to parse date/time %s for attribute type %s in file %s.", columnValue, attributeType.getDisplayName(), fileName)); //NON-NLS
}
} else if (attrType.matches("JSON")) {
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValues[columnNumber]));
bbattributes.add(new BlackboardAttribute(attributeType, MODULE_NAME, columnValue));
} else {
// Log this and continue on with processing
logger.log(Level.WARNING, String.format("Attribute Type %s not defined.", attrType)); //NON-NLS
@ -343,29 +445,43 @@ public final class LeappFileProcessor {
* headings to the columns in the XML mapping file so we know which columns
* to process.
*
* @param line a tsv heading line of the columns in the file
* @param fileName The name of the file in which these column headers exist.
* @param line a tsv heading line of the columns in the file
* @param attrList the list of headings we want to process
*
* @return the numbered column(s) and attribute(s) we want to use for the
* column(s)
* column(s)
*/
private Map<Integer, String> findColumnsToProcess(String line, List<List<String>> attrList) {
private Map<Integer, String> findColumnsToProcess(String fileName, String line, List<TsvColumn> attrList) {
String[] columnNames = line.split("\\t");
HashMap<Integer, String> columnsToProcess = new HashMap<>();
Integer columnPosition = 0;
for (String columnName : columnNames) {
// for some reason the first column of the line has unprintable characters so removing them
String cleanColumnName = columnName.replaceAll("[^\\n\\r\\t\\p{Print}]", "");
for (List<String> atList : attrList) {
if (atList.contains(cleanColumnName.toLowerCase())) {
columnsToProcess.put(columnPosition, atList.get(0));
String cleanColumnName = columnName.trim().replaceAll("[^\\n\\r\\t\\p{Print}]", "");
for (TsvColumn tsvColumn : attrList) {
if (cleanColumnName.equalsIgnoreCase(tsvColumn.getColumnName())) {
columnsToProcess.put(columnPosition, tsvColumn.getAttributeName());
break;
}
}
columnPosition++;
}
if (columnsToProcess.size() != attrList.size()) {
String missingColumns = IntStream.range(0, attrList.size())
.filter((idx) -> !columnsToProcess.containsKey(attrList.get(idx).getAttributeName()))
.mapToObj((idx) -> String.format("'%s'", attrList.get(idx).getColumnName() == null ? "<null>" : attrList.get(idx).getColumnName()))
.collect(Collectors.joining(", "));
logger.log(Level.WARNING, String.format("Columns size expected not found in file %s based on xml from %s. Column Keys Missing = [%s]; Header Line = '%s'.",
this.xmlFile == null ? "<null>" : this.xmlFile,
fileName,
missingColumns,
line));
}
return columnsToProcess;
}
@ -424,6 +540,18 @@ public final class LeappFileProcessor {
String comment = nnm.getNamedItem("comment").getNodeValue();
String parentName = artifactNlist.item(k).getParentNode().getAttributes().getNamedItem("filename").getNodeValue();
BlackboardArtifact.Type foundArtifactType = null;
try {
foundArtifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(artifactName);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("There was an issue that arose while trying to fetch artifact type for %s.", artifactName), ex);
}
if (foundArtifactType == null) {
logger.log(Level.SEVERE, String.format("No known artifact mapping found for [artifact: %s, %s]",
artifactName, getXmlFileIdentifier(parentName)));
}
tsvFileArtifacts.put(parentName, artifactName);
if (!comment.toLowerCase().matches("null")) {
@ -433,29 +561,66 @@ public final class LeappFileProcessor {
}
private String getXmlFileIdentifier(String fileName) {
return String.format("file: %s, filename: %s",
this.xmlFile == null ? "<null>" : this.xmlFile,
fileName == null ? "<null>" : fileName);
}
private String getXmlAttrIdentifier(String fileName, String attributeName) {
return String.format("attribute: %s %s",
attributeName == null ? "<null>" : attributeName,
getXmlFileIdentifier(fileName));
}
private void getAttributeNodes(Document xmlinput) {
NodeList attributeNlist = xmlinput.getElementsByTagName("AttributeName"); //NON-NLS
for (int k = 0; k < attributeNlist.getLength(); k++) {
List<String> attributeList = new ArrayList<>();
NamedNodeMap nnm = attributeNlist.item(k).getAttributes();
String attributeName = nnm.getNamedItem("attributename").getNodeValue();
if (!attributeName.toLowerCase().matches("null")) {
String columnName = nnm.getNamedItem("columnName").getNodeValue();
String required = nnm.getNamedItem("required").getNodeValue();
String parentName = attributeNlist.item(k).getParentNode().getParentNode().getAttributes().getNamedItem("filename").getNodeValue();
attributeList.add(attributeName.toLowerCase());
attributeList.add(columnName.toLowerCase());
attributeList.add(required.toLowerCase());
BlackboardAttribute.Type foundAttrType = null;
try {
foundAttrType = Case.getCurrentCase().getSleuthkitCase().getAttributeType(attributeName.toUpperCase());
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, String.format("There was an issue that arose while trying to fetch attribute type for %s.", attributeName), ex);
}
if (foundAttrType == null) {
logger.log(Level.SEVERE, String.format("No known attribute mapping found for [%s]", getXmlAttrIdentifier(parentName, attributeName)));
}
if (required != null && required.compareToIgnoreCase("yes") != 0 && required.compareToIgnoreCase("no") != 0) {
logger.log(Level.SEVERE, String.format("Required value %s did not match 'yes' or 'no' for [%s]",
required, getXmlAttrIdentifier(parentName, attributeName)));
}
if (columnName == null) {
logger.log(Level.SEVERE, String.format("No column name provided for [%s]", getXmlAttrIdentifier(parentName, attributeName)));
} else if (columnName.trim().length() != columnName.length()) {
logger.log(Level.SEVERE, String.format("Column name '%s' starts or ends with whitespace for [%s]", columnName, getXmlAttrIdentifier(parentName, attributeName)));
} else if (columnName.matches("[^ \\S]")) {
logger.log(Level.SEVERE, String.format("Column name '%s' contains invalid characters [%s]", columnName, getXmlAttrIdentifier(parentName, attributeName)));
}
TsvColumn thisCol = new TsvColumn(
attributeName.toLowerCase(),
columnName.toLowerCase(),
"yes".compareToIgnoreCase(required) == 0);
if (tsvFileAttributes.containsKey(parentName)) {
List<List<String>> attrList = tsvFileAttributes.get(parentName);
attrList.add(attributeList);
List<TsvColumn> attrList = tsvFileAttributes.get(parentName);
attrList.add(thisCol);
tsvFileAttributes.replace(parentName, attrList);
} else {
List<List<String>> attrList = new ArrayList<>();
attrList.add(attributeList);
List<TsvColumn> attrList = new ArrayList<>();
attrList.add(thisCol);
tsvFileAttributes.put(parentName, attrList);
}
}
@ -466,13 +631,12 @@ public final class LeappFileProcessor {
/**
* Generic method for creating a blackboard artifact with attributes
*
* @param type is a blackboard.artifact_type enum to determine which
* type the artifact should be
* @param type is a blackboard.artifact_type enum to determine which type
* the artifact should be
* @param abstractFile is the AbstractFile object that needs to have the
* artifact added for it
* artifact added for it
* @param bbattributes is the collection of blackboard attributes that need
* to be added to the artifact after the artifact has
* been created
* to be added to the artifact after the artifact has been created
*
* @return The newly-created artifact, or null on error
*/
@ -490,13 +654,12 @@ public final class LeappFileProcessor {
/**
* Generic method for creating a blackboard artifact with attributes
*
* @param type is a blackboard.artifact_type enum to determine which
* type the artifact should be
* @param dataSource is the Content object that needs to have the artifact
* added for it
* @param type is a blackboard.artifact_type enum to determine which type
* the artifact should be
* @param dataSource is the Content object that needs to have the artifact
* added for it
* @param bbattributes is the collection of blackboard attributes that need
* to be added to the artifact after the artifact has
* been created
* to be added to the artifact after the artifact has been created
*
* @return The newly-created artifact, or null on error
*/
@ -515,7 +678,7 @@ public final class LeappFileProcessor {
* Method to post a list of BlackboardArtifacts to the blackboard.
*
* @param artifacts A list of artifacts. IF list is empty or null, the
* function will return.
* function will return.
*/
void postArtifacts(Collection<BlackboardArtifact> artifacts) {
if (artifacts == null || artifacts.isEmpty()) {
@ -535,7 +698,44 @@ public final class LeappFileProcessor {
* @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException
*/
private void configExtractor() throws IOException {
PlatformUtil.extractResourceToUserConfigDir(LeappFileProcessor.class, xmlFile, true);
PlatformUtil.extractResourceToUserConfigDir(LeappFileProcessor.class,
xmlFile, true);
}
private static final Set<String> ALLOWED_EXTENSIONS = new HashSet<>(Arrays.asList("zip", "tar", "tgz"));
/**
* Find the files that will be processed by the iLeapp program
*
* @param dataSource
*
* @return List of abstract files to process.
*/
static List<AbstractFile> findLeappFilesToProcess(Content dataSource) {
List<AbstractFile> leappFiles = new ArrayList<>();
FileManager fileManager = getCurrentCase().getServices().getFileManager();
// findFiles use the SQL wildcard % in the file name
try {
leappFiles = fileManager.findFiles(dataSource, "%", "/"); //NON-NLS
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "No files found to process"); //NON-NLS
return leappFiles;
}
List<AbstractFile> leappFilesToProcess = new ArrayList<>();
for (AbstractFile leappFile : leappFiles) {
if (((leappFile.getLocalAbsPath() != null)
&& !leappFile.isVirtual())
&& leappFile.getNameExtension() != null
&& ALLOWED_EXTENSIONS.contains(leappFile.getNameExtension().toLowerCase())) {
leappFilesToProcess.add(leappFile);
}
}
return leappFilesToProcess;
}
}

View File

@ -31,18 +31,18 @@
<FileName filename="accounts ce 0.tsv" description="Accounts_ce">
<ArtifactName artifactname="TSK_SERVICE_ACCOUNT" comment="accounts ce 0">
<AttributeName attributename="TSK_USER_ID" columnName="Name" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName=" Type" required="yes" />
<AttributeName attributename="TSK_PASSWORD" columnName=" Password" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Type" required="yes" />
<AttributeName attributename="TSK_PASSWORD" columnName="Password" required="yes" />
</ArtifactName>
</FileName>
<FileName filename="authtokens 0.tsv" description="Authtokens">
<ArtifactName artifactname="TSK_SERVICE_ACCOUNT" comment="Authtokens">
<AttributeName attributename="null" columnName="ID" required="no" />
<AttributeName attributename="TSK_USER_ID" columnName=" Name" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName=" Account Type" required="yes" />
<AttributeName attributename="TSK_USER_ID" columnName="Name" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Account Type" required="yes" />
<AttributeName attributename="null" columnName="Authtoken Type" required="no" />
<AttributeName attributename="TSK_PASSWORD" columnName=" Authtoken" required="yes" />
<AttributeName attributename="TSK_PASSWORD" columnName="Authtoken" required="yes" />
</ArtifactName>
</FileName>
@ -56,17 +56,17 @@
<FileName filename="Browser Bookmarks.tsv" description="Browser Bookmarks">
<ArtifactName artifactname="TSK_WEB_BOOKMARK" comment="Browser Bookmarks">
<AttributeName attributename="TSK_DATETIME_CREATED " columnName="Added Date" required="yes" />
<AttributeName attributename="TSK_URL" columnName=" URL" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName=" Name" required="yes" />
<AttributeName attributename="null" columnName=" Parent" required="no" />
<AttributeName attributename="null" columnName=" Type" required="no" />
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Added Date" required="yes" />
<AttributeName attributename="TSK_URL" columnName="URL" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName="Name" required="yes" />
<AttributeName attributename="null" columnName="Parent" required="no" />
<AttributeName attributename="null" columnName="Type" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Browser cookies.tsv" description="Browser Cookies">
<ArtifactName artifactname="TSK_WEB_COOKIE" comment="Browser Cookies">
<AttributeName attributename="TSK_DATETIME_ACCESS" columnName="Last Access Date" required="yes" />
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Date" required="yes" />
<AttributeName attributename="TSK_DOMAIN" columnName="Host" required="yes" />
<AttributeName attributename="TSK_NAME" columnName="Name" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName="Value" required="yes" />
@ -108,11 +108,11 @@
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="Browser Offline Pages">
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Creation Time" required="yes" />
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Time" required="yes" />
<AttributeName attributename="TSK_URL" columnName=" Online URL" required="yes" />
<AttributeName attributename="null" columnName=" File Path" required="no" />
<AttributeName attributename="TSK_TITLE" columnName=" Title" required="no" />
<AttributeName attributename="null" columnName=" Access Count" required="no" />
<AttributeName attributename="null" columnName=" File Size" required="no" />
<AttributeName attributename="TSK_URL" columnName="Online URL" required="yes" />
<AttributeName attributename="null" columnName="File Path" required="no" />
<AttributeName attributename="TSK_TITLE" columnName="Title" required="no" />
<AttributeName attributename="null" columnName="Access Count" required="no" />
<AttributeName attributename="null" columnName="File Size" required="no" />
</ArtifactName>
</FileName>
@ -153,17 +153,17 @@
<FileName filename="Chrome Bookmarks.tsv" description="Chrome Bookmarks">
<ArtifactName artifactname="TSK_WEB_BOOKMARK" comment="Chrome Bookmarks">
<AttributeName attributename="TSK_DATETIME_CREATED " columnName="Added Date" required="yes" />
<AttributeName attributename="TSK_URL" columnName=" URL" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName=" Name" required="yes" />
<AttributeName attributename="null" columnName=" Parent" required="no" />
<AttributeName attributename="null" columnName=" Type" required="no" />
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Added Date" required="yes" />
<AttributeName attributename="TSK_URL" columnName="URL" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName="Name" required="yes" />
<AttributeName attributename="null" columnName="Parent" required="no" />
<AttributeName attributename="null" columnName="Type" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Chrome cookies.tsv" description="Chrome Cookies">
<ArtifactName artifactname="TSK_WEB_COOKIE" comment="Chrome Cookies">
<AttributeName attributename="TSK_DATETIME_ACCESS" columnName="Last Access Date" required="yes" />
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Date" required="yes" />
<AttributeName attributename="TSK_DOMAIN" columnName="Host" required="yes" />
<AttributeName attributename="TSK_NAME" columnName="Name" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName="Value" required="yes" />
@ -197,11 +197,11 @@
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="Chrome Offline Pages">
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Creation Time" required="yes" />
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Time" required="yes" />
<AttributeName attributename="TSK_URL" columnName=" Online URL" required="yes" />
<AttributeName attributename="null" columnName=" File Path" required="no" />
<AttributeName attributename="TSK_TITLE" columnName=" Title" required="no" />
<AttributeName attributename="null" columnName=" Access Count" required="no" />
<AttributeName attributename="null" columnName=" File Size" required="no" />
<AttributeName attributename="TSK_URL" columnName="Online URL" required="yes" />
<AttributeName attributename="null" columnName="File Path" required="no" />
<AttributeName attributename="TSK_TITLE" columnName="Title" required="no" />
<AttributeName attributename="null" columnName="Access Count" required="no" />
<AttributeName attributename="null" columnName="File Size" required="no" />
</ArtifactName>
</FileName>
@ -224,6 +224,79 @@
</ArtifactName>
</FileName>
<FileName filename="Edge Bookmarks.tsv" description="Edge Bookmarks">
<ArtifactName artifactname="TSK_WEB_BOOKMARK" comment="Chrome Bookmarks">
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Added Date" required="yes" />
<AttributeName attributename="TSK_URL" columnName="URL" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName="Name" required="yes" />
<AttributeName attributename="null" columnName="Parent" required="no" />
<AttributeName attributename="null" columnName="Type" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Edge cookies.tsv" description="Edge Cookies">
<ArtifactName artifactname="TSK_WEB_COOKIE" comment="Edge Cookies">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Date" required="yes" />
<AttributeName attributename="TSK_DOMAIN" columnName="Host" required="yes" />
<AttributeName attributename="TSK_NAME" columnName="Name" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName="Value" required="yes" />
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Created Date" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="Expiration Date" required="yes" />
<AttributeName attributename="TSK_PATH" columnName="Path" required="yes" />
</ArtifactName>
</FileName>
<FileName filename="Edge History.tsv" description="Edge History">
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="Edge History">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Visit Time" required="yes"/>
<AttributeName attributename="TSK_URL" columnName="URL" required="yes"/>
<AttributeName attributename="TSK_TITLE" columnName="Title" required="yes"/>
<AttributeName attributename="null" columnName="Visit Count" required="no"/>
<AttributeName attributename="null" columnName="Hidden" required="no"/>
</ArtifactName>
</FileName>
<FileName filename="Edge login data.tsv" description="Edge Login Data">
<ArtifactName artifactname="TSK_SERVICE_ACCOUNT" comment="Edge Login">
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Created Time" required="yes" />
<AttributeName attributename="TSK_USER_NAME" columnName="Username" required="yes" />
<AttributeName attributename="TSK_PASSWORD" columnName="Password" required="yes" />
<AttributeName attributename="TSK_URL" columnName="Origin URL" required="no" />
<AttributeName attributename="null" columnName="Blacklisted by User" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Edge offline pages.tsv" description="Edge Offline Pages">
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="Edge Offline Pages">
<AttributeName attributename="TSK_DATETIME_CREATED" columnName="Creation Time" required="yes" />
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Access Time" required="yes" />
<AttributeName attributename="TSK_URL" columnName="Online URL" required="yes" />
<AttributeName attributename="null" columnName="File Path" required="no" />
<AttributeName attributename="TSK_TITLE" columnName="Title" required="no" />
<AttributeName attributename="null" columnName="Access Count" required="no" />
<AttributeName attributename="null" columnName="File Size" required="no" />
</ArtifactName>
</FileName>
<FileName filename="Edge search terms.tsv" description="Edge Search Terms">
<ArtifactName artifactname="TSK_WEB_SEARCH_QUERY" comment="Chrome Search Terms">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Last Visit Time" required="yes"/>
<AttributeName attributename="TSK_TEXT" columnName="Search Term" required="yes"/>
<AttributeName attributename="TSK_URL" columnName="URL" required="yes"/>
<AttributeName attributename="null" columnName="Title" required="no"/>
<AttributeName attributename="null" columnName="Visit Count" required="no"/>
</ArtifactName>
</FileName>
<FileName filename="Edge top sites.tsv" description="Edge Top Sites">
<ArtifactName artifactname="TSK_WEB_HISTORY" comment="Edge Top Sites">
<AttributeName attributename="TSK_URL" columnName="URL" required="yes" />
<AttributeName attributename="null" columnName="Rank" required="no" />
<AttributeName attributename="TSK_TITLE" columnName="Title" required="no" />
<AttributeName attributename="null" columnName="Redirects" required="no" />
</ArtifactName>
</FileName>
<FileName filename="google play searches.tsv" description="Google Play Searches">
<ArtifactName artifactname="TSK_WEB_SEARCH_QUERY" comment="Google Play Search">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="Timestamp" required="yes" />
@ -233,10 +306,11 @@
</FileName>
<FileName filename="google quick search box.tsv" description="Google quick search box">
<ArtifactName artifactname="TSK_WEB_SEARCH_QUERY" comment="Google Quick Search Search">
<AttributeName attributename="TSK_DATETIME" columnName="File Timestamp" required="yes" />
<ArtifactName artifactname="TSK_WEB_SEARCH_QUERY" comment="Google Quick Search">
<AttributeName attributename="TSK_DATETIME_ACCESSED" columnName="File Timestamp" required="yes" />
<AttributeName attributename="null" columnName="Type" required="no" />
<AttributeName attributename="TSK_TEXT" columnName="Queries Response" required="yes" />
<AttributeName attributename="TSK_TEXT" columnName="Queries" required="yes" />
<AttributeName attributename="null" columnName="Response" required="no" />
<AttributeName attributename="null" columnName="Source File" required="no" />
</ArtifactName>
</FileName>
@ -256,12 +330,12 @@
</FileName>
<FileName filename="installed apps vending.tsv" description="Installed Apps (Vending)">
<ArtifactName artifactname="TSK_INSTALLED_PROG" comment="Installed Apps (VEnding)">
<ArtifactName artifactname="TSK_INSTALLED_PROG" comment="Installed Apps (Vending)">
<AttributeName attributename="TSK_DATETIME" columnName="First Download" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Package Name" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName=" Title" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName="Title" required="yes" />
<AttributeName attributename="null" columnName="Install Reason" required="no" />
<AttributeName attributename="null" columnName=" Auto Update?" required="no" />
<AttributeName attributename="null" columnName="Auto Update?" required="no" />
</ArtifactName>
</FileName>

View File

@ -42,8 +42,8 @@
<FileName filename="Application State.tsv" description="Application State">
<ArtifactName artifactname="TSK_INSTALLED_PROG" comment="Application State">
<AttributeName attributename="TSK_PROG_NAME" columnName="Bundle ID" required="no" />
<AttributeName attributename="TSK_INSTALLED_PATH" columnName="Bundle Path" required="yes" />
<AttributeName attributename="TSK_INSTALLED_SOURCE" columnName="Sandbox Path" required="yes" />
<AttributeName attributename="TSK_PATH" columnName="Bundle Path" required="yes" />
<AttributeName attributename="TSK_PATH_SOURCE" columnName="Sandbox Path" required="yes" />
</ArtifactName>
</FileName>
@ -84,7 +84,7 @@
<AttributeName attributename="TSK_DATETIME_END" columnName="End Date" required="yes" />
<AttributeName attributename="null" columnName="End Timezone" required="no" />
<AttributeName attributename="null" columnName="All Day?" required="no" />
<AttributeName attributename="TSK_CALENDAR_ENTRY" columnName="Summary" required="yes" />
<AttributeName attributename="TSK_CALENDAR_ENTRY_TYPE" columnName="Summary" required="yes" />
<AttributeName attributename="null" columnName="Calendar ID" required="no" />
<AttributeName attributename="null" columnName="Last Modified" required="no" />
</ArtifactName>
@ -113,7 +113,7 @@
<AttributeName attributename="null" columnName="Process Name" required="no" />
<AttributeName attributename="null" columnName="WIFI In" required="no" />
<AttributeName attributename="null" columnName="WIFI Out" required="no" />
<AttributeName attributename="TSK_BYTES_RCVD" columnName="WWAN IN" required="yes" />
<AttributeName attributename="TSK_BYTES_RECEIVED" columnName="WWAN IN" required="yes" />
<AttributeName attributename="TSK_BYTES_SENT" columnName="WWAN Out" required="yes" />
<AttributeName attributename="null" columnName="Table ID" required="no" />
</ArtifactName>
@ -143,13 +143,13 @@
<AttributeName attributename="null" columnName="Start" required="no" />
<AttributeName attributename="null" columnName="End" required="no" />
<AttributeName attributename="null" columnName="ZSTREAMNAME" required="no" />
<AttributeName attributename="TSK_PROG_NAME" columnName=" ZVALUESTRING" required="no" />
<AttributeName attributename="null" columnName=" Activity Type" required="no" />
<AttributeName attributename="null" columnName=" Title" required="no" />
<AttributeName attributename="null" columnName=" Expiration Date" required="no" />
<AttributeName attributename="null" columnName=" Content URL" required="no" />
<AttributeName attributename="null" columnName=" Calendar Date" required="no" />
<AttributeName attributename="null" columnName=" Calendar End Date" required="no" />
<AttributeName attributename="TSK_PROG_NAME" columnName="ZVALUESTRING" required="no" />
<AttributeName attributename="null" columnName="Activity Type" required="no" />
<AttributeName attributename="null" columnName="Title" required="no" />
<AttributeName attributename="null" columnName="Expiration Date" required="no" />
<AttributeName attributename="null" columnName="Content URL" required="no" />
<AttributeName attributename="null" columnName="Calendar Date" required="no" />
<AttributeName attributename="null" columnName="Calendar End Date" required="no" />
</ArtifactName>
</FileName>
@ -160,7 +160,7 @@
<AttributeName attributename="null" columnName="Bundle ID" required="no" />
<AttributeName attributename="TSK_CALENDAR_ENTRY_TYPE" columnName="Activity Type" required="yes" />
<AttributeName attributename="TSK_DESCRIPTION" columnName="User Activity Required String" required="yes" />
<AttributeName attributename="null" columnName=" Title" required="no" />
<AttributeName attributename="null" columnName="Title" required="no" />
<AttributeName attributename="null" columnName="Calendar Date" required="no" />
<AttributeName attributename="null" columnName="Calendar End Date" required="no" />
<AttributeName attributename="TSK_LOCATION" columnName="Source ID" required="yes" />
@ -209,7 +209,7 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Device Backlit">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Screen is Backlit" required="yes" />
<AttributeName attributename="null" columnName="Screen is Backlit" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -226,12 +226,12 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Battery Level">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Battery Level" required="yes" />
<AttributeName attributename="null" columnName="Battery Level" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Day of the Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName=" ZOBJECT Table ID" required="no" />
<AttributeName attributename="null" columnName="ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
@ -255,7 +255,7 @@
<ArtifactName artifactname="TSK_DEVICE_INFO" comment="KnowledgeC Car Play Connections">
<AttributeName attributename="TSK_DATETIME" columnName="Start" required="yes" />
<AttributeName attributename="null" columnName="End" required="no" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Car Play Connected" required="yes" />
<AttributeName attributename="null" columnName="Car Play Connected" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -271,7 +271,7 @@
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Bundle ID" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Value String" required="yes" />
<AttributeName attributename="null" columnName="Value String" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -286,7 +286,7 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Do Not Disturb">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Value" required="yes" />
<AttributeName attributename="null" columnName="Value" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -301,7 +301,7 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Inferred Motion">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Value" required="yes" />
<AttributeName attributename="null" columnName="Value" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -344,12 +344,12 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Device Locked">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Is Locked?" required="yes" />
<AttributeName attributename="null" columnName="Is Locked?" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Day of the Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName=" ZOBJECT Table ID" required="no" />
<AttributeName attributename="null" columnName="ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
@ -362,7 +362,7 @@
<AttributeName attributename="null" columnName="Now Playing Artists" required="no" />
<AttributeName attributename="null" columnName="Playing Genre" required="no" />
<AttributeName attributename="TSK_NAME" columnName="Playing Title" required="yes" />
<AttributeName attributename="null" columnName=" Now Playing Duration" required="no" />
<AttributeName attributename="null" columnName="Now Playing Duration" required="no" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -388,7 +388,7 @@
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName="Expiration Date" required="no" />
<AttributeName attributename="null" columnName=" UUID" required="no" />
<AttributeName attributename="null" columnName="UUID" required="no" />
<AttributeName attributename="null" columnName="ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
@ -397,7 +397,7 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Screen Orientation">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Orientation" required="yes" />
<AttributeName attributename="null" columnName="Orientation" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Usage in Minutes" required="no" />
<AttributeName attributename="null" columnName="Day of Week" required="no" />
@ -412,14 +412,14 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Plugged In">
<AttributeName attributename="TSK_DATETIME_START" columnName="Start" required="yes" />
<AttributeName attributename="TSK_DATETIME_END" columnName="End" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Is Plugged In?" required="yes" />
<AttributeName attributename="null" columnName="Is Plugged In?" required="yes" />
<AttributeName attributename="null" columnName="Usage in Seconds" required="no" />
<AttributeName attributename="null" columnName="Day of the Week" required="no" />
<AttributeName attributename="null" columnName="GMT Offset" required="no" />
<AttributeName attributename="null" columnName="Start" required="no" />
<AttributeName attributename="null" columnName="End" required="no" />
<AttributeName attributename="null" columnName="Entry Creation" required="no" />
<AttributeName attributename="null" columnName=" ZOBJECT Table ID" required="no" />
<AttributeName attributename="null" columnName="ZOBJECT Table ID" required="no" />
</ArtifactName>
</FileName>
@ -528,8 +528,8 @@
<AttributeName attributename="null" columnName="Location Date" required="no" />
<AttributeName attributename="null" columnName="Coordinates" required="no" />
<AttributeName attributename="null" columnName="Vehicle Identifier" required="no" />
<AttributeName attributename="null" columnName=" Location Identifier" required="no" />
<AttributeName attributename="null" columnName=" Identifier" required="no" />
<AttributeName attributename="null" columnName="Location Identifier" required="no" />
<AttributeName attributename="null" columnName="Identifier" required="no" />
<AttributeName attributename="null" columnName="Location Quality" required="no" />
<AttributeName attributename="null" columnName="User Set Location" required="no" />
<AttributeName attributename="null" columnName="Usual Location" required="no" />
@ -584,11 +584,11 @@
<AttributeName attributename="TSK_EMAIL_FROM" columnName="Address" required="yes" />
<AttributeName attributename="null" columnName="Comment" required="no" />
<AttributeName attributename="TSK_SUBJECT" columnName="Subject" required="yes" />
<AttributeName attributename="TSK_EMAIL_CONTENT_PLAIN" columnName=" Summary" required="yes" />
<AttributeName attributename="TSK_READ_STATUS" columnName=" Read?" required="yes" />
<AttributeName attributename="TSK_FLAG" columnName=" Flagged?" required="yes" />
<AttributeName attributename="TSK_ISDELETED" columnName=" Deleted" required="yes" />
<AttributeName attributename="null" columnName=" Mailbox" required="no" />
<AttributeName attributename="TSK_EMAIL_CONTENT_PLAIN" columnName="Summary" required="yes" />
<AttributeName attributename="TSK_READ_STATUS" columnName="Read?" required="yes" />
<AttributeName attributename="TSK_FLAG" columnName="Flagged?" required="yes" />
<AttributeName attributename="TSK_ISDELETED" columnName="Deleted" required="yes" />
<AttributeName attributename="null" columnName="Mailbox" required="no" />
</ArtifactName>
</FileName>
-->
@ -596,10 +596,10 @@
<FileName filename="Notifications.tsv" description="iOS Notificatons">
<ArtifactName artifactname="TSK_PROG_NOTIFICATIONS" comment="iOS Notificatons">
<AttributeName attributename="TSK_DATETIME" columnName="Creation Time" required="yes" />
<AttributeName attributename="TSK_PROG_NAME" columnName=" Bundle" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName=" Title[Subtitle]" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName=" Message" required="yes" />
<AttributeName attributename="null" columnName=" Other Details" required="no" />
<AttributeName attributename="TSK_PROG_NAME" columnName="Bundle" required="yes" />
<AttributeName attributename="TSK_TITLE" columnName="Title[Subtitle]" required="yes" />
<AttributeName attributename="TSK_VALUE" columnName="Message" required="yes" />
<AttributeName attributename="null" columnName="Other Details" required="no" />
</ArtifactName>
</FileName>
@ -651,7 +651,7 @@
<FileName filename="Powerlog Lightning Connector.tsv" description="Powerlog Lightning Connector Status">
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Powerlog Lightning Connector Status">
<AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Accesory Power Mode" required="yes" />
<AttributeName attributename="null" columnName="Accesory Power Mode" required="yes" />
<AttributeName attributename="null" columnName="Original Lightnint Connector Timestamp" required="no" />
<AttributeName attributename="null" columnName="Offset Timestamp" required="no" />
<AttributeName attributename="null" columnName="Table ID" required="no" />
@ -680,7 +680,7 @@
<ArtifactName artifactname="TSK_USER_DEVICE_EVENT" comment="Powerlog Torch">
<AttributeName attributename="TSK_DATETIME" columnName="Adjusted Timestamp" required="yes" />
<AttributeName attributename="null" columnName="Bundle ID" required="no" />
<AttributeName attributename="TSK_USER_DEVICE_EVENT_TYPE" columnName="Status" required="yes" />
<AttributeName attributename="null" columnName="Status" required="yes" />
<AttributeName attributename="null" columnName="Original Torch Timestamp" required="no" />
<AttributeName attributename="null" columnName="Offset Timestamp" required="no" />
<AttributeName attributename="null" columnName="Time Offset" required="no" />
@ -705,7 +705,7 @@
<AttributeName attributename="TSK_PROG_NAME" columnName="App Name" required="yes" />
<AttributeName attributename="null" columnName="App Executable Name" required="no" />
<AttributeName attributename="TSK_PATH" columnName="Bundle ID" required="yes" />
<AttributeName attributename="TSK_BUILD_VERSION" columnName="App Build Version" required="yes" />
<AttributeName attributename="null" columnName="App Build Version" required="yes" />
<AttributeName attributename="TSK_VERSION" columnName="App Bundle Version" required="yes" />
<AttributeName attributename="null" columnName="App TYpe" required="no" />
<AttributeName attributename="null" columnName="App Deleted Date" required="no" />

View File

@ -109,7 +109,10 @@ final class YaraIngestHelper {
}
/**
*
* Scan the given AbstractFile for yara rule matches from the rule sets in
* the given directory creating a blackboard artifact for each matching
* rule.
*
* @param file The Abstract File being processed.
* @param baseRuleSetDirectory Base directory of the compiled rule sets.
* @param localFile Local copy of file.
@ -138,8 +141,8 @@ final class YaraIngestHelper {
* Scan the given file byte array for rule matches using the YaraJNIWrapper
* API.
*
* @param fileBytes
* @param ruleSetDirectory
* @param fileBytes An array of the file data.
* @param ruleSetDirectory Base directory of the compiled rule sets.
*
* @return List of rules that match from the given file from the given rule
* set. Empty list is returned if no matches where found.
@ -158,6 +161,17 @@ final class YaraIngestHelper {
return matchingRules;
}
/**
* Scan the given file for rules that match from the given rule set directory.
*
* @param scanFile Locally stored file to scan.
* @param ruleSetDirectory Base directory of the compiled rule sets.
* @param timeout YARA Scanner timeout value.
*
* @return List of matching rules, if none were found the list will be empty.
*
* @throws YaraWrapperException
*/
private static List<String> scanFileForMatch(File scanFile, File ruleSetDirectory, int timeout) throws YaraWrapperException {
List<String> matchingRules = new ArrayList<>();
@ -228,7 +242,7 @@ final class YaraIngestHelper {
ProcessBuilder builder = new ProcessBuilder(commandList);
try {
int result = ExecUtil.execute(builder);
if(result != 0) {
if (result != 0) {
throw new IngestModuleException(String.format("Failed to compile Yara rules file %s. Compile error %d", file.toString(), result));
}
} catch (SecurityException | IOException ex) {
@ -249,7 +263,7 @@ final class YaraIngestHelper {
private static List<RuleSet> getRuleSetsForNames(List<String> names) {
List<RuleSet> ruleSetList = new ArrayList<>();
RuleSetManager manager = new RuleSetManager();
RuleSetManager manager = RuleSetManager.getInstance();
for (RuleSet set : manager.getRuleSetList()) {
if (names.contains(set.getName())) {
ruleSetList.add(set);

View File

@ -25,8 +25,10 @@ import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.ingest.FileIngestModule;
import org.sleuthkit.autopsy.ingest.IngestModuleFactory;
import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
import org.sleuthkit.autopsy.modules.yara.ui.YaraGlobalSettingsPanel;
import org.sleuthkit.autopsy.modules.yara.ui.YaraIngestSettingsPanel;
/**
@ -63,7 +65,7 @@ public class YaraIngestModuleFactory extends IngestModuleFactoryAdapter {
@Override
public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) {
return new YaraIngestSettingsPanel((YaraIngestJobSettings)settings);
return new YaraIngestSettingsPanel((YaraIngestJobSettings) settings);
}
@Override
@ -89,4 +91,16 @@ public class YaraIngestModuleFactory extends IngestModuleFactoryAdapter {
static String getModuleName() {
return Bundle.Yara_Module_Name();
}
@Override
public boolean hasGlobalSettingsPanel() {
return true;
}
@Override
public IngestModuleGlobalSettingsPanel getGlobalSettingsPanel() {
YaraGlobalSettingsPanel globalOptionsPanel = new YaraGlobalSettingsPanel();
globalOptionsPanel.load();
return globalOptionsPanel;
}
}

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.modules.yara.rules;
import java.io.File;
import java.io.Serializable;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -69,7 +70,15 @@ public class RuleSet implements Comparable<RuleSet>, Serializable {
* @return List of Files in current directory.
*/
public List<File> getRuleFiles() {
return Arrays.asList(path.toFile().listFiles());
List<File> fileList = new ArrayList<>();
if(path.toFile().exists()) {
File[] fileArray = path.toFile().listFiles();
if(fileArray != null) {
fileList.addAll(Arrays.asList(fileArray));
}
}
return fileList;
}
@Override

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2020 Basis Technology Corp.
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,11 +18,14 @@
*/
package org.sleuthkit.autopsy.modules.yara.rules;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import javax.swing.SwingUtilities;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
/**
@ -34,6 +37,54 @@ public class RuleSetManager {
private final static String BASE_FOLDER = "yara";
private final static String RULE_SET_FOLDER = "ruleSets";
/**
* Rule Set Property names.
*/
public final static String RULE_SET_ADDED = "YARARuleSetAdded";
public final static String RULE_SET_DELETED = "YARARuleSetDeleted";
private final PropertyChangeSupport changeSupport;
private static RuleSetManager instance;
/**
* Private constructor for this singleton.
*/
private RuleSetManager() {
changeSupport = new PropertyChangeSupport(this);
}
/**
* Returns the instance of this manager class.
*
* @return
*/
public synchronized static RuleSetManager getInstance() {
if (instance == null) {
instance = new RuleSetManager();
}
return instance;
}
/**
* Adds a property change listener to the manager.
*
* @param listener Listener to be added.
*/
public static void addPropertyChangeListener(PropertyChangeListener listener) {
getInstance().getChangeSupport().addPropertyChangeListener(listener);
}
/**
* Remove a property change listener from this manager.
*
* @param listener Listener to be added.
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
getInstance().getChangeSupport().removePropertyChangeListener(listener);
}
/**
* Create a new Yara rule set with the given set name.
*
@ -43,12 +94,11 @@ public class RuleSetManager {
*
* @throws RuleSetException RuleSet with given name already exists.
*/
public RuleSet createRuleSet(String name) throws RuleSetException {
if(name == null || name.isEmpty()) {
throw new RuleSetException("YARA rule set name cannot be null or empty string" );
public synchronized RuleSet createRuleSet(String name) throws RuleSetException {
if (name == null || name.isEmpty()) {
throw new RuleSetException("YARA rule set name cannot be null or empty string");
}
if (isRuleSetExists(name)) {
throw new RuleSetException(String.format("Yara rule set with name %s already exits.", name));
}
@ -58,7 +108,42 @@ public class RuleSetManager {
setPath.toFile().mkdir();
return new RuleSet(name, setPath);
RuleSet newSet = new RuleSet(name, setPath);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
getChangeSupport().firePropertyChange(RULE_SET_ADDED, null, newSet);
}
});
return newSet;
}
/**
* Deletes an existing RuleSet.
*
* @param ruleSet RuleSet to be deleted.
*
* @throws RuleSetException
*/
public synchronized void deleteRuleSet(RuleSet ruleSet) throws RuleSetException {
if (ruleSet == null) {
throw new RuleSetException("YARA rule set name cannot be null or empty string");
}
if (!isRuleSetExists(ruleSet.getName())) {
throw new RuleSetException(String.format("A YARA rule set with name %s does not exits.", ruleSet.getName()));
}
deleteDirectory(ruleSet.getPath().toFile());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
getChangeSupport().firePropertyChange(RULE_SET_DELETED, ruleSet, null);
}
});
}
/**
@ -66,7 +151,7 @@ public class RuleSetManager {
*
* @return
*/
public List<RuleSet> getRuleSetList() {
public synchronized List<RuleSet> getRuleSetList() {
List<RuleSet> ruleSets = new ArrayList<>();
Path basePath = getRuleSetPath();
@ -86,7 +171,7 @@ public class RuleSetManager {
*
* @return True if the rule set exist.
*/
public boolean isRuleSetExists(String name) {
public synchronized boolean isRuleSetExists(String name) {
Path basePath = getRuleSetPath();
Path setPath = Paths.get(basePath.toString(), name);
@ -110,4 +195,30 @@ public class RuleSetManager {
return basePath;
}
/**
* Returns the PropertyChangeSupport instance.
*
* @return PropertyChangeSupport instance.
*/
private PropertyChangeSupport getChangeSupport() {
return changeSupport;
}
/**
* Recursively delete the given directory and its children.
*
* @param directoryToBeDeleted
*
* @return True if the delete was successful.
*/
private boolean deleteDirectory(File directoryToBeDeleted) {
File[] allContents = directoryToBeDeleted.listFiles();
if (allContents != null) {
for (File file : allContents) {
deleteDirectory(file);
}
}
return directoryToBeDeleted.delete();
}
}

View File

@ -10,6 +10,7 @@ RuleSetDetailsPanel.setDetailsLabel.text=Set Details
RuleSetDetailsPanel.openFolderButton.text=Open Folder
RuleSetPanel.descriptionField.text=This module allows you to find files the match Yara rules. Each set has a list of Yara rule files. A file need only match one rule in the set to be found.
RuleSetDetailsPanel.openLabel.text=Place rule files in the set's folder. They will be compiled before use.
YARA_Global_Settings_Panel_Title=YARA Options
YaraIngestSettingsPanel.border.title=Select YARA rule sets to enable during ingest:
YaraIngestSettingsPanel.allFilesButton.text=All Files
YaraIngestSettingsPanel.allFilesButton.toolTipText=
@ -21,3 +22,8 @@ YaraRuleSetOptionPanel_badName_msg=Rule set name {0} already exists.\nRule set n
YaraRuleSetOptionPanel_badName_title=Create Rule Set
YaraRuleSetOptionPanel_new_rule_set_name_msg=Supply a new unique rule set name:
YaraRuleSetOptionPanel_new_rule_set_name_title=Rule Set Name
# {0} - rule set name
YaraRuleSetOptionPanel_rule_set_delete=Unable to delete the selected YARA rule set {0}.\nRule set may have already been removed.
# {0} - rule set name
YaraRuleSetOptionPanel_RuleSet_Missing=The folder for the selected YARA rule set, {0}, no longer exists.
YaraRuleSetOptionPanel_RuleSet_Missing_title=Folder removed

View File

@ -99,10 +99,6 @@
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="refreshButtonActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="2" gridY="5" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="14" weightX="0.0" weightY="0.0"/>

View File

@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.modules.yara.ui;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Graphics;
import java.io.File;
import java.io.IOException;
import java.util.List;
@ -58,7 +57,7 @@ public class RuleSetDetailsPanel extends javax.swing.JPanel {
fileList.setCellRenderer(new FileRenderer());
openFolderButton.setEnabled(false);
scrollPane.setViewportView(fileList);
refreshButton.setEnabled(false);
}
/**
@ -82,6 +81,7 @@ public class RuleSetDetailsPanel extends javax.swing.JPanel {
}
openFolderButton.setEnabled(ruleSet != null);
refreshButton.setEnabled(ruleSet != null);
}
/**
@ -120,7 +120,7 @@ public class RuleSetDetailsPanel extends javax.swing.JPanel {
openFolderButton = new javax.swing.JButton();
openLabel = new javax.swing.JLabel();
scrollPane = new javax.swing.JScrollPane();
javax.swing.JButton refreshButton = new javax.swing.JButton();
refreshButton = new javax.swing.JButton();
setLayout(new java.awt.GridBagLayout());
@ -223,6 +223,7 @@ public class RuleSetDetailsPanel extends javax.swing.JPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton openFolderButton;
private javax.swing.JLabel openLabel;
private javax.swing.JButton refreshButton;
private javax.swing.JScrollPane scrollPane;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
</Form>

View File

@ -0,0 +1,85 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2020 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.modules.yara.ui;
import java.awt.BorderLayout;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
/**
* YARA global settings panel.
*/
public class YaraGlobalSettingsPanel extends IngestModuleGlobalSettingsPanel implements OptionsPanel {
private static final long serialVersionUID = 1L;
private final YaraRuleSetOptionPanel panel = new YaraRuleSetOptionPanel();
@Messages({
"YARA_Global_Settings_Panel_Title=YARA Options"
})
/**
* Creates new form YaraGlobalSettingsPanel
*/
public YaraGlobalSettingsPanel() {
initComponents();
addOptionPanel();
}
@Override
public void saveSettings() {
}
@Override
public void store() {
}
@Override
public void load() {
if (panel != null) {
panel.updatePanel();
}
}
/**
* Add the YaraRuleSetOptionPanel to this panel.
*/
private void addOptionPanel() {
add(panel, BorderLayout.CENTER);
setName(Bundle.YARA_Global_Settings_Panel_Title());
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
setLayout(new java.awt.BorderLayout());
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
}

View File

@ -18,6 +18,8 @@
*/
package org.sleuthkit.autopsy.modules.yara.ui;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
@ -49,24 +51,42 @@ public class YaraIngestSettingsPanel extends IngestModuleIngestJobSettingsPanel
checkboxList = new CheckBoxJList<>();
scrollPane.setViewportView(checkboxList);
}
/**
* Constructs a new panel with the given JobSetting objects.
*
* @param settings Ingest job settings.
*/
public YaraIngestSettingsPanel(YaraIngestJobSettings settings) {
this();
List<String> setNames = settings.getSelectedRuleSetNames();
checkboxList.setModel(listModel);
checkboxList.setOpaque(false);
RuleSetManager manager = new RuleSetManager();
List<RuleSet> ruleSetList = manager.getRuleSetList();
List<RuleSet> ruleSetList = RuleSetManager.getInstance().getRuleSetList();
for (RuleSet set : ruleSetList) {
RuleSetListItem item = new RuleSetListItem(set);
item.setChecked(setNames.contains(set.getName()));
listModel.addElement(item);
}
allFilesButton.setSelected(!settings.onlyExecutableFiles());
executableFilesButton.setSelected(settings.onlyExecutableFiles());
RuleSetManager.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
switch (evt.getPropertyName()) {
case RuleSetManager.RULE_SET_ADDED:
handleRuleSetAdded((RuleSet) evt.getNewValue());
break;
case RuleSetManager.RULE_SET_DELETED:
handleRuleSetDeleted((RuleSet) evt.getOldValue());
break;
}
}
});
}
@Override
@ -84,6 +104,36 @@ public class YaraIngestSettingsPanel extends IngestModuleIngestJobSettingsPanel
return new YaraIngestJobSettings(selectedRules, executableFilesButton.isSelected());
}
/**
* Handle the addition of a new Rule Set.
*
* @param ruleSet
*/
private void handleRuleSetAdded(RuleSet ruleSet) {
if (ruleSet == null) {
return;
}
RuleSetListItem item = new RuleSetListItem(ruleSet);
listModel.addElement(item);
}
/**
* Handle the removal of the rule set.
*
* @param ruleSet
*/
private void handleRuleSetDeleted(RuleSet ruleSet) {
Enumeration<RuleSetListItem> enumeration = listModel.elements();
while (enumeration.hasMoreElements()) {
RuleSetListItem item = enumeration.nextElement();
if (item.getDisplayName().equals(ruleSet.getName())) {
listModel.removeElement(item);
return;
}
}
}
/**
* RuleSet wrapper class for Checkbox JList model.
*/

View File

@ -42,6 +42,14 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="viewportPanel">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[1000, 127]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[1020, 400]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>

View File

@ -20,13 +20,12 @@ package org.sleuthkit.autopsy.modules.yara.ui;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.modules.yara.rules.RuleSet;
import org.sleuthkit.autopsy.modules.yara.rules.RuleSetException;
import org.sleuthkit.autopsy.modules.yara.rules.RuleSetManager;
@ -41,16 +40,12 @@ public class YaraRuleSetOptionPanel extends javax.swing.JPanel {
private static final Logger logger = Logger.getLogger(YaraRuleSetOptionPanel.class.getName());
private final RuleSetManager manager;
/**
* Creates new form YaraRuleSetOptionPanel
*/
public YaraRuleSetOptionPanel() {
initComponents();
manager = new RuleSetManager();
ruleSetPanel.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
@ -77,25 +72,41 @@ public class YaraRuleSetOptionPanel extends javax.swing.JPanel {
* Update the panel with the current rule set.
*/
void updatePanel() {
ruleSetPanel.addSetList(manager.getRuleSetList());
ruleSetPanel.addSetList(RuleSetManager.getInstance().getRuleSetList());
}
@Messages({
"# {0} - rule set name",
"YaraRuleSetOptionPanel_RuleSet_Missing=The folder for the selected YARA rule set, {0}, no longer exists.",
"YaraRuleSetOptionPanel_RuleSet_Missing_title=Folder removed",
})
/**
* Handle the change in rule set selection. Update the detail panel with the
* selected rule.
*/
private void handleSelectionChange() {
ruleSetDetailsPanel.setRuleSet(ruleSetPanel.getSelectedRule());
RuleSet ruleSet = ruleSetPanel.getSelectedRule();
if(ruleSet != null && !ruleSet.getPath().toFile().exists()) {
ruleSetDetailsPanel.setRuleSet(null);
ruleSetPanel.removeRuleSet(ruleSet);
JOptionPane.showMessageDialog(this,
Bundle.YaraRuleSetOptionPanel_RuleSet_Missing(ruleSet.getName()),
Bundle.YaraRuleSetOptionPanel_RuleSet_Missing_title(),
JOptionPane.ERROR_MESSAGE);
} else {
ruleSetDetailsPanel.setRuleSet(ruleSet);
}
}
@NbBundle.Messages({
@Messages({
"YaraRuleSetOptionPanel_new_rule_set_name_msg=Supply a new unique rule set name:",
"YaraRuleSetOptionPanel_new_rule_set_name_title=Rule Set Name",
"# {0} - rule set name",
"YaraRuleSetOptionPanel_badName_msg=Rule set name {0} already exists.\nRule set names must be unique.",
"YaraRuleSetOptionPanel_badName_title=Create Rule Set",
"YaraRuleSetOptionPanel_badName2_msg=Rule set is invalid.\nRule set names must be non-empty string and unique.",
})
"YaraRuleSetOptionPanel_badName2_msg=Rule set is invalid.\nRule set names must be non-empty string and unique.",})
/**
* Handle the new rule set action. Prompt the user for a rule set name,
* create the new set and update the rule set list.
@ -105,16 +116,21 @@ public class YaraRuleSetOptionPanel extends javax.swing.JPanel {
Bundle.YaraRuleSetOptionPanel_new_rule_set_name_msg(),
Bundle.YaraRuleSetOptionPanel_new_rule_set_name_title());
if(value == null || value.isEmpty()) {
// User hit cancel.
if(value == null) {
return;
}
if (value.isEmpty()) {
JOptionPane.showMessageDialog(this,
Bundle.YaraRuleSetOptionPanel_badName2_msg(),
Bundle.YaraRuleSetOptionPanel_badName_title(),
JOptionPane.ERROR_MESSAGE);
return;
}
try {
ruleSetPanel.addRuleSet(manager.createRuleSet(value));
ruleSetPanel.addRuleSet(RuleSetManager.getInstance().createRuleSet(value));
} catch (RuleSetException ex) {
JOptionPane.showMessageDialog(this,
Bundle.YaraRuleSetOptionPanel_badName_msg(value),
@ -124,31 +140,29 @@ public class YaraRuleSetOptionPanel extends javax.swing.JPanel {
}
}
@Messages({
"# {0} - rule set name",
"YaraRuleSetOptionPanel_rule_set_delete=Unable to delete the selected YARA rule set {0}.\nRule set may have already been removed."
})
/**
* Handle the delete rule action. Delete the rule set and update the the
* rule set list.
*/
private void handleDeleteRuleSet() {
RuleSet ruleSet = ruleSetPanel.getSelectedRule();
ruleSetPanel.removeRuleSet(ruleSet);
deleteDirectory(ruleSet.getPath().toFile());
}
/**
* Recursively delete the given directory and its children.
*
* @param directoryToBeDeleted
*
* @return True if the delete was successful.
*/
private boolean deleteDirectory(File directoryToBeDeleted) {
File[] allContents = directoryToBeDeleted.listFiles();
if (allContents != null) {
for (File file : allContents) {
deleteDirectory(file);
if (ruleSet != null) {
try {
RuleSetManager.getInstance().deleteRuleSet(ruleSet);
} catch (RuleSetException ex) {
JOptionPane.showMessageDialog(this,
Bundle.YaraRuleSetOptionPanel_rule_set_delete(ruleSet.getName()),
Bundle.YaraRuleSetOptionPanel_badName_title(),
JOptionPane.ERROR_MESSAGE);
logger.log(Level.WARNING, String.format("Failed to delete YARA rule set %s", ruleSet.getName()), ex);
}
ruleSetPanel.removeRuleSet(ruleSet);
}
return directoryToBeDeleted.delete();
}
/**
@ -172,6 +186,8 @@ public class YaraRuleSetOptionPanel extends javax.swing.JPanel {
scrollPane.setBorder(null);
viewportPanel.setMinimumSize(new java.awt.Dimension(1000, 127));
viewportPanel.setPreferredSize(new java.awt.Dimension(1020, 400));
viewportPanel.setLayout(new java.awt.GridBagLayout());
separator.setOrientation(javax.swing.SwingConstants.VERTICAL);

View File

@ -1348,7 +1348,7 @@ class TableReportGenerator {
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH)));
columns.add(new AttributeColumn(NbBundle.getMessage(this.getClass(), "ReportGenerator.artTableColHdr.dateTime"),
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME)));
new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED )));
attributeTypeSet.remove(new Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_ID));
} else if (BlackboardArtifact.ARTIFACT_TYPE.TSK_INSTALLED_PROG.getTypeID() == artifactTypeId) {

View File

@ -390,6 +390,9 @@ public class HTMLReport implements TableReportModule {
case TSK_WEB_CATEGORIZATION:
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/domain-16.png"); //NON-NLS
break;
case TSK_YARA_HIT:
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/images/yara_16.png"); //NON-NLS
break;
default:
logger.log(Level.WARNING, "useDataTypeIcon: unhandled artifact type = {0}", dataType); //NON-NLS
in = getClass().getResourceAsStream("/org/sleuthkit/autopsy/report/images/star.png"); //NON-NLS

View File

@ -56,11 +56,6 @@ public class TaskRetryUtil {
* Constructs an object that encapsulates the specification of a task
* attempt for the attemptTask() utility. The attempt will have neither
* a delay nor a time out.
*
* @param delay The delay before the task should be attempted,
* may be zero or any positive integer.
* @param delayTimeUnit The time unit for the delay before the task
* should be attempted.
*/
public TaskAttempt() {
this.delay = 0L;
@ -168,7 +163,7 @@ public class TaskRetryUtil {
* each attempt and an optional timeout for each attempt. If an attempt
* times out, that particular attempt task will be cancelled.
*
* @param <T> The return type of the task.
* @tparam T The return type of the task.
* @param task The task.
* @param attempts The defining details for each attempt of the task.
* @param executor The scheduled task executor to be used to attempt the

View File

@ -26,7 +26,7 @@ import com.google.common.annotations.Beta;
* and should have a class annotation of '(at)ServiceProvider(service =
* DomainCategoryProvider.class)'.
*
* NOTE: The @SuppressWarnings("try") on the class is to suppress warnings
* NOTE: The (at)SuppressWarnings("try") on the class is to suppress warnings
* relating to the fact that the close method can throw an InterruptedException
* since Exception can encompass the InterruptedException. See the following
* github issue and bugs for more information:

View File

@ -256,7 +256,7 @@ public class RecentFilesSummaryTest {
*/
private BlackboardArtifact getRecentDocumentArtifact(DataSource ds, long artifactId, Long dateTime, String path) {
return getArtifact(ds, artifactId, ARTIFACT_TYPE.TSK_RECENT_OBJECT, Arrays.asList(
Pair.of(ATTRIBUTE_TYPE.TSK_DATETIME, dateTime),
Pair.of(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED , dateTime),
Pair.of(ATTRIBUTE_TYPE.TSK_PATH, path)
));
}

View File

@ -284,7 +284,11 @@ class Ingester {
//Make a SolrInputDocument out of the field map
SolrInputDocument updateDoc = new SolrInputDocument();
for (String key : fields.keySet()) {
updateDoc.addField(key, Chunker.sanitize((String)fields.get(key)).toString());
if (fields.get(key).getClass() == String.class) {
updateDoc.addField(key, Chunker.sanitize((String)fields.get(key)).toString());
} else {
updateDoc.addField(key, fields.get(key));
}
}
try {

View File

@ -62,8 +62,12 @@ class LanguageSpecificContentIndexingHelper {
//Make a SolrInputDocument out of the field map
SolrInputDocument updateDoc = new SolrInputDocument();
for (String key : fields.keySet()) {
updateDoc.addField(key, Chunker.sanitize((String)fields.get(key)).toString());
}
if (fields.get(key).getClass() == String.class) {
updateDoc.addField(key, Chunker.sanitize((String)fields.get(key)).toString());
} else {
updateDoc.addField(key, fields.get(key));
}
}
try {
updateDoc.setField(Server.Schema.ID.toString(), Chunker.sanitize(MiniChunkHelper.getChunkIdString(baseChunkID)).toString());

View File

@ -1013,16 +1013,34 @@ public class Server {
"# {0} - colelction name", "Server.deleteCore.exception.msg=Failed to delete Solr colelction {0}",})
void deleteCollection(String coreName, CaseMetadata metadata) throws KeywordSearchServiceException, KeywordSearchModuleException {
try {
IndexingServerProperties properties = getMultiUserServerProperties(metadata.getCaseDirectory());
HttpSolrClient solrServer = getSolrClient("http://" + properties.getHost() + ":" + properties.getPort() + "/solr");
connectToSolrServer(solrServer);
CollectionAdminRequest.Delete deleteCollectionRequest = CollectionAdminRequest.deleteCollection(coreName);
CollectionAdminResponse response = deleteCollectionRequest.process(solrServer);
if (response.isSuccess()) {
logger.log(Level.INFO, "Deleted collection {0}", coreName); //NON-NLS
HttpSolrClient solrServer;
if (metadata.getCaseType() == CaseType.SINGLE_USER_CASE) {
solrServer = getSolrClient("http://localhost:" + localSolrServerPort + "/solr"); //NON-NLS
CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, solrServer);
if (null != response.getCoreStatus(coreName).get("instanceDir")) { //NON-NLS
/*
* Send a core unload request to the Solr server, with the
* parameter set that request deleting the index and the
* instance directory (deleteInstanceDir = true). Note that
* this removes everything related to the core on the server
* (the index directory, the configuration files, etc.), but
* does not delete the actual Solr text index because it is
* currently stored in the case directory.
*/
org.apache.solr.client.solrj.request.CoreAdminRequest.unloadCore(coreName, true, true, solrServer);
}
} else {
logger.log(Level.WARNING, "Unable to delete collection {0}", coreName); //NON-NLS
IndexingServerProperties properties = getMultiUserServerProperties(metadata.getCaseDirectory());
solrServer = getSolrClient("http://" + properties.getHost() + ":" + properties.getPort() + "/solr");
connectToSolrServer(solrServer);
CollectionAdminRequest.Delete deleteCollectionRequest = CollectionAdminRequest.deleteCollection(coreName);
CollectionAdminResponse response = deleteCollectionRequest.process(solrServer);
if (response.isSuccess()) {
logger.log(Level.INFO, "Deleted collection {0}", coreName); //NON-NLS
} else {
logger.log(Level.WARNING, "Unable to delete collection {0}", coreName); //NON-NLS
}
}
} catch (SolrServerException | IOException ex) {
// We will get a RemoteSolrException with cause == null and detailsMessage

View File

@ -238,8 +238,11 @@ public class SolrSearchService implements KeywordSearchService, AutopsyService {
} catch (KeywordSearchModuleException ex) {
throw new KeywordSearchServiceException(Bundle.SolrSearchService_exceptionMessage_unableToDeleteCollection(index.getIndexName()), ex);
}
if (!FileUtil.deleteDir(new File(index.getIndexPath()).getParentFile())) {
throw new KeywordSearchServiceException(Bundle.SolrSearchService_exceptionMessage_failedToDeleteIndexFiles(index.getIndexPath()));
File indexDir = new File(index.getIndexPath()).getParentFile();
if (indexDir.exists()) {
if (!FileUtil.deleteDir(indexDir)) {
throw new KeywordSearchServiceException(Bundle.SolrSearchService_exceptionMessage_failedToDeleteIndexFiles(index.getIndexPath()));
}
}
}
}

View File

@ -1,36 +1,76 @@
---------------- VERSION 4.17.0 --------------
GUI:
Expanded the Data Source Summary panel to show recent activity, past cases, analysis results, etc. Also made this available from the main UI when a data source is selected.
Expanded Discovery UI to support searching for and basic display of web domains. It collapses the various web artifacts into a single view.
---------------- VERSION 4.18.0 --------------
Keyword Search:
- A major upgrade from Solr 4 to Solr 8.6.3. Single user cases continue to use the embedded server.
Multi-user clusters need to install a new Solr 8 server and can now create a Solr cloud with multiple servers.
-- NOTE: Cases created with Autopsy 4.18 cannot be opened by previous versions of Autopsy. Autopsy 4.18 can open older cases though.
- Improved text indexing speed by not doing language detection on unknown file formats and unallocated space.
Domain Discovery:
- Added details view to Domain Discovery to show what web-based artifacts are associated with the selected domain.
- Updated the Domain Discovery grouping and sorting by options.
- Added basic domain categorization for webmail-based domains.
Content Viewers:
- Built more specialized viewers for web-based artifacts.
Data Source Summary:
- Added a “Geolocations” tab that shows what cities the data source was near (based on geolocation data).
- Added a “Timeline” tab that shows counts of events from the last 30 days the data source was used.
- Added navigation buttons to jump from the summary view to the main Autopsy UI (for example to go to the map).
Ingest Modules:
Added iOS Analyzer module based on iLEAPP and a subset of its artifacts.
New Picture Analyzer module that does EXIF extraction and HEIC conversion. HEIC/HEIF images are converted to JPEGs that retain EXIF using ImageMagick (replaces the previous EXIF ingest module).
Added support for the latest version of Edge browser that is based on Chromium into Recent Activity. Other Chromium-based browsers are also supported.
Updated the rules that search Web History artifacts for search queries. Expanded module to support multiple search engines for ambiguous URLs.
Bluetooth pairing artifacts are created based on RegRipper output.
Prefetch artifacts record the full path of exes.
PhotoRec module allows you to include or exclude specific file types.
Upgraded to Tika 1.23.
- New YARA ingest module to flag files based on regular expression patterns.
- New “Android Analyzer (aLEAPP)” module based on aLEAPP. Previous “Android Analyzer” also still exists.
- Updated “iOS Analyzer (iLEAPP)” module to create more artifacts and work on disk images.
- Hash Database module will calculate SHA-256 hash in addition to MD5.
- Removed Interesting Item rule that flagged existence of Bitlocker (since it ships with Windows).
- Fixed a major bug in the PhotoRec module that could result in an incorrect file layout if the carved file spanned non-contiguous sectors.
- Fixed MBOX detection bug in Email module.
Performance:
Documents are added to Solr in batches instead of one by one.
More efficient queries to find WAL files for SQLite databases.
Use a local drive for temp files for multi-user cases instead of the shared folder.
Command Line
Command line support for report profiles.
Restored support for Windows file type association for opening a case in Autopsy by double clicking case metadata (.aut) file.
Better feedback for command line argument errors.
Reporting:
- Attachments from tagged messages are now included in a Portable Case.
Misc:
Updated versions of libvmdk, libvhdi, and libewf.
Persona UI fixes: Pre-populate account and changed order of New Persona dialog.
Streaming ingest support added to auto ingest.
Recent Activity module processes now use the global timeout.
Option to include Autopsy executable in portable case (Windows only.)
Upgraded to NetBeans 11 Rich Client Platform.
Added debug feature to save the stack trace on all threads.
- Added support for Ext4 inline data and sparse blocks (via TSK fix).
- Updated PostgreSQL JDBC driver to support any recent version of PostgreSQL for multi-user cases and PostgreSQL Central Repository.
- Added personas to the summary viewer in CVT.
- Handling of bad characters in auto ingest manifest files.
- Assorted small bug fixes.
---------------- VERSION 4.17.0 --------------
GUI:
- Expanded the Data Source Summary panel to show recent activity, past cases, analysis results, etc. Also made this available from the main UI when a data source is selected.
- Expanded Discovery UI to support searching for and basic display of web domains. It collapses the various web artifacts into a single view.
Ingest Modules:
- Added iOS Analyzer module based on iLEAPP and a subset of its artifacts.
- New Picture Analyzer module that does EXIF extraction and HEIC conversion. HEIC/HEIF images are converted to JPEGs that retain EXIF using ImageMagick (replaces the previous EXIF ingest module).
- Added support for the latest version of Edge browser that is based on Chromium into Recent Activity. Other Chromium-based browsers are also supported.
- Updated the rules that search Web History artifacts for search queries. Expanded module to support multiple search engines for ambiguous URLs.
- Bluetooth pairing artifacts are created based on RegRipper output.
- Prefetch artifacts record the full path of exes.
- PhotoRec module allows you to include or exclude specific file types.
- Upgraded to Tika 1.23.
Performance:
- Documents are added to Solr in batches instead of one by one.
- More efficient queries to find WAL files for SQLite databases.
- Use a local drive for temp files for multi-user cases instead of the shared folder.
Command Line
- Command line support for report profiles.
- Restored support for Windows file type association for opening a case in Autopsy by double clicking case metadata (.aut) file.
- Better feedback for command line argument errors.
Misc:
- Updated versions of libvmdk, libvhdi, and libewf.
- Persona UI fixes: Pre-populate account and changed order of New Persona dialog.
- Streaming ingest support added to auto ingest.
- Recent Activity module processes now use the global timeout.
- Option to include Autopsy executable in portable case (Windows only.)
- Upgraded to NetBeans 11 Rich Client Platform.
- Added debug feature to save the stack trace on all threads.
---------------- VERSION 4.16.0 --------------

View File

@ -45,7 +45,7 @@ import org.sleuthkit.autopsy.url.analytics.DomainCategory;
* messaging:
* https://www.raymond.cc/blog/list-of-web-messengers-for-your-convenience/
*
* NOTE: The @SuppressWarnings("try") on the class is to suppress warnings
* NOTE: The (at)SuppressWarnings("try") on the class is to suppress warnings
* relating to the fact that the close method can throw an InterruptedException
* since Exception can encompass the InterruptedException. See the following
* github issue and bugs for more information:

View File

@ -68,7 +68,6 @@ import java.util.Set;
import java.util.HashSet;
import static java.util.Locale.US;
import static java.util.TimeZone.getTimeZone;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
@ -98,7 +97,6 @@ import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
import org.sleuthkit.datamodel.Report;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskDataException;
/**
* Extract windows registry data using regripper. Runs two versions of
@ -539,7 +537,7 @@ class ExtractRegistry extends Extract {
//sometimes etime will be an empty string and therefore can not be parsed into a date
if (etime != null && !etime.isEmpty()) {
try {
mtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(etime).getTime();
mtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US).parse(etime).getTime();
String Tempdate = mtime.toString();
mtime = Long.valueOf(Tempdate) / MS_IN_SEC;
} catch (ParseException ex) {
@ -604,7 +602,7 @@ class ExtractRegistry extends Extract {
case "InstallDate": //NON-NLS
if (value != null && !value.isEmpty()) {
try {
installtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(value).getTime();
installtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US).parse(value).getTime();
String Tempdate = installtime.toString();
installtime = Long.valueOf(Tempdate) / MS_IN_SEC;
} catch (ParseException e) {
@ -783,7 +781,7 @@ class ExtractRegistry extends Extract {
try {
String mTimeAttr = artnode.getAttribute("mtime");
if (mTimeAttr != null && !mTimeAttr.isEmpty()) {
itemMtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(mTimeAttr).getTime(); //NON-NLS
itemMtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US).parse(mTimeAttr).getTime(); //NON-NLS
itemMtime /= MS_IN_SEC;
}
} catch (ParseException ex) {
@ -866,6 +864,8 @@ class ExtractRegistry extends Extract {
parentModuleName, sid));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
parentModuleName, homeDir));
newArtifacts.add(bbart);
} else {
//add attributes to existing artifact
BlackboardAttribute bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_NAME));
@ -878,10 +878,10 @@ class ExtractRegistry extends Extract {
if (bbattr == null) {
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
parentModuleName, homeDir));
}
}
}
bbart.addAttributes(bbattributes);
newArtifacts.add(bbart);
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error adding account artifact to blackboard.", ex); //NON-NLS
}
@ -1163,7 +1163,7 @@ class ExtractRegistry extends Extract {
Collection<BlackboardAttribute> getAttributesForAccount(Map<String, String> userInfo, List<String> groupList, boolean existingUser, AbstractFile regAbstractFile) throws ParseException {
Collection<BlackboardAttribute> bbattributes = new ArrayList<>();
SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'");
SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'", US);
regRipperTimeFormat.setTimeZone(getTimeZone("GMT"));
if (!existingUser) {
@ -1501,7 +1501,7 @@ class ExtractRegistry extends Extract {
}
Collection<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), adobeUsedTime));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, getName(), adobeUsedTime));
attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment));
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
if (bba != null) {
@ -1742,7 +1742,7 @@ class ExtractRegistry extends Extract {
String fileName = fileNameTokens[1];
Collection<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), docDate));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, getName(), docDate));
attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment));
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
if (bba != null) {
@ -1805,7 +1805,7 @@ class ExtractRegistry extends Extract {
}
Collection<BlackboardAttribute> attributes = new ArrayList<>();
attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), usedTime));
attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, getName(), usedTime));
attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment));
BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes);
if (bba != null) {

View File

@ -294,6 +294,7 @@ final class ExtractZoneIdentifier extends Extract {
private static final String REFERRER_URL = "ReferrerUrl"; //NON-NLS
private static final String HOST_URL = "HostUrl"; //NON-NLS
private static final String FAMILY_NAME = "LastWriterPackageFamilyName"; //NON-NLS
private static String fileName;
private final Properties properties = new Properties(null);
@ -307,6 +308,7 @@ final class ExtractZoneIdentifier extends Extract {
* @throws IOException
*/
ZoneIdentifierInfo(AbstractFile zoneFile) throws IOException {
fileName = zoneFile.getName();
properties.load(new ReadContentInputStream(zoneFile));
}
@ -318,8 +320,13 @@ final class ExtractZoneIdentifier extends Extract {
private int getZoneId() {
int zoneValue = -1;
String value = properties.getProperty(ZONE_ID);
if (value != null) {
zoneValue = Integer.parseInt(value);
try {
if (value != null) {
zoneValue = Integer.parseInt(value);
}
} catch (NumberFormatException ex) {
String message = String.format("Unable to parse Zone Id for File %s", fileName); //NON-NLS
LOG.log(Level.WARNING, message);
}
return zoneValue;

View File

@ -119,7 +119,7 @@ class RecentDocumentsByLnk extends Extract {
NbBundle.getMessage(this.getClass(),
"RecentDocumentsByLnk.parentModuleName.noSpace"),
Util.findID(dataSource, path)));
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME,
bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
NbBundle.getMessage(this.getClass(),
"RecentDocumentsByLnk.parentModuleName.noSpace"),
recentFile.getCrtime()));

View File

@ -1,5 +1,5 @@
<hr/>
<p><i>Copyright &#169; 2012-2020 Basis Technology. Generated on $date<br/>
<p><i>Copyright &#169; 2012-2021 Basis Technology. Generated on $date<br/>
This work is licensed under a
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>.
</i></p>

View File

@ -8,7 +8,7 @@ The Android Analyzer ingest module runs aLEAPP (https://github.com/abrignoni/aLE
\section aleapp_config Using the Module
Select the checkbox in the Ingest Modules settings screen to enable the Android Analzyer (ALEAPP) module. The module will run on .tar/.zip files found in a \ref ds_log "logical files data source" or a \ref ds_img disk image.
Select the checkbox in the Ingest Modules settings screen to enable the Android Analzyer (ALEAPP) module. The module will run on .tar/.zip files found in a \ref ds_log "logical files data source" or a \ref ds_img "disk image".
\section aleapp_results Seeing Results

View File

@ -24,7 +24,7 @@ The middle column displays each account, its device and type, and the number of
Selecting an account in the middle column will bring up the data for that account in the right hand column. There are four tabs that show information about the selected account.
<ul>
<li> The <b>Summary</b> tab displays counts of how many times the account has appeared in different data types in the top section. In the middle it displays the files this account was found in. If the \ref central_repo_page is enabled, the bottom section will show any other cases that contained this account.
<li> The <b>Summary</b> tab displays counts of how many times the account has appeared in different data types in the top section. In the middle it displays the files this account was found in. If the \ref central_repo_page is enabled, you can see if any \ref personas_page "personas" are associated with this account and whether any other cases that contained this account.
\image html cvt_summary_tab.png

View File

@ -32,7 +32,7 @@ The Types tab shows counts of different file types found in the data source.
\subsection ds_summary_user_activity User Activity
The User Activity tab shows the most recent results found in the data source.
The User Activity tab shows the most recent results found in the data source. You can right click on a row to navigate directly to the corresponding result.
\image html ds_summary_user_activity.png
@ -44,7 +44,7 @@ The Analysis tab shows the sets with the most results from the \ref hash_db_page
\subsection ds_summary_recent_files Recent Files
The Recent Files tab shows information on the most recent files opened and downloaded.
The Recent Files tab shows information on the most recent files opened and downloaded. You can right click on a row to navigate directly to the corresponding file or result.
\image html ds_summary_recent_files.png
@ -56,6 +56,18 @@ The Past Cases tab shows which cases had results or notable files in common with
Note that because these entries are based on the Interesting Items results created during ingest and not querying the central repository, they will not reflect any matches in cases processed after this case. For example, suppose we create Case A and ingest a data source with Device Z. If we make a new case Case B afterward and ingest a data source that also has Device Z, we would see Case A listed in this tab for Case B, but if we reopened Case A we would not see Case B listed unless ingest was run again.
\subsection ds_summary_geo Geolocation
The Geolocation tab uses the coordinates from geolocation results to find the nearest city for each and displays the most recent cities and most common cities. If the location is more than 150 km from a city then it will be displayed as "Unknown". The "View in Map" button under the recent cities table will open the \ref geolocation_page "Geolocation window" showing all waypoints for this data source with timestamps in the last 30 days. The "View in Map" button under the most common cities will show all waypoints for this data source.
\image html ds_summary_geo.png
\subsection ds_summary_timeline Timeline
The Timeline tab shows a simplified version of the \ref timeline_page "Timeline Viewer" for the selected data source. It will show events for the last 30 days of activity in the data source and give the first and last dates of activity. "File events" represent file creation, modification, access, and change. "Result events" represent the results from running ingest, such as the time a message was sent or when a URL was accessed. The "View in Timeline" button will open the main \ref timeline_page "Timeline Viewer".
\image html ds_summary_timeline.png
\subsection ds_summary_ingest_history Ingest History
The Ingest History tab shows which ingest modules have been run on the data source and the version of each module.

View File

@ -1,13 +1,13 @@
/*! \page drone_page Drone Analyzer
/*! \page drone_page DJI Drone Analyzer
[TOC]
\section drone_overview Overview
The Drone Analyzer module allows you to analyze files from a drone.
The DJI Drone Analyzer module allows you to analyze files from a drone.
Currently, the Drone Analyzer module works on images obtained from the internal SD card found in the following DJI drone models:
Currently, the DJI Drone Analyzer module works on images obtained from the internal SD card found in the following DJI drone models:
- Phantom 3
- Phantom 4
- Phantom 4 Pro
@ -20,7 +20,7 @@ The module will find DAT files and process them using DatCon (https://datfile.ne
\section drone_config Running the Module
To enable the Drone Analyzer ingest module select the checkbox in the \ref ingest_configure "Ingest Modules configuration screen".
To enable the DJI Drone Analyzer ingest module select the checkbox in the \ref ingest_configure "Ingest Modules configuration screen".
\section drone_results Viewing Results

View File

@ -1,5 +1,5 @@
<hr/>
<p><i>Copyright &#169; 2012-2020 Basis Technology. Generated on $date<br/>
<p><i>Copyright &#169; 2012-2021 Basis Technology. Generated on $date<br/>
This work is licensed under a
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/us/">Creative Commons Attribution-Share Alike 3.0 United States License</a>.
</i></p>

View File

@ -6,7 +6,7 @@
What Does It Do
========
The Hash Lookup Module calculates MD5 hash values for files and looks up hash values in a database to determine if the file is notable, known (in general), included in a specific set of files, or unknown.
The Hash Lookup Module calculates MD5 hash values for files and looks up hash values in a database to determine if the file is notable, known (in general), included in a specific set of files, or unknown. SHA-256 hashes are also calculated, though these will not be used in hash set lookups.
Configuration

View File

@ -8,7 +8,7 @@ The iOS Analyzer ingest module runs iLEAPP (https://github.com/abrignoni/iLEAPP)
\section ileapp_config Using the Module
Select the checkbox in the Ingest Modules settings screen to enable the IOS Analzyer (iLEAPP) module. In Autopsy 4.17.0 the module only runs on .tar/.zip files found in a \ref ds_log "logical files data source".
Select the checkbox in the Ingest Modules settings screen to enable the IOS Analzyer (iLEAPP) module. The module will run on .tar/.zip files found in a \ref ds_log "logical files data source" or a \ref ds_img "disk image".
\section ileapp_results Seeing Results

Some files were not shown because too many files have changed in this diff Show More