From 4f9b1af344f54b2fa05211b11fb2a3e93ba535f1 Mon Sep 17 00:00:00 2001
From: Mark McKinnon <Mark.McKinnon13@outlook.com>
Date: Tue, 15 Aug 2023 14:20:06 -0400
Subject: [PATCH] Change option screens

change option screens
---
 .../autopsy/ctapi/CTCloudHttpClient.java      |   3 +-
 .../autopsy/ctoptions/CTOptionsPanel.java     |   7 +-
 .../ctoptions/ctcloud/Bundle.properties       |   7 +-
 .../ctcloud/Bundle.properties-MERGED          |   7 +-
 .../ctcloud/CTMalwareScannerOptionsPanel.form | 356 +++++++++++-------
 .../ctcloud/CTMalwareScannerOptionsPanel.java | 135 +++++--
 .../autopsy/incidentoptions/Bundle.properties |  14 +
 .../incidentoptions/Bundle.properties-MERGED  |  14 +
 .../CTIncidentImportOptionsPanel.form         | 168 +++++++++
 .../CTIncidentImportOptionsPanel.java         | 287 ++++++++++++++
 .../autopsy/incidentoptions/CTSettings.java   |  71 ++++
 .../CTSettingsPersistence.java                |  79 ++++
 12 files changed, 970 insertions(+), 178 deletions(-)
 create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties
 create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties-MERGED
 create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTIncidentImportOptionsPanel.form
 create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTIncidentImportOptionsPanel.java
 create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTSettings.java
 create mode 100644 Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTSettingsPersistence.java

diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java
index 0a280c87e8..44e94f82a1 100644
--- a/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctapi/CTCloudHttpClient.java
@@ -84,7 +84,8 @@
 class CTCloudHttpClient {
 
     private static final Logger LOGGER = Logger.getLogger(CTCloudHttpClient.class.getName());
-    private static final String HOST_URL = Version.getBuildType() == Version.Type.RELEASE ? Constants.CT_CLOUD_SERVER : Constants.CT_CLOUD_DEV_SERVER;
+//    private static final String HOST_URL = Version.getBuildType() == Version.Type.RELEASE ? Constants.CT_CLOUD_SERVER : Constants.CT_CLOUD_DEV_SERVER;
+    private static final String HOST_URL = Version.getBuildType() == Version.Type.RELEASE ? Constants.CT_CLOUD_DEV_SERVER : Constants.CT_CLOUD_SERVER;
     private static final String NB_PROXY_SELECTOR_NAME = "org.netbeans.core.NbProxySelector";
 
     private static final int CONNECTION_TIMEOUT_MS = 58 * 1000; // milli sec
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java
index 745aa1d03d..c0999e3b9e 100644
--- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/CTOptionsPanel.java
@@ -64,12 +64,11 @@ public CTOptionsPanel() {
                     }
                 })
                 .filter(item -> item != null)
-                .sorted(Comparator.comparing(p -> p.getClass().getSimpleName().toUpperCase()))
                 .collect(Collectors.toList());
-        addSubOptionsPanels(new LicenseDisclaimerPanel(), this.subPanels);
+        addSubOptionsPanels(this.subPanels);
     }
 
-    private void addSubOptionsPanels(JPanel disclaimerPanel, List<CTOptionsSubPanel> subPanels) {
+    private void addSubOptionsPanels(List<CTOptionsSubPanel> subPanels) {
             GridBagConstraints disclaimerConstraints = new GridBagConstraints();
             disclaimerConstraints.gridx = 0;
             disclaimerConstraints.gridy = 0;
@@ -79,7 +78,7 @@ private void addSubOptionsPanels(JPanel disclaimerPanel, List<CTOptionsSubPanel>
             disclaimerConstraints.weighty = 0;
             disclaimerConstraints.weightx = 0;
 
-            contentPane.add(disclaimerPanel, disclaimerConstraints);
+//            contentPane.add(disclaimerPanel, disclaimerConstraints);
         
         for (int i = 0; i < subPanels.size(); i++) {
             CTOptionsSubPanel subPanel = subPanels.get(i);
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties
index d0395d6a1a..3b48b0ea4e 100644
--- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties
@@ -9,13 +9,11 @@ CTLicenseDialog.cancelButton.text=Cancel
 CTLicenseDialog.okButton.text=Ok
 CTLicenseDialog.warningLabel.text=
 CTMalwareScannerOptionsPanel.hashLookupsRemainingLabel.text=
-CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text=
 CTMalwareScannerOptionsPanel.countersResetLabel.text=
-CTMalwareScannerOptionsPanel.licenseInfoPanel.border.title=License Info
 CTMalwareScannerOptionsPanel.maxFileUploadsLabel.text=
 CTMalwareScannerOptionsPanel.maxHashLookupsLabel.text=
 CTMalwareScannerOptionsPanel.malwareScansMessageLabel.text=
-CTMalwareScannerOptionsPanel.malwareScansPanel.border.title=Malware Scans
+CTMalwareScannerOptionsPanel.malwareScansPanel.border.title=Malware Scanner
 CTMalwareScannerOptionsPanel.licenseInfoAddButton.text=Add License
 CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text=
 CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text=
@@ -26,3 +24,6 @@ EULADialog.acceptButton.text=Accept
 EULADialog.title=Cyber Triage End User License Agreement
 CTMalwareScannerOptionsPanel.fileUploadCheckbox.text=Upload file if hash lookup produces no results
 CTMalwareScannerOptionsPanel.fileUploadPanel.border.title=File Upload
+CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text=
+CTMalwareScannerOptionsPanel.disclaimer.text=<html>The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a paid subscription to use.</html>
+CTMalwareScannerOptionsPanel.purchaseFromLabel.text=For licensing information, visit
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED
index 18e60839f3..d3922b3c09 100644
--- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties-MERGED
@@ -10,13 +10,11 @@ CTLicenseDialog.okButton.text=Ok
 CTLicenseDialog.warningLabel.text=
 CTLicenseDialog_verifyInput_licenseNumberError=<html>Please verify license number format of 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'</html>
 CTMalwareScannerOptionsPanel.hashLookupsRemainingLabel.text=
-CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text=
 CTMalwareScannerOptionsPanel.countersResetLabel.text=
-CTMalwareScannerOptionsPanel.licenseInfoPanel.border.title=License Info
 CTMalwareScannerOptionsPanel.maxFileUploadsLabel.text=
 CTMalwareScannerOptionsPanel.maxHashLookupsLabel.text=
 CTMalwareScannerOptionsPanel.malwareScansMessageLabel.text=
-CTMalwareScannerOptionsPanel.malwareScansPanel.border.title=Malware Scans
+CTMalwareScannerOptionsPanel.malwareScansPanel.border.title=Malware Scanner
 CTMalwareScannerOptionsPanel.licenseInfoAddButton.text=Add License
 CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text=
 CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text=
@@ -64,3 +62,6 @@ EULADialog.acceptButton.text=Accept
 EULADialog.title=Cyber Triage End User License Agreement
 CTMalwareScannerOptionsPanel.fileUploadCheckbox.text=Upload file if hash lookup produces no results
 CTMalwareScannerOptionsPanel.fileUploadPanel.border.title=File Upload
+CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text=
+CTMalwareScannerOptionsPanel.disclaimer.text=<html>The Cyber Triage Malware Scanner module uses 40+ malware scanning engines to identify if Windows executables are malicious. It requires a paid subscription to use.</html>
+CTMalwareScannerOptionsPanel.purchaseFromLabel.text=For licensing information, visit
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.form b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.form
index 8557f946d9..e9886eadf2 100644
--- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.form
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.form
@@ -11,7 +11,7 @@
     <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
     <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
     <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
-    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-69,0,0,1,-29"/>
+    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,90,0,0,1,-29"/>
   </AuxValues>
 
   <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
@@ -26,10 +26,6 @@
           </Border>
         </Property>
       </Properties>
-      <AuxValues>
-        <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
-        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
-      </AuxValues>
       <Constraints>
         <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
           <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
@@ -59,98 +55,11 @@
         </Component>
       </SubComponents>
     </Container>
-    <Container class="javax.swing.JPanel" name="licenseInfoPanel">
-      <Properties>
-        <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
-          <Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
-            <TitledBorder title="License Info">
-              <ResourceString PropertyName="titleX" bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-            </TitledBorder>
-          </Border>
-        </Property>
-      </Properties>
-      <AuxValues>
-        <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
-        <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
-      </AuxValues>
-      <Constraints>
-        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-          <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
-        </Constraint>
-      </Constraints>
-
-      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
-      <SubComponents>
-        <Component class="javax.swing.JLabel" name="licenseInfoMessageLabel">
-          <Properties>
-            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-            </Property>
-          </Properties>
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-              <GridBagConstraints gridX="0" gridY="0" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
-            </Constraint>
-          </Constraints>
-        </Component>
-        <Component class="javax.swing.JLabel" name="licenseInfoUserLabel">
-          <Properties>
-            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-            </Property>
-          </Properties>
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-              <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
-            </Constraint>
-          </Constraints>
-        </Component>
-        <Component class="javax.swing.JLabel" name="licenseInfoExpiresLabel">
-          <Properties>
-            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-            </Property>
-          </Properties>
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-              <GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
-            </Constraint>
-          </Constraints>
-        </Component>
-        <Component class="javax.swing.JLabel" name="licenseInfoIdLabel">
-          <Properties>
-            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-            </Property>
-          </Properties>
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-              <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
-            </Constraint>
-          </Constraints>
-        </Component>
-        <Component class="javax.swing.JButton" name="licenseInfoAddButton">
-          <Properties>
-            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoAddButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-            </Property>
-          </Properties>
-          <Events>
-            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="licenseInfoAddButtonActionPerformed"/>
-          </Events>
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-              <GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="12" weightX="1.0" weightY="0.0"/>
-            </Constraint>
-          </Constraints>
-        </Component>
-      </SubComponents>
-    </Container>
     <Container class="javax.swing.JPanel" name="malwareScansPanel">
       <Properties>
         <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
           <Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
-            <TitledBorder title="Malware Scans">
+            <TitledBorder title="Malware Scanner">
               <ResourceString PropertyName="titleX" bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.malwareScansPanel.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
             </TitledBorder>
           </Border>
@@ -164,78 +73,237 @@
 
       <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
       <SubComponents>
-        <Component class="javax.swing.JLabel" name="malwareScansMessageLabel">
+        <Component class="javax.swing.JLabel" name="disclaimer">
           <Properties>
             <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.malwareScansMessageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.disclaimer.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
             </Property>
+            <Property name="verticalAlignment" type="int" value="1"/>
           </Properties>
+          <AuxValues>
+            <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
+            <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
+          </AuxValues>
           <Constraints>
             <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-              <GridBagConstraints gridX="0" gridY="0" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+              <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
             </Constraint>
           </Constraints>
         </Component>
-        <Component class="javax.swing.JLabel" name="maxHashLookupsLabel">
-          <Properties>
-            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.maxHashLookupsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-            </Property>
-          </Properties>
+        <Container class="javax.swing.JPanel" name="licenseInfoPanel">
+          <AuxValues>
+            <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
+            <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
+          </AuxValues>
           <Constraints>
             <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-              <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+              <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
             </Constraint>
           </Constraints>
-        </Component>
-        <Component class="javax.swing.JLabel" name="maxFileUploadsLabel">
-          <Properties>
-            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.maxFileUploadsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-            </Property>
-          </Properties>
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-              <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
-            </Constraint>
-          </Constraints>
-        </Component>
-        <Component class="javax.swing.JLabel" name="countersResetLabel">
-          <Properties>
-            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.countersResetLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-            </Property>
-          </Properties>
-          <Constraints>
-            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-              <GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
-            </Constraint>
-          </Constraints>
-        </Component>
-        <Component class="javax.swing.JLabel" name="hashLookupsRemainingLabel">
-          <Properties>
-            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.hashLookupsRemainingLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-            </Property>
-          </Properties>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JLabel" name="licenseInfoMessageLabel">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="0" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Component class="javax.swing.JLabel" name="licenseInfoExpiresLabel">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Component class="javax.swing.JLabel" name="licenseInfoIdLabel">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Component class="javax.swing.JLabel" name="licenseInfoUserLabel">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Component class="javax.swing.JButton" name="licenseInfoAddButton">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.licenseInfoAddButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Events>
+                <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="licenseInfoAddButtonActionPerformed"/>
+              </Events>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="12" weightX="1.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+          </SubComponents>
+        </Container>
+        <Container class="javax.swing.JPanel" name="malwareScannerStatsPanel">
           <Constraints>
             <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-              <GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+              <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
             </Constraint>
           </Constraints>
-        </Component>
-        <Component class="javax.swing.JLabel" name="fileUploadsRemainingLabel">
-          <Properties>
-            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
-              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.fileUploadsRemainingLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
-            </Property>
-          </Properties>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JLabel" name="malwareScansMessageLabel">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.malwareScansMessageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="0" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Component class="javax.swing.JLabel" name="maxHashLookupsLabel">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.maxHashLookupsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Component class="javax.swing.JLabel" name="maxFileUploadsLabel">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.maxFileUploadsLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="2" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Component class="javax.swing.JLabel" name="countersResetLabel">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.countersResetLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Component class="javax.swing.JLabel" name="hashLookupsRemainingLabel">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.hashLookupsRemainingLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Component class="javax.swing.JLabel" name="fileUploadsRemainingLabel">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.fileUploadsRemainingLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+          </SubComponents>
+        </Container>
+        <Container class="javax.swing.JPanel" name="purchasePanel">
+          <AuxValues>
+            <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
+            <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
+          </AuxValues>
           <Constraints>
             <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
-              <GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+              <GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
             </Constraint>
           </Constraints>
-        </Component>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JLabel" name="purchaseFromLabel">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+                  <ResourceString bundle="com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/Bundle.properties" key="CTMalwareScannerOptionsPanel.purchaseFromLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+                </Property>
+              </Properties>
+              <AuxValues>
+                <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
+                <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
+              </AuxValues>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+            <Component class="javax.swing.JLabel" name="purchaseLink">
+              <Properties>
+                <Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+                  <Connection code="getHtmlLink(PURCHASE_URL)" type="code"/>
+                </Property>
+                <Property name="cursor" type="java.awt.Cursor" editor="org.netbeans.modules.form.editors2.CursorEditor">
+                  <Color id="Hand Cursor"/>
+                </Property>
+              </Properties>
+              <Events>
+                <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="purchaseLinkMouseClicked"/>
+              </Events>
+              <AuxValues>
+                <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
+                <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
+              </AuxValues>
+              <Constraints>
+                <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+                  <GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
+                </Constraint>
+              </Constraints>
+            </Component>
+          </SubComponents>
+        </Container>
       </SubComponents>
     </Container>
   </SubComponents>
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java
index 2a09c1ed55..496e01d69f 100644
--- a/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/ctoptions/ctcloud/CTMalwareScannerOptionsPanel.java
@@ -28,9 +28,12 @@
 import com.basistech.df.cybertriage.autopsy.ctapi.json.LicenseResponse;
 import com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil;
 import com.basistech.df.cybertriage.autopsy.ctapi.util.LicenseDecryptorUtil.InvalidLicenseException;
+import java.awt.Desktop;
 import java.awt.event.ComponentAdapter;
 import java.awt.event.ComponentEvent;
 import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.util.Optional;
@@ -73,6 +76,8 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
     private volatile String licenseInfoMessage = null;
     private volatile LicenseFetcher licenseFetcher = null;
 
+    private static final String PURCHASE_URL = "https://cybertriage.com/autopsy-checkout";
+
     private volatile AuthTokenResponse authTokenResponse = null;
     private volatile String authTokenMessage = null;
     private volatile AuthTokenFetcher authTokenFetcher = null;
@@ -83,6 +88,8 @@ public class CTMalwareScannerOptionsPanel extends CTOptionsSubPanel {
     public CTMalwareScannerOptionsPanel() {
         initComponents();
 
+        this.fileUploadPanel.setVisible(false);
+        
         this.addComponentListener(new ComponentAdapter() {
             @Override
             public void componentHidden(ComponentEvent e) {
@@ -136,6 +143,22 @@ public synchronized void loadSettings() {
         setIngestSettings(ingestSettings);
     }
 
+    private static String getHtmlLink(String url) {
+        return "<html><span style=\"color: blue; text-decoration: underline\">" + url + "</span></html>";
+    }
+
+    private void gotoLink(String url) {
+        if (Desktop.isDesktopSupported()) {
+            try {
+                Desktop.getDesktop().browse(new URI(url));
+            } catch (IOException | URISyntaxException e) {
+                logger.log(Level.SEVERE, "Error opening link to: " + url, e);
+            }
+        } else {
+            logger.log(Level.WARNING, "Desktop API is not supported.  Link cannot be opened.");
+        }
+    }
+
     private synchronized LicenseResponse getLicenseInfo() {
         return this.licenseInfo == null ? null : this.licenseInfo.getLicenseResponse();
     }
@@ -220,21 +243,26 @@ private synchronized void loadMalwareScansInfo(LicenseInfo licenseInfo) {
     private void initComponents() {
         java.awt.GridBagConstraints gridBagConstraints;
 
-        javax.swing.JPanel fileUploadPanel = new javax.swing.JPanel();
+        fileUploadPanel = new javax.swing.JPanel();
         fileUploadCheckbox = new javax.swing.JCheckBox();
+        malwareScansPanel = new javax.swing.JPanel();
+        javax.swing.JLabel disclaimer = new javax.swing.JLabel();
         javax.swing.JPanel licenseInfoPanel = new javax.swing.JPanel();
         licenseInfoMessageLabel = new javax.swing.JLabel();
-        licenseInfoUserLabel = new javax.swing.JLabel();
         licenseInfoExpiresLabel = new javax.swing.JLabel();
         licenseInfoIdLabel = new javax.swing.JLabel();
+        licenseInfoUserLabel = new javax.swing.JLabel();
         licenseInfoAddButton = new javax.swing.JButton();
-        malwareScansPanel = new javax.swing.JPanel();
+        malwareScannerStatsPanel = new javax.swing.JPanel();
         malwareScansMessageLabel = new javax.swing.JLabel();
         maxHashLookupsLabel = new javax.swing.JLabel();
         maxFileUploadsLabel = new javax.swing.JLabel();
         countersResetLabel = new javax.swing.JLabel();
         hashLookupsRemainingLabel = new javax.swing.JLabel();
         fileUploadsRemainingLabel = new javax.swing.JLabel();
+        javax.swing.JPanel purchasePanel = new javax.swing.JPanel();
+        javax.swing.JLabel purchaseFromLabel = new javax.swing.JLabel();
+        javax.swing.JLabel purchaseLink = new javax.swing.JLabel();
 
         setLayout(new java.awt.GridBagLayout());
 
@@ -267,7 +295,20 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
         gridBagConstraints.weightx = 1.0;
         add(fileUploadPanel, gridBagConstraints);
 
-        licenseInfoPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoPanel.border.title"))); // NOI18N
+        malwareScansPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.malwareScansPanel.border.title"))); // NOI18N
+        malwareScansPanel.setLayout(new java.awt.GridBagLayout());
+
+        org.openide.awt.Mnemonics.setLocalizedText(disclaimer, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.disclaimer.text")); // NOI18N
+        disclaimer.setVerticalAlignment(javax.swing.SwingConstants.TOP);
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.weightx = 1.0;
+        gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
+        malwareScansPanel.add(disclaimer, gridBagConstraints);
+
         licenseInfoPanel.setLayout(new java.awt.GridBagLayout());
 
         org.openide.awt.Mnemonics.setLocalizedText(licenseInfoMessageLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoMessageLabel.text")); // NOI18N
@@ -281,32 +322,32 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
         gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
         licenseInfoPanel.add(licenseInfoMessageLabel, gridBagConstraints);
 
-        org.openide.awt.Mnemonics.setLocalizedText(licenseInfoUserLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text")); // NOI18N
+        org.openide.awt.Mnemonics.setLocalizedText(licenseInfoExpiresLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text")); // NOI18N
         gridBagConstraints = new java.awt.GridBagConstraints();
         gridBagConstraints.gridx = 0;
         gridBagConstraints.gridy = 1;
         gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
         gridBagConstraints.weightx = 1.0;
         gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
-        licenseInfoPanel.add(licenseInfoUserLabel, gridBagConstraints);
+        licenseInfoPanel.add(licenseInfoExpiresLabel, gridBagConstraints);
 
-        org.openide.awt.Mnemonics.setLocalizedText(licenseInfoExpiresLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoExpiresLabel.text")); // NOI18N
+        org.openide.awt.Mnemonics.setLocalizedText(licenseInfoIdLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text")); // NOI18N
         gridBagConstraints = new java.awt.GridBagConstraints();
         gridBagConstraints.gridx = 1;
         gridBagConstraints.gridy = 1;
         gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
         gridBagConstraints.weightx = 1.0;
         gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
-        licenseInfoPanel.add(licenseInfoExpiresLabel, gridBagConstraints);
+        licenseInfoPanel.add(licenseInfoIdLabel, gridBagConstraints);
 
-        org.openide.awt.Mnemonics.setLocalizedText(licenseInfoIdLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoIdLabel.text")); // NOI18N
+        org.openide.awt.Mnemonics.setLocalizedText(licenseInfoUserLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoUserLabel.text")); // NOI18N
         gridBagConstraints = new java.awt.GridBagConstraints();
         gridBagConstraints.gridx = 0;
         gridBagConstraints.gridy = 2;
         gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
         gridBagConstraints.weightx = 1.0;
-        gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
-        licenseInfoPanel.add(licenseInfoIdLabel, gridBagConstraints);
+        gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
+        licenseInfoPanel.add(licenseInfoUserLabel, gridBagConstraints);
 
         org.openide.awt.Mnemonics.setLocalizedText(licenseInfoAddButton, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.licenseInfoAddButton.text")); // NOI18N
         licenseInfoAddButton.addActionListener(new java.awt.event.ActionListener() {
@@ -328,10 +369,9 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
         gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
         gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
         gridBagConstraints.weightx = 1.0;
-        add(licenseInfoPanel, gridBagConstraints);
+        malwareScansPanel.add(licenseInfoPanel, gridBagConstraints);
 
-        malwareScansPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.malwareScansPanel.border.title"))); // NOI18N
-        malwareScansPanel.setLayout(new java.awt.GridBagLayout());
+        malwareScannerStatsPanel.setLayout(new java.awt.GridBagLayout());
 
         org.openide.awt.Mnemonics.setLocalizedText(malwareScansMessageLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.malwareScansMessageLabel.text")); // NOI18N
         gridBagConstraints = new java.awt.GridBagConstraints();
@@ -342,7 +382,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
         gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
         gridBagConstraints.weightx = 1.0;
         gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
-        malwareScansPanel.add(malwareScansMessageLabel, gridBagConstraints);
+        malwareScannerStatsPanel.add(malwareScansMessageLabel, gridBagConstraints);
 
         org.openide.awt.Mnemonics.setLocalizedText(maxHashLookupsLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.maxHashLookupsLabel.text")); // NOI18N
         gridBagConstraints = new java.awt.GridBagConstraints();
@@ -351,16 +391,17 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
         gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
         gridBagConstraints.weightx = 1.0;
         gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
-        malwareScansPanel.add(maxHashLookupsLabel, gridBagConstraints);
+        malwareScannerStatsPanel.add(maxHashLookupsLabel, gridBagConstraints);
 
         org.openide.awt.Mnemonics.setLocalizedText(maxFileUploadsLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.maxFileUploadsLabel.text")); // NOI18N
         gridBagConstraints = new java.awt.GridBagConstraints();
         gridBagConstraints.gridx = 0;
         gridBagConstraints.gridy = 2;
+        gridBagConstraints.gridwidth = 2;
         gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
         gridBagConstraints.weightx = 1.0;
         gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
-        malwareScansPanel.add(maxFileUploadsLabel, gridBagConstraints);
+        malwareScannerStatsPanel.add(maxFileUploadsLabel, gridBagConstraints);
 
         org.openide.awt.Mnemonics.setLocalizedText(countersResetLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.countersResetLabel.text")); // NOI18N
         gridBagConstraints = new java.awt.GridBagConstraints();
@@ -369,7 +410,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
         gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
         gridBagConstraints.weightx = 1.0;
         gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
-        malwareScansPanel.add(countersResetLabel, gridBagConstraints);
+        malwareScannerStatsPanel.add(countersResetLabel, gridBagConstraints);
 
         org.openide.awt.Mnemonics.setLocalizedText(hashLookupsRemainingLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.hashLookupsRemainingLabel.text")); // NOI18N
         gridBagConstraints = new java.awt.GridBagConstraints();
@@ -378,7 +419,7 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
         gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
         gridBagConstraints.weightx = 1.0;
         gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
-        malwareScansPanel.add(hashLookupsRemainingLabel, gridBagConstraints);
+        malwareScannerStatsPanel.add(hashLookupsRemainingLabel, gridBagConstraints);
 
         org.openide.awt.Mnemonics.setLocalizedText(fileUploadsRemainingLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.fileUploadsRemainingLabel.text")); // NOI18N
         gridBagConstraints = new java.awt.GridBagConstraints();
@@ -387,7 +428,46 @@ public void actionPerformed(java.awt.event.ActionEvent evt) {
         gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
         gridBagConstraints.weightx = 1.0;
         gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
-        malwareScansPanel.add(fileUploadsRemainingLabel, gridBagConstraints);
+        malwareScannerStatsPanel.add(fileUploadsRemainingLabel, gridBagConstraints);
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 2;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        malwareScansPanel.add(malwareScannerStatsPanel, gridBagConstraints);
+
+        purchasePanel.setLayout(new java.awt.GridBagLayout());
+
+        org.openide.awt.Mnemonics.setLocalizedText(purchaseFromLabel, org.openide.util.NbBundle.getMessage(CTMalwareScannerOptionsPanel.class, "CTMalwareScannerOptionsPanel.purchaseFromLabel.text")); // NOI18N
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        purchasePanel.add(purchaseFromLabel, gridBagConstraints);
+
+        org.openide.awt.Mnemonics.setLocalizedText(purchaseLink, getHtmlLink(PURCHASE_URL));
+        purchaseLink.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR));
+        purchaseLink.addMouseListener(new java.awt.event.MouseAdapter() {
+            public void mouseClicked(java.awt.event.MouseEvent evt) {
+                purchaseLinkMouseClicked(evt);
+            }
+        });
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.weightx = 1.0;
+        gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
+        purchasePanel.add(purchaseLink, gridBagConstraints);
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 3;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.weightx = 1.0;
+        gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
+        malwareScansPanel.add(purchasePanel, gridBagConstraints);
 
         gridBagConstraints = new java.awt.GridBagConstraints();
         gridBagConstraints.gridx = 0;
@@ -431,6 +511,10 @@ private void fileUploadCheckboxActionPerformed(java.awt.event.ActionEvent evt) {
         this.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
     }//GEN-LAST:event_fileUploadCheckboxActionPerformed
 
+    private void purchaseLinkMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_purchaseLinkMouseClicked
+        gotoLink(PURCHASE_URL);
+    }//GEN-LAST:event_purchaseLinkMouseClicked
+
     @NbBundle.Messages({
         "# {0} - userName",
         "# {1} - email",
@@ -646,12 +730,15 @@ public AuthTokenFetcher(DecryptedLicenseResponse decryptedLicense) {
         }
 
         @Override
-        protected AuthTokenResponse doInBackground() throws Exception {
+        protected AuthTokenResponse doInBackground() throws Exception, CTCloudException {
             if (this.isCancelled()) {
                 return null;
             }
-
-            return ctApiDAO.getAuthToken(decryptedLicense);
+            try {
+                return ctApiDAO.getAuthToken(decryptedLicense);
+            } catch (CTCloudException ex) {
+                return null;
+            }
         }
 
         @Override
@@ -692,6 +779,7 @@ protected void done() {
     // Variables declaration - do not modify//GEN-BEGIN:variables
     private javax.swing.JLabel countersResetLabel;
     private javax.swing.JCheckBox fileUploadCheckbox;
+    private javax.swing.JPanel fileUploadPanel;
     private javax.swing.JLabel fileUploadsRemainingLabel;
     private javax.swing.JLabel hashLookupsRemainingLabel;
     private javax.swing.JButton licenseInfoAddButton;
@@ -699,6 +787,7 @@ protected void done() {
     private javax.swing.JLabel licenseInfoIdLabel;
     private javax.swing.JLabel licenseInfoMessageLabel;
     private javax.swing.JLabel licenseInfoUserLabel;
+    private javax.swing.JPanel malwareScannerStatsPanel;
     private javax.swing.JLabel malwareScansMessageLabel;
     private javax.swing.JPanel malwareScansPanel;
     private javax.swing.JLabel maxFileUploadsLabel;
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties b/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties
new file mode 100644
index 0000000000..ebde860d0b
--- /dev/null
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties
@@ -0,0 +1,14 @@
+
+# Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+# Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template
+
+CTIncidentImportOptionsPanel.border.title=Local Settings
+CTIncidentImportOptionsPanel.fileRepoPathLabel.text=Update the Cyber Triage Data Folder if you are not using the default location:
+CTIncidentImportOptionsPanel.fileRepoPathField.text=
+CTIncidentImportOptionsPanel.fileRepoBrowseButton.text=Browse
+CTIncidentImportOptionsPanel.caseOpenWarningLabel.text=Some settings cannot be modified while a case is open.
+CTIncidentImportOptionsPanel.fileRepoFileChooser.title=Cyber Triage Data Folder
+CTIncidentImportOptionsPanel.border.title_1=Incident Importer
+CTIncidentImportOptionsPanel.incidentTextLabel.text=The Cyber Triage Incident Import module allows you to open data collected by Cyber Triage in Autopsy.  To use this feature you must install the Cyber Triage Import Module.
+CTincidentImportOptionsPanel.instructionsTextLabel.text=
+CTIncidentImportOptionsPanel.instructionsTextLabel.text=For instructions on obtaining the module refer to: 
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties-MERGED b/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties-MERGED
new file mode 100644
index 0000000000..ebde860d0b
--- /dev/null
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties-MERGED
@@ -0,0 +1,14 @@
+
+# Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+# Click nbfs://nbhost/SystemFileSystem/Templates/Other/properties.properties to edit this template
+
+CTIncidentImportOptionsPanel.border.title=Local Settings
+CTIncidentImportOptionsPanel.fileRepoPathLabel.text=Update the Cyber Triage Data Folder if you are not using the default location:
+CTIncidentImportOptionsPanel.fileRepoPathField.text=
+CTIncidentImportOptionsPanel.fileRepoBrowseButton.text=Browse
+CTIncidentImportOptionsPanel.caseOpenWarningLabel.text=Some settings cannot be modified while a case is open.
+CTIncidentImportOptionsPanel.fileRepoFileChooser.title=Cyber Triage Data Folder
+CTIncidentImportOptionsPanel.border.title_1=Incident Importer
+CTIncidentImportOptionsPanel.incidentTextLabel.text=The Cyber Triage Incident Import module allows you to open data collected by Cyber Triage in Autopsy.  To use this feature you must install the Cyber Triage Import Module.
+CTincidentImportOptionsPanel.instructionsTextLabel.text=
+CTIncidentImportOptionsPanel.instructionsTextLabel.text=For instructions on obtaining the module refer to: 
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTIncidentImportOptionsPanel.form b/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTIncidentImportOptionsPanel.form
new file mode 100644
index 0000000000..d887a3765e
--- /dev/null
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTIncidentImportOptionsPanel.form
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
+  <Properties>
+    <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+      <Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
+        <TitledBorder title="Incident Importer">
+          <ResourceString PropertyName="titleX" bundle="com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties" key="CTIncidentImportOptionsPanel.border.title_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+        </TitledBorder>
+      </Border>
+    </Property>
+  </Properties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-50,0,0,1,-17"/>
+  </AuxValues>
+
+  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+  <SubComponents>
+    <Container class="javax.swing.JPanel" name="incidentTextPanel">
+      <Constraints>
+        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+          <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
+        </Constraint>
+      </Constraints>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JLabel" name="incidentTextLabel">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties" key="CTIncidentImportOptionsPanel.incidentTextLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Container class="javax.swing.JPanel" name="instructionsPanel">
+      <Constraints>
+        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+          <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
+        </Constraint>
+      </Constraints>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JLabel" name="instructionsTextLabel">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties" key="CTIncidentImportOptionsPanel.instructionsTextLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+          <AccessibilityProperties>
+            <Property name="AccessibleContext.accessibleName" type="java.lang.String" value="For instructions on obtaining the module refer to:"/>
+          </AccessibilityProperties>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+        <Component class="javax.swing.JLabel" name="instructionsLinkLabel">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+              <Connection code="getHtmlLink(CT_IMPORTER_DOC_LINK)" type="code"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="instructionsLinkLabelMouseClicked"/>
+          </Events>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+      </SubComponents>
+    </Container>
+    <Container class="javax.swing.JPanel" name="repoPanel">
+      <Constraints>
+        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+          <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
+        </Constraint>
+      </Constraints>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JLabel" name="fileRepoPathLabel">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties" key="CTIncidentImportOptionsPanel.fileRepoPathLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+          <AuxValues>
+            <AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
+            <AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
+          </AuxValues>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="0" gridY="0" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="5" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+        <Component class="javax.swing.JTextField" name="fileRepoPathField">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties" key="CTIncidentImportOptionsPanel.fileRepoPathField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+          <AccessibilityProperties>
+            <Property name="AccessibleContext.accessibleName" type="java.lang.String" value=""/>
+          </AccessibilityProperties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="fileRepoPathFieldActionPerformed"/>
+          </Events>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="1.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+        <Component class="javax.swing.JButton" name="fileRepoBrowseButton">
+          <Properties>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties" key="CTIncidentImportOptionsPanel.fileRepoBrowseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="fileRepoBrowseButtonActionPerformed"/>
+          </Events>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="18" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+        <Component class="javax.swing.JLabel" name="caseOpenWarningLabel">
+          <Properties>
+            <Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
+              <Image iconType="3" name="/org/sleuthkit/autopsy/modules/hashdatabase/warning16.png"/>
+            </Property>
+            <Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
+              <ResourceString bundle="com/basistech/df/cybertriage/autopsy/incidentoptions/Bundle.properties" key="CTIncidentImportOptionsPanel.caseOpenWarningLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
+            </Property>
+          </Properties>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="0" gridY="2" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="5" insetsBottom="5" insetsRight="5" anchor="17" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTIncidentImportOptionsPanel.java b/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTIncidentImportOptionsPanel.java
new file mode 100644
index 0000000000..8a399c0f0f
--- /dev/null
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTIncidentImportOptionsPanel.java
@@ -0,0 +1,287 @@
+/** *************************************************************************
+ ** This data and information is proprietary to, and a valuable trade secret
+ ** of, Sleuth Kit Labs. It is given in confidence by Sleuth Kit Labs
+ ** and may only be used as permitted under the license agreement under which
+ ** it has been distributed, and in no other way.
+ **
+ ** Copyright (c) 2023 Sleuth Kit Labs, LLC. All rights reserved
+ **
+ ** The technical data and information provided herein are provided with
+ ** `limited rights', and the computer software provided herein is provided
+ ** with `restricted rights' as those terms are defined in DAR and ASPR
+ ** 7-104.9(a).
+ ************************************************************************** */
+package com.basistech.df.cybertriage.autopsy.incidentoptions;
+
+import com.basistech.df.cybertriage.autopsy.ctoptions.subpanel.CTOptionsSubPanel;
+import java.awt.Desktop;
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.JFileChooser;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import org.apache.commons.lang3.StringUtils;
+import org.netbeans.spi.options.OptionsPanelController;
+import org.openide.util.lookup.ServiceProvider;
+import org.sleuthkit.autopsy.casemodule.Case;
+import org.sleuthkit.autopsy.guiutils.JFileChooserFactory;
+
+/**
+ * Options panel for CyberTriage options for importing a CyberTriage incident
+ */
+@ServiceProvider(service = CTOptionsSubPanel.class)
+public class CTIncidentImportOptionsPanel extends CTOptionsSubPanel {
+
+    private static final Logger logger = Logger.getLogger(CTIncidentImportOptionsPanel.class.getName());
+
+    private static final String CT_IMPORTER_DOC_LINK = "https://docs.cybertriage.com/en/latest/chapters/integrations/autopsy.html";
+    
+    private final JFileChooserFactory fileRepoChooserFactory = new JFileChooserFactory();
+    private final CTSettingsPersistence ctPersistence = CTSettingsPersistence.getInstance();
+
+    private static String getHtmlLink(String url) {
+        return "<html><span style=\"color: blue; text-decoration: underline\">" + url + "</span></html>";
+    }
+
+    /**
+     * Creates new form CTIncidentImportOptionsPanel
+     */
+    public CTIncidentImportOptionsPanel() {
+        initComponents();
+        this.fileRepoPathField.getDocument().addDocumentListener(new DocumentListener() {
+            @Override
+            public void changedUpdate(DocumentEvent e) {
+                fireSettingsChanged();
+            }
+
+            @Override
+            public void insertUpdate(DocumentEvent e) {
+                fireSettingsChanged();
+            }
+
+            @Override
+            public void removeUpdate(DocumentEvent e) {
+                fireSettingsChanged();
+            }
+        });
+
+        Case.addEventTypeSubscriber(Collections.singleton(Case.Events.CURRENT_CASE), (evt) -> {
+            CTIncidentImportOptionsPanel.this.setEnabledItems(evt.getNewValue() != null);
+        });
+    }
+
+    private void setCTSettingsDisplay(CTSettings ctSettings) {
+        this.fileRepoPathField.setText(ctSettings.getFileRepoPath());
+    }
+
+    @Override
+    public synchronized void saveSettings() {
+        ctPersistence.saveCTSettings(getSettings());
+    }
+
+    @Override
+    public synchronized void loadSettings() {
+        CTSettings ctSettings = ctPersistence.loadCTSettings();
+        setCTSettingsDisplay(ctSettings);
+        setEnabledItems(Case.isCaseOpen());
+    }
+
+    private void setEnabledItems(boolean caseOpen) {
+        this.caseOpenWarningLabel.setVisible(caseOpen);
+        this.fileRepoBrowseButton.setEnabled(!caseOpen);
+        this.fileRepoPathField.setEnabled(!caseOpen);
+    }
+
+    private void fireSettingsChanged() {
+        this.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
+    }
+
+    private CTSettings getSettings() {
+        return new CTSettings().setFileRepoPath(this.fileRepoPathField.getText());
+    }
+
+    @Override
+    public boolean valid() {
+        return new File(this.fileRepoPathField.getText()).isDirectory();
+    }
+
+    /**
+     * This method is called from within the constructor to initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is always
+     * regenerated by the Form Editor.
+     */
+    @SuppressWarnings("unchecked")
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+        java.awt.GridBagConstraints gridBagConstraints;
+
+        incidentTextPanel = new javax.swing.JPanel();
+        incidentTextLabel = new javax.swing.JLabel();
+        instructionsPanel = new javax.swing.JPanel();
+        instructionsTextLabel = new javax.swing.JLabel();
+        instructionsLinkLabel = new javax.swing.JLabel();
+        repoPanel = new javax.swing.JPanel();
+        javax.swing.JLabel fileRepoPathLabel = new javax.swing.JLabel();
+        fileRepoPathField = new javax.swing.JTextField();
+        fileRepoBrowseButton = new javax.swing.JButton();
+        caseOpenWarningLabel = new javax.swing.JLabel();
+
+        setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(CTIncidentImportOptionsPanel.class, "CTIncidentImportOptionsPanel.border.title_1"))); // NOI18N
+        setLayout(new java.awt.GridBagLayout());
+
+        incidentTextPanel.setLayout(new java.awt.GridBagLayout());
+
+        org.openide.awt.Mnemonics.setLocalizedText(incidentTextLabel, org.openide.util.NbBundle.getMessage(CTIncidentImportOptionsPanel.class, "CTIncidentImportOptionsPanel.incidentTextLabel.text")); // NOI18N
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
+        incidentTextPanel.add(incidentTextLabel, gridBagConstraints);
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        add(incidentTextPanel, gridBagConstraints);
+
+        instructionsPanel.setLayout(new java.awt.GridBagLayout());
+
+        org.openide.awt.Mnemonics.setLocalizedText(instructionsTextLabel, org.openide.util.NbBundle.getMessage(CTIncidentImportOptionsPanel.class, "CTIncidentImportOptionsPanel.instructionsTextLabel.text")); // NOI18N
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.insets = new java.awt.Insets(0, 5, 0, 0);
+        instructionsPanel.add(instructionsTextLabel, gridBagConstraints);
+        instructionsTextLabel.getAccessibleContext().setAccessibleName("For instructions on obtaining the module refer to:");
+
+        org.openide.awt.Mnemonics.setLocalizedText(instructionsLinkLabel, getHtmlLink(CT_IMPORTER_DOC_LINK));
+        instructionsLinkLabel.addMouseListener(new java.awt.event.MouseAdapter() {
+            public void mouseClicked(java.awt.event.MouseEvent evt) {
+                instructionsLinkLabelMouseClicked(evt);
+            }
+        });
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
+        instructionsPanel.add(instructionsLinkLabel, gridBagConstraints);
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        add(instructionsPanel, gridBagConstraints);
+
+        repoPanel.setLayout(new java.awt.GridBagLayout());
+
+        org.openide.awt.Mnemonics.setLocalizedText(fileRepoPathLabel, org.openide.util.NbBundle.getMessage(CTIncidentImportOptionsPanel.class, "CTIncidentImportOptionsPanel.fileRepoPathLabel.text")); // NOI18N
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.gridwidth = 2;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5);
+        repoPanel.add(fileRepoPathLabel, gridBagConstraints);
+
+        fileRepoPathField.setText(org.openide.util.NbBundle.getMessage(CTIncidentImportOptionsPanel.class, "CTIncidentImportOptionsPanel.fileRepoPathField.text")); // NOI18N
+        fileRepoPathField.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                fileRepoPathFieldActionPerformed(evt);
+            }
+        });
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.weightx = 1.0;
+        gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
+        repoPanel.add(fileRepoPathField, gridBagConstraints);
+        fileRepoPathField.getAccessibleContext().setAccessibleName("");
+
+        org.openide.awt.Mnemonics.setLocalizedText(fileRepoBrowseButton, org.openide.util.NbBundle.getMessage(CTIncidentImportOptionsPanel.class, "CTIncidentImportOptionsPanel.fileRepoBrowseButton.text")); // NOI18N
+        fileRepoBrowseButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                fileRepoBrowseButtonActionPerformed(evt);
+            }
+        });
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 1;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
+        repoPanel.add(fileRepoBrowseButton, gridBagConstraints);
+
+        caseOpenWarningLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/modules/hashdatabase/warning16.png"))); // NOI18N
+        org.openide.awt.Mnemonics.setLocalizedText(caseOpenWarningLabel, org.openide.util.NbBundle.getMessage(CTIncidentImportOptionsPanel.class, "CTIncidentImportOptionsPanel.caseOpenWarningLabel.text")); // NOI18N
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 2;
+        gridBagConstraints.gridwidth = 2;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
+        gridBagConstraints.insets = new java.awt.Insets(0, 5, 5, 5);
+        repoPanel.add(caseOpenWarningLabel, gridBagConstraints);
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 2;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        add(repoPanel, gridBagConstraints);
+    }// </editor-fold>//GEN-END:initComponents
+    private void fileRepoBrowseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileRepoBrowseButtonActionPerformed
+        JFileChooser fileChooser = fileRepoChooserFactory.getChooser();
+        fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+        fileChooser.setMultiSelectionEnabled(false);
+
+        File curSelectedDir = StringUtils.isBlank(this.fileRepoPathField.getText()) ? null : new File(this.fileRepoPathField.getText());
+        if (curSelectedDir == null || !curSelectedDir.isDirectory()) {
+            curSelectedDir = new File(CTSettings.getDefaultFileRepoPath());
+        }
+
+        fileChooser.setCurrentDirectory(curSelectedDir);
+        fileChooser.setDialogTitle(org.openide.util.NbBundle.getMessage(CTIncidentImportOptionsPanel.class, "CTIncidentImportOptionsPanel.fileRepoFileChooser.title"));
+        int retVal = fileChooser.showOpenDialog(this);
+        if (retVal == JFileChooser.APPROVE_OPTION) {
+            this.fileRepoPathField.setText(fileChooser.getSelectedFile().getAbsolutePath());
+        }
+    }//GEN-LAST:event_fileRepoBrowseButtonActionPerformed
+
+    private void fileRepoPathFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileRepoPathFieldActionPerformed
+        // TODO add your handling code here:
+    }//GEN-LAST:event_fileRepoPathFieldActionPerformed
+
+    private void instructionsLinkLabelMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_instructionsLinkLabelMouseClicked
+        gotoLink(CT_IMPORTER_DOC_LINK);
+    }//GEN-LAST:event_instructionsLinkLabelMouseClicked
+
+    private void gotoLink(String url) {
+        if (Desktop.isDesktopSupported()) {
+            try {
+                Desktop.getDesktop().browse(new URI(url));
+            } catch (IOException | URISyntaxException e) {
+                logger.log(Level.SEVERE, "Error opening link to: " + url, e);
+            }
+        } else {
+            logger.log(Level.WARNING, "Desktop API is not supported.  Link cannot be opened.");
+        }
+    }
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JLabel caseOpenWarningLabel;
+    private javax.swing.JButton fileRepoBrowseButton;
+    private javax.swing.JTextField fileRepoPathField;
+    private javax.swing.JLabel incidentTextLabel;
+    private javax.swing.JPanel incidentTextPanel;
+    private javax.swing.JLabel instructionsLinkLabel;
+    private javax.swing.JPanel instructionsPanel;
+    private javax.swing.JLabel instructionsTextLabel;
+    private javax.swing.JPanel repoPanel;
+    // End of variables declaration//GEN-END:variables
+}
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTSettings.java b/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTSettings.java
new file mode 100644
index 0000000000..4e9d4b4697
--- /dev/null
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTSettings.java
@@ -0,0 +1,71 @@
+/** *************************************************************************
+ ** This data and information is proprietary to, and a valuable trade secret
+ ** of, Sleuth Kit Labs. It is given in confidence by Sleuth Kit Labs
+ ** and may only be used as permitted under the license agreement under which
+ ** it has been distributed, and in no other way.
+ **
+ ** Copyright (c) 2023 Sleuth Kit Labs, LLC. All rights reserved
+ **
+ ** The technical data and information provided herein are provided with
+ ** `limited rights', and the computer software provided herein is provided
+ ** with `restricted rights' as those terms are defined in DAR and ASPR
+ ** 7-104.9(a).
+ ************************************************************************** */
+package com.basistech.df.cybertriage.autopsy.incidentoptions;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Objects;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.openide.modules.Places;
+
+/**
+ * CT settings that don't include license information.
+ */
+public class CTSettings {
+
+    private static final String DEFAULT_FILE_REPO_PATH = getAppDataLocalDirectory();
+
+    // taken from com.basistech.df.cybertriage.utils.SystemProperties
+    private static String getAppDataLocalDirectory() {
+
+        Logger LOGGER = java.util.logging.Logger.getLogger(CTSettings.class.getCanonicalName());
+        if (Objects.nonNull(Places.getUserDirectory()) && Places.getUserDirectory().getAbsolutePath().endsWith("testuserdir")) { // APP is in testing .. this should return the test path
+            LOGGER.log(Level.INFO, "Application Data (test mode) Path: " + Places.getUserDirectory().getAbsolutePath());
+            return Places.getUserDirectory().getAbsolutePath();
+        } else {
+            Path localAppPath = Paths.get(System.getenv("LOCALAPPDATA"), "cybertriage");
+            try {
+                Files.createDirectories(localAppPath);
+                LOGGER.log(Level.INFO, "Application Data Path: " + localAppPath.toString());
+                return localAppPath.toString();
+            } catch (IOException ex) {
+                LOGGER.log(Level.SEVERE, "IO Error, defaulting to user dir", ex);
+                return Places.getUserDirectory().getAbsolutePath(); // In case of an IO Error
+            }
+        }
+    }
+
+    public static String getDefaultFileRepoPath() {
+        return DEFAULT_FILE_REPO_PATH;
+    }
+
+    static CTSettings getDefaultSettings() {
+        return new CTSettings()
+                .setFileRepoPath(DEFAULT_FILE_REPO_PATH);
+    }
+
+    private String fileRepoPath = DEFAULT_FILE_REPO_PATH;
+
+    public String getFileRepoPath() {
+        return fileRepoPath;
+    }
+
+    public CTSettings setFileRepoPath(String fileRepoPath) {
+        this.fileRepoPath = fileRepoPath;
+        return this;
+    }
+}
diff --git a/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTSettingsPersistence.java b/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTSettingsPersistence.java
new file mode 100644
index 0000000000..d9addd1960
--- /dev/null
+++ b/Core/src/com/basistech/df/cybertriage/autopsy/incidentoptions/CTSettingsPersistence.java
@@ -0,0 +1,79 @@
+/** *************************************************************************
+ ** This data and information is proprietary to, and a valuable trade secret
+ ** of, Sleuth Kit Labs. It is given in confidence by Sleuth Kit Labs
+ ** and may only be used as permitted under the license agreement under which
+ ** it has been distributed, and in no other way.
+ **
+ ** Copyright (c) 2023 Sleuth Kit Labs, LLC. All rights reserved
+ **
+ ** The technical data and information provided herein are provided with
+ ** `limited rights', and the computer software provided herein is provided
+ ** with `restricted rights' as those terms are defined in DAR and ASPR
+ ** 7-104.9(a).
+ ************************************************************************** */
+package com.basistech.df.cybertriage.autopsy.incidentoptions;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.logging.Level;
+import org.sleuthkit.autopsy.coreutils.Logger;
+import org.sleuthkit.autopsy.coreutils.PlatformUtil;
+
+/**
+ * Handles persisting CT Settings.
+ */
+public class CTSettingsPersistence {
+
+    private static final String CT_SETTINGS_DIR = "CyberTriage";
+    private static final String CT_SETTINGS_FILENAME = "CyberTriageSettings.json";
+
+    private static final Logger logger = Logger.getLogger(CTSettingsPersistence.class.getName());
+
+    private static final CTSettingsPersistence instance = new CTSettingsPersistence();
+
+    private final ObjectMapper objectMapper = new ObjectMapper();
+
+    public static CTSettingsPersistence getInstance() {
+        return instance;
+    }
+
+    public synchronized boolean saveCTSettings(CTSettings ctSettings) {
+        if (ctSettings != null) {
+
+            File settingsFile = getCTSettingsFile();
+            settingsFile.getParentFile().mkdirs();
+            try {
+                objectMapper.writeValue(settingsFile, ctSettings);
+                return true;
+            } catch (IOException ex) {
+                logger.log(Level.WARNING, "There was an error writing CyberTriage settings to file: " + settingsFile.getAbsolutePath(), ex);
+            }
+        }
+
+        return false;
+    }
+
+    public synchronized CTSettings loadCTSettings() {
+
+        CTSettings settings = null;
+        File settingsFile = getCTSettingsFile();
+        if (settingsFile.isFile()) {
+            try {
+                settings = objectMapper.readValue(settingsFile, CTSettings.class);
+            } catch (IOException ex) {
+                logger.log(Level.WARNING, "There was an error reading CyberTriage settings to file: " + settingsFile.getAbsolutePath(), ex);
+            }
+        }
+
+        return settings == null
+                ? CTSettings.getDefaultSettings()
+                : settings;
+
+    }
+
+    private File getCTSettingsFile() {
+        return Paths.get(PlatformUtil.getModuleConfigDirectory(), CT_SETTINGS_DIR, CT_SETTINGS_FILENAME).toFile();
+    }
+}
-- 
GitLab