Fix all power-user review issues (FR-01 through FR-12)
Some checks failed
CI / lint (push) Failing after 12s
CI / typecheck (push) Failing after 12s

FR-01: Fix data directory default from ~/.fieldwitness to ~/.fwmetadata
FR-02/05/07: Accept all file types for attestation (not just images)
  - Web UI, CLI, and batch now accept PDFs, CSVs, audio, video, etc.
  - Perceptual hashing for images, SHA-256-only for everything else
FR-03: Implement C2PA import path + CLI commands (export/verify/import/show)
FR-04: Fix GPS downsampling bias (math.floor → round)
FR-06: Add HTML/PDF evidence summaries for lawyers
  - Always generates summary.html, optional summary.pdf via xhtml2pdf
FR-08: Fix CLI help text ("FieldWitness -- FieldWitness" artifact)
FR-09: Centralize stray paths (trusted_keys, carrier_history, last_backup)
FR-10: Add 67 C2PA bridge tests (vendor assertions, cert, GPS, export)
FR-12: Add Tor onion service support for source drop box
  - fieldwitness serve --tor flag, persistent/transient modes
  - Killswitch covers hidden service keys

Also: bonus fix for attest/api.py hardcoded path bypassing paths.py

224 tests passing (67 new).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aaron D. Lee
2026-04-02 20:10:37 -04:00
parent 3a9cb17a5a
commit 5b0d90eeaf
27 changed files with 3140 additions and 186 deletions

View File

@@ -1,17 +1,18 @@
{% extends "base.html" %}
{% block title %}Attest Image — FieldWitness{% endblock %}
{% block title %}Attest File — FieldWitness{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card bg-dark border-secondary">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-patch-check me-2 text-info"></i>Attest Image</h5>
<h5 class="mb-0"><i class="bi bi-patch-check me-2 text-info"></i>Attest File</h5>
</div>
<div class="card-body">
<p class="text-muted">
Create a cryptographic provenance attestation — sign an image with your Ed25519 identity
to prove when and by whom it was captured.
Create a cryptographic provenance attestation — sign any file with your Ed25519 identity
to prove when and by whom it was captured or created. Supports photos, documents,
sensor data, audio, video, and more.
</p>
{% if not has_identity %}
@@ -25,28 +26,31 @@
<form method="POST" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="mb-4">
<label for="image" class="form-label"><i class="bi bi-image me-1"></i>Image to Attest</label>
<input type="file" class="form-control" name="image" id="image"
accept="image/png,image/jpeg,image/webp,image/tiff,image/bmp" required>
<div class="form-text">Supports PNG, JPEG, WebP, TIFF, BMP.</div>
<label for="image" class="form-label"><i class="bi bi-file-earmark me-1"></i>Evidence File</label>
<input type="file" class="form-control" name="image" id="image" required>
<div class="form-text">
Accepts images (PNG, JPEG, WebP, TIFF), documents (PDF, DOCX, CSV, TXT),
audio (MP3, WAV, FLAC), video (MP4, MOV, MKV), and sensor data files.
Perceptual matching (pHash, dHash) is available for image files only.
</div>
</div>
<div class="mb-3">
<label for="caption" class="form-label"><i class="bi bi-chat-text me-1"></i>Caption (optional)</label>
<input type="text" class="form-control" name="caption" id="caption"
placeholder="What does this image show?" maxlength="500">
placeholder="What does this file document?" maxlength="500">
</div>
<div class="mb-3">
<label for="location_name" class="form-label"><i class="bi bi-geo-alt me-1"></i>Location (optional)</label>
<input type="text" class="form-control" name="location_name" id="location_name"
placeholder="Where was this taken?" maxlength="200">
placeholder="Where was this captured?" maxlength="200">
</div>
<div class="form-check form-switch mb-4">
<input class="form-check-input" type="checkbox" name="auto_exif" id="autoExif" checked>
<label class="form-check-label" for="autoExif">
Extract EXIF metadata automatically (GPS, timestamp, device)
Extract EXIF metadata automatically (GPS, timestamp, device) — images only
</label>
</div>

View File

@@ -39,7 +39,7 @@
{% else %}
<div class="alert alert-secondary">
<i class="bi bi-inbox me-2"></i>
No attestations yet. <a href="/attest" class="alert-link">Attest your first image</a>.
No attestations yet. <a href="/attest" class="alert-link">Attest your first file</a>.
</div>
{% endif %}
</div>

View File

@@ -7,9 +7,17 @@
<div class="alert alert-success">
<i class="bi bi-check-circle me-2"></i>
<strong>Attestation created successfully!</strong>
Image <code>{{ filename }}</code> has been attested and stored in the local log (index #{{ index }}).
File <code>{{ filename }}</code> has been attested and stored in the local log (index #{{ index }}).
</div>
{% if not is_image %}
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
This file is attested by cryptographic hash. Perceptual matching (pHash, dHash)
is available for image files only.
</div>
{% endif %}
<div class="card bg-dark border-secondary mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-file-earmark-check me-2"></i>Attestation Record</h5>
@@ -38,7 +46,7 @@
<div class="card bg-dark border-secondary mb-4">
<div class="card-header">
<h6 class="mb-0"><i class="bi bi-hash me-2"></i>Image Hashes</h6>
<h6 class="mb-0"><i class="bi bi-hash me-2"></i>File Hashes</h6>
</div>
<div class="card-body">
<div class="mb-2">
@@ -84,7 +92,7 @@
<div class="d-grid gap-2">
<a href="/attest" class="btn btn-outline-info">
<i class="bi bi-plus-circle me-2"></i>Attest Another Image
<i class="bi bi-plus-circle me-2"></i>Attest Another File
</a>
<a href="/attest/log" class="btn btn-outline-secondary">
<i class="bi bi-journal-text me-2"></i>View Attestation Log

View File

@@ -1,30 +1,33 @@
{% extends "base.html" %}
{% block title %}Verify Image — FieldWitness{% endblock %}
{% block title %}Verify File — FieldWitness{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card bg-dark border-secondary">
<div class="card-header">
<h5 class="mb-0"><i class="bi bi-search me-2 text-info"></i>Verify Image</h5>
<h5 class="mb-0"><i class="bi bi-search me-2 text-info"></i>Verify File</h5>
</div>
<div class="card-body">
<p class="text-muted">
Check an image against the local attestation log. Uses SHA-256 for exact matching
and perceptual hashes (pHash, dHash) for robustness against compression and resizing.
Check a file against the local attestation log. For image files, uses SHA-256 for
exact matching and perceptual hashes (pHash, dHash) for robustness against
compression and resizing. For all other file types, SHA-256 exact matching is used.
</p>
<form method="POST" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="mb-4">
<label for="image" class="form-label"><i class="bi bi-image me-1"></i>Image to Verify</label>
<input type="file" class="form-control" name="image" id="image"
accept="image/png,image/jpeg,image/webp,image/tiff,image/bmp" required>
<div class="form-text">Upload the image you want to verify against known attestations.</div>
<label for="image" class="form-label"><i class="bi bi-file-earmark-search me-1"></i>Evidence File to Verify</label>
<input type="file" class="form-control" name="image" id="image" required>
<div class="form-text">
Upload the file you want to verify against known attestations.
Accepts images, documents, audio, video, and data files.
</div>
</div>
<button type="submit" class="btn btn-info btn-lg w-100">
<i class="bi bi-search me-2"></i>Verify Image
<i class="bi bi-search me-2"></i>Verify File
</button>
</form>
</div>

View File

@@ -16,10 +16,18 @@
</div>
{% endif %}
{# Query image hashes #}
{% if not is_image %}
<div class="alert alert-info">
<i class="bi bi-info-circle me-2"></i>
This file is attested by cryptographic hash. Perceptual matching (pHash, dHash)
is available for image files only.
</div>
{% endif %}
{# Query file hashes #}
<div class="card bg-dark border-secondary mb-4">
<div class="card-header">
<h6 class="mb-0"><i class="bi bi-hash me-2"></i>Image Hashes for <code>{{ filename }}</code></h6>
<h6 class="mb-0"><i class="bi bi-hash me-2"></i>File Hashes for <code>{{ filename }}</code></h6>
</div>
<div class="card-body">
<div class="mb-2">
@@ -103,13 +111,13 @@
<div class="card-body">
<p class="text-muted small mb-3">
Generate a signed JSON receipt for legal or archival use.
Re-upload the same image to produce the downloadable file.
Re-upload the same file to produce the downloadable receipt.
</p>
<form action="/verify/receipt" method="post" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<div class="mb-3">
<input class="form-control form-control-sm bg-dark text-light border-secondary"
type="file" name="image" accept="image/*" required>
type="file" name="image" required>
</div>
<button type="submit" class="btn btn-outline-warning btn-sm">
Download Receipt (.json)
@@ -121,7 +129,7 @@
<div class="d-grid gap-2 mt-4">
<a href="/verify" class="btn btn-outline-info">
Verify Another Image
Verify Another File
</a>
<a href="/attest/log" class="btn btn-outline-secondary">
View Attestation Log