t-bug-catcher 0.4.0__tar.gz → 0.4.1__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.
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/PKG-INFO +1 -1
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/setup.cfg +1 -1
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/setup.py +3 -1
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher/__init__.py +1 -1
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher/config.py +1 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher/jira.py +5 -5
- t_bug_catcher-0.4.1/t_bug_catcher/resources/whispers_config.yml +44 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher/stack_saver.py +24 -29
- t_bug_catcher-0.4.1/t_bug_catcher/utils/common.py +41 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher.egg-info/PKG-INFO +1 -1
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher.egg-info/SOURCES.txt +1 -0
- t_bug_catcher-0.4.0/t_bug_catcher/utils/common.py +0 -17
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/MANIFEST.in +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/README.rst +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/pyproject.toml +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/requirements.txt +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher/bug_catcher.py +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher/bug_snag.py +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher/exceptions.py +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher/utils/__init__.py +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher/utils/logger.py +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher/workitems.py +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher.egg-info/dependency_links.txt +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher.egg-info/not-zip-safe +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher.egg-info/requires.txt +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/t_bug_catcher.egg-info/top_level.txt +0 -0
- {t_bug_catcher-0.4.0 → t_bug_catcher-0.4.1}/tests/test_t_bug_catcher.py +0 -0
|
@@ -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.
|
|
29
|
+
version="0.4.1",
|
|
30
30
|
zip_safe=False,
|
|
31
31
|
install_requires=install_requirements,
|
|
32
|
+
include_package_data=True,
|
|
33
|
+
package_data={"": ["resources/*.yml"]},
|
|
32
34
|
)
|
|
@@ -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.
|
|
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.
|
|
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
|
|
36
16
|
|
|
37
17
|
|
|
38
18
|
class StackSaver:
|
|
@@ -68,11 +48,15 @@ class StackSaver:
|
|
|
68
48
|
run_args = {}
|
|
69
49
|
if frame_info["locals"]:
|
|
70
50
|
for key, value in frame_info["locals"].items():
|
|
51
|
+
if str(key).lower() == "credentials":
|
|
52
|
+
continue
|
|
71
53
|
if isinstance(value, dict):
|
|
72
54
|
run_locals[str(key)] = value
|
|
73
55
|
else:
|
|
74
56
|
run_locals[str(key)] = str(value)
|
|
75
57
|
for key, value in frame_info["args"].items():
|
|
58
|
+
if str(key).lower() == "credentials":
|
|
59
|
+
continue
|
|
76
60
|
if isinstance(value, dict):
|
|
77
61
|
run_args[str(key)] = value
|
|
78
62
|
else:
|
|
@@ -122,19 +106,30 @@ class StackSaver:
|
|
|
122
106
|
with open(file_path, "r") as f:
|
|
123
107
|
filedata = f.readlines()
|
|
124
108
|
|
|
125
|
-
|
|
109
|
+
config_path = Path(__file__).parent.resolve().as_posix() + "/resources/whispers_config.yml"
|
|
110
|
+
|
|
111
|
+
args = f"-c {config_path} {file_path}"
|
|
112
|
+
|
|
113
|
+
secrets = [secret for secret in whispers.secrets(args)]
|
|
114
|
+
unique_secrets = []
|
|
115
|
+
seen = set()
|
|
116
|
+
for secret in secrets:
|
|
117
|
+
item = (secret.key, secret.value, secret.line)
|
|
118
|
+
if item not in seen:
|
|
119
|
+
seen.add(item)
|
|
120
|
+
unique_secrets.append(secret)
|
|
126
121
|
|
|
127
122
|
for index, line in enumerate(filedata):
|
|
128
|
-
if not
|
|
123
|
+
if not unique_secrets:
|
|
129
124
|
break
|
|
130
125
|
|
|
131
|
-
for secret in
|
|
126
|
+
for secret in unique_secrets:
|
|
132
127
|
if secret.key in line and secret.value in line:
|
|
133
128
|
filedata[index] = line.replace(secret.value, secret.value[:1] + "***")
|
|
134
|
-
|
|
129
|
+
unique_secrets.pop(unique_secrets.index(secret))
|
|
135
130
|
break
|
|
136
131
|
|
|
137
|
-
if
|
|
132
|
+
if unique_secrets:
|
|
138
133
|
logger.warning("Failed to mask credentials")
|
|
139
134
|
os.remove(file_path)
|
|
140
135
|
raise Exception("Failed to mask credentials")
|
|
@@ -162,7 +157,7 @@ class StackSaver:
|
|
|
162
157
|
continue
|
|
163
158
|
frames.append(frame)
|
|
164
159
|
tb = tb.tb_next
|
|
165
|
-
frames = frames[:
|
|
160
|
+
frames = frames[: CONFIG.LIMITS.STACK_SCOPE]
|
|
166
161
|
|
|
167
162
|
for frame in frames:
|
|
168
163
|
frame_info = {
|
|
@@ -176,7 +171,7 @@ class StackSaver:
|
|
|
176
171
|
file_path = f"stack_details_{datetime.now().strftime('%Y%m%d%H%M%S')}.json"
|
|
177
172
|
|
|
178
173
|
with open(file_path, "w") as f:
|
|
179
|
-
json.dump(stack_details_json, f, indent=4, cls=
|
|
174
|
+
json.dump(stack_details_json, f, indent=4, cls=Encoder)
|
|
180
175
|
|
|
181
176
|
self.mask_credentials(file_path)
|
|
182
177
|
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
|
|
9
|
+
class Encoder(JSONEncoder):
|
|
10
|
+
"""This class is used to encode the Episode object to json."""
|
|
11
|
+
|
|
12
|
+
def default(self, o):
|
|
13
|
+
"""This method is used to encode the Episode object to json.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
o (object): The object to be encoded.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
str: The json string.
|
|
20
|
+
"""
|
|
21
|
+
if hasattr(o, "__dict__"):
|
|
22
|
+
return o.__dict__
|
|
23
|
+
if isinstance(o, (datetime, date)):
|
|
24
|
+
return o.isoformat()
|
|
25
|
+
if isinstance(o, Path):
|
|
26
|
+
return str(o)
|
|
27
|
+
return JSONEncoder.default(self, o)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_frames(exc_traceback: TracebackType) -> List:
|
|
31
|
+
"""Get the frames of the exception.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
exc_traceback (TracebackType): The traceback of the exception.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
List: The frames of the exception.
|
|
38
|
+
"""
|
|
39
|
+
return [
|
|
40
|
+
frame for frame in traceback.extract_tb(exc_traceback) if "site-packages" not in str(frame.filename).lower()
|
|
41
|
+
]
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|