#!/usr/bin/env python3
"""
Lab 13.5 - CloudTrail breach investigation (network-free stub)
Reconstruct a Capital-One-shaped attack chain from a local stub dataset.
No live AWS API calls; all assertions verifiable against the bundled JSON.

Usage:
    python3 investigate.py
"""

import json
from pathlib import Path
from collections import Counter

STUB_FILE = Path("/lab5/data/cloudtrail-stub.json")

def load_records():
    with open(STUB_FILE) as f:
        return json.load(f)["Records"]

def get_principal(rec):
    ui = rec["userIdentity"]
    return ui.get("userName") or ui.get("arn", "unknown")

def main():
    records = load_records()
    print(f"[*] Loaded {len(records)} CloudTrail records from {STUB_FILE}\n")

    # --- Step 1: Find ConsoleLogin / AssumeRole from anomalous IP ---
    print("=== Step 1: Login and role assumption chain ===")
    auth_events = [r for r in records if r["eventName"] in ("ConsoleLogin", "AssumeRole")]
    for r in auth_events:
        print(f"  {r['eventTime']}  {r['eventName']:20s}  "
              f"principal={get_principal(r):40s}  ip={r['sourceIPAddress']}")

    print()

    # --- Step 2: Identify the assumed role and anomalous source IP ---
    assume = next((r for r in records if r["eventName"] == "AssumeRole"), None)
    if assume:
        role_arn = assume.get("requestParameters", {}).get("roleArn", "?")
        attacker_ip = assume["sourceIPAddress"]
        print(f"=== Step 2: AssumeRole into over-permissioned role ===")
        print(f"  Role ARN   : {role_arn}")
        print(f"  Source IP  : {attacker_ip}  <-- anomalous; all mass-read events share this IP")
    print()

    # --- Step 3: Reconstruct mass-read burst (ListBuckets + GetObject) ---
    mass_read = [r for r in records if r["eventName"] in ("ListBuckets", "GetObject")]
    ips = {r["sourceIPAddress"] for r in mass_read}
    buckets = {r.get("requestParameters", {}).get("bucketName", "N/A") for r in mass_read}
    get_objects = [r for r in mass_read if r["eventName"] == "GetObject"]

    print("=== Step 3: Mass-read burst ===")
    print(f"  Events : {len(mass_read)} ({Counter(r['eventName'] for r in mass_read)})")
    print(f"  IPs    : {ips}")
    print(f"  Buckets: {buckets}")
    if get_objects:
        t_start = get_objects[0]["eventTime"]
        t_end   = get_objects[-1]["eventTime"]
        print(f"  Window : {t_start} -> {t_end}")
    print()

    # --- Step 4: Findings summary ---
    print("=== FINDINGS ===")
    print(f"  Offending principal : {get_principal(assume) if assume else 'N/A'}")
    print(f"  Assumed role        : {role_arn if assume else 'N/A'}")
    print(f"  Attacker IP         : {attacker_ip if assume else 'N/A'}")
    print(f"  Exfiltration window : {t_start} to {t_end}" if get_objects else "  Exfiltration window : N/A")
    print(f"  GetObject count     : {len(get_objects)}")
    print()
    print("Verify: every assertion above is backed by a record in data/cloudtrail-stub.json")

if __name__ == "__main__":
    main()
