--- 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//. 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).