fluidattacks-core 2.16.0__py3-none-any.whl → 3.0.0__py3-none-any.whl

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.
@@ -0,0 +1 @@
1
+ DEFAULT_DOWNLOAD_BUFFER_SIZE = 64 * 1024 # 64KB
@@ -4,23 +4,37 @@ from pathlib import Path
4
4
  import aiofiles
5
5
  import aiohttp
6
6
 
7
+ from .constants import DEFAULT_DOWNLOAD_BUFFER_SIZE
8
+
7
9
  LOGGER = logging.getLogger(__name__)
8
10
 
9
11
 
10
- async def download_file(url: str, destination_path: str) -> bool:
11
- async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=3600)) as session: # noqa: SIM117
12
+ async def download_file(
13
+ *,
14
+ url: str,
15
+ destination_path: str,
16
+ download_buffer_size: int = DEFAULT_DOWNLOAD_BUFFER_SIZE,
17
+ ) -> bool:
18
+ timeout = aiohttp.ClientTimeout(total=60 * 60, connect=30)
19
+ async with aiohttp.ClientSession(timeout=timeout) as session: # noqa: SIM117
12
20
  async with session.get(url) as response:
13
- if response.status == 200:
14
- async with aiofiles.open(destination_path, "wb") as file:
15
- while True:
16
- try:
17
- chunk = await response.content.read(1024 * 10)
18
- except TimeoutError:
19
- LOGGER.warning("Read timeout")
20
- return False
21
- if not chunk:
22
- break
23
- await file.write(chunk)
24
- return Path(destination_path).exists()
21
+ if response.status != 200:
22
+ LOGGER.error(
23
+ "Failed to download file: HTTP %s, for path %s",
24
+ response.status,
25
+ destination_path,
26
+ )
27
+ return False
28
+
29
+ async with aiofiles.open(destination_path, "wb") as file:
30
+ while True:
31
+ try:
32
+ chunk = await response.content.read(download_buffer_size)
33
+ except TimeoutError:
34
+ LOGGER.exception("Read timeout for path %s", destination_path)
35
+ return False
36
+ if not chunk:
37
+ break
38
+ await file.write(chunk)
25
39
 
26
- return False
40
+ return Path(destination_path).exists()
@@ -9,6 +9,7 @@ from git import GitError
9
9
  from git.cmd import Git
10
10
  from git.repo import Repo
11
11
 
12
+ from .constants import DEFAULT_DOWNLOAD_BUFFER_SIZE
12
13
  from .delete_files import delete_out_of_scope_files
13
14
  from .download_file import download_file
14
15
 
@@ -83,12 +84,18 @@ async def download_repo_from_s3(
83
84
  download_url: str,
84
85
  destination_path: Path,
85
86
  git_ignore: list[str] | None = None,
87
+ *,
88
+ download_buffer_size: int = DEFAULT_DOWNLOAD_BUFFER_SIZE,
86
89
  ) -> bool:
87
90
  destination_path.parent.mkdir(parents=True, exist_ok=True)
88
91
  with tempfile.TemporaryDirectory(prefix="fluidattacks_", ignore_cleanup_errors=True) as tmpdir:
89
92
  tmp_path = Path(tmpdir)
90
93
  file_path = tmp_path / "repo.tar.gz"
91
- result = await download_file(download_url, str(file_path.absolute()))
94
+ result = await download_file(
95
+ url=download_url,
96
+ destination_path=str(file_path.absolute()),
97
+ download_buffer_size=download_buffer_size,
98
+ )
92
99
  if not result:
93
100
  LOGGER.error("Failed to download repository from %s", download_url)
94
101
  return False
@@ -1,12 +1,44 @@
1
- from fluidattacks_core.logging.presets import BATCH_LOGGING, DATE_FORMAT, PRODUCT_LOGGING
1
+ import logging
2
+ import logging.config
3
+ import sys
4
+ from types import TracebackType
5
+
6
+ from fluidattacks_core.logging.presets import DATE_FORMAT, PRODUCT_LOGGING
2
7
  from fluidattacks_core.logging.types import JobMetadata
3
8
  from fluidattacks_core.logging.utils import get_job_metadata, set_telemetry_metadata
4
9
 
10
+
11
+ def init_uncaught_exception_logging() -> None:
12
+ logger = logging.getLogger("unhandled")
13
+
14
+ def handle_uncaught_exception(
15
+ exception_type: type[BaseException],
16
+ msg: BaseException,
17
+ traceback: TracebackType | None,
18
+ ) -> None:
19
+ if issubclass(exception_type, KeyboardInterrupt):
20
+ sys.__excepthook__(exception_type, msg, traceback)
21
+ return
22
+
23
+ logger.critical(
24
+ "Uncaught exception",
25
+ exc_info=(exception_type, msg, traceback),
26
+ )
27
+
28
+ sys.excepthook = handle_uncaught_exception
29
+
30
+
31
+ def init_logging() -> None:
32
+ logging.config.dictConfig(PRODUCT_LOGGING)
33
+ init_uncaught_exception_logging()
34
+
35
+
5
36
  __all__ = [
6
- "BATCH_LOGGING",
7
37
  "DATE_FORMAT",
8
38
  "PRODUCT_LOGGING",
9
39
  "JobMetadata",
10
40
  "get_job_metadata",
41
+ "init_logging",
42
+ "init_uncaught_exception_logging",
11
43
  "set_telemetry_metadata",
12
44
  ]
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
 
3
- from fluidattacks_core.logging.utils import get_job_metadata, is_trunk_branch
3
+ from fluidattacks_core.logging.utils import is_trunk_branch
4
4
 
5
5
 
6
6
  class NoProductionFilter(logging.Filter):
@@ -15,13 +15,3 @@ class ProductionOnlyFilter(logging.Filter):
15
15
 
16
16
  def filter(self, _record: logging.LogRecord) -> bool:
17
17
  return is_trunk_branch()
18
-
19
-
20
- class BatchOnlyFilter(logging.Filter):
21
- def filter(self, _record: logging.LogRecord) -> bool:
22
- return get_job_metadata().job_id is not None
23
-
24
-
25
- class NoBatchFilter(logging.Filter):
26
- def filter(self, _record: logging.LogRecord) -> bool:
27
- return get_job_metadata().job_id is None
@@ -13,6 +13,8 @@ from fluidattacks_core.logging.utils import (
13
13
  get_pipeline_environment,
14
14
  get_pipeline_metadata,
15
15
  get_telemetry_metadata,
16
+ is_in_batch,
17
+ is_in_lambda,
16
18
  )
17
19
 
18
20
  # Main formats
@@ -101,19 +103,20 @@ class CustomJsonFormatter(JsonFormatter):
101
103
  """Add service information to the log record.
102
104
 
103
105
  It includes:
106
+ - Source
107
+ - Service
104
108
  - Version
105
- - Product name
106
109
  """
107
110
  batch_info = get_job_metadata().job_queue
108
- is_in_batch = get_job_metadata().job_id is not None
109
- product_name = get_environment_metadata().product_id
110
- service = f"{product_name}" + (f"/{batch_info}" if is_in_batch else "")
111
+ source = (
112
+ f"batch/{batch_info}" if is_in_batch() else "lambda" if is_in_lambda() else "python"
113
+ )
114
+ service = get_environment_metadata().product_id
111
115
  version = get_environment_metadata().version
112
116
 
113
- log_record["dd.version"] = version
114
- log_record["service.version"] = version
117
+ log_record["ddsource"] = source
115
118
  log_record["dd.service"] = service
116
- log_record["service.name"] = service
119
+ log_record["dd.version"] = version
117
120
 
118
121
  def _add_deployment_fields(self, log_record: dict[str, Any]) -> None:
119
122
  """Add deployment information to the log record.
@@ -1,7 +1,4 @@
1
- from fluidattacks_core.logging.filters import BatchOnlyFilter, NoBatchFilter
2
- from fluidattacks_core.logging.formatters import ColorfulFormatter
3
1
  from fluidattacks_core.logging.handlers import DebuggingHandler, ProductionSyncHandler
4
- from fluidattacks_core.logging.utils import get_environment_metadata, get_job_metadata
5
2
 
6
3
  # Main formats
7
4
  DATE_FORMAT = "%Y-%m-%dT%H:%M:%S%z"
@@ -10,69 +7,6 @@ Default date format for logs.
10
7
  """
11
8
 
12
9
 
13
- # Configuration for logging in batch environments
14
- _JOB_METADATA = get_job_metadata()
15
- _ENVIRONMENT_METADATA = get_environment_metadata()
16
-
17
- BATCH_LOGGING = {
18
- "version": 1,
19
- "disable_existing_loggers": False,
20
- "filters": {
21
- "batch_only": {"()": BatchOnlyFilter},
22
- "no_batch": {"()": NoBatchFilter},
23
- },
24
- "formatters": {
25
- "one_line_format": {
26
- "class": "logging.Formatter",
27
- "format": (
28
- "{asctime} {levelname} [{name}] [{filename}:{lineno}] "
29
- "[trace_id=None span_id=None "
30
- f"service.name=batch/{_JOB_METADATA.job_queue} "
31
- f"service.version={_ENVIRONMENT_METADATA.version} "
32
- f"deployment.environment={_ENVIRONMENT_METADATA.environment} "
33
- "trace_sampled=False]"
34
- " - {message}, extra=None"
35
- ),
36
- "datefmt": DATE_FORMAT,
37
- "style": "{",
38
- },
39
- "simple_format": {
40
- "class": "logging.Formatter",
41
- "format": "{asctime} [{levelname}] [{name}] {message}",
42
- "datefmt": DATE_FORMAT,
43
- "style": "{",
44
- },
45
- "colorful_format": {
46
- "()": ColorfulFormatter,
47
- "datefmt": DATE_FORMAT,
48
- "style": "{",
49
- },
50
- },
51
- "handlers": {
52
- "batch_handler": {
53
- "class": "logging.StreamHandler",
54
- "stream": "ext://sys.stdout",
55
- "formatter": "one_line_format",
56
- "filters": ["batch_only"],
57
- },
58
- "console_handler": {
59
- "class": "logging.StreamHandler",
60
- "stream": "ext://sys.stdout",
61
- "formatter": "colorful_format",
62
- "filters": ["no_batch"],
63
- },
64
- },
65
- "root": {
66
- "handlers": ["batch_handler", "console_handler"],
67
- "level": "INFO",
68
- },
69
- }
70
- """
71
- Logging configuration dict for batch environments.
72
-
73
- Root logger will have two handlers for batch and non-batch environments.
74
- """
75
-
76
10
  PRODUCT_LOGGING = {
77
11
  "version": 1,
78
12
  "disable_existing_loggers": False,
@@ -10,10 +10,20 @@ DEFAULT_TELEMETRY_METADATA = {}
10
10
 
11
11
 
12
12
  def is_trunk_branch() -> bool:
13
- """Check if code is using the trunk branch."""
13
+ """Check if the code is using the trunk branch."""
14
14
  return os.environ.get("CI_COMMIT_REF_NAME", "default") == "trunk"
15
15
 
16
16
 
17
+ def is_in_batch() -> bool:
18
+ """Check if the code is running in a batch environment."""
19
+ return os.environ.get("AWS_BATCH_JOB_ID") is not None
20
+
21
+
22
+ def is_in_lambda() -> bool:
23
+ """Check if the code is running in an AWS Lambda function."""
24
+ return os.environ.get("AWS_LAMBDA_FUNCTION_NAME") is not None
25
+
26
+
17
27
  def get_job_metadata() -> JobMetadata:
18
28
  """Get the job metadata for applications running in batch environments."""
19
29
  return JobMetadata(
@@ -26,7 +36,9 @@ def get_job_metadata() -> JobMetadata:
26
36
  def get_environment_metadata() -> EnvironmentMetadata:
27
37
  """Get the environment metadata for applications."""
28
38
  environment = "production" if is_trunk_branch() else "development"
29
- product_id = os.environ.get("PRODUCT_ID", "universe")
39
+ product_id = (
40
+ os.environ.get("AWS_LAMBDA_FUNCTION_NAME") or os.environ.get("PRODUCT_ID") or "universe"
41
+ )
30
42
  commit_sha = os.environ.get("CI_COMMIT_SHA", "00000000")
31
43
  commit_short_sha = commit_sha[:8]
32
44
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fluidattacks-core
3
- Version: 2.16.0
3
+ Version: 3.0.0
4
4
  Summary: Fluid Attacks Core Library
5
5
  Author-email: Development <development@fluidattacks.com>
6
6
  License: MPL-2.0
@@ -18,9 +18,10 @@ fluidattacks_core/git/__init__.py,sha256=aLiDZd-Jl7axe4zVvIiDQP2RPBiRvAf1jqEAc33
18
18
  fluidattacks_core/git/classes.py,sha256=vgCVOUF6tqeW0lKtD9giCNFQtzRit44bnu5qOAx7qCI,579
19
19
  fluidattacks_core/git/clone.py,sha256=alvidqUITrtTkvv4Ur9djI4Ch37QdhVWtHupMmV1eMc,7571
20
20
  fluidattacks_core/git/codecommit_utils.py,sha256=Ec1Ymk9F1DTTyRTdqrni77UUktGQgQB_jSq5n3wWy7Q,3422
21
+ fluidattacks_core/git/constants.py,sha256=dTFn5bLkJ-VG-954MVJVHXxa4UCnvaurSM7GY73BiWk,49
21
22
  fluidattacks_core/git/delete_files.py,sha256=_EfPFl61tRK4CyQHL2QtvqCQQkQ38RTXVP0Db_d6rWg,1189
22
- fluidattacks_core/git/download_file.py,sha256=wJwMCJh_BCLR51N4GZOg-Xryjd2x3oguJEithXDgL9s,922
23
- fluidattacks_core/git/download_repo.py,sha256=BifLqDK-3p41hif1h5jqzE_zivqyG8HXyT51UVqGHOY,3882
23
+ fluidattacks_core/git/download_file.py,sha256=0W0jhUiA6V7LRbAlbwUU3LbQiI1-UPqno7A70s2eo8s,1296
24
+ fluidattacks_core/git/download_repo.py,sha256=GiZT0-kgqLTAg7uqV09P6V0AXyyPrxCe5QN3Fhp2iaE,4114
24
25
  fluidattacks_core/git/https_utils.py,sha256=V2Z9ClFq9F3sUvTqc_h6uf2PRdEzD-6MuC9zZJHy7_0,7036
25
26
  fluidattacks_core/git/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
27
  fluidattacks_core/git/remote.py,sha256=cPuyBMHeGrzRkEjroB6zlRLMA-QH2gIyIkGJNyf8wZc,1255
@@ -30,20 +31,20 @@ fluidattacks_core/git/warp.py,sha256=yLotfzBFWSWzM_DzI-hbp4t3hWggKNNGqBr-0mYQhLU
30
31
  fluidattacks_core/http/__init__.py,sha256=3Zz90L6e3_z-M-8Bvk_53rv-CFhPThkRGXnxCiQrmaU,60
31
32
  fluidattacks_core/http/client.py,sha256=jIhtGU2cKi5GZbxHq8WJOPgnk0beScRtxlz9tBSaKuw,2454
32
33
  fluidattacks_core/http/validations.py,sha256=h10Hr906KJqda1rJJb8eOqk1Xyyz81lAJ1glXeae4kM,3766
33
- fluidattacks_core/logging/__init__.py,sha256=p0OeNSbypmff-vrY3jZRjPL5h2aLe6OrJucAtbjI10U,382
34
- fluidattacks_core/logging/filters.py,sha256=3dPFJg90pNvk17F58CD-Qgw_LoWrEZ4z6QZbjaGfHm4,808
35
- fluidattacks_core/logging/formatters.py,sha256=70Hmz2AyhChGdcHgGa4fIqVkg_Gjogf23HlN0stIGt0,6748
34
+ fluidattacks_core/logging/__init__.py,sha256=WFH1_0ctw4zSKLjsInHUvlFguIQuJJ2dVDW7VHyXPfY,1173
35
+ fluidattacks_core/logging/filters.py,sha256=OqAS-cf-eDN8rWtbFpEK2kNPVyFTL31lKPonKHZ3kVA,492
36
+ fluidattacks_core/logging/formatters.py,sha256=iGr5WnUNKPO9kxFpP87TT7pz62_UJaNXkXecywrQ-3c,6723
36
37
  fluidattacks_core/logging/handlers.py,sha256=g6PB5L84oOk9nwLTzLL0eB9zFnfLJSM69Qb2Y3qPf9g,4084
37
- fluidattacks_core/logging/presets.py,sha256=NyLbTOY7oUkL8gHhxEWGNSbUNtVmSUa_tFfIFmDD4FI,2876
38
+ fluidattacks_core/logging/presets.py,sha256=JoUkhALSRNJ7qYxkuh7wdvWe0dXwKRxDGb6N-ihKArg,664
38
39
  fluidattacks_core/logging/types.py,sha256=aAPGXCEOSCtjVF36rAfWixAhiY7w6E3WDfd_pNAmNRw,233
39
- fluidattacks_core/logging/utils.py,sha256=vTIqYOvxN98vf3gHWPX9MDDl2P_zFUcxNk6SVXE3Q98,3815
40
+ fluidattacks_core/logging/utils.py,sha256=RhmX-aFR41XpphU0FLXCFcWzyWZocNEZ14qgVB8SwAM,4195
40
41
  fluidattacks_core/sarif/__init__.py,sha256=vZkbzafVeqRPEc_dzq6oevZuNp50NNyNGa_eS0oNXnc,101519
41
42
  fluidattacks_core/semver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
43
  fluidattacks_core/semver/match_versions.py,sha256=3L3C0TIVH0AtDpISvk5HHBXFSbJh5V7AINgfKEXYnYI,10157
43
44
  fluidattacks_core/serializers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
45
  fluidattacks_core/serializers/snippet.py,sha256=e520pZHC-fsNuYVNY30A7TcSugvUlFL6xdr74j5aCDM,12780
45
46
  fluidattacks_core/serializers/syntax.py,sha256=DkRsdMyMNrL0pRfsOSVAx79K8F0AmjBk676_d_v7PjM,15908
46
- fluidattacks_core-2.16.0.dist-info/METADATA,sha256=YuQp0jScKoZvGle2Ah823jpPegFGGUjcnAD5wDZCSZs,3200
47
- fluidattacks_core-2.16.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
48
- fluidattacks_core-2.16.0.dist-info/top_level.txt,sha256=m49ZyZ2zPQmDBxkSpjb20wr-ZbGVXdOMFBZrDiP5Lb8,18
49
- fluidattacks_core-2.16.0.dist-info/RECORD,,
47
+ fluidattacks_core-3.0.0.dist-info/METADATA,sha256=lo9JNVnYwKzZkgZtod7Ih95ITDjx5AcdUkgGZt7WhJc,3199
48
+ fluidattacks_core-3.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
49
+ fluidattacks_core-3.0.0.dist-info/top_level.txt,sha256=m49ZyZ2zPQmDBxkSpjb20wr-ZbGVXdOMFBZrDiP5Lb8,18
50
+ fluidattacks_core-3.0.0.dist-info/RECORD,,