Spaces:
Running
Running
Commit History
-
Merge pull request #184 from abinaze/fix/webhook-redirect-ssrf Running
Unverified signaturefix(webhooks): stop following redirects on webhook delivery (SSRF bypass)f3c90f0 -
test(webhooks): add regression test for the redirect-based SSRF bypass
New test spins up two real local HTTP servers (one redirecting, one "internal") and asserts the internal one is never reached. Confirmed failing against the pre-fix code first (the internal server's handler genuinely fires), then passing against the fix. Also updates one existing test that mocked the old module-level urlopen name directly -- now mocks _NO_REDIRECT_OPENER.open, the actual new call site. Not a behavior change, just following the implementation detail the previous commit changed.36c7f8b -
fix(webhooks): stop following redirects on webhook delivery (SSRF bypass)
urlopen()'s default opener follows HTTP redirects automatically. The SSRF guard validates the registered URL's hostname (at registration and again at delivery, per F-9) but never inspects a redirect's Location header -- a webhook target that legitimately passes validation can redirect to an internal address (e.g. the cloud metadata IP) at delivery time and have it followed transparently. Verified empirically against a real local HTTP server before writing this fix. Now uses a custom opener that refuses to follow redirects, surfacing them as an ordinary delivery failure through the existing retry/suspend/logging path instead. Found independently, not from the numbered audit findings (I don't have that document's detailed sections).1a5e945
-
Merge pull request #183 from abinaze/docs/m9-env-example-clarify
Unverified signaturedocs(config): clarify which startup path loads which .env.examplebce6cf2 -
Merge pull request #182 from abinaze/docs/h4-m8-api-reference
Unverified signaturedocs(readme): fix stale /heatmap path, note frontend only calls /image432c04f -
docs(config): clarify which startup path loads which .env.example
backend/core/config.py's env_file=".env" is cwd-relative, and README.md / SETUP_GUIDE.md document two different working directories to start the server from -- so these three files are each load-bearing for a different real path, not accidental duplicates. Added a header to each explaining which path loads it, instead of deleting any of them. Verified directly against config.py, both docs, and the Dockerfile. No settings changed, no code changes.fcd6b46
-
docs(readme): fix stale /heatmap path, note frontend only calls /image
M-8: the API table listed /heatmap; the real route is /image/heatmap. H-4: added a note that the deployed frontend only calls POST /image -- the other eleven endpoints in this table are real and tested, just API-only. Both verified directly against analyze.py and index.html before this PR, not assumed. No code changes. Closes out the last of the six High findings.ee97262 -
Merge pull request #181 from abinaze/fix/c3-full-fix-prepare-upload
Unverified signaturefix(analyze): consolidate upload validation across all 9 file-accepting endpoints686fb38 -
fix(deps): bump transformers to 5.10.1 (CVE-2026-9856)
7820cb8 -
test(analyze): add regression tests for the C-3 full-fix pipeline
21 tests covering _prepare_image_bytes() directly, each migrated endpoint's new quality-gate/validation rejection, the /platform+/c2pa non-rotation guarantee (with an /attribution contrast case proving the difference is deliberate config, not a test artifact), /segment's new content-type check, and /batch's skip-and-continue behavior. Verified: fails in the predicted ways against the pre-fix code (18/21 failing -- ImportError, 200 instead of 422/415, unrotated instead of rotated, 3 processed instead of 2 -- see this PR's description for the full revert-and-confirm proof), passes cleanly against the fix.31a0502 -
fix(analyze): migrate /export/{fmt}, /image/stream
Same shared pipeline as the rest. /image/stream previously streamed spatial signals (PRNU, CFA, noise map, etc.) on a rotated iPhone photo without ever correcting its orientation -- now consistent with every other analysis endpoint. /export/{fmt}'s cache key is now computed on the post-EXIF-correction bytes, matching /image (previously hashed the raw upload -- no test depends on the literal hash value). This is the last of the 9 endpoints -- all file-accepting routes in this router now share one validation pipeline. Verified: full suite still green, fresh-checkout diff clean.eaa6846 -
fix(analyze): migrate /segment, add its missing content-type check
/segment was the only one of this router's 9 file-accepting endpoints with no content-type header check at all -- real image bytes with a mismatched declared content-type were previously accepted (the C-3 stopgap's validate_file() sniffs actual content, not the header). It now 415s consistently with every sibling endpoint, and also gains EXIF correction and the quality gate via the same shared pipeline. No existing test relied on the missing check (confirmed: zero endpoint-level tests existed for /segment before this PR); new coverage is included in Commit 7. Verified: full suite still green, fresh-checkout diff clean.348cbba -
fix(analyze): migrate /batch onto _prepare_image_bytes() per file
Each file in a batch now gets EXIF correction, MIME/extension validation, and the quality gate -- none of which /batch had before, beyond a bare per-file size check. Uses _prepare_image_bytes() directly (not prepare_upload()) since /batch already has its own whole-request Content-Length precheck before this loop; wrapped in the same skip-invalid-and-continue pattern already used for content-type and size. Verified: full suite still green, fresh-checkout diff clean.c89e868 -
fix(analyze): migrate /platform, /c2pa with correct_exif=False
Same shared pipeline, but EXIF correction deliberately disabled: both endpoints fingerprint properties of the ORIGINAL file structure (EXIF presence/absence for /platform; a binary JUMBF C2PA manifest for /c2pa) that a re-encode would corrupt or destroy. Still gain MIME/extension validation and the quality gate. Verified: full suite still green, fresh-checkout diff clean, plus new regression tests proving neither endpoint rotates an oriented image (test_c3_full_fix_prepare_upload.py, added in Commit 7).c559bab -
fix(analyze): migrate /image/heatmap, /attribution, /robustness
Same shared prepare_upload() pipeline as /image (Commit 1). All three previously had content-type + size checks only -- now also get EXIF correction, MIME/extension validation, and the quality gate. Verified: full suite still green, fresh-checkout diff clean.600f3fc -
fix(analyze): add shared prepare_upload() pipeline, migrate /image
Extract /image's six upload guards (content-type check, Content-Length precheck, EXIF correction, post-read size check, MIME/extension validation, quality gate) into _prepare_image_bytes()/prepare_upload(), shared functions the other 8 file-accepting endpoints migrate onto in the following commits. /image now calls prepare_upload() instead of its own inline copy of this logic; removed the now-dead except FileValidationError clause this makes unreachable. Verified: full suite still green, fresh-checkout diff clean.f5b299a
-
Merge pull request #180 from abinaze/refactor/h5c-retire-legacy-detector-facades
Unverified signaturerefactor: retire 3 dead legacy detector facades, rewrite their tests8fda679 -
docs(architecture): update legacy-facade section after retirement
Said the 3 classes "still exist as thin ... facades." They don't, after this change. Updated to describe the retirement and confirm StatisticalDetector itself is unaffected.d5afa21 -
docs(readme): fix stale class-hierarchy line (M-7)
Described an inheritance chain that was already replaced by composition, and now describes classes that don't exist at all after this same change retired them. Rewrote to state the real composition and note the retirement.d4ad75d -
refactor: retire 3 dead legacy detector facades, rewrite their tests
AdvancedAIDetector/UltraAdvancedDetector/CovarianceDetector had zero production imports anywhere -- confirmed by repo-wide search -- kept alive only by their own dedicated test files constructing them directly. Audited every assertion in those 3 test files (18 tests) against the rest of the suite before deleting anything: - "complete detection" tests (exactly 10/13/16 signals) tested a scoped subset that doesn't exist anywhere reachable anymore -- StatisticalDetector always runs the full 19. Not preserved. - "forensics_integration" tests (total_signals == 30) were near- identical duplicates across all 3 files, already covered repeatedly by test_determinism.py/test_invariants.py/test_api_stress.py. - Every remaining assertion (8 of 18) tested something not covered anywhere else -- specific signal_name/method/confidence values for DCT Coefficients, Wavelet Energy, RGB Noise Covariance, Patch Spectral Variance, Natural Prior Deviation, Eigenvalue Spread, Local Covariance …23de5d3
-
Fix license link in README
Unverified signatureUpdated license link in README to point to LICENSE.md.07fe586 -
Merge pull request #179 from abinaze/docs/h5b-pdf-export-rationale
Unverified signaturedocs: document the two PDF export paths as an intentional trade-off, not duplication277ff60 -
docs(report-exporter): cross-reference the frontend's separate PDF path
Matching note on the backend side: this module is reached only via a from-scratch re-analysis, never the already-computed report the frontend's download button uses. Same rationale as the frontend commit.a4a1dd2 -
docs(frontend): document why PDF export is client-side, not backend-called
H-5 flagged this as unexplained duplication. It isn't accidental: calling POST /export/pdf here would mean re-uploading the file and re-running the full analysis pipeline a second time just to get a PDF of a report already on screen, and that endpoint's own output has no thumbnail. Documented the trade-off in both files rather than merging them, which would be a real UX regression for this button.033a100
-
Merge pull request #178 from abinaze/refactor/h5a-shared-sanitize
Unverified signaturedocs(deployment): document the persistence gap for JSONL stateda069f3 -
refactor(sse): use shared json_safe.sanitize()
Removes the local, incomplete duplicate (was missing numpy-scalar conversion, unlike analyze.py's copy) in favor of the shared implementation both now use.406c86b -
refactor(analyze): use shared json_safe.sanitize()
Replaces the module-level _sanitize() definition with an import from the new shared module. All existing call sites unchanged (imported under the same local name).091223d -
refactor(utils): add shared json_safe.sanitize()
Extracted from analyze.py's _sanitize(), which sse_analyzer.py had a second, near-duplicate (and already-drifted) copy of. Also fixes a real bug found while writing tests for this: a numpy NaN/Infinity scalar was converted to a native float but never actually checked for being non-finite, because the numpy-conversion branch returned early. Both duplicates had this bug; fixed here rather than carried forward.91db04e
-
Merge pull request #177 from abinaze/docs/h2-persistence-guidance
Unverified signaturedocs(deployment): document the persistence gap for JSONL state3c90aae -
docs(deployment): document the persistence gap for JSONL state
Keys/cases/webhooks/feedback/audit-log persist to backend/data/*.jsonl on local disk; none of the three documented deployment paths attach persistent storage there by default, so a redeploy silently loses all of it. Uploaded images are unaffected (memory-only, unchanged). Added exact steps for HF Spaces Persistent Storage, a Docker volume mount for the VM option, and an optional render.yaml disk: snippet for Render -- not applied automatically, since it provisions a paid resource and that's a real decision, not a docs fix.fd2f030
-
Merge pull request #176 from abinaze/docs/h1-git-lfs-and-roadmap-fix
Unverified signatureDocs/h1 git lfs and roadmap fixa4f6847 -
docs(roadmap): correct stale SVG-logo changelog claim
Phase 32's changelog says the LFS-pointer PNG logo was replaced with an SVG. It wasn't: frontend/logo2.png is still an LFS stub, no .svg exists, and README's header still points at the .png. Appended a correction rather than rewriting the original entry -- editing history to match reality would make the record less accurate, not more. The logo itself is still unresolved (see H-1).51f872f -
docs(readme): note Git LFS prerequisite
README's Prerequisites list didn't mention Git LFS at all, unlike SETUP_GUIDE.md's own Prerequisites table, which already covers this correctly. Added a matching line so README isn't the odd one out.6978213 -
Merge pull request #175 from abinaze/fix/h6-ci-gates
Unverified signatureci: stop silently ignoring lint/type/security findingsc907a28 -
fix(deps): bump regex for transformers 5.5.0 compatibility
pip's resolver: transformers 5.5.0 needs regex>=2025.10.22; this project pinned 2023.12.25. clip's own regex dependency is unversioned, so it doesn't conflict with the bump.4ae0618 -
Merge branch 'fix/h6-ci-gates' of https://github.com/abinaze/VeriFile-X into fix/h6-ci-gates
# Conflicts: # backend/requirements.txt005ad46
-
fix(deps): bump transformers to 5.5.0, closing 5 CVEs
pip-audit found PYSEC-2025-217/PYSEC-2026-2288/2289/2290 in transformers==4.57.6; 5.5.0 satisfies all listed fix versions. transformers v5 requires huggingface_hub>=1.0.0,<2.0 -- bumped that too (0.34.0 -> 1.4.0). No other pinned dependency needed a change: torch==2.7.1, accelerate==1.7.0, and Pillow==12.3.0 already satisfy v5's stated minimums. Not directly imported anywhere in backend/ -- pulled in transitively by diffusers for CLIP text encoding in the SD pipeline DIRE uses. diffusers==0.38.0 is confirmed compatible with transformers 5.x in production elsewhere; the one documented diffusers+v5 break (missing MT5Tokenizer) only affects HunyuanDiT pipelines, which this project doesn't use. Could not test this end-to-end here (no functional native torch in this sandbox). This project's own CI now actually runs the full suite against these exact pinned versions (see H-6) -- if it turns up a real incompatibility, revert just this commit; the cryptography and setuptools fixes above sta…6e4555d -
fix(deps): bump transformers to 5.5.0, closing 5 CVEs
pip-audit found PYSEC-2025-217/PYSEC-2026-2288/2289/2290 in transformers==4.57.6; 5.5.0 satisfies all listed fix versions. transformers v5 requires huggingface_hub>=1.0.0,<2.0 -- bumped that too (0.34.0 -> 1.4.0). No other pinned dependency needed a change: torch==2.7.1, accelerate==1.7.0, and Pillow==12.3.0 already satisfy v5's stated minimums. Not directly imported anywhere in backend/ -- pulled in transitively by diffusers for CLIP text encoding in the SD pipeline DIRE uses. diffusers==0.38.0 is confirmed compatible with transformers 5.x in production elsewhere; the one documented diffusers+v5 break (missing MT5Tokenizer) only affects HunyuanDiT pipelines, which this project doesn't use. Could not test this end-to-end here (no functional native torch in this sandbox). This project's own CI now actually runs the full suite against these exact pinned versions (see H-6) -- if it turns up a real incompatibility, revert just this commit; the cryptography and setuptools fixes above sta…aa7b25b -
fix(deps): pin setuptools to close PYSEC-2026-3447
Was unpinned (resolved transitively to 79.0.1). Added an explicit floor at 83.0.0, the fix version pip-audit reported. Build-time only (MANIFEST.in exclusion bypass on macOS); no app-code impact.2aa5985 -
fix(deps): bump cryptography to close 3 CVEs
pip-audit (now enforced, see H-6) found PYSEC-2026-3552/3553/3554 in cryptography==48.0.1. Bumped to 50.0.0, satisfying all three fix versions. Not directly imported by app code (pinned only to close a transitive CVE, like idna/Pygments above); low risk.157e26c -
ci: enforce the coverage floor CI already claims to have
README documents --cov-fail-under=70 as the local command; CI only printed a warning below 60% and never failed. Added the same flag to the CI coverage step and aligned the summary step's threshold to match.4122720 -
ci: fail the build on pip-audit findings
pip-audit ran with continue-on-error: true, so a real CVE in a pinned dependency could not fail CI. Removed the flag. Could not get a clean local pip-audit run in this sandbox (Python 3.12 vs. the project's pinned 3.11; an old-style PyWavelets build breaks under newer setuptools) -- unrelated to any actual vulnerability. The real CI runner matches the pinned Python version, so if this surfaces a finding there, that's the fix doing its job, not a regression; use pip-audit --ignore-vuln for any accepted, documented exception rather than reverting to continue-on-error.23498fe -
ci: label flake8/mypy as explicitly advisory
Both already used continue-on-error: true (52+ known mypy errors, deferred per F-20). Renamed steps to say so explicitly instead of leaving it silent in the run log.e5193a5 -
Merge pull request #174 from abinaze/fix/h3-signal-isolation
Unverified signaturefix(statistical-signals): isolate exceptions per-signala2560cd
-
test(fuzz): stop hiding signal crashes behind pytest.skip()
test_random_valid_images() caught (TypeError, ValueError) and skipped instead of failing, masking the exact crash class fixed in the previous commit. Now asserts the detector never raises. Added test_h3_signal_isolation.py covering the fix directly.b70fbbd -
fix(signals): isolate exceptions in statistical signal bundle
16 of 19 signals in BasicSignals/UltraSignals/CovarianceSignals had no exception handling; one failure crashed the whole 12-signal ensemble. Added _safe_compute() wrapper, applied across all three compute_all(). Verified: a 1x1 image reproducibly crashes the original code (ValueError in analyze_fft_radial_spectrum); fixed code returns a neutral result instead. New tests confirm isolation, not just survival. Full suite: no regressions.8bc399c -
Merge pull request #173 from abinaze/fix/c3-content-length-precheck
Unverified signaturefix(analyze): add pre-read Content-Length size guard to 8 endpoints missing ita1ab057 -
fix(analyze): add missing per-file and total size checks to /batch
Problem: /batch had NO size check anywhere in the route handler -- the 5MB MAX_IMAGE_BYTES cap only existed inside batch_processor.process_batch(), checked only after all (up to 10) files were already fully buffered into memory in the route handler's own loop. Fix: added a coarse pre-read guard on the whole request's declared size (MAX_BATCH_SIZE * MAX_IMAGE_BYTES), plus a genuine per-file check right after each file's own read, skipping an individually oversized file rather than retaining it for the downstream pipeline -- matching the existing pattern of skipping unsupported content-types in the same loop. Verified: added the full regression suite for this delivery (test_c3_content_length_precheck.py, 15 tests) covering all 9 endpoints touched across commits 1-3. Each previously-unguarded endpoint is called directly with an oversized Content-Length header and a mock UploadFile whose .read() raises if called, proving the guard fires strictly before any read. Directly proved the origi…f00d03b -
fix(analyze): correct /segment's size ceiling and add missing file.close()
Problem: /segment relied solely on validate_file()'s general-purpose 50MB (MAX_FILE_SIZE_MB) ceiling -- five times higher than the 10MB (MAX_ANALYSIS_SIZE_MB) limit every sibling CPU-heavy analysis endpoint enforces. It was also the only file-accepting endpoint in this router with no finally: file.close(). Fix: added the same pre-read Content-Length guard and an explicit post-read check, both using MAX_ANALYSIS_SIZE_BYTES; added finally: await file.close(); added except HTTPException: raise as the first exception clause (closes a pre-existing latent NameError risk, where an exception raised before FileValidationError is imported inside the same try block could otherwise surface as a confusing NameError instead of the intended error). Verified: regression test confirms a file just over 10MB (well under the old 50MB ceiling) is now rejected at the correct, intended ceiling.0b8ca8f -
fix(analyze): add pre-read Content-Length guard to 7 endpoints missing it
Problem: /image/heatmap, /attribution, /platform, /c2pa, /robustness, /export/{fmt}, and /image/stream all read the entire request body via await file.read() before checking its size at all -- meaning an oversized upload was fully buffered into memory before ever being rejected. Only POST /image had this pre-check. Fix: added a shared _reject_if_content_length_exceeds(request, max_bytes) helper (mirroring /image's existing check) and wired it into all 7 endpoints, before their respective await file.read() call. Verified: proved the gap directly against the original code by calling the real, unpatched endpoint function with an oversized Content-Length header and a mock UploadFile whose .read() raises if called -- read() was reached, confirming the missing guard. Added regression tests (test_c3_content_length_precheck.py) using the same technique to prove the fix: each endpoint now raises HTTP 413 WITHOUT ever calling file.read(). Full existing suite shows zero regressions (same 7 pre-…ee4c4cb