From c1d4c0aa9cc1c077c1cb1f70ccde511db97e5e50 Mon Sep 17 00:00:00 2001 From: mantisfury Date: Sun, 18 Jan 2026 03:03:16 -0700 Subject: [PATCH] Feature: Add Media Forensics shard Add comprehensive image authenticity analysis capabilities: - EXIF metadata extraction and GPS analysis - Error Level Analysis (ELA) for manipulation detection - Perceptual hashing for image similarity - C2PA provenance verification - Sun position verification against claimed timestamps - Reverse image search integration (Google Lens, TinEye, Yandex, Bing) - Similar image detection within document corpus Frontend includes side-by-side ELA viewer, EXIF metadata display, and clickable reverse image search URLs for manual verification. Co-Authored-By: Claude Opus 4.5 --- .env.example | 19 + Dockerfile | 2 + README.md | 13 +- docker-compose.yml | 2 +- docs/Screens/mediaforensics.png | Bin 0 -> 378014 bytes .../arkham-shard-media-forensics/README.md | 137 + .../arkham_shard_media_forensics/__init__.py | 6 + .../arkham_shard_media_forensics/api.py | 1189 +++++++++ .../arkham_shard_media_forensics/models.py | 282 +++ .../services/__init__.py | 15 + .../services/c2pa_parser.py | 236 ++ .../services/ela_analyzer.py | 191 ++ .../services/exif_extractor.py | 261 ++ .../services/perceptual_hash.py | 180 ++ .../services/sun_position.py | 195 ++ .../arkham_shard_media_forensics/shard.py | 1913 ++++++++++++++ .../pyproject.toml | 29 + .../arkham-shard-media-forensics/shard.yaml | 56 + packages/arkham-shard-shell/src/App.tsx | 7 + .../media-forensics/MediaForensicsPage.css | 2243 +++++++++++++++++ .../media-forensics/MediaForensicsPage.tsx | 866 +++++++ .../src/pages/media-forensics/api.ts | 320 +++ .../media-forensics/components/C2PAPanel.tsx | 304 +++ .../media-forensics/components/ELAViewer.tsx | 287 +++ .../media-forensics/components/ExifPanel.tsx | 351 +++ .../components/SimilarImagesPanel.tsx | 382 +++ .../components/SunPositionPanel.tsx | 382 +++ .../components/WarningsPanel.tsx | 235 ++ .../pages/media-forensics/components/index.ts | 12 + .../src/pages/media-forensics/index.ts | 36 + .../src/pages/media-forensics/types.ts | 490 ++++ 31 files changed, 10637 insertions(+), 4 deletions(-) create mode 100644 docs/Screens/mediaforensics.png create mode 100644 packages/arkham-shard-media-forensics/README.md create mode 100644 packages/arkham-shard-media-forensics/arkham_shard_media_forensics/__init__.py create mode 100644 packages/arkham-shard-media-forensics/arkham_shard_media_forensics/api.py create mode 100644 packages/arkham-shard-media-forensics/arkham_shard_media_forensics/models.py create mode 100644 packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/__init__.py create mode 100644 packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/c2pa_parser.py create mode 100644 packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/ela_analyzer.py create mode 100644 packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/exif_extractor.py create mode 100644 packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/perceptual_hash.py create mode 100644 packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/sun_position.py create mode 100644 packages/arkham-shard-media-forensics/arkham_shard_media_forensics/shard.py create mode 100644 packages/arkham-shard-media-forensics/pyproject.toml create mode 100644 packages/arkham-shard-media-forensics/shard.yaml create mode 100644 packages/arkham-shard-shell/src/pages/media-forensics/MediaForensicsPage.css create mode 100644 packages/arkham-shard-shell/src/pages/media-forensics/MediaForensicsPage.tsx create mode 100644 packages/arkham-shard-shell/src/pages/media-forensics/api.ts create mode 100644 packages/arkham-shard-shell/src/pages/media-forensics/components/C2PAPanel.tsx create mode 100644 packages/arkham-shard-shell/src/pages/media-forensics/components/ELAViewer.tsx create mode 100644 packages/arkham-shard-shell/src/pages/media-forensics/components/ExifPanel.tsx create mode 100644 packages/arkham-shard-shell/src/pages/media-forensics/components/SimilarImagesPanel.tsx create mode 100644 packages/arkham-shard-shell/src/pages/media-forensics/components/SunPositionPanel.tsx create mode 100644 packages/arkham-shard-shell/src/pages/media-forensics/components/WarningsPanel.tsx create mode 100644 packages/arkham-shard-shell/src/pages/media-forensics/components/index.ts create mode 100644 packages/arkham-shard-shell/src/pages/media-forensics/index.ts create mode 100644 packages/arkham-shard-shell/src/pages/media-forensics/types.ts diff --git a/.env.example b/.env.example index c6122c0..96c7e02 100644 --- a/.env.example +++ b/.env.example @@ -103,6 +103,25 @@ POSTGRES_DB=arkhamdb # Only set if using an external Redis instance: # REDIS_URL=redis://your-redis-host:6379 +# ----------------------------------------------------------------------------- +# Media Forensics - Reverse Image Search API Keys (Optional) +# ----------------------------------------------------------------------------- +# These API keys enable automated reverse image search functionality. +# Without them, the Media Forensics shard still provides clickable URLs +# for manual reverse image searches on Google, TinEye, Yandex, and Bing. + +# TinEye API - Commercial reverse image search +# Sign up at: https://tineye.com/developers +# TINEYE_API_KEY=your-tineye-api-key + +# Google Cloud Vision API - Image analysis and web detection +# Enable at: https://console.cloud.google.com/apis/library/vision.googleapis.com +# GOOGLE_VISION_API_KEY=your-google-vision-api-key + +# SerpAPI - Google Images search results (has free tier) +# Sign up at: https://serpapi.com/ +# SERPAPI_KEY=your-serpapi-key + # ----------------------------------------------------------------------------- # Production HTTPS (Traefik) - Optional # ----------------------------------------------------------------------------- diff --git a/Dockerfile b/Dockerfile index a12af6e..104812d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -78,6 +78,7 @@ COPY packages/arkham-shard-settings/ ./packages/arkham-shard-settings/ COPY packages/arkham-shard-summary/ ./packages/arkham-shard-summary/ COPY packages/arkham-shard-templates/ ./packages/arkham-shard-templates/ COPY packages/arkham-shard-timeline/ ./packages/arkham-shard-timeline/ +COPY packages/arkham-shard-media-forensics/ ./packages/arkham-shard-media-forensics/ # Install CPU-only PyTorch BEFORE shards to avoid downloading ~3GB of CUDA libraries # This must come before sentence-transformers (used by arkham-shard-embed) @@ -147,6 +148,7 @@ COPY packages/arkham-shard-settings/shard.yaml /app/manifests/settings.yaml COPY packages/arkham-shard-summary/shard.yaml /app/manifests/summary.yaml COPY packages/arkham-shard-templates/shard.yaml /app/manifests/templates.yaml COPY packages/arkham-shard-timeline/shard.yaml /app/manifests/timeline.yaml +COPY packages/arkham-shard-media-forensics/shard.yaml /app/manifests/media-forensics.yaml WORKDIR /app diff --git a/README.md b/README.md index 474d210..3a6fa42 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,11 @@ Hybrid semantic/keyword search with built-in regex patterns for PII, financial d ![Search](docs/Screens/regex.png) +### Media Forensics +Image authenticity analysis with EXIF extraction, Error Level Analysis (ELA), perceptual hashing, and reverse image search integration. + +![Media Forensics](docs/Screens/mediaforensics.png) + --- @@ -113,7 +118,7 @@ SHATTERED uses the **Voltron** architectural philosophy: a modular, plug-and-pla +--------------------+--------------------+ | | | | | +----v----+ +--v--+ +-----v-----+ +--v--+ +---v---+ - |Dashboard| | ACH | | Search | |Graph| |Timeline| <-- SHARDS (25) + |Dashboard| | ACH | | Search | |Graph| |Timeline| <-- SHARDS (26) +---------+ +-----+ +-----------+ +-----+ +--------+ ``` @@ -268,7 +273,7 @@ The Frame provides 17 core services available to all shards: |-------|-------------|--------------| | **Search** | Document search | Semantic, keyword, hybrid search, facets, suggestions | -### Analysis (8 shards) +### Analysis (9 shards) | Shard | Description | Key Features | |-------|-------------|--------------| @@ -280,6 +285,7 @@ The Frame provides 17 core services available to all shards: | **Patterns** | Pattern recognition | Recurring, behavioral, temporal, correlation patterns | | **Provenance** | Evidence chains | Data lineage, audit trails, artifact verification | | **Summary** | Auto-summarization | Multiple summary types, batch processing, auto-summarize on ingest | +| **Media Forensics** | Image authenticity analysis | EXIF extraction, ELA analysis, perceptual hashing, C2PA verification, reverse image search | ### Visualization (2 shards) @@ -332,6 +338,7 @@ The Frame provides 17 core services available to all shards: | **NER** | spaCy (en_core_web_sm/lg/trf) | | **OCR** | PaddleOCR, Vision LLM (Qwen-VL local, or cloud GPT-4o/Claude) | | **Embeddings** | sentence-transformers, OpenAI | +| **Reverse Image Search** | TinEye, Google Vision, SerpAPI (optional APIs for Media Forensics) | --- @@ -782,7 +789,7 @@ Each shard has its own README with API documentation, events, and usage examples | Metric | Value | |--------|-------| | **Lines of Code** | ~217,000 | -| **Total Packages** | 26 (25 shards + shell) | +| **Total Packages** | 27 (26 shards + shell) | | **Frame Services** | 17 | | **API Endpoints** | 400+ | | **Graph Visualization Modes** | 10+ | diff --git a/docker-compose.yml b/docker-compose.yml index a13fb15..401ae0c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,7 +40,7 @@ services: # Recommended: BAAI/bge-m3 (1024 dims, ~2.2GB, high quality) # Lightweight: all-MiniLM-L6-v2 (384 dims, ~80MB, fast) # WARNING: Once set, changing models requires rebuilding vector collections - - EMBED_MODEL=${EMBED_MODEL:-} + - EMBED_MODEL=${EMBED_MODEL:-all-MiniLM-L6-v2} # Serve the React shell from FastAPI - ARKHAM_SERVE_SHELL=true # Auth security (REQUIRED for production - generate with: openssl rand -hex 32) diff --git a/docs/Screens/mediaforensics.png b/docs/Screens/mediaforensics.png new file mode 100644 index 0000000000000000000000000000000000000000..0c7a3ca6fa68d812832cd243f44efe8dd3e5beca GIT binary patch literal 378014 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

1b6u)lClqR)jpD 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

F8CHv^6E_LL}{@niJxe)^?U3HaFO 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
HH{ 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 Any: + """Convert numpy types to Python native types for JSON serialization.""" + if value is None: + return None + try: + import numpy as np + if isinstance(value, np.bool_): + return bool(value) + if isinstance(value, np.integer): + return int(value) + if isinstance(value, np.floating): + return float(value) + except ImportError: + pass + return value + +router = APIRouter(prefix="/api/media-forensics", tags=["media-forensics"]) + +# Shard reference set by init_api +_shard: Optional["MediaForensicsShard"] = None + + +def get_shard(request: Request) -> "MediaForensicsShard": + """Get the Media Forensics shard instance from app state.""" + shard = getattr(request.app.state, "media_forensics_shard", None) + if not shard: + raise HTTPException(status_code=503, detail="Media Forensics shard not available") + return shard + + +def init_api(shard: "MediaForensicsShard"): + """Initialize API with shard reference.""" + global _shard + _shard = shard + logger.info("Media Forensics API initialized") + + +# =========================================== +# Request/Response Models +# =========================================== + + +class AnalyzeRequest(BaseModel): + """Request to analyze a document.""" + document_id: str + + +class BatchAnalyzeRequest(BaseModel): + """Request to analyze multiple documents.""" + document_ids: list[str] + + +class ELARequest(BaseModel): + """Request to generate ELA analysis.""" + analysis_id: str + quality: int = Field(default=95, ge=70, le=100) + scale: int = Field(default=15, ge=5, le=30) + + +class SunPositionManualRequest(BaseModel): + """Request to calculate sun position with manual coordinates.""" + latitude: float = Field(..., ge=-90, le=90) + longitude: float = Field(..., ge=-180, le=180) + datetime: str # ISO format + + +class SunPositionRequest(BaseModel): + """Request to verify sun position from an analysis.""" + analysis_id: str + override_location: Optional[dict] = None # {"lat": float, "lon": float} + override_time: Optional[str] = None # ISO format + + +class SimilarSearchRequest(BaseModel): + """Request to find similar images.""" + analysis_id: str + hash_type: str = Field(default="phash", pattern="^(phash|dhash|ahash)$") + threshold: int = Field(default=15, ge=0, le=64) # Default 15 allows more visually similar results + search_type: str = Field(default="internal", pattern="^(internal|external|both)$") + limit: int = Field(default=50, ge=1, le=100) + + +# =========================================== +# Badge/Count Endpoint +# =========================================== + + +@router.get("/analyses/count") +async def get_analyses_count(request: Request): + """Get count of analyses for badge display.""" + shard = get_shard(request) + try: + count = await shard.get_analysis_count() + return {"count": count} + except Exception as e: + logger.error(f"Failed to get count: {e}") + return {"count": 0} + + +# =========================================== +# Analysis Endpoints +# =========================================== + + +@router.get("/analyses") +async def list_analyses( + request: Request, + limit: Annotated[int, Query(ge=1, le=500)] = 50, + offset: Annotated[int, Query(ge=0)] = 0, + status: Annotated[Optional[str], Query()] = None, + verification_status: Annotated[Optional[str], Query()] = None, + has_c2pa: Annotated[Optional[bool], Query()] = None, + has_warnings: Annotated[Optional[bool], Query()] = None, + has_findings: Annotated[Optional[bool], Query()] = None, + integrity_status: Annotated[Optional[str], Query()] = None, + doc_id: Annotated[Optional[str], Query()] = None, +): + """ + List media analyses with optional filtering. + + - **limit**: Maximum number of results (default 50, max 500) + - **offset**: Number of results to skip for pagination + - **status**: Filter by analysis status (pending, processing, completed, failed) + - **verification_status**: Filter by verification status (verified, flagged, unknown, tampered) + - **has_c2pa**: Filter by C2PA presence (true/false) + - **has_warnings**: Filter by warning presence (true/false) + - **has_findings**: Filter by findings presence (true/false) + - **integrity_status**: Filter by integrity status (verified, flagged, unverified, unknown) + - **doc_id**: Filter by document ID + """ + shard = get_shard(request) + + try: + items = await shard.list_analyses( + limit=limit, + offset=offset, + status=status, + verification_status=verification_status, + has_c2pa=has_c2pa, + has_warnings=has_warnings, + has_findings=has_findings, + integrity_status=integrity_status, + doc_id=doc_id, + ) + + total = await shard.get_analysis_count() + has_more = offset + len(items) < total + + return { + "items": items, + "total": total, + "limit": limit, + "offset": offset, + "has_more": has_more, + } + + except Exception as e: + logger.error(f"Failed to list analyses: {e}") + raise HTTPException(status_code=500, detail=f"Failed to list analyses: {str(e)}") + + +@router.get("/analyses/{analysis_id}") +async def get_analysis(request: Request, analysis_id: str): + """Get a specific analysis by ID.""" + shard = get_shard(request) + + analysis = await shard.get_analysis(analysis_id) + if not analysis: + raise HTTPException(status_code=404, detail=f"Analysis not found: {analysis_id}") + + return analysis + + +@router.get("/document/{document_id}") +async def get_analysis_by_document(request: Request, document_id: str): + """Get analysis for a specific document.""" + shard = get_shard(request) + + analysis = await shard.get_analysis_by_document(document_id) + if not analysis: + raise HTTPException(status_code=404, detail=f"No analysis found for document: {document_id}") + + return analysis + + +# =========================================== +# Analysis Actions +# =========================================== + + +@router.post("/analyze") +async def analyze_document(request: Request, body: AnalyzeRequest): + """ + Analyze a single document for media forensics. + + Extracts EXIF metadata, computes perceptual hashes, and parses C2PA content credentials. + Auto-finds similar images using perceptual hashing. + + Returns: + Full analysis results including: + - EXIF/XMP metadata + - Perceptual hashes (pHash, dHash, aHash) + - C2PA content credentials (if present) + - Warnings and anomalies detected + - Integrity status assessment + """ + shard = get_shard(request) + + try: + result = await shard.analyze_document(body.document_id) + return result + + except FileNotFoundError as e: + raise HTTPException(status_code=404, detail=str(e)) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + except Exception as e: + logger.error(f"Analysis failed: {e}") + raise HTTPException(status_code=500, detail=f"Analysis failed: {str(e)}") + + +@router.post("/analyze/batch") +async def analyze_batch(request: Request, body: BatchAnalyzeRequest): + """ + Analyze multiple documents in batch. + + Performs full media forensics analysis on each document. + + Returns: + List of analysis results with status per document. + """ + shard = get_shard(request) + + results = [] + for doc_id in body.document_ids: + try: + result = await shard.analyze_document(doc_id) + results.append({ + "document_id": doc_id, + "status": "success", + "analysis_id": result["analysis_id"], + "integrity_status": result["integrity_status"], + }) + except FileNotFoundError as e: + results.append({ + "document_id": doc_id, + "status": "error", + "error": str(e), + }) + except Exception as e: + logger.error(f"Batch analysis failed for {doc_id}: {e}") + results.append({ + "document_id": doc_id, + "status": "error", + "error": str(e), + }) + + return { + "results": results, + "total": len(results), + "successful": len([r for r in results if r["status"] == "success"]), + "failed": len([r for r in results if r["status"] == "error"]), + } + + +@router.post("/upload") +async def upload_and_analyze( + request: Request, + file: UploadFile = File(...), + run_ela: bool = Query(default=False, description="Also generate ELA analysis"), +): + """ + Upload an image file and analyze it directly. + + This endpoint allows direct file upload for media forensics analysis + without going through the standard document ingest pipeline. + + Accepts: JPEG, PNG, TIFF, WebP, GIF, BMP images. + + **Parameters:** + - **file**: Image file to analyze + - **run_ela**: Also generate ELA analysis (default: false) + + **Returns:** + Full analysis results including: + - EXIF/XMP metadata + - Perceptual hashes (pHash, dHash, aHash) + - C2PA content credentials (if present) + - Warnings and anomalies detected + - Integrity status assessment + - ELA results (if run_ela=true) + """ + shard = get_shard(request) + + # Validate file type + allowed_types = { + "image/jpeg", "image/png", "image/tiff", "image/webp", + "image/gif", "image/bmp", "image/jpg" + } + content_type = file.content_type or "" + filename = file.filename or "unknown" + + # Check by extension if content_type is generic + ext = Path(filename).suffix.lower() + ext_to_mime = { + ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".png": "image/png", + ".tiff": "image/tiff", ".tif": "image/tiff", ".webp": "image/webp", + ".gif": "image/gif", ".bmp": "image/bmp" + } + + if content_type not in allowed_types: + if ext in ext_to_mime: + content_type = ext_to_mime[ext] + else: + raise HTTPException( + status_code=400, + detail=f"Unsupported file type: {content_type}. Supported: JPEG, PNG, TIFF, WebP, GIF, BMP" + ) + + try: + # Save uploaded file to persistent location (not temp, since ELA needs it later) + upload_id = str(uuid.uuid4()) + + # Get storage path from frame config + storage_path = "data_silo" + if hasattr(shard._frame, 'config'): + config = shard._frame.config + if hasattr(config, 'get'): + storage_path = config.get("storage_path", "data_silo") + elif hasattr(config, 'storage_path'): + storage_path = config.storage_path or "data_silo" + + # Store in media_forensics subdirectory (persistent for ELA) + media_dir = Path(storage_path) / "media_forensics" + media_dir.mkdir(parents=True, exist_ok=True) + + # Preserve original extension with unique ID + stored_path = media_dir / f"{upload_id}{ext}" + + with open(stored_path, "wb") as f: + shutil.copyfileobj(file.file, f) + + logger.info(f"Saved uploaded file to {stored_path}") + + # Run analysis directly on file (file_path stored for future ELA) + result = await shard.analyze_file( + file_path=stored_path, + filename=filename, + run_ela=run_ela, + ) + + return result + + except Exception as e: + logger.error(f"Upload analysis failed: {e}") + raise HTTPException(status_code=500, detail=f"Analysis failed: {str(e)}") + + +@router.post("/analyze/path") +async def analyze_file_path( + request: Request, + file_path: str = Query(..., description="Absolute path to image file"), + run_ela: bool = Query(default=False, description="Also generate ELA analysis"), +): + """ + Analyze an image file by its file system path. + + This endpoint allows analysis of images already on the server + without uploading. + + **Parameters:** + - **file_path**: Absolute path to the image file + - **run_ela**: Also generate ELA analysis (default: false) + + **Returns:** + Full analysis results. + """ + shard = get_shard(request) + + path = Path(file_path) + if not path.exists(): + raise HTTPException(status_code=404, detail=f"File not found: {file_path}") + + if not path.is_file(): + raise HTTPException(status_code=400, detail=f"Not a file: {file_path}") + + # Check extension + ext = path.suffix.lower() + allowed_ext = {".jpg", ".jpeg", ".png", ".tiff", ".tif", ".webp", ".gif", ".bmp"} + if ext not in allowed_ext: + raise HTTPException( + status_code=400, + detail=f"Unsupported file type: {ext}. Supported: {', '.join(allowed_ext)}" + ) + + try: + result = await shard.analyze_file( + file_path=path, + filename=path.name, + run_ela=run_ela, + ) + return result + + except Exception as e: + logger.error(f"Path analysis failed: {e}") + raise HTTPException(status_code=500, detail=f"Analysis failed: {str(e)}") + + +# =========================================== +# ELA (Error Level Analysis) Endpoints +# =========================================== + + +@router.post("/ela") +async def generate_ela(request: Request, body: ELARequest): + """ + Generate Error Level Analysis (ELA) for an analyzed image. + + ELA works by re-saving the image at a known quality level and comparing + the difference. Modified regions may show different error levels. + + **Parameters:** + - **analysis_id**: ID of the analysis to generate ELA for + - **quality**: JPEG quality for resave (70-100, default 95) + - **scale**: Multiplier for error visualization (5-30, default 15) + + **Returns:** + - Base64-encoded ELA visualization image + - Interpretation of results + - List of caveats about ELA reliability + + **Important caveats:** + - ELA is NOT definitive proof of manipulation + - Different compression levels in original cause natural variations + - Works best on JPEG images + """ + shard = get_shard(request) + + if not shard.ela_analyzer: + raise HTTPException( + status_code=503, + detail="ELA analyzer not available. Pillow library may not be installed." + ) + + try: + result = await shard.generate_ela( + analysis_id=body.analysis_id, + quality=body.quality, + scale=body.scale, + ) + + if not result.get("success"): + raise HTTPException(status_code=400, detail=result.get("error", "ELA generation failed")) + + # Transform result to match frontend expected format (ELAResult) + interpretation = result.get("interpretation", {}) + ela_result = { + "id": str(uuid.uuid4()), + "doc_id": "", + "analysis_id": body.analysis_id, + "quality_level": result.get("quality_used", body.quality), + "original_image_url": None, + "ela_image_url": None, + "ela_image_base64": result.get("ela_image_base64"), + "global_avg_intensity": interpretation.get("mean_error", 0), + "global_max_intensity": interpretation.get("max_error", 0), + "suspicious_regions": [], # TODO: detect suspicious regions + "is_potentially_edited": interpretation.get("uniformity_score", 1.0) < 0.5 or interpretation.get("std_error", 0) > 30, + "confidence": interpretation.get("uniformity_score", 0.5), + "generated_at": datetime.utcnow().isoformat(), + } + + return {"result": ela_result} + + except HTTPException: + raise + except Exception as e: + logger.error(f"ELA generation failed: {e}") + raise HTTPException(status_code=500, detail=f"ELA generation failed: {str(e)}") + + +# =========================================== +# Sun Position Verification Endpoints +# =========================================== + + +@router.post("/sun-position") +async def verify_sun_position(request: Request, body: SunPositionRequest): + """ + Verify sun position for an analysis using EXIF data or overrides. + + Uses the GPS coordinates and timestamp from EXIF metadata (or provided overrides) + to calculate the expected sun position at the claimed time and location. + + **Parameters:** + - **analysis_id**: ID of the analysis to verify + - **override_location**: Optional override for GPS coordinates {"lat": float, "lon": float} + - **override_time**: Optional override for timestamp (ISO format) + + **Returns:** + - Sun position result with consistency assessment + """ + shard = get_shard(request) + + if not shard.sun_position: + raise HTTPException( + status_code=503, + detail="Sun position service not available. Pysolar library may not be installed." + ) + + try: + # Get the analysis + analysis = await shard.get_analysis(body.analysis_id) + if not analysis: + raise HTTPException(status_code=404, detail=f"Analysis not found: {body.analysis_id}") + + exif_data = analysis.get("exif_data", {}) + gps = exif_data.get("gps", {}) + timestamps = exif_data.get("timestamps", {}) + + # Use overrides if provided, otherwise use EXIF data + if body.override_location: + latitude = body.override_location.get("lat") + longitude = body.override_location.get("lon") + else: + latitude = gps.get("latitude") + longitude = gps.get("longitude") + + if body.override_time: + claimed_time = body.override_time + else: + claimed_time = ( + timestamps.get("datetime_original") + or timestamps.get("datetime_digitized") + or timestamps.get("datetime_modified") + ) + + # Validate we have required data + if latitude is None or longitude is None: + return { + "result": { + "id": str(uuid.uuid4()), + "doc_id": analysis.get("document_id", ""), + "analysis_id": body.analysis_id, + "claimed_location": None, + "claimed_time": claimed_time, + "calculated_sun_position": None, + "shadow_analysis": None, + "is_consistent": True, # Can't verify without location + "inconsistency_details": ["No GPS coordinates available for verification"], + "confidence": 0.0, + "generated_at": datetime.utcnow().isoformat(), + } + } + + if not claimed_time: + return { + "result": { + "id": str(uuid.uuid4()), + "doc_id": analysis.get("document_id", ""), + "analysis_id": body.analysis_id, + "claimed_location": {"lat": latitude, "lon": longitude}, + "claimed_time": None, + "calculated_sun_position": None, + "shadow_analysis": None, + "is_consistent": True, # Can't verify without time + "inconsistency_details": ["No timestamp available for verification"], + "confidence": 0.0, + "generated_at": datetime.utcnow().isoformat(), + } + } + + # Parse datetime + try: + # Handle EXIF format (YYYY:MM:DD HH:MM:SS) and ISO format + if ":" == claimed_time[4:5] and ":" == claimed_time[7:8]: + # EXIF format - convert colons in date part to dashes + claimed_time = claimed_time.replace(":", "-", 2) + dt = datetime.fromisoformat(claimed_time.replace("Z", "+00:00")) + except Exception as e: + logger.warning(f"Could not parse timestamp: {claimed_time} - {e}") + dt = datetime.utcnow() + + # Calculate sun position + sun_result = await shard.calculate_sun_position( + latitude=latitude, + longitude=longitude, + dt=dt, + ) + + if not sun_result.get("success"): + raise HTTPException(status_code=400, detail=sun_result.get("error", "Calculation failed")) + + # Convert numpy types to Python native for JSON serialization + sun_above_horizon = bool(sun_result.get("sun_above_horizon", True)) + sun_azimuth = _to_python_native(sun_result.get("sun_azimuth", 0)) + sun_altitude = _to_python_native(sun_result.get("sun_altitude", 0)) + shadow_direction = _to_python_native(sun_result.get("expected_shadow_direction")) + + # Build the response in the format the frontend expects + return { + "result": { + "id": str(uuid.uuid4()), + "doc_id": analysis.get("document_id", ""), + "analysis_id": body.analysis_id, + "claimed_location": {"lat": float(latitude), "lon": float(longitude)}, + "claimed_time": dt.isoformat(), + "calculated_sun_position": { + "azimuth": sun_azimuth, + "altitude": sun_altitude, + "calculated_time": dt.isoformat(), + "location_lat": float(latitude), + "location_lon": float(longitude), + }, + "shadow_analysis": { + "detected_shadows": [], + "average_shadow_direction": shadow_direction, + "consistency_score": 1.0 if sun_above_horizon else 0.5, + }, + "is_consistent": sun_above_horizon, + "inconsistency_details": [] if sun_above_horizon else [ + "Sun was below the horizon at the claimed time" + ], + "confidence": 0.85 if sun_above_horizon else 0.3, + "generated_at": datetime.utcnow().isoformat(), + } + } + + except HTTPException: + raise + except Exception as e: + logger.error(f"Sun position verification failed: {e}") + raise HTTPException(status_code=500, detail=f"Verification failed: {str(e)}") + + +@router.post("/sun-position/manual") +async def calculate_sun_position_manual(request: Request, body: SunPositionManualRequest): + """ + Calculate sun position for given coordinates and time (manual input). + + Uses the NOAA solar position algorithm to compute: + - Sun altitude (elevation above horizon) + - Sun azimuth (compass direction) + - Expected shadow direction (opposite of sun) + - Shadow length ratio (relative to object height) + + **Parameters:** + - **latitude**: Latitude in decimal degrees (-90 to 90) + - **longitude**: Longitude in decimal degrees (-180 to 180) + - **datetime**: ISO format datetime string with timezone + + **Returns:** + - Sun altitude and azimuth + - Expected shadow direction + - Interpretation text + """ + shard = get_shard(request) + + if not shard.sun_position: + raise HTTPException( + status_code=503, + detail="Sun position service not available. Pysolar library may not be installed." + ) + + try: + # Parse datetime + dt = datetime.fromisoformat(body.datetime.replace("Z", "+00:00")) + + result = await shard.calculate_sun_position( + latitude=body.latitude, + longitude=body.longitude, + dt=dt, + ) + + if not result.get("success"): + raise HTTPException(status_code=400, detail=result.get("error", "Calculation failed")) + + return result + + except ValueError as e: + raise HTTPException(status_code=400, detail=f"Invalid datetime format: {str(e)}") + except HTTPException: + raise + except Exception as e: + logger.error(f"Sun position calculation failed: {e}") + raise HTTPException(status_code=500, detail=f"Calculation failed: {str(e)}") + + +@router.get("/sun-position/{analysis_id}") +async def get_sun_position_from_exif(request: Request, analysis_id: str): + """ + Calculate sun position from an analyzed image's EXIF data. + + Uses the GPS coordinates and timestamp from EXIF metadata to + automatically calculate the expected sun position at the time + and place the photo was supposedly taken. + + **Requirements:** + - Analysis must have GPS coordinates in EXIF + - Analysis must have timestamp in EXIF + + **Returns:** + - Sun position data + - Interpretation for shadow verification + """ + shard = get_shard(request) + + if not shard.sun_position: + raise HTTPException( + status_code=503, + detail="Sun position service not available. Pysolar library may not be installed." + ) + + try: + result = await shard.get_sun_position_from_analysis(analysis_id) + + if not result.get("success"): + raise HTTPException(status_code=400, detail=result.get("error", "Calculation failed")) + + return result + + except HTTPException: + raise + except Exception as e: + logger.error(f"Sun position calculation failed: {e}") + raise HTTPException(status_code=500, detail=f"Calculation failed: {str(e)}") + + +# =========================================== +# Similar Image Search Endpoints +# =========================================== + + +@router.post("/similar") +async def find_similar_images(request: Request, body: SimilarSearchRequest): + """ + Find images similar to a given analysis using perceptual hashing. + + Perceptual hashing creates a fingerprint of an image that is + similar for visually similar images, even after resizing, compression, + or minor modifications. + + **Parameters:** + - **analysis_id**: Source analysis to find similar images for + - **hash_type**: Type of perceptual hash to use (phash, dhash, ahash) + - **threshold**: Maximum Hamming distance (0-64, lower = more similar) + + **Hash types:** + - **phash**: Perceptual hash - good general purpose + - **dhash**: Difference hash - fast, good for detecting crops + - **ahash**: Average hash - simplest, may have more false positives + + **Returns:** + - List of similar images with similarity scores + """ + shard = get_shard(request) + + if not shard.hash_service: + raise HTTPException( + status_code=503, + detail="Hash service not available. ImageHash library may not be installed." + ) + + try: + # Transform results to match frontend expected format + transformed_images = [] + exact_matches = 0 + near_duplicates = 0 + visually_similar = 0 + + # Internal search: search within local database + if body.search_type in ("internal", "both"): + similar = await shard.find_similar_images( + analysis_id=body.analysis_id, + hash_type=body.hash_type, + threshold=body.threshold, + ) + + for img in similar[:body.limit]: + # Determine similarity type based on hamming distance + distance = img.get("hamming_distance", 64) + similarity_score = img.get("similarity_score", 0) + + if distance == 0: + similarity_type = "exact" + exact_matches += 1 + elif distance <= 5: + similarity_type = "near_duplicate" + near_duplicates += 1 + else: + similarity_type = "visually_similar" + visually_similar += 1 + + # Fetch the analysis to get filename + similar_analysis = await shard.get_analysis(img.get("analysis_id")) + filename = "unknown" + if similar_analysis: + filename = similar_analysis.get("filename", "unknown") + + transformed_images.append({ + "id": img.get("analysis_id", str(uuid.uuid4())), + "doc_id": img.get("document_id", ""), + "filename": filename, + "similarity_score": similarity_score, + "similarity_type": similarity_type, + "match_details": { + "hash_distance": distance, + }, + "thumbnail_url": None, + "thumbnail_base64": None, + "source": "internal", + "found_at": datetime.utcnow().isoformat(), + }) + + # External search: reverse image search via web APIs or URL generators + search_urls = [] + if body.search_type in ("external", "both"): + # Get base URL from request for generating image URLs + base_url = str(request.base_url).rstrip("/") + + # Call the updated reverse_image_search that returns URLs and API results + external_data = await shard.reverse_image_search(body.analysis_id, base_url) + + # Add search URLs for manual searching + search_urls = external_data.get("search_urls", []) + + # Add any API-based results + for result in external_data.get("api_results", [])[:body.limit - len(transformed_images)]: + transformed_images.append({ + "id": str(uuid.uuid4()), + "doc_id": "", + "filename": result.get("title", "External Result"), + "similarity_score": result.get("similarity_score", 0.5), + "similarity_type": "content_similar", + "match_details": { + "source_url": result.get("url"), + "source_domain": result.get("domain"), + }, + "thumbnail_url": result.get("thumbnail_url"), + "thumbnail_base64": None, + "source": result.get("source", "external"), + "found_at": datetime.utcnow().isoformat(), + }) + + return { + "result": { + "id": str(uuid.uuid4()), + "doc_id": "", + "analysis_id": body.analysis_id, + "search_type": body.search_type, + "total_found": len(transformed_images), + "similar_images": transformed_images, + "exact_matches": exact_matches, + "near_duplicates": near_duplicates, + "visually_similar": len(transformed_images) - exact_matches - near_duplicates, + "generated_at": datetime.utcnow().isoformat(), + "search_urls": search_urls, # URLs for manual reverse image search + } + } + + except Exception as e: + logger.error(f"Similar image search failed: {e}") + raise HTTPException(status_code=500, detail=f"Search failed: {str(e)}") + + +# =========================================== +# Image Serving Endpoint (for reverse image search) +# =========================================== + + +@router.get("/image/{analysis_id}") +async def serve_analysis_image(request: Request, analysis_id: str): + """ + Serve the original image file for an analysis. + + This endpoint is used to provide a URL for external reverse image + search engines. The URL can be used with Google Lens, Yandex Images, + TinEye, and other services. + + **Note:** For URL-based reverse image search to work, this endpoint + must be accessible from the internet (not just localhost). + """ + shard = get_shard(request) + + try: + analysis = await shard.get_analysis(analysis_id) + if not analysis: + raise HTTPException(status_code=404, detail="Analysis not found") + + file_path = analysis.get("file_path") + if not file_path: + raise HTTPException(status_code=404, detail="No file path in analysis") + + path = Path(file_path) + if not path.exists(): + raise HTTPException(status_code=404, detail="Image file not found") + + # Determine content type from extension + ext_to_mime = { + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".png": "image/png", + ".gif": "image/gif", + ".webp": "image/webp", + ".bmp": "image/bmp", + ".tiff": "image/tiff", + ".tif": "image/tiff", + } + content_type = ext_to_mime.get(path.suffix.lower(), "application/octet-stream") + + # Stream the file + def iter_file(): + with open(path, "rb") as f: + while chunk := f.read(65536): # 64KB chunks + yield chunk + + return StreamingResponse( + iter_file(), + media_type=content_type, + headers={ + "Content-Disposition": f'inline; filename="{path.name}"', + "Cache-Control": "public, max-age=3600", + }, + ) + + except HTTPException: + raise + except Exception as e: + logger.error(f"Failed to serve image: {e}") + raise HTTPException(status_code=500, detail=f"Failed to serve image: {str(e)}") + + +# =========================================== +# Statistics Endpoint +# =========================================== + + +@router.get("/stats") +async def get_stats(request: Request): + """ + Get media forensics statistics. + + Returns aggregated statistics including: + - Total number of analyses + - Count with EXIF metadata + - Count with GPS data + - Count with C2PA content credentials + - Count with warnings/anomalies + - AI-generated images detected + - Breakdown by integrity status + - Breakdown by file type + """ + shard = get_shard(request) + + try: + stats = await shard.get_stats() + return {"stats": stats} + + except Exception as e: + logger.error(f"Failed to get stats: {e}") + raise HTTPException(status_code=500, detail=f"Failed to get stats: {str(e)}") + + +# =========================================== +# C2PA Support Check +# =========================================== + + +@router.get("/c2pa/supported") +async def check_c2pa_support(request: Request): + """ + Check which optional features are available. + + Returns availability of: + - C2PA parsing (requires c2pa-python library) + - C2PA signature verification + - Sun position calculations (requires pysolar library) + """ + shard = get_shard(request) + + return { + "c2pa_available": shard.c2pa_parser is not None, + "signature_verification_available": ( + shard.c2pa_parser is not None + and hasattr(shard.c2pa_parser, "verify_signature") + ), + "pysolar_available": shard.sun_position is not None, + "imagehash_available": shard.hash_service is not None, + "ela_available": shard.ela_analyzer is not None, + "exif_available": shard.exif_extractor is not None, + } + + +# =========================================== +# Bulk Operations +# =========================================== + + +@router.delete("/analyses/{analysis_id}") +async def delete_analysis(request: Request, analysis_id: str): + """Delete an analysis record.""" + shard = get_shard(request) + + if not shard._db: + raise HTTPException(status_code=503, detail="Database not available") + + # Check if analysis exists + analysis = await shard.get_analysis(analysis_id) + if not analysis: + raise HTTPException(status_code=404, detail=f"Analysis not found: {analysis_id}") + + try: + # Delete related records first (cascade should handle this, but explicit is safer) + await shard._db.execute( + "DELETE FROM arkham_media_ela WHERE analysis_id = :id", + {"id": analysis_id} + ) + await shard._db.execute( + "DELETE FROM arkham_media_sun_verification WHERE analysis_id = :id", + {"id": analysis_id} + ) + await shard._db.execute( + "DELETE FROM arkham_media_similar WHERE source_analysis_id = :id OR target_analysis_id = :id", + {"id": analysis_id} + ) + await shard._db.execute( + "DELETE FROM arkham_media_analyses WHERE id = :id", + {"id": analysis_id} + ) + + # Emit event + if shard._event_bus: + await shard._event_bus.emit( + "media.analysis.deleted", + { + "analysis_id": analysis_id, + "document_id": analysis["document_id"], + }, + source="media-forensics", + ) + + return {"status": "deleted", "analysis_id": analysis_id} + + except Exception as e: + logger.error(f"Failed to delete analysis: {e}") + raise HTTPException(status_code=500, detail=f"Delete failed: {str(e)}") + + +@router.post("/{analysis_id}/reanalyze") +async def reanalyze(request: Request, analysis_id: str): + """ + Re-run analysis on an existing record. + + Useful when analysis algorithms are updated or to refresh stale data. + """ + shard = get_shard(request) + + # Get existing analysis + analysis = await shard.get_analysis(analysis_id) + if not analysis: + raise HTTPException(status_code=404, detail=f"Analysis not found: {analysis_id}") + + try: + # Re-run analysis on the same document + result = await shard.analyze_document(analysis["document_id"]) + + return { + "status": "reanalyzed", + "old_analysis_id": analysis_id, + "new_analysis_id": result["analysis_id"], + "changes_detected": result["integrity_status"] != analysis["integrity_status"], + } + + except Exception as e: + logger.error(f"Reanalysis failed: {e}") + raise HTTPException(status_code=500, detail=f"Reanalysis failed: {str(e)}") + + +# =========================================== +# AI Junior Analyst Endpoint +# =========================================== + + +class AIJuniorAnalystRequest(BaseModel): + """Request for AI Junior Analyst analysis.""" + target_id: str + context: dict[str, Any] = {} + depth: str = "quick" + session_id: Optional[str] = None + message: Optional[str] = None + conversation_history: Optional[list[dict[str, str]]] = None + + +@router.post("/ai/junior-analyst") +async def ai_junior_analyst(request: Request, body: AIJuniorAnalystRequest): + """ + AI Junior Analyst endpoint for Media Forensics analysis. + + Provides AI-powered interpretation of media analysis including: + - EXIF metadata significance and anomalies + - C2PA content credential verification + - ELA interpretation for manipulation detection + - Sun position consistency analysis + - Similar image findings + - Overall authenticity assessment + + **Parameters:** + - **target_id**: ID of the analysis to interpret + - **context**: Additional context data + - **depth**: Analysis depth (quick, standard, deep) + - **session_id**: Optional session ID for conversation continuity + - **message**: Optional follow-up message + - **conversation_history**: Optional previous conversation messages + + **Returns:** + - Streaming SSE response with AI analysis + """ + shard = get_shard(request) + frame = shard._frame + + if not frame or not getattr(frame, "ai_analyst", None): + raise HTTPException( + status_code=503, + detail="AI Analyst service not available" + ) + + # Build context from request + try: + from arkham_frame.services import AnalysisRequest, AnalysisDepth, AnalystMessage + except ImportError: + raise HTTPException( + status_code=503, + detail="AI Analyst service not available (import failed)" + ) + + # Parse depth + try: + depth = AnalysisDepth(body.depth) + except ValueError: + depth = AnalysisDepth.QUICK + + # Build conversation history + history = None + if body.conversation_history: + history = [ + AnalystMessage(role=msg["role"], content=msg["content"]) + for msg in body.conversation_history + ] + + analysis_request = AnalysisRequest( + shard="media-forensics", + target_id=body.target_id, + context=body.context, + depth=depth, + session_id=body.session_id, + message=body.message, + conversation_history=history, + ) + + # Stream the response + return StreamingResponse( + frame.ai_analyst.stream_analyze(analysis_request), + media_type="text/event-stream", + headers={ + "Cache-Control": "no-cache", + "Connection": "keep-alive", + "X-Accel-Buffering": "no", + }, + ) diff --git a/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/models.py b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/models.py new file mode 100644 index 0000000..30ad6fd --- /dev/null +++ b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/models.py @@ -0,0 +1,282 @@ +"""Data models for the Media Forensics Shard.""" + +from dataclasses import dataclass, field +from datetime import datetime +from enum import Enum +from typing import Any, Optional, List, Dict + + +class IntegrityStatus(str, Enum): + """Integrity status of analyzed media.""" + UNKNOWN = "unknown" + VERIFIED = "verified" + FLAGGED = "flagged" + UNVERIFIED = "unverified" + + +class ELAAssessment(str, Enum): + """ELA analysis assessment.""" + UNIFORM = "uniform" + VARIABLE = "variable" + TYPICAL = "typical" + + +class SunVerificationStatus(str, Enum): + """Sun position verification status.""" + CONSISTENT = "consistent" + INCONSISTENT = "inconsistent" + UNCERTAIN = "uncertain" + UNAVAILABLE = "unavailable" + + +class HashType(str, Enum): + """Types of perceptual hashes.""" + PHASH = "phash" + DHASH = "dhash" + AHASH = "ahash" + + +# =========================================== +# API Request Models (Pydantic) +# =========================================== + +from pydantic import BaseModel, Field + + +class AnalyzeRequest(BaseModel): + """Request to analyze a document.""" + document_id: str + + +class BatchAnalyzeRequest(BaseModel): + """Request to analyze multiple documents.""" + document_ids: List[str] + + +class ELARequest(BaseModel): + """Request to generate ELA analysis.""" + analysis_id: str + quality: int = Field(default=95, ge=70, le=100) + scale: int = Field(default=15, ge=5, le=30) + + +class SunPositionRequest(BaseModel): + """Request to calculate sun position.""" + latitude: float = Field(..., ge=-90, le=90) + longitude: float = Field(..., ge=-180, le=180) + datetime: str # ISO format + + +class SimilarSearchRequest(BaseModel): + """Request to find similar images.""" + analysis_id: str + hash_type: str = Field(default="phash", pattern="^(phash|dhash|ahash)$") + threshold: int = Field(default=10, ge=0, le=64) + + +# =========================================== +# API Response Models (Pydantic) +# =========================================== + +class CountResponse(BaseModel): + """Response with count.""" + count: int + + +class AnalysisListResponse(BaseModel): + """Response with list of analyses.""" + items: List[Dict[str, Any]] + total: int + + +class AnalysisResponse(BaseModel): + """Response for a single analysis.""" + analysis_id: str + document_id: str + exif: Dict[str, Any] + hashes: Dict[str, str] + c2pa: Optional[Dict[str, Any]] + c2pa_interpretation: Optional[Dict[str, Any]] + warnings: List[str] + integrity_status: str + + +class BatchAnalyzeResponse(BaseModel): + """Response for batch analysis.""" + results: List[Dict[str, Any]] + + +class ELAResponse(BaseModel): + """Response for ELA analysis.""" + success: bool + ela_image_base64: Optional[str] = None + quality_used: Optional[int] = None + scale_used: Optional[int] = None + interpretation: Optional[Dict[str, Any]] = None + caveats: List[str] = [] + error: Optional[str] = None + + +class SunPositionResponse(BaseModel): + """Response for sun position calculation.""" + success: bool + latitude: Optional[float] = None + longitude: Optional[float] = None + datetime: Optional[str] = None + sun_altitude: Optional[float] = None + sun_azimuth: Optional[float] = None + expected_shadow_direction: Optional[float] = None + shadow_length_ratio: Optional[float] = None + sun_above_horizon: Optional[bool] = None + interpretation: Optional[str] = None + error: Optional[str] = None + + +class SimilarImageResult(BaseModel): + """Result for a similar image.""" + analysis_id: str + hash: str + hamming_distance: int + similarity_score: float + + +class SimilarSearchResponse(BaseModel): + """Response for similar image search.""" + similar_images: List[SimilarImageResult] + + +class StatsResponse(BaseModel): + """Response for statistics.""" + stats: Dict[str, Any] + + +class C2PASupportResponse(BaseModel): + """Response for C2PA support check.""" + c2pa_available: bool + signature_verification_available: bool + pysolar_available: bool + + +# =========================================== +# Data Classes for Internal Use +# =========================================== + +@dataclass +class MediaAnalysis: + """A media analysis record.""" + id: str + document_id: str + tenant_id: Optional[str] = None + + # File info + file_type: Optional[str] = None + file_size: Optional[int] = None + width: Optional[int] = None + height: Optional[int] = None + + # Cryptographic hashes + sha256: Optional[str] = None + md5: Optional[str] = None + + # Perceptual hashes + phash: Optional[str] = None + dhash: Optional[str] = None + ahash: Optional[str] = None + + # EXIF data + exif_data: Dict[str, Any] = field(default_factory=dict) + + # Camera info + camera_make: Optional[str] = None + camera_model: Optional[str] = None + software: Optional[str] = None + + # Timestamps + datetime_original: Optional[str] = None + datetime_digitized: Optional[str] = None + datetime_modified: Optional[str] = None + + # GPS + gps_latitude: Optional[float] = None + gps_longitude: Optional[float] = None + gps_altitude: Optional[float] = None + + # C2PA + c2pa_data: Dict[str, Any] = field(default_factory=dict) + has_c2pa: bool = False + c2pa_signer: Optional[str] = None + c2pa_timestamp: Optional[str] = None + + # Analysis results + warnings: List[str] = field(default_factory=list) + anomalies: List[str] = field(default_factory=list) + integrity_status: str = "unknown" + confidence_score: float = 0.0 + + # Timestamps + created_at: datetime = field(default_factory=datetime.utcnow) + updated_at: datetime = field(default_factory=datetime.utcnow) + + +@dataclass +class SimilarImage: + """A similar image match record.""" + id: str + source_analysis_id: str + target_analysis_id: str + hash_type: str + hamming_distance: int + similarity_score: float + created_at: datetime = field(default_factory=datetime.utcnow) + + +@dataclass +class ELAResult: + """An ELA analysis result.""" + id: str + analysis_id: str + quality: int = 95 + ela_image_path: Optional[str] = None + + # Analysis results + uniform_regions: List[Dict[str, Any]] = field(default_factory=list) + anomalous_regions: List[Dict[str, Any]] = field(default_factory=list) + interpretation: Optional[str] = None + + created_at: datetime = field(default_factory=datetime.utcnow) + + +@dataclass +class SunVerification: + """A sun position verification record.""" + id: str + analysis_id: str + + # Input parameters + claimed_datetime: Optional[str] = None + latitude: Optional[float] = None + longitude: Optional[float] = None + + # Calculated sun position + sun_altitude: Optional[float] = None + sun_azimuth: Optional[float] = None + expected_shadow_direction: Optional[float] = None + + # Verification result + verification_status: str = "unknown" + notes: Optional[str] = None + + created_at: datetime = field(default_factory=datetime.utcnow) + + +@dataclass +class AnalysisStats: + """Statistics about media analyses.""" + total_analyses: int = 0 + with_exif: int = 0 + with_gps: int = 0 + with_c2pa: int = 0 + with_warnings: int = 0 + ai_generated_detected: int = 0 + by_integrity_status: Dict[str, int] = field(default_factory=dict) + by_file_type: Dict[str, int] = field(default_factory=dict) diff --git a/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/__init__.py b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/__init__.py new file mode 100644 index 0000000..de0a29d --- /dev/null +++ b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/__init__.py @@ -0,0 +1,15 @@ +"""Media Forensics services.""" + +from .exif_extractor import ExifExtractor +from .perceptual_hash import PerceptualHashService +from .c2pa_parser import C2PAParser +from .ela_analyzer import ELAAnalyzer +from .sun_position import SunPositionService + +__all__ = [ + "ExifExtractor", + "PerceptualHashService", + "C2PAParser", + "ELAAnalyzer", + "SunPositionService", +] diff --git a/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/c2pa_parser.py b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/c2pa_parser.py new file mode 100644 index 0000000..1444ca0 --- /dev/null +++ b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/c2pa_parser.py @@ -0,0 +1,236 @@ +""" +C2PA (Content Credentials) parsing service. +Verifies provenance and authenticity metadata. +""" + +from typing import Dict, Any, Optional +from pathlib import Path +import json +import os + +import structlog + +logger = structlog.get_logger() + +# Try to import c2pa-python +try: + import c2pa + C2PA_AVAILABLE = True +except ImportError: + C2PA_AVAILABLE = False + logger.info("c2pa-python not installed, C2PA features disabled") + + +class C2PAParser: + """ + Parse and verify C2PA Content Credentials. + + C2PA is an open standard for media provenance adopted by: + - Adobe (Photoshop, Lightroom, Firefly) + - OpenAI (DALL-E) + - Microsoft (Designer) + - Google + - Camera manufacturers (Leica, Nikon) + + AIR-GAP SAFE: This parser makes NO network calls. + - Manifest parsing is fully offline + - Signature verification requires pre-downloaded trust anchors + - Without trust anchors, parsing still works but signatures are unverified + """ + + def __init__(self, frame, trust_anchors_path: Optional[str] = None): + """ + Initialize C2PA parser. + + Args: + frame: SHATTERED frame reference + trust_anchors_path: Optional path to local trust anchors PEM file. + Can also be set via MEDIA_FORENSICS_TRUST_ANCHORS_PATH env var. + If not provided, signature verification is skipped + but manifest parsing still works fully offline. + """ + self.frame = frame + self._signature_verification_available = False + + # Check for trust anchors path (explicit param > env var) + anchors_path = trust_anchors_path or os.environ.get("MEDIA_FORENSICS_TRUST_ANCHORS_PATH") + + if C2PA_AVAILABLE and anchors_path and Path(anchors_path).exists(): + try: + with open(anchors_path) as f: + anchors_content = f.read() + c2pa.load_settings({ + "verify": {"verify_cert_anchors": True}, + "trust": {"trust_anchors": anchors_content} + }) + self._signature_verification_available = True + logger.info("C2PA trust anchors loaded from local file", path=anchors_path) + except Exception as e: + logger.warning("Failed to load C2PA trust anchors", error=str(e)) + else: + logger.info( + "C2PA signature verification disabled (no trust anchors). " + "Manifest parsing still works. For signature verification, " + "set MEDIA_FORENSICS_TRUST_ANCHORS_PATH to a local .pem file." + ) + + def is_available(self) -> bool: + """Check if C2PA parsing is available.""" + return C2PA_AVAILABLE + + def is_signature_verification_available(self) -> bool: + """Check if signature verification is available (requires trust anchors).""" + return self._signature_verification_available + + async def parse(self, file_path: Path) -> Optional[Dict[str, Any]]: + """ + Parse C2PA manifest from a media file. + + Returns: + Dict with manifest data if C2PA credentials exist, None otherwise. + """ + if not C2PA_AVAILABLE: + return {"error": "c2pa-python library not installed", "has_c2pa": False} + + try: + with c2pa.Reader(str(file_path)) as reader: + manifest_json = reader.json() + + if not manifest_json: + return {"has_c2pa": False} + + store = json.loads(manifest_json) if isinstance(manifest_json, str) else manifest_json + + result = { + "has_c2pa": True, + "manifests": [], + "active_manifest": None, + "signature_valid": False, + "signature_verification_available": self._signature_verification_available, + "signer": None, + "timestamp": None, + "actions": [], + "ingredients": [], + "claim_generator": None, + "ai_training_permissions": {}, + "validation_status": [], + "raw_manifest": store, + } + + # Extract active manifest + if "active_manifest" in store: + active_id = store["active_manifest"] + result["active_manifest"] = active_id + + if "manifests" in store and active_id in store["manifests"]: + manifest = store["manifests"][active_id] + + # Claim generator + result["claim_generator"] = manifest.get("claim_generator") + + # Signature info (present in manifest, verification depends on trust anchors) + if "signature_info" in manifest: + sig_info = manifest["signature_info"] + # signature_valid only true if we have trust anchors loaded + result["signature_valid"] = self._signature_verification_available + result["signer"] = sig_info.get("issuer") + result["timestamp"] = sig_info.get("time") + + # Parse assertions + for assertion in manifest.get("assertions", []): + label = assertion.get("label", "") + + # Actions (edit history) + if label == "c2pa.actions": + result["actions"] = assertion.get("data", {}).get("actions", []) + + # AI training permissions + if label == "c2pa.training-mining": + entries = assertion.get("data", {}).get("entries", {}) + result["ai_training_permissions"] = { + "ai_training": entries.get("c2pa.ai_training", {}).get("use"), + "ai_inference": entries.get("c2pa.ai_inference", {}).get("use"), + "data_mining": entries.get("c2pa.data_mining", {}).get("use"), + "ai_generative_training": entries.get("c2pa.ai_generative_training", {}).get("use"), + } + + # Ingredients (source materials) + for ing in manifest.get("ingredients", []): + result["ingredients"].append({ + "title": ing.get("title"), + "format": ing.get("format"), + "instance_id": ing.get("instance_id"), + }) + + # All manifest IDs + if "manifests" in store: + result["manifests"] = list(store["manifests"].keys()) + + return result + + except Exception as e: + logger.warning("C2PA parsing failed", error=str(e), path=str(file_path)) + return {"error": str(e), "has_c2pa": False} + + def interpret_c2pa(self, c2pa_data: Dict) -> Dict[str, Any]: + """ + Interpret C2PA data into human-readable findings. + """ + if not c2pa_data or c2pa_data.get("error") or not c2pa_data.get("has_c2pa"): + return { + "finding": "NO_C2PA", + "confidence": "low", + "interpretation": ( + "No Content Credentials found. This doesn't prove the content is fake, " + "but means we cannot verify its provenance through C2PA." + ), + "implications": [ + "Content may be genuine but created with non-C2PA tools", + "Content credentials may have been stripped", + "Content may be AI-generated without C2PA tagging", + ], + } + + findings = { + "finding": "C2PA_PRESENT", + "confidence": "high" if c2pa_data.get("signature_valid") else "medium", + "signer": c2pa_data.get("signer"), + "timestamp": c2pa_data.get("timestamp"), + "interpretation": "", + "implications": [], + "is_ai_generated": False, + } + + # Interpret based on signer + signer = (c2pa_data.get("signer") or "").lower() + + if "openai" in signer or "dall-e" in signer: + findings["interpretation"] = ( + "This image was generated by OpenAI's DALL-E and has valid Content Credentials." + ) + findings["implications"] = [ + "Image is AI-generated (confirmed by creator)", + "OpenAI has signed this as their creation", + "This is a legitimate disclosure, not a fake", + ] + findings["is_ai_generated"] = True + + elif "adobe" in signer: + findings["interpretation"] = "This content has Adobe Content Credentials." + # Check actions for AI generation + actions = c2pa_data.get("actions", []) + if any("generative" in str(a).lower() or "firefly" in str(a).lower() for a in actions): + findings["implications"].append("Contains AI-generated elements (Adobe Firefly)") + findings["is_ai_generated"] = True + else: + findings["implications"].append("Created or edited with Adobe software") + + elif "microsoft" in signer: + findings["interpretation"] = "This content has Microsoft Content Credentials." + findings["implications"].append("May have been created with Microsoft Designer or Copilot") + + else: + findings["interpretation"] = f"Content signed by: {c2pa_data.get('signer', 'Unknown')}" + findings["implications"].append("Provenance verified through C2PA signature") + + return findings diff --git a/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/ela_analyzer.py b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/ela_analyzer.py new file mode 100644 index 0000000..adc8e39 --- /dev/null +++ b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/ela_analyzer.py @@ -0,0 +1,191 @@ +""" +Error Level Analysis (ELA) service. +Highlights regions with different compression levels. + +IMPORTANT: ELA has high false positive rates and should be used +as a visualization tool, NOT as definitive evidence of manipulation. +""" + +from typing import Dict, Any +from pathlib import Path +import io +import base64 +import uuid + +from PIL import Image +import numpy as np + +import structlog + +logger = structlog.get_logger() + + +class ELAAnalyzer: + """ + Perform Error Level Analysis on images. + + ELA works by re-saving an image at a known quality level and + comparing the result to the original. Regions that have been + modified may show different error levels. + + CAVEAT: ELA is unreliable for: + - Images saved multiple times + - AI-generated images (often uniform ELA) + - High-quality original images + - Certain types of edits + """ + + def __init__(self, frame): + self.frame = frame + self.storage = frame.get_service("storage") if frame else None + + async def analyze( + self, + file_path: Path, + quality: int = 95, + scale: int = 15, + ) -> Dict[str, Any]: + """ + Perform ELA analysis on an image. + + Args: + file_path: Path to image file + quality: JPEG quality for resave (90-95 recommended) + scale: Multiplier for error visualization (10-20 recommended) + + Returns: + Dict with ELA image, interpretation, and caveats + """ + try: + # Load original image + with Image.open(file_path) as original: + # Convert to RGB if necessary + if original.mode != "RGB": + original = original.convert("RGB") + + original_array = np.array(original) + + # Resave at specified quality + buffer = io.BytesIO() + original.save(buffer, format="JPEG", quality=quality) + buffer.seek(0) + + with Image.open(buffer) as resaved: + resaved_array = np.array(resaved) + + # Compute absolute difference + diff = np.abs(original_array.astype(np.int16) - resaved_array.astype(np.int16)) + + # Scale for visibility + ela = np.clip(diff * scale, 0, 255).astype(np.uint8) + + # Create ELA image + ela_image = Image.fromarray(ela) + + # Convert to base64 for response + ela_buffer = io.BytesIO() + ela_image.save(ela_buffer, format="PNG") + ela_base64 = base64.b64encode(ela_buffer.getvalue()).decode() + + # Analyze the ELA result + interpretation = self._interpret_ela(ela) + + return { + "success": True, + "ela_image_base64": ela_base64, + "quality_used": quality, + "scale_used": scale, + "interpretation": interpretation, + "caveats": [ + "ELA has high false positive rates", + "Uniform ELA may indicate AI generation OR multiple saves", + "Different error levels don't definitively prove manipulation", + "Use as one signal among many, not as proof", + ], + } + + except Exception as e: + logger.error("ELA analysis failed", error=str(e)) + return { + "success": False, + "error": str(e), + } + + def _interpret_ela(self, ela_array: np.ndarray) -> Dict[str, Any]: + """ + Analyze ELA result for patterns. + + Returns interpretation with appropriate caveats. + """ + # Calculate statistics + mean_error = np.mean(ela_array) + std_error = np.std(ela_array) + max_error = np.max(ela_array) + + # Check uniformity + # Split into blocks and check variance + block_size = 64 + h, w = ela_array.shape[:2] + + block_means = [] + for y in range(0, h - block_size, block_size): + for x in range(0, w - block_size, block_size): + block = ela_array[y:y + block_size, x:x + block_size] + block_means.append(np.mean(block)) + + block_std = np.std(block_means) if block_means else 0 + + interpretation = { + "mean_error": float(mean_error), + "std_error": float(std_error), + "max_error": float(max_error), + "uniformity_score": float(1.0 - min(block_std / 50, 1.0)), # 0-1, higher = more uniform + "assessment": "", + "details": [], + } + + # Interpret results (with heavy caveats) + if block_std < 5: + interpretation["assessment"] = "Highly uniform error levels" + interpretation["details"].append( + "The error levels are unusually uniform across the image. " + "This MAY indicate: (1) AI-generated content, (2) multiple JPEG saves, " + "or (3) heavy post-processing. This is NOT definitive." + ) + elif block_std > 20: + interpretation["assessment"] = "Variable error levels detected" + interpretation["details"].append( + "Different regions show different error levels. " + "This MAY indicate editing, or may simply reflect different content " + "(e.g., sky vs. detailed areas naturally compress differently)." + ) + else: + interpretation["assessment"] = "Error levels appear typical" + interpretation["details"].append( + "No obvious anomalies in error level distribution. " + "This does NOT prove the image is authentic." + ) + + return interpretation + + async def save_ela_image( + self, + analysis_id: str, + ela_base64: str, + ) -> str: + """Save ELA image to storage and return path.""" + # Generate filename + filename = f"ela_{analysis_id}_{uuid.uuid4().hex[:8]}.png" + + # Decode and save + ela_bytes = base64.b64decode(ela_base64) + + if self.storage: + path = await self.storage.save( + ela_bytes, + filename=filename, + category="media_forensics", + ) + return path + + return filename diff --git a/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/exif_extractor.py b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/exif_extractor.py new file mode 100644 index 0000000..6e6f034 --- /dev/null +++ b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/exif_extractor.py @@ -0,0 +1,261 @@ +""" +EXIF/XMP metadata extraction service. +Uses multiple extraction methods for comprehensive coverage. +""" + +from typing import Dict, Any, Optional, List +from pathlib import Path +import re + +from PIL import Image +from PIL.ExifTags import TAGS + +try: + import exifread + EXIFREAD_AVAILABLE = True +except ImportError: + EXIFREAD_AVAILABLE = False + +import structlog + +logger = structlog.get_logger() + + +def _make_serializable(value): + """Convert EXIF value to JSON-serializable type.""" + if value is None: + return None + if isinstance(value, (str, int, float, bool)): + return value + if isinstance(value, bytes): + try: + return value.decode("utf-8", errors="replace") + except Exception: + return f"" + if isinstance(value, (list, tuple)): + return [_make_serializable(v) for v in value] + if isinstance(value, dict): + return {str(k): _make_serializable(v) for k, v in value.items()} + # Handle IFDRational, Fraction, and other numeric types + try: + # Try to convert to float first (handles Rational types) + return float(value) + except (TypeError, ValueError): + pass + # Fall back to string representation + return str(value) + + +class ExifExtractor: + """Extract metadata from image files using multiple methods.""" + + def __init__(self, frame): + self.frame = frame + self.storage = frame.get_service("storage") if frame else None + + async def extract_all(self, file_path: Path) -> Dict[str, Any]: + """ + Extract all available metadata from an image file. + + Returns a unified metadata structure regardless of source format. + """ + result = { + "basic": {}, + "exif": {}, + "gps": {}, + "camera": {}, + "timestamps": {}, + "warnings": [], + "extraction_methods": [], + } + + # Method 1: Pillow (fast, basic EXIF) + pillow_data = await self._extract_pillow(file_path) + if pillow_data: + result["exif"].update(pillow_data.get("exif", {})) + result["basic"] = pillow_data.get("basic", {}) + result["extraction_methods"].append("pillow") + + # Method 2: ExifRead (comprehensive, pure Python) + if EXIFREAD_AVAILABLE: + exifread_data = await self._extract_exifread(file_path) + if exifread_data: + # Merge, preferring exifread for detailed data + for key, value in exifread_data.items(): + if key not in result["exif"] or not result["exif"][key]: + result["exif"][key] = value + result["extraction_methods"].append("exifread") + + # Extract structured data from raw EXIF + result["gps"] = self._parse_gps(result["exif"]) + result["camera"] = self._parse_camera(result["exif"]) + result["timestamps"] = self._parse_timestamps(result["exif"]) + + # Check for suspicious patterns + result["warnings"] = self._analyze_metadata_anomalies(result) + + return result + + async def _extract_pillow(self, file_path: Path) -> Optional[Dict]: + """Extract EXIF using Pillow.""" + try: + with Image.open(file_path) as img: + basic = { + "width": img.width, + "height": img.height, + "format": img.format, + "mode": img.mode, + } + + exif_data = img.getexif() + if not exif_data: + return {"basic": basic, "exif": {}} + + exif = {} + for tag_id, value in exif_data.items(): + tag_name = TAGS.get(tag_id, str(tag_id)) + # Convert to JSON-serializable type + exif[tag_name] = _make_serializable(value) + + return {"basic": basic, "exif": exif} + + except Exception as e: + logger.warning("Pillow extraction failed", error=str(e)) + return None + + async def _extract_exifread(self, file_path: Path) -> Optional[Dict]: + """Extract EXIF using ExifRead (more comprehensive).""" + try: + with open(file_path, "rb") as f: + tags = exifread.process_file(f, details=True) + + result = {} + for tag, value in tags.items(): + # Skip thumbnail data + if tag in ("JPEGThumbnail", "TIFFThumbnail", "Filename", "EXIF MakerNote"): + continue + result[tag] = str(value) + + return result + + except Exception as e: + logger.warning("ExifRead extraction failed", error=str(e)) + return None + + def _parse_gps(self, exif: Dict) -> Dict[str, Any]: + """Parse GPS data from EXIF into decimal coordinates.""" + gps = {} + + # Look for GPS tags (different naming in different extractors) + lat_tag = exif.get("GPS GPSLatitude") or exif.get("GPSLatitude") + lat_ref = exif.get("GPS GPSLatitudeRef") or exif.get("GPSLatitudeRef") + lon_tag = exif.get("GPS GPSLongitude") or exif.get("GPSLongitude") + lon_ref = exif.get("GPS GPSLongitudeRef") or exif.get("GPSLongitudeRef") + + if lat_tag and lon_tag: + try: + gps["latitude"] = self._gps_to_decimal(lat_tag, lat_ref) + gps["longitude"] = self._gps_to_decimal(lon_tag, lon_ref) + gps["raw_latitude"] = str(lat_tag) + gps["raw_longitude"] = str(lon_tag) + except Exception as e: + gps["parse_error"] = str(e) + + # Altitude + alt_tag = exif.get("GPS GPSAltitude") or exif.get("GPSAltitude") + if alt_tag: + gps["altitude"] = str(alt_tag) + + return gps + + def _gps_to_decimal(self, coord_str: str, ref: str) -> float: + """Convert GPS coordinates from DMS to decimal degrees.""" + # Parse formats like "[40, 26, 46.302]" or "40/1, 26/1, 46302/1000" + numbers = re.findall(r"[\d.]+(?:/[\d.]+)?", str(coord_str)) + + if len(numbers) >= 3: + def parse_rational(s): + if "/" in s: + num, den = s.split("/") + return float(num) / float(den) + return float(s) + + d = parse_rational(numbers[0]) + m = parse_rational(numbers[1]) + s = parse_rational(numbers[2]) + + decimal = d + (m / 60.0) + (s / 3600.0) + + if ref and str(ref).upper() in ["S", "W"]: + decimal = -decimal + + return round(decimal, 6) + + raise ValueError(f"Could not parse GPS coordinate: {coord_str}") + + def _parse_camera(self, exif: Dict) -> Dict[str, Any]: + """Extract camera/device information.""" + return { + "make": exif.get("Image Make") or exif.get("Make"), + "model": exif.get("Image Model") or exif.get("Model"), + "software": exif.get("Image Software") or exif.get("Software"), + "lens_model": exif.get("EXIF LensModel") or exif.get("LensModel"), + "focal_length": exif.get("EXIF FocalLength") or exif.get("FocalLength"), + "aperture": exif.get("EXIF FNumber") or exif.get("FNumber"), + "iso": exif.get("EXIF ISOSpeedRatings") or exif.get("ISOSpeedRatings"), + "exposure_time": exif.get("EXIF ExposureTime") or exif.get("ExposureTime"), + "flash": exif.get("EXIF Flash") or exif.get("Flash"), + "orientation": exif.get("Image Orientation") or exif.get("Orientation"), + } + + def _parse_timestamps(self, exif: Dict) -> Dict[str, Any]: + """Extract all timestamp-related fields.""" + return { + "datetime_original": exif.get("EXIF DateTimeOriginal") or exif.get("DateTimeOriginal"), + "datetime_digitized": exif.get("EXIF DateTimeDigitized") or exif.get("DateTimeDigitized"), + "datetime_modified": exif.get("Image DateTime") or exif.get("DateTime"), + "gps_timestamp": exif.get("GPS GPSTimeStamp"), + "gps_datestamp": exif.get("GPS GPSDateStamp"), + } + + def _analyze_metadata_anomalies(self, metadata: Dict) -> List[str]: + """Check for suspicious patterns in metadata.""" + warnings = [] + + # Warning: No EXIF at all + if not metadata.get("exif"): + warnings.append( + "NO_EXIF: Image has no EXIF metadata - may indicate AI generation or metadata stripping" + ) + + # Warning: Missing camera info but has other EXIF + if metadata.get("exif") and not metadata.get("camera", {}).get("make"): + warnings.append( + "NO_CAMERA: Has EXIF but missing camera make/model - may indicate editing software origin" + ) + + # Warning: Software field indicates editing + software = metadata.get("camera", {}).get("software", "") or "" + editing_keywords = ["photoshop", "gimp", "lightroom", "canva", "snapseed", "pixlr", "firefly"] + if any(kw in software.lower() for kw in editing_keywords): + warnings.append(f"EDITING_SOFTWARE: Software field indicates editing: {software}") + + # Warning: Timestamp inconsistencies + timestamps = metadata.get("timestamps", {}) + original = timestamps.get("datetime_original") + digitized = timestamps.get("datetime_digitized") + modified = timestamps.get("datetime_modified") + + if original and modified and original != modified: + warnings.append("TIMESTAMP_MISMATCH: Original and modified timestamps differ") + + if digitized and original and digitized != original: + warnings.append("DIGITIZED_MISMATCH: Digitized timestamp differs from original") + + # Warning: GPS present but no camera info + if metadata.get("gps") and not metadata.get("camera", {}).get("make"): + warnings.append( + "GPS_NO_CAMERA: Has GPS data but no camera info - may indicate metadata injection" + ) + + return warnings diff --git a/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/perceptual_hash.py b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/perceptual_hash.py new file mode 100644 index 0000000..0ae9a9d --- /dev/null +++ b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/perceptual_hash.py @@ -0,0 +1,180 @@ +""" +Perceptual hash computation for image similarity detection. +Computes pHash, dHash, and aHash. +""" + +from typing import Dict, List +from pathlib import Path +import hashlib + +from PIL import Image +import numpy as np + +import structlog + +logger = structlog.get_logger() + + +class PerceptualHashService: + """Compute perceptual hashes for images.""" + + def __init__(self, frame): + self.frame = frame + + async def compute_all_hashes(self, file_path: Path) -> Dict[str, str]: + """Compute all available hashes for an image file.""" + result = { + "sha256": await self._compute_sha256(file_path), + "md5": await self._compute_md5(file_path), + } + + # Perceptual hashes + try: + result["phash"] = await self._compute_phash(file_path) + result["ahash"] = await self._compute_ahash(file_path) + result["dhash"] = await self._compute_dhash(file_path) + except Exception as e: + logger.warning("Perceptual hash computation failed", error=str(e)) + + return result + + async def _compute_sha256(self, file_path: Path) -> str: + """Compute SHA-256 hash.""" + sha256 = hashlib.sha256() + with open(file_path, "rb") as f: + for chunk in iter(lambda: f.read(8192), b""): + sha256.update(chunk) + return sha256.hexdigest() + + async def _compute_md5(self, file_path: Path) -> str: + """Compute MD5 hash.""" + md5 = hashlib.md5() + with open(file_path, "rb") as f: + for chunk in iter(lambda: f.read(8192), b""): + md5.update(chunk) + return md5.hexdigest() + + async def _compute_phash(self, file_path: Path) -> str: + """ + Compute perceptual hash (pHash). + Resistant to minor modifications like resizing, compression. + """ + from scipy.fftpack import dct + + with Image.open(file_path) as img: + # Convert to grayscale and resize to 32x32 + img = img.convert("L").resize((32, 32), Image.Resampling.LANCZOS) + pixels = np.array(img, dtype=np.float64) + + # Compute 2D DCT + dct_result = dct(dct(pixels.T, norm="ortho").T, norm="ortho") + + # Use top-left 8x8 (excluding DC component) + dct_low = dct_result[:8, :8] + + # Compute median (excluding DC) + med = np.median(dct_low.flatten()[1:]) + + # Generate hash + diff = dct_low > med + return self._bool_array_to_hex(diff.flatten()) + + async def _compute_ahash(self, file_path: Path) -> str: + """ + Compute average hash (aHash). + Fast but less robust than pHash. + """ + with Image.open(file_path) as img: + img = img.convert("L").resize((8, 8), Image.Resampling.LANCZOS) + pixels = np.array(img) + avg = pixels.mean() + diff = pixels > avg + return self._bool_array_to_hex(diff.flatten()) + + async def _compute_dhash(self, file_path: Path) -> str: + """ + Compute difference hash (dHash). + Good for detecting subtle modifications. + """ + with Image.open(file_path) as img: + img = img.convert("L").resize((9, 8), Image.Resampling.LANCZOS) + pixels = np.array(img) + # Compare adjacent pixels + diff = pixels[:, :-1] > pixels[:, 1:] + return self._bool_array_to_hex(diff.flatten()) + + def _bool_array_to_hex(self, arr: np.ndarray) -> str: + """Convert boolean array to hex string.""" + bits = "".join(["1" if b else "0" for b in arr]) + return hex(int(bits, 2))[2:].zfill(len(arr) // 4) + + def compute_hamming_distance(self, hash1: str, hash2: str) -> int: + """ + Compute Hamming distance between two perceptual hashes. + Lower = more similar. 0 = identical. >10 typically different images. + """ + if len(hash1) != len(hash2): + raise ValueError("Hashes must be same length") + + # Convert hex to binary + bin1 = bin(int(hash1, 16))[2:].zfill(len(hash1) * 4) + bin2 = bin(int(hash2, 16))[2:].zfill(len(hash2) * 4) + + return sum(c1 != c2 for c1, c2 in zip(bin1, bin2)) + + def similarity_score(self, hash1: str, hash2: str) -> float: + """ + Compute similarity score (0.0 to 1.0) between two hashes. + 1.0 = identical, 0.0 = completely different. + """ + distance = self.compute_hamming_distance(hash1, hash2) + max_distance = len(hash1) * 4 # Each hex char = 4 bits + return 1.0 - (distance / max_distance) + + async def find_similar( + self, target_hash: str, hash_type: str = "phash", threshold: int = 10 + ) -> List[Dict]: + """ + Find images with similar hashes in the database. + + Args: + target_hash: Hash to compare against + hash_type: Type of hash (phash, dhash, ahash) + threshold: Maximum Hamming distance to consider similar + + Returns: + List of dicts with analysis_id, hash, hamming_distance, similarity_score + """ + db = self.frame.database if self.frame else None + if not db: + logger.warning("Database not available for similarity search") + return [] + + # Get all hashes from database + try: + rows = await db.fetch_all( + f"SELECT id, {hash_type} FROM arkham_media_analyses WHERE {hash_type} IS NOT NULL" + ) + except Exception as e: + logger.warning("Failed to query hashes from database", error=str(e)) + return [] + + similar = [] + for row in rows: + hash_value = row.get(hash_type) or row.get(hash_type.upper()) + if hash_value: + try: + distance = self.compute_hamming_distance(target_hash, hash_value) + if distance <= threshold: + similar.append({ + "analysis_id": row.get("id"), + "hash": hash_value, + "hamming_distance": distance, + "similarity_score": self.similarity_score(target_hash, hash_value), + }) + except Exception: + continue + + # Sort by distance (most similar first) + similar.sort(key=lambda x: x["hamming_distance"]) + return similar diff --git a/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/sun_position.py b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/sun_position.py new file mode 100644 index 0000000..a95b4ee --- /dev/null +++ b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/services/sun_position.py @@ -0,0 +1,195 @@ +""" +Sun position verification service. +Calculates expected shadow direction based on GPS and timestamp. +""" + +from typing import Dict, Any +from datetime import datetime, timezone +import math + +import structlog + +logger = structlog.get_logger() + +try: + from pysolar.solar import get_altitude, get_azimuth + PYSOLAR_AVAILABLE = True +except ImportError: + PYSOLAR_AVAILABLE = False + logger.info("pysolar not installed, sun position verification disabled") + + +class SunPositionService: + """ + Verify shadow consistency using sun position calculations. + + Uses GPS coordinates and timestamp from EXIF to calculate + where the sun would have been, and therefore where shadows + should point. + """ + + def __init__(self, frame): + self.frame = frame + + def is_available(self) -> bool: + """Check if sun position calculation is available.""" + return PYSOLAR_AVAILABLE + + async def calculate_sun_position( + self, + latitude: float, + longitude: float, + dt: datetime, + ) -> Dict[str, Any]: + """ + Calculate sun position for given coordinates and time. + + Args: + latitude: Decimal degrees (positive = North) + longitude: Decimal degrees (positive = East) + dt: Timezone-aware datetime + + Returns: + Dict with sun altitude, azimuth, and shadow direction + """ + if not PYSOLAR_AVAILABLE: + return {"error": "pysolar not installed", "success": False} + + # Ensure datetime is timezone-aware + if dt.tzinfo is None: + dt = dt.replace(tzinfo=timezone.utc) + + try: + # Get sun position + altitude = get_altitude(latitude, longitude, dt) + azimuth = get_azimuth(latitude, longitude, dt) + + # Shadow direction is opposite to sun azimuth + shadow_direction = (azimuth + 180) % 360 + + # Calculate expected shadow length ratio (for vertical objects) + # shadow_length = object_height / tan(altitude) + if altitude > 0: + shadow_length_ratio = 1.0 / math.tan(math.radians(altitude)) + else: + shadow_length_ratio = float("inf") # Sun below horizon + + return { + "success": True, + "latitude": latitude, + "longitude": longitude, + "datetime": dt.isoformat(), + "sun_altitude": round(altitude, 2), + "sun_azimuth": round(azimuth, 2), + "expected_shadow_direction": round(shadow_direction, 2), + "shadow_length_ratio": round(shadow_length_ratio, 2) if shadow_length_ratio != float("inf") else None, + "sun_above_horizon": altitude > 0, + "interpretation": self._interpret_position(altitude, azimuth), + } + + except Exception as e: + logger.error("Sun position calculation failed", error=str(e)) + return {"success": False, "error": str(e)} + + def _interpret_position(self, altitude: float, azimuth: float) -> str: + """Generate human-readable interpretation of sun position.""" + if altitude < 0: + return "Sun is below the horizon - no direct sunlight possible" + + # Time of day approximation + if altitude < 10: + time_desc = "very low (sunrise/sunset)" + elif altitude < 30: + time_desc = "low (morning/evening)" + elif altitude < 60: + time_desc = "moderate (mid-morning/afternoon)" + else: + time_desc = "high (midday)" + + # Direction + if 337.5 <= azimuth or azimuth < 22.5: + direction = "North" + elif 22.5 <= azimuth < 67.5: + direction = "Northeast" + elif 67.5 <= azimuth < 112.5: + direction = "East" + elif 112.5 <= azimuth < 157.5: + direction = "Southeast" + elif 157.5 <= azimuth < 202.5: + direction = "South" + elif 202.5 <= azimuth < 247.5: + direction = "Southwest" + elif 247.5 <= azimuth < 292.5: + direction = "West" + else: + direction = "Northwest" + + return f"Sun is {time_desc}, positioned to the {direction}. Shadows should point roughly opposite." + + async def verify_from_exif( + self, + gps_data: Dict[str, Any], + timestamp_data: Dict[str, Any], + ) -> Dict[str, Any]: + """ + Calculate sun position from EXIF GPS and timestamp data. + + Args: + gps_data: GPS data from EXIF (latitude, longitude) + timestamp_data: Timestamp data from EXIF (datetime_original, etc.) + + Returns: + Sun position calculation or error + """ + # Get coordinates + latitude = gps_data.get("latitude") + longitude = gps_data.get("longitude") + + if latitude is None or longitude is None: + return { + "success": False, + "error": "GPS coordinates not available in EXIF", + } + + # Get timestamp + dt_str = ( + timestamp_data.get("datetime_original") + or timestamp_data.get("datetime_digitized") + or timestamp_data.get("datetime_modified") + ) + + if not dt_str: + return { + "success": False, + "error": "No timestamp available in EXIF", + } + + # Parse timestamp (EXIF format is typically "YYYY:MM:DD HH:MM:SS") + try: + dt_str = dt_str.replace(":", "-", 2) # Fix date separators + dt = datetime.fromisoformat(dt_str) + + # Check for GPS timestamp for more precision + gps_time = timestamp_data.get("gps_timestamp") + gps_date = timestamp_data.get("gps_datestamp") + if gps_time and gps_date: + # GPS timestamps are always UTC + dt = dt.replace(tzinfo=timezone.utc) + else: + # Assume local time, but note uncertainty + dt = dt.replace(tzinfo=timezone.utc) # Treat as UTC for calculation + + except Exception as e: + return { + "success": False, + "error": f"Could not parse timestamp: {dt_str} ({e})", + } + + result = await self.calculate_sun_position(latitude, longitude, dt) + result["timestamp_source"] = "exif" + result["timezone_note"] = ( + "Timezone assumed UTC if not specified in EXIF. " + "Actual local time may differ, affecting accuracy." + ) + + return result diff --git a/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/shard.py b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/shard.py new file mode 100644 index 0000000..12535d9 --- /dev/null +++ b/packages/arkham-shard-media-forensics/arkham_shard_media_forensics/shard.py @@ -0,0 +1,1913 @@ +"""Media Forensics Shard - Media metadata extraction and forensic analysis.""" + +import base64 +import json +import logging +import os +import uuid +from datetime import datetime +from pathlib import Path +from typing import Any, Dict, List, Optional +from urllib.parse import quote, urlencode + +from arkham_frame.shard_interface import ArkhamShard + + +def _make_json_safe(obj: Any) -> Any: + """Recursively convert an object to be JSON-serializable.""" + if obj is None: + return None + if isinstance(obj, (str, int, float, bool)): + return obj + if isinstance(obj, bytes): + try: + return obj.decode("utf-8", errors="replace") + except Exception: + return f"" + if isinstance(obj, (list, tuple)): + return [_make_json_safe(v) for v in obj] + if isinstance(obj, dict): + return {str(k): _make_json_safe(v) for k, v in obj.items()} + # Handle numpy types + try: + import numpy as np + if isinstance(obj, np.bool_): + return bool(obj) + if isinstance(obj, np.integer): + return int(obj) + if isinstance(obj, np.floating): + return float(obj) + if isinstance(obj, np.ndarray): + return obj.tolist() + except ImportError: + pass + # Handle IFDRational, Fraction, and other numeric types + try: + return float(obj) + except (TypeError, ValueError): + pass + # Fall back to string representation + return str(obj) + +from .api import init_api, router +from .models import ( + AnalysisStats, + ELAResult, + IntegrityStatus, + MediaAnalysis, + SimilarImage, + SunVerification, +) + +logger = logging.getLogger(__name__) + + +class MediaForensicsShard(ArkhamShard): + """ + Media Forensics shard for ArkhamFrame. + + Provides media metadata extraction and forensic analysis for images: + - EXIF/XMP metadata extraction + - Perceptual hashing (pHash, dHash, aHash) for image similarity + - C2PA Content Credentials parsing and verification + - Error Level Analysis (ELA) visualization + - Sun position verification for shadow analysis + + All features work fully offline (air-gap compatible). + """ + + name = "media-forensics" + version = "0.1.0" + description = "Media metadata extraction and forensic analysis" + + def __init__(self): + super().__init__() + self._frame = None + self._db = None + self._event_bus = None + self._storage = None + + # Services (lazy-loaded) + self.exif_extractor = None + self.hash_service = None + self.c2pa_parser = None + self.ela_analyzer = None + self.sun_position = None + + async def initialize(self, frame) -> None: + """ + Initialize the shard with Frame services. + + Args: + frame: The ArkhamFrame instance + """ + self._frame = frame + self._db = frame.database + self._event_bus = frame.get_service("events") + self._storage = frame.get_service("storage") + + logger.info("Initializing Media Forensics Shard...") + + # Create database schema + await self._create_schema() + + # Initialize services + self._init_services() + + # Initialize API with shard reference + init_api(self) + + # Subscribe to events + if self._event_bus: + await self._event_bus.subscribe("document.ingested", self._handle_document_ingested) + logger.info("Subscribed to document.ingested event") + + # Register on app state for API access + if hasattr(frame, "app") and frame.app: + frame.app.state.media_forensics_shard = self + logger.debug("Media Forensics Shard registered on app.state") + + logger.info("Media Forensics Shard initialized") + + def _init_services(self) -> None: + """Initialize service classes (lazy import to avoid import errors).""" + try: + from .services.exif_extractor import ExifExtractor + self.exif_extractor = ExifExtractor(self._frame) + logger.debug("ExifExtractor initialized") + except ImportError as e: + logger.warning(f"ExifExtractor not available: {e}") + + try: + from .services.perceptual_hash import PerceptualHashService + self.hash_service = PerceptualHashService(self._frame) + logger.debug("PerceptualHashService initialized") + except ImportError as e: + logger.warning(f"PerceptualHashService not available: {e}") + + try: + from .services.c2pa_parser import C2PAParser + self.c2pa_parser = C2PAParser(self._frame) + logger.debug("C2PAParser initialized") + except ImportError as e: + logger.warning(f"C2PAParser not available: {e}") + + try: + from .services.ela_analyzer import ELAAnalyzer + self.ela_analyzer = ELAAnalyzer(self._frame) + logger.debug("ELAAnalyzer initialized") + except ImportError as e: + logger.warning(f"ELAAnalyzer not available: {e}") + + try: + from .services.sun_position import SunPositionService + self.sun_position = SunPositionService(self._frame) + logger.debug("SunPositionService initialized") + except ImportError as e: + logger.warning(f"SunPositionService not available: {e}") + + async def shutdown(self) -> None: + """Clean up shard resources.""" + logger.info("Shutting down Media Forensics Shard...") + + # Unsubscribe from events + if self._event_bus: + self._event_bus.unsubscribe("document.ingested") + + # Clear services + self.exif_extractor = None + self.hash_service = None + self.c2pa_parser = None + self.ela_analyzer = None + self.sun_position = None + + logger.info("Media Forensics Shard shutdown complete") + + def get_routes(self): + """Return FastAPI router for this shard.""" + return router + + # =========================================== + # Database Schema + # =========================================== + + async def _create_schema(self) -> None: + """Create database tables for media forensics.""" + if not self._db: + logger.warning("Database not available - persistence disabled") + return + + try: + # Main analyses table + await self._db.execute(""" + CREATE TABLE IF NOT EXISTS arkham_media_analyses ( + id TEXT PRIMARY KEY, + document_id TEXT, -- Nullable for direct file analysis + tenant_id UUID, + + -- File info + filename TEXT, -- Original filename + file_path TEXT, -- Full path to file for ELA and other operations + file_type TEXT, + file_size INTEGER, + width INTEGER, + height INTEGER, + + -- Cryptographic hashes + sha256 TEXT, + md5 TEXT, + + -- Perceptual hashes + phash TEXT, + dhash TEXT, + ahash TEXT, + + -- EXIF data (JSON) + exif_data TEXT DEFAULT '{}', + + -- Camera info (extracted from EXIF) + camera_make TEXT, + camera_model TEXT, + software TEXT, + + -- Timestamps (extracted from EXIF) + datetime_original TEXT, + datetime_digitized TEXT, + datetime_modified TEXT, + + -- GPS (extracted from EXIF) + gps_latitude REAL, + gps_longitude REAL, + gps_altitude REAL, + + -- C2PA data (JSON) + c2pa_data TEXT DEFAULT '{}', + has_c2pa INTEGER DEFAULT 0, + c2pa_signer TEXT, + c2pa_timestamp TEXT, + + -- Analysis results + warnings TEXT DEFAULT '[]', + anomalies TEXT DEFAULT '[]', + integrity_status TEXT DEFAULT 'unknown', + confidence_score REAL DEFAULT 0.0, + + -- Timestamps + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + + # Add file_path column if not exists (for existing databases) + try: + await self._db.execute(""" + ALTER TABLE arkham_media_analyses ADD COLUMN IF NOT EXISTS file_path TEXT + """) + except Exception: + pass # Column might already exist or not supported + + # Similar images table (for perceptual hash matches) + await self._db.execute(""" + CREATE TABLE IF NOT EXISTS arkham_media_similar ( + id TEXT PRIMARY KEY, + source_analysis_id TEXT NOT NULL, + target_analysis_id TEXT NOT NULL, + + hash_type TEXT NOT NULL, + hamming_distance INTEGER NOT NULL, + similarity_score REAL NOT NULL, + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + FOREIGN KEY (source_analysis_id) REFERENCES arkham_media_analyses(id) ON DELETE CASCADE, + FOREIGN KEY (target_analysis_id) REFERENCES arkham_media_analyses(id) ON DELETE CASCADE, + UNIQUE(source_analysis_id, target_analysis_id, hash_type) + ) + """) + + # ELA results table (generated on demand) + await self._db.execute(""" + CREATE TABLE IF NOT EXISTS arkham_media_ela ( + id TEXT PRIMARY KEY, + analysis_id TEXT NOT NULL, + + quality INTEGER DEFAULT 95, + ela_image_path TEXT, + + -- Analysis of ELA result + uniform_regions TEXT DEFAULT '[]', + anomalous_regions TEXT DEFAULT '[]', + interpretation TEXT, + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + FOREIGN KEY (analysis_id) REFERENCES arkham_media_analyses(id) ON DELETE CASCADE + ) + """) + + # Sun position verification table + await self._db.execute(""" + CREATE TABLE IF NOT EXISTS arkham_media_sun_verification ( + id TEXT PRIMARY KEY, + analysis_id TEXT NOT NULL, + + -- Input parameters + claimed_datetime TEXT, + latitude REAL, + longitude REAL, + + -- Calculated sun position + sun_altitude REAL, + sun_azimuth REAL, + expected_shadow_direction REAL, + + -- Verification result + verification_status TEXT, + notes TEXT, + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + FOREIGN KEY (analysis_id) REFERENCES arkham_media_analyses(id) ON DELETE CASCADE + ) + """) + + # Create indexes + await self._db.execute( + "CREATE INDEX IF NOT EXISTS idx_media_analyses_document ON arkham_media_analyses(document_id)" + ) + await self._db.execute( + "CREATE INDEX IF NOT EXISTS idx_media_analyses_tenant ON arkham_media_analyses(tenant_id)" + ) + await self._db.execute( + "CREATE INDEX IF NOT EXISTS idx_media_analyses_phash ON arkham_media_analyses(phash)" + ) + await self._db.execute( + "CREATE INDEX IF NOT EXISTS idx_media_analyses_integrity ON arkham_media_analyses(integrity_status)" + ) + await self._db.execute( + "CREATE INDEX IF NOT EXISTS idx_media_similar_source ON arkham_media_similar(source_analysis_id)" + ) + await self._db.execute( + "CREATE INDEX IF NOT EXISTS idx_media_ela_analysis ON arkham_media_ela(analysis_id)" + ) + await self._db.execute( + "CREATE INDEX IF NOT EXISTS idx_media_sun_analysis ON arkham_media_sun_verification(analysis_id)" + ) + + # Migration: add tenant_id column if missing + await self._db.execute(""" + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'arkham_media_analyses' + AND column_name = 'tenant_id' + ) THEN + ALTER TABLE arkham_media_analyses ADD COLUMN tenant_id UUID; + END IF; + END $$; + """) + + # Migration: make document_id nullable for direct file analysis + try: + await self._db.execute(""" + ALTER TABLE arkham_media_analyses + ALTER COLUMN document_id DROP NOT NULL; + """) + except Exception: + pass # Column may already be nullable + + # Migration: add filename column if it doesn't exist + try: + await self._db.execute(""" + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 FROM information_schema.columns + WHERE table_name = 'arkham_media_analyses' + AND column_name = 'filename' + ) THEN + ALTER TABLE arkham_media_analyses ADD COLUMN filename TEXT; + END IF; + END $$; + """) + except Exception: + pass # Column may already exist + + logger.info("Media forensics schema created successfully") + + except Exception as e: + logger.error(f"Failed to create schema: {e}") + + # =========================================== + # Event Handlers + # =========================================== + + async def _handle_document_ingested(self, event_data: Dict) -> None: + """Auto-analyze image documents when ingested.""" + doc_id = event_data.get("document_id") + doc_type = event_data.get("doc_type", "").lower() + + # Only process image types + image_types = ["image/jpeg", "image/png", "image/tiff", "image/webp", "image/heic", "image/gif"] + if any(t in doc_type for t in image_types): + try: + await self.analyze_document(doc_id) + logger.info(f"Auto-analyzed document {doc_id}") + except Exception as e: + logger.warning(f"Auto-analysis failed for {doc_id}: {e}") + + # =========================================== + # Public API Methods + # =========================================== + + async def analyze_document(self, document_id: str) -> Dict[str, Any]: + """ + Perform full media analysis on a document. + + Extracts EXIF, computes hashes, parses C2PA. + + Args: + document_id: Document ID to analyze + + Returns: + Analysis results dictionary + + Raises: + FileNotFoundError: If document or file not found + ValueError: If document is not an image + """ + if not self._db: + raise RuntimeError("Database service not available") + + # Get document from frame + doc = await self._db.fetch_one( + "SELECT id, path, doc_type FROM arkham_frame.documents WHERE id = :id", + {"id": document_id}, + ) + + if not doc: + raise FileNotFoundError(f"Document {document_id} not found") + + file_path = Path(doc["path"]) + if not file_path.exists(): + raise FileNotFoundError(f"File not found: {file_path}") + + # Create analysis record + analysis_id = str(uuid.uuid4()) + tenant_id = self.get_tenant_id_or_none() + + # Extract metadata + exif_data = {} + if self.exif_extractor: + exif_data = await self.exif_extractor.extract_all(file_path) + + # Compute hashes + hashes = {} + if self.hash_service: + hashes = await self.hash_service.compute_all_hashes(file_path) + + # Parse C2PA + c2pa_data = {} + c2pa_interpretation = {} + if self.c2pa_parser: + c2pa_data = await self.c2pa_parser.parse(file_path) or {} + c2pa_interpretation = self.c2pa_parser.interpret_c2pa(c2pa_data) + + # Compile warnings + warnings = exif_data.get("warnings", []) + if c2pa_interpretation.get("is_ai_generated"): + warnings.append(f"AI_GENERATED: C2PA indicates AI generation by {c2pa_data.get('signer')}") + + # Determine integrity status + if c2pa_data and c2pa_data.get("has_c2pa") and c2pa_data.get("signature_valid"): + integrity_status = "verified" + elif warnings: + integrity_status = "flagged" + else: + integrity_status = "unverified" + + # Calculate confidence score + confidence_score = 0.8 if c2pa_data and c2pa_data.get("has_c2pa") else 0.5 + + # Save to database + await self._db.execute( + """ + INSERT INTO arkham_media_analyses ( + id, document_id, tenant_id, filename, file_type, + width, height, + sha256, md5, phash, dhash, ahash, + exif_data, + camera_make, camera_model, software, + datetime_original, datetime_digitized, datetime_modified, + gps_latitude, gps_longitude, gps_altitude, + c2pa_data, has_c2pa, c2pa_signer, c2pa_timestamp, + warnings, integrity_status, confidence_score, + created_at, updated_at + ) VALUES ( + :id, :document_id, :tenant_id, :filename, :file_type, + :width, :height, + :sha256, :md5, :phash, :dhash, :ahash, + :exif_data, + :camera_make, :camera_model, :software, + :datetime_original, :datetime_digitized, :datetime_modified, + :gps_latitude, :gps_longitude, :gps_altitude, + :c2pa_data, :has_c2pa, :c2pa_signer, :c2pa_timestamp, + :warnings, :integrity_status, :confidence_score, + :created_at, :updated_at + ) + ON CONFLICT (id) DO UPDATE SET + exif_data = EXCLUDED.exif_data, + warnings = EXCLUDED.warnings, + integrity_status = EXCLUDED.integrity_status, + updated_at = NOW() + """, + { + "id": analysis_id, + "document_id": document_id, + "tenant_id": str(tenant_id) if tenant_id else None, + "filename": file_path.name, + "file_type": doc["doc_type"], + "width": exif_data.get("basic", {}).get("width"), + "height": exif_data.get("basic", {}).get("height"), + "sha256": hashes.get("sha256"), + "md5": hashes.get("md5"), + "phash": hashes.get("phash"), + "dhash": hashes.get("dhash"), + "ahash": hashes.get("ahash"), + "exif_data": json.dumps(_make_json_safe(exif_data.get("exif", {}))), + "camera_make": exif_data.get("camera", {}).get("make"), + "camera_model": exif_data.get("camera", {}).get("model"), + "software": exif_data.get("camera", {}).get("software"), + "datetime_original": exif_data.get("timestamps", {}).get("datetime_original"), + "datetime_digitized": exif_data.get("timestamps", {}).get("datetime_digitized"), + "datetime_modified": exif_data.get("timestamps", {}).get("datetime_modified"), + "gps_latitude": exif_data.get("gps", {}).get("latitude"), + "gps_longitude": exif_data.get("gps", {}).get("longitude"), + "gps_altitude": exif_data.get("gps", {}).get("altitude"), + "c2pa_data": json.dumps(_make_json_safe(c2pa_data)) if c2pa_data else "{}", + "has_c2pa": 1 if c2pa_data and c2pa_data.get("has_c2pa") else 0, + "c2pa_signer": c2pa_data.get("signer") if c2pa_data else None, + "c2pa_timestamp": c2pa_data.get("timestamp") if c2pa_data else None, + "warnings": json.dumps(warnings), + "integrity_status": integrity_status, + "confidence_score": confidence_score, + "created_at": datetime.utcnow().isoformat(), + "updated_at": datetime.utcnow().isoformat(), + }, + ) + + # Emit event + if self._event_bus: + await self._event_bus.emit( + "media.metadata.extracted", + { + "document_id": document_id, + "analysis_id": analysis_id, + "has_exif": bool(exif_data.get("exif")), + "has_gps": bool(exif_data.get("gps", {}).get("latitude")), + "has_c2pa": bool(c2pa_data and c2pa_data.get("has_c2pa")), + "warnings": warnings, + "integrity_status": integrity_status, + }, + source="media-forensics", + ) + + # Find similar images + if self.hash_service and hashes.get("phash"): + similar = await self.hash_service.find_similar( + hashes["phash"], hash_type="phash", threshold=10 + ) + # Filter out self + similar = [s for s in similar if s["analysis_id"] != analysis_id] + + if similar and self._event_bus: + await self._event_bus.emit( + "media.similar.found", + { + "source_analysis_id": analysis_id, + "similar_count": len(similar), + "matches": [s["analysis_id"] for s in similar[:5]], + }, + source="media-forensics", + ) + + return { + "analysis_id": analysis_id, + "document_id": document_id, + "exif": exif_data, + "hashes": hashes, + "c2pa": c2pa_data, + "c2pa_interpretation": c2pa_interpretation, + "warnings": warnings, + "integrity_status": integrity_status, + } + + async def analyze_file( + self, + file_path: Path, + filename: str, + run_ela: bool = False, + document_id: Optional[str] = None, + ) -> Dict[str, Any]: + """ + Analyze an image file directly without requiring a document record. + + This is the core analysis method that works on raw files. + + Args: + file_path: Path to the image file + filename: Original filename for display + run_ela: Whether to also run ELA analysis + document_id: Optional document ID to link to + + Returns: + Analysis results dictionary + """ + if not file_path.exists(): + raise FileNotFoundError(f"File not found: {file_path}") + + # Create analysis record + analysis_id = str(uuid.uuid4()) + tenant_id = self.get_tenant_id_or_none() + + # Get file info + file_size = file_path.stat().st_size + file_type = file_path.suffix.lower().lstrip(".") + + # Extract EXIF metadata + exif_data = {} + if self.exif_extractor: + try: + exif_data = await self.exif_extractor.extract_all(file_path) + logger.info(f"EXIF extraction complete: {len(exif_data)} keys") + except Exception as e: + logger.warning(f"EXIF extraction failed: {e}") + exif_data = {"error": str(e), "warnings": [f"EXIF extraction failed: {e}"]} + + # Compute perceptual hashes + hashes = {} + if self.hash_service: + try: + hashes = await self.hash_service.compute_all_hashes(file_path) + logger.info(f"Hash computation complete: {list(hashes.keys())}") + except Exception as e: + logger.warning(f"Hash computation failed: {e}") + hashes = {"error": str(e)} + + # Parse C2PA content credentials + c2pa_data = {} + c2pa_interpretation = {} + if self.c2pa_parser: + try: + c2pa_data = await self.c2pa_parser.parse(file_path) or {} + c2pa_interpretation = self.c2pa_parser.interpret_c2pa(c2pa_data) + logger.info(f"C2PA parsing complete: has_c2pa={c2pa_data.get('has_c2pa')}") + except Exception as e: + logger.warning(f"C2PA parsing failed: {e}") + c2pa_data = {"error": str(e)} + + # Compile warnings from all sources + warnings = list(exif_data.get("warnings", [])) + if c2pa_interpretation.get("is_ai_generated"): + warnings.append(f"AI_GENERATED: C2PA indicates AI generation by {c2pa_data.get('signer')}") + + # Determine integrity status + if c2pa_data and c2pa_data.get("has_c2pa") and c2pa_data.get("signature_valid"): + integrity_status = "verified" + elif warnings: + integrity_status = "flagged" + else: + integrity_status = "unverified" + + # Calculate confidence score + confidence_score = 0.8 if c2pa_data and c2pa_data.get("has_c2pa") else 0.5 + + # ELA analysis if requested + ela_result = None + if run_ela and self.ela_analyzer: + try: + ela_result = await self.ela_analyzer.analyze(file_path) + logger.info("ELA analysis complete") + except Exception as e: + logger.warning(f"ELA analysis failed: {e}") + ela_result = {"error": str(e)} + + # Save to database if available + if self._db: + try: + await self._db.execute( + """ + INSERT INTO arkham_media_analyses ( + id, document_id, tenant_id, filename, file_path, file_type, file_size, + width, height, + sha256, md5, phash, dhash, ahash, + exif_data, + camera_make, camera_model, software, + datetime_original, datetime_digitized, datetime_modified, + gps_latitude, gps_longitude, gps_altitude, + c2pa_data, has_c2pa, c2pa_signer, c2pa_timestamp, + warnings, integrity_status, confidence_score, + created_at, updated_at + ) VALUES ( + :id, :document_id, :tenant_id, :filename, :file_path, :file_type, :file_size, + :width, :height, + :sha256, :md5, :phash, :dhash, :ahash, + :exif_data, + :camera_make, :camera_model, :software, + :datetime_original, :datetime_digitized, :datetime_modified, + :gps_latitude, :gps_longitude, :gps_altitude, + :c2pa_data, :has_c2pa, :c2pa_signer, :c2pa_timestamp, + :warnings, :integrity_status, :confidence_score, + :created_at, :updated_at + ) + """, + { + "id": analysis_id, + "document_id": document_id, + "tenant_id": str(tenant_id) if tenant_id else None, + "filename": file_path.name, + "file_path": str(file_path.absolute()), + "file_type": file_type, + "file_size": file_size, + "width": exif_data.get("basic", {}).get("width"), + "height": exif_data.get("basic", {}).get("height"), + "sha256": hashes.get("sha256"), + "md5": hashes.get("md5"), + "phash": hashes.get("phash"), + "dhash": hashes.get("dhash"), + "ahash": hashes.get("ahash"), + "exif_data": json.dumps(_make_json_safe(exif_data.get("exif", {}))), + "camera_make": exif_data.get("camera", {}).get("make"), + "camera_model": exif_data.get("camera", {}).get("model"), + "software": exif_data.get("camera", {}).get("software"), + "datetime_original": exif_data.get("timestamps", {}).get("datetime_original"), + "datetime_digitized": exif_data.get("timestamps", {}).get("datetime_digitized"), + "datetime_modified": exif_data.get("timestamps", {}).get("datetime_modified"), + "gps_latitude": exif_data.get("gps", {}).get("latitude"), + "gps_longitude": exif_data.get("gps", {}).get("longitude"), + "gps_altitude": exif_data.get("gps", {}).get("altitude"), + "c2pa_data": json.dumps(_make_json_safe(c2pa_data)) if c2pa_data else "{}", + "has_c2pa": 1 if c2pa_data and c2pa_data.get("has_c2pa") else 0, + "c2pa_signer": c2pa_data.get("signer") if c2pa_data else None, + "c2pa_timestamp": c2pa_data.get("timestamp") if c2pa_data else None, + "warnings": json.dumps(warnings), + "integrity_status": integrity_status, + "confidence_score": confidence_score, + "created_at": datetime.utcnow().isoformat(), + "updated_at": datetime.utcnow().isoformat(), + }, + ) + logger.info(f"Saved analysis {analysis_id} to database") + except Exception as e: + logger.error(f"Failed to save analysis to database: {e}") + + # Emit event + if self._event_bus: + try: + await self._event_bus.emit( + "media.file.analyzed", + { + "analysis_id": analysis_id, + "filename": filename, + "file_type": file_type, + "has_exif": bool(exif_data.get("exif")), + "has_gps": bool(exif_data.get("gps", {}).get("latitude")), + "has_c2pa": bool(c2pa_data and c2pa_data.get("has_c2pa")), + "warnings_count": len(warnings), + "integrity_status": integrity_status, + }, + source="media-forensics", + ) + except Exception as e: + logger.warning(f"Failed to emit event: {e}") + + # Build result + result = { + "analysis_id": analysis_id, + "filename": filename, + "file_type": file_type, + "file_size": file_size, + "exif": exif_data, + "hashes": hashes, + "c2pa": c2pa_data, + "c2pa_interpretation": c2pa_interpretation, + "warnings": warnings, + "integrity_status": integrity_status, + "confidence_score": confidence_score, + } + + if ela_result: + result["ela"] = ela_result + + return result + + async def get_analysis(self, analysis_id: str) -> Optional[Dict[str, Any]]: + """Get a specific analysis by ID.""" + if not self._db: + return None + + row = await self._db.fetch_one( + "SELECT * FROM arkham_media_analyses WHERE id = :id", + {"id": analysis_id} + ) + + if not row: + return None + + return self._row_to_analysis_dict(row) + + async def get_analysis_by_document(self, document_id: str) -> Optional[Dict[str, Any]]: + """Get analysis for a specific document.""" + if not self._db: + return None + + row = await self._db.fetch_one( + "SELECT * FROM arkham_media_analyses WHERE document_id = :document_id ORDER BY created_at DESC LIMIT 1", + {"document_id": document_id} + ) + + if not row: + return None + + return self._row_to_analysis_dict(row) + + async def list_analyses( + self, + limit: int = 50, + offset: int = 0, + status: Optional[str] = None, + verification_status: Optional[str] = None, + has_c2pa: Optional[bool] = None, + has_warnings: Optional[bool] = None, + has_findings: Optional[bool] = None, + integrity_status: Optional[str] = None, + doc_id: Optional[str] = None, + ) -> List[Dict[str, Any]]: + """List media analyses with optional filtering.""" + if not self._db: + return [] + + # Build query with filters + query = "SELECT * FROM arkham_media_analyses WHERE 1=1" + params: Dict[str, Any] = {"limit": limit, "offset": offset} + + # Add tenant filtering + tenant_id = self.get_tenant_id_or_none() + if tenant_id: + query += " AND tenant_id = :tenant_id" + params["tenant_id"] = str(tenant_id) + + # Status filters (map to integrity_status for now as we don't have a separate status column) + if status: + # Status is mapped to integrity_status for compatibility + pass # Status not yet implemented as a separate column + + if verification_status: + query += " AND integrity_status = :verification_status" + params["verification_status"] = verification_status + + if has_c2pa is not None: + query += " AND has_c2pa = :has_c2pa" + params["has_c2pa"] = 1 if has_c2pa else 0 + + if has_warnings is not None: + if has_warnings: + query += " AND warnings != '[]'" + else: + query += " AND warnings = '[]'" + + if has_findings is not None: + # has_findings maps to has_warnings for now + if has_findings: + query += " AND warnings != '[]'" + else: + query += " AND warnings = '[]'" + + if integrity_status: + query += " AND integrity_status = :integrity_status" + params["integrity_status"] = integrity_status + + if doc_id: + query += " AND document_id = :doc_id" + params["doc_id"] = doc_id + + query += " ORDER BY created_at DESC LIMIT :limit OFFSET :offset" + + rows = await self._db.fetch_all(query, params) + return [self._row_to_analysis_dict(row) for row in rows] + + async def get_analysis_count(self) -> int: + """Get total analysis count.""" + if not self._db: + return 0 + + query = "SELECT COUNT(*) as count FROM arkham_media_analyses WHERE 1=1" + params: Dict[str, Any] = {} + + tenant_id = self.get_tenant_id_or_none() + if tenant_id: + query += " AND tenant_id = :tenant_id" + params["tenant_id"] = str(tenant_id) + + row = await self._db.fetch_one(query, params) + return row["count"] if row else 0 + + async def generate_ela( + self, + analysis_id: str, + quality: int = 95, + scale: int = 15, + ) -> Dict[str, Any]: + """ + Generate Error Level Analysis for an analysis. + + Args: + analysis_id: Analysis ID + quality: JPEG quality for resave (90-95 recommended) + scale: Multiplier for error visualization (10-20 recommended) + + Returns: + ELA result with base64 image and interpretation + """ + if not self.ela_analyzer: + return {"success": False, "error": "ELA analyzer not available"} + + analysis = await self.get_analysis(analysis_id) + if not analysis: + return {"success": False, "error": "Analysis not found"} + + # Get file path - first try from analysis record, then from documents table + file_path = None + + # Check if file_path is stored directly on the analysis + if analysis.get("file_path"): + file_path = Path(analysis["file_path"]) + + # Fall back to documents table if we have a document_id + if (not file_path or not file_path.exists()) and analysis.get("document_id"): + doc = await self._db.fetch_one( + "SELECT path FROM arkham_frame.documents WHERE id = :id", + {"id": analysis["document_id"]} + ) + if doc and doc.get("path"): + file_path = Path(doc["path"]) + + if not file_path: + return {"success": False, "error": "File path not found in analysis or documents"} + + if not file_path.exists(): + return {"success": False, "error": f"File not found: {file_path}"} + + # Generate ELA + result = await self.ela_analyzer.analyze(file_path, quality=quality, scale=scale) + + # Save to database if successful + if result.get("success") and self._db: + ela_id = str(uuid.uuid4()) + await self._db.execute( + """ + INSERT INTO arkham_media_ela (id, analysis_id, quality, interpretation, created_at) + VALUES (:id, :analysis_id, :quality, :interpretation, :created_at) + """, + { + "id": ela_id, + "analysis_id": analysis_id, + "quality": quality, + "interpretation": json.dumps(result.get("interpretation", {})), + "created_at": datetime.utcnow().isoformat(), + } + ) + + # Emit event + if self._event_bus: + await self._event_bus.emit( + "media.ela.generated", + { + "analysis_id": analysis_id, + "ela_id": ela_id, + "quality": quality, + }, + source="media-forensics", + ) + + return result + + async def find_similar_images( + self, + analysis_id: str, + hash_type: str = "phash", + threshold: int = 10, + ) -> List[Dict[str, Any]]: + """ + Find images similar to a given analysis using perceptual hashing. + + Args: + analysis_id: Source analysis ID + hash_type: Type of hash to use (phash, dhash, ahash) + threshold: Maximum Hamming distance + + Returns: + List of similar images + """ + if not self.hash_service: + return [] + + analysis = await self.get_analysis(analysis_id) + if not analysis: + return [] + + target_hash = analysis.get(hash_type) + if not target_hash: + return [] + + similar = await self.hash_service.find_similar(target_hash, hash_type=hash_type, threshold=threshold) + + # Filter out self + similar = [s for s in similar if s["analysis_id"] != analysis_id] + + return similar + + async def reverse_image_search( + self, analysis_id: str, base_url: Optional[str] = None + ) -> Dict[str, Any]: + """ + Perform reverse image search using external web APIs or generate search URLs. + + This requires network access for API-based searches. + Always generates clickable search URLs for manual searching. + + Args: + analysis_id: Source analysis ID + base_url: Base URL for constructing image URL (e.g., http://localhost:8100) + + Returns: + Dict containing search URLs and any API-based results + """ + analysis = await self.get_analysis(analysis_id) + if not analysis: + return {"search_urls": [], "api_results": [], "error": "Analysis not found"} + + file_path = analysis.get("file_path") + if not file_path: + return {"search_urls": [], "api_results": [], "error": "No file path in analysis"} + + # Check if file exists + if not Path(file_path).exists(): + return {"search_urls": [], "api_results": [], "error": "Image file not found"} + + # Generate search URLs for manual reverse image search + search_urls = await self._generate_search_urls(analysis_id, file_path, base_url) + + # Try API-based searches if configured + api_results = [] + + # Check if we're in offline mode + offline_mode = False + if self._frame and hasattr(self._frame, "config"): + config = self._frame.config + offline_mode = getattr(config, "offline_mode", False) + + if not offline_mode: + try: + import aiohttp + + # Use TinEye API if configured (requires API key) + tineye_key = os.environ.get("TINEYE_API_KEY") + if tineye_key: + tineye_results = await self._tineye_search(file_path, tineye_key) + api_results.extend(tineye_results) + + # Use Google Vision API if configured (requires API key) + google_key = os.environ.get("GOOGLE_VISION_API_KEY") + if google_key: + google_results = await self._google_vision_search(file_path, google_key) + api_results.extend(google_results) + + # Use SerpAPI for Google Reverse Image Search (has free tier) + serpapi_key = os.environ.get("SERPAPI_KEY") + if serpapi_key and base_url: + serpapi_results = await self._serpapi_search(analysis_id, base_url, serpapi_key) + api_results.extend(serpapi_results) + + except ImportError: + logger.debug("aiohttp not installed - API-based reverse image search unavailable") + + return { + "search_urls": search_urls, + "api_results": api_results, + "has_api_keys": bool( + os.environ.get("TINEYE_API_KEY") or + os.environ.get("GOOGLE_VISION_API_KEY") or + os.environ.get("SERPAPI_KEY") + ), + } + + async def _generate_search_urls( + self, analysis_id: str, file_path: str, base_url: Optional[str] = None + ) -> List[Dict[str, Any]]: + """Generate URLs for reverse image search engines.""" + search_urls = [] + + # If we have a base URL, generate a URL to the served image + image_url = None + if base_url: + # Use the image endpoint to serve the analysis image + image_url = f"{base_url}/api/media-forensics/image/{analysis_id}" + + # Google Images reverse search (supports URL-based search) + if image_url: + google_url = f"https://lens.google.com/uploadbyurl?url={quote(image_url)}" + search_urls.append({ + "engine": "Google Lens", + "url": google_url, + "icon": "search", + "description": "Search with Google Lens (URL-based)", + "type": "url_search", + }) + + # Google Images upload page (always available) + search_urls.append({ + "engine": "Google Images", + "url": "https://images.google.com/", + "icon": "image", + "description": "Upload image to Google Images", + "type": "upload_search", + "instructions": "Click the camera icon and upload the image", + }) + + # TinEye (upload-based) + search_urls.append({ + "engine": "TinEye", + "url": "https://tineye.com/", + "icon": "eye", + "description": "Upload image to TinEye", + "type": "upload_search", + "instructions": "Upload the image to search for matches", + }) + + # Yandex Images (supports URL-based search) + if image_url: + yandex_url = f"https://yandex.com/images/search?url={quote(image_url)}&rpt=imageview" + search_urls.append({ + "engine": "Yandex Images", + "url": yandex_url, + "icon": "globe", + "description": "Search with Yandex Images (URL-based)", + "type": "url_search", + }) + + # Yandex upload page (always available) + search_urls.append({ + "engine": "Yandex Images", + "url": "https://yandex.com/images/", + "icon": "globe", + "description": "Upload image to Yandex Images", + "type": "upload_search", + "instructions": "Click the camera icon and upload the image", + }) + + # Bing Visual Search (URL-based) + if image_url: + bing_url = f"https://www.bing.com/images/search?view=detailv2&iss=sbi&q=imgurl:{quote(image_url)}" + search_urls.append({ + "engine": "Bing Visual Search", + "url": bing_url, + "icon": "search", + "description": "Search with Bing Visual Search (URL-based)", + "type": "url_search", + }) + + return search_urls + + async def _serpapi_search( + self, analysis_id: str, base_url: str, api_key: str + ) -> List[Dict[str, Any]]: + """Search using SerpAPI Google Reverse Image Search.""" + try: + import aiohttp + + image_url = f"{base_url}/api/media-forensics/image/{analysis_id}" + + params = { + "engine": "google_reverse_image", + "image_url": image_url, + "api_key": api_key, + } + + async with aiohttp.ClientSession() as session: + async with session.get( + "https://serpapi.com/search", + params=params, + timeout=aiohttp.ClientTimeout(total=30), + ) as resp: + if resp.status == 200: + data = await resp.json() + results = [] + + # Process image results + for match in data.get("image_results", [])[:10]: + results.append({ + "url": match.get("link"), + "domain": match.get("source"), + "title": match.get("title", "SerpAPI Match"), + "thumbnail_url": match.get("thumbnail"), + "similarity_score": 0.75, + "source": "serpapi", + }) + + # Process inline images + for match in data.get("inline_images", [])[:5]: + if match.get("link"): + results.append({ + "url": match.get("link"), + "domain": match.get("source"), + "title": match.get("title", "SerpAPI Match"), + "thumbnail_url": match.get("thumbnail"), + "similarity_score": 0.7, + "source": "serpapi", + }) + + return results + else: + logger.warning(f"SerpAPI search returned status {resp.status}") + except Exception as e: + logger.warning(f"SerpAPI search failed: {e}") + return [] + + async def _tineye_search(self, file_path: str, api_key: str) -> List[Dict[str, Any]]: + """Search TinEye for matching images.""" + try: + import aiohttp + import base64 + + # Read and encode image + with open(file_path, "rb") as f: + image_data = base64.b64encode(f.read()).decode() + + async with aiohttp.ClientSession() as session: + async with session.post( + "https://api.tineye.com/rest/search/", + data={"image": image_data}, + headers={"x-api-key": api_key}, + timeout=aiohttp.ClientTimeout(total=30), + ) as resp: + if resp.status == 200: + data = await resp.json() + results = [] + for match in data.get("matches", [])[:10]: + results.append({ + "url": match.get("backlinks", [{}])[0].get("url"), + "domain": match.get("domain"), + "title": match.get("backlinks", [{}])[0].get("page_title", "TinEye Match"), + "thumbnail_url": match.get("image_url"), + "similarity_score": match.get("score", 50) / 100, + "source": "tineye", + }) + return results + except Exception as e: + logger.warning(f"TinEye search failed: {e}") + return [] + + async def _google_vision_search(self, file_path: str, api_key: str) -> List[Dict[str, Any]]: + """Search Google Vision for web detection.""" + try: + import aiohttp + import base64 + + # Read and encode image + with open(file_path, "rb") as f: + image_data = base64.b64encode(f.read()).decode() + + async with aiohttp.ClientSession() as session: + async with session.post( + f"https://vision.googleapis.com/v1/images:annotate?key={api_key}", + json={ + "requests": [{ + "image": {"content": image_data}, + "features": [{"type": "WEB_DETECTION", "maxResults": 10}], + }] + }, + timeout=aiohttp.ClientTimeout(total=30), + ) as resp: + if resp.status == 200: + data = await resp.json() + results = [] + web_detection = data.get("responses", [{}])[0].get("webDetection", {}) + + # Pages with matching images + for page in web_detection.get("pagesWithMatchingImages", [])[:10]: + results.append({ + "url": page.get("url"), + "domain": page.get("url", "").split("/")[2] if page.get("url") else None, + "title": page.get("pageTitle", "Google Vision Match"), + "thumbnail_url": page.get("fullMatchingImages", [{}])[0].get("url") + if page.get("fullMatchingImages") else None, + "similarity_score": 0.8, # Google doesn't provide score + "source": "google_vision", + }) + return results + except Exception as e: + logger.warning(f"Google Vision search failed: {e}") + return [] + + async def calculate_sun_position( + self, + latitude: float, + longitude: float, + dt: datetime, + ) -> Dict[str, Any]: + """Calculate sun position for given coordinates and time.""" + if not self.sun_position: + return {"success": False, "error": "Sun position service not available"} + + return await self.sun_position.calculate_sun_position(latitude, longitude, dt) + + async def get_sun_position_from_analysis(self, analysis_id: str) -> Dict[str, Any]: + """Calculate sun position from EXIF data of an analyzed image.""" + if not self.sun_position: + return {"success": False, "error": "Sun position service not available"} + + analysis = await self.get_analysis(analysis_id) + if not analysis: + return {"success": False, "error": "Analysis not found"} + + gps_data = { + "latitude": analysis.get("gps_latitude"), + "longitude": analysis.get("gps_longitude"), + } + + timestamp_data = { + "datetime_original": analysis.get("datetime_original"), + "datetime_digitized": analysis.get("datetime_digitized"), + "datetime_modified": analysis.get("datetime_modified"), + } + + result = await self.sun_position.verify_from_exif(gps_data, timestamp_data) + + # Save to database if successful + if result.get("success") and self._db: + sun_id = str(uuid.uuid4()) + await self._db.execute( + """ + INSERT INTO arkham_media_sun_verification + (id, analysis_id, claimed_datetime, latitude, longitude, + sun_altitude, sun_azimuth, expected_shadow_direction, + verification_status, notes, created_at) + VALUES + (:id, :analysis_id, :claimed_datetime, :latitude, :longitude, + :sun_altitude, :sun_azimuth, :expected_shadow_direction, + :verification_status, :notes, :created_at) + """, + { + "id": sun_id, + "analysis_id": analysis_id, + "claimed_datetime": result.get("datetime"), + "latitude": result.get("latitude"), + "longitude": result.get("longitude"), + "sun_altitude": result.get("sun_altitude"), + "sun_azimuth": result.get("sun_azimuth"), + "expected_shadow_direction": result.get("expected_shadow_direction"), + "verification_status": "calculated", + "notes": result.get("interpretation"), + "created_at": datetime.utcnow().isoformat(), + } + ) + + return result + + async def get_stats(self) -> Dict[str, Any]: + """Get media forensics statistics.""" + if not self._db: + return { + "total_analyses": 0, + "by_status": {"pending": 0, "processing": 0, "completed": 0, "failed": 0}, + "by_verification": {"verified": 0, "flagged": 0, "unknown": 0, "tampered": 0}, + "with_c2pa": 0, + "with_exif": 0, + "with_gps": 0, + "with_findings": 0, + "critical_findings_total": 0, + "high_findings_total": 0, + "ela_analyses": 0, + "sun_position_analyses": 0, + "similar_images_searches": 0, + "avg_findings_per_analysis": 0, + } + + # Initialize with frontend-expected structure + stats = { + "total_analyses": 0, + "by_status": {"pending": 0, "processing": 0, "completed": 0, "failed": 0}, + "by_verification": {"verified": 0, "flagged": 0, "unknown": 0, "tampered": 0}, + "with_c2pa": 0, + "with_exif": 0, + "with_gps": 0, + "with_findings": 0, + "critical_findings_total": 0, + "high_findings_total": 0, + "ela_analyses": 0, + "sun_position_analyses": 0, + "similar_images_searches": 0, + "avg_findings_per_analysis": 0.0, + # Also keep legacy fields for backward compatibility + "with_warnings": 0, + "ai_generated_detected": 0, + "by_integrity_status": {}, + "by_file_type": {}, + } + + try: + # Total count + total_row = await self._db.fetch_one( + "SELECT COUNT(*) as count FROM arkham_media_analyses" + ) + total = total_row["count"] if total_row else 0 + stats["total_analyses"] = total + # All records in DB are completed + stats["by_status"]["completed"] = total + + # With EXIF (camera_make not null) + exif_row = await self._db.fetch_one( + "SELECT COUNT(*) as count FROM arkham_media_analyses WHERE camera_make IS NOT NULL" + ) + stats["with_exif"] = exif_row["count"] if exif_row else 0 + + # With GPS + gps_row = await self._db.fetch_one( + "SELECT COUNT(*) as count FROM arkham_media_analyses WHERE gps_latitude IS NOT NULL" + ) + stats["with_gps"] = gps_row["count"] if gps_row else 0 + + # With C2PA + c2pa_row = await self._db.fetch_one( + "SELECT COUNT(*) as count FROM arkham_media_analyses WHERE has_c2pa = 1" + ) + stats["with_c2pa"] = c2pa_row["count"] if c2pa_row else 0 + + # With warnings/findings + warnings_row = await self._db.fetch_one( + "SELECT COUNT(*) as count FROM arkham_media_analyses WHERE warnings != '[]'" + ) + warnings_count = warnings_row["count"] if warnings_row else 0 + stats["with_warnings"] = warnings_count + stats["with_findings"] = warnings_count + + # AI generated (check warnings for AI_GENERATED) + ai_row = await self._db.fetch_one( + "SELECT COUNT(*) as count FROM arkham_media_analyses WHERE warnings LIKE '%AI_GENERATED%'" + ) + stats["ai_generated_detected"] = ai_row["count"] if ai_row else 0 + + # By integrity status (maps to by_verification) + status_rows = await self._db.fetch_all( + "SELECT integrity_status, COUNT(*) as count FROM arkham_media_analyses GROUP BY integrity_status" + ) + by_integrity = {row["integrity_status"]: row["count"] for row in status_rows} + stats["by_integrity_status"] = by_integrity + # Map to frontend verification status + stats["by_verification"]["verified"] = by_integrity.get("verified", 0) + stats["by_verification"]["flagged"] = by_integrity.get("flagged", 0) + by_integrity.get("unverified", 0) + stats["by_verification"]["unknown"] = by_integrity.get("unknown", 0) + stats["by_verification"]["tampered"] = by_integrity.get("tampered", 0) + + # By file type + type_rows = await self._db.fetch_all( + "SELECT file_type, COUNT(*) as count FROM arkham_media_analyses WHERE file_type IS NOT NULL GROUP BY file_type" + ) + stats["by_file_type"] = {row["file_type"]: row["count"] for row in type_rows} + + except Exception as e: + logger.error(f"Failed to get stats: {e}") + + return stats + + # =========================================== + # Helper Methods + # =========================================== + + def _row_to_analysis_dict(self, row: Dict[str, Any]) -> Dict[str, Any]: + """Convert a database row to an analysis dictionary.""" + # Parse JSON fields + raw_exif_data = row.get("exif_data", "{}") + if isinstance(raw_exif_data, str): + try: + raw_exif_data = json.loads(raw_exif_data) + except json.JSONDecodeError: + raw_exif_data = {} + + # Structure exif_data to match frontend ExifData type + exif_data = self._structure_exif_data(raw_exif_data, row) + + raw_c2pa_data = row.get("c2pa_data", "{}") + if isinstance(raw_c2pa_data, str): + try: + raw_c2pa_data = json.loads(raw_c2pa_data) + except json.JSONDecodeError: + raw_c2pa_data = {} + + # Transform C2PA data to frontend expected format + c2pa_data = self._transform_c2pa_for_frontend(raw_c2pa_data) + + warnings = row.get("warnings", "[]") + if isinstance(warnings, str): + try: + warnings = json.loads(warnings) + except json.JSONDecodeError: + warnings = [] + + anomalies = row.get("anomalies", "[]") + if isinstance(anomalies, str): + try: + anomalies = json.loads(anomalies) + except json.JSONDecodeError: + anomalies = [] + + # Convert string warnings to ForensicFinding objects for frontend compatibility + findings = [] + for i, warning in enumerate(warnings if isinstance(warnings, list) else []): + if isinstance(warning, str): + # Determine category and severity from warning text + category = "general" + severity = "medium" + if warning.startswith("EXIF_"): + category = "exif" + elif warning.startswith("C2PA_"): + category = "c2pa" + elif warning.startswith("AI_GENERATED"): + category = "c2pa" + severity = "high" + elif warning.startswith("TIMESTAMP_"): + category = "exif" + elif warning.startswith("GPS_"): + category = "exif" + + findings.append({ + "id": f"{row['id']}_warning_{i}", + "category": category, + "severity": severity, + "title": warning.split(":")[0] if ":" in warning else warning[:50], + "description": warning, + "evidence": {}, + "recommendation": None, + "confidence": 0.7, + "auto_detected": True, + "detected_at": row["created_at"].isoformat() if row.get("created_at") else datetime.utcnow().isoformat(), + }) + elif isinstance(warning, dict): + # Already a finding object + findings.append(warning) + + # Map integrity_status to verification_status for frontend compatibility + integrity_status = row.get("integrity_status", "unknown") + + # Derive filename from document_id or file path if stored + filename = row.get("filename") + if not filename and row.get("document_id"): + # Use document_id as filename placeholder + filename = row.get("document_id", "unknown") + + return { + "id": row["id"], + "doc_id": row.get("document_id"), # Frontend expects doc_id + "document_id": row.get("document_id"), + "filename": filename, + "file_path": row.get("file_path"), # For ELA and other operations + "tenant_id": row.get("tenant_id"), + "file_type": row.get("file_type"), + "file_size": row.get("file_size"), + "file_hash_md5": row.get("md5"), + "file_hash_sha256": row.get("sha256"), + "width": row.get("width"), + "height": row.get("height"), + "sha256": row.get("sha256"), + "md5": row.get("md5"), + "phash": row.get("phash"), + "dhash": row.get("dhash"), + "ahash": row.get("ahash"), + "exif_data": exif_data, + "camera_make": row.get("camera_make"), + "camera_model": row.get("camera_model"), + "software": row.get("software"), + "datetime_original": row.get("datetime_original"), + "datetime_digitized": row.get("datetime_digitized"), + "datetime_modified": row.get("datetime_modified"), + "gps_latitude": row.get("gps_latitude"), + "gps_longitude": row.get("gps_longitude"), + "gps_altitude": row.get("gps_altitude"), + "c2pa_data": c2pa_data, + "has_c2pa": bool(row.get("has_c2pa")), + "c2pa_signer": row.get("c2pa_signer"), + "c2pa_timestamp": row.get("c2pa_timestamp"), + "warnings": warnings, + "anomalies": anomalies, + # Status fields for frontend compatibility + "status": "completed", # Analysis is always completed when in DB + "verification_status": integrity_status, + "integrity_status": integrity_status, + "confidence_score": row.get("confidence_score", 0.0), + # Findings fields - properly structured ForensicFinding objects + "findings": findings, + "findings_count": len(findings), + "critical_findings": sum(1 for f in findings if f.get("severity") == "critical"), + "high_findings": sum(1 for f in findings if f.get("severity") == "high"), + # Timestamps + "analyzed_at": row["created_at"].isoformat() if row.get("created_at") else None, + "created_at": row["created_at"].isoformat() if row.get("created_at") else None, + "updated_at": row["updated_at"].isoformat() if row.get("updated_at") else None, + "analyzed_by": "system", + "notes": None, + # Component results (null by default, populated when requested) + "ela_result": None, + "sun_position_result": None, + "similar_images_result": None, + } + + def _structure_exif_data(self, raw_exif: Dict[str, Any], row: Dict[str, Any]) -> Dict[str, Any]: + """ + Transform raw EXIF data into the structured format expected by the frontend. + + Frontend expects: + { + camera: { make, model, serial_number, lens_make, lens_model, lens_serial }, + image: { width, height, orientation, color_space, bits_per_sample, compression }, + gps: { latitude, longitude, altitude, altitude_ref, gps_timestamp, gps_date, direction, direction_ref }, + capture: { exposure_time, f_number, iso, focal_length, focal_length_35mm, exposure_mode, exposure_program, metering_mode, white_balance, flash }, + timestamps: { datetime_original, datetime_digitized, datetime_modified, timezone_offset }, + software: { software, processing_software, host_computer, firmware }, + raw_data: { ... original raw exif ... }, + warnings: [ ... ] + } + """ + # Build camera info from raw EXIF or row columns + camera = { + "make": row.get("camera_make") or raw_exif.get("Make") or raw_exif.get("Image Make"), + "model": row.get("camera_model") or raw_exif.get("Model") or raw_exif.get("Image Model"), + "serial_number": raw_exif.get("SerialNumber") or raw_exif.get("EXIF SerialNumber"), + "lens_make": raw_exif.get("LensMake") or raw_exif.get("EXIF LensMake"), + "lens_model": raw_exif.get("LensModel") or raw_exif.get("EXIF LensModel"), + "lens_serial": raw_exif.get("LensSerialNumber") or raw_exif.get("EXIF LensSerialNumber"), + } + + # Build image info + image = { + "width": row.get("width") or raw_exif.get("EXIF ExifImageWidth") or raw_exif.get("ImageWidth"), + "height": row.get("height") or raw_exif.get("EXIF ExifImageLength") or raw_exif.get("ImageLength"), + "orientation": raw_exif.get("Orientation") or raw_exif.get("Image Orientation"), + "color_space": raw_exif.get("EXIF ColorSpace") or raw_exif.get("ColorSpace"), + "bits_per_sample": raw_exif.get("BitsPerSample") or raw_exif.get("Image BitsPerSample"), + "compression": raw_exif.get("Compression") or raw_exif.get("Image Compression"), + } + + # Build GPS info + gps = { + "latitude": row.get("gps_latitude"), + "longitude": row.get("gps_longitude"), + "altitude": row.get("gps_altitude"), + "altitude_ref": raw_exif.get("GPS GPSAltitudeRef"), + "gps_timestamp": raw_exif.get("GPS GPSTimeStamp"), + "gps_date": raw_exif.get("GPS GPSDate"), + "direction": None, # Would need parsing from GPS GPSImgDirection + "direction_ref": raw_exif.get("GPS GPSImgDirectionRef"), + } + # Try to parse direction + if raw_exif.get("GPS GPSImgDirection"): + try: + direction_str = str(raw_exif.get("GPS GPSImgDirection")) + if "/" in direction_str: + num, den = direction_str.split("/") + gps["direction"] = float(num) / float(den) + else: + gps["direction"] = float(direction_str) + except (ValueError, ZeroDivisionError): + pass + + # Build capture settings + capture = { + "exposure_time": str(raw_exif.get("EXIF ExposureTime")) if raw_exif.get("EXIF ExposureTime") else None, + "f_number": None, + "iso": None, + "focal_length": None, + "focal_length_35mm": None, + "exposure_mode": raw_exif.get("EXIF ExposureMode"), + "exposure_program": raw_exif.get("EXIF ExposureProgram"), + "metering_mode": raw_exif.get("EXIF MeteringMode"), + "white_balance": raw_exif.get("EXIF WhiteBalance"), + "flash": raw_exif.get("EXIF Flash"), + } + # Parse numeric values + if raw_exif.get("EXIF FNumber"): + try: + fn_str = str(raw_exif.get("EXIF FNumber")) + if "/" in fn_str: + num, den = fn_str.split("/") + capture["f_number"] = round(float(num) / float(den), 1) + else: + capture["f_number"] = float(fn_str) + except (ValueError, ZeroDivisionError): + pass + if raw_exif.get("EXIF ISOSpeedRatings"): + try: + capture["iso"] = int(str(raw_exif.get("EXIF ISOSpeedRatings")).split()[0]) + except (ValueError, IndexError): + pass + if raw_exif.get("EXIF FocalLength"): + try: + fl_str = str(raw_exif.get("EXIF FocalLength")) + if "/" in fl_str: + num, den = fl_str.split("/") + capture["focal_length"] = round(float(num) / float(den), 1) + else: + capture["focal_length"] = float(fl_str) + except (ValueError, ZeroDivisionError): + pass + if raw_exif.get("EXIF FocalLengthIn35mmFilm"): + try: + capture["focal_length_35mm"] = int(str(raw_exif.get("EXIF FocalLengthIn35mmFilm"))) + except ValueError: + pass + + # Build timestamps + timestamps = { + "datetime_original": row.get("datetime_original") or raw_exif.get("EXIF DateTimeOriginal"), + "datetime_digitized": row.get("datetime_digitized") or raw_exif.get("EXIF DateTimeDigitized"), + "datetime_modified": row.get("datetime_modified") or raw_exif.get("DateTime") or raw_exif.get("Image DateTime"), + "timezone_offset": raw_exif.get("EXIF OffsetTime") or raw_exif.get("OffsetTime"), + } + + # Build software info + software = { + "software": row.get("software") or raw_exif.get("Software") or raw_exif.get("Image Software"), + "processing_software": raw_exif.get("ProcessingSoftware"), + "host_computer": raw_exif.get("HostComputer") or raw_exif.get("Image HostComputer"), + "firmware": raw_exif.get("Firmware") or raw_exif.get("MakerNote FirmwareVersion"), + } + + # Collect warnings from raw exif (if any embedded warnings) + warnings = [] + + return { + "camera": camera, + "image": image, + "gps": gps, + "capture": capture, + "timestamps": timestamps, + "software": software, + "raw_data": raw_exif, + "warnings": warnings, + } + + def _transform_c2pa_for_frontend(self, raw_c2pa: Dict[str, Any]) -> Dict[str, Any]: + """ + Transform backend C2PA data into the format expected by the frontend. + + Backend stores: + { + has_c2pa: bool, + manifests: ["urn:..."], # Array of URN strings + active_manifest: "urn:...", + signature_valid: bool, + signer: "Adobe Inc.", + timestamp: "...", + actions: [...], + ingredients: [...], + claim_generator: "...", + raw_manifest: {...} # Full raw manifest data + } + + Frontend expects: + { + has_manifest: bool, + manifests: C2PAManifest[], # Array of full manifest objects + active_manifest_index: number | null, + validation_status: 'valid' | 'invalid' | 'expired' | 'revoked' | 'unknown', + validation_errors: string[], + provenance_chain: string[], + } + """ + if not raw_c2pa or not raw_c2pa.get("has_c2pa"): + return { + "has_manifest": False, + "manifests": [], + "active_manifest_index": None, + "validation_status": "unknown", + "validation_errors": [], + "provenance_chain": [], + } + + # Build the manifest object from raw data + manifests = [] + + # Get raw manifest data if available + raw_manifest_store = raw_c2pa.get("raw_manifest", {}) + active_manifest_id = raw_c2pa.get("active_manifest") + + # Determine validation status + if raw_c2pa.get("signature_valid"): + validation_status = "valid" + elif raw_c2pa.get("signature_verification_available") is False: + # Signature verification not available (no trust anchors) + validation_status = "unknown" + else: + validation_status = "invalid" + + # Build manifest from available data + manifest = { + "claim_generator": raw_c2pa.get("claim_generator"), + "title": None, + "format": None, + "instance_id": active_manifest_id, + "assertions": [], + "actions": [], + "ingredients": [], + "signer": None, + "signature_date": raw_c2pa.get("timestamp"), + } + + # Build signer object + if raw_c2pa.get("signer"): + manifest["signer"] = { + "name": raw_c2pa.get("signer"), + "organization": None, + "issued_date": None, + "expiry_date": None, + "is_trusted": raw_c2pa.get("signature_valid", False), + "trust_chain": [], + "validation_status": validation_status, + } + # Try to extract organization from signer name + signer_name = raw_c2pa.get("signer", "") + if " Inc" in signer_name or " LLC" in signer_name or " Corp" in signer_name: + manifest["signer"]["organization"] = signer_name + + # Transform actions + for action in raw_c2pa.get("actions", []): + if isinstance(action, dict): + manifest["actions"].append({ + "action": action.get("action", "unknown"), + "when": action.get("when"), + "software_agent": action.get("softwareAgent"), + "parameters": action.get("parameters", {}), + }) + elif isinstance(action, str): + manifest["actions"].append({ + "action": action, + "when": None, + "software_agent": None, + "parameters": {}, + }) + + # Transform ingredients + for ingredient in raw_c2pa.get("ingredients", []): + if isinstance(ingredient, dict): + manifest["ingredients"].append({ + "title": ingredient.get("title"), + "format": ingredient.get("format"), + "document_id": ingredient.get("document_id"), + "instance_id": ingredient.get("instance_id"), + "relationship": ingredient.get("relationship", "componentOf"), + "thumbnail": ingredient.get("thumbnail"), + }) + + # Try to get more details from raw_manifest if available + if raw_manifest_store and "manifests" in raw_manifest_store and active_manifest_id: + if active_manifest_id in raw_manifest_store["manifests"]: + full_manifest = raw_manifest_store["manifests"][active_manifest_id] + + # Get title from manifest + if "title" in full_manifest: + manifest["title"] = full_manifest["title"] + if "format" in full_manifest: + manifest["format"] = full_manifest["format"] + + # Get assertions + for assertion in full_manifest.get("assertions", []): + manifest["assertions"].append({ + "label": assertion.get("label", ""), + "data": assertion.get("data", {}), + "instance": assertion.get("instance"), + }) + + # Get signature info + if "signature_info" in full_manifest: + sig_info = full_manifest["signature_info"] + if manifest["signer"]: + manifest["signer"]["issued_date"] = sig_info.get("time") + manifest["signature_date"] = sig_info.get("time") + + manifests.append(manifest) + + # Build provenance chain from ingredient titles + provenance_chain = [] + for ing in manifest["ingredients"]: + if ing.get("title"): + provenance_chain.append(ing["title"]) + + return { + "has_manifest": True, + "manifests": manifests, + "active_manifest_index": 0, # Always first manifest since we only build one + "validation_status": validation_status, + "validation_errors": [], + "provenance_chain": provenance_chain, + } diff --git a/packages/arkham-shard-media-forensics/pyproject.toml b/packages/arkham-shard-media-forensics/pyproject.toml new file mode 100644 index 0000000..02f36cd --- /dev/null +++ b/packages/arkham-shard-media-forensics/pyproject.toml @@ -0,0 +1,29 @@ +[project] +name = "arkham-shard-media-forensics" +version = "0.1.0" +description = "Media metadata extraction and forensic analysis for images" +license = {text = "MIT"} +readme = "README.md" +requires-python = ">=3.10" + +dependencies = [ + "arkham-frame>=0.1.0", + "pydantic>=2.0.0", + "pillow>=10.0.0", + "exifread>=3.0.0", + "pysolar>=0.10", + "scipy>=1.10.0", + "numpy>=1.20.0", + "structlog>=21.0.0", + "c2pa-python>=0.4.0", +] + +[project.optional-dependencies] +dev = ["pytest", "pytest-asyncio", "black", "mypy"] + +[project.entry-points."arkham.shards"] +media-forensics = "arkham_shard_media_forensics:MediaForensicsShard" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/packages/arkham-shard-media-forensics/shard.yaml b/packages/arkham-shard-media-forensics/shard.yaml new file mode 100644 index 0000000..f9dd1d3 --- /dev/null +++ b/packages/arkham-shard-media-forensics/shard.yaml @@ -0,0 +1,56 @@ +# Media Forensics Shard - Image authenticity and metadata analysis +# Production Manifest v1.0 + +name: media-forensics +version: "0.1.0" +description: Media metadata extraction and forensic analysis for images +entry_point: arkham_shard_media_forensics:MediaForensicsShard +api_prefix: /api/media-forensics +requires_frame: ">=0.1.0" + +navigation: + category: Analysis + order: 46 + icon: ImageSearch + label: Media Forensics + route: /media-forensics + badge_endpoint: /api/media-forensics/analyses/count + badge_type: count + +dependencies: + services: + - database + - events + - storage + optional: + - llm + shards: [] + +capabilities: + - exif_extraction + - perceptual_hashing + - c2pa_verification + - ela_analysis + - sun_position_verification + - similar_image_detection + +events: + publishes: + - media.metadata.extracted + - media.hash.computed + - media.c2pa.verified + - media.ela.generated + - media.similar.found + subscribes: + - document.ingested + - document.processed + +state: + strategy: url + url_params: + - documentId + - analysisId + - tab + +ui: + has_custom_ui: true diff --git a/packages/arkham-shard-shell/src/App.tsx b/packages/arkham-shard-shell/src/App.tsx index 464049d..521e509 100644 --- a/packages/arkham-shard-shell/src/App.tsx +++ b/packages/arkham-shard-shell/src/App.tsx @@ -77,6 +77,9 @@ import { CredibilityPage } from './pages/credibility'; import { PatternsPage } from './pages/patterns'; import { ProvenancePage } from './pages/provenance'; +// Wave 6 shard imports +import { MediaForensicsPage } from './pages/media-forensics'; + // Wave 4 shard imports import { ExportPage } from './pages/export'; import { ReportsPage } from './pages/reports'; @@ -210,6 +213,10 @@ export function App() { {/* Summary shard */} } /> + {/* Wave 6 shards */} + {/* Media Forensics shard */} + } /> + {/* Catch-all: try to render shard with generic UI */} } /> diff --git a/packages/arkham-shard-shell/src/pages/media-forensics/MediaForensicsPage.css b/packages/arkham-shard-shell/src/pages/media-forensics/MediaForensicsPage.css new file mode 100644 index 0000000..859a724 --- /dev/null +++ b/packages/arkham-shard-shell/src/pages/media-forensics/MediaForensicsPage.css @@ -0,0 +1,2243 @@ +/* Media Forensics Page Styles */ + +.media-forensics-page { + display: flex; + flex-direction: column; + height: 100%; + background: var(--color-bg-primary); +} + +/* Stats Grid */ +.forensics-stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 1rem; + padding: 0 1rem 1rem; +} + +.forensics-stat-card { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + padding: 1rem; + background: var(--color-bg-secondary, #1e2530); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 8px; + text-align: center; +} + +.forensics-stat-card svg { + color: var(--color-primary, #3b82f6); +} + +.forensics-stat-card.stat-card-warning svg { + color: var(--arkham-error, #ef4444); +} + +.forensics-stat-value { + font-size: 1.75rem; + font-weight: 700; + color: var(--color-text-primary, #e5e7eb); +} + +.forensics-stat-label { + font-size: 0.75rem; + color: var(--color-text-secondary, #9ca3af); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +/* Filters */ +.forensics-filters { + display: flex; + align-items: flex-end; + gap: 1rem; + padding: 0 1rem 1rem; + flex-wrap: wrap; +} + +.filter-group { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.filter-group label { + font-size: 0.75rem; + color: var(--color-text-secondary, #9ca3af); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.filter-group select { + padding: 0.5rem 0.75rem; + background: var(--color-bg-secondary, #1e2530); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 6px; + color: var(--color-text-primary, #e5e7eb); + font-size: 0.875rem; + min-width: 150px; +} + +.filter-group select:focus { + outline: none; + border-color: var(--color-primary, #3b82f6); +} + +/* Layout */ +.forensics-layout { + display: flex; + flex: 1; + gap: 1rem; + padding: 0 1rem 1rem; + overflow: hidden; +} + +/* List Container */ +.forensics-list-container { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + background: var(--color-bg-secondary, #1e2530); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 8px; +} + +.forensics-list { + flex: 1; + overflow-y: auto; + padding: 1rem; + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +/* Analysis Card */ +.analysis-card { + position: relative; + background: var(--color-bg-primary, #151a21); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 8px; + padding: 1rem; + cursor: pointer; + transition: all 0.15s ease; +} + +.analysis-card:hover { + background: var(--color-bg-hover, #252d38); + border-color: var(--color-primary, #3b82f6); +} + +.analysis-card.selected { + background: #1a2a3d; + border: 2px solid #3b82f6; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2); + padding: calc(1rem - 1px); +} + +.selection-indicator { + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 4px; + background: linear-gradient(180deg, #3b82f6 0%, #60a5fa 100%); + border-radius: 8px 0 0 8px; +} + +.analysis-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 0.75rem; + gap: 0.75rem; +} + +.analysis-file-info { + display: flex; + align-items: flex-start; + gap: 0.5rem; + flex: 1; + min-width: 0; +} + +.analysis-file-info svg { + color: var(--color-text-tertiary, #6b7280); + flex-shrink: 0; + margin-top: 2px; +} + +.file-details { + display: flex; + flex-direction: column; + gap: 0.125rem; + min-width: 0; +} + +.filename { + font-weight: 600; + color: var(--color-text-primary, #e5e7eb); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.file-meta { + font-size: 0.75rem; + color: var(--color-text-secondary, #9ca3af); +} + +.analysis-badges { + display: flex; + gap: 0.5rem; + flex-shrink: 0; +} + +.badge { + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-size: 0.7rem; + font-weight: 600; + text-transform: uppercase; +} + +.badge-blue { + background: rgba(59, 130, 246, 0.2); + color: #60a5fa; +} + +.badge-yellow { + background: rgba(234, 179, 8, 0.2); + color: #fbbf24; +} + +.badge-green { + background: rgba(34, 197, 94, 0.2); + color: #4ade80; +} + +.badge-red { + background: rgba(239, 68, 68, 0.2); + color: #f87171; +} + +.verification-badge { + display: inline-flex; + align-items: center; + gap: 0.25rem; + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-size: 0.7rem; + font-weight: 600; + color: white; +} + +/* Analysis Indicators */ +.analysis-indicators { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-bottom: 0.75rem; +} + +.indicator { + display: inline-flex; + align-items: center; + gap: 0.25rem; + padding: 0.25rem 0.5rem; + background: var(--color-bg-tertiary, #2a323e); + border-radius: 4px; + font-size: 0.7rem; + color: var(--color-text-secondary, #9ca3af); +} + +.indicator svg { + color: var(--color-text-tertiary, #6b7280); +} + +.indicator-c2pa { + background: rgba(34, 197, 94, 0.15); + color: #4ade80; +} + +.indicator-c2pa svg { + color: #4ade80; +} + +/* Findings Summary */ +.analysis-findings-summary { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem; + background: rgba(234, 179, 8, 0.1); + border: 1px solid rgba(234, 179, 8, 0.3); + border-radius: 6px; + margin-bottom: 0.75rem; + font-size: 0.8rem; + color: #fbbf24; +} + +.analysis-findings-summary svg { + color: #fbbf24; +} + +.critical-count { + color: #f87171; + font-weight: 600; + margin-left: 0.25rem; +} + +/* Analysis Meta */ +.analysis-meta { + display: flex; + gap: 1rem; + font-size: 0.75rem; + color: var(--color-text-secondary, #9ca3af); +} + +.meta-item { + display: flex; + align-items: center; + gap: 0.25rem; +} + +.meta-item svg { + color: var(--color-text-tertiary, #6b7280); +} + +/* Pagination */ +.forensics-pagination { + display: flex; + justify-content: center; + gap: 0.5rem; + padding: 1rem; + border-top: 1px solid var(--color-border, #3f4a5a); +} + +/* Detail Panel */ +.forensics-detail-panel { + width: 480px; + background: var(--color-bg-secondary, #1e2530); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 8px; + display: flex; + flex-direction: column; + overflow: hidden; +} + +.detail-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1rem; + border-bottom: 1px solid var(--color-border, #3f4a5a); +} + +.detail-header h3 { + margin: 0; + font-size: 1rem; + font-weight: 600; + color: var(--color-text-primary, #e5e7eb); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; + margin-right: 0.5rem; +} + +.detail-header-actions { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.close-btn { + background: none; + border: none; + padding: 0.25rem; + cursor: pointer; + color: var(--color-text-secondary, #9ca3af); + transition: color 0.15s ease; +} + +.close-btn:hover { + color: var(--color-text-primary, #e5e7eb); +} + +/* Verification Banner */ +.verification-banner { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.75rem; + color: white; + font-weight: 600; + font-size: 0.875rem; +} + +/* Detail Tabs */ +.detail-tabs { + display: flex; + flex-wrap: wrap; + border-bottom: 1px solid var(--color-border, #3f4a5a); + gap: 0; +} + +.detail-tab { + display: flex; + align-items: center; + gap: 0.25rem; + padding: 0.5rem 0.625rem; + background: none; + border: none; + border-bottom: 2px solid transparent; + cursor: pointer; + color: var(--color-text-secondary, #9ca3af); + font-size: 0.7rem; + font-weight: 500; + transition: all 0.15s ease; + white-space: nowrap; +} + +.detail-tab:hover:not(:disabled) { + color: var(--color-text-primary, #e5e7eb); + background: var(--color-bg-hover, #252d38); +} + +.detail-tab.active { + color: var(--color-primary, #3b82f6); + border-bottom-color: var(--color-primary, #3b82f6); +} + +.detail-tab:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.tab-badge { + display: inline-flex; + align-items: center; + justify-content: center; + width: 14px; + height: 14px; + background: var(--arkham-success, #22c55e); + border-radius: 50%; + color: white; +} + +.tab-count { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 18px; + height: 18px; + padding: 0 4px; + background: var(--arkham-warning, #eab308); + border-radius: 9px; + font-size: 0.65rem; + font-weight: 700; + color: #000; +} + +/* Detail Content */ +.detail-content { + flex: 1; + overflow-y: auto; + padding: 1rem; +} + +/* Content States */ +.content-loading, +.content-error, +.content-empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + gap: 1rem; + color: var(--color-text-secondary, #9ca3af); + padding: 2rem; + text-align: center; +} + +.content-loading svg, +.content-error svg, +.content-empty svg { + color: var(--color-text-tertiary, #6b7280); +} + +.content-empty h2, +.content-error h2 { + margin: 0; + font-size: 1.25rem; + color: var(--color-text-primary, #e5e7eb); +} + +.content-empty p, +.content-error p { + margin: 0; + font-size: 0.875rem; + max-width: 300px; +} + +/* Panel Sections */ +.panel-section { + margin-bottom: 1.5rem; +} + +.panel-section:last-child { + margin-bottom: 0; +} + +.panel-section-header { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; +} + +.panel-section-header h4 { + margin: 0; + font-size: 0.875rem; + font-weight: 600; + color: var(--color-text-secondary, #9ca3af); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.panel-section-header svg { + color: var(--color-text-tertiary, #6b7280); +} + +/* Metadata Grid */ +.metadata-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 0.5rem; +} + +.metadata-item { + display: flex; + flex-direction: column; + gap: 0.125rem; + padding: 0.5rem; + background: var(--color-bg-primary, #151a21); + border-radius: 4px; +} + +.metadata-item.full-width { + grid-column: span 2; +} + +.metadata-label { + font-size: 0.7rem; + color: var(--color-text-tertiary, #6b7280); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.metadata-value { + font-size: 0.875rem; + color: var(--color-text-primary, #e5e7eb); + word-break: break-word; +} + +.metadata-value.monospace { + font-family: 'Monaco', 'Consolas', monospace; + font-size: 0.8rem; +} + +/* Warning/Finding Cards */ +.finding-card { + padding: 0.75rem; + background: var(--color-bg-primary, #151a21); + border: 1px solid var(--color-border, #3f4a5a); + border-left-width: 3px; + border-radius: 6px; + margin-bottom: 0.75rem; +} + +.finding-card:last-child { + margin-bottom: 0; +} + +.finding-card.severity-critical { + border-left-color: var(--arkham-error, #ef4444); +} + +.finding-card.severity-high { + border-left-color: var(--arkham-warning, #f97316); +} + +.finding-card.severity-medium { + border-left-color: var(--arkham-warning, #eab308); +} + +.finding-card.severity-low { + border-left-color: var(--arkham-info, #3b82f6); +} + +.finding-card.severity-info { + border-left-color: var(--color-text-tertiary, #6b7280); +} + +.finding-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-bottom: 0.5rem; +} + +.finding-title { + font-weight: 600; + color: var(--color-text-primary, #e5e7eb); + font-size: 0.875rem; +} + +.finding-severity { + font-size: 0.65rem; + font-weight: 700; + text-transform: uppercase; + padding: 0.125rem 0.375rem; + border-radius: 3px; +} + +.finding-severity.severity-critical { + background: rgba(239, 68, 68, 0.2); + color: #f87171; +} + +.finding-severity.severity-high { + background: rgba(249, 115, 22, 0.2); + color: #fb923c; +} + +.finding-severity.severity-medium { + background: rgba(234, 179, 8, 0.2); + color: #fbbf24; +} + +.finding-severity.severity-low { + background: rgba(59, 130, 246, 0.2); + color: #60a5fa; +} + +.finding-severity.severity-info { + background: rgba(107, 114, 128, 0.2); + color: #9ca3af; +} + +.finding-description { + font-size: 0.8rem; + color: var(--color-text-secondary, #9ca3af); + line-height: 1.5; + margin: 0; +} + +.finding-meta { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 0.5rem; + font-size: 0.7rem; + color: var(--color-text-tertiary, #6b7280); +} + +/* ELA Viewer Specific */ +.ela-container { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.ela-controls { + display: flex; + align-items: center; + gap: 1rem; + padding: 0.75rem; + background: var(--color-bg-primary, #151a21); + border-radius: 6px; +} + +.ela-controls label { + font-size: 0.8rem; + color: var(--color-text-secondary, #9ca3af); +} + +.ela-controls input[type="range"] { + flex: 1; +} + +.ela-images { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.75rem; +} + +.ela-image-container { + position: relative; + background: var(--color-bg-primary, #151a21); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 6px; + overflow: hidden; +} + +.ela-image-label { + position: absolute; + top: 0.5rem; + left: 0.5rem; + padding: 0.25rem 0.5rem; + background: rgba(0, 0, 0, 0.7); + border-radius: 4px; + font-size: 0.7rem; + color: white; + z-index: 1; +} + +.ela-image-container img { + width: 100%; + height: auto; + display: block; +} + +.ela-regions-list { + margin-top: 1rem; +} + +.ela-region-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.5rem; + background: var(--color-bg-primary, #151a21); + border-radius: 4px; + margin-bottom: 0.5rem; + font-size: 0.8rem; +} + +.ela-region-item.suspicious { + background: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.3); +} + +/* C2PA Specific */ +.c2pa-signer-card { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 1rem; + background: var(--color-bg-primary, #151a21); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 8px; + margin-bottom: 1rem; +} + +.c2pa-signer-badge { + display: flex; + align-items: center; + justify-content: center; + width: 48px; + height: 48px; + border-radius: 50%; + background: var(--arkham-success, #22c55e); + color: white; +} + +.c2pa-signer-badge.invalid { + background: var(--arkham-error, #ef4444); +} + +.c2pa-signer-info { + flex: 1; +} + +.c2pa-signer-name { + font-weight: 600; + color: var(--color-text-primary, #e5e7eb); + margin-bottom: 0.25rem; +} + +.c2pa-signer-org { + font-size: 0.8rem; + color: var(--color-text-secondary, #9ca3af); +} + +.c2pa-validation-status { + padding: 0.25rem 0.75rem; + border-radius: 12px; + font-size: 0.75rem; + font-weight: 600; + color: white; +} + +.c2pa-actions-list { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.c2pa-action-item { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem; + background: var(--color-bg-primary, #151a21); + border-radius: 4px; + font-size: 0.8rem; +} + +.c2pa-action-icon { + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + background: var(--color-bg-tertiary, #2a323e); + border-radius: 4px; + color: var(--color-text-secondary, #9ca3af); +} + +.c2pa-action-details { + flex: 1; +} + +.c2pa-action-name { + font-weight: 500; + color: var(--color-text-primary, #e5e7eb); +} + +.c2pa-action-meta { + font-size: 0.7rem; + color: var(--color-text-tertiary, #6b7280); +} + +/* Similar Images Grid */ +.similar-images-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 0.75rem; +} + +.similar-image-card { + position: relative; + background: var(--color-bg-primary, #151a21); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 6px; + overflow: hidden; + cursor: pointer; + transition: all 0.15s ease; +} + +.similar-image-card:hover { + border-color: var(--color-primary, #3b82f6); + transform: translateY(-2px); +} + +.similar-image-thumb { + width: 100%; + aspect-ratio: 1; + object-fit: cover; + background: var(--color-bg-tertiary, #2a323e); +} + +.similar-image-info { + padding: 0.5rem; +} + +.similar-image-score { + font-size: 0.875rem; + font-weight: 600; + color: var(--color-text-primary, #e5e7eb); +} + +.similar-image-type { + font-size: 0.7rem; + color: var(--color-text-tertiary, #6b7280); + text-transform: capitalize; +} + +/* Sun Position Panel */ +.sun-position-container { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.sun-consistency-banner { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 1rem; + border-radius: 8px; + font-weight: 500; +} + +.sun-consistency-banner.consistent { + background: rgba(34, 197, 94, 0.1); + border: 1px solid rgba(34, 197, 94, 0.3); + color: #4ade80; +} + +.sun-consistency-banner.inconsistent { + background: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.3); + color: #f87171; +} + +.sun-data-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; +} + +.sun-data-card { + padding: 1rem; + background: var(--color-bg-primary, #151a21); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 6px; +} + +.sun-data-card h5 { + margin: 0 0 0.75rem 0; + font-size: 0.8rem; + color: var(--color-text-secondary, #9ca3af); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.sun-data-value { + font-size: 1.5rem; + font-weight: 700; + color: var(--color-text-primary, #e5e7eb); +} + +.sun-data-unit { + font-size: 0.875rem; + color: var(--color-text-tertiary, #6b7280); + margin-left: 0.25rem; +} + +/* Modal Styles */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + padding: 1rem; +} + +.modal-content { + width: 100%; + max-width: 520px; + max-height: 90vh; + overflow-y: auto; + background: #1e2530; + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 12px; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem 1.25rem; + border-bottom: 1px solid var(--color-border, #3f4a5a); +} + +.modal-header h3 { + display: flex; + align-items: center; + gap: 0.5rem; + margin: 0; + font-size: 1.1rem; + font-weight: 600; + color: #f3f4f6; +} + +.modal-body { + padding: 1.25rem; +} + +.form-group { + margin-bottom: 1rem; +} + +.form-group:last-child { + margin-bottom: 0; +} + +.form-group label { + display: block; + margin-bottom: 0.5rem; + font-size: 0.85rem; + font-weight: 500; + color: #d1d5db; +} + +.form-group select, +.form-group input { + width: 100%; + padding: 0.625rem 0.75rem; + background: #151a21; + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 6px; + font-size: 0.9rem; + color: #e5e7eb; +} + +.form-group select:focus, +.form-group input:focus { + outline: none; + border-color: var(--color-primary, #3b82f6); +} + +.form-hint { + display: block; + margin-top: 0.25rem; + font-size: 0.75rem; + color: #6b7280; +} + +.checkbox-group { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.checkbox-label { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: #d1d5db; + cursor: pointer; +} + +.checkbox-label input[type="checkbox"] { + width: auto; +} + +.modal-footer { + display: flex; + justify-content: flex-end; + gap: 0.75rem; + padding: 1rem 1.25rem; + border-top: 1px solid var(--color-border, #3f4a5a); + background: #1a1f28; +} + +/* Buttons */ +.btn { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 6px; + font-size: 0.875rem; + font-weight: 500; + cursor: pointer; + transition: all 0.15s ease; +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.btn-primary { + background: var(--color-primary, #3b82f6); + border-color: var(--color-primary, #3b82f6); + color: white; +} + +.btn-primary:hover:not(:disabled) { + background: var(--color-primary-hover, #2563eb); + border-color: var(--color-primary-hover, #2563eb); +} + +.btn-secondary { + background: var(--color-bg-secondary, #1e2530); + color: var(--color-text-primary, #e5e7eb); +} + +.btn-secondary:hover:not(:disabled) { + background: var(--color-bg-hover, #252d38); + border-color: var(--color-primary, #3b82f6); +} + +.btn-danger { + background: var(--arkham-error, #dc2626); + border-color: var(--arkham-error, #dc2626); + color: white; +} + +.btn-danger:hover:not(:disabled) { + background: #b91c1c; + border-color: #b91c1c; +} + +.btn-sm { + padding: 0.375rem 0.75rem; + font-size: 0.8rem; +} + +/* Animations */ +.spin { + animation: spin 1s linear infinite; +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +/* ========================================== + Additional Component Styles + ========================================== */ + +/* EXIF Panel */ +.exif-panel { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.exif-warnings { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.exif-warning-item { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem; + background: rgba(234, 179, 8, 0.1); + border: 1px solid rgba(234, 179, 8, 0.3); + border-radius: 4px; + font-size: 0.8rem; + color: #fbbf24; +} + +.exif-warning-item svg { + color: #fbbf24; + flex-shrink: 0; +} + +/* C2PA Panel */ +.c2pa-panel { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.c2pa-status-banner { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.75rem; + border-radius: 8px; + color: white; + font-weight: 600; +} + +.c2pa-errors { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.c2pa-error-item { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem; + background: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.3); + border-radius: 4px; + font-size: 0.8rem; + color: #f87171; +} + +.c2pa-error-item svg { + color: #f87171; + flex-shrink: 0; +} + +.c2pa-signer-date { + font-size: 0.75rem; + color: var(--color-text-tertiary, #6b7280); + margin-top: 0.25rem; +} + +.c2pa-trust-chain { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.25rem; + margin-top: 0.75rem; + padding: 0.5rem; + background: var(--color-bg-tertiary, #2a323e); + border-radius: 4px; + font-size: 0.75rem; +} + +.trust-chain-label { + color: var(--color-text-secondary, #9ca3af); + margin-right: 0.5rem; +} + +.trust-chain-item { + display: inline-flex; + align-items: center; + gap: 0.25rem; + color: var(--color-text-primary, #e5e7eb); +} + +.c2pa-ingredients-list { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.c2pa-ingredient-item { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.5rem; + background: var(--color-bg-primary, #151a21); + border-radius: 4px; +} + +.ingredient-thumb { + width: 48px; + height: 48px; + object-fit: cover; + border-radius: 4px; + background: var(--color-bg-tertiary, #2a323e); +} + +.ingredient-details { + flex: 1; +} + +.ingredient-title { + font-weight: 500; + color: var(--color-text-primary, #e5e7eb); +} + +.ingredient-meta { + font-size: 0.75rem; + color: var(--color-text-secondary, #9ca3af); +} + +.ingredient-relationship { + margin-left: 0.5rem; + padding: 0.125rem 0.375rem; + background: var(--color-bg-tertiary, #2a323e); + border-radius: 3px; + text-transform: capitalize; +} + +.c2pa-assertions-list { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.c2pa-assertion-item { + display: inline-flex; + align-items: center; + gap: 0.25rem; + padding: 0.375rem 0.625rem; + background: var(--color-bg-primary, #151a21); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 4px; + font-size: 0.75rem; +} + +.assertion-label { + color: var(--color-text-primary, #e5e7eb); +} + +.assertion-instance { + color: var(--color-text-tertiary, #6b7280); +} + +.c2pa-provenance-chain { + display: flex; + flex-direction: column; +} + +.provenance-step { + display: flex; + align-items: flex-start; + gap: 0.75rem; +} + +.provenance-marker { + display: flex; + flex-direction: column; + align-items: center; + width: 16px; +} + +.provenance-dot { + width: 8px; + height: 8px; + background: var(--color-primary, #3b82f6); + border-radius: 50%; +} + +.provenance-line { + width: 2px; + flex: 1; + min-height: 24px; + background: var(--color-border, #3f4a5a); +} + +.provenance-content { + flex: 1; + padding-bottom: 1rem; + font-size: 0.8rem; + color: var(--color-text-secondary, #9ca3af); +} + +.c2pa-multiple-notice { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem; + background: rgba(59, 130, 246, 0.1); + border: 1px solid rgba(59, 130, 246, 0.3); + border-radius: 4px; + font-size: 0.8rem; + color: #60a5fa; +} + +/* ELA Panel */ +.ela-result-banner { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 1rem; + border-radius: 8px; +} + +.ela-result-banner.clean { + background: rgba(34, 197, 94, 0.1); + border: 1px solid rgba(34, 197, 94, 0.3); + color: #4ade80; +} + +.ela-result-banner.warning { + background: rgba(234, 179, 8, 0.1); + border: 1px solid rgba(234, 179, 8, 0.3); + color: #fbbf24; +} + +.ela-result-text { + display: flex; + flex-direction: column; + gap: 0.125rem; +} + +.ela-result-title { + font-weight: 600; +} + +.ela-result-confidence { + font-size: 0.75rem; + opacity: 0.8; +} + +.ela-view-toggle { + display: flex; + gap: 0.5rem; + margin-bottom: 0.5rem; +} + +.view-toggle-btn { + display: flex; + align-items: center; + gap: 0.375rem; + padding: 0.375rem 0.75rem; + background: var(--color-bg-primary, #151a21); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 4px; + color: var(--color-text-secondary, #9ca3af); + font-size: 0.75rem; + cursor: pointer; + transition: all 0.15s ease; +} + +.view-toggle-btn:hover:not(:disabled) { + border-color: var(--color-primary, #3b82f6); + color: var(--color-text-primary, #e5e7eb); +} + +.view-toggle-btn.active { + background: var(--color-primary, #3b82f6); + border-color: var(--color-primary, #3b82f6); + color: white; +} + +.view-toggle-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.ela-images.ela-only { + grid-template-columns: 1fr; +} + +.ela-placeholder { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 2rem; + color: var(--color-text-tertiary, #6b7280); + font-size: 0.8rem; +} + +.quality-value { + min-width: 40px; + text-align: right; + font-weight: 600; + color: var(--color-text-primary, #e5e7eb); +} + +.region-info { + display: flex; + flex-direction: column; + gap: 0.125rem; +} + +.region-coords { + font-weight: 500; + color: var(--color-text-primary, #e5e7eb); +} + +.region-description { + font-size: 0.75rem; + color: var(--color-text-secondary, #9ca3af); +} + +.region-metrics { + display: flex; + gap: 0.75rem; + font-size: 0.75rem; + color: var(--color-text-tertiary, #6b7280); +} + +.ela-explanation { + display: flex; + gap: 0.5rem; + padding: 0.75rem; + background: var(--color-bg-tertiary, #2a323e); + border-radius: 6px; + font-size: 0.75rem; + color: var(--color-text-secondary, #9ca3af); +} + +.ela-explanation svg { + flex-shrink: 0; + color: var(--color-text-tertiary, #6b7280); +} + +.ela-explanation p { + margin: 0; + line-height: 1.5; +} + +/* Sun Position Panel */ +.sun-override-form { + padding: 1rem; + background: var(--color-bg-secondary, #1e2530); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 8px; +} + +.override-inputs { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.75rem; +} + +.override-input-group { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.override-input-group.full-width { + grid-column: span 2; +} + +.override-input-group label { + font-size: 0.75rem; + color: var(--color-text-secondary, #9ca3af); +} + +.override-input-group input { + padding: 0.5rem; + background: var(--color-bg-primary, #151a21); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 4px; + color: var(--color-text-primary, #e5e7eb); + font-size: 0.875rem; +} + +.override-input-group input:focus { + outline: none; + border-color: var(--color-primary, #3b82f6); +} + +.sun-inconsistencies { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.inconsistency-item { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem; + background: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.3); + border-radius: 4px; + font-size: 0.8rem; + color: #f87171; +} + +.inconsistency-item svg { + color: #f87171; + flex-shrink: 0; +} + +.consistency-title { + font-weight: 600; +} + +.consistency-confidence { + font-size: 0.8rem; + opacity: 0.8; +} + +.sun-location-data { + display: flex; + gap: 1rem; +} + +.location-coord { + display: flex; + gap: 0.25rem; +} + +.coord-label { + color: var(--color-text-tertiary, #6b7280); +} + +.coord-value { + font-family: monospace; + color: var(--color-text-primary, #e5e7eb); +} + +.claimed-time { + display: flex; + align-items: center; + gap: 0.375rem; + margin-top: 0.5rem; + font-size: 0.8rem; + color: var(--color-text-secondary, #9ca3af); +} + +.sun-position-data { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.sun-metric { + display: flex; + justify-content: space-between; + align-items: baseline; +} + +.metric-label { + font-size: 0.75rem; + color: var(--color-text-secondary, #9ca3af); +} + +.shadow-consistency { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 1rem; +} + +.consistency-label { + font-size: 0.8rem; + color: var(--color-text-secondary, #9ca3af); +} + +.consistency-bar { + flex: 1; + height: 8px; + background: var(--color-bg-tertiary, #2a323e); + border-radius: 4px; + overflow: hidden; +} + +.consistency-fill { + height: 100%; + transition: width 0.3s ease; +} + +.consistency-value { + font-weight: 600; + color: var(--color-text-primary, #e5e7eb); +} + +.detected-shadows h5 { + margin: 1rem 0 0.5rem; + font-size: 0.8rem; + color: var(--color-text-secondary, #9ca3af); +} + +.shadows-list { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.shadow-item { + display: flex; + justify-content: space-between; + padding: 0.5rem; + background: var(--color-bg-primary, #151a21); + border-radius: 4px; + font-size: 0.8rem; +} + +.shadow-id { + color: var(--color-text-primary, #e5e7eb); +} + +.shadow-direction, +.shadow-confidence { + color: var(--color-text-secondary, #9ca3af); +} + +/* Similar Images Panel */ +.similar-images-container { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.similar-stats { + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 0.5rem; +} + +.similar-stat { + display: flex; + flex-direction: column; + align-items: center; + padding: 0.75rem; + background: var(--color-bg-primary, #151a21); + border-radius: 6px; +} + +.similar-stat .stat-value { + font-size: 1.5rem; + font-weight: 700; + color: var(--color-text-primary, #e5e7eb); +} + +.similar-stat .stat-label { + font-size: 0.65rem; + color: var(--color-text-tertiary, #6b7280); + text-transform: uppercase; +} + +.similarity-badge { + position: absolute; + top: 0.5rem; + right: 0.5rem; + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-size: 0.7rem; + font-weight: 700; + color: white; +} + +.similar-image-thumb.placeholder { + display: flex; + align-items: center; + justify-content: center; + color: var(--color-text-tertiary, #6b7280); +} + +.selected-image-details { + padding: 1rem; + background: var(--color-bg-primary, #151a21); + border: 1px solid var(--color-primary, #3b82f6); + border-radius: 8px; +} + +.search-options { + padding: 1rem; + background: var(--color-bg-secondary, #1e2530); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 8px; +} + +.search-type-selector { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.search-type-selector label { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: var(--color-text-secondary, #9ca3af); + cursor: pointer; +} + +.search-type-selector label:hover { + color: var(--color-text-primary, #e5e7eb); +} + +.search-type-selector.inline { + flex-direction: row; +} + +.search-type-selector.inline select { + padding: 0.375rem 0.5rem; + background: var(--color-bg-primary, #151a21); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 4px; + color: var(--color-text-primary, #e5e7eb); + font-size: 0.8rem; +} + +.search-controls { + display: flex; + align-items: center; + gap: 0.75rem; + padding-top: 1rem; + border-top: 1px solid var(--color-border, #3f4a5a); +} + +/* Warnings Panel */ +.warnings-panel { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.warnings-summary { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem; + background: rgba(234, 179, 8, 0.1); + border: 1px solid rgba(234, 179, 8, 0.3); + border-radius: 8px; +} + +.summary-total { + display: flex; + align-items: center; + gap: 0.5rem; + font-weight: 600; + color: #fbbf24; +} + +.summary-breakdown { + display: flex; + gap: 1rem; + font-size: 0.8rem; +} + +.severity-count { + font-weight: 600; +} + +.findings-list { + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.finding-title-row { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.finding-recommendation { + display: flex; + align-items: flex-start; + gap: 0.5rem; + margin-top: 0.5rem; + padding: 0.5rem; + background: rgba(59, 130, 246, 0.1); + border-radius: 4px; + font-size: 0.75rem; + color: #60a5fa; +} + +.finding-recommendation svg { + flex-shrink: 0; + margin-top: 2px; +} + +.finding-evidence { + margin-top: 0.5rem; +} + +.finding-evidence details { + font-size: 0.75rem; +} + +.finding-evidence summary { + display: flex; + align-items: center; + gap: 0.375rem; + padding: 0.375rem; + background: var(--color-bg-tertiary, #2a323e); + border-radius: 4px; + color: var(--color-text-secondary, #9ca3af); + cursor: pointer; +} + +.finding-evidence summary:hover { + color: var(--color-text-primary, #e5e7eb); +} + +.finding-evidence pre { + margin: 0.5rem 0 0; + padding: 0.5rem; + background: var(--color-bg-primary, #151a21); + border-radius: 4px; + overflow-x: auto; + font-size: 0.7rem; + color: var(--color-text-secondary, #9ca3af); +} + +.severity-legend { + margin-top: 1rem; + padding-top: 1rem; + border-top: 1px solid var(--color-border, #3f4a5a); +} + +.severity-legend h5 { + margin: 0 0 0.5rem; + font-size: 0.75rem; + color: var(--color-text-tertiary, #6b7280); + text-transform: uppercase; +} + +.legend-items { + display: flex; + flex-wrap: wrap; + gap: 1rem; +} + +.legend-item { + display: flex; + align-items: center; + gap: 0.375rem; +} + +.legend-color { + width: 12px; + height: 12px; + border-radius: 2px; +} + +.legend-label { + font-size: 0.75rem; + color: var(--color-text-secondary, #9ca3af); +} + +/* Page Header */ +.page-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem; + border-bottom: 1px solid var(--color-border, #3f4a5a); +} + +.page-title { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.page-title svg { + color: var(--color-primary, #3b82f6); +} + +.page-title h1 { + margin: 0; + font-size: 1.5rem; + font-weight: 600; + color: var(--color-text-primary, #e5e7eb); +} + +.page-description { + margin: 0; + font-size: 0.875rem; + color: var(--color-text-secondary, #9ca3af); +} + +.page-actions { + display: flex; + gap: 0.5rem; +} + +/* Empty state for panels */ +.panel-empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.75rem; + padding: 2rem; + text-align: center; + color: var(--color-text-secondary, #9ca3af); +} + +.panel-empty svg { + color: var(--color-text-tertiary, #6b7280); +} + +.panel-empty p { + margin: 0; + font-size: 0.875rem; +} + +/* GPS Map placeholder */ +.gps-map-placeholder { + width: 100%; + height: 200px; + background: var(--color-bg-tertiary, #2a323e); + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + color: var(--color-text-tertiary, #6b7280); + font-size: 0.875rem; +} + +/* Mode Toggle */ +.mode-toggle { + display: flex; + gap: 0.5rem; + margin-bottom: 1rem; + padding: 0.25rem; + background: var(--color-bg-primary, #151a21); + border-radius: 8px; +} + +.mode-btn { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 0.625rem 1rem; + background: transparent; + border: none; + border-radius: 6px; + color: var(--color-text-secondary, #9ca3af); + font-size: 0.875rem; + font-weight: 500; + cursor: pointer; + transition: all 0.15s ease; +} + +.mode-btn:hover:not(:disabled) { + color: var(--color-text-primary, #e5e7eb); + background: var(--color-bg-hover, #252d38); +} + +.mode-btn.active { + background: var(--color-primary, #3b82f6); + color: white; +} + +.mode-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +/* Upload Dropzone */ +.upload-dropzone { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 1rem; + padding: 2rem; + border: 2px dashed var(--color-border, #3f4a5a); + border-radius: 8px; + background: var(--color-bg-primary, #151a21); + transition: all 0.15s ease; + min-height: 160px; +} + +.upload-dropzone:hover { + border-color: var(--color-primary, #3b82f6); + background: rgba(59, 130, 246, 0.05); +} + +.upload-dropzone.drag-active { + border-color: var(--color-primary, #3b82f6); + background: rgba(59, 130, 246, 0.1); + border-style: solid; +} + +.upload-dropzone.has-file { + border-color: var(--arkham-success, #22c55e); + border-style: solid; + background: rgba(34, 197, 94, 0.05); +} + +.dropzone-content { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + text-align: center; +} + +.dropzone-content svg { + color: var(--color-text-tertiary, #6b7280); +} + +.dropzone-content p { + margin: 0; + color: var(--color-text-secondary, #9ca3af); + font-size: 0.9rem; +} + +.supported-formats { + font-size: 0.75rem; + color: var(--color-text-tertiary, #6b7280); +} + +.selected-file { + display: flex; + align-items: center; + gap: 0.75rem; + width: 100%; + padding: 0.75rem; + background: var(--color-bg-secondary, #1e2530); + border-radius: 6px; +} + +.selected-file svg { + color: var(--arkham-success, #22c55e); + flex-shrink: 0; +} + +.selected-file .file-info { + flex: 1; + min-width: 0; +} + +.selected-file .file-name { + display: block; + font-weight: 500; + color: var(--color-text-primary, #e5e7eb); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.selected-file .file-size { + display: block; + font-size: 0.75rem; + color: var(--color-text-secondary, #9ca3af); +} + +.btn-ghost { + background: transparent; + border: 1px solid transparent; + color: var(--color-text-secondary, #9ca3af); +} + +.btn-ghost:hover:not(:disabled) { + background: var(--color-bg-hover, #252d38); + color: var(--color-text-primary, #e5e7eb); +} + +/* Reverse Image Search URLs */ +.reverse-search-urls { + padding: 1rem; + background: var(--color-bg-secondary, #1e2530); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 8px; +} + +.reverse-search-urls .panel-section-header { + margin-bottom: 1rem; +} + +.reverse-search-urls .section-subtitle { + margin-left: auto; + font-size: 0.75rem; + color: var(--color-text-tertiary, #6b7280); + font-weight: 400; +} + +.search-urls-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 0.75rem; +} + +.search-url-card { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.75rem; + background: var(--color-bg-primary, #151a21); + border: 1px solid var(--color-border, #3f4a5a); + border-radius: 6px; + text-decoration: none; + transition: all 0.15s ease; +} + +.search-url-card:hover { + border-color: var(--color-primary, #3b82f6); + background: rgba(59, 130, 246, 0.05); + transform: translateY(-1px); +} + +.search-url-card.url_search { + border-left: 3px solid var(--arkham-success, #22c55e); +} + +.search-url-card.upload_search { + border-left: 3px solid var(--arkham-warning, #eab308); +} + +.search-url-icon { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + background: var(--color-bg-tertiary, #2a323e); + border-radius: 6px; + color: var(--color-text-secondary, #9ca3af); +} + +.search-url-card:hover .search-url-icon { + color: var(--color-primary, #3b82f6); +} + +.search-url-info { + flex: 1; + min-width: 0; +} + +.search-url-engine { + display: block; + font-weight: 500; + font-size: 0.875rem; + color: var(--color-text-primary, #e5e7eb); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.search-url-type { + display: block; + font-size: 0.7rem; + color: var(--color-text-tertiary, #6b7280); +} + +.search-url-card.url_search .search-url-type { + color: var(--arkham-success, #22c55e); +} + +.search-url-card.upload_search .search-url-type { + color: var(--arkham-warning, #eab308); +} + +.search-url-card .external-icon { + color: var(--color-text-tertiary, #6b7280); + flex-shrink: 0; +} + +.search-url-card:hover .external-icon { + color: var(--color-primary, #3b82f6); +} + +.search-url-note { + display: flex; + align-items: center; + gap: 0.5rem; + margin-top: 0.75rem; + padding: 0.5rem 0.75rem; + background: rgba(234, 179, 8, 0.1); + border: 1px solid rgba(234, 179, 8, 0.2); + border-radius: 4px; + font-size: 0.75rem; + color: #fbbf24; +} + +.search-url-note svg { + flex-shrink: 0; +} + +/* Panel info message */ +.panel-info { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem; + background: rgba(59, 130, 246, 0.1); + border: 1px solid rgba(59, 130, 246, 0.2); + border-radius: 6px; + color: #60a5fa; + font-size: 0.875rem; +} + +.panel-info svg { + flex-shrink: 0; +} + +.panel-info p { + margin: 0; +} diff --git a/packages/arkham-shard-shell/src/pages/media-forensics/MediaForensicsPage.tsx b/packages/arkham-shard-shell/src/pages/media-forensics/MediaForensicsPage.tsx new file mode 100644 index 0000000..b51134f --- /dev/null +++ b/packages/arkham-shard-shell/src/pages/media-forensics/MediaForensicsPage.tsx @@ -0,0 +1,866 @@ +/** + * MediaForensicsPage - Media Forensics Analysis + * + * Main page for analyzing images and media files for authenticity, + * including EXIF metadata, C2PA credentials, ELA, and more. + */ + +import { useState, useCallback, useEffect, useMemo } from 'react'; +import { useSearchParams } from 'react-router-dom'; +import { Icon } from '../../components/common/Icon'; +import { useToast } from '../../context/ToastContext'; +import { useFetch } from '../../hooks/useFetch'; +import { AIAnalystButton } from '../../components/AIAnalyst'; + +import * as api from './api'; +import { ExifPanel } from './components/ExifPanel'; +import { C2PAPanel } from './components/C2PAPanel'; +import { ELAViewer } from './components/ELAViewer'; +import { SunPositionPanel } from './components/SunPositionPanel'; +import { SimilarImagesPanel } from './components/SimilarImagesPanel'; +import { WarningsPanel } from './components/WarningsPanel'; + +import type { + MediaAnalysis, + AnalysisStats, + AnalysisStatus, + VerificationStatus, +} from './types'; +import { + STATUS_LABELS, + STATUS_COLORS, + VERIFICATION_LABELS, + VERIFICATION_COLORS, + STATUS_OPTIONS, + VERIFICATION_OPTIONS, +} from './types'; + +import './MediaForensicsPage.css'; + +// Detail tab types +type DetailTab = 'exif' | 'c2pa' | 'ela' | 'sun' | 'similar' | 'warnings'; + +// Document item for selection +interface DocumentItem { + id: string; + filename: string; + file_type: string; + status: string; +} + +export function MediaForensicsPage() { + const [searchParams, setSearchParams] = useSearchParams(); + const { toast } = useToast(); + + // State + const [analyses, setAnalyses] = useState([]); + const [selectedAnalysis, setSelectedAnalysis] = useState(null); + const [detailTab, setDetailTab] = useState('exif'); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + // Filters + const [statusFilter, setStatusFilter] = useState(''); + const [verificationFilter, setVerificationFilter] = useState(''); + const [offset, setOffset] = useState(0); + const [hasMore, setHasMore] = useState(false); + + // Modal states + const [showAnalyzeModal, setShowAnalyzeModal] = useState(false); + const [selectedDocId, setSelectedDocId] = useState(''); + const [analyzing, setAnalyzing] = useState(false); + const [analyzeOptions, setAnalyzeOptions] = useState({ + run_ela: true, + run_sun_position: false, + run_similar_search: false, + }); + + // Upload states + const [uploadMode, setUploadMode] = useState<'select' | 'upload'>('upload'); + const [selectedFile, setSelectedFile] = useState(null); + const [dragActive, setDragActive] = useState(false); + + // Fetch stats + const { data: statsData, refetch: refetchStats } = useFetch<{ stats: AnalysisStats }>( + '/api/media-forensics/stats' + ); + const stats = statsData?.stats; + + // Fetch documents for selection + const { data: documentsData } = useFetch<{ + items: DocumentItem[]; + }>('/api/documents/items?status=processed&limit=100&file_type=image'); + const documents = documentsData?.items || []; + + // Fetch analyses + const fetchAnalyses = useCallback(async () => { + setLoading(true); + setError(null); + try { + const response = await api.listAnalyses({ + offset, + limit: 20, + status: statusFilter || undefined, + verification_status: verificationFilter || undefined, + }); + setAnalyses(response.items); + setHasMore(response.has_more); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load analyses'); + } finally { + setLoading(false); + } + }, [offset, statusFilter, verificationFilter]); + + useEffect(() => { + fetchAnalyses(); + }, [fetchAnalyses]); + + // Reset offset when filters change + useEffect(() => { + setOffset(0); + }, [statusFilter, verificationFilter]); + + // Handle URL params for direct linking + useEffect(() => { + const analysisId = searchParams.get('id'); + if (analysisId && analyses.length > 0) { + const analysis = analyses.find((a) => a.id === analysisId); + if (analysis) { + setSelectedAnalysis(analysis); + } + } + }, [searchParams, analyses]); + + const handleSelectAnalysis = useCallback( + (analysis: MediaAnalysis) => { + setSelectedAnalysis(analysis); + setDetailTab('exif'); + setSearchParams({ id: analysis.id }); + }, + [setSearchParams] + ); + + const handleCloseDetail = useCallback(() => { + setSelectedAnalysis(null); + setSearchParams({}); + }, [setSearchParams]); + + const handleAnalyzeDocument = async () => { + // Validate input based on mode + if (uploadMode === 'upload' && !selectedFile) { + toast.error('Please select a file to upload'); + return; + } + if (uploadMode === 'select' && !selectedDocId) { + toast.error('Please select a document to analyze'); + return; + } + + setAnalyzing(true); + try { + let analysis: MediaAnalysis | undefined; + + if (uploadMode === 'upload' && selectedFile) { + // Upload file directly + analysis = await api.uploadAndAnalyze(selectedFile, { + run_ela: analyzeOptions.run_ela, + }); + toast.success('File uploaded and analyzed successfully'); + } else { + // Analyze existing document + const response = await api.analyzeDocument({ + doc_id: selectedDocId, + ...analyzeOptions, + }); + analysis = response.analysis; + toast.success('Analysis started successfully'); + } + + setShowAnalyzeModal(false); + setSelectedDocId(''); + setSelectedFile(null); + fetchAnalyses(); + refetchStats(); + + // Select the new analysis + if (analysis) { + setSelectedAnalysis(analysis); + setSearchParams({ id: analysis.id }); + } + } catch (err) { + toast.error(err instanceof Error ? err.message : 'Failed to start analysis'); + } finally { + setAnalyzing(false); + } + }; + + // Handle file drop + const handleDrop = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setDragActive(false); + + const files = e.dataTransfer.files; + if (files.length > 0) { + const file = files[0]; + // Validate file type + const validTypes = ['image/jpeg', 'image/png', 'image/tiff', 'image/webp', 'image/gif', 'image/bmp']; + if (validTypes.includes(file.type) || /\.(jpg|jpeg|png|tiff|tif|webp|gif|bmp)$/i.test(file.name)) { + setSelectedFile(file); + } else { + toast.error('Unsupported file type. Please upload JPEG, PNG, TIFF, WebP, GIF, or BMP.'); + } + } + }, [toast]); + + const handleDragOver = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setDragActive(true); + }, []); + + const handleDragLeave = useCallback((e: React.DragEvent) => { + e.preventDefault(); + setDragActive(false); + }, []); + + const handleFileSelect = useCallback((e: React.ChangeEvent) => { + const files = e.target.files; + if (files && files.length > 0) { + setSelectedFile(files[0]); + } + }, []); + + const handleDeleteAnalysis = async (analysisId: string) => { + if (!confirm('Are you sure you want to delete this analysis?')) { + return; + } + + try { + await api.deleteAnalysis(analysisId); + toast.success('Analysis deleted'); + fetchAnalyses(); + refetchStats(); + if (selectedAnalysis?.id === analysisId) { + setSelectedAnalysis(null); + setSearchParams({}); + } + } catch (err) { + toast.error(err instanceof Error ? err.message : 'Failed to delete analysis'); + } + }; + + const handleReanalyze = async (analysisId: string) => { + try { + const response = await api.reanalyze(analysisId); + toast.success('Re-analysis started'); + fetchAnalyses(); + if (response.analysis) { + setSelectedAnalysis(response.analysis); + } + } catch (err) { + toast.error(err instanceof Error ? err.message : 'Failed to start re-analysis'); + } + }; + + const getVerificationIcon = (status: VerificationStatus): string => { + switch (status) { + case 'verified': + return 'CheckCircle'; + case 'flagged': + return 'AlertTriangle'; + case 'tampered': + return 'XCircle'; + default: + return 'HelpCircle'; + } + }; + + const formatFileSize = (bytes: number): string => { + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; + }; + + const formatDate = (dateStr: string): string => { + try { + return new Date(dateStr).toLocaleString(); + } catch { + return dateStr; + } + }; + + // Build AI Analyst context from selected analysis + const aiAnalystContext = useMemo(() => { + if (!selectedAnalysis) return {}; + + return { + filename: selectedAnalysis.filename, + file_type: selectedAnalysis.file_type, + file_size: selectedAnalysis.file_size, + verification_status: selectedAnalysis.verification_status, + integrity_status: selectedAnalysis.integrity_status, + findings_count: selectedAnalysis.findings_count, + critical_findings: selectedAnalysis.critical_findings, + exif_data: selectedAnalysis.exif_data, + c2pa_data: selectedAnalysis.c2pa_data, + ela_result: selectedAnalysis.ela_result, + perceptual_hashes: selectedAnalysis.perceptual_hashes, + analyzed_at: selectedAnalysis.analyzed_at, + }; + }, [selectedAnalysis]); + + if (loading && analyses.length === 0) { + return ( +

+
+ + Loading analyses... +
+
+ ); + } + + if (error && analyses.length === 0) { + return ( +
+
+ +

Failed to load analyses

+

{error}

+ +
+
+ ); + } + + return ( +
+
+
+ +
+

Media Forensics

+

+ Analyze images for authenticity, metadata, and manipulation +

+
+
+
+ +
+
+ + {/* Stats Dashboard */} + {stats && ( +
+
+ +
{stats.total_analyses}
+
Total Analyses
+
+
+ +
{stats.with_c2pa}
+
With C2PA
+
+
+ +
{stats.with_findings}
+
With Findings
+
+
+ +
{stats.critical_findings_total}
+
Critical Issues
+
+
+ )} + + {/* Filters */} +
+
+ + +
+ +
+ + +
+ + +
+ +
+ {/* Analysis List */} +
+ {analyses.length === 0 ? ( +
+ +

No Analyses Found

+

Analyze an image to detect manipulation and verify authenticity.

+ +
+ ) : ( +
+ {analyses.map((analysis) => ( +
handleSelectAnalysis(analysis)} + > + {selectedAnalysis?.id === analysis.id && ( +
+ )} + +
+
+ +
+ {analysis.filename} + + {analysis.file_type} - {formatFileSize(analysis.file_size)} + +
+
+
+ + {STATUS_LABELS[analysis.status as AnalysisStatus]} + + + + {VERIFICATION_LABELS[analysis.verification_status as VerificationStatus]} + +
+
+ +
+ {analysis.exif_data && ( + + + EXIF + + )} + {analysis.c2pa_data?.has_manifest && ( + + + C2PA + + )} + {analysis.ela_result && ( + + + ELA + + )} + {analysis.exif_data?.gps?.latitude && ( + + + GPS + + )} +
+ + {analysis.findings_count > 0 && ( +
+ + + {analysis.findings_count} finding{analysis.findings_count !== 1 ? 's' : ''} + {analysis.critical_findings > 0 && ( + + ({analysis.critical_findings} critical) + + )} + +
+ )} + +
+ + + {formatDate(analysis.analyzed_at)} + +
+
+ ))} +
+ )} + + {/* Pagination */} + {(offset > 0 || hasMore) && ( +
+ + +
+ )} +
+ + {/* Detail Panel */} + {selectedAnalysis && ( +
+
+

{selectedAnalysis.filename}

+
+ + + + +
+
+ + {/* Verification Status Banner */} +
+ + + {VERIFICATION_LABELS[selectedAnalysis.verification_status as VerificationStatus]} + +
+ + {/* Detail Tabs */} +
+ + + + + + +
+ + {/* Tab Content */} +
+ {detailTab === 'exif' && } + {detailTab === 'c2pa' && } + {detailTab === 'ela' && ( + + )} + {detailTab === 'sun' && } + {detailTab === 'similar' && ( + + )} + {detailTab === 'warnings' && } +
+
+ )} +
+ + {/* Analyze Modal */} + {showAnalyzeModal && ( +
setShowAnalyzeModal(false)}> +
e.stopPropagation()}> +
+

+ + New Media Analysis +

+ +
+
+ {/* Mode Toggle */} +
+ + +
+ + {uploadMode === 'upload' ? ( +
+ +
+ {selectedFile ? ( +
+ +
+ {selectedFile.name} + + {(selectedFile.size / 1024).toFixed(1)} KB + +
+ +
+ ) : ( +
+ +

Drag & drop an image here, or click to browse

+ + Supports: JPEG, PNG, TIFF, WebP, GIF, BMP + +
+ )} + + {!selectedFile && ( + + )} +
+
+ ) : ( +
+ + + {documents.length === 0 && ( + + No processed image documents found. Use "Upload File" to add new images. + + )} +
+ )} + +
+ +
+ + {uploadMode === 'select' && ( + <> + + + + )} +
+
+
+
+ + +
+
+
+ )} +
+ ); +} + +export default MediaForensicsPage; diff --git a/packages/arkham-shard-shell/src/pages/media-forensics/api.ts b/packages/arkham-shard-shell/src/pages/media-forensics/api.ts new file mode 100644 index 0000000..f5778f9 --- /dev/null +++ b/packages/arkham-shard-shell/src/pages/media-forensics/api.ts @@ -0,0 +1,320 @@ +/** + * Media Forensics API Service + * + * API client for the Media Forensics shard backend. + */ + +import type { + MediaAnalysis, + AnalysisListResponse, + AnalyzeDocumentRequest, + AnalyzeDocumentResponse, + GenerateELARequest, + GenerateELAResponse, + SunPositionRequest, + SunPositionResponse, + FindSimilarRequest, + FindSimilarResponse, + StatsResponse, + C2PASupportResponse, + ELAResult, +} from './types'; + +const API_PREFIX = '/api/media-forensics'; + +// Generic fetch wrapper with error handling +async function fetchAPI(endpoint: string, options?: RequestInit): Promise { + const response = await fetch(`${API_PREFIX}${endpoint}`, { + headers: { + 'Content-Type': 'application/json', + ...options?.headers, + }, + ...options, + }); + + if (!response.ok) { + const error = await response.json().catch(() => ({ detail: response.statusText })); + throw new Error(error.detail || error.message || `HTTP ${response.status}`); + } + + return response.json(); +} + +// ============================================ +// Analysis Operations +// ============================================ + +/** + * Get list of media analyses with optional filters + */ +export async function listAnalyses(filters?: { + offset?: number; + limit?: number; + status?: string; + verification_status?: string; + has_findings?: boolean; + doc_id?: string; +}): Promise { + const params = new URLSearchParams(); + if (filters?.offset !== undefined) params.set('offset', filters.offset.toString()); + if (filters?.limit !== undefined) params.set('limit', filters.limit.toString()); + if (filters?.status) params.set('status', filters.status); + if (filters?.verification_status) params.set('verification_status', filters.verification_status); + if (filters?.has_findings !== undefined) params.set('has_findings', filters.has_findings.toString()); + if (filters?.doc_id) params.set('doc_id', filters.doc_id); + + const query = params.toString(); + return fetchAPI(`/analyses${query ? `?${query}` : ''}`); +} + +/** + * Get a single analysis by ID + */ +export async function getAnalysis(analysisId: string): Promise<{ analysis: MediaAnalysis }> { + return fetchAPI<{ analysis: MediaAnalysis }>(`/analyses/${analysisId}`); +} + +/** + * Get analysis for a specific document + */ +export async function getDocumentAnalysis(docId: string): Promise<{ analysis: MediaAnalysis | null }> { + return fetchAPI<{ analysis: MediaAnalysis | null }>(`/document/${docId}`); +} + +/** + * Analyze a document for media forensics + */ +export async function analyzeDocument(request: AnalyzeDocumentRequest): Promise { + return fetchAPI('/analyze', { + method: 'POST', + body: JSON.stringify(request), + }); +} + +/** + * Upload and analyze an image file directly + */ +export async function uploadAndAnalyze( + file: File, + options?: { run_ela?: boolean } +): Promise { + const formData = new FormData(); + formData.append('file', file); + + const params = new URLSearchParams(); + if (options?.run_ela) params.set('run_ela', 'true'); + + const query = params.toString(); + const response = await fetch(`${API_PREFIX}/upload${query ? `?${query}` : ''}`, { + method: 'POST', + body: formData, + // Note: Don't set Content-Type header - browser sets it with boundary for FormData + }); + + if (!response.ok) { + const error = await response.json().catch(() => ({ detail: response.statusText })); + throw new Error(error.detail || error.message || `HTTP ${response.status}`); + } + + return response.json(); +} + +/** + * Re-analyze an existing analysis + */ +export async function reanalyze(analysisId: string): Promise { + return fetchAPI(`/${analysisId}/reanalyze`, { + method: 'POST', + }); +} + +/** + * Delete an analysis + */ +export async function deleteAnalysis(analysisId: string): Promise<{ success: boolean }> { + return fetchAPI<{ success: boolean }>(`/${analysisId}`, { + method: 'DELETE', + }); +} + +// ============================================ +// ELA (Error Level Analysis) Operations +// ============================================ + +/** + * Generate ELA for an analysis + */ +export async function generateELA(request: GenerateELARequest): Promise { + return fetchAPI('/ela', { + method: 'POST', + body: JSON.stringify(request), + }); +} + +/** + * Get ELA result for an analysis + */ +export async function getELA(analysisId: string): Promise<{ result: ELAResult | null }> { + return fetchAPI<{ result: ELAResult | null }>(`/${analysisId}/ela`); +} + +/** + * Regenerate ELA with different quality settings + */ +export async function regenerateELA( + analysisId: string, + qualityLevel: number +): Promise { + return fetchAPI(`/${analysisId}/ela/regenerate`, { + method: 'POST', + body: JSON.stringify({ quality_level: qualityLevel }), + }); +} + +// ============================================ +// Sun Position Operations +// ============================================ + +/** + * Calculate sun position verification + */ +export async function getSunPosition(request: SunPositionRequest): Promise { + return fetchAPI('/sun-position', { + method: 'POST', + body: JSON.stringify(request), + }); +} + +/** + * Get existing sun position analysis + */ +export async function getSunPositionResult( + analysisId: string +): Promise<{ result: SunPositionResponse['result'] | null }> { + return fetchAPI<{ result: SunPositionResponse['result'] | null }>(`/${analysisId}/sun-position`); +} + +// ============================================ +// Similar Images Operations +// ============================================ + +/** + * Find similar images + */ +export async function findSimilar(request: FindSimilarRequest): Promise { + return fetchAPI('/similar', { + method: 'POST', + body: JSON.stringify(request), + }); +} + +/** + * Get existing similar images search results + */ +export async function getSimilarResults( + analysisId: string +): Promise<{ result: FindSimilarResponse['result'] | null }> { + return fetchAPI<{ result: FindSimilarResponse['result'] | null }>(`/${analysisId}/similar`); +} + +// ============================================ +// Statistics & Support +// ============================================ + +/** + * Get media forensics statistics + */ +export async function getStats(): Promise { + return fetchAPI('/stats'); +} + +/** + * Check if C2PA is supported + */ +export async function checkC2PASupport(): Promise { + return fetchAPI('/c2pa/supported'); +} + +// ============================================ +// Notes & Findings Operations +// ============================================ + +/** + * Add a note to an analysis + */ +export async function addNote( + analysisId: string, + content: string, + author: string +): Promise<{ success: boolean }> { + return fetchAPI<{ success: boolean }>(`/${analysisId}/notes`, { + method: 'POST', + body: JSON.stringify({ content, author }), + }); +} + +/** + * Update verification status + */ +export async function updateVerificationStatus( + analysisId: string, + status: string, + reason?: string +): Promise<{ analysis: MediaAnalysis }> { + return fetchAPI<{ analysis: MediaAnalysis }>(`/${analysisId}/verification`, { + method: 'PUT', + body: JSON.stringify({ status, reason }), + }); +} + +/** + * Dismiss a finding + */ +export async function dismissFinding( + analysisId: string, + findingId: string, + reason: string, + dismissedBy: string +): Promise<{ success: boolean }> { + return fetchAPI<{ success: boolean }>(`/${analysisId}/findings/${findingId}/dismiss`, { + method: 'POST', + body: JSON.stringify({ reason, dismissed_by: dismissedBy }), + }); +} + +// ============================================ +// Batch Operations +// ============================================ + +/** + * Analyze multiple documents + */ +export async function batchAnalyze( + docIds: string[], + options?: { + run_ela?: boolean; + run_sun_position?: boolean; + run_similar_search?: boolean; + } +): Promise<{ job_id: string; queued_count: number }> { + return fetchAPI<{ job_id: string; queued_count: number }>('/batch/analyze', { + method: 'POST', + body: JSON.stringify({ doc_ids: docIds, ...options }), + }); +} + +/** + * Get batch job status + */ +export async function getBatchStatus( + jobId: string +): Promise<{ + job_id: string; + status: string; + total: number; + completed: number; + failed: number; + results: Array<{ doc_id: string; analysis_id: string | null; error: string | null }>; +}> { + return fetchAPI(`/batch/${jobId}`); +} diff --git a/packages/arkham-shard-shell/src/pages/media-forensics/components/C2PAPanel.tsx b/packages/arkham-shard-shell/src/pages/media-forensics/components/C2PAPanel.tsx new file mode 100644 index 0000000..a79f50e --- /dev/null +++ b/packages/arkham-shard-shell/src/pages/media-forensics/components/C2PAPanel.tsx @@ -0,0 +1,304 @@ +/** + * C2PAPanel - Display Content Credentials (C2PA) information + */ + +import { Icon } from '../../../components/common/Icon'; +import type { MediaAnalysis, C2PAManifest, C2PAAction } from '../types'; +import { C2PA_VALIDATION_LABELS, C2PA_VALIDATION_COLORS } from '../types'; + +interface C2PAPanelProps { + analysis: MediaAnalysis; +} + +export function C2PAPanel({ analysis }: C2PAPanelProps) { + const { c2pa_data } = analysis; + + if (!c2pa_data || !c2pa_data.has_manifest) { + return ( +
+ +

No C2PA Content Credentials found

+

+ This image does not contain Content Authenticity Initiative (CAI) credentials +

+
+ ); + } + + const activeManifest = + c2pa_data.active_manifest_index !== null + ? c2pa_data.manifests[c2pa_data.active_manifest_index] + : c2pa_data.manifests[0]; + + const formatDate = (dateStr: string | null): string => { + if (!dateStr) return 'N/A'; + try { + return new Date(dateStr).toLocaleString(); + } catch { + return dateStr; + } + }; + + const getActionIcon = (action: string): string => { + const actionLower = action.toLowerCase(); + if (actionLower.includes('create') || actionLower.includes('captured')) return 'Camera'; + if (actionLower.includes('edit') || actionLower.includes('modified')) return 'Edit3'; + if (actionLower.includes('crop') || actionLower.includes('resize')) return 'Crop'; + if (actionLower.includes('filter') || actionLower.includes('adjust')) return 'Sliders'; + if (actionLower.includes('publish') || actionLower.includes('export')) return 'Upload'; + if (actionLower.includes('sign')) return 'Key'; + return 'Activity'; + }; + + const renderManifest = (manifest: C2PAManifest, index: number) => ( +
+ {/* Signer Information */} + {manifest.signer && ( +
+
+ +

Signed By

+
+
+
+ +
+
+
{manifest.signer.name}
+ {manifest.signer.organization && ( +
{manifest.signer.organization}
+ )} + {manifest.signer.issued_date && ( +
+ Issued: {formatDate(manifest.signer.issued_date)} +
+ )} +
+
+ {C2PA_VALIDATION_LABELS[manifest.signer.validation_status]} +
+
+ + {/* Trust Chain */} + {manifest.signer.trust_chain && manifest.signer.trust_chain.length > 0 && ( +
+ Trust Chain: + {manifest.signer.trust_chain.map((cert, idx) => ( + + {cert} + {idx < manifest.signer!.trust_chain.length - 1 && ( + + )} + + ))} +
+ )} +
+ )} + + {/* Manifest Info */} +
+
+ +

Manifest Details

+
+
+ {manifest.title && ( +
+ Title + {manifest.title} +
+ )} + {manifest.format && ( +
+ Format + {manifest.format} +
+ )} + {manifest.claim_generator && ( +
+ Generated By + {manifest.claim_generator} +
+ )} + {manifest.signature_date && ( +
+ Signed + {formatDate(manifest.signature_date)} +
+ )} + {manifest.instance_id && ( +
+ Instance ID + + {manifest.instance_id} + +
+ )} +
+
+ + {/* Actions History */} + {manifest.actions && manifest.actions.length > 0 && ( +
+
+ +

Edit History ({manifest.actions.length} actions)

+
+
+ {manifest.actions.map((action: C2PAAction, idx: number) => ( +
+
+ +
+
+
{action.action}
+
+ {action.software_agent && {action.software_agent}} + {action.when && - {formatDate(action.when)}} +
+
+
+ ))} +
+
+ )} + + {/* Ingredients (Parent Assets) */} + {manifest.ingredients && manifest.ingredients.length > 0 && ( +
+
+ +

Source Assets ({manifest.ingredients.length})

+
+
+ {manifest.ingredients.map((ingredient, idx) => ( +
+ {ingredient.thumbnail && ( + {ingredient.title} + )} +
+
{ingredient.title}
+
+ {ingredient.format} + {ingredient.relationship} +
+
+
+ ))} +
+
+ )} + + {/* Assertions */} + {manifest.assertions && manifest.assertions.length > 0 && ( +
+
+ +

Assertions ({manifest.assertions.length})

+
+
+ {manifest.assertions.map((assertion, idx) => ( +
+
{assertion.label}
+ {assertion.instance !== undefined && ( + #{assertion.instance} + )} +
+ ))} +
+
+ )} +
+ ); + + return ( +
+ {/* Validation Status Banner */} +
+ + + Content Credentials: {C2PA_VALIDATION_LABELS[c2pa_data.validation_status]} + +
+ + {/* Validation Errors */} + {c2pa_data.validation_errors && c2pa_data.validation_errors.length > 0 && ( +
+ {c2pa_data.validation_errors.map((error, idx) => ( +
+ + {error} +
+ ))} +
+ )} + + {/* Provenance Chain */} + {c2pa_data.provenance_chain && c2pa_data.provenance_chain.length > 0 && ( +
+
+ +

Provenance Chain

+
+
+ {c2pa_data.provenance_chain.map((step, idx) => ( +
+
+
+ {idx < c2pa_data.provenance_chain.length - 1 && ( +
+ )} +
+
{step}
+
+ ))} +
+
+ )} + + {/* Manifests */} + {activeManifest && renderManifest(activeManifest, 0)} + + {/* Multiple Manifests Notice */} + {c2pa_data.manifests.length > 1 && ( +
+ + + This image contains {c2pa_data.manifests.length} manifests. Showing active manifest. + +
+ )} +
+ ); +} + +export default C2PAPanel; diff --git a/packages/arkham-shard-shell/src/pages/media-forensics/components/ELAViewer.tsx b/packages/arkham-shard-shell/src/pages/media-forensics/components/ELAViewer.tsx new file mode 100644 index 0000000..83f7ea9 --- /dev/null +++ b/packages/arkham-shard-shell/src/pages/media-forensics/components/ELAViewer.tsx @@ -0,0 +1,287 @@ +/** + * ELAViewer - Error Level Analysis side-by-side viewer with controls + */ + +import { useState, useCallback } from 'react'; +import { Icon } from '../../../components/common/Icon'; +import { useToast } from '../../../context/ToastContext'; +import * as api from '../api'; +import type { MediaAnalysis, ELAResult } from '../types'; + +interface ELAViewerProps { + analysis: MediaAnalysis; + onRegenerate?: () => void; +} + +export function ELAViewer({ analysis, onRegenerate }: ELAViewerProps) { + const { toast } = useToast(); + const [elaResult, setElaResult] = useState(analysis.ela_result); + const [qualityLevel, setQualityLevel] = useState(95); + const [generating, setGenerating] = useState(false); + const [viewMode, setViewMode] = useState<'side-by-side' | 'ela-only' | 'overlay'>('side-by-side'); + + const handleGenerateELA = useCallback(async () => { + setGenerating(true); + try { + const response = await api.generateELA({ + analysis_id: analysis.id, + quality_level: qualityLevel, + }); + setElaResult(response.result); + toast.success('ELA generated successfully'); + onRegenerate?.(); + } catch (err) { + toast.error(err instanceof Error ? err.message : 'Failed to generate ELA'); + } finally { + setGenerating(false); + } + }, [analysis.id, qualityLevel, toast, onRegenerate]); + + const handleRegenerateELA = useCallback(async () => { + setGenerating(true); + try { + const response = await api.regenerateELA(analysis.id, qualityLevel); + setElaResult(response.result); + toast.success('ELA regenerated with new quality level'); + onRegenerate?.(); + } catch (err) { + toast.error(err instanceof Error ? err.message : 'Failed to regenerate ELA'); + } finally { + setGenerating(false); + } + }, [analysis.id, qualityLevel, toast, onRegenerate]); + + // No ELA yet + if (!elaResult) { + return ( +
+
+ +

Error Level Analysis not yet performed

+

+ ELA can help detect edited or manipulated regions in JPEG images +

+
+ +
+ + setQualityLevel(Number(e.target.value))} + disabled={generating} + /> + {qualityLevel}% +
+ + +
+ ); + } + + return ( +
+ {/* Result Summary */} +
+ +
+ + {elaResult.is_potentially_edited + ? 'Potential Editing Detected' + : 'No Obvious Manipulation'} + + + Confidence: {(elaResult.confidence * 100).toFixed(0)}% + +
+
+ + {/* View Mode Toggle */} +
+ + + +
+ + {/* Image Comparison */} +
+ {viewMode !== 'ela-only' && ( +
+ Original + {/* Use the image serving endpoint for the original image */} + Original { + // Hide the image on error and show placeholder + const target = e.target as HTMLImageElement; + target.style.display = 'none'; + const placeholder = target.nextElementSibling; + if (placeholder) (placeholder as HTMLElement).style.display = 'flex'; + }} + /> +
+ + Original not available +
+
+ )} +
+ ELA (Q={elaResult.quality_level}) + {elaResult.ela_image_url ? ( + ELA + ) : elaResult.ela_image_base64 ? ( + ELA + ) : ( +
+ + ELA not available +
+ )} +
+
+ + {/* Intensity Metrics */} +
+
+ +

Intensity Metrics

+
+
+
+ Global Average + {elaResult.global_avg_intensity.toFixed(2)} +
+
+ Global Maximum + {elaResult.global_max_intensity.toFixed(2)} +
+
+ Quality Level + {elaResult.quality_level}% +
+
+ Suspicious Regions + {elaResult.suspicious_regions.length} +
+
+
+ + {/* Suspicious Regions */} + {elaResult.suspicious_regions.length > 0 && ( +
+
+ +

Suspicious Regions ({elaResult.suspicious_regions.length})

+
+
+ {elaResult.suspicious_regions.map((region, idx) => ( +
+
+ + Region {idx + 1}: ({region.x}, {region.y}) {region.width}x{region.height} + + {region.description} +
+
+ Avg: {region.avg_intensity.toFixed(1)} + Max: {region.max_intensity.toFixed(1)} +
+
+ ))} +
+
+ )} + + {/* Regenerate Controls */} +
+ + setQualityLevel(Number(e.target.value))} + disabled={generating} + /> + {qualityLevel}% + +
+ + {/* ELA Explanation */} +
+ +

+ How to read ELA: Areas with high intensity (bright spots) have been + saved at a different quality level than the rest of the image, which may indicate + manipulation. Uniform intensity usually indicates an unedited image. +

+
+
+ ); +} + +export default ELAViewer; diff --git a/packages/arkham-shard-shell/src/pages/media-forensics/components/ExifPanel.tsx b/packages/arkham-shard-shell/src/pages/media-forensics/components/ExifPanel.tsx new file mode 100644 index 0000000..5c7ced1 --- /dev/null +++ b/packages/arkham-shard-shell/src/pages/media-forensics/components/ExifPanel.tsx @@ -0,0 +1,351 @@ +/** + * ExifPanel - Display EXIF metadata in organized grid + */ + +import { Icon } from '../../../components/common/Icon'; +import type { MediaAnalysis } from '../types'; + +interface ExifPanelProps { + analysis: MediaAnalysis; +} + +export function ExifPanel({ analysis }: ExifPanelProps) { + const { exif_data } = analysis; + + if (!exif_data) { + return ( +
+ +

No EXIF data available for this image

+

+ EXIF data may have been stripped or the image format does not support metadata +

+
+ ); + } + + const formatDate = (dateStr: string | null): string => { + if (!dateStr) return 'N/A'; + try { + return new Date(dateStr).toLocaleString(); + } catch { + return dateStr; + } + }; + + const formatGPS = (lat: number | null, lon: number | null): string => { + if (lat === null || lon === null) return 'N/A'; + const latDir = lat >= 0 ? 'N' : 'S'; + const lonDir = lon >= 0 ? 'E' : 'W'; + return `${Math.abs(lat).toFixed(6)}${latDir}, ${Math.abs(lon).toFixed(6)}${lonDir}`; + }; + + const { camera, image, gps, capture, timestamps, software } = exif_data; + + return ( +
+ {/* Warnings */} + {exif_data.warnings && exif_data.warnings.length > 0 && ( +
+
+ +

Warnings

+
+
+ {exif_data.warnings.map((warning, idx) => ( +
+ + {warning} +
+ ))} +
+
+ )} + + {/* Camera Information */} + {(camera.make || camera.model || camera.serial_number) && ( +
+
+ +

Camera Information

+
+
+ {camera.make && ( +
+ Make + {camera.make} +
+ )} + {camera.model && ( +
+ Model + {camera.model} +
+ )} + {camera.serial_number && ( +
+ Serial Number + {camera.serial_number} +
+ )} + {camera.lens_make && ( +
+ Lens Make + {camera.lens_make} +
+ )} + {camera.lens_model && ( +
+ Lens Model + {camera.lens_model} +
+ )} + {camera.lens_serial && ( +
+ Lens Serial + {camera.lens_serial} +
+ )} +
+
+ )} + + {/* Image Properties */} + {(image.width || image.height || image.color_space) && ( +
+
+ +

Image Properties

+
+
+ {image.width && image.height && ( +
+ Dimensions + + {image.width} x {image.height} px + +
+ )} + {image.color_space && ( +
+ Color Space + {image.color_space} +
+ )} + {image.bits_per_sample && ( +
+ Bit Depth + {image.bits_per_sample} bits +
+ )} + {image.compression && ( +
+ Compression + {image.compression} +
+ )} + {image.orientation && ( +
+ Orientation + {image.orientation} +
+ )} +
+
+ )} + + {/* GPS Location */} + {(gps.latitude !== null || gps.longitude !== null) && ( +
+
+ +

GPS Location

+
+
+
+ Coordinates + {formatGPS(gps.latitude, gps.longitude)} +
+ {gps.altitude !== null && ( +
+ Altitude + + {gps.altitude.toFixed(1)} m {gps.altitude_ref || ''} + +
+ )} + {gps.direction !== null && ( +
+ Direction + + {gps.direction.toFixed(1)}{gps.direction_ref || ''} + +
+ )} + {gps.gps_timestamp && ( +
+ GPS Time + {formatDate(gps.gps_timestamp)} +
+ )} + {gps.gps_date && ( +
+ GPS Date + {gps.gps_date} +
+ )} +
+
+ + + Map view: {gps.latitude?.toFixed(4)}, {gps.longitude?.toFixed(4)} + +
+
+ )} + + {/* Capture Settings */} + {(capture.exposure_time || capture.f_number || capture.iso) && ( +
+
+ +

Capture Settings

+
+
+ {capture.exposure_time && ( +
+ Exposure + {capture.exposure_time} +
+ )} + {capture.f_number !== null && ( +
+ Aperture + f/{capture.f_number} +
+ )} + {capture.iso !== null && ( +
+ ISO + {capture.iso} +
+ )} + {capture.focal_length !== null && ( +
+ Focal Length + + {capture.focal_length}mm + {capture.focal_length_35mm && ` (${capture.focal_length_35mm}mm eq.)`} + +
+ )} + {capture.exposure_mode && ( +
+ Exposure Mode + {capture.exposure_mode} +
+ )} + {capture.exposure_program && ( +
+ Program + {capture.exposure_program} +
+ )} + {capture.metering_mode && ( +
+ Metering + {capture.metering_mode} +
+ )} + {capture.white_balance && ( +
+ White Balance + {capture.white_balance} +
+ )} + {capture.flash && ( +
+ Flash + {capture.flash} +
+ )} +
+
+ )} + + {/* Timestamps */} + {(timestamps.datetime_original || timestamps.datetime_digitized || timestamps.datetime_modified) && ( +
+
+ +

Timestamps

+
+
+ {timestamps.datetime_original && ( +
+ Original Date + {formatDate(timestamps.datetime_original)} +
+ )} + {timestamps.datetime_digitized && ( +
+ Digitized + {formatDate(timestamps.datetime_digitized)} +
+ )} + {timestamps.datetime_modified && ( +
+ Modified + {formatDate(timestamps.datetime_modified)} +
+ )} + {timestamps.timezone_offset && ( +
+ Timezone + {timestamps.timezone_offset} +
+ )} +
+
+ )} + + {/* Software */} + {(software.software || software.processing_software || software.host_computer) && ( +
+
+ +

Software

+
+
+ {software.software && ( +
+ Software + {software.software} +
+ )} + {software.processing_software && ( +
+ Processing Software + {software.processing_software} +
+ )} + {software.host_computer && ( +
+ Host Computer + {software.host_computer} +
+ )} + {software.firmware && ( +
+ Firmware + {software.firmware} +
+ )} +
+
+ )} +
+ ); +} + +export default ExifPanel; diff --git a/packages/arkham-shard-shell/src/pages/media-forensics/components/SimilarImagesPanel.tsx b/packages/arkham-shard-shell/src/pages/media-forensics/components/SimilarImagesPanel.tsx new file mode 100644 index 0000000..8ad7fd4 --- /dev/null +++ b/packages/arkham-shard-shell/src/pages/media-forensics/components/SimilarImagesPanel.tsx @@ -0,0 +1,382 @@ +/** + * SimilarImagesPanel - Grid display of similar/duplicate images + */ + +import { useState, useCallback } from 'react'; +import { Icon } from '../../../components/common/Icon'; +import { useToast } from '../../../context/ToastContext'; +import * as api from '../api'; +import type { MediaAnalysis, SimilarImagesResult, SimilarImage } from '../types'; + +interface SimilarImagesPanelProps { + analysis: MediaAnalysis; + onSearch?: () => void; +} + +export function SimilarImagesPanel({ analysis, onSearch }: SimilarImagesPanelProps) { + const { toast } = useToast(); + const [result, setResult] = useState( + analysis.similar_images_result + ); + const [searching, setSearching] = useState(false); + const [searchType, setSearchType] = useState<'internal' | 'external' | 'both'>('internal'); + const [selectedImage, setSelectedImage] = useState(null); + + const handleSearch = useCallback(async () => { + setSearching(true); + try { + const response = await api.findSimilar({ + analysis_id: analysis.id, + search_type: searchType, + limit: 50, + }); + setResult(response.result); + toast.success( + `Found ${response.result.total_found} similar image${response.result.total_found !== 1 ? 's' : ''}` + ); + onSearch?.(); + } catch (err) { + toast.error(err instanceof Error ? err.message : 'Failed to search for similar images'); + } finally { + setSearching(false); + } + }, [analysis.id, searchType, toast, onSearch]); + + const getSimilarityTypeLabel = (type: SimilarImage['similarity_type']): string => { + switch (type) { + case 'exact': + return 'Exact Match'; + case 'near_duplicate': + return 'Near Duplicate'; + case 'visually_similar': + return 'Visually Similar'; + case 'content_similar': + return 'Content Similar'; + default: + return type; + } + }; + + const getSimilarityTypeColor = (type: SimilarImage['similarity_type']): string => { + switch (type) { + case 'exact': + return 'var(--arkham-error, #ef4444)'; + case 'near_duplicate': + return 'var(--arkham-warning, #f97316)'; + case 'visually_similar': + return 'var(--arkham-warning, #eab308)'; + case 'content_similar': + return 'var(--arkham-info, #3b82f6)'; + default: + return 'var(--color-text-muted)'; + } + }; + + // No search performed yet + if (!result) { + return ( +
+
+ +

Similar image search not yet performed

+

+ Search for visually similar or duplicate images in the document collection +

+
+ +
+
+ +

Search Options

+
+
+ + + +
+
+ + +
+ ); + } + + return ( +
+ {/* Summary Stats */} +
+
+ {result.total_found} + Total Found +
+
+ + {result.exact_matches} + + Exact Matches +
+
+ + {result.near_duplicates} + + Near Duplicates +
+
+ + {result.visually_similar} + + Visually Similar +
+
+ + {/* Search URLs for Manual Reverse Image Search */} + {result.search_urls && result.search_urls.length > 0 && ( +
+ )} + + {/* No Results */} + {result.similar_images.length === 0 && !result.search_urls?.length && ( +
+ +

No similar images found

+

+ This image appears to be unique in the searched collection +

+
+ )} + + {result.similar_images.length === 0 && (result.search_urls?.length ?? 0) > 0 && ( +
+ +

No automatic results found. Use the reverse image search links above to search manually.

+
+ )} + + {/* Results Grid */} + {result.similar_images.length > 0 && ( +
+ {result.similar_images.map((image) => ( +
setSelectedImage(selectedImage?.id === image.id ? null : image)} + > + {/* Thumbnail */} + {image.thumbnail_url ? ( + {image.filename} + ) : image.thumbnail_base64 ? ( + {image.filename} + ) : ( +
+ +
+ )} + + {/* Similarity Badge */} +
+ {Math.round(image.similarity_score * 100)}% +
+ + {/* Info */} +
+
+ {getSimilarityTypeLabel(image.similarity_type)} +
+
{image.filename}
+
+
+ ))} +
+ )} + + {/* Selected Image Details */} + {selectedImage && ( +
+
+ +

Match Details

+ +
+
+
+ Filename + {selectedImage.filename} +
+
+ Similarity + + {(selectedImage.similarity_score * 100).toFixed(1)}% + +
+
+ Type + + {getSimilarityTypeLabel(selectedImage.similarity_type)} + +
+ {selectedImage.source && ( +
+ Source + {selectedImage.source} +
+ )} + {selectedImage.match_details.hash_distance !== undefined && ( +
+ Hash Distance + {selectedImage.match_details.hash_distance} +
+ )} + {selectedImage.match_details.feature_similarity !== undefined && ( +
+ Feature Similarity + + {(selectedImage.match_details.feature_similarity * 100).toFixed(1)}% + +
+ )} +
+ Found At + + {new Date(selectedImage.found_at).toLocaleString()} + +
+
+
+ )} + + {/* Re-search Controls */} +
+
+ +
+ +
+
+ ); +} + +export default SimilarImagesPanel; diff --git a/packages/arkham-shard-shell/src/pages/media-forensics/components/SunPositionPanel.tsx b/packages/arkham-shard-shell/src/pages/media-forensics/components/SunPositionPanel.tsx new file mode 100644 index 0000000..83c89ed --- /dev/null +++ b/packages/arkham-shard-shell/src/pages/media-forensics/components/SunPositionPanel.tsx @@ -0,0 +1,382 @@ +/** + * SunPositionPanel - Sun position and shadow verification display + */ + +import { useState, useCallback } from 'react'; +import { Icon } from '../../../components/common/Icon'; +import { useToast } from '../../../context/ToastContext'; +import * as api from '../api'; +import type { MediaAnalysis, SunPositionResult, SunPositionRequest } from '../types'; + +interface SunPositionPanelProps { + analysis: MediaAnalysis; +} + +export function SunPositionPanel({ analysis }: SunPositionPanelProps) { + const { toast } = useToast(); + const [sunResult, setSunResult] = useState( + analysis.sun_position_result + ); + const [calculating, setCalculating] = useState(false); + const [overrideLocation, setOverrideLocation] = useState<{ lat: string; lon: string }>({ + lat: '', + lon: '', + }); + const [overrideTime, setOverrideTime] = useState(''); + + const hasGPS = analysis.exif_data?.gps?.latitude !== null; + + const handleCalculate = useCallback(async () => { + setCalculating(true); + try { + const request: SunPositionRequest = { + analysis_id: analysis.id, + }; + + // Add overrides if provided + if (overrideLocation.lat && overrideLocation.lon) { + request.override_location = { + lat: parseFloat(overrideLocation.lat), + lon: parseFloat(overrideLocation.lon), + }; + } + if (overrideTime) { + request.override_time = overrideTime; + } + + const response = await api.getSunPosition(request); + setSunResult(response.result); + toast.success('Sun position calculated'); + } catch (err) { + toast.error(err instanceof Error ? err.message : 'Failed to calculate sun position'); + } finally { + setCalculating(false); + } + }, [analysis.id, overrideLocation, overrideTime, toast]); + + // No GPS data + if (!hasGPS && !sunResult) { + return ( +
+
+ +

No GPS data available for sun position verification

+

+ Sun position analysis requires GPS coordinates and timestamp from the image metadata +

+
+ + {/* Manual Override Form */} +
+
+ +

Manual Coordinates

+
+

+ Enter coordinates manually to verify sun position for a claimed location and time. +

+
+
+ + + setOverrideLocation({ ...overrideLocation, lat: e.target.value }) + } + /> +
+
+ + + setOverrideLocation({ ...overrideLocation, lon: e.target.value }) + } + /> +
+
+ + setOverrideTime(e.target.value)} + /> +
+
+ +
+
+ ); + } + + // No result yet but has GPS + if (!sunResult) { + return ( +
+
+ +

Sun position verification not yet performed

+
+ +
+ ); + } + + const formatAngle = (angle: number): string => { + return `${angle.toFixed(2)}`; + }; + + return ( +
+ {/* Consistency Banner */} +
+ +
+
+ {sunResult.is_consistent + ? 'Sun Position Consistent' + : 'Sun Position Inconsistent'} +
+
+ Confidence: {(sunResult.confidence * 100).toFixed(0)}% +
+
+
+ + {/* Inconsistency Details */} + {!sunResult.is_consistent && sunResult.inconsistency_details.length > 0 && ( +
+ {sunResult.inconsistency_details.map((detail, idx) => ( +
+ + {detail} +
+ ))} +
+ )} + + {/* Claimed vs Calculated */} +
+ {/* Claimed Data */} +
+
+ + Claimed Location +
+ {sunResult.claimed_location ? ( +
+
+ Lat: + {sunResult.claimed_location.lat.toFixed(6)} +
+
+ Lon: + {sunResult.claimed_location.lon.toFixed(6)} +
+
+ ) : ( + Not available + )} + {sunResult.claimed_time && ( +
+ + {new Date(sunResult.claimed_time).toLocaleString()} +
+ )} +
+ + {/* Calculated Sun Position */} + {sunResult.calculated_sun_position && ( +
+
+ + Calculated Sun +
+
+
+ Azimuth + + {formatAngle(sunResult.calculated_sun_position.azimuth)} + (deg) + +
+
+ Altitude + + {formatAngle(sunResult.calculated_sun_position.altitude)} + (deg) + +
+
+
+ )} +
+ + {/* Shadow Analysis */} + {sunResult.shadow_analysis && ( +
+
+ +

Shadow Analysis

+
+ + {/* Consistency Score */} +
+ Shadow Consistency Score: +
+
0.7 + ? 'var(--arkham-success)' + : sunResult.shadow_analysis.consistency_score > 0.4 + ? 'var(--arkham-warning)' + : 'var(--arkham-error)', + }} + /> +
+ + {(sunResult.shadow_analysis.consistency_score * 100).toFixed(0)}% + +
+ + {/* Average Shadow Direction */} + {sunResult.shadow_analysis.average_shadow_direction !== null && ( +
+ Average Shadow Direction + + {sunResult.shadow_analysis.average_shadow_direction.toFixed(1)} (degrees) + +
+ )} + + {/* Detected Shadows */} + {sunResult.shadow_analysis.detected_shadows.length > 0 && ( +
+
Detected Shadows ({sunResult.shadow_analysis.detected_shadows.length})
+
+ {sunResult.shadow_analysis.detected_shadows.map((shadow, idx) => ( +
+ {shadow.object_id} + + {shadow.shadow_direction.toFixed(1)} (deg) + + + {(shadow.confidence * 100).toFixed(0)}% + +
+ ))} +
+
+ )} +
+ )} + + {/* Recalculate with Overrides */} +
+
+ +

Recalculate with Custom Values

+
+
+
+ + + setOverrideLocation({ ...overrideLocation, lat: e.target.value }) + } + /> +
+
+ + + setOverrideLocation({ ...overrideLocation, lon: e.target.value }) + } + /> +
+
+ + setOverrideTime(e.target.value)} + /> +
+
+ +
+
+ ); +} + +export default SunPositionPanel; diff --git a/packages/arkham-shard-shell/src/pages/media-forensics/components/WarningsPanel.tsx b/packages/arkham-shard-shell/src/pages/media-forensics/components/WarningsPanel.tsx new file mode 100644 index 0000000..68ee1b8 --- /dev/null +++ b/packages/arkham-shard-shell/src/pages/media-forensics/components/WarningsPanel.tsx @@ -0,0 +1,235 @@ +/** + * WarningsPanel - Display metadata warnings and forensic findings + */ + +import { Icon } from '../../../components/common/Icon'; +import type { MediaAnalysis, ForensicFinding, FindingSeverity } from '../types'; +import { SEVERITY_LABELS, SEVERITY_COLORS } from '../types'; + +interface WarningsPanelProps { + analysis: MediaAnalysis; +} + +export function WarningsPanel({ analysis }: WarningsPanelProps) { + const { findings } = analysis; + + if (!findings || findings.length === 0) { + return ( +
+ +

No warnings or findings detected

+

+ The analysis did not identify any suspicious indicators in this image +

+
+ ); + } + + // Group findings by category + const groupedFindings = findings.reduce( + (acc, finding) => { + if (!acc[finding.category]) { + acc[finding.category] = []; + } + acc[finding.category].push(finding); + return acc; + }, + {} as Record + ); + + // Sort findings by severity within each category + const severityOrder: FindingSeverity[] = ['critical', 'high', 'medium', 'low', 'info']; + Object.keys(groupedFindings).forEach((category) => { + groupedFindings[category].sort( + (a, b) => severityOrder.indexOf(a.severity) - severityOrder.indexOf(b.severity) + ); + }); + + const getCategoryIcon = (category: string): string => { + switch (category) { + case 'exif': + return 'Camera'; + case 'c2pa': + return 'ShieldCheck'; + case 'ela': + return 'Layers'; + case 'sun_position': + return 'Sun'; + case 'similar_images': + return 'Images'; + default: + return 'AlertTriangle'; + } + }; + + const getCategoryLabel = (category: string): string => { + switch (category) { + case 'exif': + return 'EXIF Metadata'; + case 'c2pa': + return 'Content Credentials'; + case 'ela': + return 'Error Level Analysis'; + case 'sun_position': + return 'Sun Position'; + case 'similar_images': + return 'Similar Images'; + default: + return 'General'; + } + }; + + const getSeverityIcon = (severity: FindingSeverity): string => { + switch (severity) { + case 'critical': + return 'XCircle'; + case 'high': + return 'AlertTriangle'; + case 'medium': + return 'AlertCircle'; + case 'low': + return 'Info'; + default: + return 'HelpCircle'; + } + }; + + const formatDate = (dateStr: string): string => { + try { + return new Date(dateStr).toLocaleString(); + } catch { + return dateStr; + } + }; + + // Count by severity + const severityCounts = findings.reduce( + (acc, f) => { + acc[f.severity] = (acc[f.severity] || 0) + 1; + return acc; + }, + {} as Record + ); + + return ( +
+ {/* Summary */} +
+
+ + + {findings.length} finding{findings.length !== 1 ? 's' : ''} detected + +
+
+ {severityOrder.map( + (severity) => + severityCounts[severity] > 0 && ( + + {severityCounts[severity]} {SEVERITY_LABELS[severity]} + + ) + )} +
+
+ + {/* Grouped Findings */} + {Object.entries(groupedFindings).map(([category, categoryFindings]) => ( +
+
+ +

+ {getCategoryLabel(category)} ({categoryFindings.length}) +

+
+
+ {categoryFindings.map((finding) => ( +
+
+
+ + {finding.title} +
+ + {SEVERITY_LABELS[finding.severity]} + +
+

{finding.description}

+ + {/* Recommendation */} + {finding.recommendation && ( +
+ + {finding.recommendation} +
+ )} + + {/* Evidence */} + {finding.evidence && Object.keys(finding.evidence).length > 0 && ( +
+
+ + + Evidence Details + +
{JSON.stringify(finding.evidence, null, 2)}
+
+
+ )} + +
+ + + {(finding.confidence * 100).toFixed(0)}% confidence + + + {finding.auto_detected ? ( + <> + + Auto-detected + + ) : ( + <> + + Manual + + )} + + + + {formatDate(finding.detected_at)} + +
+
+ ))} +
+
+ ))} + + {/* Legend */} +
+
Severity Legend
+
+ {severityOrder.map((severity) => ( +
+
+ {SEVERITY_LABELS[severity]} +
+ ))} +
+
+
+ ); +} + +export default WarningsPanel; diff --git a/packages/arkham-shard-shell/src/pages/media-forensics/components/index.ts b/packages/arkham-shard-shell/src/pages/media-forensics/components/index.ts new file mode 100644 index 0000000..453488c --- /dev/null +++ b/packages/arkham-shard-shell/src/pages/media-forensics/components/index.ts @@ -0,0 +1,12 @@ +/** + * Media Forensics Components + * + * Export all panel components for the Media Forensics shard. + */ + +export { ExifPanel } from './ExifPanel'; +export { C2PAPanel } from './C2PAPanel'; +export { ELAViewer } from './ELAViewer'; +export { SunPositionPanel } from './SunPositionPanel'; +export { SimilarImagesPanel } from './SimilarImagesPanel'; +export { WarningsPanel } from './WarningsPanel'; diff --git a/packages/arkham-shard-shell/src/pages/media-forensics/index.ts b/packages/arkham-shard-shell/src/pages/media-forensics/index.ts new file mode 100644 index 0000000..cdb3572 --- /dev/null +++ b/packages/arkham-shard-shell/src/pages/media-forensics/index.ts @@ -0,0 +1,36 @@ +/** + * Media Forensics shard pages + * + * Export all page components for the Media Forensics shard. + */ + +export { MediaForensicsPage } from './MediaForensicsPage'; + +// Re-export types for convenience +export type { + MediaAnalysis, + ExifData, + C2PAData, + ELAResult, + SunPositionResult, + SimilarImage, + SimilarImagesResult, + ForensicFinding, + AnalysisStats, + AnalysisStatus, + VerificationStatus, + FindingSeverity, + C2PAValidationStatus, +} from './types'; + +// Re-export display helpers +export { + STATUS_LABELS, + STATUS_COLORS, + VERIFICATION_LABELS, + VERIFICATION_COLORS, + SEVERITY_LABELS, + SEVERITY_COLORS, + C2PA_VALIDATION_LABELS, + C2PA_VALIDATION_COLORS, +} from './types'; diff --git a/packages/arkham-shard-shell/src/pages/media-forensics/types.ts b/packages/arkham-shard-shell/src/pages/media-forensics/types.ts new file mode 100644 index 0000000..59428de --- /dev/null +++ b/packages/arkham-shard-shell/src/pages/media-forensics/types.ts @@ -0,0 +1,490 @@ +/** + * Media Forensics Types + * + * Type definitions for the Media Forensics shard. + */ + +// ============================================ +// Status and Enum Types +// ============================================ + +export type AnalysisStatus = 'pending' | 'processing' | 'completed' | 'failed'; + +export type VerificationStatus = 'verified' | 'flagged' | 'unknown' | 'tampered'; + +export type FindingSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info'; + +export type C2PAValidationStatus = 'valid' | 'invalid' | 'expired' | 'revoked' | 'unknown'; + +// ============================================ +// EXIF Data Types +// ============================================ + +export interface ExifGPSData { + latitude: number | null; + longitude: number | null; + altitude: number | null; + altitude_ref: string | null; + gps_timestamp: string | null; + gps_date: string | null; + direction: number | null; + direction_ref: string | null; +} + +export interface ExifCameraData { + make: string | null; + model: string | null; + serial_number: string | null; + lens_make: string | null; + lens_model: string | null; + lens_serial: string | null; +} + +export interface ExifImageData { + width: number | null; + height: number | null; + orientation: number | null; + color_space: string | null; + bits_per_sample: number | null; + compression: string | null; +} + +export interface ExifCaptureSettings { + exposure_time: string | null; + f_number: number | null; + iso: number | null; + focal_length: number | null; + focal_length_35mm: number | null; + exposure_mode: string | null; + exposure_program: string | null; + metering_mode: string | null; + white_balance: string | null; + flash: string | null; +} + +export interface ExifTimestamps { + datetime_original: string | null; + datetime_digitized: string | null; + datetime_modified: string | null; + timezone_offset: string | null; +} + +export interface ExifSoftware { + software: string | null; + processing_software: string | null; + host_computer: string | null; + firmware: string | null; +} + +export interface ExifData { + camera: ExifCameraData; + image: ExifImageData; + gps: ExifGPSData; + capture: ExifCaptureSettings; + timestamps: ExifTimestamps; + software: ExifSoftware; + raw_data: Record; + warnings: string[]; +} + +// ============================================ +// C2PA (Content Credentials) Types +// ============================================ + +export interface C2PAAssertion { + label: string; + data: Record; + instance?: number; +} + +export interface C2PAAction { + action: string; + when: string | null; + software_agent: string | null; + parameters: Record; +} + +export interface C2PAIngredient { + title: string; + format: string; + document_id: string | null; + instance_id: string | null; + relationship: string; + thumbnail: string | null; +} + +export interface C2PASigner { + name: string; + organization: string | null; + issued_date: string | null; + expiry_date: string | null; + is_trusted: boolean; + trust_chain: string[]; + validation_status: C2PAValidationStatus; +} + +export interface C2PAManifest { + claim_generator: string; + title: string | null; + format: string | null; + instance_id: string | null; + assertions: C2PAAssertion[]; + actions: C2PAAction[]; + ingredients: C2PAIngredient[]; + signer: C2PASigner | null; + signature_date: string | null; +} + +export interface C2PAData { + has_manifest: boolean; + manifests: C2PAManifest[]; + active_manifest_index: number | null; + validation_status: C2PAValidationStatus; + validation_errors: string[]; + provenance_chain: string[]; +} + +// ============================================ +// ELA (Error Level Analysis) Types +// ============================================ + +export interface ELARegion { + x: number; + y: number; + width: number; + height: number; + avg_intensity: number; + max_intensity: number; + is_suspicious: boolean; + description: string; +} + +export interface ELAResult { + id: string; + doc_id: string; + analysis_id: string; + quality_level: number; + original_image_url: string | null; + ela_image_url: string | null; + ela_image_base64: string | null; + global_avg_intensity: number; + global_max_intensity: number; + suspicious_regions: ELARegion[]; + is_potentially_edited: boolean; + confidence: number; + generated_at: string; +} + +// ============================================ +// Sun Position Analysis Types +// ============================================ + +export interface SunPosition { + azimuth: number; + altitude: number; + calculated_time: string; + location_lat: number; + location_lon: number; +} + +export interface ShadowAnalysis { + detected_shadows: { + object_id: string; + shadow_direction: number; + shadow_length: number; + confidence: number; + }[]; + average_shadow_direction: number | null; + consistency_score: number; +} + +export interface SunPositionResult { + id: string; + doc_id: string; + analysis_id: string; + claimed_location: { lat: number; lon: number } | null; + claimed_time: string | null; + calculated_sun_position: SunPosition | null; + shadow_analysis: ShadowAnalysis | null; + is_consistent: boolean; + inconsistency_details: string[]; + confidence: number; + generated_at: string; +} + +// ============================================ +// Similar Images Types +// ============================================ + +export interface SimilarImage { + id: string; + doc_id: string; + filename: string; + similarity_score: number; + similarity_type: 'exact' | 'near_duplicate' | 'visually_similar' | 'content_similar'; + match_details: { + hash_distance?: number; + feature_similarity?: number; + matching_regions?: { x: number; y: number; width: number; height: number }[]; + source_url?: string; + source_domain?: string; + }; + thumbnail_url: string | null; + thumbnail_base64: string | null; + source: string | null; + found_at: string; +} + +export interface ReverseImageSearchUrl { + engine: string; + url: string; + icon: string; + description: string; + type: 'url_search' | 'upload_search'; + instructions?: string; +} + +export interface SimilarImagesResult { + id: string; + doc_id: string; + analysis_id: string; + search_type: 'internal' | 'external' | 'both'; + total_found: number; + similar_images: SimilarImage[]; + exact_matches: number; + near_duplicates: number; + visually_similar: number; + generated_at: string; + search_urls?: ReverseImageSearchUrl[]; // URLs for manual reverse image search +} + +// ============================================ +// Forensic Finding Types +// ============================================ + +export interface ForensicFinding { + id: string; + category: 'exif' | 'c2pa' | 'ela' | 'sun_position' | 'similar_images' | 'general'; + severity: FindingSeverity; + title: string; + description: string; + evidence: Record; + recommendation: string | null; + confidence: number; + auto_detected: boolean; + detected_at: string; +} + +// ============================================ +// Media Analysis Types +// ============================================ + +export interface PerceptualHashes { + phash: string | null; + dhash: string | null; + ahash: string | null; +} + +export interface MediaAnalysis { + id: string; + doc_id: string; + filename: string; + file_type: string; + file_size: number; + file_hash_md5: string | null; + file_hash_sha256: string | null; + status: AnalysisStatus; + verification_status: VerificationStatus; + integrity_status: string; + + // Analysis components + exif_data: ExifData | null; + c2pa_data: C2PAData | null; + ela_result: ELAResult | null; + sun_position_result: SunPositionResult | null; + similar_images_result: SimilarImagesResult | null; + perceptual_hashes: PerceptualHashes | null; + + // Findings + findings: ForensicFinding[]; + findings_count: number; + critical_findings: number; + high_findings: number; + + // Metadata + analyzed_at: string; + updated_at: string; + analyzed_by: string; + notes: string | null; +} + +// ============================================ +// Statistics Types +// ============================================ + +export interface AnalysisStats { + total_analyses: number; + by_status: Record; + by_verification: Record; + with_c2pa: number; + with_exif: number; + with_gps: number; + with_findings: number; + critical_findings_total: number; + high_findings_total: number; + ela_analyses: number; + sun_position_analyses: number; + similar_images_searches: number; + avg_findings_per_analysis: number; +} + +// ============================================ +// API Request/Response Types +// ============================================ + +export interface AnalyzeDocumentRequest { + doc_id: string; + run_ela?: boolean; + run_sun_position?: boolean; + run_similar_search?: boolean; + similar_search_type?: 'internal' | 'external' | 'both'; +} + +export interface AnalyzeDocumentResponse { + analysis: MediaAnalysis; + job_id?: string; +} + +export interface AnalysisListResponse { + total: number; + items: MediaAnalysis[]; + offset: number; + limit: number; + has_more: boolean; +} + +export interface GenerateELARequest { + analysis_id: string; + quality_level?: number; +} + +export interface GenerateELAResponse { + result: ELAResult; +} + +export interface SunPositionRequest { + analysis_id: string; + override_location?: { lat: number; lon: number }; + override_time?: string; +} + +export interface SunPositionResponse { + result: SunPositionResult; +} + +export interface FindSimilarRequest { + analysis_id: string; + search_type?: 'internal' | 'external' | 'both'; + limit?: number; +} + +export interface FindSimilarResponse { + result: SimilarImagesResult; +} + +export interface StatsResponse { + stats: AnalysisStats; +} + +export interface C2PASupportResponse { + supported: boolean; + version: string | null; + message: string; +} + +// ============================================ +// Display Helpers +// ============================================ + +export const STATUS_LABELS: Record = { + pending: 'Pending', + processing: 'Processing', + completed: 'Completed', + failed: 'Failed', +}; + +export const STATUS_COLORS: Record = { + pending: 'blue', + processing: 'yellow', + completed: 'green', + failed: 'red', +}; + +export const VERIFICATION_LABELS: Record = { + verified: 'Verified', + flagged: 'Flagged', + unknown: 'Unknown', + tampered: 'Tampered', +}; + +export const VERIFICATION_COLORS: Record = { + verified: 'var(--arkham-success, #22c55e)', + flagged: 'var(--arkham-warning, #eab308)', + unknown: 'var(--arkham-text-muted, #6b7280)', + tampered: 'var(--arkham-error, #ef4444)', +}; + +export const SEVERITY_LABELS: Record = { + critical: 'Critical', + high: 'High', + medium: 'Medium', + low: 'Low', + info: 'Info', +}; + +export const SEVERITY_COLORS: Record = { + critical: 'var(--arkham-error, #ef4444)', + high: 'var(--arkham-warning, #f97316)', + medium: 'var(--arkham-warning, #eab308)', + low: 'var(--arkham-info, #3b82f6)', + info: 'var(--arkham-text-muted, #6b7280)', +}; + +export const C2PA_VALIDATION_LABELS: Record = { + valid: 'Valid', + invalid: 'Invalid', + expired: 'Expired', + revoked: 'Revoked', + unknown: 'Unknown', +}; + +export const C2PA_VALIDATION_COLORS: Record = { + valid: 'var(--arkham-success, #22c55e)', + invalid: 'var(--arkham-error, #ef4444)', + expired: 'var(--arkham-warning, #f97316)', + revoked: 'var(--arkham-error, #dc2626)', + unknown: 'var(--arkham-text-muted, #6b7280)', +}; + +// Filter options +export const STATUS_OPTIONS: { value: AnalysisStatus; label: string }[] = [ + { value: 'pending', label: 'Pending' }, + { value: 'processing', label: 'Processing' }, + { value: 'completed', label: 'Completed' }, + { value: 'failed', label: 'Failed' }, +]; + +export const VERIFICATION_OPTIONS: { value: VerificationStatus; label: string }[] = [ + { value: 'verified', label: 'Verified' }, + { value: 'flagged', label: 'Flagged' }, + { value: 'unknown', label: 'Unknown' }, + { value: 'tampered', label: 'Tampered' }, +]; + +export const SEVERITY_OPTIONS: { value: FindingSeverity; label: string }[] = [ + { value: 'critical', label: 'Critical' }, + { value: 'high', label: 'High' }, + { value: 'medium', label: 'Medium' }, + { value: 'low', label: 'Low' }, + { value: 'info', label: 'Info' }, +];