t-bug-catcher 0.4.0__tar.gz → 0.4.2__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 (27) hide show
  1. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/PKG-INFO +1 -1
  2. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/setup.cfg +1 -1
  3. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/setup.py +3 -1
  4. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher/__init__.py +1 -1
  5. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher/config.py +3 -0
  6. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher/jira.py +5 -5
  7. t_bug_catcher-0.4.2/t_bug_catcher/resources/whispers_config.yml +44 -0
  8. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher/stack_saver.py +13 -41
  9. t_bug_catcher-0.4.2/t_bug_catcher/utils/common.py +69 -0
  10. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher.egg-info/PKG-INFO +1 -1
  11. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher.egg-info/SOURCES.txt +1 -0
  12. t_bug_catcher-0.4.0/t_bug_catcher/utils/common.py +0 -17
  13. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/MANIFEST.in +0 -0
  14. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/README.rst +0 -0
  15. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/pyproject.toml +0 -0
  16. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/requirements.txt +0 -0
  17. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher/bug_catcher.py +0 -0
  18. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher/bug_snag.py +0 -0
  19. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher/exceptions.py +0 -0
  20. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher/utils/__init__.py +0 -0
  21. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher/utils/logger.py +0 -0
  22. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher/workitems.py +0 -0
  23. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher.egg-info/dependency_links.txt +0 -0
  24. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher.egg-info/not-zip-safe +0 -0
  25. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher.egg-info/requires.txt +0 -0
  26. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/t_bug_catcher.egg-info/top_level.txt +0 -0
  27. {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.2}/tests/test_t_bug_catcher.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: t_bug_catcher
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: Bug catcher
5
5
  Home-page: https://www.thoughtful.ai/
6
6
  Author: Thoughtful
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 0.4.0
2
+ current_version = 0.4.2
3
3
  commit = True
4
4
  tag = False
5
5
 
@@ -26,7 +26,9 @@ setup(
26
26
  packages=find_packages(include=["t_bug_catcher", "t_bug_catcher.*"]),
27
27
  test_suite="tests",
28
28
  url="https://www.thoughtful.ai/",
29
- version="0.4.0",
29
+ version="0.4.2",
30
30
  zip_safe=False,
31
31
  install_requires=install_requirements,
32
+ include_package_data=True,
33
+ package_data={"": ["resources/*.yml"]},
32
34
  )
@@ -3,7 +3,7 @@
3
3
  __author__ = """Thoughtful"""
4
4
  __email__ = "support@thoughtful.ai"
5
5
  # fmt: off
6
- __version__ = '0.4.0'
6
+ __version__ = '0.4.2'
7
7
  # fmt: on
8
8
 
9
9
  from .bug_catcher import (
@@ -13,9 +13,12 @@ class Config:
13
13
  MAX_ISSUE_ATTACHMENTS: int = 100
14
14
  MAX_DESCRIPTION_LENGTH: int = 250
15
15
  SUMMARY_LENGTH: int = 120
16
+ STACK_SCOPE: int = 3
16
17
 
17
18
  SUPPORT_BOARD = "AB"
18
19
 
20
+ KEYS_TO_REMOVE = ["credential", "password"]
21
+
19
22
  RC_RUN_LINK = (
20
23
  f"https://cloud.robocorp.com/organizations/{os.environ.get('RC_ORGANIZATION_ID')}"
21
24
  f"/workspaces/{os.environ.get('RC_WORKSPACE_ID')}/processes"
@@ -1,12 +1,12 @@
1
1
  """JiraPoster class for interacting with the Jira API."""
2
2
 
3
- import datetime
4
3
  import hashlib
5
4
  import json
6
5
  import os
7
6
  import re
8
7
  import sys
9
8
  import traceback
9
+ from datetime import datetime
10
10
  from importlib.metadata import version
11
11
  from pathlib import Path
12
12
  from types import TracebackType
@@ -19,7 +19,7 @@ from retry import retry
19
19
  from .config import CONFIG
20
20
  from .exceptions import BadRequestError
21
21
  from .utils import logger
22
- from .utils.common import get_frames
22
+ from .utils.common import Encoder, get_frames
23
23
  from .workitems import variables
24
24
 
25
25
 
@@ -389,7 +389,7 @@ class Jira:
389
389
  },
390
390
  {
391
391
  "type": "text",
392
- "text": str(datetime.datetime.now().strftime("%B %d, %Y %I:%M:%S %p")),
392
+ "text": str(datetime.now().strftime("%B %d, %Y %I:%M:%S %p")),
393
393
  },
394
394
  ],
395
395
  }
@@ -521,7 +521,7 @@ class Jira:
521
521
  "content": [
522
522
  {
523
523
  "type": "text",
524
- "text": json.dumps(metadata, indent=4),
524
+ "text": json.dumps(metadata, indent=4, cls=Encoder),
525
525
  }
526
526
  ],
527
527
  },
@@ -735,7 +735,7 @@ class Jira:
735
735
  date_markup = [
736
736
  {
737
737
  "type": "text",
738
- "text": f" at {str(datetime.datetime.now().strftime('%B %d, %Y %I:%M:%S %p'))}",
738
+ "text": f" at {str(datetime.now().strftime('%B %d, %Y %I:%M:%S %p'))}",
739
739
  },
740
740
  {"type": "hardBreak"},
741
741
  ]
@@ -0,0 +1,44 @@
1
+ include:
2
+ files:
3
+ - "**/*"
4
+ rules:
5
+ - password
6
+ - uri
7
+ - secret
8
+ - webhook
9
+ - apikey
10
+ - apikey-known
11
+ - apikey-maybe
12
+ - id: username
13
+ group: misc
14
+ description: Variable names refering to user
15
+ message: Secret
16
+ severity: Low
17
+ key:
18
+ regex: ^\S*(user(name?)|login|id|uid|otp)_?(hash)?[0-9]*$
19
+ ignorecase: True
20
+ value:
21
+ minlen: 1
22
+ severity:
23
+ - Critical
24
+ - High
25
+ - Medium
26
+
27
+ exclude:
28
+ files:
29
+ - __pycache__|\.eggs|build|dev|\.vscode|\.git
30
+ - .*/(locale|spec|test|mock)s?/
31
+ - integration|node_modules
32
+ - (package(-lock)?|npm-shrinkwrap)\.json
33
+
34
+ keys:
35
+ - .*(public|project).*
36
+
37
+ values:
38
+ - ^(true|false|yes|no|1|0)$
39
+ - .*_(user|password|token|key|placeholder|name)$
40
+ - ^aws_(access_key_id|secret_access_key|session_token)$
41
+ - ^((cn?trl|alt|shift|del|ins|esc|tab|f[\d]+) ?[\+_\-\\/] ?)+[\w]+$
42
+
43
+
44
+
@@ -4,35 +4,15 @@ import os
4
4
  import re
5
5
  import sys
6
6
  from datetime import datetime
7
- from json import JSONEncoder
8
7
  from pathlib import Path
9
8
  from types import FunctionType, ModuleType
10
9
  from typing import Optional
11
10
 
12
11
  import whispers
13
12
 
13
+ from .config import CONFIG
14
14
  from .utils import logger
15
-
16
-
17
- class _Encoder(JSONEncoder):
18
- """Encoder class for encoding the Episode object to json."""
19
-
20
- def default(self, o):
21
- """This method is used to encode the Episode object to json.
22
-
23
- Args:
24
- o (object): The object to be encoded.
25
-
26
- Returns:
27
- str: The json string.
28
- """
29
- if hasattr(o, "__dict__"):
30
- return o.__dict__
31
- if isinstance(o, datetime):
32
- return o.isoformat()
33
- if isinstance(o, Path):
34
- return str(o)
35
- return JSONEncoder.default(self, o)
15
+ from .utils.common import Encoder, convert_keys_to_primitives
36
16
 
37
17
 
38
18
  class StackSaver:
@@ -54,8 +34,7 @@ class StackSaver:
54
34
  """
55
35
  return path.replace(os.getcwd(), "").strip(os.sep)
56
36
 
57
- @staticmethod
58
- def serialize_frame_info(frame_info: dict) -> dict:
37
+ def serialize_frame_info(self, frame_info: dict) -> dict:
59
38
  """A static method to serialize the frame info.
60
39
 
61
40
  Args:
@@ -64,19 +43,8 @@ class StackSaver:
64
43
  Returns:
65
44
  dict: The serialized frame info.
66
45
  """
67
- run_locals = {}
68
- run_args = {}
69
- if frame_info["locals"]:
70
- for key, value in frame_info["locals"].items():
71
- if isinstance(value, dict):
72
- run_locals[str(key)] = value
73
- else:
74
- run_locals[str(key)] = str(value)
75
- for key, value in frame_info["args"].items():
76
- if isinstance(value, dict):
77
- run_args[str(key)] = value
78
- else:
79
- run_args[str(key)] = str(value)
46
+ run_locals = convert_keys_to_primitives(frame_info["locals"]) if frame_info["locals"] else {}
47
+ run_args = convert_keys_to_primitives(frame_info["args"]) if frame_info["args"] else {}
80
48
  serializable_frame_info = {
81
49
  "filename": frame_info["filename"],
82
50
  "function_name": frame_info["function_name"],
@@ -104,7 +72,7 @@ class StackSaver:
104
72
  continue
105
73
  if isinstance(var, (ModuleType, FunctionType)):
106
74
  continue
107
- local_variables[var_name] = var
75
+ local_variables[str(var_name)] = var
108
76
  return local_variables
109
77
 
110
78
  def mask_credentials(self, file_path: str) -> None:
@@ -122,7 +90,11 @@ class StackSaver:
122
90
  with open(file_path, "r") as f:
123
91
  filedata = f.readlines()
124
92
 
125
- secrets = [secret for secret in whispers.secrets(file_path)]
93
+ config_path = Path(__file__).parent.resolve().as_posix() + "/resources/whispers_config.yml"
94
+
95
+ args = f"-c {config_path} {file_path}"
96
+
97
+ secrets = [secret for secret in whispers.secrets(args)]
126
98
 
127
99
  for index, line in enumerate(filedata):
128
100
  if not secrets:
@@ -162,7 +134,7 @@ class StackSaver:
162
134
  continue
163
135
  frames.append(frame)
164
136
  tb = tb.tb_next
165
- frames = frames[:3]
137
+ frames = frames[-CONFIG.LIMITS.STACK_SCOPE :]
166
138
 
167
139
  for frame in frames:
168
140
  frame_info = {
@@ -176,7 +148,7 @@ class StackSaver:
176
148
  file_path = f"stack_details_{datetime.now().strftime('%Y%m%d%H%M%S')}.json"
177
149
 
178
150
  with open(file_path, "w") as f:
179
- json.dump(stack_details_json, f, indent=4, cls=_Encoder)
151
+ json.dump(stack_details_json, f, indent=4, cls=Encoder)
180
152
 
181
153
  self.mask_credentials(file_path)
182
154
 
@@ -0,0 +1,69 @@
1
+ import traceback
2
+ from datetime import date, datetime
3
+ from json import JSONEncoder
4
+ from pathlib import Path
5
+ from types import TracebackType
6
+ from typing import List
7
+
8
+ from ..config import CONFIG
9
+
10
+
11
+ class Encoder(JSONEncoder):
12
+ """This class is used to encode the Episode object to json."""
13
+
14
+ def default(self, o):
15
+ """This method is used to encode the Episode object to json.
16
+
17
+ Args:
18
+ o (object): The object to be encoded.
19
+
20
+ Returns:
21
+ str: The json string.
22
+ """
23
+ try:
24
+ if hasattr(o, "__dict__"):
25
+ keys_to_remove = [
26
+ key for key in o.__dict__.keys() if any(s in str(key).lower() for s in CONFIG.KEYS_TO_REMOVE)
27
+ ]
28
+ for key in keys_to_remove:
29
+ del o.__dict__[key]
30
+ return {str(key): str(value) for key, value in o.__dict__.items()}
31
+ if isinstance(o, (datetime, date)):
32
+ return o.isoformat()
33
+ if isinstance(o, Path):
34
+ return str(o)
35
+ return super().default(self, o)
36
+ except TypeError:
37
+ return str(o)
38
+
39
+
40
+ def get_frames(exc_traceback: TracebackType) -> List:
41
+ """Get the frames of the exception.
42
+
43
+ Args:
44
+ exc_traceback (TracebackType): The traceback of the exception.
45
+
46
+ Returns:
47
+ List: The frames of the exception.
48
+ """
49
+ return [
50
+ frame for frame in traceback.extract_tb(exc_traceback) if "site-packages" not in str(frame.filename).lower()
51
+ ]
52
+
53
+
54
+ def convert_keys_to_primitives(data: dict) -> dict:
55
+ """A function that recursively converts keys in a nested dictionary to primitives.
56
+
57
+ Args:
58
+ data (dict): The input dictionary to convert keys.
59
+
60
+ Returns:
61
+ dict: A new dictionary with keys converted to strings.
62
+ """
63
+ new_dict = {}
64
+ for key, value in data.items():
65
+ if isinstance(value, dict):
66
+ new_dict[str(key)] = convert_keys_to_primitives(value)
67
+ else:
68
+ new_dict[str(key)] = value
69
+ return new_dict
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: t_bug_catcher
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: Bug catcher
5
5
  Home-page: https://www.thoughtful.ai/
6
6
  Author: Thoughtful
@@ -18,6 +18,7 @@ t_bug_catcher.egg-info/dependency_links.txt
18
18
  t_bug_catcher.egg-info/not-zip-safe
19
19
  t_bug_catcher.egg-info/requires.txt
20
20
  t_bug_catcher.egg-info/top_level.txt
21
+ t_bug_catcher/resources/whispers_config.yml
21
22
  t_bug_catcher/utils/__init__.py
22
23
  t_bug_catcher/utils/common.py
23
24
  t_bug_catcher/utils/logger.py
@@ -1,17 +0,0 @@
1
- import traceback
2
- from types import TracebackType
3
- from typing import List
4
-
5
-
6
- def get_frames(exc_traceback: TracebackType) -> List:
7
- """Get the frames of the exception.
8
-
9
- Args:
10
- exc_traceback (TracebackType): The traceback of the exception.
11
-
12
- Returns:
13
- List: The frames of the exception.
14
- """
15
- return [
16
- frame for frame in traceback.extract_tb(exc_traceback) if "site-packages" not in str(frame.filename).lower()
17
- ]
File without changes
File without changes