From 9ada1e681ba553deabe548c6fdec536dbc208342 Mon Sep 17 00:00:00 2001 From: Tim Walsh Date: Tue, 10 Jan 2017 11:25:12 -0500 Subject: [PATCH 1/6] Add main() function to brunnhilde --- brunnhilde.py | 319 +++++++++++++++++++++++++------------------------- 1 file changed, 162 insertions(+), 157 deletions(-) diff --git a/brunnhilde.py b/brunnhilde.py index 8943ace..bb04cb3 100644 --- a/brunnhilde.py +++ b/brunnhilde.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- """ -Brunnhilde 1.3.1 +Brunnhilde --- A Siegfried-based digital archives reporting tool @@ -30,7 +30,7 @@ import subprocess import sys -def run_siegfried(source_dir): +def run_siegfried(args, source_dir): """Run siegfried on directory""" print("\nRunning Siegfried against %s. This may take a few minutes." % source_dir) global sf_command @@ -79,7 +79,7 @@ def run_bulkext(source_dir): bulkext_command = "bulk_extractor -S ssn_mode=2 -o '%s' -R '%s' | tee '%s'" % (bulkext_dir, source_dir, bulkext_log) subprocess.call(bulkext_command, shell=True) -def import_csv(): +def import_csv(cursor, conn): """Import csv file into sqlite db""" with open(sf_file, 'r') as f: reader = csv.reader(f) @@ -99,7 +99,7 @@ def import_csv(): cursor.execute(insertsql, row) conn.commit() -def get_stats(source_dir, scan_started): +def get_stats(args, source_dir, scan_started, cursor, html, brunnhilde_version, siegfried_version): """Get aggregate statistics and write to html report""" # get stats from sqlite db @@ -232,7 +232,7 @@ def get_stats(source_dir, scan_started): if args.bulkextractor == True: html.write('\n

Personally Identifiable Information (PII)

') -def generate_reports(): +def generate_reports(args, cursor, html): """Run sql queries on db to generate reports, write to csv and html""" full_header = ['Filename', 'Filesize', 'Date modified', 'Errors', 'Checksum', 'Namespace', 'ID', 'Format', 'Format version', 'MIME type', @@ -242,56 +242,56 @@ def generate_reports(): sql = "SELECT format, id, COUNT(*) as 'num' FROM siegfried GROUP BY format ORDER BY num DESC" path = os.path.join(csv_dir, 'formats.csv') format_header = ['Format', 'ID', 'Count'] - sqlite_to_csv(sql, path, format_header) - write_html('File formats', path, ',') + sqlite_to_csv(sql, path, format_header, cursor) + write_html('File formats', path, ',', html) # sorted format and version list report sql = "SELECT format, id, version, COUNT(*) as 'num' FROM siegfried GROUP BY format, version ORDER BY num DESC" path = os.path.join(csv_dir, 'formatVersions.csv') version_header = ['Format', 'ID', 'Version', 'Count'] - sqlite_to_csv(sql, path, version_header) - write_html('File formats and versions', path, ',') + sqlite_to_csv(sql, path, version_header, cursor) + write_html('File formats and versions', path, ',', html) # sorted mimetype list report sql = "SELECT mime, COUNT(*) as 'num' FROM siegfried GROUP BY mime ORDER BY num DESC" path = os.path.join(csv_dir, 'mimetypes.csv') mime_header = ['MIME type', 'Count'] - sqlite_to_csv(sql, path, mime_header) - write_html('MIME types', path, ',') + sqlite_to_csv(sql, path, mime_header, cursor) + write_html('MIME types', path, ',', html) # dates report sql = "SELECT SUBSTR(modified, 1, 4) as 'year', COUNT(*) as 'num' FROM siegfried GROUP BY year ORDER BY num DESC" path = os.path.join(csv_dir, 'years.csv') year_header = ['Year Last Modified', 'Count'] - sqlite_to_csv(sql, path, year_header) - write_html('Last modified dates by year', path, ',') + sqlite_to_csv(sql, path, year_header, cursor) + write_html('Last modified dates by year', path, ',', html) # unidentified files report sql = "SELECT * FROM siegfried WHERE id='UNKNOWN';" path = os.path.join(csv_dir, 'unidentified.csv') - sqlite_to_csv(sql, path, full_header) - write_html('Unidentified', path, ',') + sqlite_to_csv(sql, path, full_header, cursor) + write_html('Unidentified', path, ',', html) # warnings report sql = "SELECT * FROM siegfried WHERE warning <> '';" path = os.path.join(csv_dir, 'warnings.csv') - sqlite_to_csv(sql, path, full_header) + sqlite_to_csv(sql, path, full_header, cursor) if args.showwarnings == True: - write_html('Warnings', path, ',') + write_html('Warnings', path, ',', html) # errors report sql = "SELECT * FROM siegfried WHERE errors <> '';" path = os.path.join(csv_dir, 'errors.csv') - sqlite_to_csv(sql, path, full_header) - write_html('Errors', path, ',') + sqlite_to_csv(sql, path, full_header, cursor) + write_html('Errors', path, ',', html) # duplicates report sql = "SELECT * FROM siegfried t1 WHERE EXISTS (SELECT 1 from siegfried t2 WHERE t2.hash = t1.hash AND t1.filename != t2.filename) AND filesize<>'0' ORDER BY hash;" path = os.path.join(csv_dir, 'duplicates.csv') - sqlite_to_csv(sql, path, full_header) - write_html('Duplicates', path, ',') + sqlite_to_csv(sql, path, full_header, cursor) + write_html('Duplicates', path, ',', html) -def sqlite_to_csv(sql, path, header): +def sqlite_to_csv(sql, path, header, cursor): """Write sql query result to csv""" with open(path, 'w') as report: w = csv.writer(report) @@ -299,7 +299,7 @@ def sqlite_to_csv(sql, path, header): for row in cursor.execute(sql): w.writerow(row) -def write_html(header, path, file_delimiter): +def write_html(header, path, file_delimiter, html): """Write csv file to html table""" with open(path, 'r') as in_file: # count lines and then return to start of file @@ -397,7 +397,7 @@ def write_html(header, path, file_delimiter): # write link to top html.write('\n

(Return to top)

') -def close_html(): +def close_html(html): """Write html closing tags""" html.write('\n') html.write('\n') @@ -407,14 +407,14 @@ def make_tree(source_dir): tree_command = "tree -tDhR '%s' > '%s'" % (source_dir, os.path.join(report_dir, 'tree.txt')) subprocess.call(tree_command, shell=True) -def process_content(source_dir): +def process_content(args, source_dir, cursor, conn, html, brunnhilde_version, siegfried_version): """Run through main processing flow on specified directory""" scan_started = str(datetime.datetime.now()) # get time - run_siegfried(source_dir) # run siegfried - import_csv() # load csv into sqlite db - get_stats(source_dir, scan_started) # get aggregate stats and write to html file - generate_reports() # run sql queries, print to html and csv - close_html() # close HTML file tags + run_siegfried(args, source_dir) # run siegfried + import_csv(cursor, conn) # load csv into sqlite db + get_stats(args, source_dir, scan_started, cursor, html, brunnhilde_version, siegfried_version) # get aggregate stats and write to html file + generate_reports(args, cursor, html) # run sql queries, print to html and csv + close_html(html) # close HTML file tags make_tree(source_dir) # create tree.txt def write_pronom_links(old_file, new_file): @@ -431,143 +431,148 @@ def write_pronom_links(old_file, new_file): line = new_line # allow for more than one match per line out_file.write(new_line) -""" -MAIN FLOW -""" - -# system info -brunnhilde_version = 'Brunnhilde 1.3.1' -siegfried_version = subprocess.check_output(["sf", "-version"]).decode() - -# parse arguments -parser = argparse.ArgumentParser() -parser.add_argument("-b", "--bulkextractor", help="Run Bulk Extractor on source", action="store_true") -parser.add_argument("-d", "--diskimage", help="Use disk image instead of dir as input", action="store_true") -parser.add_argument("--hash", help="Specify hash algorithm", dest="hash", action="store", type=str) -parser.add_argument("--hfs", help="Use for raw disk images of HFS disks", action="store_true") -parser.add_argument("-n", "--noclam", help="Skip ClamScan Virus Check", action="store_true") -parser.add_argument("-r", "--removefiles", help="Delete 'carved_files' directory when done (disk image input only)", action="store_true") -parser.add_argument("-t", "--throttle", help="Pause for 1s between Siegfried scans", action="store_true") -parser.add_argument("-V", "--version", help="Display Brunnhilde version", action="version", version="%s" % brunnhilde_version) -parser.add_argument("-w", "--showwarnings", help="Add Siegfried warnings to HTML report", action="store_true") -parser.add_argument("-z", "--scanarchives", help="Decompress and scan zip, tar, gzip, warc, arc with Siegfried", action="store_true") -parser.add_argument("source", help="Path to source directory or disk image") -parser.add_argument("destination", help="Path to destination for reports") -parser.add_argument("basename", help="Accession number or identifier, used as basename for outputs") -args = parser.parse_args() - -# global variables -destination = args.destination -basename = args.basename -report_dir = os.path.join(destination, '%s' % basename) -csv_dir = os.path.join(report_dir, 'csv_reports') -log_dir = os.path.join(report_dir, 'logs') -bulkext_dir = os.path.join(report_dir, 'bulk_extractor') -sf_file = os.path.join(report_dir, 'siegfried.csv') - -# create directory for reports -try: - os.makedirs(report_dir) -except OSError as exception: - if exception.errno != errno.EEXIST: - raise - -# create subdirectory for CSV reports -try: - os.makedirs(csv_dir) -except OSError as exception: - if exception.errno != errno.EEXIST: - raise - -# create subdirectory for logs if needed -if args.bulkextractor == False and args.noclam == True: - pass -else: +def _make_parser(version): + parser = argparse.ArgumentParser() + parser.add_argument("-b", "--bulkextractor", help="Run Bulk Extractor on source", action="store_true") + parser.add_argument("-d", "--diskimage", help="Use disk image instead of dir as input", action="store_true") + parser.add_argument("--hash", help="Specify hash algorithm", dest="hash", action="store", type=str) + parser.add_argument("--hfs", help="Use for raw disk images of HFS disks", action="store_true") + parser.add_argument("-n", "--noclam", help="Skip ClamScan Virus Check", action="store_true") + parser.add_argument("-r", "--removefiles", help="Delete 'carved_files' directory when done (disk image input only)", action="store_true") + parser.add_argument("-t", "--throttle", help="Pause for 1s between Siegfried scans", action="store_true") + parser.add_argument("-V", "--version", help="Display Brunnhilde version", action="version", version="%s" % version) + parser.add_argument("-w", "--showwarnings", help="Add Siegfried warnings to HTML report", action="store_true") + parser.add_argument("-z", "--scanarchives", help="Decompress and scan zip, tar, gzip, warc, arc with Siegfried", action="store_true") + parser.add_argument("source", help="Path to source directory or disk image") + parser.add_argument("destination", help="Path to destination for reports") + parser.add_argument("basename", help="Accession number or identifier, used as basename for outputs") + + return parser + +def main(): + # system info + brunnhilde_version = 'brunnhilde 1.4.0' + siegfried_version = subprocess.check_output(["sf", "-version"]).decode() + + parser = _make_parser(brunnhilde_version) + args = parser.parse_args() + + # global variables + global destination, basename, report_dir, csv_dir, log_dir, bulkext_dir, sf_file + destination = args.destination + basename = args.basename + report_dir = os.path.join(destination, '%s' % basename) + csv_dir = os.path.join(report_dir, 'csv_reports') + log_dir = os.path.join(report_dir, 'logs') + bulkext_dir = os.path.join(report_dir, 'bulk_extractor') + sf_file = os.path.join(report_dir, 'siegfried.csv') + + # create directory for reports try: - os.makedirs(log_dir) + os.makedirs(report_dir) except OSError as exception: if exception.errno != errno.EEXIST: raise - -# create html report -temp_html = os.path.join(report_dir, 'temp.html') -html = open(temp_html, 'w') - -# open sqlite db -db = os.path.join(report_dir, 'siegfried.sqlite') -conn = sqlite3.connect(db) -conn.text_factory = str # allows utf-8 data to be stored -cursor = conn.cursor() - -# characterize source -if args.diskimage == True: # source is a disk image - # make tempdir - tempdir = os.path.join(report_dir, 'carved_files') + + # create subdirectory for CSV reports try: - os.makedirs(tempdir) + os.makedirs(csv_dir) except OSError as exception: if exception.errno != errno.EEXIST: raise - # export disk image contents to tempdir - if args.hfs == True: # hfs disks - carvefiles = "bash /usr/share/hfsexplorer/bin/unhfs -v -resforks APPLEDOUBLE -o '%s' '%s'" % (tempdir, args.source) - print("\nAttempting to carve files from disk image using HFS Explorer.") + # create subdirectory for logs if needed + if args.bulkextractor == False and args.noclam == True: + pass + else: try: - subprocess.call(carvefiles, shell=True) - print("\nFile carving successful.") - except subprocess.CalledProcessError as e: - print(e.output) - print("\nBrunnhilde was unable to export files from disk image. Ending process.") - shutil.rmtree(report_dir) - sys.exit() - - else: # non-hfs disks (note: no UDF support yet) - carvefiles = ['tsk_recover', '-a', args.source, tempdir] - print("\nAttempting to carve files from disk image using tsk_recover.") + os.makedirs(log_dir) + except OSError as exception: + if exception.errno != errno.EEXIST: + raise + + # create html report + temp_html = os.path.join(report_dir, 'temp.html') + html = open(temp_html, 'w') + + # open sqlite db + db = os.path.join(report_dir, 'siegfried.sqlite') + conn = sqlite3.connect(db) + conn.text_factory = str # allows utf-8 data to be stored + cursor = conn.cursor() + + # characterize source + if args.diskimage == True: # source is a disk image + # make tempdir + tempdir = os.path.join(report_dir, 'carved_files') try: - subprocess.check_output(carvefiles) - print("\nFile carving successful.") - except subprocess.CalledProcessError as e: - print(e.output) - print("\nBrunnhilde was unable to export files from disk image. Ending process.") - shutil.rmtree(report_dir) + os.makedirs(tempdir) + except OSError as exception: + if exception.errno != errno.EEXIST: + raise + + # export disk image contents to tempdir + if args.hfs == True: # hfs disks + carvefiles = "bash /usr/share/hfsexplorer/bin/unhfs -v -resforks APPLEDOUBLE -o '%s' '%s'" % (tempdir, args.source) + print("\nAttempting to carve files from disk image using HFS Explorer.") + try: + subprocess.call(carvefiles, shell=True) + print("\nFile carving successful.") + except subprocess.CalledProcessError as e: + print(e.output) + print("\nBrunnhilde was unable to export files from disk image. Ending process.") + shutil.rmtree(report_dir) + sys.exit() + + else: # non-hfs disks (note: no UDF support yet) + carvefiles = ['tsk_recover', '-a', args.source, tempdir] + print("\nAttempting to carve files from disk image using tsk_recover.") + try: + subprocess.check_output(carvefiles) + print("\nFile carving successful.") + except subprocess.CalledProcessError as e: + print(e.output) + print("\nBrunnhilde was unable to export files from disk image. Ending process.") + shutil.rmtree(report_dir) + sys.exit() + + # process tempdir + if args.noclam == False: # run clamAV virus check unless specified otherwise + run_clamav(tempdir) + process_content(args, tempdir, cursor, conn, html, brunnhilde_version, siegfried_version) + if args.bulkextractor == True: # bulk extractor option is chosen + run_bulkext(tempdir) + write_html('Personally Identifiable Information (PII)', '%s' % os.path.join(bulkext_dir, 'pii.txt'), '\t', html) + if args.removefiles == True: + shutil.rmtree(tempdir) + + + else: #source is a directory + if os.path.isdir(args.source) == False: + print("\nSource is not a Directory. If you're processing a disk image, place '-d' before source.") sys.exit() + if args.noclam == False: # run clamAV virus check unless specified otherwise + run_clamav(args.source) + process_content(args, args.source, cursor, conn, html, brunnhilde_version, siegfried_version) + if args.bulkextractor == True: # bulk extractor option is chosen + run_bulkext(args.source) + write_html('Personally Identifiable Information (PII)', '%s' % os.path.join(bulkext_dir, 'pii.txt'), '\t', html) + + # close HTML file + html.close() + + # write new html file, with hrefs for PRONOM IDs + new_html = os.path.join(report_dir, '%s.html' % basename) + write_pronom_links(temp_html, new_html) + + # remove temp html file + os.remove(temp_html) + + # close database connections + cursor.close() + conn.close() + + print("\nProcess complete. Reports in %s." % report_dir) - # process tempdir - if args.noclam == False: # run clamAV virus check unless specified otherwise - run_clamav(tempdir) - process_content(tempdir) - if args.bulkextractor == True: # bulk extractor option is chosen - run_bulkext(tempdir) - write_html('Personally Identifiable Information (PII)', '%s' % os.path.join(bulkext_dir, 'pii.txt'), '\t') - if args.removefiles == True: - shutil.rmtree(tempdir) - - -else: #source is a directory - if os.path.isdir(args.source) == False: - print("\nSource is not a Directory. If you're processing a disk image, place '-d' before source.") - sys.exit() - if args.noclam == False: # run clamAV virus check unless specified otherwise - run_clamav(args.source) - process_content(args.source) - if args.bulkextractor == True: # bulk extractor option is chosen - run_bulkext(args.source) - write_html('Personally Identifiable Information (PII)', '%s' % os.path.join(bulkext_dir, 'pii.txt'), '\t') - -# close HTML file -html.close() - -# write new html file, with hrefs for PRONOM IDs -new_html = os.path.join(report_dir, '%s.html' % basename) -write_pronom_links(temp_html, new_html) - -# remove temp html file -os.remove(temp_html) - -# close database connections -cursor.close() -conn.close() - -print("\nProcess complete. Reports in %s." % report_dir) +if __name__ == '__main__': + main() From ce5883c6a7a0c8c8f79c941f66288ce748b1b405 Mon Sep 17 00:00:00 2001 From: Tim Walsh Date: Tue, 10 Jan 2017 11:26:24 -0500 Subject: [PATCH 2/6] Add setup.py --- setup.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 setup.py diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..7f1d283 --- /dev/null +++ b/setup.py @@ -0,0 +1,31 @@ +from setuptools import setup + +setup( + name = 'brunnhilde', + version = '1.4.0', + url = 'https://github.com/timothyryanwalsh/brunnhilde', + author = 'Tim Walsh', + author_email = 'timothyryanwalsh@gmail.com', + py_modules = ['brunnhilde'], + scripts = ['brunnhilde.py'], + description = 'A Siegfried-based digital archives reporting tool for directories and disk images', + keywords = 'archives reporting formats directories diskimages', + platforms = ['POSIX'], + classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'License :: OSI Approved :: MIT License', + 'Intended Audience :: End Users/Desktop', + 'Intended Audience :: Developers', + 'Natural Language :: English', + 'Operating System :: MacOS', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: POSIX :: Linux', + 'Topic :: Communications :: File Sharing', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.5', + 'Topic :: Database', + 'Topic :: System :: Archiving', + 'Topic :: System :: Filesystems', + 'Topic :: Utilities' + ], +) \ No newline at end of file From 5baf4c42de11ef82e5c031a225ae2daf9459787a Mon Sep 17 00:00:00 2001 From: Tim Walsh Date: Tue, 10 Jan 2017 11:28:43 -0500 Subject: [PATCH 3/6] Update README installation instructions for 1.4.0 --- LICENSE | 21 +++++++++++++++++++++ README.md | 18 +++--------------- 2 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fd35989 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Tim Walsh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index aeddab5..2583fd8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## Brunnhilde - A reporting companion to Siegfried -### Version: Brunnhilde 1.3.1 +### Version: Brunnhilde 1.4.0 Generates aggregate reports of files in a directory or disk image based on input from Richard Lehane's [Siegfried](http://www.itforarchivists.com/siegfried). @@ -32,22 +32,10 @@ For a more detailed explanation of how multiple identifications are handled by S ### Installation -#### Install in Linux: -Clone or download Brunnhilde repository and extract to location of your choice (for use with Brunnhilde GUI, it is recommended to extract files to /usr/share/brunnhilde). +`sudo pip install brunnhilde` -#### Install in macOS/OS X with Homebrew: -`brew install timothyryanwalsh/digipres/brunnhilde` +Once installed, you can call brunnhilde with just `brunnhilde.py [arguments]`. -Once installed, you can call Brunnhilde on your Mac with just `brunnhilde.py [arguments]`. - -To upgrade from a previous version of Brunnhilde: -`brew update && brew upgrade timothyryanwalsh/digipres/brunnhilde` - -#### Usage in Bitcurator with the Brunnhilde GUI: -* Create directory /usr/share/brunnhilde: -`sudo mkdir /usr/share/brunnhilde` -* Move brunnhilde.py to /usr/share/brunnhilde: -`sudo mv /path/to/brunnhilde.py /usr/share/brunnhilde` ### Usage From f78b5c4544efe595bddd3ee78320663a5f2a33d7 Mon Sep 17 00:00:00 2001 From: Tim Walsh Date: Tue, 10 Jan 2017 11:30:53 -0500 Subject: [PATCH 4/6] Remove LICENSE.txt --- LICENSE.txt | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index fd35989..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 Tim Walsh - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. From dfd24ff114b5dc50ab0722ea97f3f04a97721a5b Mon Sep 17 00:00:00 2001 From: Tim Walsh Date: Tue, 10 Jan 2017 11:32:52 -0500 Subject: [PATCH 5/6] Update requirements --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2583fd8..61fe1e9 100644 --- a/README.md +++ b/README.md @@ -120,16 +120,18 @@ To characterize HFS formatted disks, pass both the "-d" and "--hfs" flags as arg All dependencies are already installed in Bitcurator (except for an issue with HFSExplorer - see section "HFS-formatted disk iamges" above for details). See instructions below for installing dependencies if you wish to use Brunnhilde in OS X or a different Linux environment (Brunnhilde is not supported in Windows). -#### General +#### Core requirements * Python (tested in 2.7 and 3.5) -* [Siegfried](http://www.itforarchivists.com/siegfried): Brunnhilde is now compatible with all version of Siegfried, including 1.6+. It does not support MIME-Info or FDD signatures: for Brunnhilde to work, Siegfried must be using the PRONOM signature file only. If you have been using MIME-Info or FDD signatures as a replacement for or alongside PRONOM with Siegfried 1.5/1.6 on your machine, entering "roy build -multi 0" in the terminal should return you to Siegfried's default PRONOM-only identification mode and allow Brunnhilde to work properly. -* tree: Installed by default in most Linux distros. On OS X, install using [Homebrew](http://brewformulas.org/tree). If tree is not installed on your machine, a blank tree.txt file will be created instead. +* [Siegfried](http://www.itforarchivists.com/siegfried): Brunnhilde is now compatible with all version of Siegfried, including 1.6+. It does not support MIME-Info or FDD signatures: for Brunnhilde to work, Siegfried must be using the PRONOM signature file only. If you have been using MIME-Info or FDD signatures as a replacement for or alongside PRONOM with Siegfried 1.5/1.6 on your machine, entering "roy build -multi 0" in the terminal should return you to Siegfried's default PRONOM-only identification mode and allow Brunnhilde to work properly. + +#### Optional * [bulk_extractor](https://github.com/simsong/bulk_extractor): Can be built on Linux and OS X from source distribution found [here](https://github.com/simsong/bulk_extractor) or installed using [Homebrew](http://brewformulas.org/BulkExtractor). * [ClamAV](https://www.clamav.net): Brunnhilde checks for viruses using ClamAV, which can be built from the source distribution found at [clamav.net](http://clamav.net) or using [Homebrew](http://brewformulas.org/Clamav). +* tree: Installed by default in most Linux distros. On OS X, install using [Homebrew](http://brewformulas.org/tree). If tree is not installed on your machine, a blank tree.txt file will be created instead. #### To process disk images * [SleuthKit](http://www.sleuthkit.org/): Install from source or, in OS X, using [Homebrew](http://brewformulas.org/sleuthkit). -* [HFSExplorer](https://sourceforge.net/projects/catacombae/files/HFSExplorer/0.23.1%20%28snapshot%202016-09-02%29/ ): Install from source. +* [HFSExplorer](https://sourceforge.net/projects/catacombae/files/HFSExplorer/0.23.1%20%28snapshot%202016-09-02%29/): Install from source. ### Future development to-dos From 0a54c190bdefc41c70219d134fdf3854f5d75520 Mon Sep 17 00:00:00 2001 From: Tim Walsh Date: Tue, 10 Jan 2017 11:34:04 -0500 Subject: [PATCH 6/6] Update installation instructions --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 61fe1e9..c32387c 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,8 @@ For a more detailed explanation of how multiple identifications are handled by S ### Installation +Brunnhilde minally requires that Python 2 or 3 and Siegfried are installed on your system. For more information, see "Dependencies" below. + `sudo pip install brunnhilde` Once installed, you can call brunnhilde with just `brunnhilde.py [arguments]`.