Fix Sonar issue, formatting #99
| @@ -2,4 +2,4 @@ | |||||||
|  |  | ||||||
| """MODULE: Specifies app version.""" | """MODULE: Specifies app version.""" | ||||||
|  |  | ||||||
| VERSION = "1.2"  # pragma: no cover | VERSION = "1.3"  # pragma: no cover | ||||||
|   | |||||||
| @@ -2,19 +2,18 @@ | |||||||
|  |  | ||||||
| """MODULE: Provides functions to call various APIs to retrieve IP/prefix information.""" | """MODULE: Provides functions to call various APIs to retrieve IP/prefix information.""" | ||||||
|  |  | ||||||
| from typing import Union |  | ||||||
|  |  | ||||||
| import ipaddress | import ipaddress | ||||||
|  |  | ||||||
| import requests | 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.""" |     """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: |     try: | ||||||
|         resp = requests.get(api_endpoint, timeout=10) |         resp: requests.Response = requests.get(api_endpoint, timeout=10) | ||||||
|         resp.raise_for_status() |         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): |     except (requests.exceptions.JSONDecodeError, requests.exceptions.HTTPError): | ||||||
|         ret = None |         ret = None | ||||||
|     return ret |     return ret | ||||||
| @@ -22,18 +21,18 @@ def get_ip_information(ipv4_address: ipaddress.IPv4Address) -> dict: | |||||||
|  |  | ||||||
| def get_autonomous_system_number(as_info: str) -> str: | def get_autonomous_system_number(as_info: str) -> str: | ||||||
|     """Parses AS number from provided AS information.""" |     """Parses AS number from provided AS information.""" | ||||||
|     as_number = as_info.split(" ")[0] |     as_number: str = as_info.split(" ")[0] | ||||||
|     return as_number |     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.""" |     """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: |     try: | ||||||
|         resp = requests.get(api_endpoint, timeout=10) |         resp: requests.Response = requests.get(api_endpoint, timeout=10) | ||||||
|         resp.raise_for_status() |         resp.raise_for_status() | ||||||
|     except requests.exceptions.HTTPError: |     except requests.exceptions.HTTPError: | ||||||
|         return None |         return None | ||||||
|     prefixes = resp.text.split("\n") |     prefixes: list[str] = resp.text.split("\n") | ||||||
|     prefixes.pop(0) |     prefixes.pop(0) | ||||||
|     return prefixes |     return prefixes | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								app/main.py
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								app/main.py
									
									
									
									
									
								
							| @@ -5,14 +5,13 @@ | |||||||
| import sys | import sys | ||||||
|  |  | ||||||
| from app.args import parse_args | 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 ( | from app.ip_info import ( | ||||||
|     get_ip_information, |  | ||||||
|     get_autonomous_system_number, |     get_autonomous_system_number, | ||||||
|  |     get_ip_information, | ||||||
|     get_prefix_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 = """----------------------------------------------- | HEADER = """----------------------------------------------- | ||||||
| | IP Address Information Lookup Tool (iPilot) | | | IP Address Information Lookup Tool (iPilot) | | ||||||
| @@ -20,16 +19,14 @@ HEADER = """----------------------------------------------- | |||||||
| -----------------------------------------------\n""" | -----------------------------------------------\n""" | ||||||
|  |  | ||||||
|  |  | ||||||
| def main(): | def main() -> None: | ||||||
|     """Main function.""" |     """Main function.""" | ||||||
|     args = parse_args() |     args = parse_args() | ||||||
|     if not args.noheader: |     if not args.noheader: | ||||||
|         print(HEADER) |         print(HEADER) | ||||||
|  |  | ||||||
|     # Set IP to passed IP address, or resolve passed domain name to IPv4 |     # Set IP to passed IP address, or resolve passed domain name to IPv4 | ||||||
|     ip_address = ( |     ip_address = resolve_domain_name(args.query) if not is_ip_address(args.query) else args.query | ||||||
|         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 given an IPv4, and can't resolve to IPv4, then throw error and exit | ||||||
|     if not ip_address: |     if not ip_address: | ||||||
| @@ -37,22 +34,22 @@ def main(): | |||||||
|         sys.exit(1) |         sys.exit(1) | ||||||
|  |  | ||||||
|     # Get information from API |     # Get information from API | ||||||
|     ip_info = get_ip_information(ip_address) |     ip_info: dict | None = get_ip_information(ip_address) | ||||||
|     if not ip_info: |     if not ip_info: | ||||||
|         print("ERROR: could not retrieve IP information from API.") |         print("ERROR: could not retrieve IP information from API.") | ||||||
|         sys.exit(1) |         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 |     # Assemble list for table generation | ||||||
|     country = ip_info.get("country") |     country: str = ip_info["country"] | ||||||
|     region = ip_info.get("regionName") |     region: str = ip_info["regionName"] | ||||||
|     city = ip_info.get("city") |     city: str = ip_info["city"] | ||||||
|     table_data = [ |     table_data: list = [ | ||||||
|         ["IP Address", ip_info.get("query")], |         ["IP Address", ip_info["query"]], | ||||||
|         ["Organization", ip_info.get("org")], |         ["Organization", ip_info["org"]], | ||||||
|         ["Location", f"{country}/{region}/{city}"], |         ["Location", f"{country}/{region}/{city}"], | ||||||
|         ["Timezone", ip_info.get("timezone")], |         ["Timezone", ip_info["timezone"]], | ||||||
|         ["Internet Service Provider", ip_info.get("isp")], |         ["Internet Service Provider", ip_info["isp"]], | ||||||
|         ["Autonomous System", as_number], |         ["Autonomous System", as_number], | ||||||
|     ] |     ] | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,15 +2,14 @@ | |||||||
|  |  | ||||||
| """MODULE: Provides functions for preparing, then printing, retrieved data.""" | """MODULE: Provides functions for preparing, then printing, retrieved data.""" | ||||||
|  |  | ||||||
| from typing import Union |  | ||||||
| from tabulate import tabulate | 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.""" |     """Generate a string that spilts prefixes into rows of 4.""" | ||||||
|     num_per_row = 4 |     num_per_row = 4 | ||||||
|     try: |     try: | ||||||
|         ret = "" |         ret: str = "" | ||||||
|         for i in range(0, len(prefixes), num_per_row): |         for i in range(0, len(prefixes), num_per_row): | ||||||
|             ret += ", ".join(prefixes[i : i + num_per_row]) + "\n" |             ret += ", ".join(prefixes[i : i + num_per_row]) + "\n" | ||||||
|         return ret |         return ret | ||||||
|   | |||||||
| @@ -3,8 +3,9 @@ | |||||||
| """MODULE: Provides functions that ensure an IP address is | """MODULE: Provides functions that ensure an IP address is | ||||||
| available to query the APIs for.""" | available to query the APIs for.""" | ||||||
|  |  | ||||||
| import socket |  | ||||||
| import ipaddress | import ipaddress | ||||||
|  | import socket | ||||||
|  |  | ||||||
| import requests | import requests | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -17,16 +18,17 @@ def is_ip_address(query: str) -> bool: | |||||||
|         return False |         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.""" |     """Resolve a domain name via DNS or return None.""" | ||||||
|     try: |     try: | ||||||
|         ip_address = socket.gethostbyname(domain_name) |         result: str = socket.gethostbyname(domain_name) | ||||||
|     except socket.gaierror: |         ip_address: ipaddress.IPv4Address = ipaddress.IPv4Address(result) | ||||||
|         ip_address = None |         return ip_address | ||||||
|     return ip_address |     except (socket.gaierror, ipaddress.AddressValueError): | ||||||
|  |         return None | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_public_ip() -> ipaddress.IPv4Address: | def get_public_ip() -> ipaddress.IPv4Address: | ||||||
|     """Get the user's current public IPv4 address.""" |     """Get the user's current public IPv4 address.""" | ||||||
|     ip_address = requests.get("https://api.ipify.org", timeout=10).text |     ip_address: str = requests.get("https://api.ipify.org", timeout=10).text | ||||||
|     return ip_address |     return ipaddress.IPv4Address(ip_address) | ||||||
|   | |||||||
| @@ -1,15 +1,12 @@ | |||||||
| astroid==2.15.5 | astroid==2.15.5 | ||||||
| attrs==23.1.0 | attrs==23.1.0 | ||||||
| black==23.3.0 |  | ||||||
| certifi==2023.5.7 | certifi==2023.5.7 | ||||||
| charset-normalizer==3.1.0 | charset-normalizer==3.1.0 | ||||||
| click==8.1.3 | click==8.1.3 | ||||||
| coverage==7.2.7 |  | ||||||
| dill==0.3.6 | dill==0.3.6 | ||||||
| exceptiongroup==1.1.1 | exceptiongroup==1.1.1 | ||||||
| idna==3.4 | idna==3.4 | ||||||
| iniconfig==2.0.0 | iniconfig==2.0.0 | ||||||
| isort==5.12.0 |  | ||||||
| lazy-object-proxy==1.9.0 | lazy-object-proxy==1.9.0 | ||||||
| mccabe==0.7.0 | mccabe==0.7.0 | ||||||
| mypy-extensions==1.0.0 | mypy-extensions==1.0.0 | ||||||
| @@ -18,11 +15,8 @@ pathspec==0.11.1 | |||||||
| platformdirs==3.5.1 | platformdirs==3.5.1 | ||||||
| pluggy==1.0.0 | pluggy==1.0.0 | ||||||
| py==1.11.0 | py==1.11.0 | ||||||
| pylint==2.17.4 |  | ||||||
| pyparsing==3.0.9 | pyparsing==3.0.9 | ||||||
| pytest==7.3.1 |  | ||||||
| requests==2.31.0 | requests==2.31.0 | ||||||
| requests-mock==1.10.0 |  | ||||||
| six==1.16.0 | six==1.16.0 | ||||||
| tabulate==0.9.0 | tabulate==0.9.0 | ||||||
| tomli==2.0.1 | tomli==2.0.1 | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								setup.py
									
									
									
									
									
								
							| @@ -6,7 +6,6 @@ from setuptools import setup | |||||||
|  |  | ||||||
| from app._version import VERSION | from app._version import VERSION | ||||||
|  |  | ||||||
|  |  | ||||||
| dependencies = [] | dependencies = [] | ||||||
| with open("requirements.txt", "r", encoding="ascii") as dep_file: | with open("requirements.txt", "r", encoding="ascii") as dep_file: | ||||||
|     for dep_line in dep_file.readlines(): |     for dep_line in dep_file.readlines(): | ||||||
|   | |||||||
| @@ -5,15 +5,15 @@ | |||||||
| import requests_mock | import requests_mock | ||||||
|  |  | ||||||
| from app.ip_info import (  # pragma: no cover | from app.ip_info import (  # pragma: no cover | ||||||
|     get_ip_information, |  | ||||||
|     get_autonomous_system_number, |     get_autonomous_system_number, | ||||||
|  |     get_ip_information, | ||||||
|     get_prefix_information, |     get_prefix_information, | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_get_ip_information() -> None: | def test_get_ip_information() -> None: | ||||||
|     """TEST: ensure that the IP information API is working correctly.""" |     """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) |     ip_info = get_ip_information(test_query) | ||||||
|     assert ip_info.get("status") == "success" and ip_info.get("query") == test_query |     assert ip_info.get("status") == "success" and ip_info.get("query") == test_query | ||||||
|  |  | ||||||
| @@ -39,7 +39,7 @@ def test_get_ip_information_bad_response() -> None: | |||||||
| def test_get_autonomous_system_number() -> None: | def test_get_autonomous_system_number() -> None: | ||||||
|     """TEST: ensure that AS information is parsed into AS number correctly.""" |     """TEST: ensure that AS information is parsed into AS number correctly.""" | ||||||
|     as_info = "AS5089 Virgin Media Limited" |     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" |     assert as_number == "AS5089" | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,13 +7,13 @@ from app.print_table import generate_prefix_string  # pragma: no cover | |||||||
|  |  | ||||||
| def test_generate_prefix_string_small() -> None: | def test_generate_prefix_string_small() -> None: | ||||||
|     """TEST: Verifies if a small prefix list results in one line.""" |     """TEST: Verifies if a small prefix list results in one line.""" | ||||||
|     test_query = ["abc", "def"] |     test_query: list[str] = ["abc", "def"] | ||||||
|     result = generate_prefix_string(prefixes=test_query) |     result: str | None = generate_prefix_string(prefixes=test_query) | ||||||
|     assert result == "abc, def\n" |     assert result == "abc, def\n" | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_generate_prefix_string_large() -> None: | def test_generate_prefix_string_large() -> None: | ||||||
|     """TEST: Verifies if a large prefix list results in multiple lines.""" |     """TEST: Verifies if a large prefix list results in multiple lines.""" | ||||||
|     test_query = ["abc", "def", "ghi", "jkl", "mno", "pqr"] |     test_query: list[str] = ["abc", "def", "ghi", "jkl", "mno", "pqr"] | ||||||
|     result = generate_prefix_string(prefixes=test_query) |     result: str | None = generate_prefix_string(prefixes=test_query) | ||||||
|     assert result == "abc, def, ghi, jkl\nmno, pqr\n" |     assert result == "abc, def, ghi, jkl\nmno, pqr\n" | ||||||
|   | |||||||
| @@ -3,9 +3,9 @@ | |||||||
| """MODULE: Provides test cases for app/query_normalisation.py.""" | """MODULE: Provides test cases for app/query_normalisation.py.""" | ||||||
|  |  | ||||||
| from app.query_normalisation import (  # pragma: no cover | from app.query_normalisation import (  # pragma: no cover | ||||||
|  |     get_public_ip, | ||||||
|     is_ip_address, |     is_ip_address, | ||||||
|     resolve_domain_name, |     resolve_domain_name, | ||||||
|     get_public_ip, |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -30,8 +30,8 @@ def test_is_ip_address_false_fqdn() -> None: | |||||||
| def test_resolve_domain_name_true() -> None: | def test_resolve_domain_name_true() -> None: | ||||||
|     """TEST: Verifies that DNS resolution is working correctly.""" |     """TEST: Verifies that DNS resolution is working correctly.""" | ||||||
|     domain_name = "one.one.one.one" |     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 |     assert str(resolve_domain_name(domain_name)) in expected_results | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_resolve_domain_name_false() -> None: | def test_resolve_domain_name_false() -> None: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user