From 8c9ba33fafd43f7ba63f9f1742892fbba00f3f7a Mon Sep 17 00:00:00 2001 From: "qwen.ai[bot]" Date: Wed, 12 Aug 2026 13:19:59 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=B2=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0=D1=82=D0=BE?= =?UTF-8?q?=D1=80=20=D0=B2=D1=85=D0=BE=D0=B4=D0=BD=D1=8B=D1=85=20=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B4=D0=BB=D1=8F=20=D0=B1?= =?UTF-8?q?=D0=B5=D0=B7=D0=BE=D0=BF=D0=B0=D1=81=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B8=20(=D0=B7=D0=B0=D1=89=D0=B8=D1=82=D0=B0=20=D0=BE=D1=82?= =?UTF-8?q?=20SSRF)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/netsleuth/validator.py | 121 +++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 src/netsleuth/validator.py diff --git a/src/netsleuth/validator.py b/src/netsleuth/validator.py new file mode 100644 index 0000000..85911f0 --- /dev/null +++ b/src/netsleuth/validator.py @@ -0,0 +1,121 @@ +"""Валидация входных данных для безопасности.""" + +from __future__ import annotations + +import ipaddress +from typing import Final + +import typer + +# Запрещённые префиксы (private, reserved, multicast) +FORBIDDEN_PREFIXES: Final = [ + "127.", # localhost + "0.", # current network + "10.", # private class A + "192.168.", # private class C + "172.16.", # private class B (начало диапазона) + "172.17.", + "172.18.", + "172.19.", + "172.20.", + "172.21.", + "172.22.", + "172.23.", + "172.24.", + "172.25.", + "172.26.", + "172.27.", + "172.28.", + "172.29.", + "172.30.", + "172.31.", + "224.", # multicast + "240.", # reserved + "::1", # IPv6 localhost + "fe80:", # IPv6 link-local + "fc00:", # IPv6 unique local + "fd00:", # IPv6 unique local +] + + +def validate_target(value: str) -> str: + """Валидировать целевой адрес (target). + + Проверяет, что адрес не является private/reserved IP. + + Args: + value: IP-адрес или hostname для проверки + + Returns: + Оригинальное значение если валидно + + Raises: + typer.BadParameter: Если адрес запрещён + """ + # Пропускаем hostnames (содержат буквы) + if any(c.isalpha() for c in value): + return value + + # Проверка на запрещённые префиксы + value_lower = value.lower() + for prefix in FORBIDDEN_PREFIXES: + if value_lower.startswith(prefix.lower()): + raise typer.BadParameter( + f"Target '{value}' cannot be a private/reserved IP address. " + "Please use a public IP or hostname." + ) + + # Попытка распарсить как IP для дополнительной проверки + try: + ip = ipaddress.ip_address(value) + if ip.is_private or ip.is_reserved or ip.is_multicast: + raise typer.BadParameter( + f"Target '{value}' is a private/reserved/multicast address. " + "Please use a public IP address." + ) + except ValueError: + # Не IP-адрес (возможно hostname), это ок + pass + + return value + + +def validate_bind_address(value: str) -> str: + """Валидировать адрес для привязки (--my-server). + + Args: + value: IP-адрес для проверки + + Returns: + Оригинальное значение если валидно + + Raises: + typer.BadParameter: Если адрес невалиден + """ + try: + ipaddress.ip_address(value) + except ValueError: + raise typer.BadParameter( + f"'{value}' is not a valid IP address for --my-server" + ) + + return value + + +def validate_port(value: int) -> int: + """Валидировать номер порта. + + Args: + value: Номер порта + + Returns: + Номер порта если валиден + + Raises: + typer.BadParameter: Если порт вне диапазона + """ + if not (1 <= value <= 65535): + raise typer.BadParameter( + f"Port {value} must be between 1 and 65535" + ) + return value