fluidattacks-core 2.2.3__tar.gz → 2.2.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.
Files changed (25) hide show
  1. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/PKG-INFO +1 -1
  2. fluidattacks_core-2.2.4/fluidattacks_core/git/remote.py +199 -0
  3. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/pyproject.toml +1 -1
  4. fluidattacks_core-2.2.3/fluidattacks_core/git/remote.py +0 -54
  5. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/README.md +0 -0
  6. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/__init__.py +0 -0
  7. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/authz/__init__.py +0 -0
  8. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/authz/py.typed +0 -0
  9. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/authz/types.py +0 -0
  10. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/git/__init__.py +0 -0
  11. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/git/classes.py +0 -0
  12. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/git/clone.py +0 -0
  13. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/git/codecommit_utils.py +0 -0
  14. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/git/delete_files.py +0 -0
  15. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/git/download_file.py +0 -0
  16. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/git/download_repo.py +0 -0
  17. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/git/https_utils.py +0 -0
  18. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/git/py.typed +0 -0
  19. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/git/ssh_utils.py +0 -0
  20. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/git/utils.py +0 -0
  21. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/git/warp.py +0 -0
  22. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/http/__init__.py +0 -0
  23. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/http/client.py +0 -0
  24. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/http/validations.py +0 -0
  25. {fluidattacks_core-2.2.3 → fluidattacks_core-2.2.4}/fluidattacks_core/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fluidattacks-core
3
- Version: 2.2.3
3
+ Version: 2.2.4
4
4
  Summary: Fluid Attacks Core Library
5
5
  License: MPL-2.0
6
6
  Author: Development
@@ -0,0 +1,199 @@
1
+ import base64
2
+ import logging
3
+ from contextlib import suppress
4
+
5
+ import aiohttp
6
+ from urllib3.exceptions import LocationParseError
7
+ from urllib3.util import Url, parse_url
8
+
9
+ from ..http.client import request
10
+ from ..http.validations import HTTPValidationError
11
+ from .classes import InvalidParameter
12
+ from .codecommit_utils import call_codecommit_ls_remote
13
+ from .https_utils import call_https_ls_remote
14
+ from .ssh_utils import call_ssh_ls_remote
15
+
16
+ LOGGER = logging.getLogger(__name__)
17
+
18
+
19
+ def _format_redirected_url(
20
+ original_url: Url,
21
+ redirect_url: Url,
22
+ ) -> str:
23
+ return (
24
+ redirect_url._replace(
25
+ query=None,
26
+ path=(redirect_url.path or "").removesuffix("info/refs"),
27
+ ).url
28
+ if original_url.host == redirect_url.host
29
+ else original_url.url
30
+ )
31
+
32
+
33
+ async def get_redirected_url(
34
+ url: str,
35
+ user: str | None,
36
+ password: str | None,
37
+ token: str | None,
38
+ is_pat: bool,
39
+ ) -> str:
40
+ if user is not None and password is not None:
41
+ with suppress(LocationParseError):
42
+ return await _get_redirected_url(
43
+ parse_url(url)._replace(auth=None).url,
44
+ authorization="Basic "
45
+ + base64.b64encode(f"{user}:{password}".encode()).decode(),
46
+ )
47
+ return await _get_redirected_url(
48
+ url,
49
+ authorization="Basic "
50
+ + base64.b64encode(f"{user}:{password}".encode()).decode(),
51
+ )
52
+ if token is not None and is_pat:
53
+ return await _get_redirected_url(
54
+ url,
55
+ authorization=(
56
+ "Basic "
57
+ + base64.b64encode(f":{token}".encode()).decode()
58
+ ),
59
+ )
60
+ if token is not None and not is_pat:
61
+ return await _get_redirected_url(url, authorization=f"Bearer {token}")
62
+ if url.startswith("http"):
63
+ return await _get_redirected_url(url)
64
+ raise InvalidParameter()
65
+
66
+
67
+ async def _get_redirected_url(
68
+ url: str,
69
+ authorization: str | None = None,
70
+ ) -> str:
71
+ try:
72
+ return await _get_url(url, authorization=authorization)
73
+ except (
74
+ TimeoutError,
75
+ ValueError,
76
+ aiohttp.ClientError,
77
+ HTTPValidationError,
78
+ ) as exc:
79
+ LOGGER.warning(
80
+ "Failed to get redirected-url",
81
+ extra={"extra": {"url": url, "exc": exc}},
82
+ )
83
+ return url
84
+
85
+
86
+ async def _get_url(
87
+ original_url: str,
88
+ *,
89
+ redirect_url: str = "",
90
+ max_retries: int = 5,
91
+ authorization: str | None = None,
92
+ ) -> str:
93
+ try:
94
+ original = parse_url(original_url.removesuffix("/"))
95
+ url = (
96
+ parse_url(redirect_url.removesuffix("/"))
97
+ if redirect_url else original
98
+ )
99
+ except LocationParseError as exc:
100
+ raise HTTPValidationError(f"Invalid URL {redirect_url}") from exc
101
+
102
+ if max_retries < 1:
103
+ return _format_redirected_url(original, url)
104
+
105
+ # https://git-scm.com/book/en/v2/Git-Internals-Transfer-Protocols
106
+ url = (
107
+ url._replace(path=(url.path or "") + "/info/refs")
108
+ if not (url.path or "").endswith("info/refs")
109
+ else url
110
+ )
111
+ url = url._replace(query="service=git-upload-pack")
112
+
113
+ result = await request(
114
+ url.url,
115
+ method="GET",
116
+ headers={
117
+ "Host": url.host or "",
118
+ "Accept": "*/*",
119
+ "Accept-Encoding": "deflate, gzip",
120
+ "Pragma": "no-cache",
121
+ "Accept-Language": "*",
122
+ **({"Authorization": authorization} if authorization else {}),
123
+ },
124
+ )
125
+ if result.status == 200:
126
+ return _format_redirected_url(original, url)
127
+ if (
128
+ result.status > 300
129
+ and result.status < 400
130
+ and "Location" in result.headers
131
+ ):
132
+ with suppress(LocationParseError):
133
+ _url = parse_url(result.headers["Location"])
134
+ return await _get_url(
135
+ original_url,
136
+ redirect_url=result.headers["Location"],
137
+ max_retries=max_retries - 1,
138
+ authorization=authorization if _url.host == url.host else None,
139
+ )
140
+
141
+ return await _get_url(
142
+ original_url,
143
+ redirect_url=result.headers["Location"],
144
+ max_retries=max_retries - 1,
145
+ )
146
+
147
+ return _format_redirected_url(original, url)
148
+
149
+
150
+ async def ls_remote(
151
+ repo_url: str,
152
+ repo_branch: str,
153
+ *,
154
+ credential_key: str | None = None,
155
+ user: str | None = None,
156
+ password: str | None = None,
157
+ token: str | None = None,
158
+ provider: str | None = None,
159
+ is_pat: bool = False,
160
+ arn: str | None = None,
161
+ org_external_id: str | None = None,
162
+ follow_redirects: bool = True,
163
+ ) -> str | None:
164
+ last_commit: str | None = None
165
+ if credential_key is not None:
166
+ last_commit = await call_ssh_ls_remote(
167
+ repo_url,
168
+ credential_key,
169
+ repo_branch,
170
+ )
171
+ elif arn is not None and org_external_id is not None:
172
+ last_commit = await call_codecommit_ls_remote(
173
+ repo_url,
174
+ arn,
175
+ repo_branch,
176
+ org_external_id=org_external_id,
177
+ follow_redirects=follow_redirects,
178
+ )
179
+ else:
180
+ if not follow_redirects:
181
+ repo_url = await get_redirected_url(
182
+ repo_url,
183
+ user=user,
184
+ password=password,
185
+ token=token,
186
+ is_pat=is_pat,
187
+ )
188
+ last_commit = await call_https_ls_remote(
189
+ repo_url=repo_url,
190
+ user=user,
191
+ password=password,
192
+ token=token,
193
+ branch=repo_branch,
194
+ provider=provider,
195
+ is_pat=is_pat,
196
+ follow_redirects=follow_redirects,
197
+ )
198
+
199
+ return last_commit
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "fluidattacks-core"
3
- version = "2.2.3"
3
+ version = "2.2.4"
4
4
  description = "Fluid Attacks Core Library"
5
5
  authors = ["Development <development@fluidattacks.com>"]
6
6
  license = "MPL-2.0"
@@ -1,54 +0,0 @@
1
- from .codecommit_utils import (
2
- call_codecommit_ls_remote,
3
- )
4
- from .https_utils import (
5
- call_https_ls_remote,
6
- )
7
- from .ssh_utils import (
8
- call_ssh_ls_remote,
9
- )
10
- import logging
11
-
12
- LOGGER = logging.getLogger(__name__)
13
-
14
-
15
- async def ls_remote(
16
- repo_url: str,
17
- repo_branch: str,
18
- *,
19
- credential_key: str | None = None,
20
- user: str | None = None,
21
- password: str | None = None,
22
- token: str | None = None,
23
- provider: str | None = None,
24
- is_pat: bool = False,
25
- arn: str | None = None,
26
- org_external_id: str | None = None,
27
- follow_redirects: bool = True,
28
- ) -> str | None:
29
- last_commit: str | None = None
30
- if credential_key is not None:
31
- last_commit = await call_ssh_ls_remote(
32
- repo_url, credential_key, repo_branch
33
- )
34
- elif arn is not None and org_external_id is not None:
35
- last_commit = await call_codecommit_ls_remote(
36
- repo_url,
37
- arn,
38
- repo_branch,
39
- org_external_id=org_external_id,
40
- follow_redirects=follow_redirects,
41
- )
42
- else:
43
- last_commit = await call_https_ls_remote(
44
- repo_url=repo_url,
45
- user=user,
46
- password=password,
47
- token=token,
48
- branch=repo_branch,
49
- provider=provider,
50
- is_pat=is_pat,
51
- follow_redirects=follow_redirects,
52
- )
53
-
54
- return last_commit