From 1b09bc300dd1bb77d52f8b5de7f7259a4d0c7d0d Mon Sep 17 00:00:00 2001 From: Luke Tainton Date: Sun, 4 Jun 2023 11:52:10 +0100 Subject: [PATCH 1/3] Fix Sonar issue, formatting --- app/_version.py | 2 +- app/ip_info.py | 21 ++++++++++---------- app/main.py | 33 ++++++++++++++----------------- app/print_table.py | 5 ++--- app/query_normalisation.py | 11 ++++++----- setup.py | 1 - tests/test_ip_info.py | 2 +- tests/test_print_table.py | 8 ++++---- tests/test_query_normalisation.py | 4 ++-- 9 files changed, 41 insertions(+), 46 deletions(-) diff --git a/app/_version.py b/app/_version.py index fc0c99d..c3b8225 100644 --- a/app/_version.py +++ b/app/_version.py @@ -2,4 +2,4 @@ """MODULE: Specifies app version.""" -VERSION = "1.2" # pragma: no cover +VERSION = "1.3" # pragma: no cover diff --git a/app/ip_info.py b/app/ip_info.py index 8a8e6e5..8435232 100644 --- a/app/ip_info.py +++ b/app/ip_info.py @@ -2,19 +2,18 @@ """MODULE: Provides functions to call various APIs to retrieve IP/prefix information.""" -from typing import Union - import ipaddress + import requests -def get_ip_information(ipv4_address: ipaddress.IPv4Address) -> dict: +def get_ip_information(ipv4_address: ipaddress.IPv4Address) -> dict | None: """Retrieves information about a given IPv4 address from IP-API.com.""" - api_endpoint = f"http://ip-api.com/json/{ipv4_address}" + api_endpoint: str = f"http://ip-api.com/json/{ipv4_address}" try: - resp = requests.get(api_endpoint, timeout=10) + resp: requests.Response = requests.get(api_endpoint, timeout=10) resp.raise_for_status() - ret = resp.json() if resp.json().get("status") == "success" else None + ret: dict | None = resp.json() if resp.json().get("status") == "success" else None except (requests.exceptions.JSONDecodeError, requests.exceptions.HTTPError): ret = None return ret @@ -22,18 +21,18 @@ def get_ip_information(ipv4_address: ipaddress.IPv4Address) -> dict: def get_autonomous_system_number(as_info: str) -> str: """Parses AS number from provided AS information.""" - as_number = as_info.split(" ")[0] + as_number: str = as_info.split(" ")[0] return as_number -def get_prefix_information(autonomous_system: int) -> Union[list, None]: +def get_prefix_information(autonomous_system: str) -> list | None: """Retrieves prefix information about a given autonomous system.""" - api_endpoint = f"https://api.hackertarget.com/aslookup/?q={str(autonomous_system)}" + api_endpoint: str = f"https://api.hackertarget.com/aslookup/?q={str(autonomous_system)}" try: - resp = requests.get(api_endpoint, timeout=10) + resp: requests.Response = requests.get(api_endpoint, timeout=10) resp.raise_for_status() except requests.exceptions.HTTPError: return None - prefixes = resp.text.split("\n") + prefixes: list[str] = resp.text.split("\n") prefixes.pop(0) return prefixes diff --git a/app/main.py b/app/main.py index b91a625..501dfb3 100644 --- a/app/main.py +++ b/app/main.py @@ -5,14 +5,13 @@ import sys from app.args import parse_args -from app.print_table import print_table, generate_prefix_string -from app.query_normalisation import is_ip_address, resolve_domain_name from app.ip_info import ( - get_ip_information, get_autonomous_system_number, + get_ip_information, get_prefix_information, ) - +from app.print_table import generate_prefix_string, print_table +from app.query_normalisation import is_ip_address, resolve_domain_name HEADER = """----------------------------------------------- | IP Address Information Lookup Tool (iPilot) | @@ -20,16 +19,14 @@ HEADER = """----------------------------------------------- -----------------------------------------------\n""" -def main(): +def main() -> None: """Main function.""" args = parse_args() if not args.noheader: print(HEADER) # Set IP to passed IP address, or resolve passed domain name to IPv4 - ip_address = ( - resolve_domain_name(args.query) if not is_ip_address(args.query) else args.query - ) + ip_address = resolve_domain_name(args.query) if not is_ip_address(args.query) else args.query # If not given an IPv4, and can't resolve to IPv4, then throw error and exit if not ip_address: @@ -37,22 +34,22 @@ def main(): sys.exit(1) # Get information from API - ip_info = get_ip_information(ip_address) + ip_info: dict | None = get_ip_information(ip_address) if not ip_info: print("ERROR: could not retrieve IP information from API.") sys.exit(1) - as_number = get_autonomous_system_number(ip_info.get("as")) + as_number: str = get_autonomous_system_number(ip_info["as"]) # Assemble list for table generation - country = ip_info.get("country") - region = ip_info.get("regionName") - city = ip_info.get("city") - table_data = [ - ["IP Address", ip_info.get("query")], - ["Organization", ip_info.get("org")], + country: str = ip_info["country"] + region: str = ip_info["regionName"] + city: str = ip_info["city"] + table_data: list = [ + ["IP Address", ip_info["query"]], + ["Organization", ip_info["org"]], ["Location", f"{country}/{region}/{city}"], - ["Timezone", ip_info.get("timezone")], - ["Internet Service Provider", ip_info.get("isp")], + ["Timezone", ip_info["timezone"]], + ["Internet Service Provider", ip_info["isp"]], ["Autonomous System", as_number], ] diff --git a/app/print_table.py b/app/print_table.py index cbb8a74..2b71656 100644 --- a/app/print_table.py +++ b/app/print_table.py @@ -2,15 +2,14 @@ """MODULE: Provides functions for preparing, then printing, retrieved data.""" -from typing import Union from tabulate import tabulate -def generate_prefix_string(prefixes: list) -> Union[str, None]: +def generate_prefix_string(prefixes: list) -> str | None: """Generate a string that spilts prefixes into rows of 4.""" num_per_row = 4 try: - ret = "" + ret: str = "" for i in range(0, len(prefixes), num_per_row): ret += ", ".join(prefixes[i : i + num_per_row]) + "\n" return ret diff --git a/app/query_normalisation.py b/app/query_normalisation.py index fb6b783..f5daa85 100644 --- a/app/query_normalisation.py +++ b/app/query_normalisation.py @@ -3,8 +3,9 @@ """MODULE: Provides functions that ensure an IP address is available to query the APIs for.""" -import socket import ipaddress +import socket + import requests @@ -17,10 +18,10 @@ def is_ip_address(query: str) -> bool: return False -def resolve_domain_name(domain_name: str) -> ipaddress.IPv4Address: +def resolve_domain_name(domain_name: str) -> ipaddress.IPv4Address | None: """Resolve a domain name via DNS or return None.""" try: - ip_address = socket.gethostbyname(domain_name) + ip_address: ipaddress.IPv4Address = ipaddress.IPv4Address(socket.gethostbyname(domain_name)) except socket.gaierror: ip_address = None return ip_address @@ -28,5 +29,5 @@ def resolve_domain_name(domain_name: str) -> ipaddress.IPv4Address: def get_public_ip() -> ipaddress.IPv4Address: """Get the user's current public IPv4 address.""" - ip_address = requests.get("https://api.ipify.org", timeout=10).text - return ip_address + ip_address: str = requests.get("https://api.ipify.org", timeout=10).text + return ipaddress.IPv4Address(ip_address) diff --git a/setup.py b/setup.py index 98e3d45..9eba7ee 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,6 @@ from setuptools import setup from app._version import VERSION - dependencies = [] with open("requirements.txt", "r", encoding="ascii") as dep_file: for dep_line in dep_file.readlines(): diff --git a/tests/test_ip_info.py b/tests/test_ip_info.py index 32906bf..52af7a9 100644 --- a/tests/test_ip_info.py +++ b/tests/test_ip_info.py @@ -39,7 +39,7 @@ def test_get_ip_information_bad_response() -> None: def test_get_autonomous_system_number() -> None: """TEST: ensure that AS information is parsed into AS number correctly.""" as_info = "AS5089 Virgin Media Limited" - as_number = get_autonomous_system_number(as_info) + as_number: str = get_autonomous_system_number(as_info) assert as_number == "AS5089" diff --git a/tests/test_print_table.py b/tests/test_print_table.py index 7610486..d5907dd 100644 --- a/tests/test_print_table.py +++ b/tests/test_print_table.py @@ -7,13 +7,13 @@ from app.print_table import generate_prefix_string # pragma: no cover def test_generate_prefix_string_small() -> None: """TEST: Verifies if a small prefix list results in one line.""" - test_query = ["abc", "def"] - result = generate_prefix_string(prefixes=test_query) + test_query: list[str] = ["abc", "def"] + result: str | None = generate_prefix_string(prefixes=test_query) assert result == "abc, def\n" def test_generate_prefix_string_large() -> None: """TEST: Verifies if a large prefix list results in multiple lines.""" - test_query = ["abc", "def", "ghi", "jkl", "mno", "pqr"] - result = generate_prefix_string(prefixes=test_query) + test_query: list[str] = ["abc", "def", "ghi", "jkl", "mno", "pqr"] + result: str | None = generate_prefix_string(prefixes=test_query) assert result == "abc, def, ghi, jkl\nmno, pqr\n" diff --git a/tests/test_query_normalisation.py b/tests/test_query_normalisation.py index 6fdd6a6..569625e 100644 --- a/tests/test_query_normalisation.py +++ b/tests/test_query_normalisation.py @@ -3,9 +3,9 @@ """MODULE: Provides test cases for app/query_normalisation.py.""" from app.query_normalisation import ( # pragma: no cover + get_public_ip, is_ip_address, resolve_domain_name, - get_public_ip, ) @@ -30,7 +30,7 @@ def test_is_ip_address_false_fqdn() -> None: def test_resolve_domain_name_true() -> None: """TEST: Verifies that DNS resolution is working correctly.""" domain_name = "one.one.one.one" - expected_results = ["1.1.1.1", "1.0.0.1"] # Could resolve to either IP + expected_results: list[str] = ["1.1.1.1", "1.0.0.1"] # Could resolve to either IP assert resolve_domain_name(domain_name) in expected_results -- 2.47.2 From a2455dff8885ca65b2b5ed15fb38999892db73d0 Mon Sep 17 00:00:00 2001 From: Luke Tainton Date: Sun, 4 Jun 2023 11:53:20 +0100 Subject: [PATCH 2/3] Remove dev dependencies from requirements.tzt --- requirements.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7072341..ab57ae4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,15 +1,12 @@ astroid==2.15.5 attrs==23.1.0 -black==23.3.0 certifi==2023.5.7 charset-normalizer==3.1.0 click==8.1.3 -coverage==7.2.7 dill==0.3.6 exceptiongroup==1.1.1 idna==3.4 iniconfig==2.0.0 -isort==5.12.0 lazy-object-proxy==1.9.0 mccabe==0.7.0 mypy-extensions==1.0.0 @@ -18,11 +15,8 @@ pathspec==0.11.1 platformdirs==3.5.1 pluggy==1.0.0 py==1.11.0 -pylint==2.17.4 pyparsing==3.0.9 -pytest==7.3.1 requests==2.31.0 -requests-mock==1.10.0 six==1.16.0 tabulate==0.9.0 tomli==2.0.1 -- 2.47.2 From 7f2766757c0e597acf7ca78f7e0a5cd39ddce535 Mon Sep 17 00:00:00 2001 From: Luke Tainton Date: Sun, 4 Jun 2023 12:02:26 +0100 Subject: [PATCH 3/3] Fix unit test failure --- app/query_normalisation.py | 9 +++++---- tests/test_ip_info.py | 4 ++-- tests/test_query_normalisation.py | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/query_normalisation.py b/app/query_normalisation.py index f5daa85..595ae21 100644 --- a/app/query_normalisation.py +++ b/app/query_normalisation.py @@ -21,10 +21,11 @@ def is_ip_address(query: str) -> bool: def resolve_domain_name(domain_name: str) -> ipaddress.IPv4Address | None: """Resolve a domain name via DNS or return None.""" try: - ip_address: ipaddress.IPv4Address = ipaddress.IPv4Address(socket.gethostbyname(domain_name)) - except socket.gaierror: - ip_address = None - return ip_address + result: str = socket.gethostbyname(domain_name) + ip_address: ipaddress.IPv4Address = ipaddress.IPv4Address(result) + return ip_address + except (socket.gaierror, ipaddress.AddressValueError): + return None def get_public_ip() -> ipaddress.IPv4Address: diff --git a/tests/test_ip_info.py b/tests/test_ip_info.py index 52af7a9..d8901e2 100644 --- a/tests/test_ip_info.py +++ b/tests/test_ip_info.py @@ -5,15 +5,15 @@ import requests_mock from app.ip_info import ( # pragma: no cover - get_ip_information, get_autonomous_system_number, + get_ip_information, get_prefix_information, ) def test_get_ip_information() -> None: """TEST: ensure that the IP information API is working correctly.""" - test_query = "1.2.3.4" + test_query: str = "1.2.3.4" ip_info = get_ip_information(test_query) assert ip_info.get("status") == "success" and ip_info.get("query") == test_query diff --git a/tests/test_query_normalisation.py b/tests/test_query_normalisation.py index 569625e..f6183ec 100644 --- a/tests/test_query_normalisation.py +++ b/tests/test_query_normalisation.py @@ -31,7 +31,7 @@ def test_resolve_domain_name_true() -> None: """TEST: Verifies that DNS resolution is working correctly.""" domain_name = "one.one.one.one" expected_results: list[str] = ["1.1.1.1", "1.0.0.1"] # Could resolve to either IP - assert resolve_domain_name(domain_name) in expected_results + assert str(resolve_domain_name(domain_name)) in expected_results def test_resolve_domain_name_false() -> None: -- 2.47.2