6.9 KiB
6.9 KiB
In [ ]:
from pathlib import Path
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
DATA_PATH = Path("data/auth_log.csv")
OUTPUT_DIR = Path("output")
OUTPUT_DIR.mkdir(exist_ok=True)
In [ ]:
def clean_auth_log(df):
result = df.copy()
# TODO 1: drop duplicate rows
# TODO 2: parse timestamp using errors="coerce" and utc=True
# TODO 3: drop rows missing timestamp, source_ip, or status
# TODO 4: normalize source_ip, username, and status
# TODO 5: retain SUCCESS and FAILED records only
# TODO 6: create hour and is_off_hours (< 6 or >= 22)
# TODO 7: sort by timestamp and reset the index
return result
raw_df = pd.read_csv(DATA_PATH)
clean_df = clean_auth_log(raw_df)
print(f"Raw rows: {len(raw_df)} | Clean rows: {len(clean_df)}")
clean_df.head()
In [ ]:
# TODO 8: filter FAILED records
failed_df = None
# TODO 9: group by source_ip, count, sort descending, and name the Series failed_attempts
failed_by_ip = None
THRESHOLD = 3
# TODO 10: retain IP addresses with count >= THRESHOLD
suspicious_ips = None
print(failed_by_ip)
print(suspicious_ips)
In [ ]:
# TODO 11: create status_counts in SUCCESS, FAILED order
status_counts = None
# TODO 12: filter off-hours failed events
off_hours_failed = None
# TODO 13: determine top_ip, top_ip_count, and failure_rate (%)
top_ip = None
top_ip_count = None
failure_rate = None
print(f"Top suspicious IP: {top_ip} ({top_ip_count} failures)")
print(f"Failure rate: {failure_rate}%")
print("Off-hours failed attempts:", None if off_hours_failed is None else len(off_hours_failed))
In [ ]:
# TODO 14: create a figure with two charts:
# - a SUCCESS/FAILED bar chart
# - a horizontal bar chart of the top five IP addresses by failed attempts
# TODO 15: add titles and labels, save to output/auth_log_analysis.png, and call plt.show()
In [ ]:
# TODO 16: export clean_df and suspicious_ips to CSV
# TODO 17: create a summary string containing clean event count, FAILED count,
# failure rate, top IP, off-hours failures, and recommendations.
# TODO 18: save summary to output/capstone_summary.txt
In [ ]:
assert len(raw_df) > len(clean_df)
assert clean_df.duplicated().sum() == 0
assert failed_by_ip.index[0] == "198.51.100.14"
assert top_ip_count >= THRESHOLD
assert len(off_hours_failed) >= 1
assert (OUTPUT_DIR / "auth_log_analysis.png").exists()
assert (OUTPUT_DIR / "capstone_summary.txt").exists()
print("✓ Capstone lab completed")