This Python script acts as a local security monitor that watches incoming HTTP requests and alerts the user when suspicious activity is detected. It runs a lightweight HTTP server and inspects every GET and POST request for signs of intrusion, such as dangerous protocols (gopher://), command‑execution keywords (bash, powershell, cmd.exe), encoded payloads, or other attack patterns. Before scanning the request, the script verifies that the incoming connection comes from a specific trusted IP and MAC address. This prevents unauthorized devices from interacting with the server. If the IP or MAC does not match the configured values, the request is immediately rejected.
When a malicious pattern is detected, the script logs the event and displays a popup alert window on the user’s desktop. This popup is implemented using PyQt6 and appears as a small, always‑on‑top notification in the corner of the screen. It fades out automatically after a few seconds, ensuring the user is visually warned without relying on Windows system notifications.
The script also includes a tray icon with a menu that allows the user to update the trusted IP and MAC address. All events are written to a log file for later review. Overall, it functions as a simple intrusion‑detection and alerting tool for local network traffic.
This is a groper test on my browser and result was:

Let's see the source code:
import re
import threading
import queue
import subprocess
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
from datetime import datetime
import sys
from urllib.parse import unquote
# FIX pentru Windows 10/11 – AppID
import ctypes
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("Python.SecurityMonitor")
from PyQt6.QtWidgets import (
QApplication, QSystemTrayIcon, QMenu, QDialog,
QVBoxLayout, QLabel, QLineEdit, QPushButton, QWidget
)
from PyQt6.QtGui import QIcon
from PyQt6.QtCore import QTimer, Qt, QPropertyAnimation
# ---------------- CONFIG ---------------- #
ALERT_QUEUE = queue.Queue()
LOG_FILE = "security_log.txt"
MONITOR_IP = "192.168.0.136"
MONITOR_MAC = "60:45:cb:c3:4d:02" # normalizat
RULES = {
"Gopher protocol": r"gopher://",
"CRLF injection": r"%0d%0a",
"WinRM port": r"5985",
"Base64 payload": r"base64",
"bash execution": r"bash",
"powershell execution": r"powershell",
"cmd execution": r"cmd\.exe",
}
# ---------------------------------------- #
def log_event(text: str):
ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
line = f"[{ts}] {text}\n"
with open(LOG_FILE, "a", encoding="utf-8") as f:
f.write(line)
print(line, end="")
def get_mac(ip):
try:
output = subprocess.check_output(["arp", "-a", ip], text=True)
for line in output.splitlines():
if ip in line:
mac = line.split()[1]
mac = mac.replace("-", ":").lower()
return mac
except:
return None
# ---------------- POPUP ALERT ---------------- #
class AlertPopup(QWidget):
def __init__(self, message):
super().__init__()
self.setWindowFlags(
Qt.WindowType.FramelessWindowHint |
Qt.WindowType.Tool |
Qt.WindowType.WindowStaysOnTopHint
)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
layout = QVBoxLayout()
label = QLabel(message)
label.setStyleSheet("""
background-color: #CC0000;
color: white;
padding: 12px;
border-radius: 8px;
font-size: 14px;
""")
layout.addWidget(label)
self.setLayout(layout)
self.resize(350, 80)
screen = QApplication.primaryScreen().geometry()
self.move(screen.width() - 380, screen.height() - 150)
self.show()
self.anim = QPropertyAnimation(self, b"windowOpacity")
self.anim.setDuration(6000)
self.anim.setStartValue(1.0)
self.anim.setEndValue(0.0)
self.anim.finished.connect(self.close)
self.anim.start()
# ---------------- HTTP SERVER ---------------- #
class SecurityHandler(BaseHTTPRequestHandler):
def _check_ip_mac(self):
global MONITOR_IP, MONITOR_MAC
client_ip = self.client_address[0]
# bypass MAC check dacă este același PC
if client_ip == MONITOR_IP:
return True, None
if client_ip != MONITOR_IP:
return False, f"IP neautorizat: {client_ip}"
mac = get_mac(client_ip)
if mac is None:
return False, f"MAC necunoscut pentru {client_ip}"
if mac.lower() != MONITOR_MAC.lower():
return False, f"MAC neautorizat: {mac} pentru IP {client_ip}"
return True, None
def _scan_and_block(self, source: str, data: str):
data_lower = data.lower()
for reason, pattern in RULES.items():
if re.search(pattern, data_lower):
msg = f"Blocked {source}: {reason} → {data}"
log_event(msg)
ALERT_QUEUE.put(msg)
self.send_response(403)
self.end_headers()
self.wfile.write(f"Cerere blocată: {reason}".encode("utf-8"))
return True
return False
def do_GET(self):
ok, reason = self._check_ip_mac()
if not ok:
ALERT_QUEUE.put(reason)
log_event(reason)
self.send_response(403)
self.end_headers()
self.wfile.write(reason.encode())
return
url = unquote(self.path)
if self._scan_and_block("GET URL", url):
return
self.send_response(200)
self.end_headers()
self.wfile.write(b"Cerere GET permisa")
log_event(f"Allowed GET: {url}")
def do_POST(self):
ok, reason = self._check_ip_mac()
if not ok:
ALERT_QUEUE.put(reason)
log_event(reason)
self.send_response(403)
self.end_headers()
self.wfile.write(reason.encode())
return
url = unquote(self.path)
content_length = int(self.headers.get("Content-Length", 0))
body = unquote(self.rfile.read(content_length).decode("utf-8", errors="ignore"))
if self._scan_and_block("POST URL", url):
return
if self._scan_and_block("POST body", body):
return
self.send_response(200)
self.end_headers()
self.wfile.write(b"Cerere POST permisa")
log_event(f"Allowed POST: {url} | body: {body[:200]}")
def run_server():
server = HTTPServer(("0.0.0.0", 8080), SecurityHandler)
log_event("Security server running on http://0.0.0.0:8080")
server.serve_forever()
# ---------------- DIALOG IP & MAC ---------------- #
class IpMacDialog(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("Set IP & MAC")
layout = QVBoxLayout()
self.ip_label = QLabel("IP Address:")
self.ip_input = QLineEdit()
self.ip_input.setText(MONITOR_IP)
self.mac_label = QLabel("MAC Address:")
self.mac_input = QLineEdit()
self.mac_input.setText(MONITOR_MAC)
self.save_btn = QPushButton("Save")
self.save_btn.clicked.connect(self.save_values)
layout.addWidget(self.ip_label)
layout.addWidget(self.ip_input)
layout.addWidget(self.mac_label)
layout.addWidget(self.mac_input)
layout.addWidget(self.save_btn)
self.setLayout(layout)
def save_values(self):
global MONITOR_IP, MONITOR_MAC
MONITOR_IP = self.ip_input.text().strip()
MONITOR_MAC = self.mac_input.text().strip().replace("-", ":").lower()
log_event(f"Updated IP/MAC → IP={MONITOR_IP}, MAC={MONITOR_MAC}")
self.hide()
# ---------------- TRAY APP ---------------- #
class TrayApp:
def __init__(self):
self.app = QApplication(sys.argv)
icon = QIcon("icon.svg") if os.path.exists("icon.svg") else QIcon()
self.tray = QSystemTrayIcon(icon)
self.tray.setToolTip("Python Security Monitor")
menu = QMenu()
set_ip_mac = menu.addAction("Set IP & MAC")
set_ip_mac.triggered.connect(self.open_ip_mac_dialog)
exit_action = menu.addAction("Exit")
exit_action.triggered.connect(self.app.quit)
self.tray.setContextMenu(menu)
self.tray.show()
self.dialog = None
self.timer = QTimer()
self.timer.timeout.connect(self.check_alerts)
self.timer.start(300)
def open_ip_mac_dialog(self):
if self.dialog is None:
self.dialog = IpMacDialog()
self.dialog.show()
self.dialog.raise_()
self.dialog.activateWindow()
def check_alerts(self):
while not ALERT_QUEUE.empty():
msg = ALERT_QUEUE.get()
AlertPopup(msg)
def run(self):
self.app.exec()
# ---------------- MAIN ---------------- #
def main():
server_thread = threading.Thread(target=run_server, daemon=True)
server_thread.start()
tray = TrayApp()
tray.run()
if __name__ == "__main__":
main()