---
name: scan_ficha_attachments_metadata
kind: function
lang: py
domain: cybersecurity
version: "1.0.0"
purity: impure
signature: "def scan_ficha_attachments_metadata(attachments_dir: str) -> dict"
description: "Orquestador OSINT pasivo: recorre un directorio de attachments de una ficha (imagenes y PDFs), extrae sus metadatos componiendo extract_exif_metadata (imagenes .jpg/.jpeg/.png/.heic) y extract_pdf_metadata (.pdf), y agrega los puntos GPS y las fechas encontradas. Devuelve files + gps_points + dates + summary."
tags: [osint-enrich, osint-passive, cybersecurity, metadata, exif, pdf]
uses_functions: [extract_exif_metadata_py_cybersecurity, extract_pdf_metadata_py_cybersecurity]
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [os]
params:
  - name: attachments_dir
    desc: "ruta a un directorio de attachments, p.ej. /home/enmanuel/Obsidian/osint/attachments/personas/<slug>/. Se recorre recursivamente. Si no es un directorio existente lanza NotADirectoryError"
output: "dict con files (lista de {path, type, metadata} por archivo procesado: type es 'image' o 'pdf'), gps_points (lista de {file, lat, lon} agregando las coordenadas EXIF), dates (lista de fechas str de EXIF y PDF) y summary ({n_files, n_images, n_pdfs, n_gps_points, n_dates, errors}). Los archivos cuya extraccion falla quedan con metadata={'error': ...} y suman a summary.errors"
tested: true
tests: ["test_golden_agrega_gps_y_fechas_de_imagen_y_pdf", "test_directorio_inexistente_lanza", "test_ignora_extensiones_no_soportadas", "test_error_en_archivo_se_captura_en_summary"]
test_file_path: "python/functions/cybersecurity/scan_ficha_attachments_metadata_test.py"
file_path: "python/functions/cybersecurity/scan_ficha_attachments_metadata.py"
---

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from cybersecurity import scan_ficha_attachments_metadata

# Escanea los attachments de una persona en el vault OSINT.
res = scan_ficha_attachments_metadata(
    "/home/enmanuel/Obsidian/osint/attachments/personas/juan-perez/"
)

print(res["summary"])          # {'n_files': 7, 'n_images': 5, 'n_pdfs': 2, 'n_gps_points': 2, 'n_dates': 4, 'errors': 0}
for p in res["gps_points"]:
    print(p["file"], p["lat"], p["lon"])   # coordenadas extraidas de las fotos
print(res["dates"])            # fechas EXIF + fechas de creacion/modificacion de los PDFs
```

## Cuando usarla

- Cuando montas la ficha OSINT de una persona u organizacion y quieres saber de un vistazo que huella de metadatos dejan los documentos/fotos que ya tienes guardados (donde y cuando se hicieron).
- Antes de publicar/compartir attachments propios: para auditar que GPS y fechas se filtrarian.
- Como paso de enriquecimiento dentro de un pipeline de investigacion, justo despues de descargar los attachments al directorio de la ficha.

## Gotchas

- **Uso solo para investigacion autorizada.** Extraer metadatos de archivos ajenos sin permiso puede ser ilegal segun jurisdiccion. Limitate a tus propios documentos o a material que estes autorizado a analizar.
- Funcion IMPURA: hace I/O sobre el sistema de archivos (recorrido recursivo con `os.walk`). Si `attachments_dir` no existe lanza `NotADirectoryError`.
- La extraccion por archivo se aisla con try/except: un archivo corrupto no aborta el escaneo, queda con `metadata={"error": ...}` y suma a `summary.errors`.
- Solo procesa imagenes .jpg/.jpeg/.png/.heic y PDFs .pdf; el resto se ignora silenciosamente. El soporte real de HEIC/EXIF depende de lo que `extract_exif_metadata` (Pillow) pueda abrir en el sistema.
- Las fechas se devuelven tal cual las reportan EXIF/PDF (formatos heterogeneos, sin normalizar a ISO).
