Autopsy  4.21.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
CommandLineOptionProcessor.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019-2022 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.commandlineingest;
20 
21 import java.beans.PropertyChangeListener;
22 import java.beans.PropertyChangeSupport;
23 import java.io.File;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.logging.Level;
31 import java.util.stream.Collectors;
32 import java.util.stream.Stream;
33 import org.apache.commons.lang3.ArrayUtils;
35 import org.netbeans.api.sendopts.CommandException;
36 import org.netbeans.spi.sendopts.Env;
37 import org.netbeans.spi.sendopts.Option;
38 import org.netbeans.spi.sendopts.OptionProcessor;
39 import org.openide.util.lookup.ServiceProvider;
41 
45 @ServiceProvider(service = OptionProcessor.class)
46 public class CommandLineOptionProcessor extends OptionProcessor {
47 
48  private static final Logger logger = Logger.getLogger(CommandLineOptionProcessor.class.getName());
49  private final Option caseNameOption = Option.requiredArgument('n', "caseName");
50  private final Option caseTypeOption = Option.requiredArgument('t', "caseType");
51  private final Option caseBaseDirOption = Option.requiredArgument('o', "caseBaseDir");
52  private final Option createCaseCommandOption = Option.withoutArgument('c', "createCase");
53  private final Option dataSourcePathOption = Option.requiredArgument('s', "dataSourcePath");
54  private final Option dataSourceObjectIdOption = Option.requiredArgument('i', "dataSourceObjectId");
55  private final Option addDataSourceCommandOption = Option.withoutArgument('a', "addDataSource");
56  private final Option bitlockerKeyCommandOption = Option.requiredArgument('k', "key");
57  private final Option runIngestCommandOption = Option.optionalArgument('r', "runIngest");
58  private final Option listAllDataSourcesCommandOption = Option.withoutArgument('l', "listAllDataSources");
59  private final Option generateReportsOption = Option.optionalArgument('g', "generateReports");
60  private final Option listAllIngestProfileOption = Option.withoutArgument('p', "listAllIngestProfiles");
61  private final Option defaultArgument = Option.defaultArguments();
62 
63  private boolean runFromCommandLine = false;
64 
65  private final List<CommandLineCommand> commands = new ArrayList<>();
66 
67  final static String CASETYPE_MULTI = "multi";
68  final static String CASETYPE_SINGLE = "single";
69 
70  private String defaultArgumentValue = null;
71 
72  private PropertyChangeSupport changes = new PropertyChangeSupport(this);
73  public static String PROCESSING_STARTED = "command line process started";
74  public static String PROCESSING_COMPLETED = "command line process completed";
75 
76  public enum ProcessState {
79  COMPLETED
80  }
81 
83 
84  @Override
85  protected Set<Option> getOptions() {
86  return Set.of(
87  createCaseCommandOption,
88  caseNameOption,
89  caseTypeOption,
90  caseBaseDirOption,
91  dataSourcePathOption,
92  addDataSourceCommandOption,
93  bitlockerKeyCommandOption,
94  dataSourceObjectIdOption,
95  runIngestCommandOption,
96  listAllDataSourcesCommandOption,
97  generateReportsOption,
98  listAllIngestProfileOption,
99  defaultArgument
100  );
101  }
102 
103  @Override
104  protected void process(Env env, Map<Option, String[]> values) throws CommandException {
105  logger.log(Level.INFO, "Processing Autopsy command line options"); //NON-NLS
106  System.out.println("Processing Autopsy command line options");
107  setState(ProcessState.RUNNING);
108  changes.firePropertyChange(PROCESSING_STARTED, false, true);
109 
110  if (values.containsKey(defaultArgument)) {
111  defaultArgumentValue = values.get(defaultArgument)[0];
112  runFromCommandLine(true);
113  return;
114  }
115 
116  // input arguments must contain at least one command
117  if (!(values.containsKey(createCaseCommandOption) || values.containsKey(addDataSourceCommandOption)
118  || values.containsKey(runIngestCommandOption) || values.containsKey(listAllDataSourcesCommandOption)
119  || values.containsKey(generateReportsOption) || values.containsKey(listAllIngestProfileOption))) {
120  // not running from command line
121  handleError("Invalid command line, an input option must be supplied.");
122  }
123 
124  // parse input parameters
125  String[] argDirs;
126  String inputCaseName = "";
127 
128  if(values.containsKey(listAllIngestProfileOption)) {
129  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.LIST_ALL_INGEST_PROFILES);
130  commands.add(newCommand);
131  runFromCommandLine(true);
132  } else {
133  if (values.containsKey(caseNameOption)) {
134  argDirs = values.get(caseNameOption);
135  if (argDirs.length < 1) {
136  handleError("Missing argument 'caseName'");
137  }
138  inputCaseName = argDirs[0];
139 
140  if (inputCaseName == null || inputCaseName.isEmpty()) {
141  handleError("'caseName' argument is empty");
142  }
143  }
144 
145  // 'caseName' must always be specified
146  if (inputCaseName == null || inputCaseName.isEmpty()) {
147  handleError("'caseName' argument is empty");
148  }
149 
150  String caseType = "";
151  if (values.containsKey(caseTypeOption)) {
152  argDirs = values.get(caseTypeOption);
153 
154  if (argDirs.length < 1) {
155  handleError("Missing argument 'caseType'");
156  }
157  caseType = argDirs[0];
158 
159  if (caseType == null || caseType.isEmpty()) {
160  handleError("'caseType' argument is empty");
161  } else if (!caseType.equalsIgnoreCase(CASETYPE_MULTI) && !caseType.equalsIgnoreCase(CASETYPE_SINGLE)) {
162  handleError("'caseType' argument is invalid");
163  } else if (caseType.equalsIgnoreCase(CASETYPE_MULTI) && !FeatureAccessUtils.canCreateMultiUserCases()) {
164  handleError("Unable to create multi user case. Confirm that multi user settings are configured correctly.");
165  }
166  }
167 
168  String caseBaseDir = "";
169  if (values.containsKey(caseBaseDirOption)) {
170  argDirs = values.get(caseBaseDirOption);
171  if (argDirs.length < 1) {
172  handleError("Missing argument 'caseBaseDir'");
173  }
174  caseBaseDir = argDirs[0];
175 
176  if (caseBaseDir == null || caseBaseDir.isEmpty()) {
177  handleError("Missing argument 'caseBaseDir' option");
178  }
179 
180  if (!(new File(caseBaseDir).exists()) || !(new File(caseBaseDir).isDirectory())) {
181  handleError("'caseBaseDir' directory doesn't exist or is not a directory: " + caseBaseDir);
182  }
183  }
184 
185  // 'caseBaseDir' must always be specified
186  if (caseBaseDir == null || caseBaseDir.isEmpty()) {
187  handleError("Missing argument 'caseBaseDir' option");
188  }
189 
190  String dataSourcePath = "";
191  if (values.containsKey(dataSourcePathOption)) {
192 
193  argDirs = values.get(dataSourcePathOption);
194  if (argDirs.length < 1) {
195  handleError("Missing argument 'dataSourcePath'");
196  }
197  dataSourcePath = argDirs[0];
198 
199  // verify inputs
200  if (dataSourcePath == null || dataSourcePath.isEmpty()) {
201  handleError("Missing argument 'dataSourcePath'");
202  }
203 
204  if (!(new File(dataSourcePath).exists())) {
205  handleError("Input data source file " + dataSourcePath + " doesn't exist");
206  }
207  }
208 
209  String[] keysArgs = values.get(bitlockerKeyCommandOption);
210  String bitlockerKey = ArrayUtils.isNotEmpty(keysArgs) ? keysArgs[0] : null;
211 
212  String dataSourceId = "";
213  if (values.containsKey(dataSourceObjectIdOption)) {
214 
215  argDirs = values.get(dataSourceObjectIdOption);
216  if (argDirs.length < 1) {
217  handleError("Missing argument 'dataSourceObjectIdOption'");
218  }
219  dataSourceId = argDirs[0];
220 
221  // verify inputs
222  if (dataSourceId == null || dataSourceId.isEmpty()) {
223  handleError("Input data source id is empty");
224  }
225  }
226 
227  // Create commands in order in which they should be executed:
228  // First create the "CREATE_CASE" command, if present
229  if (values.containsKey(createCaseCommandOption)) {
230 
231  // 'caseName' must always be specified for "CREATE_CASE" command
232  if (inputCaseName == null || inputCaseName.isEmpty()) {
233  handleError("'caseName' argument is empty");
234  }
235 
236  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.CREATE_CASE);
237  newCommand.addInputValue(CommandLineCommand.InputType.CASE_NAME.name(), inputCaseName);
238  newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir);
239  newCommand.addInputValue(CommandLineCommand.InputType.CASE_TYPE.name(), caseType);
240  commands.add(newCommand);
241  runFromCommandLine(true);
242  }
243 
244  // Add ADD_DATA_SOURCE command, if present
245  if (values.containsKey(addDataSourceCommandOption)) {
246 
247  // 'dataSourcePath' must always be specified for "ADD_DATA_SOURCE" command
248  if (dataSourcePath == null || dataSourcePath.isEmpty()) {
249  handleError("'dataSourcePath' argument is empty");
250  }
251 
252  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.ADD_DATA_SOURCE);
253  newCommand.addInputValue(CommandLineCommand.InputType.CASE_NAME.name(), inputCaseName);
254  newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir);
255  newCommand.addInputValue(CommandLineCommand.InputType.DATA_SOURCE_PATH.name(), dataSourcePath);
256 
257  if (bitlockerKey != null) {
258  newCommand.addInputValue(CommandLineCommand.InputType.BITLOCKER_KEY.name(), bitlockerKey);
259  }
260 
261  commands.add(newCommand);
262  runFromCommandLine(true);
263  }
264 
265  String ingestProfile = "";
266  // Add RUN_INGEST command, if present
267  if (values.containsKey(runIngestCommandOption)) {
268 
269  argDirs = values.get(runIngestCommandOption);
270  if(argDirs != null && argDirs.length > 0) {
271  ingestProfile = argDirs[0];
272  }
273 
274  // if new data source is being added during this run, then 'dataSourceId' is not specified
275  if (!values.containsKey(addDataSourceCommandOption) && dataSourceId.isEmpty()) {
276  // data source is not being added during this run, so 'dataSourceId' should have been specified
277  handleError("'dataSourceId' argument is empty");
278  }
279 
280  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.RUN_INGEST);
281  newCommand.addInputValue(CommandLineCommand.InputType.CASE_NAME.name(), inputCaseName);
282  newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir);
283  newCommand.addInputValue(CommandLineCommand.InputType.DATA_SOURCE_ID.name(), dataSourceId);
284  newCommand.addInputValue(CommandLineCommand.InputType.INGEST_PROFILE_NAME.name(), ingestProfile);
285  newCommand.addInputValue(CommandLineCommand.InputType.DATA_SOURCE_PATH.name(), dataSourcePath);
286  commands.add(newCommand);
287  runFromCommandLine(true);
288  }
289 
290  // Add "LIST_ALL_DATA_SOURCES" command, if present
291  if (values.containsKey(listAllDataSourcesCommandOption)) {
292 
293  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.LIST_ALL_DATA_SOURCES);
294  newCommand.addInputValue(CommandLineCommand.InputType.CASE_NAME.name(), inputCaseName);
295  newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir);
296  commands.add(newCommand);
297  runFromCommandLine(true);
298  }
299 
300  // Add "GENERATE_REPORTS" command, if present
301  if (values.containsKey(generateReportsOption)) {
302  List<String> reportProfiles;
303  argDirs = values.get(generateReportsOption);
304  if (argDirs.length > 0) {
305  // use custom report configuration(s)
306  reportProfiles = Stream.of(argDirs[0].split(","))
307  .map(String::trim)
308  .collect(Collectors.toList());
309 
310  if (reportProfiles == null || reportProfiles.isEmpty()) {
311  handleError("'generateReports' argument is empty");
312  }
313 
314  for (String reportProfile : reportProfiles) {
315  if (reportProfile.isEmpty()) {
316  handleError("Empty report profile name");
317  }
318  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.GENERATE_REPORTS);
319  newCommand.addInputValue(CommandLineCommand.InputType.CASE_NAME.name(), inputCaseName);
320  newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir);
321  newCommand.addInputValue(CommandLineCommand.InputType.REPORT_PROFILE_NAME.name(), reportProfile);
322  commands.add(newCommand);
323  }
324  } else {
325  // use default report configuration
326  CommandLineCommand newCommand = new CommandLineCommand(CommandLineCommand.CommandType.GENERATE_REPORTS);
327  newCommand.addInputValue(CommandLineCommand.InputType.CASE_NAME.name(), inputCaseName);
328  newCommand.addInputValue(CommandLineCommand.InputType.CASES_BASE_DIR_PATH.name(), caseBaseDir);
329  commands.add(newCommand);
330  }
331 
332  runFromCommandLine(true);
333  }
334  }
335 
336  setState(ProcessState.COMPLETED);
337  System.out.println("Completed processing Autopsy command line options");
338  changes.firePropertyChange(PROCESSING_COMPLETED, false, true);
339  }
340 
346  public synchronized boolean isRunFromCommandLine() {
347  return runFromCommandLine;
348  }
349 
350  public synchronized void runFromCommandLine(boolean runFromCommandLine) {
351  this.runFromCommandLine = runFromCommandLine;
352  }
353 
359  public String getDefaultArgument() {
360  return defaultArgumentValue;
361  }
362 
368  List<CommandLineCommand> getCommands() {
369  return Collections.unmodifiableList(commands);
370  }
371 
379  private void handleError(String errorMessage) throws CommandException {
380  logger.log(Level.SEVERE, errorMessage);
381  throw new CommandException(CommandLineIngestManager.CL_PROCESS_FAILURE, errorMessage);
382  }
383 
385  PropertyChangeListener l) {
386  changes.addPropertyChangeListener(l);
387  }
389  PropertyChangeListener l) {
390  changes.removePropertyChangeListener(l);
391  }
392 
393  private synchronized void setState(ProcessState state) {
394  this.state = state;
395  }
396 
397  public synchronized ProcessState getState() {
398  return state;
399  }
400 }
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2024 Sleuth Kit Labs. Generated on: Mon Feb 17 2025
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.