Autopsy 4.22.1
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 */
19package org.sleuthkit.autopsy.commandlineingest;
20
21import java.beans.PropertyChangeListener;
22import java.beans.PropertyChangeSupport;
23import java.io.File;
24import java.util.ArrayList;
25import java.util.Collections;
26import java.util.HashSet;
27import java.util.List;
28import java.util.Map;
29import java.util.Set;
30import java.util.logging.Level;
31import java.util.stream.Collectors;
32import java.util.stream.Stream;
33import org.apache.commons.lang3.ArrayUtils;
34import org.sleuthkit.autopsy.coreutils.Logger;
35import org.netbeans.api.sendopts.CommandException;
36import org.netbeans.spi.sendopts.Env;
37import org.netbeans.spi.sendopts.Option;
38import org.netbeans.spi.sendopts.OptionProcessor;
39import org.openide.util.lookup.ServiceProvider;
40import org.sleuthkit.autopsy.featureaccess.FeatureAccessUtils;
41
45@ServiceProvider(service = OptionProcessor.class)
46public 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
81
83
84 @Override
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");
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
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() {
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:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.