binarycookies 2.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- binarycookies-2.1.4/LICENSE +16 -0
- binarycookies-2.1.4/PKG-INFO +143 -0
- binarycookies-2.1.4/README.md +127 -0
- binarycookies-2.1.4/pyproject.toml +111 -0
- binarycookies-2.1.4/setup.py +38 -0
- binarycookies-2.1.4/src/binarycookies/__init__.py +4 -0
- binarycookies-2.1.4/src/binarycookies/__main__.py +48 -0
- binarycookies-2.1.4/src/binarycookies/_deserialize.py +155 -0
- binarycookies-2.1.4/src/binarycookies/_serialize.py +142 -0
- binarycookies-2.1.4/src/binarycookies/models.py +57 -0
- binarycookies-2.1.4/src/binarycookies/parser.py +12 -0
- binarycookies-2.1.4/src/binarycookies/py.typed +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so
|
|
9
|
+
|
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
11
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
12
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
13
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
14
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
15
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
16
|
+
SOFTWARE.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: binarycookies
|
|
3
|
+
Version: 2.1.4
|
|
4
|
+
Summary: Python Binary Cookies (de)serializer
|
|
5
|
+
Author: Daniel Tom
|
|
6
|
+
Author-email: d.e.tom89@gmail.com
|
|
7
|
+
Requires-Python: >=3.8,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Requires-Dist: pydantic (>=2.0.0,<3.0.0)
|
|
14
|
+
Requires-Dist: typer (>=0.12.3,<0.17.0)
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
[](https://github.com/dan1elt0m/binary-cookies-reader/actions/workflows/test.yml)
|
|
18
|
+
|
|
19
|
+
# Binary Cookies
|
|
20
|
+
|
|
21
|
+
Python library and CLI tool for reading and writing binary cookies files.
|
|
22
|
+
|
|
23
|
+
## Requirements
|
|
24
|
+
|
|
25
|
+
- Python 3.9 or higher
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
```bash
|
|
29
|
+
pip install binarycookies
|
|
30
|
+
```
|
|
31
|
+
If you want to use the parser as CLI, it's recommended to use pipx to install the package in an isolated environment.
|
|
32
|
+
```bash
|
|
33
|
+
pipx install binarycookies
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Basic Usage CLI
|
|
37
|
+
After installation, you can use the command-line interface to read a binary cookies file:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
bcparser <path_to_binary_cookies_file>
|
|
41
|
+
```
|
|
42
|
+
Replace <path_to_binary_cookies_file> with the path to the binary cookie file you want to read.
|
|
43
|
+
|
|
44
|
+
### Basic Usage Python
|
|
45
|
+
|
|
46
|
+
#### Deserialization
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
import binarycookies
|
|
50
|
+
|
|
51
|
+
with open("path/to/cookies.binarycookies", "rb") as f:
|
|
52
|
+
cookies = binarycookies.load(f)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### Serialization
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
import binarycookies
|
|
59
|
+
|
|
60
|
+
cookie = {
|
|
61
|
+
"name": "session_id",
|
|
62
|
+
"value": "abc123",
|
|
63
|
+
"url": "https://example.com",
|
|
64
|
+
"path": "/",
|
|
65
|
+
"create_datetime": "2023-10-01T12:34:56+00:00",
|
|
66
|
+
"expiry_datetime": "2023-12-31T23:59:59+00:00",
|
|
67
|
+
"flag": "Secure"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
with open("path/to/cookies.binarycookies", "wb") as f:
|
|
71
|
+
binarycookies.dump(cookie, f)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Output Types
|
|
75
|
+
|
|
76
|
+
The `bcparser` CLI supports two output types: `json` (default) and `ascii`.
|
|
77
|
+
|
|
78
|
+
### JSON Output
|
|
79
|
+
|
|
80
|
+
The `json` output type formats the cookies as a JSON array, making it easy to parse and manipulate programmatically.
|
|
81
|
+
|
|
82
|
+
Example usage:
|
|
83
|
+
```sh
|
|
84
|
+
bcparser path/to/cookies.binarycookies --output json
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Example output JSON:
|
|
88
|
+
```json
|
|
89
|
+
[
|
|
90
|
+
{
|
|
91
|
+
"name": "session_id",
|
|
92
|
+
"value": "abc123",
|
|
93
|
+
"url": "https://example.com",
|
|
94
|
+
"path": "/",
|
|
95
|
+
"create_datetime": "2023-10-01T12:34:56+00:00",
|
|
96
|
+
"expiry_datetime": "2023-12-31T23:59:59+00:00",
|
|
97
|
+
"flag": "Secure"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"name": "user_token",
|
|
101
|
+
"value": "xyz789",
|
|
102
|
+
"url": "https://example.com",
|
|
103
|
+
"path": "/account",
|
|
104
|
+
"create_datetime": "2023-10-01T12:34:56+00:00",
|
|
105
|
+
"expiry_datetime": "2023-12-31T23:59:59+00:00",
|
|
106
|
+
"flag": "HttpOnly"
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### ASCII Output
|
|
112
|
+
The ascii output type formats the cookies in a simple, line-by-line text format, making it easy to read and pipe to other command-line tools.
|
|
113
|
+
|
|
114
|
+
Example usage:
|
|
115
|
+
```sh
|
|
116
|
+
bcparser path/to/cookies.binarycookies --output ascii
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Example output ASCII:
|
|
120
|
+
```text
|
|
121
|
+
Name: session_id
|
|
122
|
+
Value: abc123
|
|
123
|
+
URL: https://example.com
|
|
124
|
+
Path: /
|
|
125
|
+
Created: 2023-10-01T12:34:56+00:00
|
|
126
|
+
Expires: 2023-12-31T23:59:59+00:00
|
|
127
|
+
Flag: Secure
|
|
128
|
+
----------------------------------------
|
|
129
|
+
Name: user_token
|
|
130
|
+
Value: xyz789
|
|
131
|
+
URL: https://example.com
|
|
132
|
+
Path: /account
|
|
133
|
+
Created: 2023-10-01T12:34:56+00:00
|
|
134
|
+
Expires: 2023-12-31T23:59:59+00:00
|
|
135
|
+
Flag: HttpOnly
|
|
136
|
+
----------------------------------------
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### License
|
|
140
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
|
141
|
+
|
|
142
|
+
### Contributing
|
|
143
|
+
Contributions are welcome! If you find a bug or have a feature request, please open an issue on GitHub. Pull requests are also welcome.
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
[](https://github.com/dan1elt0m/binary-cookies-reader/actions/workflows/test.yml)
|
|
2
|
+
|
|
3
|
+
# Binary Cookies
|
|
4
|
+
|
|
5
|
+
Python library and CLI tool for reading and writing binary cookies files.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Python 3.9 or higher
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
```bash
|
|
13
|
+
pip install binarycookies
|
|
14
|
+
```
|
|
15
|
+
If you want to use the parser as CLI, it's recommended to use pipx to install the package in an isolated environment.
|
|
16
|
+
```bash
|
|
17
|
+
pipx install binarycookies
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Basic Usage CLI
|
|
21
|
+
After installation, you can use the command-line interface to read a binary cookies file:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bcparser <path_to_binary_cookies_file>
|
|
25
|
+
```
|
|
26
|
+
Replace <path_to_binary_cookies_file> with the path to the binary cookie file you want to read.
|
|
27
|
+
|
|
28
|
+
### Basic Usage Python
|
|
29
|
+
|
|
30
|
+
#### Deserialization
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
import binarycookies
|
|
34
|
+
|
|
35
|
+
with open("path/to/cookies.binarycookies", "rb") as f:
|
|
36
|
+
cookies = binarycookies.load(f)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### Serialization
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import binarycookies
|
|
43
|
+
|
|
44
|
+
cookie = {
|
|
45
|
+
"name": "session_id",
|
|
46
|
+
"value": "abc123",
|
|
47
|
+
"url": "https://example.com",
|
|
48
|
+
"path": "/",
|
|
49
|
+
"create_datetime": "2023-10-01T12:34:56+00:00",
|
|
50
|
+
"expiry_datetime": "2023-12-31T23:59:59+00:00",
|
|
51
|
+
"flag": "Secure"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
with open("path/to/cookies.binarycookies", "wb") as f:
|
|
55
|
+
binarycookies.dump(cookie, f)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Output Types
|
|
59
|
+
|
|
60
|
+
The `bcparser` CLI supports two output types: `json` (default) and `ascii`.
|
|
61
|
+
|
|
62
|
+
### JSON Output
|
|
63
|
+
|
|
64
|
+
The `json` output type formats the cookies as a JSON array, making it easy to parse and manipulate programmatically.
|
|
65
|
+
|
|
66
|
+
Example usage:
|
|
67
|
+
```sh
|
|
68
|
+
bcparser path/to/cookies.binarycookies --output json
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Example output JSON:
|
|
72
|
+
```json
|
|
73
|
+
[
|
|
74
|
+
{
|
|
75
|
+
"name": "session_id",
|
|
76
|
+
"value": "abc123",
|
|
77
|
+
"url": "https://example.com",
|
|
78
|
+
"path": "/",
|
|
79
|
+
"create_datetime": "2023-10-01T12:34:56+00:00",
|
|
80
|
+
"expiry_datetime": "2023-12-31T23:59:59+00:00",
|
|
81
|
+
"flag": "Secure"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "user_token",
|
|
85
|
+
"value": "xyz789",
|
|
86
|
+
"url": "https://example.com",
|
|
87
|
+
"path": "/account",
|
|
88
|
+
"create_datetime": "2023-10-01T12:34:56+00:00",
|
|
89
|
+
"expiry_datetime": "2023-12-31T23:59:59+00:00",
|
|
90
|
+
"flag": "HttpOnly"
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### ASCII Output
|
|
96
|
+
The ascii output type formats the cookies in a simple, line-by-line text format, making it easy to read and pipe to other command-line tools.
|
|
97
|
+
|
|
98
|
+
Example usage:
|
|
99
|
+
```sh
|
|
100
|
+
bcparser path/to/cookies.binarycookies --output ascii
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Example output ASCII:
|
|
104
|
+
```text
|
|
105
|
+
Name: session_id
|
|
106
|
+
Value: abc123
|
|
107
|
+
URL: https://example.com
|
|
108
|
+
Path: /
|
|
109
|
+
Created: 2023-10-01T12:34:56+00:00
|
|
110
|
+
Expires: 2023-12-31T23:59:59+00:00
|
|
111
|
+
Flag: Secure
|
|
112
|
+
----------------------------------------
|
|
113
|
+
Name: user_token
|
|
114
|
+
Value: xyz789
|
|
115
|
+
URL: https://example.com
|
|
116
|
+
Path: /account
|
|
117
|
+
Created: 2023-10-01T12:34:56+00:00
|
|
118
|
+
Expires: 2023-12-31T23:59:59+00:00
|
|
119
|
+
Flag: HttpOnly
|
|
120
|
+
----------------------------------------
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### License
|
|
124
|
+
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
|
125
|
+
|
|
126
|
+
### Contributing
|
|
127
|
+
Contributions are welcome! If you find a bug or have a feature request, please open an issue on GitHub. Pull requests are also welcome.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "binarycookies"
|
|
3
|
+
version = "2.1.4"
|
|
4
|
+
description = "Python Binary Cookies (de)serializer"
|
|
5
|
+
authors = ["Daniel Tom <d.e.tom89@gmail.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
packages = [{include = "binarycookies", from="src"}]
|
|
8
|
+
|
|
9
|
+
[tool.poetry.dependencies]
|
|
10
|
+
python = ">=3.8,<4.0"
|
|
11
|
+
typer = ">=0.12.3,<0.17.0"
|
|
12
|
+
pydantic = ">=2.0.0,<3.0.0"
|
|
13
|
+
|
|
14
|
+
[tool.poetry.scripts]
|
|
15
|
+
bcparser = "binarycookies.__main__:main"
|
|
16
|
+
|
|
17
|
+
[tool.poetry.group.dev.dependencies]
|
|
18
|
+
pytest = "^8.2.2"
|
|
19
|
+
ruff = ">=0.5.1,<0.8.0"
|
|
20
|
+
pytest-cov = "^5.0.0"
|
|
21
|
+
|
|
22
|
+
[build-system]
|
|
23
|
+
requires = ["poetry-core"]
|
|
24
|
+
build-backend = "poetry.core.masonry.api"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
[tool.ruff]
|
|
28
|
+
# https://beta.ruff.rs/docs/rules/
|
|
29
|
+
# https://beta.ruff.rs/docs/settings/
|
|
30
|
+
lint.ignore = [
|
|
31
|
+
"FBT003",
|
|
32
|
+
"ANN101",
|
|
33
|
+
"DTZ007",
|
|
34
|
+
"TRY003",
|
|
35
|
+
"A003", # shadow built-in types
|
|
36
|
+
"ANN002", # type annotation for **kwargs
|
|
37
|
+
"ANN003", # type annotation for *args
|
|
38
|
+
"ANN102", # Missing type annotation for `cls` in classmethod
|
|
39
|
+
"D101", # docstring in public classes
|
|
40
|
+
"D102", # docstring in public methods
|
|
41
|
+
"D103", # docstring in public functions
|
|
42
|
+
"DTZ", # timezone information in datetimes
|
|
43
|
+
"RUF012", # causes failures in pydantic models
|
|
44
|
+
"PLR0911", # for some reason can't ignore this on func definition
|
|
45
|
+
"ISC001",
|
|
46
|
+
"COM812"
|
|
47
|
+
]
|
|
48
|
+
lint.extend-select = [
|
|
49
|
+
"I",
|
|
50
|
+
"N",
|
|
51
|
+
"ASYNC",
|
|
52
|
+
"ANN",
|
|
53
|
+
"BLE",
|
|
54
|
+
"FBT",
|
|
55
|
+
"A",
|
|
56
|
+
"COM",
|
|
57
|
+
"C4",
|
|
58
|
+
"DTZ",
|
|
59
|
+
"ISC",
|
|
60
|
+
"ICN",
|
|
61
|
+
"T20",
|
|
62
|
+
"D101",
|
|
63
|
+
"D102",
|
|
64
|
+
"D103",
|
|
65
|
+
"D419",
|
|
66
|
+
"PT",
|
|
67
|
+
"Q",
|
|
68
|
+
"RSE",
|
|
69
|
+
"RET",
|
|
70
|
+
"SLF",
|
|
71
|
+
"SLOT",
|
|
72
|
+
"SIM",
|
|
73
|
+
"TID252",
|
|
74
|
+
"ARG",
|
|
75
|
+
"ERA001",
|
|
76
|
+
"G010",
|
|
77
|
+
"PGH005",
|
|
78
|
+
"PL",
|
|
79
|
+
"TRY",
|
|
80
|
+
"FLY",
|
|
81
|
+
"NPY",
|
|
82
|
+
"AIR",
|
|
83
|
+
"PERF101",
|
|
84
|
+
"PERF102",
|
|
85
|
+
"RUF",
|
|
86
|
+
]
|
|
87
|
+
line-length = 120
|
|
88
|
+
target-version = "py310"
|
|
89
|
+
exclude = ["deploy", ".venv"]
|
|
90
|
+
|
|
91
|
+
[tool.ruff.lint.per-file-ignores]
|
|
92
|
+
"tests/*" = [
|
|
93
|
+
"ANN001",
|
|
94
|
+
"ARG001",
|
|
95
|
+
"D101",
|
|
96
|
+
"D102",
|
|
97
|
+
"D103",
|
|
98
|
+
"D419",
|
|
99
|
+
"DTZ005",
|
|
100
|
+
"DTZ011",
|
|
101
|
+
"PLR2004",
|
|
102
|
+
"SLF001",
|
|
103
|
+
"PLR0913",
|
|
104
|
+
"PGH005",
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
[tool.ruff.lint.pylint]
|
|
108
|
+
max-args = 7
|
|
109
|
+
|
|
110
|
+
[tool.ruff.lint.flake8-annotations]
|
|
111
|
+
suppress-none-returning = true
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from setuptools import setup
|
|
3
|
+
|
|
4
|
+
package_dir = \
|
|
5
|
+
{'': 'src'}
|
|
6
|
+
|
|
7
|
+
packages = \
|
|
8
|
+
['binarycookies']
|
|
9
|
+
|
|
10
|
+
package_data = \
|
|
11
|
+
{'': ['*']}
|
|
12
|
+
|
|
13
|
+
install_requires = \
|
|
14
|
+
['pydantic>=2.0.0,<3.0.0', 'typer>=0.12.3,<0.17.0']
|
|
15
|
+
|
|
16
|
+
entry_points = \
|
|
17
|
+
{'console_scripts': ['bcparser = binarycookies.__main__:main']}
|
|
18
|
+
|
|
19
|
+
setup_kwargs = {
|
|
20
|
+
'name': 'binarycookies',
|
|
21
|
+
'version': '2.1.4',
|
|
22
|
+
'description': 'Python Binary Cookies (de)serializer',
|
|
23
|
+
'long_description': '[](https://github.com/dan1elt0m/binary-cookies-reader/actions/workflows/test.yml)\n\n# Binary Cookies\n\nPython library and CLI tool for reading and writing binary cookies files.\n\n## Requirements\n\n- Python 3.9 or higher\n\n## Installation\n```bash \npip install binarycookies\n```\nIf you want to use the parser as CLI, it\'s recommended to use pipx to install the package in an isolated environment.\n```bash \npipx install binarycookies\n```\n\n## Basic Usage CLI\nAfter installation, you can use the command-line interface to read a binary cookies file:\n\n```bash\nbcparser <path_to_binary_cookies_file>\n```\nReplace <path_to_binary_cookies_file> with the path to the binary cookie file you want to read.\n\n### Basic Usage Python\n\n#### Deserialization\n\n```python\nimport binarycookies \n\nwith open("path/to/cookies.binarycookies", "rb") as f:\n cookies = binarycookies.load(f)\n```\n\n#### Serialization\n\n```python\nimport binarycookies \n\ncookie = {\n "name": "session_id",\n "value": "abc123",\n "url": "https://example.com",\n "path": "/",\n "create_datetime": "2023-10-01T12:34:56+00:00",\n "expiry_datetime": "2023-12-31T23:59:59+00:00",\n "flag": "Secure"\n}\n\nwith open("path/to/cookies.binarycookies", "wb") as f:\n binarycookies.dump(cookie, f)\n```\n\n## Output Types\n\nThe `bcparser` CLI supports two output types: `json` (default) and `ascii`.\n\n### JSON Output\n\nThe `json` output type formats the cookies as a JSON array, making it easy to parse and manipulate programmatically.\n\nExample usage:\n```sh\nbcparser path/to/cookies.binarycookies --output json\n```\n\nExample output JSON:\n```json\n[\n {\n "name": "session_id",\n "value": "abc123",\n "url": "https://example.com",\n "path": "/",\n "create_datetime": "2023-10-01T12:34:56+00:00",\n "expiry_datetime": "2023-12-31T23:59:59+00:00",\n "flag": "Secure"\n },\n {\n "name": "user_token",\n "value": "xyz789",\n "url": "https://example.com",\n "path": "/account",\n "create_datetime": "2023-10-01T12:34:56+00:00",\n "expiry_datetime": "2023-12-31T23:59:59+00:00",\n "flag": "HttpOnly"\n }\n]\n```\n\n### ASCII Output\nThe ascii output type formats the cookies in a simple, line-by-line text format, making it easy to read and pipe to other command-line tools.\n\nExample usage:\n```sh\nbcparser path/to/cookies.binarycookies --output ascii\n```\n\nExample output ASCII:\n```text\nName: session_id\nValue: abc123\nURL: https://example.com\nPath: /\nCreated: 2023-10-01T12:34:56+00:00\nExpires: 2023-12-31T23:59:59+00:00\nFlag: Secure\n----------------------------------------\nName: user_token\nValue: xyz789\nURL: https://example.com\nPath: /account\nCreated: 2023-10-01T12:34:56+00:00\nExpires: 2023-12-31T23:59:59+00:00\nFlag: HttpOnly\n----------------------------------------\n```\n\n### License\nThis project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.\n\n### Contributing\nContributions are welcome! If you find a bug or have a feature request, please open an issue on GitHub. Pull requests are also welcome.',
|
|
24
|
+
'author': 'Daniel Tom',
|
|
25
|
+
'author_email': 'd.e.tom89@gmail.com',
|
|
26
|
+
'maintainer': 'None',
|
|
27
|
+
'maintainer_email': 'None',
|
|
28
|
+
'url': 'None',
|
|
29
|
+
'package_dir': package_dir,
|
|
30
|
+
'packages': packages,
|
|
31
|
+
'package_data': package_data,
|
|
32
|
+
'install_requires': install_requires,
|
|
33
|
+
'entry_points': entry_points,
|
|
34
|
+
'python_requires': '>=3.8,<4.0',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
setup(**setup_kwargs)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from enum import Enum
|
|
4
|
+
from typing import Type
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich import print
|
|
8
|
+
|
|
9
|
+
from binarycookies import load
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DateTimeEncoder(json.JSONEncoder):
|
|
13
|
+
def default(self, obj: Type) -> str:
|
|
14
|
+
if isinstance(obj, datetime):
|
|
15
|
+
return obj.isoformat()
|
|
16
|
+
return super().default(obj)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class OutputType(str, Enum):
|
|
20
|
+
json = "json"
|
|
21
|
+
ascii = "ascii"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def cli(file_path: str, output: str = "json"):
|
|
25
|
+
"""CLI entrypoint for reading Binary Cookies"""
|
|
26
|
+
with open(file_path, "rb") as f:
|
|
27
|
+
cookies = load(f)
|
|
28
|
+
if output == OutputType.json:
|
|
29
|
+
print(json.dumps([cookie.model_dump() for cookie in cookies], indent=2, cls=DateTimeEncoder))
|
|
30
|
+
elif output == OutputType.ascii:
|
|
31
|
+
for cookie in cookies:
|
|
32
|
+
print(f"Name: {cookie.name}")
|
|
33
|
+
print(f"Value: {cookie.value}")
|
|
34
|
+
print(f"URL: {cookie.url}")
|
|
35
|
+
print(f"Path: {cookie.path}")
|
|
36
|
+
print(f"Created: {cookie.create_datetime.isoformat()}")
|
|
37
|
+
print(f"Expires: {cookie.expiry_datetime.isoformat()}")
|
|
38
|
+
print(f"Flag: {cookie.flag.value}")
|
|
39
|
+
print("-" * 40)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main():
|
|
43
|
+
"""CLI entrypoint for reading Binary Cookies"""
|
|
44
|
+
typer.run(cli)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
main()
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
from io import BytesIO
|
|
3
|
+
from struct import unpack
|
|
4
|
+
from typing import BinaryIO, List, Union
|
|
5
|
+
|
|
6
|
+
from binarycookies.models import (
|
|
7
|
+
BcField,
|
|
8
|
+
BinaryCookiesDecodeError,
|
|
9
|
+
Cookie,
|
|
10
|
+
CookieFields,
|
|
11
|
+
FileFields,
|
|
12
|
+
Flag,
|
|
13
|
+
Format,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
FLAGS = {
|
|
17
|
+
0: Flag.UNKNOWN,
|
|
18
|
+
1: Flag.SECURE,
|
|
19
|
+
4: Flag.HTTPONLY,
|
|
20
|
+
5: Flag.SECURE_HTTPONLY,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def interpret_flag(flags: int) -> Flag:
|
|
25
|
+
"""Interprets the flags of a cookie and returns a human-readable string."""
|
|
26
|
+
return FLAGS.get(flags, Flag.UNKNOWN)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def mac_epoch_to_date(epoch: int) -> datetime:
|
|
30
|
+
"""Converts a mac epoch time to a datetime object."""
|
|
31
|
+
return datetime.fromtimestamp(epoch + 978307200, tz=timezone.utc)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def read_string(data: BytesIO, size: int) -> str:
|
|
35
|
+
"""Reads a string from binary file."""
|
|
36
|
+
result = ""
|
|
37
|
+
count = 0
|
|
38
|
+
c = data.read(1)
|
|
39
|
+
while unpack("<b", c)[0] != 0:
|
|
40
|
+
count += 1
|
|
41
|
+
if count > size:
|
|
42
|
+
break
|
|
43
|
+
result += str(c.decode())
|
|
44
|
+
c = data.read(1)
|
|
45
|
+
return result
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def read_field(data: BytesIO, field: BcField) -> Union[str, int]:
|
|
49
|
+
"""Reads a field from binary data."""
|
|
50
|
+
data.seek(field.offset)
|
|
51
|
+
if field.format == Format.string:
|
|
52
|
+
return read_string(data, field.size)
|
|
53
|
+
return unpack(field.format, data.read(field.size))[0]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def read_cookie(cookie: BytesIO, cookie_size: int) -> Cookie:
|
|
57
|
+
"""Reads a cookie from the given offset in the page."""
|
|
58
|
+
|
|
59
|
+
cookie_fields = CookieFields()
|
|
60
|
+
flag_int = read_field(cookie, cookie_fields.flag)
|
|
61
|
+
flag = interpret_flag(flag_int)
|
|
62
|
+
|
|
63
|
+
url_offset = read_field(cookie, cookie_fields.url_offset)
|
|
64
|
+
name_offset = read_field(cookie, cookie_fields.name_offset)
|
|
65
|
+
path_offset = read_field(cookie, cookie_fields.path_offset)
|
|
66
|
+
value_offset = read_field(cookie, cookie_fields.value_offset)
|
|
67
|
+
|
|
68
|
+
expiry_datetime = mac_epoch_to_date(read_field(cookie, cookie_fields.expiry_date))
|
|
69
|
+
create_datetime = mac_epoch_to_date(read_field(cookie, cookie_fields.create_date))
|
|
70
|
+
|
|
71
|
+
url = read_field(cookie, BcField(offset=url_offset, size=name_offset - url_offset, format=Format.string))
|
|
72
|
+
name = read_field(cookie, BcField(offset=name_offset, size=path_offset - name_offset, format=Format.string))
|
|
73
|
+
path = read_field(cookie, BcField(offset=path_offset, size=value_offset - path_offset, format=Format.string))
|
|
74
|
+
value = read_field(cookie, BcField(offset=value_offset, size=cookie_size - value_offset, format=Format.string))
|
|
75
|
+
|
|
76
|
+
return Cookie(
|
|
77
|
+
name=name,
|
|
78
|
+
value=value,
|
|
79
|
+
url=url,
|
|
80
|
+
path=path,
|
|
81
|
+
create_datetime=create_datetime,
|
|
82
|
+
expiry_datetime=expiry_datetime,
|
|
83
|
+
flag=flag,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_cookie_offsets(page: BytesIO, num_cookies: int) -> List[int]:
|
|
88
|
+
"""Reads the offsets of the cookies in the page."""
|
|
89
|
+
return [read_field(page, BcField(offset=8 + (4 * i), size=4, format=Format.integer)) for i in range(num_cookies)]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_file_pages(binary_file: BytesIO, num_pages: int) -> List[int]:
|
|
93
|
+
"""Reads the sizes of the pages in the binary file."""
|
|
94
|
+
return [
|
|
95
|
+
read_field(binary_file, BcField(offset=8 + (i * 4), size=4, format=Format.integer_be)) for i in range(num_pages)
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _deserialize_page(page: BytesIO) -> List[Cookie]:
|
|
100
|
+
"""Reads a binary cookie file and returns a list of cookies."""
|
|
101
|
+
num_cookies = read_field(page, BcField(offset=4, size=4, format=Format.integer))
|
|
102
|
+
cookie_offsets = get_cookie_offsets(page, num_cookies)
|
|
103
|
+
cookies = []
|
|
104
|
+
for offset in cookie_offsets:
|
|
105
|
+
cookie_size = read_field(page, BcField(offset=offset, size=4, format=Format.integer))
|
|
106
|
+
page.seek(offset)
|
|
107
|
+
cookie = page.read(cookie_size)
|
|
108
|
+
cookies.append(read_cookie(BytesIO(cookie), cookie_size))
|
|
109
|
+
return cookies
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def load(bf: BinaryIO) -> List[Cookie]:
|
|
113
|
+
"""Deserializes a binary cookie file and returns a list of Cookie objects.
|
|
114
|
+
Args:
|
|
115
|
+
bf (BinaryIO): A binary file object containing the binary cookie data."""
|
|
116
|
+
# Check if the file is empty
|
|
117
|
+
if bf.readable() and bf.read(1) == b"":
|
|
118
|
+
raise BinaryCookiesDecodeError("The file is empty.")
|
|
119
|
+
# Reset the file pointer to the beginning
|
|
120
|
+
bf.seek(0)
|
|
121
|
+
# Check if the file is a valid binary cookies file
|
|
122
|
+
if bf.readable() and bf.read(4) != b"cook":
|
|
123
|
+
raise BinaryCookiesDecodeError("The file is not a valid binary cookies file. Missing magic String:cook.")
|
|
124
|
+
# Reset the file pointer to the beginning
|
|
125
|
+
bf.seek(0)
|
|
126
|
+
# Deserialize the binary cookies file
|
|
127
|
+
return loads(BytesIO(bf.read()))
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def loads(b: BytesIO) -> List[Cookie]:
|
|
131
|
+
"""Deserializes a binary cookie file and returns a list of Cookie objects.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
b (BytesIO): A BytesIO object containing the binary cookie data.
|
|
135
|
+
Returns:
|
|
136
|
+
List[Cookie]: A list of Cookie objects.
|
|
137
|
+
"""
|
|
138
|
+
all_cookies = []
|
|
139
|
+
file_fields = FileFields()
|
|
140
|
+
|
|
141
|
+
# Number of pages in the binary file: 4 bytes
|
|
142
|
+
num_pages = read_field(b, field=file_fields.num_pages)
|
|
143
|
+
page_sizes = get_file_pages(b, num_pages)
|
|
144
|
+
|
|
145
|
+
pages = []
|
|
146
|
+
b.seek(8 + (num_pages * 4))
|
|
147
|
+
for ps in page_sizes:
|
|
148
|
+
# Grab individual pages and each page will contain >= one cookie
|
|
149
|
+
pages.append(b.read(ps))
|
|
150
|
+
|
|
151
|
+
for page in pages:
|
|
152
|
+
cookies = _deserialize_page(BytesIO(page))
|
|
153
|
+
all_cookies.extend(cookies)
|
|
154
|
+
|
|
155
|
+
return all_cookies
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
from io import BufferedWriter, BytesIO
|
|
3
|
+
from struct import pack
|
|
4
|
+
from typing import BinaryIO, Dict, List, Tuple, Union
|
|
5
|
+
|
|
6
|
+
from binarycookies._deserialize import FLAGS
|
|
7
|
+
from binarycookies.models import BcField, Cookie, CookieFields, FileFields, Format
|
|
8
|
+
|
|
9
|
+
CookiesCollection = Union[List[Dict], List[Cookie], Tuple[Dict], Tuple[Cookie], Cookie, Dict[str, str]]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def date_to_mac_epoch(date: datetime) -> int:
|
|
13
|
+
"""Converts a datetime object to mac epoch time."""
|
|
14
|
+
mac_epoch_start = datetime(2001, 1, 1, tzinfo=timezone.utc)
|
|
15
|
+
return int((date - mac_epoch_start).total_seconds())
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def write_string(data: BytesIO, value: str):
|
|
19
|
+
"""Writes a string to binary file."""
|
|
20
|
+
data.write(value.encode() + b"\x00")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def write_field(data: BytesIO, field: BcField, value: Union[str, int]):
|
|
24
|
+
"""Writes a field to binary data."""
|
|
25
|
+
data.seek(field.offset)
|
|
26
|
+
if field.format == Format.string:
|
|
27
|
+
write_string(data, value)
|
|
28
|
+
else:
|
|
29
|
+
data.write(pack(field.format, value))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def serialize_cookie(cookie: Cookie) -> bytes:
|
|
33
|
+
"""Serializes a cookie object to binary format."""
|
|
34
|
+
cookie_data = BytesIO()
|
|
35
|
+
cookie_fields = CookieFields()
|
|
36
|
+
# Write flag
|
|
37
|
+
write_field(cookie_data, cookie_fields.flag, list(FLAGS.keys())[list(FLAGS.values()).index(cookie.flag)])
|
|
38
|
+
|
|
39
|
+
# Calculate offsets
|
|
40
|
+
url_offset = 56 # The actual cookies content always starts at byte 56
|
|
41
|
+
name_offset = 1 + url_offset + len(cookie.url.encode("utf-8"))
|
|
42
|
+
path_offset = 1 + name_offset + len(cookie.name.encode("utf-8"))
|
|
43
|
+
value_offset = 1 + path_offset + len(cookie.path.encode("utf-8"))
|
|
44
|
+
|
|
45
|
+
write_field(cookie_data, cookie_fields.url_offset, url_offset)
|
|
46
|
+
write_field(cookie_data, cookie_fields.name_offset, name_offset)
|
|
47
|
+
write_field(cookie_data, cookie_fields.path_offset, path_offset)
|
|
48
|
+
write_field(cookie_data, cookie_fields.value_offset, value_offset)
|
|
49
|
+
|
|
50
|
+
write_field(cookie_data, cookie_fields.expiry_date, date_to_mac_epoch(cookie.expiry_datetime))
|
|
51
|
+
write_field(cookie_data, cookie_fields.create_date, date_to_mac_epoch(cookie.create_datetime))
|
|
52
|
+
|
|
53
|
+
# Write cookie data
|
|
54
|
+
write_string(cookie_data, cookie.url)
|
|
55
|
+
write_string(cookie_data, cookie.name)
|
|
56
|
+
write_string(cookie_data, cookie.path)
|
|
57
|
+
write_string(cookie_data, cookie.value)
|
|
58
|
+
|
|
59
|
+
# Write size at the beginning
|
|
60
|
+
size = len(cookie_data.getvalue())
|
|
61
|
+
cookie_data.seek(0)
|
|
62
|
+
cookie_data.write(pack(Format.integer, size))
|
|
63
|
+
return cookie_data.getvalue()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def dump(cookies: CookiesCollection, f: Union[BufferedWriter, BytesIO, BinaryIO]):
|
|
67
|
+
"""Dumps a Binary Cookies object to create a binary cookies file.k
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
cookies: A Binary Cookies object to be serialized.
|
|
71
|
+
f: The file-like object to write the binary cookies data to.
|
|
72
|
+
"""
|
|
73
|
+
binary = dumps(cookies)
|
|
74
|
+
f.write(binary)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def dumps(cookies: CookiesCollection) -> bytes:
|
|
78
|
+
"""Dumps a Binary Cookies object to a byte string.
|
|
79
|
+
Args:
|
|
80
|
+
cookies: A Binary Cookies object to be serialized.
|
|
81
|
+
Returns:
|
|
82
|
+
bytes: The serialized binary cookies data.
|
|
83
|
+
"""
|
|
84
|
+
if isinstance(cookies, dict):
|
|
85
|
+
cookies = [Cookie.model_validate(cookies)]
|
|
86
|
+
elif isinstance(cookies, (list, tuple)):
|
|
87
|
+
cookies = [Cookie.model_validate(cookie) for cookie in cookies]
|
|
88
|
+
elif isinstance(cookies, Cookie):
|
|
89
|
+
cookies = [cookies]
|
|
90
|
+
else:
|
|
91
|
+
raise TypeError("Invalid type for cookies. Expected dict, list, tuple, or Cookie.")
|
|
92
|
+
|
|
93
|
+
file_fields = FileFields()
|
|
94
|
+
|
|
95
|
+
data = BytesIO()
|
|
96
|
+
|
|
97
|
+
# Write file header
|
|
98
|
+
write_field(data, file_fields.header, "cook")
|
|
99
|
+
|
|
100
|
+
# Number of pages (1 for simplicity)
|
|
101
|
+
write_field(data, file_fields.num_pages, 1)
|
|
102
|
+
|
|
103
|
+
# Write number of cookies
|
|
104
|
+
data.write(pack(Format.integer, len(cookies)))
|
|
105
|
+
|
|
106
|
+
# Placeholder for page size
|
|
107
|
+
page_size_offset = data.tell()
|
|
108
|
+
data.write(b"\x00\x00\x00\x00")
|
|
109
|
+
|
|
110
|
+
# Write number of cookies
|
|
111
|
+
data.write(pack(Format.integer, len(cookies)))
|
|
112
|
+
cookie_data_list = []
|
|
113
|
+
# Write cookies
|
|
114
|
+
for cookie in cookies:
|
|
115
|
+
cookie_data_list.append(serialize_cookie(cookie))
|
|
116
|
+
|
|
117
|
+
initial_cookie_offset = data.tell() + (len(cookies) * 4)
|
|
118
|
+
initial_cookie = True
|
|
119
|
+
previous_sizes = 0
|
|
120
|
+
for cookie_data in cookie_data_list:
|
|
121
|
+
if initial_cookie:
|
|
122
|
+
data.write(pack(Format.integer, initial_cookie_offset))
|
|
123
|
+
initial_cookie = False
|
|
124
|
+
else:
|
|
125
|
+
data.write(pack(Format.integer, previous_sizes + initial_cookie_offset))
|
|
126
|
+
|
|
127
|
+
previous_sizes += len(cookie_data)
|
|
128
|
+
|
|
129
|
+
# Unknown data
|
|
130
|
+
data.write(b"\x00\x00\x00\x00")
|
|
131
|
+
data.write(b"\x00\x00\x00\x00")
|
|
132
|
+
data.write(b"\x00\x00\x00\x00")
|
|
133
|
+
|
|
134
|
+
for cookie_data in cookie_data_list:
|
|
135
|
+
data.write(cookie_data)
|
|
136
|
+
|
|
137
|
+
# Update page size
|
|
138
|
+
page_size = data.tell()
|
|
139
|
+
data.seek(page_size_offset)
|
|
140
|
+
data.write(pack(Format.integer, page_size))
|
|
141
|
+
|
|
142
|
+
return data.getvalue()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BinaryCookiesDecodeError(Exception):
|
|
8
|
+
"""Custom exception for binary cookies decoding errors."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, message: str):
|
|
11
|
+
super().__init__(message)
|
|
12
|
+
self.message = message
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Flag(str, Enum):
|
|
16
|
+
SECURE = "Secure"
|
|
17
|
+
HTTPONLY = "HttpOnly"
|
|
18
|
+
UNKNOWN = "Unknown"
|
|
19
|
+
SECURE_HTTPONLY = "Secure; HttpOnly"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Cookie(BaseModel):
|
|
23
|
+
name: str
|
|
24
|
+
value: str
|
|
25
|
+
url: str
|
|
26
|
+
path: str
|
|
27
|
+
create_datetime: datetime
|
|
28
|
+
expiry_datetime: datetime
|
|
29
|
+
flag: Flag
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Format(str, Enum):
|
|
33
|
+
integer = "<i" # Integer format is a 4 byte integer
|
|
34
|
+
integer_be = ">i" # Integer format is a 4 byte integer big endian
|
|
35
|
+
string = "<b" # String format is a byte
|
|
36
|
+
date = "<d" # Date format is a double (epoch mac)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class BcField(BaseModel):
|
|
40
|
+
offset: int
|
|
41
|
+
size: int
|
|
42
|
+
format: Format
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class CookieFields(BaseModel):
|
|
46
|
+
flag: BcField = BcField(offset=8, size=4, format=Format.integer)
|
|
47
|
+
url_offset: BcField = BcField(offset=16, size=4, format=Format.integer)
|
|
48
|
+
name_offset: BcField = BcField(offset=20, size=4, format=Format.integer)
|
|
49
|
+
path_offset: BcField = BcField(offset=24, size=4, format=Format.integer)
|
|
50
|
+
value_offset: BcField = BcField(offset=28, size=4, format=Format.integer)
|
|
51
|
+
expiry_date: BcField = BcField(offset=40, size=8, format=Format.date)
|
|
52
|
+
create_date: BcField = BcField(offset=48, size=8, format=Format.date)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class FileFields(BaseModel):
|
|
56
|
+
header: BcField = BcField(offset=0, size=4, format=Format.string)
|
|
57
|
+
num_pages: BcField = BcField(offset=4, size=4, format=Format.integer_be)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from typing_extensions import deprecated
|
|
4
|
+
|
|
5
|
+
from binarycookies._deserialize import load
|
|
6
|
+
from binarycookies.models import Cookie
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@deprecated("read_binary_cookies_file is deprecated, use binary_cookies_parser.load instead.")
|
|
10
|
+
def read_binary_cookies_file(file_path: str) -> List[Cookie]:
|
|
11
|
+
with open(file_path, "rb") as f:
|
|
12
|
+
return load(f)
|
|
File without changes
|