diff --git a/scripts/artifacts/kik.py b/scripts/artifacts/kik.py index eac4a44..1c7e7f9 100644 --- a/scripts/artifacts/kik.py +++ b/scripts/artifacts/kik.py @@ -1,171 +1,213 @@ __artifacts_v2__ = { "kik_subscriber": { "name": "Kik - Subscriber Info", - "description": "Parses Kik subscriber/account data from the returns package PDF", + "description": "Parses Kik subscriber/account fields from the returns package PDF", "author": "@OneSixForensics", - "version": "0.3", - "date": "2026-05-08", + "version": "0.4", + "date": "2026-06-25", "requirements": "pdfminer.six", "category": "Kik", "notes": "Located at package/subscriber-data-_.pdf", "paths": ('*/package/subscriber-data-*.pdf',), - "function": "get_kik_subscriber", + "output_types": "standard", + "function": "kik_subscriber", "artifact_icon": "user" }, + "kik_subscriber_pics": { + "name": "Kik - Subscriber Profile Pics", + "description": "Profile picture URLs and their original/scaled MD5s from the subscriber PDF", + "author": "@OneSixForensics", + "version": "0.4", + "date": "2026-06-25", + "requirements": "pdfminer.six", + "category": "Kik", + "notes": "Located at package/subscriber-data-_.pdf", + "paths": ('*/package/subscriber-data-*.pdf',), + "output_types": "standard", + "html_columns": ['Profile Pic URL'], + "function": "kik_subscriber_pics", + "artifact_icon": "image" + }, + "kik_subscriber_events": { + "name": "Kik - Subscriber Events", + "description": "Timestamped account events from the subscriber PDF event log", + "author": "@OneSixForensics", + "version": "0.4", + "date": "2026-06-25", + "requirements": "pdfminer.six", + "category": "Kik", + "notes": "Located at package/subscriber-data-_.pdf", + "paths": ('*/package/subscriber-data-*.pdf',), + "output_types": "standard", + "function": "kik_subscriber_events", + "artifact_icon": "activity" + }, "kik_group_legend": { "name": "Kik - Group Legend", "description": "Parses group-legend-_.csv — maps group JIDs to names and metadata", "author": "@OneSixForensics", - "version": "0.1", - "date": "2026-05-08", + "version": "0.2", + "date": "2026-06-25", "requirements": "none", "category": "Kik", "notes": "Located at package/group-legend-_.csv", "paths": ('*/package/group-legend-*.csv',), - "function": "get_kik_group_legend", + "output_types": "standard", + "function": "kik_group_legend", "artifact_icon": "users" }, "kik_chat_text": { "name": "Kik - Chat Messages (Text)", "description": "Parses data-text.csv from Kik returns content folder - direct message text content", "author": "@OneSixForensics", - "version": "0.1", - "date": "2026-05-08", + "version": "0.2", + "date": "2026-06-25", "requirements": "none", "category": "Kik", "notes": "Located at package/content/data-text.csv", "paths": ('*/package/content/data-text.csv',), - "function": "get_kik_chat_text", + "output_types": "standard", + "function": "kik_chat_text", "artifact_icon": "message-square" }, "kik_chat_media": { "name": "Kik - Chat Messages (Media)", "description": "Parses data-media.csv from Kik returns content folder - media message metadata", "author": "@OneSixForensics", - "version": "0.1", - "date": "2026-05-08", + "version": "0.2", + "date": "2026-06-25", "requirements": "none", "category": "Kik", "notes": "Located at package/content/data-media.csv. Filenames in medias/ folder are Base64-encoded.", "paths": ('*/package/content/data-media.csv', '*/package/content/medias/*'), - "function": "get_kik_chat_media", + "output_types": "standard", + "function": "kik_chat_media", "artifact_icon": "image" }, "kik_chat_sent": { "name": "Kik - Sent Chats (Log)", "description": "Parses chat_sent.csv from the logs folder", "author": "@OneSixForensics", - "version": "0.1", - "date": "2026-05-08", + "version": "0.2", + "date": "2026-06-25", "requirements": "none", "category": "Kik", "notes": "Located at package/logs/chat_sent.csv", "paths": ('*/package/logs/chat_sent.csv',), - "function": "get_kik_chat_sent", + "output_types": "standard", + "function": "kik_chat_sent", "artifact_icon": "message-square" }, "kik_chat_sent_received": { "name": "Kik - Sent & Received Chats (Log)", "description": "Parses chat_sent_received.csv from the logs folder", "author": "@OneSixForensics", - "version": "0.1", - "date": "2026-05-08", + "version": "0.2", + "date": "2026-06-25", "requirements": "none", "category": "Kik", "notes": "Located at package/logs/chat_sent_received.csv", "paths": ('*/package/logs/chat_sent_received.csv',), - "function": "get_kik_chat_sent_received", + "output_types": "standard", + "function": "kik_chat_sent_received", "artifact_icon": "message-square" }, "kik_chat_platform_sent": { "name": "Kik - Platform Chat Sent (Log)", "description": "Parses chat_platform_sent.csv from the logs folder", "author": "@OneSixForensics", - "version": "0.1", - "date": "2026-05-08", + "version": "0.2", + "date": "2026-06-25", "requirements": "none", "category": "Kik", "notes": "Located at package/logs/chat_platform_sent.csv", "paths": ('*/package/logs/chat_platform_sent.csv',), - "function": "get_kik_chat_platform_sent", + "output_types": "standard", + "function": "kik_chat_platform_sent", "artifact_icon": "message-square" }, "kik_chat_platform_sent_received": { "name": "Kik - Platform Chat Sent & Received (Log)", "description": "Parses chat_platform_sent_received.csv from the logs folder", "author": "@OneSixForensics", - "version": "0.1", - "date": "2026-05-08", + "version": "0.2", + "date": "2026-06-25", "requirements": "none", "category": "Kik", "notes": "Located at package/logs/chat_platform_sent_received.csv", "paths": ('*/package/logs/chat_platform_sent_received.csv',), - "function": "get_kik_chat_platform_sent_received", + "output_types": "standard", + "function": "kik_chat_platform_sent_received", "artifact_icon": "message-square" }, "kik_group_receive": { "name": "Kik - Group Messages Received (Log)", "description": "Parses group_receive.csv from the logs folder", "author": "@OneSixForensics", - "version": "0.1", - "date": "2026-05-08", + "version": "0.2", + "date": "2026-06-25", "requirements": "none", "category": "Kik", "notes": "Located at package/logs/group_receive.csv", "paths": ('*/package/logs/group_receive.csv',), - "function": "get_kik_group_receive", + "output_types": "standard", + "function": "kik_group_receive", "artifact_icon": "users" }, "kik_group_send": { "name": "Kik - Group Messages Sent (Log)", "description": "Parses group_send_msg_platform.csv from the logs folder", "author": "@OneSixForensics", - "version": "0.1", - "date": "2026-05-08", + "version": "0.2", + "date": "2026-06-25", "requirements": "none", "category": "Kik", "notes": "Located at package/logs/group_send_msg_platform.csv", "paths": ('*/package/logs/group_send_msg_platform.csv',), - "function": "get_kik_group_send", + "output_types": "standard", + "function": "kik_group_send", "artifact_icon": "users" }, "kik_friends_added": { "name": "Kik - Friends Added (Log)", "description": "Parses friends_added.csv from the logs folder", "author": "@OneSixForensics", - "version": "0.1", - "date": "2026-05-08", + "version": "0.2", + "date": "2026-06-25", "requirements": "none", "category": "Kik", "notes": "Located at package/logs/friends_added.csv", "paths": ('*/package/logs/friends_added.csv',), - "function": "get_kik_friends_added", + "output_types": "standard", + "function": "kik_friends_added", "artifact_icon": "user-plus" }, "kik_block_user": { "name": "Kik - Blocked Users (Log)", "description": "Parses block_user.csv from the logs folder", "author": "@OneSixForensics", - "version": "0.1", - "date": "2026-05-08", + "version": "0.2", + "date": "2026-06-25", "requirements": "none", "category": "Kik", "notes": "Located at package/logs/block_user.csv", "paths": ('*/package/logs/block_user.csv',), - "function": "get_kik_block_user", + "output_types": "standard", + "function": "kik_block_user", "artifact_icon": "user-x" }, "kik_binds": { "name": "Kik - Binds (Log)", "description": "Parses binds.csv from the logs folder - device/session binding events", "author": "@OneSixForensics", - "version": "0.1", - "date": "2026-05-08", + "version": "0.2", + "date": "2026-06-25", "requirements": "none", "category": "Kik", "notes": "Located at package/logs/binds.csv", "paths": ('*/package/logs/binds.csv',), - "function": "get_kik_binds", + "output_types": "standard", + "function": "kik_binds", "artifact_icon": "smartphone" }, } @@ -175,11 +217,10 @@ # --------------------------------------------------------------------------- import csv import os +import re from datetime import datetime, timezone -import re -from scripts.artifact_report import ArtifactHtmlReport -import scripts.ilapfuncs as ilapfuncs +from scripts.ilapfuncs import artifact_processor, logfunc, check_in_media try: from pdfminer.high_level import extract_text as pdf_extract_text @@ -191,6 +232,14 @@ # Helper utilities # --------------------------------------------------------------------------- +def _clean_path(path): + """Strip Windows extended-length path prefix if present (\\\\?\\).""" + p = str(path) + if p.startswith('\\\\?\\'): + return p[4:] + return p + + def _open_csv(file_found): """Return (headers, rows) for a CSV file, stripping BOM if present.""" rows = [] @@ -203,42 +252,10 @@ def _open_csv(file_found): for row in reader: rows.append({k.strip(): v for k, v in row.items()}) except Exception as e: - ilapfuncs.logfunc(f'Kik parser CSV read error ({file_found}): {e}') + logfunc(f'Kik parser CSV read error ({file_found}): {e}') return headers, rows -def _write_report(report_folder, artifact_name, data_headers, data_list, source_file, - timeline_ts_col=None, html_no_escape=None): - """Write HTML report, TSV, and optionally timeline for a Kik artifact. - - Files are written into a 'Kik' subfolder of report_folder so that - RLEAPP's report.py derives SectionHeader='Kik' for icon lookup. - """ - if not data_list: - ilapfuncs.logfunc(f'Kik: No data found for {artifact_name}') - return - - # Write into a Kik/ subfolder so report.py sets SectionHeader='Kik' - kik_folder = os.path.join(report_folder, 'Kik') - os.makedirs(kik_folder, exist_ok=True) - - report = ArtifactHtmlReport(artifact_name) - report.start_artifact_report(kik_folder, artifact_name) - report.add_script() - if html_no_escape: - report.write_artifact_data_table(data_headers, data_list, source_file, - html_no_escape=html_no_escape) - else: - report.write_artifact_data_table(data_headers, data_list, source_file) - report.end_artifact_report() - - ilapfuncs.tsv(report_folder, data_headers, data_list, artifact_name) - - if timeline_ts_col and timeline_ts_col in data_headers: - ts_index = data_headers.index(timeline_ts_col) - ilapfuncs.timeline(report_folder, artifact_name, data_list, [data_headers[ts_index]]) - - def _fallback_rows(headers, rows): """Return all columns as-is when schema is unknown or unexpected.""" return headers, [tuple(row.get(h, '') for h in headers) for row in rows] @@ -258,551 +275,420 @@ def _ms_epoch_to_utc(ms_value): return str(ms_value) -def _clean_path(path): - """Strip Windows extended-length path prefix if present (\\\\?\\).""" - p = str(path) - if p.startswith('\\\\?\\'): - return p[4:] - return p +def _register_media(file_path, name): + """Register a media file for inline HTML + LAVA rendering, returning its + media-reference id (or '' if it can't be registered). Thin wrapper over the + framework's check_in_media so call sites never propagate a None. + """ + return check_in_media(file_path, name) or '' # --------------------------------------------------------------------------- -# Artifact functions +# Subscriber PDF (one PDF -> three artifacts: Info, Profile Pics, Events) # --------------------------------------------------------------------------- -def get_kik_subscriber(files_found, report_folder, seeker, wrap_text): - """ - subscriber-data-_.pdf - Extracts key subscriber fields from the Kik legal return PDF using regex. - Confirmed fields: First Name, Last Name, Email, Username, Registration Timestamp, - Client Version, User Locale, User Location, Registration Client Info, Profile Pic URLs. - """ - if not PDF_SUPPORT: - ilapfuncs.logfunc( - 'Kik subscriber PDF: pdfminer.six not installed. ' - 'Run: pip install pdfminer.six then re-run RLEAPP.' - ) - return - - # Regex patterns keyed to observed PDF format - PATTERNS = [ - ('First Name', r'First Name:\s*(.+)'), - ('Last Name', r'Last Name:[ \t]*([^\n]*)'), - ('Email', r'Email:\s*(\S+)'), - ('Email Confirmed', r'Email:\s*\S+\s*(\(confirmed\))'), - ('Username', r'Username:\s*(\S+)'), - ('Registration Timestamp', r'REGISTRATION_TIMESTAMP\s+([\d/]{10}\s[\d:]{8})'), - ('User Locale', r'USER_LOCALE\s+(\S+)'), - ('Client Version', r'CLIENT_VERSION[\s\S]*?([\d]+\.[\d]+\.[\d]+)'), - ('User Location', r'USER_LOCATION[\s\S]*?(\S.*?)\n'), - ('System Version', r'system-version=([\d.]+)'), - ('App Version', r'(?