Skip to content

Commit 791cc20

Browse files
useruser
authored andcommitted
maintenance: Update project to use newer standards
1 parent 7f43542 commit 791cc20

File tree

6 files changed

+290
-142
lines changed

6 files changed

+290
-142
lines changed

harlogger/__main__.py

Lines changed: 82 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,107 @@
1-
import click
2-
from pymobiledevice3.cli.cli_common import Command
3-
from pymobiledevice3.lockdown_service_provider import LockdownServiceProvider
1+
from typing import Annotated, Optional
42

5-
from harlogger.sniffers import Filters, HostSnifferProfile, MobileSnifferProfile, SnifferPreference
6-
7-
8-
@click.group()
9-
def cli():
10-
pass
11-
12-
13-
@cli.group()
14-
def mobile():
15-
""" Mobile sniffing options """
16-
pass
3+
import typer
4+
from pymobiledevice3.cli.cli_common import ServiceProviderDep
5+
from typer_injector import InjectingTyper
176

7+
from harlogger.sniffers import Filters, HostSnifferProfile, MobileSnifferProfile, SnifferPreference
188

19-
@mobile.command('profile', cls=Command)
20-
@click.option('pids', '-p', '--pid', type=click.INT, multiple=True, help='filter pid list')
21-
@click.option('--color/--no-color', default=True)
22-
@click.option('process_names', '-pn', '--process-name', multiple=True, help='filter process name list')
23-
@click.option('images', '-i', '--image', multiple=True, help='filter image list')
24-
@click.option('--request/--no-request', is_flag=True, default=True, help='show requests')
25-
@click.option('--response/--no-response', is_flag=True, default=True, help='show responses')
26-
@click.option('-u', '--unique', is_flag=True, help='show only unique requests per image/pid/method/uri combination')
27-
@click.option('--black-list/--white-list', default=True, is_flag=True)
28-
def mobile_profile(service_provider: LockdownServiceProvider, pids, process_names, color, request, response, images,
29-
unique, black_list):
9+
cli = InjectingTyper(
10+
help="Monitor HTTP traffic on given macOS/iOS devices.",
11+
no_args_is_help=True,
12+
)
13+
mobile = InjectingTyper(help="Mobile sniffing options")
14+
cli.add_typer(mobile, name="mobile")
15+
16+
17+
@mobile.command("profile")
18+
def mobile_profile(
19+
service_provider: ServiceProviderDep,
20+
pids: Annotated[Optional[list[int]], typer.Option("-p", "--pid", help="filter pid list")] = None,
21+
process_names: Annotated[
22+
Optional[list[str]],
23+
typer.Option("-pn", "--process-name", help="filter process name list"),
24+
] = None,
25+
color: Annotated[bool, typer.Option("--color/--no-color")] = True,
26+
request: Annotated[bool, typer.Option("--request/--no-request", help="show requests")] = True,
27+
response: Annotated[bool, typer.Option("--response/--no-response", help="show responses")] = True,
28+
images: Annotated[Optional[list[str]], typer.Option("-i", "--image", help="filter image list")] = None,
29+
unique: Annotated[
30+
bool,
31+
typer.Option("-u", "--unique", help="show only unique requests per image/pid/method/uri combination"),
32+
] = False,
33+
black_list: Annotated[bool, typer.Option("--black-list/--white-list")] = True,
34+
):
3035
"""
3136
Sniff using CFNetworkDiagnostics.mobileconfig profile.
3237
3338
This requires the specific Apple profile to be installed for the sniff to work.
3439
"""
3540
filters = Filters(pids, process_names, images, black_list)
36-
MobileSnifferProfile(service_provider, filters=filters, request=request, response=response, color=color,
37-
unique=unique).sniff()
38-
39-
40-
@mobile.command('preference', cls=Command)
41-
@click.option('-o', '--out', type=click.File('w'), help='file to store the har entries into upon exit (ctrl+c)')
42-
@click.option('pids', '-p', '--pid', type=click.INT, multiple=True, help='filter pid list')
43-
@click.option('--color/--no-color', default=True)
44-
@click.option('process_names', '-pn', '--process-name', multiple=True, help='filter process name list')
45-
@click.option('images', '-i', '--image', multiple=True, help='filter image list')
46-
@click.option('--request/--no-request', is_flag=True, default=True, help='show requests')
47-
@click.option('--response/--no-response', is_flag=True, default=True, help='show responses')
48-
@click.option('-u', '--unique', is_flag=True, help='show only unique requests per image/pid/method/uri combination')
49-
@click.option('--black-list/--white-list', default=True, is_flag=True)
50-
def mobile_preference(service_provider: LockdownServiceProvider, out, pids, process_names, images, request, response,
51-
color, unique, black_list):
41+
MobileSnifferProfile(
42+
service_provider, filters=filters, request=request, response=response, color=color, unique=unique
43+
).sniff()
44+
45+
46+
@mobile.command("preference")
47+
def mobile_preference(
48+
service_provider: ServiceProviderDep,
49+
out: Annotated[
50+
Optional[typer.FileTextWrite],
51+
typer.Option("-o", "--out", help="file to store the har entries into upon exit (ctrl+c)"),
52+
] = None,
53+
pids: Annotated[Optional[list[int]], typer.Option("-p", "--pid", help="filter pid list")] = None,
54+
process_names: Annotated[
55+
Optional[list[str]],
56+
typer.Option("-pn", "--process-name", help="filter process name list"),
57+
] = None,
58+
color: Annotated[bool, typer.Option("--color/--no-color")] = True,
59+
images: Annotated[Optional[list[str]], typer.Option("-i", "--image", help="filter image list")] = None,
60+
request: Annotated[bool, typer.Option("--request/--no-request", help="show requests")] = True,
61+
response: Annotated[bool, typer.Option("--response/--no-response", help="show responses")] = True,
62+
unique: Annotated[
63+
bool,
64+
typer.Option("-u", "--unique", help="show only unique requests per image/pid/method/uri combination"),
65+
] = False,
66+
black_list: Annotated[bool, typer.Option("--black-list/--white-list")] = True,
67+
):
5268
"""
5369
Sniff using the secret com.apple.CFNetwork.plist configuration.
5470
5571
This sniff includes the request/response body as well but requires the device to be jailbroken for
5672
the sniff to work
5773
"""
5874
filters = Filters(pids, process_names, images, black_list)
59-
SnifferPreference(service_provider, filters=filters, request=request, response=response, out=out, color=color,
60-
unique=unique).sniff()
61-
62-
63-
@cli.command('profile')
64-
@click.option('pids', '-p', '--pid', type=click.INT, multiple=True, help='filter pid list')
65-
@click.option('--color/--no-color', default=True)
66-
@click.option('process_names', '-pn', '--process-name', multiple=True, help='filter process name list')
67-
@click.option('images', '-i', '--image', multiple=True, help='filter image list')
68-
@click.option('--request/--no-request', is_flag=True, default=True, help='show requests')
69-
@click.option('--response/--no-response', is_flag=True, default=True, help='show responses')
70-
@click.option('-u', '--unique', is_flag=True, help='show only unique requests per image/pid/method/uri combination')
71-
@click.option('--black-list/--white-list', default=True, is_flag=True)
72-
def host_profile(pids, process_names, color, request, response, images, unique, black_list):
75+
SnifferPreference(
76+
service_provider, filters=filters, request=request, response=response, out=out, color=color, unique=unique
77+
).sniff()
78+
79+
80+
@cli.command("profile")
81+
def host_profile(
82+
pids: Annotated[Optional[list[int]], typer.Option("-p", "--pid", help="filter pid list")] = None,
83+
process_names: Annotated[
84+
Optional[list[str]],
85+
typer.Option("-pn", "--process-name", help="filter process name list"),
86+
] = None,
87+
color: Annotated[bool, typer.Option("--color/--no-color")] = True,
88+
request: Annotated[bool, typer.Option("--request/--no-request", help="show requests")] = True,
89+
response: Annotated[bool, typer.Option("--response/--no-response", help="show responses")] = True,
90+
images: Annotated[Optional[list[str]], typer.Option("-i", "--image", help="filter image list")] = None,
91+
unique: Annotated[
92+
bool,
93+
typer.Option("-u", "--unique", help="show only unique requests per image/pid/method/uri combination"),
94+
] = False,
95+
black_list: Annotated[bool, typer.Option("--black-list/--white-list")] = True,
96+
):
7397
"""
7498
Sniff using CFNetworkDiagnostics.mobileconfig profile.
7599
76100
This requires the specific Apple profile to be installed for the sniff to work.
77101
"""
78102
filters = Filters(pids, process_names, images, black_list)
79-
HostSnifferProfile(filters=filters, request=request, response=response, color=color,
80-
unique=unique).sniff()
103+
HostSnifferProfile(filters=filters, request=request, response=response, color=color, unique=unique).sniff()
81104

82105

83-
if __name__ == '__main__':
106+
if __name__ == "__main__":
84107
cli()

harlogger/haralyzer_patches.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ def text(self) -> Optional[str]:
1616

1717

1818
def add_text_base64_support_for_haralyzer() -> None:
19-
setattr(Response, 'text', ResponseHook.text)
19+
Response.text = ResponseHook.text

harlogger/http_transaction.py

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,50 @@
11
from abc import abstractmethod
2-
from typing import List, Mapping, MutableMapping, Union
2+
from collections.abc import Mapping, MutableMapping
3+
from typing import Optional, Union
34

45
from cached_property import cached_property
56

67

78
class HTTPTransaction:
8-
def __init__(self, url: str, http_version: str, headers: Mapping = None, body: Union[str, bytes] = None):
9+
def __init__(
10+
self, url: str, http_version: str, headers: Optional[Mapping] = None, body: Optional[Union[str, bytes]] = None
11+
):
912
self.url = url
1013
self.http_version = http_version
1114
self.headers = headers
1215
self.body = body
1316

1417
@staticmethod
15-
def parse_transaction(message: str) -> 'HTTPTransaction':
18+
def parse_transaction(message: str) -> "HTTPTransaction":
1619
res = None
1720
parsed_transaction = HTTPTransaction._parse_fields(message=message)
1821

19-
if 'Protocol Enqueue' in parsed_transaction:
20-
info = parsed_transaction.pop('Protocol Enqueue').split()[1:]
22+
if "Protocol Enqueue" in parsed_transaction:
23+
info = parsed_transaction.pop("Protocol Enqueue").split()[1:]
2124
if len(info) == 2:
2225
method, url = info
23-
http_version = 'unknown'
26+
http_version = "unknown"
2427
else:
2528
method, url, http_version = info
26-
parsed_transaction.pop('Message')
27-
parsed_transaction.pop('Request')
29+
parsed_transaction.pop("Message")
30+
parsed_transaction.pop("Request")
2831
res = HTTPRequest(url, method, http_version, parsed_transaction)
2932

30-
elif 'Protocol Received' in parsed_transaction:
31-
url = parsed_transaction.pop('Protocol Received').split()[2]
32-
http_version, status, *status_text = parsed_transaction.pop('Response').split(' ', 2)
33+
elif "Protocol Received" in parsed_transaction:
34+
url = parsed_transaction.pop("Protocol Received").split()[2]
35+
http_version, status, *status_text = parsed_transaction.pop("Response").split(" ", 2)
3336
res = HTTPResponse(url, http_version, status, status_text, parsed_transaction)
3437
return res
3538

3639
@staticmethod
3740
def _parse_fields(message: str) -> MutableMapping:
3841
result = {}
39-
for line in message.split('\n'):
40-
if ': ' not in line:
42+
for line in message.split("\n"):
43+
if ": " not in line:
4144
continue
4245

4346
line = line.strip()
44-
k, v = line.split(':', 1)
47+
k, v = line.split(":", 1)
4548
k = k.strip()
4649
v = v.strip()
4750
result[k] = v
@@ -53,28 +56,41 @@ def _start_line(self) -> str:
5356

5457
@cached_property
5558
def formatted(self) -> str:
56-
formatted_headers = ''
59+
formatted_headers = ""
5760
for k, v in self.headers.items():
58-
formatted_headers += f'{k}: {v}\n'
59-
return f'{self._start_line()}\n{formatted_headers}\n{self.body if self.body else ""}\n'
61+
formatted_headers += f"{k}: {v}\n"
62+
return f"{self._start_line()}\n{formatted_headers}\n{self.body if self.body else ''}\n"
6063

6164

6265
class HTTPRequest(HTTPTransaction):
63-
def __init__(self, url: str, method: str, http_version: str, headers: Mapping = None,
64-
body: Union[str, bytes] = None):
66+
def __init__(
67+
self,
68+
url: str,
69+
method: str,
70+
http_version: str,
71+
headers: Optional[Mapping] = None,
72+
body: Optional[Union[str, bytes]] = None,
73+
):
6574
super().__init__(url, http_version, headers, body)
6675
self.method = method
6776

6877
def _start_line(self) -> str:
69-
return f'{self.method} {self.url} {self.http_version}'
78+
return f"{self.method} {self.url} {self.http_version}"
7079

7180

7281
class HTTPResponse(HTTPTransaction):
73-
def __init__(self, url: str, http_version: str, status: int, status_text: List, headers: Mapping = None,
74-
body: Union[str, bytes] = None):
82+
def __init__(
83+
self,
84+
url: str,
85+
http_version: str,
86+
status: int,
87+
status_text: list,
88+
headers: Optional[Mapping] = None,
89+
body: Optional[Union[str, bytes]] = None,
90+
):
7591
super().__init__(url, http_version, headers, body)
7692
self.status = status
77-
self.status_text = ' '.join(status_text)
93+
self.status_text = " ".join(status_text)
7894

7995
def _start_line(self) -> str:
80-
return f'{self.http_version} {self.status} {self.status_text}'
96+
return f"{self.http_version} {self.status} {self.status_text}"

0 commit comments

Comments
 (0)