Merge branch 'develop' of https://github.com/sleuthkit/autopsy into 6516-take2-normalize-cr-account
# Conflicts: # Core/src/org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties-MERGED # Core/src/org/sleuthkit/autopsy/centralrepository/persona/CreatePersonaAccountDialog.java
@ -2,7 +2,7 @@ Manifest-Version: 1.0
|
||||
OpenIDE-Module: org.sleuthkit.autopsy.core/10
|
||||
OpenIDE-Module-Localizing-Bundle: org/sleuthkit/autopsy/core/Bundle.properties
|
||||
OpenIDE-Module-Layer: org/sleuthkit/autopsy/core/layer.xml
|
||||
OpenIDE-Module-Implementation-Version: 31
|
||||
OpenIDE-Module-Implementation-Version: 32
|
||||
OpenIDE-Module-Requires: org.openide.windows.WindowManager
|
||||
AutoUpdate-Show-In-Client: true
|
||||
AutoUpdate-Essential-Module: true
|
||||
|
@ -138,5 +138,5 @@ nbm.homepage=http://www.sleuthkit.org/
|
||||
nbm.module.author=Brian Carrier
|
||||
nbm.needs.restart=true
|
||||
source.reference.curator-recipes-2.8.0.jar=release/modules/ext/curator-recipes-2.8.0-sources.jar
|
||||
spec.version.base=10.19
|
||||
spec.version.base=10.20
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2019 Basis Technology Corp.
|
||||
* Copyright 2013-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -139,7 +139,7 @@ abstract class AddTagAction extends AbstractAction implements Presenter.Popup {
|
||||
if (!tagNamesMap.isEmpty()) {
|
||||
for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) {
|
||||
TagName tagName = entry.getValue();
|
||||
TagSet tagSet = tagName.getTagSet();
|
||||
TagSet tagSet = tagsManager.getTagSet(tagName);
|
||||
|
||||
// Show custom tags before predefined tags in the menu
|
||||
if (tagSet != null) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2018 Basis Technology Corp.
|
||||
* Copyright 2013-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -23,7 +23,6 @@ import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -36,7 +35,6 @@ import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.KeyStroke;
|
||||
import org.openide.util.Exceptions;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
@ -161,7 +159,7 @@ public class GetTagNameAndCommentDialog extends JDialog {
|
||||
tagNamesMap.entrySet().stream().map((entry) -> entry.getValue()).forEachOrdered((tagName) -> {
|
||||
TagSet tagSet = null;
|
||||
try {
|
||||
tagSet = tagName.getTagSet();
|
||||
tagSet = tagsManager.getTagSet(tagName);
|
||||
} catch (TskCoreException ex) {
|
||||
Logger.getLogger(GetTagNameAndCommentDialog.class
|
||||
.getName()).log(Level.SEVERE, "Failed to get tag set", ex); //NON-NLS
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2018 Basis Technology Corp.
|
||||
* Copyright 2018-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -131,7 +131,7 @@ abstract class ReplaceTagAction<T extends Tag> extends AbstractAction implements
|
||||
if (!tagNamesMap.isEmpty()) {
|
||||
for (Map.Entry<String, TagName> entry : tagNamesMap.entrySet()) {
|
||||
TagName tagName = entry.getValue();
|
||||
TagSet tagSet = tagName.getTagSet();
|
||||
TagSet tagSet = tagsManager.getTagSet(tagName);
|
||||
|
||||
// Show custom tags before predefined tags in the menu
|
||||
if (tagSet != null) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2011-2020 Basis Technology Corp.
|
||||
* Copyright 2013-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -300,6 +300,19 @@ public class TagsManager implements Closeable {
|
||||
return caseDb.getTaggingManager().getTagSets();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag set a tag name (tag definition) belongs to, if any.
|
||||
*
|
||||
* @param tagName The tag name.
|
||||
*
|
||||
* @return A TagSet object or null.
|
||||
*
|
||||
* @throws TskCoreException If there is an error querying the case database.
|
||||
*/
|
||||
public TagSet getTagSet(TagName tagName) throws TskCoreException {
|
||||
return caseDb.getTaggingManager().getTagSet(tagName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new TagSet to the case database. Tags will be ranked in the order
|
||||
* which they are passed to this method.
|
||||
|
@ -255,7 +255,7 @@ public class Persona {
|
||||
/**
|
||||
* Sets the comment of this persona.
|
||||
*
|
||||
* @param name The new comment.
|
||||
* @param comment The new comment.
|
||||
*
|
||||
* @throws CentralRepoException If there is an error.
|
||||
*/
|
||||
@ -313,7 +313,9 @@ public class Persona {
|
||||
/**
|
||||
* Modifies the confidence / justification of the given PersonaAccount
|
||||
*
|
||||
* @param account account to modify
|
||||
* @param account Account to modify.
|
||||
* @param confidence Level of confidence.
|
||||
* @param justification Justification.
|
||||
*
|
||||
* @throws CentralRepoException If there is an error in querying the
|
||||
* Personas table.
|
||||
@ -485,7 +487,9 @@ public class Persona {
|
||||
/**
|
||||
* Modifies the given alias.
|
||||
*
|
||||
* @param alias alias to modify
|
||||
* @param key Key for the alias to modify.
|
||||
* @param confidence Level of confidence.
|
||||
* @param justification Justification.
|
||||
*
|
||||
* @throws CentralRepoException If there is an error in querying the
|
||||
* Personas table.
|
||||
@ -535,7 +539,9 @@ public class Persona {
|
||||
/**
|
||||
* Modifies the given metadata.
|
||||
*
|
||||
* @param metadata metadata to modify
|
||||
* @param key Key for the metadata to modify.
|
||||
* @param confidence Level of confidence.
|
||||
* @param justification Justification.
|
||||
*
|
||||
* @throws CentralRepoException If there is an error in querying the
|
||||
* Personas table.
|
||||
|
@ -79,3 +79,4 @@ CreatePersonaAccountDialog.typeLbl.text=Type:
|
||||
CreatePersonaAccountDialog.identifierTextField.text=
|
||||
CreatePersonaAccountDialog.identiferLbl.text=Identifier:
|
||||
CreatePersonaAccountDialog.okBtn.text=OK
|
||||
PersonasTopComponent.introText.text=Personas represent an online identity. They span cases and are stored in the Central Repository based on accounts that were found in artifacts. You can create, edit, and delete personas here.
|
||||
|
@ -4,8 +4,10 @@ AddMetadataDialog_dup_Title=Metadata add failure
|
||||
AddMetadataDialog_empty_name_msg=A metadata entry cannot have an empty name or value.
|
||||
AddMetadataDialog_empty_name_Title=Missing field(s)
|
||||
CreatePersonaAccountDialog.title.text=Create Account
|
||||
CreatePersonaAccountDialog_dup_msg=An account with this identifier and type already exists.
|
||||
CreatePersonaAccountDialog_dup_Title=Account creation failure
|
||||
CreatePersonaAccountDialog_error_msg=Failed to create account.
|
||||
CreatePersonaAccountDialog_error_title=Account failure
|
||||
CreatePersonaAccountDialog_success_msg=Account added.
|
||||
CreatePersonaAccountDialog_success_title=Account added
|
||||
CreatePersonaAccountDialog_invalid_account_msg=Account identifier is not valid.
|
||||
CreatePersonaAccountDialog_invalid_account_Title=Invalid account identifier
|
||||
CTL_OpenPersonas=Personas
|
||||
@ -123,6 +125,7 @@ CreatePersonaAccountDialog.typeLbl.text=Type:
|
||||
CreatePersonaAccountDialog.identifierTextField.text=
|
||||
CreatePersonaAccountDialog.identiferLbl.text=Identifier:
|
||||
CreatePersonaAccountDialog.okBtn.text=OK
|
||||
PersonasTopComponent.introText.text=Personas represent an online identity. They span cases and are stored in the Central Repository based on accounts that were found in artifacts. You can create, edit, and delete personas here.
|
||||
PersonasTopComponent_delete_confirmation_msg=Are you sure you want to delete this persona?
|
||||
PersonasTopComponent_delete_confirmation_Title=Are you sure?
|
||||
PersonasTopComponent_delete_exception_msg=Failed to delete persona.
|
||||
|
@ -214,6 +214,8 @@ public class CreatePersonaAccountDialog extends JDialog {
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"CreatePersonaAccountDialog_error_title=Account failure",
|
||||
"CreatePersonaAccountDialog_error_msg=Failed to create account.",
|
||||
"CreatePersonaAccountDialog_invalid_account_Title=Invalid account identifier",
|
||||
"CreatePersonaAccountDialog_invalid_account_msg=Account identifier is not valid.",})
|
||||
private CentralRepoAccount createAccount(CentralRepoAccount.CentralRepoAccountType type, String identifier) {
|
||||
@ -224,10 +226,10 @@ public class CreatePersonaAccountDialog extends JDialog {
|
||||
ret = cr.getOrCreateAccount(type, identifier);
|
||||
}
|
||||
} catch (CentralRepoException e) {
|
||||
logger.log(Level.SEVERE, "Failed to access central repository", e);
|
||||
logger.log(Level.SEVERE, "Failed to create account", e);
|
||||
JOptionPane.showMessageDialog(this,
|
||||
Bundle.PersonaAccountDialog_get_types_exception_msg(),
|
||||
Bundle.PersonaAccountDialog_get_types_exception_Title(),
|
||||
Bundle.CreatePersonaAccountDialog_error_msg(),
|
||||
Bundle.CreatePersonaAccountDialog_error_title(),
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
} catch (InvalidAccountIDException e) {
|
||||
logger.log(Level.WARNING, "Invalid account identifier", e);
|
||||
@ -240,8 +242,9 @@ public class CreatePersonaAccountDialog extends JDialog {
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"CreatePersonaAccountDialog_dup_Title=Account creation failure",
|
||||
"CreatePersonaAccountDialog_dup_msg=An account with this identifier and type already exists.",})
|
||||
"CreatePersonaAccountDialog_success_title=Account added",
|
||||
"CreatePersonaAccountDialog_success_msg=Account added.",
|
||||
})
|
||||
private void okBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okBtnActionPerformed
|
||||
if (identifierTextField.getText().isEmpty()) {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
@ -256,6 +259,12 @@ public class CreatePersonaAccountDialog extends JDialog {
|
||||
String identifier = identifierTextField.getText();
|
||||
|
||||
if (createAccount(type, identifier) != null) {
|
||||
// show account created message
|
||||
JOptionPane.showMessageDialog(this,
|
||||
Bundle.CreatePersonaAccountDialog_success_msg(),
|
||||
Bundle.CreatePersonaAccountDialog_success_title(),
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
|
||||
dispose();
|
||||
}
|
||||
}//GEN-LAST:event_okBtnActionPerformed
|
||||
|
@ -1,26 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<!--
|
||||
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you 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.
|
||||
|
||||
-->
|
||||
|
||||
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<NonVisualComponents>
|
||||
<Component class="javax.swing.ButtonGroup" name="searchButtonGroup">
|
||||
@ -44,17 +23,52 @@
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jSplitPane1" alignment="0" pref="794" max="32767" attributes="0"/>
|
||||
<Component id="introTextScrollPane" alignment="0" max="32767" attributes="0"/>
|
||||
<Component id="mainSplitPane" alignment="0" pref="724" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jSplitPane1" alignment="0" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="1" attributes="0">
|
||||
<Component id="introTextScrollPane" min="-2" pref="49" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="mainSplitPane" pref="470" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Container class="javax.swing.JSplitPane" name="jSplitPane1">
|
||||
<Container class="javax.swing.JScrollPane" name="introTextScrollPane">
|
||||
<Properties>
|
||||
<Property name="verticalScrollBarPolicy" type="int" value="21"/>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JTextArea" name="introText">
|
||||
<Properties>
|
||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
|
||||
<Connection component="Form" name="getBackground" type="method"/>
|
||||
</Property>
|
||||
<Property name="columns" type="int" value="20"/>
|
||||
<Property name="lineWrap" type="boolean" value="true"/>
|
||||
<Property name="rows" type="int" value="5"/>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.introText.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="wrapStyleWord" type="boolean" value="true"/>
|
||||
<Property name="focusable" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JSplitPane" name="mainSplitPane">
|
||||
<Properties>
|
||||
<Property name="dividerLocation" type="int" value="400"/>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
@ -71,14 +85,7 @@
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jSeparator1" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="createBtn" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="editBtn" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="deleteBtn" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Component id="createButtonSeparator" max="32767" attributes="0"/>
|
||||
<Component id="resultsPane" pref="0" max="32767" attributes="0"/>
|
||||
<Component id="searchField" max="32767" attributes="0"/>
|
||||
<Group type="102" attributes="0">
|
||||
@ -88,8 +95,17 @@
|
||||
<EmptySpace max="32767" attributes="0"/>
|
||||
<Component id="searchBtn" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="createAccountBtn" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="createBtn" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="editBtn" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="deleteBtn" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
@ -109,7 +125,7 @@
|
||||
<Component id="searchBtn" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="resultsPane" pref="457" max="32767" attributes="0"/>
|
||||
<Component id="resultsPane" pref="300" max="32767" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="editBtn" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
@ -117,7 +133,7 @@
|
||||
<Component id="deleteBtn" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="jSeparator1" min="-2" pref="4" max="-2" attributes="0"/>
|
||||
<Component id="createButtonSeparator" min="-2" pref="4" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="createAccountBtn" min="-2" pref="32" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
@ -154,6 +170,13 @@
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="searchBtn">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.searchBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JScrollPane" name="resultsPane">
|
||||
<AuxValues>
|
||||
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
|
||||
@ -191,13 +214,6 @@
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="javax.swing.JButton" name="searchBtn">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.searchBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="createAccountBtn">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
@ -208,16 +224,6 @@
|
||||
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="createBtn">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.createBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="editBtn">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
@ -234,18 +240,34 @@
|
||||
<Property name="enabled" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JSeparator" name="jSeparator1">
|
||||
<Component class="javax.swing.JSeparator" name="createButtonSeparator">
|
||||
</Component>
|
||||
<Component class="javax.swing.JButton" name="createBtn">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/centralrepository/persona/Bundle.properties" key="PersonasTopComponent.createBtn.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Component class="org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel" name="detailsPanel">
|
||||
<Container class="javax.swing.JScrollPane" name="detailsScrollPane">
|
||||
<Constraints>
|
||||
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
|
||||
<JSplitPaneConstraints position="right"/>
|
||||
</Constraint>
|
||||
</Constraints>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
|
||||
<SubComponents>
|
||||
<Component class="org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel" name="detailsPanel">
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -263,23 +263,39 @@ public final class PersonasTopComponent extends TopComponent {
|
||||
private void initComponents() {
|
||||
|
||||
searchButtonGroup = new javax.swing.ButtonGroup();
|
||||
jSplitPane1 = new javax.swing.JSplitPane();
|
||||
introTextScrollPane = new javax.swing.JScrollPane();
|
||||
introText = new javax.swing.JTextArea();
|
||||
mainSplitPane = new javax.swing.JSplitPane();
|
||||
searchPanel = new javax.swing.JPanel();
|
||||
searchField = new javax.swing.JTextField();
|
||||
searchNameRadio = new javax.swing.JRadioButton();
|
||||
searchAccountRadio = new javax.swing.JRadioButton();
|
||||
searchBtn = new javax.swing.JButton();
|
||||
resultsPane = new javax.swing.JScrollPane();
|
||||
resultsTable = new javax.swing.JTable();
|
||||
searchBtn = new javax.swing.JButton();
|
||||
createAccountBtn = new javax.swing.JButton();
|
||||
createBtn = new javax.swing.JButton();
|
||||
editBtn = new javax.swing.JButton();
|
||||
deleteBtn = new javax.swing.JButton();
|
||||
jSeparator1 = new javax.swing.JSeparator();
|
||||
createButtonSeparator = new javax.swing.JSeparator();
|
||||
createBtn = new javax.swing.JButton();
|
||||
detailsScrollPane = new javax.swing.JScrollPane();
|
||||
detailsPanel = new org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel();
|
||||
|
||||
setName(""); // NOI18N
|
||||
|
||||
introTextScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
|
||||
|
||||
introText.setBackground(getBackground());
|
||||
introText.setColumns(20);
|
||||
introText.setLineWrap(true);
|
||||
introText.setRows(5);
|
||||
introText.setText(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.introText.text")); // NOI18N
|
||||
introText.setWrapStyleWord(true);
|
||||
introText.setFocusable(false);
|
||||
introTextScrollPane.setViewportView(introText);
|
||||
|
||||
mainSplitPane.setDividerLocation(400);
|
||||
|
||||
searchField.setText(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchField.text")); // NOI18N
|
||||
|
||||
searchButtonGroup.add(searchNameRadio);
|
||||
@ -289,6 +305,8 @@ public final class PersonasTopComponent extends TopComponent {
|
||||
searchButtonGroup.add(searchAccountRadio);
|
||||
org.openide.awt.Mnemonics.setLocalizedText(searchAccountRadio, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchAccountRadio.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(searchBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchBtn.text")); // NOI18N
|
||||
|
||||
resultsTable.setToolTipText(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.resultsTable.toolTipText")); // NOI18N
|
||||
resultsTable.getTableHeader().setReorderingAllowed(false);
|
||||
resultsPane.setViewportView(resultsTable);
|
||||
@ -298,18 +316,16 @@ public final class PersonasTopComponent extends TopComponent {
|
||||
resultsTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.resultsTable.columnModel.title1")); // NOI18N
|
||||
}
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(searchBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.searchBtn.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(createAccountBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.createAccountBtn.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(createBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.createBtn.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(editBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.editBtn.text")); // NOI18N
|
||||
editBtn.setEnabled(false);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(deleteBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.deleteBtn.text")); // NOI18N
|
||||
deleteBtn.setEnabled(false);
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(createBtn, org.openide.util.NbBundle.getMessage(PersonasTopComponent.class, "PersonasTopComponent.createBtn.text")); // NOI18N
|
||||
|
||||
javax.swing.GroupLayout searchPanelLayout = new javax.swing.GroupLayout(searchPanel);
|
||||
searchPanel.setLayout(searchPanelLayout);
|
||||
searchPanelLayout.setHorizontalGroup(
|
||||
@ -317,13 +333,7 @@ public final class PersonasTopComponent extends TopComponent {
|
||||
.addGroup(searchPanelLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jSeparator1)
|
||||
.addGroup(searchPanelLayout.createSequentialGroup()
|
||||
.addComponent(createBtn)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(editBtn)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(deleteBtn))
|
||||
.addComponent(createButtonSeparator)
|
||||
.addComponent(resultsPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
|
||||
.addComponent(searchField)
|
||||
.addGroup(searchPanelLayout.createSequentialGroup()
|
||||
@ -333,7 +343,14 @@ public final class PersonasTopComponent extends TopComponent {
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(searchBtn))
|
||||
.addGroup(searchPanelLayout.createSequentialGroup()
|
||||
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(createAccountBtn)
|
||||
.addGroup(searchPanelLayout.createSequentialGroup()
|
||||
.addComponent(createBtn)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(editBtn)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(deleteBtn)))
|
||||
.addGap(0, 0, Short.MAX_VALUE)))
|
||||
.addContainerGap())
|
||||
);
|
||||
@ -348,42 +365,52 @@ public final class PersonasTopComponent extends TopComponent {
|
||||
.addComponent(searchAccountRadio)
|
||||
.addComponent(searchBtn))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(resultsPane, javax.swing.GroupLayout.DEFAULT_SIZE, 457, Short.MAX_VALUE)
|
||||
.addComponent(resultsPane, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(searchPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(editBtn)
|
||||
.addComponent(createBtn)
|
||||
.addComponent(deleteBtn))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 4, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(createButtonSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 4, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(createAccountBtn, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
jSplitPane1.setLeftComponent(searchPanel);
|
||||
jSplitPane1.setRightComponent(detailsPanel);
|
||||
mainSplitPane.setLeftComponent(searchPanel);
|
||||
|
||||
detailsScrollPane.setViewportView(detailsPanel);
|
||||
|
||||
mainSplitPane.setRightComponent(detailsScrollPane);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 794, Short.MAX_VALUE)
|
||||
.addComponent(introTextScrollPane)
|
||||
.addComponent(mainSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 724, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jSplitPane1)
|
||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||
.addComponent(introTextScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 49, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(mainSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 470, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JButton createAccountBtn;
|
||||
private javax.swing.JButton createBtn;
|
||||
private javax.swing.JSeparator createButtonSeparator;
|
||||
private javax.swing.JButton deleteBtn;
|
||||
private org.sleuthkit.autopsy.centralrepository.persona.PersonaDetailsPanel detailsPanel;
|
||||
private javax.swing.JScrollPane detailsScrollPane;
|
||||
private javax.swing.JButton editBtn;
|
||||
private javax.swing.JSeparator jSeparator1;
|
||||
private javax.swing.JSplitPane jSplitPane1;
|
||||
private javax.swing.JTextArea introText;
|
||||
private javax.swing.JScrollPane introTextScrollPane;
|
||||
private javax.swing.JSplitPane mainSplitPane;
|
||||
private javax.swing.JScrollPane resultsPane;
|
||||
private javax.swing.JTable resultsTable;
|
||||
private javax.swing.JRadioButton searchAccountRadio;
|
||||
|
@ -17,7 +17,7 @@ ContactsViewer_columnHeader_Phone=Phone
|
||||
ContactsViewer_noContacts_message=<No contacts found for selected account>
|
||||
ContactsViewer_tabTitle=Contacts
|
||||
MediaViewer_Name=Media Attachments
|
||||
MessageNode_Node_Property_Attms=Attachments
|
||||
MessageNode_Node_Property_Attms=Attachment Count
|
||||
MessageNode_Node_Property_Date=Date
|
||||
MessageNode_Node_Property_From=From
|
||||
MessageNode_Node_Property_Subject=Subject
|
||||
|
@ -73,12 +73,12 @@ class MessageNode extends BlackboardArtifactNode {
|
||||
"MessageNode_Node_Property_To=To",
|
||||
"MessageNode_Node_Property_Date=Date",
|
||||
"MessageNode_Node_Property_Subject=Subject",
|
||||
"MessageNode_Node_Property_Attms=Attachments"
|
||||
"MessageNode_Node_Property_Attms=Attachment Count"
|
||||
})
|
||||
|
||||
@Override
|
||||
protected Sheet createSheet() {
|
||||
Sheet sheet = super.createSheet();
|
||||
Sheet sheet = Sheet.createDefault();
|
||||
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
|
||||
if (sheetSet == null) {
|
||||
sheetSet = Sheet.createPropertiesSet();
|
||||
@ -90,13 +90,14 @@ class MessageNode extends BlackboardArtifactNode {
|
||||
final BlackboardArtifact artifact = getArtifact();
|
||||
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());
|
||||
|
||||
if(fromID == null ||
|
||||
(fromID != TSK_EMAIL_MSG &&
|
||||
fromID != TSK_MESSAGE)) {
|
||||
if (fromID == null
|
||||
|| (fromID != TSK_EMAIL_MSG
|
||||
&& fromID != TSK_MESSAGE)) {
|
||||
return sheet;
|
||||
}
|
||||
|
||||
sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID","",threadID == null ? UNTHREADED_ID : threadID)); //NON-NLS
|
||||
if (threadID != null) {
|
||||
sheetSet.put(new NodeProperty<>("ThreadID", "ThreadID", "", threadID)); //NON-NLS
|
||||
}
|
||||
sheetSet.put(new NodeProperty<>("Subject", Bundle.MessageNode_Node_Property_Subject(), "",
|
||||
getAttributeDisplayString(artifact, TSK_SUBJECT))); //NON-NLS
|
||||
try {
|
||||
@ -105,26 +106,28 @@ class MessageNode extends BlackboardArtifactNode {
|
||||
logger.log(Level.WARNING, "Error loading attachment count for " + artifact, ex); //NON-NLS
|
||||
}
|
||||
|
||||
switch (fromID) {
|
||||
case TSK_EMAIL_MSG:
|
||||
sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "",
|
||||
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;"))); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "",
|
||||
StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;"))); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "",
|
||||
getAttributeDisplayString(artifact, TSK_DATETIME_SENT))); //NON-NLS
|
||||
break;
|
||||
case TSK_MESSAGE:
|
||||
sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM))); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "",
|
||||
getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO))); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "",
|
||||
getAttributeDisplayString(artifact, TSK_DATETIME))); //NON-NLS
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
String msg_from = getAttributeDisplayString(artifact, TSK_EMAIL_FROM);
|
||||
String msg_to = getAttributeDisplayString(artifact, TSK_EMAIL_TO);
|
||||
String date = getAttributeDisplayString(artifact, TSK_DATETIME_SENT);
|
||||
|
||||
if (msg_from.isEmpty()) {
|
||||
msg_from = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM);
|
||||
|
||||
}
|
||||
if (msg_to.isEmpty()) {
|
||||
msg_to = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO);
|
||||
}
|
||||
if (date.isEmpty()) {
|
||||
date = getAttributeDisplayString(artifact, TSK_DATETIME);
|
||||
}
|
||||
|
||||
sheetSet.put(new NodeProperty<>("From", Bundle.MessageNode_Node_Property_From(), "",
|
||||
msg_from)); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("To", Bundle.MessageNode_Node_Property_To(), "",
|
||||
msg_to)); //NON-NLS
|
||||
sheetSet.put(new NodeProperty<>("Date", Bundle.MessageNode_Node_Property_Date(), "",
|
||||
date)); //NON-NLS
|
||||
|
||||
return sheet;
|
||||
}
|
||||
|
||||
@ -158,8 +161,7 @@ class MessageNode extends BlackboardArtifactNode {
|
||||
try {
|
||||
MessageAttachments msgAttachments = BlackboardJsonAttrUtil.fromAttribute(attachmentsAttr, MessageAttachments.class);
|
||||
return msgAttachments.getAttachmentsCount();
|
||||
}
|
||||
catch (BlackboardJsonAttrUtil.InvalidJsonException ex) {
|
||||
} catch (BlackboardJsonAttrUtil.InvalidJsonException ex) {
|
||||
logger.log(Level.WARNING, String.format("Unable to parse json for MessageAttachments object in artifact: %s", artifact.getName()), ex);
|
||||
return 0;
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
|
||||
* section panel.
|
||||
*
|
||||
* @param sectionAttributesList List of attributes to display.
|
||||
* @param sectionLabel Section name label.
|
||||
* @param sectionHeader Section name label.
|
||||
* @param contactPanelLayout Panel layout.
|
||||
* @param contactPanelConstraints Layout constraints.
|
||||
*
|
||||
@ -326,11 +326,9 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks off a search for personas, based in the given list of attributes.
|
||||
* Initiates a search for Personas for the accounts associated with the
|
||||
* Contact.
|
||||
*
|
||||
* @param accountAttributesList a list of account identifying attributes.
|
||||
*
|
||||
* @throws CentralRepoException
|
||||
*/
|
||||
@NbBundle.Messages({
|
||||
"ContactArtifactViewer_persona_header=Persona",
|
||||
@ -338,12 +336,6 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
|
||||
"ContactArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas.",
|
||||
"ContactArtifactViewer_persona_unknown=Unknown"
|
||||
})
|
||||
|
||||
/**
|
||||
* Initiates a search for Personas for the accounts associated with the
|
||||
* Contact.
|
||||
*
|
||||
*/
|
||||
private void initiatePersonasSearch() {
|
||||
|
||||
// add a section header
|
||||
@ -425,6 +417,7 @@ public class ContactArtifactViewer extends javax.swing.JPanel implements Artifac
|
||||
* Displays the given persona in the persona panel.
|
||||
*
|
||||
* @param persona Persona to display.
|
||||
* @param matchNumber Number of matches.
|
||||
* @param missingAccountsList List of contact accounts this persona may be
|
||||
* missing.
|
||||
* @param gridBagLayout Layout to use.
|
||||
|
@ -33,6 +33,8 @@ import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import javafx.application.Platform;
|
||||
import javafx.embed.swing.JFXPanel;
|
||||
import net.sf.sevenzipjbinding.SevenZip;
|
||||
import net.sf.sevenzipjbinding.SevenZipNativeInitializationException;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.openide.modules.InstalledFileLocator;
|
||||
@ -336,8 +338,8 @@ public class Installer extends ModuleInstall {
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a folder in the config directory for object detection classifiers if one does not
|
||||
* exist.
|
||||
* Make a folder in the config directory for object detection classifiers if
|
||||
* one does not exist.
|
||||
*/
|
||||
private static void ensureClassifierFolderExists() {
|
||||
File objectDetectionClassifierDir = new File(PlatformUtil.getObjectDetectionClassifierPath());
|
||||
@ -381,6 +383,7 @@ public class Installer extends ModuleInstall {
|
||||
ensureClassifierFolderExists();
|
||||
ensureOcrLanguagePacksFolderExists();
|
||||
initJavaFx();
|
||||
initializeSevenZip();
|
||||
for (ModuleInstall mi : packageInstallers) {
|
||||
try {
|
||||
mi.restored();
|
||||
@ -394,6 +397,19 @@ public class Installer extends ModuleInstall {
|
||||
preloadJython();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes 7zip-java bindings. We are performing initialization once
|
||||
* because we encountered issues related to file locking when initialization
|
||||
* was performed closer to where the bindings are used. See JIRA-6528.
|
||||
*/
|
||||
private void initializeSevenZip() {
|
||||
try {
|
||||
SevenZip.initSevenZipFromPlatformJAR();
|
||||
logger.log(Level.INFO, "7zip-java bindings loaded"); //NON-NLS
|
||||
} catch (SevenZipNativeInitializationException e) {
|
||||
logger.log(Level.SEVERE, "Error loading 7zip-java bindings", e); //NON-NLS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an initial load of the Jython modules to speed up subsequent loads.
|
||||
@ -403,8 +419,7 @@ public class Installer extends ModuleInstall {
|
||||
try {
|
||||
JythonModuleLoader.getIngestModuleFactories();
|
||||
JythonModuleLoader.getGeneralReportModules();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
} catch (Exception ex) {
|
||||
// This is a firewall exception to ensure that any possible exception caused
|
||||
// by this initial load of the Jython modules are caught and logged.
|
||||
logger.log(Level.SEVERE, "There was an error while doing an initial load of python plugins.", ex);
|
||||
|
@ -450,6 +450,7 @@
|
||||
<file name="cvt.wsmode" url="cvtWsmode.xml"/>
|
||||
<file name="discovery.wsmode" url="discoveryWsmode.xml"/>
|
||||
<file name="geolocation.wsmode" url="geolocationWsmode.xml"/>
|
||||
<file name="personas.wsmode" url="personasWsmode.xml"/>
|
||||
</folder>
|
||||
</folder>
|
||||
|
||||
|
11
Core/src/org/sleuthkit/autopsy/core/personasWsmode.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mode version="2.4">
|
||||
<name unique="personas"/>
|
||||
<kind type="editor"/>
|
||||
<state type="separated"/>
|
||||
<bounds x="76" y="68" width="1200" height="800"/>
|
||||
<frame state="0"/>
|
||||
|
||||
<empty-behavior permanent="false"/>
|
||||
</mode>
|
||||
|
@ -83,6 +83,12 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
|
||||
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.DATA_SOURCE_ADDED, Case.Events.CURRENT_CASE);
|
||||
|
||||
/**
|
||||
* RefreshThrottler is used to limit the number of refreshes performed when
|
||||
* CONTENT_CHANGED and DATA_ADDED ingest module events are received.
|
||||
*/
|
||||
private final RefreshThrottler refreshThrottler;
|
||||
|
||||
/**
|
||||
* Create the base expression used as the where clause in the queries for
|
||||
* files by mime type. Filters out certain kinds of files and directories,
|
||||
@ -108,6 +114,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
private void removeListeners() {
|
||||
deleteObservers();
|
||||
IngestManager.getInstance().removeIngestJobEventListener(pcl);
|
||||
refreshThrottler.unregisterEventListener();
|
||||
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
|
||||
}
|
||||
|
||||
@ -155,32 +162,20 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
this.typesRoot = typesRoot;
|
||||
this.pcl = (PropertyChangeEvent evt) -> {
|
||||
String eventType = evt.getPropertyName();
|
||||
if (eventType.equals(IngestManager.IngestModuleEvent.CONTENT_CHANGED.toString())
|
||||
|| eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|
||||
if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString())
|
||||
|| eventType.equals(IngestManager.IngestJobEvent.CANCELLED.toString())
|
||||
|| eventType.equals(Case.Events.DATA_SOURCE_ADDED.toString())) {
|
||||
/**
|
||||
* Checking for a current case is a stop gap measure until a
|
||||
* different way of handling the closing of cases is worked out.
|
||||
* Currently, remote events may be received for a case that is
|
||||
* already closed.
|
||||
*/
|
||||
try {
|
||||
Case.getCurrentCaseThrows();
|
||||
typesRoot.updateShowCounts();
|
||||
populateHashMap();
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
/**
|
||||
* Case is closed, do nothing.
|
||||
*/
|
||||
}
|
||||
|
||||
refreshMimeTypes();
|
||||
} else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) {
|
||||
if (evt.getNewValue() == null) {
|
||||
removeListeners();
|
||||
}
|
||||
}
|
||||
};
|
||||
refreshThrottler = new RefreshThrottler(new FileTypesByMimeTypeRefresher());
|
||||
IngestManager.getInstance().addIngestJobEventListener(INGEST_JOB_EVENTS_OF_INTEREST, pcl);
|
||||
refreshThrottler.registerForIngestModuleEvents();
|
||||
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, pcl);
|
||||
populateHashMap();
|
||||
}
|
||||
@ -212,6 +207,41 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
|
||||
|
||||
}
|
||||
|
||||
private void refreshMimeTypes() {
|
||||
/**
|
||||
* Checking for a current case is a stop gap measure until a different
|
||||
* way of handling the closing of cases is worked out. Currently, remote
|
||||
* events may be received for a case that is already closed.
|
||||
*/
|
||||
try {
|
||||
Case.getCurrentCaseThrows();
|
||||
typesRoot.updateShowCounts();
|
||||
populateHashMap();
|
||||
} catch (NoCurrentCaseException notUsed) {
|
||||
/**
|
||||
* Case is closed, do nothing.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Responsible for updating the 'By Mime Type' view in the UI. See
|
||||
* RefreshThrottler for more details.
|
||||
*/
|
||||
private class FileTypesByMimeTypeRefresher implements RefreshThrottler.Refresher {
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
refreshMimeTypes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRefreshRequired(PropertyChangeEvent evt) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class which represents the root node of the "By MIME Type" tree, will
|
||||
* have children of each media type present in the database or no children
|
||||
|
@ -78,7 +78,7 @@ final class MapWaypoint extends KdTree.XYZPoint implements org.jxmapviewer.viewe
|
||||
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH.getTypeID(), Color.GREEN);
|
||||
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACK.getTypeID(), Color.ORANGE);
|
||||
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT.getTypeID(), Color.ORANGE);
|
||||
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID(), Color.CYAN);
|
||||
artifactTypesToColors.put(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID(), Color.MAGENTA);
|
||||
}
|
||||
|
||||
private final Waypoint dataModelWaypoint;
|
||||
|
@ -139,9 +139,10 @@ class SevenZipExtractor {
|
||||
}
|
||||
|
||||
SevenZipExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) throws SevenZipNativeInitializationException {
|
||||
if (!SevenZip.isInitializedSuccessfully() && (SevenZip.getLastInitializationException() == null)) {
|
||||
SevenZip.initSevenZipFromPlatformJAR();
|
||||
if (!SevenZip.isInitializedSuccessfully()) {
|
||||
throw new SevenZipNativeInitializationException("SevenZip has not been previously initialized.");
|
||||
}
|
||||
|
||||
this.context = context;
|
||||
this.fileTypeDetector = fileTypeDetector;
|
||||
this.moduleDirRelative = moduleDirRelative;
|
||||
|
@ -6,6 +6,7 @@ FilesIdentifierIngestModule.indexError.message=Failed to index interesting file
|
||||
FilesSet.rule.dateRule.toString=(modified within {0} day(s))
|
||||
FilesSetDefsPanel.bytes=Bytes
|
||||
FilesSetDefsPanel.cancelImportMsg=Cancel import
|
||||
FilesSetDefsPanel.cancelNewSetMsg=Cancel
|
||||
# {0} - file name
|
||||
FilesSetDefsPanel.exportButtonActionPerformed.fileExistPrompt=File {0} exists, overwrite?
|
||||
FilesSetDefsPanel.gigaBytes=Gigabytes
|
||||
@ -23,10 +24,12 @@ FilesSetDefsPanel.interesting.fileExtensionFilterLbl=Autopsy Interesting File Se
|
||||
FilesSetDefsPanel.interesting.importButtonAction.featureName=Interesting Files Set Import
|
||||
FilesSetDefsPanel.interesting.importOwConflict=Import Interesting files set conflict
|
||||
FilesSetDefsPanel.interesting.importSetButton.text=Import Set
|
||||
FilesSetDefsPanel.interesting.newOwConflict=Interesting files set conflict
|
||||
# {0} - FilesSet name
|
||||
FilesSetDefsPanel.interesting.overwriteSetPrompt=Interesting files set <{0}> already exists locally, overwrite?
|
||||
FilesSetDefsPanel.interesting.overwriteSetPrompt=Interesting files set "{0}" already exists locally, overwrite?
|
||||
# {0} - FilesSet name
|
||||
FilesSetDefsPanel.interesting.standardFileConflict=A standard interesting file set already exists with the name <{0}>. Would you like to create a custom version of this file set?
|
||||
# {1} - New FilesSet name
|
||||
FilesSetDefsPanel.interesting.standardFileConflict=A standard interesting file set already exists with the name "{0}." Would you like to rename the set to "{1}?"
|
||||
FilesSetDefsPanel.Interesting.Title=Global Interesting Items Settings
|
||||
FilesSetDefsPanel.kiloBytes=Kilobytes
|
||||
FilesSetDefsPanel.loadError=Error loading interesting files sets from file.
|
||||
|
@ -41,6 +41,7 @@ import javax.swing.filechooser.FileNameExtensionFilter;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.netbeans.spi.options.OptionsPanelController;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.windows.WindowManager;
|
||||
import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@ -424,15 +425,6 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
|
||||
option = JOptionPane.showConfirmDialog(this, panel, NbBundle.getMessage(FilesSetPanel.class, filterDialogTitle), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
|
||||
} while (option == JOptionPane.OK_OPTION && !panel.isValidDefinition());
|
||||
|
||||
// While adding new ruleset(selectedSet == null), if rule set with same name already exists, do not add to the filesSets hashMap.
|
||||
// In case of editing an existing ruleset(selectedSet != null), following check is not performed.
|
||||
if (this.filesSets.containsKey(panel.getFilesSetName()) && shouldCreateNew) {
|
||||
MessageNotifyUtil.Message.error(NbBundle.getMessage(this.getClass(),
|
||||
"FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text",
|
||||
panel.getFilesSetName()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (option == JOptionPane.OK_OPTION) {
|
||||
Map<String, FilesSet.Rule> rules = new HashMap<>();
|
||||
if (selectedSet != null) {
|
||||
@ -450,10 +442,16 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
|
||||
rules
|
||||
);
|
||||
|
||||
Pair<FilesSet, Integer> result = handleConflict(filesSet, false);
|
||||
option = result.getRight();
|
||||
FilesSet toAddOrUpdate = result.getLeft();
|
||||
|
||||
if (result.getRight() == JOptionPane.OK_OPTION) {
|
||||
if (shouldCreateNew) {
|
||||
this.replaceFilesSet(null, filesSet, null);
|
||||
this.replaceFilesSet(null, toAddOrUpdate, null);
|
||||
} else {
|
||||
this.replaceFilesSet(selectedSet, filesSet, null);
|
||||
this.replaceFilesSet(selectedSet, toAddOrUpdate, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -519,10 +517,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
|
||||
*
|
||||
* @param oldSet A set to replace, null if the new set is not a
|
||||
* replacement.
|
||||
* @param name The name of the files set.
|
||||
* @param description The description of the files set.
|
||||
* @param ignoresKnownFiles Whether or not the files set ignores known
|
||||
* files.
|
||||
* @param newSet The new set of rules.
|
||||
* @param rules The set membership rules for the set. If null,
|
||||
* the rules in the new set will be used.
|
||||
*/
|
||||
@ -1120,16 +1115,8 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
|
||||
this.doFileSetsDialog(this.setsList.getSelectedValue(), true);
|
||||
firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
|
||||
}//GEN-LAST:event_copySetButtonActionPerformed
|
||||
|
||||
@NbBundle.Messages({
|
||||
"FilesSetDefsPanel.yesOwMsg=Yes, overwrite",
|
||||
"FilesSetDefsPanel.yesStandardFileConflictCreate=Yes, create",
|
||||
"FilesSetDefsPanel.noSkipMsg=No, skip",
|
||||
"FilesSetDefsPanel.cancelImportMsg=Cancel import",
|
||||
"# {0} - FilesSet name",
|
||||
"FilesSetDefsPanel.interesting.overwriteSetPrompt=Interesting files set <{0}> already exists locally, overwrite?",
|
||||
"# {0} - FilesSet name",
|
||||
"FilesSetDefsPanel.interesting.standardFileConflict=A standard interesting file set already exists with the name <{0}>. Would you like to create a custom version of this file set?",
|
||||
"FilesSetDefsPanel.interesting.importOwConflict=Import Interesting files set conflict",
|
||||
"FilesSetDefsPanel.interesting.failImportMsg=Interesting files set not imported",
|
||||
"FilesSetDefsPanel.interesting.fileExtensionFilterLbl=Autopsy Interesting File Set File (xml)",
|
||||
"FilesSetDefsPanel.interesting.importButtonAction.featureName=Interesting Files Set Import"
|
||||
@ -1204,7 +1191,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
|
||||
FilesSet selectedSet = null;
|
||||
|
||||
for (FilesSet set : importedSets) {
|
||||
Pair<FilesSet, Integer> conflictResult = handleConflict(set);
|
||||
Pair<FilesSet, Integer> conflictResult = handleConflict(set, true);
|
||||
int choice = conflictResult.getRight();
|
||||
FilesSet resultingFilesSet = conflictResult.getLeft();
|
||||
|
||||
@ -1223,48 +1210,61 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
|
||||
* Handles any possible conflicts that may arise from importing a files set.
|
||||
*
|
||||
* @param set The set to potentially import.
|
||||
* @param isImport The set with which to handle the conflict is being
|
||||
* imported, otherwise this is a set to be added from the
|
||||
* "New Set" button.
|
||||
*
|
||||
* @return A pair of the files set to be imported (or null if none) and the
|
||||
* integer corresponding to the JOptionPane choice of the
|
||||
* yes_no_cancel_option.
|
||||
* JOptionPane.YES_NO_CANCEL option.
|
||||
*/
|
||||
private Pair<FilesSet, Integer> handleConflict(FilesSet set) {
|
||||
private Pair<FilesSet, Integer> handleConflict(FilesSet set, boolean isImport) {
|
||||
FilesSet conflict = this.filesSets.get(set.getName());
|
||||
// if no conflict, return the files set as is with the option to proceed
|
||||
if (conflict == null) {
|
||||
return Pair.of(set, JOptionPane.OK_OPTION);
|
||||
} else if (conflict.isStandardSet()) {
|
||||
// if there is a conflict and the conflicting files set is a standard files set,
|
||||
// see if allowing a custom files set is okay.
|
||||
Object[] options = {
|
||||
Bundle.FilesSetDefsPanel_yesStandardFileConflictCreate(),
|
||||
Bundle.FilesSetDefsPanel_noSkipMsg(),
|
||||
Bundle.FilesSetDefsPanel_cancelImportMsg()
|
||||
};
|
||||
|
||||
int conflictChoice = JOptionPane.showOptionDialog(this,
|
||||
Bundle.FilesSetDefsPanel_interesting_standardFileConflict(set.getName()),
|
||||
Bundle.FilesSetDefsPanel_interesting_importOwConflict(),
|
||||
JOptionPane.YES_NO_CANCEL_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE,
|
||||
null,
|
||||
options,
|
||||
options[0]);
|
||||
|
||||
// if it is okay, try again to see if there is a conflict.
|
||||
if (conflictChoice == JOptionPane.OK_OPTION) {
|
||||
return handleConflict(StandardInterestingFilesSetsLoader.getAsCustomFileSet(set));
|
||||
}
|
||||
|
||||
return Pair.of(null, conflictChoice);
|
||||
if (isImport) {
|
||||
if (conflict.isStandardSet()) {
|
||||
return onImportStandardSetConflict(set);
|
||||
} else {
|
||||
return onImportConflict(set);
|
||||
}
|
||||
} else {
|
||||
if (conflict.isStandardSet()) {
|
||||
return onNewEditSetStandardSetConflict(set);
|
||||
} else {
|
||||
return onNewEditSetConflict(set);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* When a user imports a files set and the files set name collides with a
|
||||
* pre-existing files set (not a standard files set), the user is prompted
|
||||
* for how they would like that handled (overwrite, skip, or cancel whole
|
||||
* operation)
|
||||
*
|
||||
* @param set The set to be imported.
|
||||
*
|
||||
* @return a pair of the files set and the JOptionPane.YES_NO_CANCEL option
|
||||
*/
|
||||
@Messages({
|
||||
"FilesSetDefsPanel.yesOwMsg=Yes, overwrite",
|
||||
"FilesSetDefsPanel.noSkipMsg=No, skip",
|
||||
"FilesSetDefsPanel.cancelImportMsg=Cancel import",
|
||||
"# {0} - FilesSet name",
|
||||
"FilesSetDefsPanel.interesting.overwriteSetPrompt=Interesting files set \"{0}\" already exists locally, overwrite?",
|
||||
"FilesSetDefsPanel.interesting.importOwConflict=Import Interesting files set conflict",})
|
||||
private Pair<FilesSet, Integer> onImportConflict(FilesSet set) {
|
||||
// if there is a conflict, see if it is okay to overwrite.
|
||||
Object[] options = {
|
||||
Bundle.FilesSetDefsPanel_yesOwMsg(),
|
||||
Bundle.FilesSetDefsPanel_noSkipMsg(),
|
||||
Bundle.FilesSetDefsPanel_cancelImportMsg()
|
||||
};
|
||||
|
||||
int conflictChoice = JOptionPane.showOptionDialog(this,
|
||||
Bundle.FilesSetDefsPanel_interesting_overwriteSetPrompt(set.getName()),
|
||||
Bundle.FilesSetDefsPanel_interesting_importOwConflict(),
|
||||
@ -1281,6 +1281,123 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
|
||||
|
||||
return Pair.of(null, conflictChoice);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a user imports a files set and the files set name collides with a
|
||||
* pre-existing standard files set, the user is prompted for how they would
|
||||
* like that handled (create files set with a " custom" suffix, skip, or
|
||||
* cancel whole operation)
|
||||
*
|
||||
* @param set The set to be imported.
|
||||
*
|
||||
* @return a pair of the files set and the JOptionPane.YES_NO_CANCEL option
|
||||
*/
|
||||
@Messages({
|
||||
"FilesSetDefsPanel.yesStandardFileConflictCreate=Yes, create",
|
||||
"# {0} - FilesSet name",
|
||||
"# {1} - New FilesSet name",
|
||||
"FilesSetDefsPanel.interesting.standardFileConflict=A standard interesting file set already exists with the name \"{0}.\" Would you like to rename your set to \"{1}?\"",})
|
||||
private Pair<FilesSet, Integer> onImportStandardSetConflict(FilesSet set) {
|
||||
// if there is a conflict and the conflicting files set is a standard files set,
|
||||
// see if allowing a custom files set is okay.
|
||||
Object[] options = {
|
||||
Bundle.FilesSetDefsPanel_yesStandardFileConflictCreate(),
|
||||
Bundle.FilesSetDefsPanel_noSkipMsg(),
|
||||
Bundle.FilesSetDefsPanel_cancelImportMsg()
|
||||
};
|
||||
|
||||
String setName = set.getName();
|
||||
String customSetName = Bundle.StandardInterestingFileSetsLoader_customSuffixed(set.getName());
|
||||
|
||||
int conflictChoice = JOptionPane.showOptionDialog(this,
|
||||
Bundle.FilesSetDefsPanel_interesting_standardFileConflict(setName, customSetName),
|
||||
Bundle.FilesSetDefsPanel_interesting_importOwConflict(),
|
||||
JOptionPane.YES_NO_CANCEL_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE,
|
||||
null,
|
||||
options,
|
||||
options[0]);
|
||||
|
||||
// if it is okay to create with custom prefix, try again to see if there is a conflict.
|
||||
if (conflictChoice == JOptionPane.OK_OPTION) {
|
||||
return handleConflict(StandardInterestingFilesSetsLoader.getAsCustomFileSet(set), true);
|
||||
}
|
||||
|
||||
return Pair.of(null, conflictChoice);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a user creates a files set or edits a files set and the files set
|
||||
* name collides with a pre-existing files set (not a standard files set),
|
||||
* the user is prompted for how they would like that handled (overwrite or
|
||||
* cancel whole operation)
|
||||
*
|
||||
* @param set The set to be added.
|
||||
*
|
||||
* @return a pair of the files set and the JOptionPane.YES_NO_CANCEL option
|
||||
*/
|
||||
@Messages({
|
||||
"FilesSetDefsPanel.cancelNewSetMsg=Cancel",
|
||||
"FilesSetDefsPanel.interesting.newOwConflict=Interesting files set conflict",})
|
||||
private Pair<FilesSet, Integer> onNewEditSetConflict(FilesSet set) {
|
||||
// if there is a conflict, see if it is okay to overwrite.
|
||||
Object[] options = {
|
||||
Bundle.FilesSetDefsPanel_yesOwMsg(),
|
||||
Bundle.FilesSetDefsPanel_cancelNewSetMsg()
|
||||
};
|
||||
int conflictChoice = JOptionPane.showOptionDialog(this,
|
||||
Bundle.FilesSetDefsPanel_interesting_overwriteSetPrompt(set.getName()),
|
||||
Bundle.FilesSetDefsPanel_interesting_newOwConflict(),
|
||||
JOptionPane.OK_CANCEL_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE,
|
||||
null,
|
||||
options,
|
||||
options[0]);
|
||||
|
||||
if (conflictChoice == JOptionPane.OK_OPTION) {
|
||||
// if so, just return the files set to be placed in the map overwriting what is currently present.
|
||||
return Pair.of(set, conflictChoice);
|
||||
}
|
||||
|
||||
return Pair.of(null, conflictChoice);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a user creates a files set and the files set name collides with a
|
||||
* pre-existing standard files set, the user is prompted for how they would
|
||||
* like that handled (create files set with a " custom" suffix or cancel
|
||||
* whole operation)
|
||||
*
|
||||
* @param set The set to be adedd.
|
||||
*
|
||||
* @return a pair of the files set and the JOptionPane.YES_NO_CANCEL option
|
||||
*/
|
||||
private Pair<FilesSet, Integer> onNewEditSetStandardSetConflict(FilesSet set) {
|
||||
// if there is a conflict and the conflicting files set is a standard files set,
|
||||
// see if allowing a custom files set is okay.
|
||||
Object[] options = {
|
||||
Bundle.FilesSetDefsPanel_yesStandardFileConflictCreate(),
|
||||
Bundle.FilesSetDefsPanel_cancelNewSetMsg()
|
||||
};
|
||||
|
||||
String setName = set.getName();
|
||||
String customSetName = Bundle.StandardInterestingFileSetsLoader_customSuffixed(set.getName());
|
||||
|
||||
int conflictChoice = JOptionPane.showOptionDialog(this,
|
||||
Bundle.FilesSetDefsPanel_interesting_standardFileConflict(setName, customSetName),
|
||||
Bundle.FilesSetDefsPanel_interesting_newOwConflict(),
|
||||
JOptionPane.OK_CANCEL_OPTION,
|
||||
JOptionPane.QUESTION_MESSAGE,
|
||||
null,
|
||||
options,
|
||||
options[0]);
|
||||
|
||||
// if it is okay to create with custom prefix, try again to see if there is a conflict.
|
||||
if (conflictChoice == JOptionPane.OK_OPTION) {
|
||||
return handleConflict(StandardInterestingFilesSetsLoader.getAsCustomFileSet(set), false);
|
||||
}
|
||||
|
||||
return Pair.of(null, conflictChoice);
|
||||
}
|
||||
|
||||
@NbBundle.Messages({"FilesSetDefsPanel.interesting.exportButtonAction.featureName=Interesting Files Set Export",
|
||||
|
@ -26,6 +26,7 @@ import java.io.Serializable;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -51,6 +52,9 @@ import org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager.FilesSets
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import java.util.Comparator;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class InterestingItemsFilesSetSettings implements Serializable {
|
||||
|
||||
@ -537,6 +541,34 @@ class InterestingItemsFilesSetSettings implements Serializable {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generates an alphabetically sorted list based on the provided collection and Function to retrieve a string field from each object.
|
||||
* @param itemsToSort The items to be sorted into the newly generated list.
|
||||
* @param getName The method to retrieve the given field from the object.
|
||||
* @return The newly generated list sorted alphabetically by the given field.
|
||||
*/
|
||||
private static <T> List<T> sortOnField(Collection<T> itemsToSort, final Function<T, String> getName) {
|
||||
Comparator<T> comparator = (a,b) -> {
|
||||
String aName = getName.apply(a);
|
||||
String bName = getName.apply(b);
|
||||
if (aName == null) {
|
||||
aName = "";
|
||||
}
|
||||
|
||||
if (bName == null) {
|
||||
bName = "";
|
||||
}
|
||||
|
||||
return aName.compareToIgnoreCase(bName);
|
||||
};
|
||||
|
||||
return itemsToSort.stream()
|
||||
.sorted(comparator)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write the FilesSets to a file as an xml.
|
||||
*
|
||||
@ -555,7 +587,12 @@ class InterestingItemsFilesSetSettings implements Serializable {
|
||||
Element rootElement = doc.createElement(FILE_SETS_ROOT_TAG);
|
||||
doc.appendChild(rootElement);
|
||||
// Add the interesting files sets to the document.
|
||||
for (FilesSet set : interestingFilesSets) {
|
||||
|
||||
List<FilesSet> sortedFilesSets = sortOnField(
|
||||
interestingFilesSets,
|
||||
filesSet -> filesSet == null ? null : filesSet.getName());
|
||||
|
||||
for (FilesSet set : sortedFilesSets) {
|
||||
// Add the files set element and its attributes.
|
||||
Element setElement = doc.createElement(FILE_SET_TAG);
|
||||
setElement.setAttribute(NAME_ATTR, set.getName());
|
||||
@ -565,7 +602,12 @@ class InterestingItemsFilesSetSettings implements Serializable {
|
||||
setElement.setAttribute(VERSION_NUMBER, Integer.toString(set.getVersionNumber()));
|
||||
// Add the child elements for the set membership rules.
|
||||
// All conditions of a rule will be written as a single element in the xml
|
||||
for (FilesSet.Rule rule : set.getRules().values()) {
|
||||
|
||||
List<FilesSet.Rule> sortedRules = sortOnField(
|
||||
set.getRules().values(),
|
||||
rule -> rule == null ? null : rule.getName());
|
||||
|
||||
for (FilesSet.Rule rule : sortedRules) {
|
||||
// Add a rule element with the appropriate name Condition
|
||||
// type tag.
|
||||
Element ruleElement;
|
||||
|
@ -151,6 +151,7 @@ final class ReportVisualPanel1 extends JPanel implements ListSelectionListener {
|
||||
}
|
||||
|
||||
modulesJList.getSelectionModel().addListSelectionListener(this);
|
||||
modulesJList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
modulesJList.setCellRenderer(new ModuleCellRenderer());
|
||||
modulesJList.setListData(modules.toArray(new ReportModule[modules.size()]));
|
||||
modulesJList.setSelectedIndex(selectedIndex);
|
||||
|
@ -19,6 +19,9 @@
|
||||
package org.sleuthkit.autopsy.tags;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.TagName;
|
||||
import org.sleuthkit.datamodel.TagSet;
|
||||
@ -52,12 +55,13 @@ public final class TagUtils {
|
||||
public static String getDecoratedTagDisplayName(TagName tagName) {
|
||||
String displayName = tagName.getDisplayName();
|
||||
try {
|
||||
TagSet tagSet = tagName.getTagSet();
|
||||
TagsManager tagsManager = Case.getCurrentCaseThrows().getServices().getTagsManager();
|
||||
TagSet tagSet = tagsManager.getTagSet(tagName);
|
||||
if (tagSet != null) {
|
||||
displayName = tagSet.getName() + ": " + displayName;
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.WARNING, String.format("Failed to get TagSet for TagName (%d)", tagName.getId()));
|
||||
} catch (NoCurrentCaseException | TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Failed to get TagSet for TagName '%s' (ID=%d)", tagName.getDisplayName(), tagName.getId()));
|
||||
}
|
||||
|
||||
if (tagName.getKnownStatus() == TskData.FileKnown.BAD) {
|
||||
|
@ -62,7 +62,7 @@ public final class TextFileExtractor implements TextExtractor {
|
||||
encoding = StandardCharsets.UTF_8;
|
||||
}
|
||||
} catch (TskCoreException | IOException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Error detecting the "
|
||||
logger.log(Level.WARNING, String.format("Error detecting the "
|
||||
+ "encoding for %s (objID=%d)", file.getName(), file.getId()), ex);
|
||||
encoding = StandardCharsets.UTF_8;
|
||||
}
|
||||
|
@ -135,7 +135,7 @@
|
||||
<compile-dependency/>
|
||||
<run-dependency>
|
||||
<release-version>10</release-version>
|
||||
<specification-version>10.19</specification-version>
|
||||
<specification-version>10.20</specification-version>
|
||||
</run-dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 Basis Technology Corp.
|
||||
* Copyright 2015-2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -71,16 +71,26 @@ public final class AutopsyManifestFileParser implements ManifestFileParser {
|
||||
Date dateFileCreated = new Date(attrs.creationTime().toMillis());
|
||||
Document doc = this.createManifestDOM(filePath);
|
||||
XPath xpath = XPathFactory.newInstance().newXPath();
|
||||
|
||||
XPathExpression expr = xpath.compile(CASE_NAME_XPATH);
|
||||
String caseName = (String) expr.evaluate(doc, XPathConstants.STRING);
|
||||
if (caseName.isEmpty()) {
|
||||
throw new ManifestFileParserException("Case name not found, manifest is invalid");
|
||||
}
|
||||
|
||||
expr = xpath.compile(DEVICE_ID_XPATH);
|
||||
String deviceId = (String) expr.evaluate(doc, XPathConstants.STRING);
|
||||
if (deviceId.isEmpty()) {
|
||||
deviceId = UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
expr = xpath.compile(DATA_SOURCE_NAME_XPATH);
|
||||
String dataSourceName = (String) expr.evaluate(doc, XPathConstants.STRING);
|
||||
if (dataSourceName.isEmpty()) {
|
||||
throw new ManifestFileParserException("Data source path not found, manifest is invalid");
|
||||
}
|
||||
Path dataSourcePath = filePath.getParent().resolve(dataSourceName);
|
||||
|
||||
return new Manifest(filePath, dateFileCreated, caseName, deviceId, dataSourcePath, new HashMap<>());
|
||||
} catch (Exception ex) {
|
||||
throw new ManifestFileParserException(String.format("Error parsing manifest %s", filePath), ex);
|
||||
|
@ -127,7 +127,7 @@
|
||||
<compile-dependency/>
|
||||
<run-dependency>
|
||||
<release-version>10</release-version>
|
||||
<specification-version>10.19</specification-version>
|
||||
<specification-version>10.20</specification-version>
|
||||
</run-dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -42,6 +42,7 @@ from org.sleuthkit.datamodel import TskCoreException
|
||||
import traceback
|
||||
import general
|
||||
import struct
|
||||
import os
|
||||
|
||||
"""
|
||||
Parses cache files that Android maintains for Wifi and cell towers. Adds GPS points to blackboard.
|
||||
@ -79,7 +80,10 @@ class CacheLocationAnalyzer(general.AndroidComponentAnalyzer):
|
||||
# code to parse the cache.wifi and cache.cell taken from https://forensics.spreitzenbarth.de/2011/10/28/decoding-cache-cell-and-cache-wifi-files/
|
||||
cacheFile = open(str(file), 'rb')
|
||||
(version, entries) = struct.unpack('>hh', cacheFile.read(4))
|
||||
# Check the number of entries * 32 (entry record size) to see if it is bigger then the file, this is a indication the file is malformed or corrupted
|
||||
if ((entries * 32) < abstractFile.getSize()):
|
||||
i = 0
|
||||
self._logger.log(Level.INFO, "Number of Entries is " + str(entries) + " File size is " + str(abstractFile.getSize()))
|
||||
while i < entries:
|
||||
key = cacheFile.read(struct.unpack('>h', cacheFile.read(2))[0])
|
||||
(accuracy, confidence, latitude, longitude, readtime) = struct.unpack('>iiddQ', cacheFile.read(32))
|
||||
@ -107,7 +111,8 @@ class CacheLocationAnalyzer(general.AndroidComponentAnalyzer):
|
||||
self._logger.log(Level.SEVERE, traceback.format_exc())
|
||||
MessageNotifyUtil.Notify.error("Failed to index GPS trackpoint artifact for keyword search.", artifact.getDisplayName())
|
||||
cacheFile.close()
|
||||
|
||||
else:
|
||||
self._logger.log(Level.WARNING, "Number of entries in file exceeds file size of file " + os.path.join(abstractFile.getParentPath(), abstractFile.getName()))
|
||||
except Exception as ex:
|
||||
self._logger.log(Level.SEVERE, "Error parsing Cached GPS locations to blackboard", ex)
|
||||
self._logger.log(Level.SEVERE, traceback.format_exc())
|
||||
|
@ -119,7 +119,7 @@
|
||||
<compile-dependency/>
|
||||
<run-dependency>
|
||||
<release-version>10</release-version>
|
||||
<specification-version>10.19</specification-version>
|
||||
<specification-version>10.20</specification-version>
|
||||
</run-dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -60,7 +60,7 @@
|
||||
<compile-dependency/>
|
||||
<run-dependency>
|
||||
<release-version>10</release-version>
|
||||
<specification-version>10.19</specification-version>
|
||||
<specification-version>10.20</specification-version>
|
||||
</run-dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -47,7 +47,7 @@
|
||||
<compile-dependency/>
|
||||
<run-dependency>
|
||||
<release-version>10</release-version>
|
||||
<specification-version>10.19</specification-version>
|
||||
<specification-version>10.20</specification-version>
|
||||
</run-dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -1,5 +1,5 @@
|
||||
#Updated by build script
|
||||
#Fri, 19 Jun 2020 10:14:47 -0400
|
||||
#Wed, 08 Jul 2020 15:15:46 -0400
|
||||
LBL_splash_window_title=Starting Autopsy
|
||||
SPLASH_HEIGHT=314
|
||||
SPLASH_WIDTH=538
|
||||
@ -8,4 +8,4 @@ SplashRunningTextBounds=0,289,538,18
|
||||
SplashRunningTextColor=0x0
|
||||
SplashRunningTextFontSize=19
|
||||
|
||||
currentVersion=Autopsy 4.15.0
|
||||
currentVersion=Autopsy 4.16.0
|
||||
|
@ -1,4 +1,4 @@
|
||||
#Updated by build script
|
||||
#Fri, 19 Jun 2020 10:14:47 -0400
|
||||
CTL_MainWindow_Title=Autopsy 4.15.0
|
||||
CTL_MainWindow_Title_No_Project=Autopsy 4.15.0
|
||||
#Wed, 08 Jul 2020 15:15:46 -0400
|
||||
CTL_MainWindow_Title=Autopsy 4.16.0
|
||||
CTL_MainWindow_Title_No_Project=Autopsy 4.16.0
|
||||
|
@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy User Documentation"
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 4.15.0
|
||||
PROJECT_NUMBER = 4.16.0
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
@ -1025,7 +1025,7 @@ GENERATE_HTML = YES
|
||||
# The default directory is: html.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_OUTPUT = 4.15.0
|
||||
HTML_OUTPUT = 4.16.0
|
||||
|
||||
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
|
||||
# generated HTML page (for example: .htm, .php, .asp).
|
||||
|
@ -57,7 +57,7 @@ If you right-click on a case, you can open it, see the log, delete the case, or
|
||||
|
||||
Note that you can select multiple cases at once to delete. If you choose to delete a case (or cases), you'll see the following confirmation dialog:
|
||||
|
||||
\image html case_delete_confirm.png
|
||||
\image html AutoIngest/case_delete_confirm.png
|
||||
|
||||
\section auto_ingest_admin_health_monitor Health Monitor
|
||||
|
||||
|
BIN
docs/doxygen-user/images/InterestingFiles/if_create_set.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
docs/doxygen-user/images/InterestingFiles/if_export.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
docs/doxygen-user/images/InterestingFiles/if_new_rule.png
Normal file
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 70 KiB |
@ -15,7 +15,7 @@ This module allows you to make sets of rules that will be run against each file
|
||||
|
||||
\section interesting_files_config Configuration
|
||||
|
||||
To create and edit your rule sets, go to "Tools", "Options" and then select the "Interesting Files" tab. The area on the left side will show you a list of all the rule sets that are currently available. Selecting a rule set will display its description and information about each of its rules on the right side of the panel.
|
||||
To create and edit your rule sets, go to "Tools", "Options" and then select the "Interesting Files" tab. The area on the left side will show you a list of all the rule sets that are currently available. This will include the official rule sets that are included with Autopsy and any rule sets that you create. Selecting a rule set will display its description and information about each of its rules on the right side of the panel.
|
||||
|
||||
\image html InterestingFiles/main.png
|
||||
|
||||
@ -31,6 +31,8 @@ The buttons on the bottom of the left side of the panel control the rule sets.
|
||||
<li><b>Export Set</b> - Exports the selected rule set in a format that can be shared with other Autopsy users.
|
||||
</ul>
|
||||
|
||||
Note that the predefined rule sets can not be deleted or edited. If you believe you have additions that would be useful to the community, see the \ref update_interesting_files_page page for instructions on submitting updates.
|
||||
|
||||
Selecting a rule set will display its description, whether it ignores known files, and the rules contained in the set. Selecting a rule will display the conditions for that rule in the "Rule Details" section.
|
||||
|
||||
The buttons under the list of rules allow you to create new rules and edit or delete existing rules. Selecting "New Rule" will bring up a new window to create the rule.
|
||||
|
@ -93,7 +93,9 @@ The following topics are available here:
|
||||
- \ref object_detection_page
|
||||
- \ref volatility_dsp_page
|
||||
|
||||
- Community Contributions
|
||||
- \subpage translations_page
|
||||
- \subpage update_interesting_files_page
|
||||
|
||||
|
||||
If the topic you need is not listed, then you can:
|
||||
|
@ -22,7 +22,7 @@ Ingest Settings
|
||||
------
|
||||
The run-time setting for this module allows you to choose whether to keep corrupted files.
|
||||
|
||||
\image html photo_rec_settings.png
|
||||
\image html photo_rec_settings.PNG
|
||||
|
||||
Also note that the "Run ingest modules on" selection needs to include unallocated space for this module to run.
|
||||
|
||||
|
@ -22,7 +22,7 @@ At this point there are three options:
|
||||
|
||||
- New tag -- Create a new tag and add it to the file/result
|
||||
|
||||
\image html tagging_new_tag.png
|
||||
\image html tagging_new_tag.PNG
|
||||
|
||||
There are several default tag names:
|
||||
- Bookmark - Default tag for marking files of interest
|
||||
|
@ -56,7 +56,7 @@ VHD is a file format used by Microsoft Virtual Machines that is readable by Wind
|
||||
|
||||
To create a sparse VHD, check the box for "Make a VHD image..." when selecting the disk to analyze.
|
||||
|
||||
\image html createVHD.png
|
||||
\image html triage/createVHD.png
|
||||
|
||||
\section triage_scenarios Scenarios
|
||||
|
||||
|
@ -5,12 +5,12 @@ The user interface quick search feature allows you to search within the data on
|
||||
How to use it
|
||||
-----
|
||||
In order to use the search you need to select any item in the area you wish to search, and start typing. If user interface quick search is available in the area you have selected a search field will appear in the bottom left hand corner of the area. As you type the string you are searching for it will auto-update to select one of the results which matches your string. You can switch between the results which match the string you have typed with the up and down keys. The search does not support the use of regular expressions but will match against any sub-sting in the fields it searches, not just at the beginning of the field.
|
||||
\image html quick_search_result.PNG
|
||||
\image html quick_search_result.png
|
||||
|
||||
Configuration
|
||||
-----
|
||||
By default the search will match against the data in all fields which are in the currently selected area. The search will also ignore case by default. If you want to change either of these default behaviors you can click the magnifying glass with the down arrow icon and configure which columns will be searched as well as if the search should ignore case.
|
||||
\image html quick_search_configuration.PNG
|
||||
\image html quick_search_configuration.png
|
||||
|
||||
Where it can be used
|
||||
-----
|
||||
|
26
docs/doxygen-user/updating_interesting_file_sets.dox
Normal file
@ -0,0 +1,26 @@
|
||||
/*! \page update_interesting_files_page Updating the Official Interesting File Sets
|
||||
|
||||
The \ref interesting_files_identifier_page contains several official rule sets. You can select a rule set to display the rules it contains in the middle of the right side of the panel.
|
||||
|
||||
\image html InterestingFiles/if_official_rule_details.png
|
||||
|
||||
If you have one or more rules that you think should be included in an official rule set you can submit your new rules using the process below. Consult the \ref interesting_files_config section for general instructions on creating and editing interesting file sets.
|
||||
|
||||
<ol>
|
||||
<li> Create a new interesting file set. Give it a name similar to the set you wish to update to make it clear which set your new rules belong to. Do not copy the existing rule set.
|
||||
|
||||
\image html InterestingFiles/if_create_set.png
|
||||
|
||||
<li> Create your rule(s). Make sure each rule has a "Rule Name" that identifies the application it is detecting. Click the "Apply" button on the main panel when done.
|
||||
|
||||
\image html InterestingFiles/if_new_rule.png
|
||||
|
||||
<li> Export the set as XML.
|
||||
|
||||
\image html InterestingFiles/if_export.png
|
||||
|
||||
<li> Create an Autopsy Github issue that identifes the set to update and what applications were added, and attach the XML. Go to: https://github.com/sleuthkit/autopsy/issues
|
||||
</ol>
|
||||
|
||||
|
||||
*/
|
@ -14,7 +14,7 @@ If you have a disk image associated with your memory image, ingest the disk imag
|
||||
|
||||
On the next screen, you can select your memory image and then adjust the settings to choose a profile and which Volatility plugins to run.
|
||||
|
||||
\image html volatility_dsp_config.png
|
||||
\image html volatility_dsp_config.PNG
|
||||
|
||||
Next you'll see the ingest module configuration panel. No ingest modules will be run when using the Volatility data source processor, so simply hit the "Next" button. When it finishes, you may have some non-critical errors. These frequently come from the data source processor being unable to find files in the original disk image. If you did not add the associated disk image before running the Volatility data source processor on the memory image, there will be a large number of these errors but the Volatility module output will still be available.
|
||||
|
||||
|
@ -38,7 +38,7 @@ PROJECT_NAME = "Autopsy"
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 4.15.0
|
||||
PROJECT_NUMBER = 4.16.0
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears a the top of each page and should give viewer a
|
||||
@ -1066,7 +1066,7 @@ GENERATE_HTML = YES
|
||||
# The default directory is: html.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_OUTPUT = api-docs/4.15.0/
|
||||
HTML_OUTPUT = api-docs/4.16.0/
|
||||
|
||||
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
|
||||
# generated HTML page (for example: .htm, .php, .asp).
|
||||
|
@ -4,7 +4,7 @@ app.title=Autopsy
|
||||
### lowercase version of above
|
||||
app.name=${branding.token}
|
||||
### if left unset, version will default to today's date
|
||||
app.version=4.15.0
|
||||
app.version=4.16.0
|
||||
### build.type must be one of: DEVELOPMENT, RELEASE
|
||||
#build.type=RELEASE
|
||||
build.type=DEVELOPMENT
|
||||
|
220
release_scripts/diffscript.py
Normal file
@ -0,0 +1,220 @@
|
||||
"""This script determines the updated, added, and deleted properties from the '.properties-MERGED' files
|
||||
and generates a csv file containing the items changed. This script requires the python libraries:
|
||||
gitpython and jproperties. As a consequence, it also requires git >= 1.7.0 and python >= 3.4.
|
||||
"""
|
||||
|
||||
from git import Repo
|
||||
from typing import List, Dict, Tuple
|
||||
import re
|
||||
import csv
|
||||
from jproperties import Properties
|
||||
import sys
|
||||
|
||||
|
||||
class ItemChange:
|
||||
def __init__(self, rel_path: str, key: str, prev_val: str, cur_val: str):
|
||||
"""Describes the change that occurred for a particular key of a properties file.
|
||||
|
||||
Args:
|
||||
rel_path (str): The relative path of the properties file.
|
||||
key (str): The key in the properties file.
|
||||
prev_val (str): The previous value for the key.
|
||||
cur_val (str): The current value for the key.
|
||||
"""
|
||||
self.rel_path = rel_path
|
||||
self.key = key
|
||||
self.prev_val = prev_val
|
||||
self.cur_val = cur_val
|
||||
if ItemChange.has_str_content(cur_val) and not ItemChange.has_str_content(prev_val):
|
||||
self.type = 'ADDITION'
|
||||
elif not ItemChange.has_str_content(cur_val) and ItemChange.has_str_content(prev_val):
|
||||
self.type = 'DELETION'
|
||||
else:
|
||||
self.type = 'CHANGE'
|
||||
|
||||
@staticmethod
|
||||
def has_str_content(content: str):
|
||||
"""Determines whether or not the content is empty or None.
|
||||
|
||||
Args:
|
||||
content (str): The text.
|
||||
|
||||
Returns:
|
||||
bool: Whether or not it has content.
|
||||
"""
|
||||
return content is not None and len(content.strip()) > 0
|
||||
|
||||
@staticmethod
|
||||
def get_headers() -> List[str]:
|
||||
"""Returns the csv headers to insert when serializing a list of ItemChange objects to csv.
|
||||
|
||||
Returns:
|
||||
List[str]: The column headers
|
||||
"""
|
||||
return ['Relative Path', 'Key', 'Change Type', 'Previous Value', 'Current Value']
|
||||
|
||||
def get_row(self) -> List[str]:
|
||||
"""Returns the list of values to be entered as a row in csv serialization.
|
||||
|
||||
Returns:
|
||||
List[str]: The list of values to be entered as a row in csv serialization.
|
||||
"""
|
||||
return [
|
||||
self.rel_path,
|
||||
self.key,
|
||||
self.type,
|
||||
self.prev_val,
|
||||
self.cur_val]
|
||||
|
||||
|
||||
def get_entry_dict(diff_str: str) -> Dict[str, str]:
|
||||
"""Retrieves a dictionary mapping the properties represented in the string.
|
||||
|
||||
Args:
|
||||
diff_str (str): The string of the properties file.
|
||||
|
||||
Returns:
|
||||
Dict[str,str]: The mapping of keys to values in that properties file.
|
||||
"""
|
||||
props = Properties()
|
||||
props.load(diff_str, "utf-8")
|
||||
return props.properties
|
||||
|
||||
|
||||
def get_item_change(rel_path: str, key: str, prev_val: str, cur_val: str) -> ItemChange:
|
||||
"""Returns an ItemChange object if the previous value is not equal to the current value.
|
||||
|
||||
Args:
|
||||
rel_path (str): The relative path for the properties file.
|
||||
key (str): The key within the properties file for this potential change.
|
||||
prev_val (str): The previous value.
|
||||
cur_val (str): The current value.
|
||||
|
||||
Returns:
|
||||
ItemChange: The ItemChange object or None if values are the same.
|
||||
"""
|
||||
if (prev_val == cur_val):
|
||||
return None
|
||||
else:
|
||||
return ItemChange(rel_path, key, prev_val, cur_val)
|
||||
|
||||
|
||||
def get_changed(rel_path: str, a_str: str, b_str: str) -> List[ItemChange]:
|
||||
"""Given the relative path of the properties file that
|
||||
|
||||
Args:
|
||||
rel_path (str): The relative path for the properties file.
|
||||
a_str (str): The string representing the original state of the file.
|
||||
b_str (str): The string representing the current state of the file.
|
||||
|
||||
Returns:
|
||||
List[ItemChange]: The changes determined.
|
||||
"""
|
||||
print('Retrieving changes for {}...'.format(rel_path))
|
||||
a_dict = get_entry_dict(a_str)
|
||||
b_dict = get_entry_dict(b_str)
|
||||
all_keys = set().union(a_dict.keys(), b_dict.keys())
|
||||
mapped = map(lambda key: get_item_change(
|
||||
rel_path, key, a_dict.get(key), b_dict.get(key)), all_keys)
|
||||
return filter(lambda entry: entry is not None, mapped)
|
||||
|
||||
|
||||
def get_text(blob) -> str:
|
||||
return blob.data_stream.read().decode('utf-8')
|
||||
|
||||
|
||||
def get_changed_from_diff(rel_path: str, diff) -> List[ItemChange]:
|
||||
"""Determines changes from a git python diff.
|
||||
|
||||
Args:
|
||||
rel_path (str): The relative path for the properties file.
|
||||
diff: The git python diff.
|
||||
|
||||
Returns:
|
||||
List[ItemChange]: The changes in properties.
|
||||
"""
|
||||
# an item was added
|
||||
if diff.change_type == 'A':
|
||||
changes = get_changed(rel_path, '', get_text(diff.b_blob))
|
||||
# an item was deleted
|
||||
elif diff.change_type == 'D':
|
||||
changes = get_changed(rel_path, get_text(diff.a_blob), '')
|
||||
# an item was modified
|
||||
elif diff.change_type == 'M':
|
||||
changes = get_changed(rel_path, get_text(
|
||||
diff.a_blob), get_text(diff.b_blob))
|
||||
else:
|
||||
changes = []
|
||||
|
||||
return changes
|
||||
|
||||
|
||||
def get_rel_path(diff) -> str:
|
||||
"""Determines the relative path based on the git python.
|
||||
|
||||
Args:
|
||||
diff: The git python diff.
|
||||
|
||||
Returns:
|
||||
str: The determined relative path.
|
||||
"""
|
||||
if diff.b_path is not None:
|
||||
return diff.b_path
|
||||
elif diff.a_path is not None:
|
||||
return diff.a_path
|
||||
else:
|
||||
return '<Uknown Path>'
|
||||
|
||||
|
||||
def write_diff_to_csv(repo_path: str, output_path: str, commit_1_id: str, commit_2_id: str):
|
||||
"""Determines the changes made in '.properties-MERGED' files from one commit to another commit.
|
||||
|
||||
Args:
|
||||
repo_path (str): The local path to the git repo.
|
||||
output_path (str): The output path for the csv file.
|
||||
commit_1_id (str): The initial commit for the diff.
|
||||
commit_2_id (str): The latest commit for the diff.
|
||||
"""
|
||||
repo = Repo(repo_path)
|
||||
commit_1 = repo.commit(commit_1_id)
|
||||
commit_2 = repo.commit(commit_2_id)
|
||||
|
||||
diffs = commit_1.diff(commit_2)
|
||||
with open(output_path, 'w', newline='') as csvfile:
|
||||
writer = csv.writer(csvfile)
|
||||
writer.writerow(ItemChange.get_headers())
|
||||
|
||||
for diff in diffs:
|
||||
rel_path = get_rel_path(diff)
|
||||
if not rel_path.endswith('.properties-MERGED'):
|
||||
continue
|
||||
|
||||
changes = get_changed_from_diff(rel_path, diff)
|
||||
|
||||
for item_change in changes:
|
||||
writer.writerow(item_change.get_row())
|
||||
|
||||
|
||||
def print_help():
|
||||
"""Prints a quick help message.
|
||||
"""
|
||||
print("diffscript.py [path to repo] [csv output path] [commit for previous release] [commit for current release (optional; defaults to 'HEAD')]")
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) <= 3:
|
||||
print_help()
|
||||
sys.exit(1)
|
||||
|
||||
repo_path = sys.argv[1]
|
||||
output_path = sys.argv[2]
|
||||
commit_1_id = sys.argv[3]
|
||||
commit_2_id = sys.argv[4] if len(sys.argv) > 4 else 'HEAD'
|
||||
|
||||
write_diff_to_csv(repo_path, output_path, commit_1_id, commit_2_id)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,23 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<INTERESTING_FILE_SETS>
|
||||
<INTERESTING_FILE_SET description="Finds Encryption Programs installed on the machine" ignoreKnown="false" name="Encryption Programs" standardSet="true" versionNumber="1">
|
||||
<NAME name="Cryptomator" regex="false" typeFilter="file">Cryptomator.exe</NAME>
|
||||
<NAME name="Gpg4win" regex="false" typeFilter="file">gdbus.exe</NAME>
|
||||
<NAME name="Gihosoft File Encryption" regex="false" typeFilter="file">GFileEncryption.exe</NAME>
|
||||
<NAME name="EncFSMP" regex="false" typeFilter="file">EncFSMP.exe</NAME>
|
||||
<NAME name="Tor Browser" regex="false" typeFilter="all">Tor Browser</NAME>
|
||||
<NAME name="Proton Bridge" regex="false" typeFilter="file">Desktop-Bridge.exe</NAME>
|
||||
<NAME name="CryptoExpert 8" regex="false" typeFilter="file">cexpertcmd.exe</NAME>
|
||||
<NAME name="Encrypto" regex="false" typeFilter="file">Encrypto.exe</NAME>
|
||||
<INTERESTING_FILE_SET description="Finds Encryption Programs installed on the machine" ignoreKnown="false" name="Encryption Programs" standardSet="true" versionNumber="2">
|
||||
<NAME name="aescrypt" regex="false" typeFilter="file">aescrypt.exe</NAME>
|
||||
<NAME name="certainsafe" regex="false" typeFilter="file">certainsafe.exe</NAME>
|
||||
<NAME name="Folder Lock" regex="false" typeFilter="file">Folder Lock.exe</NAME>
|
||||
<NAME name="BitLocker" regex="false" typeFilter="all">BitLockerDeviceEncryption.exe</NAME>
|
||||
<NAME name="VeraCrypt" regex="false" typeFilter="file">VeraCrypt.exe</NAME>
|
||||
<NAME name="CryptoExpert 8" regex="false" typeFilter="file"> cexpert_gui.exe</NAME>
|
||||
<NAME name="GnuPG" regex="false" typeFilter="file"> gpg.exe</NAME>
|
||||
<NAME name="AxCrypt" regex="false" typeFilter="file">AxCrypt.exe</NAME>
|
||||
<NAME name="Tutanota Desktop" regex="false" typeFilter="file">Tutanota Desktop.exe</NAME>
|
||||
<NAME name="BitLocker" regex="false" typeFilter="all">BitLockerDeviceEncryption.exe</NAME>
|
||||
<NAME name="certainsafe" regex="false" typeFilter="file">certainsafe.exe</NAME>
|
||||
<NAME name="CryptoExpert 8" regex="false" typeFilter="file">cexpertcmd.exe</NAME>
|
||||
<NAME name="CryptoExpert 8" regex="false" typeFilter="file"> cexpert_gui.exe</NAME>
|
||||
<NAME name="Cryptomator" regex="false" typeFilter="file">Cryptomator.exe</NAME>
|
||||
<NAME name="Ecrypt(Linux)" regex="true" typeFilter="all">ecryptfs</NAME>
|
||||
<NAME name="EncFSMP" regex="false" typeFilter="file">EncFSMP.exe</NAME>
|
||||
<NAME name="Encrypto" regex="false" typeFilter="file">Encrypto.exe</NAME>
|
||||
<NAME name="Folder Lock" regex="false" typeFilter="file">Folder Lock.exe</NAME>
|
||||
<NAME name="Gihosoft File Encryption" regex="false" typeFilter="file">GFileEncryption.exe</NAME>
|
||||
<NAME name="GnuPG" regex="false" typeFilter="file"> gpg.exe</NAME>
|
||||
<NAME name="Gpg4win" regex="false" typeFilter="file">gdbus.exe</NAME>
|
||||
<NAME name="KeePass" regex="false" typeFilter="file">KeePass.exe</NAME>
|
||||
<NAME name="LUKS(Linux)" regex="true" typeFilter="all">luks</NAME>
|
||||
<NAME name="Proton Bridge" regex="false" typeFilter="file">Desktop-Bridge.exe</NAME>
|
||||
<NAME name="Tor Browser" regex="false" typeFilter="all">Tor Browser</NAME>
|
||||
<NAME name="Tutanota Desktop" regex="false" typeFilter="file">Tutanota Desktop.exe</NAME>
|
||||
<NAME name="VeraCrypt" regex="false" typeFilter="file">VeraCrypt.exe</NAME>
|
||||
</INTERESTING_FILE_SET>
|
||||
</INTERESTING_FILE_SETS>
|
||||
|
@ -45,7 +45,7 @@
|
||||
<compile-dependency/>
|
||||
<run-dependency>
|
||||
<release-version>10</release-version>
|
||||
<specification-version>10.19</specification-version>
|
||||
<specification-version>10.20</specification-version>
|
||||
</run-dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|