CSAI2026 labs: Lab-2..Lab-6 (Practical + Solution, data), Module-2..Module-6 notebooks and datasets

This commit is contained in:
mikrotik
2026-10-12 19:16:14 +07:00
commit 2fefa615cc
184 changed files with 1016761 additions and 0 deletions
@@ -0,0 +1,284 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "3d9865d1",
"metadata": {},
"source": [
"# Module 2 — Lab 6: Capstone — Investigating Suspicious Logins\n",
"\n",
" \n",
"**Topics:** Pandas, NumPy, Matplotlib, export, analyst summary\n",
"\n",
"\n",
"> **Instructions:** Run the cells in order. Complete every `TODO`, then run the **Self-check** cell. Do not open the Solution notebook before attempting the tasks yourself.\n"
]
},
{
"cell_type": "markdown",
"id": "dda323ff",
"metadata": {},
"source": [
"## Mission\n",
"\n",
"As a junior security analyst, you must build a complete workflow:\n",
"\n",
"1. load and clean authentication logs;\n",
"2. identify IP addresses with repeated failed logins;\n",
"3. identify off-hours activity;\n",
"4. visualize the results;\n",
"5. export the data and write a short conclusion.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7b91b90f",
"metadata": {},
"outputs": [],
"source": [
"from pathlib import Path\n",
"import numpy as np\n",
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
"\n",
"DATA_PATH = Path(\"data/auth_log.csv\")\n",
"OUTPUT_DIR = Path(\"output\")\n",
"OUTPUT_DIR.mkdir(exist_ok=True)\n"
]
},
{
"cell_type": "markdown",
"id": "2e73090b",
"metadata": {
"tags": [
"exercise"
]
},
"source": [
"## 1. Write a reusable cleaning function\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "21e69c93",
"metadata": {
"tags": [
"exercise"
]
},
"outputs": [],
"source": [
"def clean_auth_log(df):\n",
" result = df.copy()\n",
" # TODO 1: drop duplicate rows\n",
" # TODO 2: parse timestamp using errors=\"coerce\" and utc=True\n",
" # TODO 3: drop rows missing timestamp, source_ip, or status\n",
" # TODO 4: normalize source_ip, username, and status\n",
" # TODO 5: retain SUCCESS and FAILED records only\n",
" # TODO 6: create hour and is_off_hours (< 6 or >= 22)\n",
" # TODO 7: sort by timestamp and reset the index\n",
" return result\n",
"\n",
"\n",
"raw_df = pd.read_csv(DATA_PATH)\n",
"clean_df = clean_auth_log(raw_df)\n",
"print(f\"Raw rows: {len(raw_df)} | Clean rows: {len(clean_df)}\")\n",
"clean_df.head()\n"
]
},
{
"cell_type": "markdown",
"id": "57d60f86",
"metadata": {
"tags": [
"exercise"
]
},
"source": [
"## 2. Analyze failures and rank IP addresses\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1ea702f5",
"metadata": {
"tags": [
"exercise"
]
},
"outputs": [],
"source": [
"# TODO 8: filter FAILED records\n",
"failed_df = None\n",
"\n",
"# TODO 9: group by source_ip, count, sort descending, and name the Series failed_attempts\n",
"failed_by_ip = None\n",
"\n",
"THRESHOLD = 3\n",
"# TODO 10: retain IP addresses with count >= THRESHOLD\n",
"suspicious_ips = None\n",
"\n",
"print(failed_by_ip)\n",
"print(suspicious_ips)\n"
]
},
{
"cell_type": "markdown",
"id": "eba0fd3d",
"metadata": {
"tags": [
"exercise"
]
},
"source": [
"## 3. Calculate additional investigation metrics\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fea68f16",
"metadata": {
"tags": [
"exercise"
]
},
"outputs": [],
"source": [
"# TODO 11: create status_counts in SUCCESS, FAILED order\n",
"status_counts = None\n",
"\n",
"# TODO 12: filter off-hours failed events\n",
"off_hours_failed = None\n",
"\n",
"# TODO 13: determine top_ip, top_ip_count, and failure_rate (%)\n",
"top_ip = None\n",
"top_ip_count = None\n",
"failure_rate = None\n",
"\n",
"print(f\"Top suspicious IP: {top_ip} ({top_ip_count} failures)\")\n",
"print(f\"Failure rate: {failure_rate}%\")\n",
"print(\"Off-hours failed attempts:\", None if off_hours_failed is None else len(off_hours_failed))\n"
]
},
{
"cell_type": "markdown",
"id": "eb9420ec",
"metadata": {
"tags": [
"exercise"
]
},
"source": [
"## 4. Visualize the findings\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3f438336",
"metadata": {
"tags": [
"exercise"
]
},
"outputs": [],
"source": [
"# TODO 14: create a figure with two charts:\n",
"# - a SUCCESS/FAILED bar chart\n",
"# - a horizontal bar chart of the top five IP addresses by failed attempts\n",
"# TODO 15: add titles and labels, save to output/auth_log_analysis.png, and call plt.show()\n"
]
},
{
"cell_type": "markdown",
"id": "fe92f571",
"metadata": {
"tags": [
"exercise"
]
},
"source": [
"## 5. Export results and write a short report\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "608ca490",
"metadata": {
"tags": [
"exercise"
]
},
"outputs": [],
"source": [
"# TODO 16: export clean_df and suspicious_ips to CSV\n",
"# TODO 17: create a summary string containing clean event count, FAILED count,\n",
"# failure rate, top IP, off-hours failures, and recommendations.\n",
"# TODO 18: save summary to output/capstone_summary.txt\n"
]
},
{
"cell_type": "markdown",
"id": "a6f05e23",
"metadata": {},
"source": [
"## Self-check\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "10601dd1",
"metadata": {},
"outputs": [],
"source": [
"assert len(raw_df) > len(clean_df)\n",
"assert clean_df.duplicated().sum() == 0\n",
"assert failed_by_ip.index[0] == \"198.51.100.14\"\n",
"assert top_ip_count >= THRESHOLD\n",
"assert len(off_hours_failed) >= 1\n",
"assert (OUTPUT_DIR / \"auth_log_analysis.png\").exists()\n",
"assert (OUTPUT_DIR / \"capstone_summary.txt\").exists()\n",
"print(\"✓ Capstone lab completed\")\n"
]
},
{
"cell_type": "markdown",
"id": "b56e4f9a",
"metadata": {},
"source": [
"## Report questions\n",
"\n",
"1. Which IP address should be investigated first, and what evidence supports that decision?\n",
"2. In what situations could a threshold of three failed attempts produce a false positive?\n",
"3. Which additional log sources should be reviewed before deciding to block an IP address?\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}