Skip to content

Audit fixes - #28

Merged
boyter merged 15 commits into
masterfrom
audit-fixes
Oct 28, 2026
Merged

boyter merged 15 commits into
masterfrom
audit-fixes

Conversation

@boyter

@boyter boyter commented Oct 28, 2026

Copy link
Copy Markdown
Owner

hashdeep audit fixes

Reproduced the failures reported in #12, which had four causes:

  • md5 only files reported every file as modified. Files generated with -c md5 (e.g. by 1.4.0) have no sha256 column, and the empty value was
    compared against the calculated sha256. Only hashes present in the audit file are now calculated and compared, along with size.
  • Absolute paths matched nothing. Relative paths are now resolved against the ## Invoked from: directory as well as the current directory, and
    ./file and file are treated as the same.
  • Filenames with , or " broke parsing. Lines are split by column position as hashdeep does not quote filenames, rather than parsed as CSV.
  • The audit file itself was counted as a new file when inside the audited directory. It is now skipped, as is the output file and SQLite sidecar
    files.

Also:

  • Moved files now fail the audit, matching hashdeep.
  • Results are reported in audit file order, then new files sorted.
  • Identical files moved at the same time are each matched to their own record.
  • A leading ./ the user supplied is kept in output paths, as hashdeep and md5sum do, so hashdeep -a can verify hashit -f hashdeep . output.

sum format

  • Can be used as an audit file: GNU md5sum/sha256sum style (text and binary mode, escaped names), BSD --tag style, and hashit's own multi hash
    sum output. The hash is determined by file extension, then by length.
  • Output escapes filenames containing \ or newlines the same way md5sum does, so md5sum -c can verify it.
  • --mtime lines are written as # mtime ... comments which md5sum -c ignores.

SQLite audit

Merges sqliteaudit and finishes it:

  • The audit database is opened read-only and never modified. Generated databases are left out of WAL mode so auditing creates no -wal/-shm files.
  • Seen files are tracked in an on disk temporary table and the report streams over the database, so memory stays flat. 100k files used 32 MB vs 225 MB
    for a hashdeep file audit.
  • The invoked from directory is stored in a new metadata table so relative databases can be audited using absolute paths or from another directory.
    Older databases without it still work.
  • The hashes requested with --hash (or the defaults) are calculated and compared where the database also has them, with a warning when there are
    none in common.
  • Moves are detected using the strongest common hash.
  • Output is labelled hashit: SQLite Audit passed/failed.
  • Restores the hashdeep cross verification tests the branch had removed from test-all.sh.

Other fixes

  • An mtime read error stopped the worker processing any further files and leaked the open file handle. The file is now still hashed with mtime unset.
  • Errors writing the output file are now reported rather than ignored.

Behaviour changes for release notes

  • Moved files fail an audit (matches hashdeep).
  • A leading ./ supplied on the command line is kept in output, e.g. hashit . prints ./file. Running with no arguments is unchanged.
  • --mtime in sum format is written as # mtime <time> <file> comment lines.
  • --output no longer replaces an existing file unless --overwrite is passed. For SQLite this replaces the database rather than merging into it,
    and is refused if the existing file is not a SQLite database.
  • A failed audit exits non zero when using --output.

Testing

  • New unit tests in processor/audit_parser_test.go and processor/audit_test.go.
  • test-all.sh has new tests covering the Add Audit Mode #12 scenarios: audit file inside the directory, absolute vs relative paths, md5 only hashdeep files,
    hashdeep verifying hashit output from . and ./sub, md5sum/sha256sum/BSD tag files, md5sum -c verifying sum output with and without --mtime,
    SQLite path resolution, no files left behind, moves, and overwrite protection.
  • test-all.sh passes in full.

boyter and others added 15 commits December 17, 2025 10:26
- Only compare hashes present in the audit file, so md5 only hashdeep
  files no longer report every file as modified
- Resolve relative paths against hashdeep's "Invoked from" directory and
  treat ./file and file as the same, so audits work with absolute paths
- Parse hashdeep lines without CSV so filenames with commas or quotes work
- Accept md5sum/sha256sum style files (GNU and BSD tag) as audit input
- Skip the audit file itself when it is inside the audited directory
- Fail the audit on moved files, matching hashdeep
- Report results in audit file order, then new files sorted
- Escape filenames in sum output the same way md5sum does

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Open the audit database read-only, without running migrations, and
  leave generated databases out of WAL mode so audits create no files
- Track seen files in an on disk temporary table and stream the final
  report, so memory stays flat regardless of database size
- Calculate the hashes stored in the database rather than using --hash
- Store the invoked from directory in a metadata table so relative
  databases can be audited using absolute paths or from elsewhere
- Detect moves using the strongest stored hash, handling duplicates
- Skip the output file and SQLite sidecar files when processing
- Restore hashdeep cross verification tests dropped on the branch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hashdeep and md5sum keep the ./ when run against . or ./dir, while we
cleaned it away, so hashdeep reported every file in an audit file we
generated from . as moved. Running with no arguments is unchanged.

SQLite audits now try paths with and without ./ so databases created
either way still match.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An mtime read error returned from the worker, which leaked the open file
and stopped that worker processing any further files. Now the error is
reported and the file is still hashed with mtime left unset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With --mtime the sum format wrote a "date  file" line per file which
md5sum -c reported as improperly formatted. It is now written as
"# mtime date  file". The audit parser still skips the old style lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SQLite audits calculate the hashes requested with --hash, or the
defaults, and compare those the database also stores, warning when
there are none in common. Output is labelled as a SQLite audit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Writing to an existing database merged into it, leaving rows for files
which no longer exist so later audits reported them missing. Writing
now fails unless --overwrite is passed, which replaces the database and
is only allowed when the existing file is a SQLite database.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every format now refuses to write over an existing --output file unless
--overwrite is passed. Also report errors writing the output file, and
exit non zero for a failed audit when writing to an output file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pr-insights pr-insights Bot added L/size Large change VH/complexity Very high complexity labels Oct 28, 2026
@boyter
boyter merged commit 3ee1a50 into master Oct 28, 2026
1 check passed
@boyter
boyter deleted the audit-fixes branch October 28, 2026 23:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L/size Large change VH/complexity Very high complexity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant