23 package org.sleuthkit.autopsy.recentactivity;
25 import com.google.gson.JsonArray;
26 import com.google.gson.JsonElement;
27 import com.google.gson.JsonIOException;
28 import com.google.gson.JsonObject;
29 import com.google.gson.JsonParser;
30 import com.google.gson.JsonSyntaxException;
32 import java.io.FileNotFoundException;
33 import java.io.FileReader;
34 import java.io.IOException;
35 import java.io.UnsupportedEncodingException;
36 import java.net.URLDecoder;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.List;
44 import java.util.logging.Level;
45 import org.apache.commons.io.FilenameUtils;
46 import org.openide.util.NbBundle;
47 import org.openide.util.NbBundle.Messages;
59 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
61 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
63 import org.
sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
65 import org.
sleuthkit.datamodel.blackboardutils.WebBrowserArtifactsHelper;
68 "Progress_Message_Firefox_History=Firefox History",
69 "Progress_Message_Firefox_Bookmarks=Firefox Bookmarks",
70 "Progress_Message_Firefox_Cookies=Firefox Cookies",
71 "Progress_Message_Firefox_Downloads=Firefox Downloads",
72 "Progress_Message_Firefox_FormHistory=Firefox Form History",
73 "Progress_Message_Firefox_AutoFill=Firefox Auto Fill"
79 class Firefox extends Extract {
81 private static final Logger logger = Logger.getLogger(Firefox.class.getName());
82 private static final String PLACE_URL_PREFIX =
"place:";
83 private static final String HISTORY_QUERY =
"SELECT moz_historyvisits.id, url, title, visit_count,(visit_date/1000000) AS visit_date,from_visit,"
84 +
"(SELECT url FROM moz_historyvisits history, moz_places places where history.id = moz_historyvisits.from_visit and history.place_id = places.id ) as ref "
85 +
"FROM moz_places, moz_historyvisits "
86 +
"WHERE moz_places.id = moz_historyvisits.place_id "
88 private static final String COOKIE_QUERY =
"SELECT name,value,host,expiry,(lastAccessed/1000000) AS lastAccessed,(creationTime/1000000) AS creationTime FROM moz_cookies";
89 private static final String COOKIE_QUERY_V3 =
"SELECT name,value,host,expiry,(lastAccessed/1000000) AS lastAccessed FROM moz_cookies";
90 private static final String BOOKMARK_QUERY =
"SELECT fk, moz_bookmarks.title, url, (moz_bookmarks.dateAdded/1000000) AS dateAdded FROM moz_bookmarks INNER JOIN moz_places ON moz_bookmarks.fk=moz_places.id";
91 private static final String DOWNLOAD_QUERY =
"SELECT target, source,(startTime/1000000) AS startTime, maxBytes FROM moz_downloads";
92 private static final String DOWNLOAD_QUERY_V24 =
"SELECT url, content AS target, (lastModified/1000000) AS lastModified "
93 +
" FROM moz_places, moz_annos, moz_anno_attributes "
94 +
" WHERE moz_places.id = moz_annos.place_id"
95 +
" AND moz_annos.anno_attribute_id = moz_anno_attributes.id"
96 +
" AND moz_anno_attributes.name='downloads/destinationFileURI'";
97 private static final String FORMHISTORY_QUERY =
"SELECT fieldname, value FROM moz_formhistory";
98 private static final String FORMHISTORY_QUERY_V64 =
"SELECT fieldname, value, timesUsed, firstUsed, lastUsed FROM moz_formhistory";
99 private Content dataSource;
100 private IngestJobContext context;
103 moduleName = NbBundle.getMessage(Firefox.class,
"Firefox.moduleName");
107 public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) {
108 this.dataSource = dataSource;
109 this.context = context;
112 progressBar.progress(Bundle.Progress_Message_Firefox_History());
115 progressBar.progress(Bundle.Progress_Message_Firefox_Bookmarks());
118 progressBar.progress(Bundle.Progress_Message_Firefox_Downloads());
121 progressBar.progress(Bundle.Progress_Message_Firefox_Cookies());
124 progressBar.progress(Bundle.Progress_Message_Firefox_FormHistory());
125 this.getFormsHistory();
127 progressBar.progress(Bundle.Progress_Message_Firefox_AutoFill());
128 this.getAutofillProfiles();
131 private void getHistory() {
132 FileManager fileManager = currentCase.getServices().getFileManager();
133 List<AbstractFile> historyFiles;
135 historyFiles = fileManager.findFiles(dataSource,
"places.sqlite",
"Firefox");
136 }
catch (TskCoreException ex) {
137 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getHistory.errMsg.errFetchingFiles");
138 logger.log(Level.WARNING, msg);
139 this.addErrorMessage(this.getName() +
": " + msg);
143 if (historyFiles.isEmpty()) {
144 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getHistory.errMsg.noFilesFound");
145 logger.log(Level.INFO, msg);
150 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
152 for (AbstractFile historyFile : historyFiles) {
154 if (context.dataSourceIngestIsCancelled()) {
158 if (historyFile.getSize() == 0) {
162 String fileName = historyFile.getName();
163 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox") + File.separator + fileName + j +
".db";
165 ContentUtils.writeToFile(historyFile,
new File(temps), context::dataSourceIngestIsCancelled);
166 }
catch (ReadContentInputStreamException ex) {
167 logger.log(Level.WARNING, String.format(
"Error reading Firefox web history artifacts file '%s' (id=%d).",
168 fileName, historyFile.getId()), ex);
169 this.addErrorMessage(
170 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
173 }
catch (IOException ex) {
174 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox web history artifacts file '%s' (id=%d).",
175 temps, fileName, historyFile.getId()), ex);
176 this.addErrorMessage(
177 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
181 File dbFile =
new File(temps);
182 if (context.dataSourceIngestIsCancelled()) {
186 List<HashMap<String, Object>> tempList = this.dbConnect(temps, HISTORY_QUERY);
187 logger.log(Level.INFO,
"{0} - Now getting history from {1} with {2} artifacts identified.",
new Object[]{moduleName, temps, tempList.size()});
188 for (HashMap<String, Object> result : tempList) {
190 if (context.dataSourceIngestIsCancelled()) {
194 String url = result.get(
"url").toString();
196 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
197 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
198 RecentActivityExtracterModuleFactory.getModuleName(),
199 ((url != null) ? url :
"")));
201 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
202 RecentActivityExtracterModuleFactory.getModuleName(),
203 (Long.valueOf(result.get(
"visit_date").toString()))));
204 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REFERRER,
205 RecentActivityExtracterModuleFactory.getModuleName(),
206 ((result.get(
"ref").toString() != null) ? result.get(
"ref").toString() :
"")));
207 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
208 RecentActivityExtracterModuleFactory.getModuleName(),
209 ((result.get(
"title").toString() != null) ? result.get(
"title").toString() :
"")));
210 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
211 RecentActivityExtracterModuleFactory.getModuleName(),
212 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
213 String domain = extractDomain(url);
214 if (domain != null && domain.isEmpty() ==
false) {
215 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
216 RecentActivityExtracterModuleFactory.getModuleName(), domain));
219 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_HISTORY, historyFile, bbattributes);
221 bbartifacts.add(bbart);
228 postArtifacts(bbartifacts);
234 private void getBookmark() {
236 FileManager fileManager = currentCase.getServices().getFileManager();
237 List<AbstractFile> bookmarkFiles;
239 bookmarkFiles = fileManager.findFiles(dataSource,
"places.sqlite",
"Firefox");
240 }
catch (TskCoreException ex) {
241 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getBookmark.errMsg.errFetchFiles");
242 logger.log(Level.WARNING, msg);
243 this.addErrorMessage(this.getName() +
": " + msg);
247 if (bookmarkFiles.isEmpty()) {
248 logger.log(Level.INFO,
"Didn't find any firefox bookmark files.");
253 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
255 for (AbstractFile bookmarkFile : bookmarkFiles) {
256 if (bookmarkFile.getSize() == 0) {
259 String fileName = bookmarkFile.getName();
260 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox") + File.separator + fileName + j +
".db";
262 ContentUtils.writeToFile(bookmarkFile,
new File(temps), context::dataSourceIngestIsCancelled);
263 }
catch (ReadContentInputStreamException ex) {
264 logger.log(Level.WARNING, String.format(
"Error reading Firefox bookmark artifacts file '%s' (id=%d).",
265 fileName, bookmarkFile.getId()), ex);
266 this.addErrorMessage(
267 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
270 }
catch (IOException ex) {
271 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox bookmark artifacts file '%s' (id=%d).",
272 temps, fileName, bookmarkFile.getId()), ex);
273 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getBookmark.errMsg.errAnalyzeFile",
274 this.getName(), fileName));
277 File dbFile =
new File(temps);
278 if (context.dataSourceIngestIsCancelled()) {
282 List<HashMap<String, Object>> tempList = this.dbConnect(temps, BOOKMARK_QUERY);
283 logger.log(Level.INFO,
"{0} - Now getting bookmarks from {1} with {2} artifacts identified.",
new Object[]{moduleName, temps, tempList.size()});
284 for (HashMap<String, Object> result : tempList) {
286 if (context.dataSourceIngestIsCancelled()) {
290 String url = result.get(
"url").toString();
292 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
293 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
294 RecentActivityExtracterModuleFactory.getModuleName(),
295 ((url != null) ? url :
"")));
296 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TITLE,
297 RecentActivityExtracterModuleFactory.getModuleName(),
298 ((result.get(
"title").toString() != null) ? result.get(
"title").toString() :
"")));
299 if (Long.valueOf(result.get(
"dateAdded").toString()) > 0) {
300 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
301 RecentActivityExtracterModuleFactory.getModuleName(),
302 (Long.valueOf(result.get(
"dateAdded").toString()))));
304 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
305 RecentActivityExtracterModuleFactory.getModuleName(),
306 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
307 String domain = extractDomain(url);
308 if (domain != null && domain.isEmpty() ==
false) {
309 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
310 RecentActivityExtracterModuleFactory.getModuleName(), domain));
313 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_BOOKMARK, bookmarkFile, bbattributes);
315 bbartifacts.add(bbart);
322 postArtifacts(bbartifacts);
328 private void getCookie() {
329 FileManager fileManager = currentCase.getServices().getFileManager();
330 List<AbstractFile> cookiesFiles;
332 cookiesFiles = fileManager.findFiles(dataSource,
"cookies.sqlite",
"Firefox");
333 }
catch (TskCoreException ex) {
334 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getCookie.errMsg.errFetchFile");
335 logger.log(Level.WARNING, msg);
336 this.addErrorMessage(this.getName() +
": " + msg);
340 if (cookiesFiles.isEmpty()) {
341 logger.log(Level.INFO,
"Didn't find any Firefox cookie files.");
346 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
348 for (AbstractFile cookiesFile : cookiesFiles) {
349 if (cookiesFile.getSize() == 0) {
352 String fileName = cookiesFile.getName();
353 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox") + File.separator + fileName + j +
".db";
355 ContentUtils.writeToFile(cookiesFile,
new File(temps), context::dataSourceIngestIsCancelled);
356 }
catch (ReadContentInputStreamException ex) {
357 logger.log(Level.WARNING, String.format(
"Error reading Firefox cookie artifacts file '%s' (id=%d).",
358 fileName, cookiesFile.getId()), ex);
359 this.addErrorMessage(
360 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
363 }
catch (IOException ex) {
364 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox cookie artifacts file '%s' (id=%d).",
365 temps, fileName, cookiesFile.getId()), ex);
366 this.addErrorMessage(
367 NbBundle.getMessage(
this.getClass(),
"Firefox.getCookie.errMsg.errAnalyzeFile", this.getName(),
371 File dbFile =
new File(temps);
372 if (context.dataSourceIngestIsCancelled()) {
376 boolean checkColumn = Util.checkColumn(
"creationTime",
"moz_cookies", temps);
379 query = COOKIE_QUERY;
381 query = COOKIE_QUERY_V3;
384 List<HashMap<String, Object>> tempList = this.dbConnect(temps, query);
385 logger.log(Level.INFO,
"{0} - Now getting cookies from {1} with {2} artifacts identified.",
new Object[]{moduleName, temps, tempList.size()});
386 for (HashMap<String, Object> result : tempList) {
388 if (context.dataSourceIngestIsCancelled()) {
392 String host = result.get(
"host").toString();
394 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
395 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
396 RecentActivityExtracterModuleFactory.getModuleName(),
397 ((host != null) ? host :
"")));
398 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME,
399 RecentActivityExtracterModuleFactory.getModuleName(),
400 (Long.valueOf(result.get(
"lastAccessed").toString()))));
401 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
402 RecentActivityExtracterModuleFactory.getModuleName(),
403 ((result.get(
"name").toString() != null) ? result.get(
"name").toString() :
"")));
404 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
405 RecentActivityExtracterModuleFactory.getModuleName(),
406 ((result.get(
"value").toString() != null) ? result.get(
"value").toString() :
"")));
407 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
408 RecentActivityExtracterModuleFactory.getModuleName(),
409 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
411 if (checkColumn ==
true) {
412 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
413 RecentActivityExtracterModuleFactory.getModuleName(),
414 (Long.valueOf(result.get(
"creationTime").toString()))));
416 String domain = extractDomain(host);
417 if (domain != null && domain.isEmpty() ==
false) {
418 domain = domain.replaceFirst(
"^\\.+(?!$)",
"");
419 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
420 RecentActivityExtracterModuleFactory.getModuleName(), domain));
423 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_COOKIE, cookiesFile, bbattributes);
425 bbartifacts.add(bbart);
432 postArtifacts(bbartifacts);
438 private void getDownload() {
439 getDownloadPreVersion24();
440 getDownloadVersion24();
448 private void getDownloadPreVersion24() {
450 FileManager fileManager = currentCase.getServices().getFileManager();
451 List<AbstractFile> downloadsFiles;
453 downloadsFiles = fileManager.findFiles(dataSource,
"downloads.sqlite",
"Firefox");
454 }
catch (TskCoreException ex) {
455 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getDlPre24.errMsg.errFetchFiles");
456 logger.log(Level.WARNING, msg);
457 this.addErrorMessage(this.getName() +
": " + msg);
461 if (downloadsFiles.isEmpty()) {
462 logger.log(Level.INFO,
"Didn't find any pre-version-24.0 Firefox download files.");
467 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
469 for (AbstractFile downloadsFile : downloadsFiles) {
470 if (downloadsFile.getSize() == 0) {
473 String fileName = downloadsFile.getName();
474 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox") + File.separator + fileName + j +
".db";
477 ContentUtils.writeToFile(downloadsFile,
new File(temps), context::dataSourceIngestIsCancelled);
478 }
catch (ReadContentInputStreamException ex) {
479 logger.log(Level.WARNING, String.format(
"Error reading Firefox download artifacts file '%s' (id=%d).",
480 fileName, downloadsFile.getId()), ex);
481 this.addErrorMessage(
482 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
485 }
catch (IOException ex) {
486 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox download artifacts file '%s' (id=%d).",
487 temps, fileName, downloadsFile.getId()), ex);
488 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getDlPre24.errMsg.errAnalyzeFiles",
489 this.getName(), fileName));
492 File dbFile =
new File(temps);
493 if (context.dataSourceIngestIsCancelled()) {
498 List<HashMap<String, Object>> tempList = this.dbConnect(temps, DOWNLOAD_QUERY);
499 logger.log(Level.INFO,
"{0}- Now getting downloads from {1} with {2} artifacts identified.",
new Object[]{moduleName, temps, tempList.size()});
500 for (HashMap<String, Object> result : tempList) {
502 if (context.dataSourceIngestIsCancelled()) {
506 String source = result.get(
"source").toString();
508 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
510 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
511 RecentActivityExtracterModuleFactory.getModuleName(),
514 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
515 RecentActivityExtracterModuleFactory.getModuleName(),
516 (Long.valueOf(result.get(
"startTime").toString()))));
518 String target = result.get(
"target").toString();
519 String downloadedFilePath =
"";
520 if (target != null) {
522 downloadedFilePath = URLDecoder.decode(target.replaceAll(
"file:///",
""),
"UTF-8");
523 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
524 RecentActivityExtracterModuleFactory.getModuleName(),
525 downloadedFilePath));
526 long pathID = Util.findID(dataSource, downloadedFilePath);
528 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
529 RecentActivityExtracterModuleFactory.getModuleName(),
532 }
catch (UnsupportedEncodingException ex) {
533 logger.log(Level.SEVERE,
"Error decoding Firefox download URL in " + temps, ex);
538 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
539 RecentActivityExtracterModuleFactory.getModuleName(),
540 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
541 String domain = extractDomain(source);
542 if (domain != null && domain.isEmpty() ==
false) {
543 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
544 RecentActivityExtracterModuleFactory.getModuleName(),
548 BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
549 if (webDownloadArtifact != null) {
550 bbartifacts.add(webDownloadArtifact);
554 for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
555 BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
556 associatedObjectArtifact.addAttribute(
557 new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
558 RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
560 bbartifacts.add(associatedObjectArtifact);
563 }
catch (TskCoreException ex) {
564 logger.log(Level.SEVERE, String.format(
"Error creating associated object artifact for file '%s'",
565 downloadedFilePath), ex);
570 this.addErrorMessage(
571 NbBundle.getMessage(
this.getClass(),
"Firefox.getDlPre24.errMsg.errParsingArtifacts",
572 this.getName(), errors));
579 postArtifacts(bbartifacts);
587 private void getDownloadVersion24() {
588 FileManager fileManager = currentCase.getServices().getFileManager();
589 List<AbstractFile> downloadsFiles;
591 downloadsFiles = fileManager.findFiles(dataSource,
"places.sqlite",
"Firefox");
592 }
catch (TskCoreException ex) {
593 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getDlV24.errMsg.errFetchFiles");
594 logger.log(Level.WARNING, msg);
595 this.addErrorMessage(this.getName() +
": " + msg);
599 if (downloadsFiles.isEmpty()) {
600 logger.log(Level.INFO,
"Didn't find any version-24.0 Firefox download files.");
605 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
607 for (AbstractFile downloadsFile : downloadsFiles) {
608 if (downloadsFile.getSize() == 0) {
611 String fileName = downloadsFile.getName();
612 String temps = RAImageIngestModule.getRATempPath(currentCase,
"firefox") + File.separator + fileName +
"-downloads" + j +
".db";
615 ContentUtils.writeToFile(downloadsFile,
new File(temps), context::dataSourceIngestIsCancelled);
616 }
catch (ReadContentInputStreamException ex) {
617 logger.log(Level.WARNING, String.format(
"Error reading Firefox download artifacts file '%s' (id=%d).",
618 fileName, downloadsFile.getId()), ex);
619 this.addErrorMessage(
620 NbBundle.getMessage(
this.getClass(),
"Firefox.getHistory.errMsg.errAnalyzeFile", this.getName(),
623 }
catch (IOException ex) {
624 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox download artifacts file '%s' (id=%d).",
625 temps, fileName, downloadsFile.getId()), ex);
626 this.addErrorMessage(
627 NbBundle.getMessage(
this.getClass(),
"Firefox.getDlV24.errMsg.errAnalyzeFile", this.getName(),
631 File dbFile =
new File(temps);
632 if (context.dataSourceIngestIsCancelled()) {
637 List<HashMap<String, Object>> tempList = this.dbConnect(temps, DOWNLOAD_QUERY_V24);
639 logger.log(Level.INFO,
"{0} - Now getting downloads from {1} with {2} artifacts identified.",
new Object[]{moduleName, temps, tempList.size()});
640 for (HashMap<String, Object> result : tempList) {
642 if (context.dataSourceIngestIsCancelled()) {
646 String url = result.get(
"url").toString();
648 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
650 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_URL,
651 RecentActivityExtracterModuleFactory.getModuleName(),
657 String target = result.get(
"target").toString();
658 String downloadedFilePath =
"";
659 if (target != null) {
661 downloadedFilePath = URLDecoder.decode(target.replaceAll(
"file:///",
""),
"UTF-8");
662 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH,
663 RecentActivityExtracterModuleFactory.getModuleName(),
664 downloadedFilePath));
665 long pathID = Util.findID(dataSource, downloadedFilePath);
667 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH_ID,
668 RecentActivityExtracterModuleFactory.getModuleName(),
671 }
catch (UnsupportedEncodingException ex) {
672 logger.log(Level.SEVERE,
"Error decoding Firefox download URL in " + temps, ex);
676 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
677 RecentActivityExtracterModuleFactory.getModuleName(),
678 Long.valueOf(result.get(
"lastModified").toString())));
679 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME,
680 RecentActivityExtracterModuleFactory.getModuleName(),
681 NbBundle.getMessage(this.getClass(),
"Firefox.moduleName")));
682 String domain = extractDomain(url);
683 if (domain != null && domain.isEmpty() ==
false) {
684 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN,
685 RecentActivityExtracterModuleFactory.getModuleName(), domain));
688 BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadsFile, bbattributes);
689 if (webDownloadArtifact != null) {
690 bbartifacts.add(webDownloadArtifact);
694 for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(downloadedFilePath), FilenameUtils.getPath(downloadedFilePath))) {
695 BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT);
696 associatedObjectArtifact.addAttribute(
697 new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT,
698 RecentActivityExtracterModuleFactory.getModuleName(), webDownloadArtifact.getArtifactID()));
699 bbartifacts.add(associatedObjectArtifact);
702 }
catch (TskCoreException ex) {
703 logger.log(Level.SEVERE, String.format(
"Error creating associated object artifact for file '%s'",
704 downloadedFilePath), ex);
709 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getDlV24.errMsg.errParsingArtifacts",
710 this.getName(), errors));
717 postArtifacts(bbartifacts);
724 private void getFormsHistory() {
725 FileManager fileManager = currentCase.getServices().getFileManager();
726 List<AbstractFile> formHistoryFiles;
729 Set<String> excludedFieldNames =
new HashSet<>(Arrays.asList(
735 formHistoryFiles = fileManager.findFiles(dataSource,
"formhistory.sqlite",
"Firefox");
736 }
catch (TskCoreException ex) {
737 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getFormsAutofill.errMsg.errFetchingFiles");
738 logger.log(Level.WARNING, msg);
739 this.addErrorMessage(this.getName() +
": " + msg);
743 if (formHistoryFiles.isEmpty()) {
744 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getFormsAutofill.errMsg.noFilesFound");
745 logger.log(Level.INFO, msg);
750 Collection<BlackboardArtifact> bbartifacts =
new ArrayList<>();
752 for (AbstractFile formHistoryFile : formHistoryFiles) {
753 if (formHistoryFile.getSize() == 0) {
757 String fileName = formHistoryFile.getName();
758 String tempFilePath = RAImageIngestModule.getRATempPath(currentCase,
"firefox") + File.separator + fileName + j +
".db";
760 ContentUtils.writeToFile(formHistoryFile,
new File(tempFilePath), context::dataSourceIngestIsCancelled);
761 }
catch (ReadContentInputStreamException ex) {
762 logger.log(Level.WARNING, String.format(
"Error reading Firefox web history artifacts file '%s' (id=%d).",
763 fileName, formHistoryFile.getId()), ex);
764 this.addErrorMessage(
765 NbBundle.getMessage(
this.getClass(),
"Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getName(),
768 }
catch (IOException ex) {
769 logger.log(Level.SEVERE, String.format(
"Error writing temp sqlite db file '%s' for Firefox web history artifacts file '%s' (id=%d).",
770 tempFilePath, fileName, formHistoryFile.getId()), ex);
771 this.addErrorMessage(
772 NbBundle.getMessage(
this.getClass(),
"Firefox.getFormsAutofill.errMsg.errAnalyzeFile", this.getName(),
776 File dbFile =
new File(tempFilePath);
777 if (context.dataSourceIngestIsCancelled()) {
783 boolean isFirefoxV64 = Util.checkColumn(
"timesUsed",
"moz_formhistory", tempFilePath);
784 String formHistoryQuery = (isFirefoxV64) ? FORMHISTORY_QUERY_V64 : FORMHISTORY_QUERY;
786 List<HashMap<String, Object>> tempList = this.dbConnect(tempFilePath, formHistoryQuery);
787 logger.log(Level.INFO,
"{0} - Now getting history from {1} with {2} artifacts identified.",
new Object[]{moduleName, tempFilePath, tempList.size()});
788 for (HashMap<String, Object> result : tempList) {
790 if (context.dataSourceIngestIsCancelled()) {
794 Collection<BlackboardAttribute> bbattributes =
new ArrayList<>();
796 String fieldName = ((result.get(
"fieldname").toString() != null) ? result.get(
"fieldname").toString() :
"");
798 if (excludedFieldNames.contains(fieldName.toLowerCase())) {
802 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME,
803 RecentActivityExtracterModuleFactory.getModuleName(),
806 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE,
807 RecentActivityExtracterModuleFactory.getModuleName(),
808 ((result.get(
"value").toString() != null) ? result.get(
"value").toString() :
"")));
812 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED,
813 RecentActivityExtracterModuleFactory.getModuleName(),
814 (Long.valueOf(result.get(
"firstUsed").toString()) / 1000000)));
816 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED,
817 RecentActivityExtracterModuleFactory.getModuleName(),
818 (Long.valueOf(result.get(
"lastUsed").toString()) / 1000000)));
820 bbattributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT,
821 RecentActivityExtracterModuleFactory.getModuleName(),
822 (Integer.valueOf(result.get(
"timesUsed").toString()))));
826 BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, formHistoryFile, bbattributes);
828 bbartifacts.add(bbart);
835 postArtifacts(bbartifacts);
844 private void getAutofillProfiles() {
845 FileManager fileManager = currentCase.getServices().getFileManager();
846 List<AbstractFile> autofillProfilesFiles;
848 autofillProfilesFiles = fileManager.findFiles(dataSource,
"autofill-profiles.json",
"Firefox");
849 }
catch (TskCoreException ex) {
850 String msg = NbBundle.getMessage(this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errGettingFiles");
851 logger.log(Level.SEVERE, msg, ex);
852 this.addErrorMessage(this.getName() +
": " + msg);
856 if (autofillProfilesFiles.isEmpty()) {
857 logger.log(Level.INFO,
"Didn't find any Firefox Autofill Profiles files.");
864 while (j < autofillProfilesFiles.size()) {
865 AbstractFile profileFile = autofillProfilesFiles.get(j++);
866 if (profileFile.getSize() == 0) {
869 String temps = RAImageIngestModule.getRATempPath(currentCase,
"Firefox") + File.separator + profileFile.getName() + j +
".json";
871 ContentUtils.writeToFile(profileFile,
new File(temps), context::dataSourceIngestIsCancelled);
872 }
catch (ReadContentInputStreamException ex) {
873 logger.log(Level.WARNING, String.format(
"Error reading Firefox Autofill profiles artifacts file '%s' (id=%d).",
874 profileFile.getName(), profileFile.getId()), ex);
875 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
876 this.getName(), profileFile.getName()));
878 }
catch (IOException ex) {
879 logger.log(Level.SEVERE, String.format(
"Error writing temp file '%s' for Firefox Autofill profiles file '%s' (id=%d).",
880 temps, profileFile.getName(), profileFile.getId()), ex);
881 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile",
882 this.getName(), profileFile.getName()));
886 logger.log(Level.INFO,
"{0}- Now getting Bookmarks from {1}",
new Object[]{moduleName, temps});
887 File dbFile =
new File(temps);
888 if (context.dataSourceIngestIsCancelled()) {
893 FileReader tempReader;
895 tempReader =
new FileReader(temps);
896 }
catch (FileNotFoundException ex) {
897 logger.log(Level.SEVERE,
"Error while trying to read the Autofill profiles json file for Firefox.", ex);
898 this.addErrorMessage(
899 NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzeFile", this.getName(),
900 profileFile.getName()));
904 final JsonParser parser =
new JsonParser();
906 JsonObject jsonRootObject;
907 JsonArray jAddressesArray;
910 jsonRootObject = parser.parse(tempReader).getAsJsonObject();
911 jAddressesArray = jsonRootObject.getAsJsonArray(
"addresses");
912 }
catch (JsonIOException | JsonSyntaxException | IllegalStateException ex) {
913 logger.log(Level.WARNING,
"Error parsing Json for Firefox Autofill profiles.", ex);
914 this.addErrorMessage(NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile3",
915 this.getName(), profileFile.getName()));
919 WebBrowserArtifactsHelper helper;
922 helper =
new WebBrowserArtifactsHelper(
923 Case.getCurrentCaseThrows().getSleuthkitCase(),
924 NbBundle.getMessage(this.getClass(),
"Firefox.parentModuleName"),
927 }
catch (NoCurrentCaseException ex) {
928 logger.log(Level.SEVERE,
"No case open, bailing.", ex);
932 for (JsonElement result : jAddressesArray) {
933 JsonObject address = result.getAsJsonObject();
934 if (address == null) {
938 JsonElement nameEl = address.get(
"name");
939 String name = (nameEl != null) ? nameEl.getAsString() :
"";
941 JsonElement emailEl = address.get(
"email");
942 String email = (emailEl != null) ? emailEl.getAsString() :
"";
944 JsonElement telEl = address.get(
"tel");
945 String tel = (telEl != null) ? telEl.getAsString() :
"";
946 JsonElement telCountryCodeEl = address.get(
"tel-country-code");
947 String telCountryCode = (telCountryCodeEl != null) ? telCountryCodeEl.getAsString() :
"";
948 JsonElement telNationalEl = address.get(
"tel-national");
949 String telNational = (telNationalEl != null) ? telNationalEl.getAsString() :
"";
951 String phoneNumber = makeTelNumber(tel, telCountryCode, telNational);
953 JsonElement createdEl = address.get(
"timeCreated");
954 Long datetimeCreated = (createdEl != null) ? createdEl.getAsLong()/1000 : Long.valueOf(0);
955 JsonElement lastusedEl = address.get(
"timeLastUsed");
956 Long datetimeLastUsed = (lastusedEl != null) ? lastusedEl.getAsLong()/1000 : Long.valueOf(0);
957 JsonElement timesUsedEl = address.get(
"timesUsed");
958 Integer timesUsed = (timesUsedEl != null) ? timesUsedEl.getAsShort() : Integer.valueOf(0);
960 JsonElement addressLine1El = address.get(
"address-line1");
961 String addressLine1 = (addressLine1El != null) ? addressLine1El.getAsString() :
"";
962 JsonElement addressLine2El = address.get(
"address-line2");
963 String addressLine2 = (addressLine2El != null) ? addressLine2El.getAsString() :
"";
964 JsonElement addressLine3El = address.get(
"address-line3");
965 String addressLine3 = (addressLine3El != null) ? addressLine3El.getAsString() :
"";
967 JsonElement postalCodeEl = address.get(
"postal-code");
968 String postalCode = (postalCodeEl != null) ? postalCodeEl.getAsString() :
"";
969 JsonElement countryEl = address.get(
"country");
970 String country = (countryEl != null) ? countryEl.getAsString() :
"";
972 String mailingAddress = makeFullAddress(addressLine1, addressLine2, addressLine3, postalCode, country );
975 helper.addWebFormAddress(name, email, phoneNumber,
976 mailingAddress, datetimeCreated, datetimeLastUsed, timesUsed);
977 }
catch (TskCoreException | Blackboard.BlackboardException ex) {
978 logger.log(Level.SEVERE,
"Error while trying to insert Firefox Autofill profile artifact{0}", ex);
979 this.addErrorMessage(
980 NbBundle.getMessage(
this.getClass(),
"Firefox.getAutofillProfiles.errMsg.errAnalyzingFile4",
981 this.getName(), profileFile.getName()));
996 private String extractDomain(String url) {
997 if (url == null || url.isEmpty()) {
1001 if (url.toLowerCase().startsWith(PLACE_URL_PREFIX)) {
1008 return NetworkUtils.extractDomain(url);
1021 private String makeTelNumber(String tel, String telCountryCode, String telNational) {
1023 if (tel != null && !tel.isEmpty()) {
1027 if ((telCountryCode != null && !telCountryCode.isEmpty()) &&
1028 (telNational != null && !telNational.isEmpty())) {
1029 return telCountryCode + telNational;
1046 private String makeFullAddress(String addressLine1, String addressLine2, String addressLine3, String postalCode, String country ) {
1047 String fullAddress =
"";
1048 fullAddress = appendAddressField(fullAddress, addressLine1 );
1049 fullAddress = appendAddressField(fullAddress, addressLine2 );
1050 fullAddress = appendAddressField(fullAddress, addressLine3 );
1051 fullAddress = appendAddressField(fullAddress, postalCode );
1052 fullAddress = appendAddressField(fullAddress, country );
1065 private String appendAddressField(String address, String addressfield) {
1067 String updatedAddress = address;
1068 if (addressfield != null && !addressfield.isEmpty()) {
1069 if (!updatedAddress.isEmpty()) {
1070 updatedAddress +=
", ";
1072 updatedAddress += addressfield;
1075 return updatedAddress;