credsweeper 1.11.2__py3-none-any.whl → 1.11.3__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.
Potentially problematic release.
This version of credsweeper might be problematic. Click here for more details.
- credsweeper/__init__.py +1 -1
- credsweeper/__main__.py +6 -4
- credsweeper/app.py +7 -3
- credsweeper/common/keyword_pattern.py +15 -9
- credsweeper/common/morpheme_checklist.txt +4 -2
- credsweeper/credentials/line_data.py +14 -10
- credsweeper/deep_scanner/abstract_scanner.py +10 -1
- credsweeper/deep_scanner/deep_scanner.py +19 -8
- credsweeper/deep_scanner/docx_scanner.py +1 -1
- credsweeper/deep_scanner/encoder_scanner.py +2 -2
- credsweeper/deep_scanner/html_scanner.py +3 -3
- credsweeper/deep_scanner/jks_scanner.py +2 -4
- credsweeper/deep_scanner/lang_scanner.py +2 -2
- credsweeper/deep_scanner/lzma_scanner.py +40 -0
- credsweeper/deep_scanner/pkcs12_scanner.py +3 -5
- credsweeper/deep_scanner/xml_scanner.py +2 -2
- credsweeper/file_handler/data_content_provider.py +21 -12
- credsweeper/filters/value_array_dictionary_check.py +3 -1
- credsweeper/filters/value_azure_token_check.py +1 -2
- credsweeper/filters/value_base64_part_check.py +30 -21
- credsweeper/filters/value_discord_bot_check.py +1 -2
- credsweeper/filters/value_entropy_base32_check.py +11 -31
- credsweeper/filters/value_entropy_base36_check.py +11 -34
- credsweeper/filters/value_entropy_base64_check.py +19 -48
- credsweeper/filters/value_entropy_base_check.py +37 -0
- credsweeper/filters/value_file_path_check.py +1 -1
- credsweeper/filters/value_hex_number_check.py +3 -3
- credsweeper/filters/value_json_web_token_check.py +4 -5
- credsweeper/filters/value_string_type_check.py +11 -3
- credsweeper/filters/value_token_base32_check.py +0 -4
- credsweeper/filters/value_token_base36_check.py +0 -4
- credsweeper/filters/value_token_base64_check.py +0 -4
- credsweeper/filters/value_token_check.py +1 -1
- credsweeper/ml_model/features/file_extension.py +1 -1
- credsweeper/ml_model/features/morpheme_dense.py +0 -4
- credsweeper/ml_model/features/rule_name.py +1 -1
- credsweeper/ml_model/features/word_in_path.py +0 -9
- credsweeper/ml_model/features/word_in_postamble.py +0 -11
- credsweeper/ml_model/features/word_in_preamble.py +0 -11
- credsweeper/ml_model/features/word_in_transition.py +0 -11
- credsweeper/ml_model/features/word_in_value.py +0 -11
- credsweeper/ml_model/features/word_in_variable.py +0 -11
- credsweeper/ml_model/ml_validator.py +4 -3
- credsweeper/rules/config.yaml +238 -208
- credsweeper/scanner/scan_type/scan_type.py +2 -3
- credsweeper/scanner/scanner.py +7 -1
- credsweeper/secret/config.json +16 -5
- credsweeper/utils/pem_key_detector.py +4 -5
- credsweeper/utils/util.py +67 -144
- {credsweeper-1.11.2.dist-info → credsweeper-1.11.3.dist-info}/METADATA +1 -1
- {credsweeper-1.11.2.dist-info → credsweeper-1.11.3.dist-info}/RECORD +54 -53
- credsweeper/utils/entropy_validator.py +0 -72
- {credsweeper-1.11.2.dist-info → credsweeper-1.11.3.dist-info}/WHEEL +0 -0
- {credsweeper-1.11.2.dist-info → credsweeper-1.11.3.dist-info}/entry_points.txt +0 -0
- {credsweeper-1.11.2.dist-info → credsweeper-1.11.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
import re
|
|
3
3
|
import statistics
|
|
4
|
+
from itertools import takewhile
|
|
4
5
|
|
|
5
6
|
from credsweeper.common.constants import Chars
|
|
6
7
|
from credsweeper.config import Config
|
|
@@ -16,8 +17,8 @@ class ValueBase64PartCheck(Filter):
|
|
|
16
17
|
Check that candidate is NOT a part of base64 long line
|
|
17
18
|
"""
|
|
18
19
|
|
|
19
|
-
base64_pattern = re.compile(r"^(\\{1,8}[0abfnrtv]|[0-9A-Za-z+/=]){1,4000}")
|
|
20
|
-
|
|
20
|
+
base64_pattern = re.compile(r"^(\\{1,8}[0abfnrtv]|[0-9A-Za-z+/=]){1,4000}$")
|
|
21
|
+
base64_char_set = set(Chars.BASE64STDPAD_CHARS.value + '\\')
|
|
21
22
|
|
|
22
23
|
def __init__(self, config: Config = None) -> None:
|
|
23
24
|
pass
|
|
@@ -64,38 +65,46 @@ class ValueBase64PartCheck(Filter):
|
|
|
64
65
|
elif right_end - left_start >= 2 * len_value:
|
|
65
66
|
# simple analysis for data too large to yield sensible insights
|
|
66
67
|
part_set = set(line[left_start:right_end])
|
|
67
|
-
if not part_set.difference(
|
|
68
|
+
if not part_set.difference(ValueBase64PartCheck.base64_char_set):
|
|
68
69
|
# obvious case: all characters are base64 standard
|
|
69
70
|
return True
|
|
70
71
|
|
|
71
|
-
left_part =
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
left_part = ''.join(
|
|
73
|
+
takewhile(lambda x: x in ValueBase64PartCheck.base64_char_set,
|
|
74
|
+
reversed(line[left_start:line_data.value_start])))
|
|
75
|
+
|
|
76
|
+
right_part = ''.join(
|
|
77
|
+
takewhile(lambda x: x in ValueBase64PartCheck.base64_char_set, line[line_data.value_end:right_end]))
|
|
75
78
|
|
|
76
79
|
min_entropy_value = ValueEntropyBase64Check.get_min_data_entropy(len_value)
|
|
77
|
-
value_entropy = Util.get_shannon_entropy(value, Chars.BASE64STD_CHARS.value)
|
|
78
80
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
left_entropy = Util.get_shannon_entropy(left_part)
|
|
82
|
+
value_entropy = Util.get_shannon_entropy(value)
|
|
83
|
+
right_entropy = Util.get_shannon_entropy(right_part)
|
|
84
|
+
common = left_part + value + right_part
|
|
85
|
+
common_entropy = Util.get_shannon_entropy(common)
|
|
86
|
+
min_entropy_common = ValueEntropyBase64Check.get_min_data_entropy(len(common))
|
|
87
|
+
if min_entropy_common < common_entropy:
|
|
88
|
+
return True
|
|
85
89
|
|
|
86
|
-
if
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
if left_entropy and right_entropy:
|
|
91
|
+
data = [left_entropy, value_entropy, right_entropy, min_entropy_value, common_entropy]
|
|
92
|
+
elif left_entropy and not right_entropy:
|
|
93
|
+
data = [left_entropy, value_entropy, min_entropy_value, min_entropy_value, common_entropy]
|
|
94
|
+
elif not left_entropy and right_entropy:
|
|
95
|
+
data = [value_entropy, right_entropy, min_entropy_value, min_entropy_value, common_entropy]
|
|
90
96
|
else:
|
|
91
|
-
|
|
97
|
+
return False
|
|
92
98
|
|
|
93
|
-
data = [left_entropy, value_entropy, right_entropy, min_entropy_value]
|
|
94
99
|
avg = statistics.mean(data)
|
|
95
100
|
stdev = statistics.stdev(data, avg)
|
|
96
101
|
avg_min = avg - 1.1 * stdev
|
|
97
|
-
if
|
|
102
|
+
if (0. == left_entropy or avg_min < left_entropy or left_entropy < value_entropy < right_entropy) \
|
|
103
|
+
and (
|
|
104
|
+
0. == right_entropy or avg_min < right_entropy or right_entropy < value_entropy < left_entropy):
|
|
98
105
|
# high entropy of bound parts looks like a part of base64 long line
|
|
99
106
|
return True
|
|
107
|
+
else:
|
|
108
|
+
return False
|
|
100
109
|
|
|
101
110
|
return False
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
|
|
3
|
-
from credsweeper.common.constants import Chars
|
|
4
3
|
from credsweeper.config import Config
|
|
5
4
|
from credsweeper.credentials import LineData
|
|
6
5
|
from credsweeper.file_handler.analysis_target import AnalysisTarget
|
|
@@ -32,7 +31,7 @@ class ValueDiscordBotCheck(Filter):
|
|
|
32
31
|
id_part = line_data.value[:dot_separator_index]
|
|
33
32
|
discord_id = int(Util.decode_base64(id_part, padding_safe=True, urlsafe_detect=True))
|
|
34
33
|
entropy_part = line_data.value[dot_separator_index:]
|
|
35
|
-
entropy = Util.get_shannon_entropy(entropy_part
|
|
34
|
+
entropy = Util.get_shannon_entropy(entropy_part)
|
|
36
35
|
min_entropy = ValueEntropyBase64Check.get_min_data_entropy(len(entropy_part))
|
|
37
36
|
if 1000 <= discord_id and min_entropy <= entropy:
|
|
38
37
|
return False
|
|
@@ -1,42 +1,22 @@
|
|
|
1
1
|
import math
|
|
2
|
+
from functools import cache
|
|
2
3
|
|
|
3
|
-
from credsweeper.
|
|
4
|
-
from credsweeper.config import Config
|
|
5
|
-
from credsweeper.credentials import LineData
|
|
6
|
-
from credsweeper.file_handler.analysis_target import AnalysisTarget
|
|
7
|
-
from credsweeper.filters import Filter
|
|
8
|
-
from credsweeper.utils import Util
|
|
4
|
+
from credsweeper.filters.value_entropy_base_check import ValueEntropyBaseCheck
|
|
9
5
|
|
|
10
6
|
|
|
11
|
-
class ValueEntropyBase32Check(
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
def __init__(self, config: Config = None) -> None:
|
|
15
|
-
pass
|
|
7
|
+
class ValueEntropyBase32Check(ValueEntropyBaseCheck):
|
|
8
|
+
"""Base32 entropy check"""
|
|
16
9
|
|
|
17
10
|
@staticmethod
|
|
11
|
+
@cache
|
|
18
12
|
def get_min_data_entropy(x: int) -> float:
|
|
19
13
|
"""Returns average entropy for size of random data. Precalculated data is applied for speedup"""
|
|
20
|
-
if
|
|
21
|
-
y =
|
|
22
|
-
elif
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
if 8 <= x < 17:
|
|
15
|
+
y = 0.80569236 * math.log2(x) + 0.13439734
|
|
16
|
+
elif 17 <= x < 33:
|
|
17
|
+
y = 0.66350481 * math.log2(x) + 0.71143862
|
|
18
|
+
elif 33 <= x:
|
|
19
|
+
y = 4.04
|
|
25
20
|
else:
|
|
26
21
|
y = 0
|
|
27
22
|
return y
|
|
28
|
-
|
|
29
|
-
def run(self, line_data: LineData, target: AnalysisTarget) -> bool:
|
|
30
|
-
"""Run filter checks on received credential candidate data 'line_data'.
|
|
31
|
-
|
|
32
|
-
Args:
|
|
33
|
-
line_data: credential candidate data
|
|
34
|
-
target: multiline target from which line data was obtained
|
|
35
|
-
|
|
36
|
-
Return:
|
|
37
|
-
True, if need to filter candidate and False if left
|
|
38
|
-
|
|
39
|
-
"""
|
|
40
|
-
entropy = Util.get_shannon_entropy(line_data.value, Chars.BASE32_CHARS.value)
|
|
41
|
-
min_entropy = ValueEntropyBase32Check.get_min_data_entropy(len(line_data.value))
|
|
42
|
-
return min_entropy > entropy or 0 == min_entropy
|
|
@@ -1,46 +1,23 @@
|
|
|
1
1
|
import math
|
|
2
|
+
from functools import cache
|
|
2
3
|
|
|
3
|
-
from credsweeper.
|
|
4
|
-
from credsweeper.config import Config
|
|
5
|
-
from credsweeper.credentials import LineData
|
|
6
|
-
from credsweeper.file_handler.analysis_target import AnalysisTarget
|
|
7
|
-
from credsweeper.filters import Filter
|
|
8
|
-
from credsweeper.utils import Util
|
|
4
|
+
from credsweeper.filters.value_entropy_base_check import ValueEntropyBaseCheck
|
|
9
5
|
|
|
10
6
|
|
|
11
|
-
class ValueEntropyBase36Check(
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
def __init__(self, config: Config = None) -> None:
|
|
15
|
-
pass
|
|
7
|
+
class ValueEntropyBase36Check(ValueEntropyBaseCheck):
|
|
8
|
+
"""Base36 entropy check"""
|
|
16
9
|
|
|
17
10
|
@staticmethod
|
|
11
|
+
@cache
|
|
18
12
|
def get_min_data_entropy(x: int) -> float:
|
|
19
13
|
"""Returns minimal entropy for size of random data. Precalculated data is applied for speedup"""
|
|
20
14
|
if 15 == x:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# approximation does not exceed standard deviation
|
|
28
|
-
y = 0.7 * math.log2(x) + 0.7
|
|
15
|
+
# workaround for Dropbox App secret
|
|
16
|
+
y = 3.374
|
|
17
|
+
elif 10 <= x < 26:
|
|
18
|
+
y = 0.731566857 * math.log2(x) + 0.474132
|
|
19
|
+
elif 26 <= x:
|
|
20
|
+
y = 3.9
|
|
29
21
|
else:
|
|
30
22
|
y = 0
|
|
31
23
|
return y
|
|
32
|
-
|
|
33
|
-
def run(self, line_data: LineData, target: AnalysisTarget) -> bool:
|
|
34
|
-
"""Run filter checks on received credential candidate data 'line_data'.
|
|
35
|
-
|
|
36
|
-
Args:
|
|
37
|
-
line_data: credential candidate data
|
|
38
|
-
target: multiline target from which line data was obtained
|
|
39
|
-
|
|
40
|
-
Return:
|
|
41
|
-
True, if need to filter candidate and False if left
|
|
42
|
-
|
|
43
|
-
"""
|
|
44
|
-
entropy = Util.get_shannon_entropy(line_data.value, Chars.BASE36_CHARS.value)
|
|
45
|
-
min_entropy = ValueEntropyBase36Check.get_min_data_entropy(len(line_data.value))
|
|
46
|
-
return min_entropy > entropy or 0 == min_entropy
|
|
@@ -1,59 +1,30 @@
|
|
|
1
1
|
import math
|
|
2
|
+
from functools import cache
|
|
2
3
|
|
|
3
|
-
from credsweeper.
|
|
4
|
-
from credsweeper.config import Config
|
|
5
|
-
from credsweeper.credentials import LineData
|
|
6
|
-
from credsweeper.file_handler.analysis_target import AnalysisTarget
|
|
7
|
-
from credsweeper.filters import Filter
|
|
8
|
-
from credsweeper.utils import Util
|
|
4
|
+
from credsweeper.filters.value_entropy_base_check import ValueEntropyBaseCheck
|
|
9
5
|
|
|
10
6
|
|
|
11
|
-
class ValueEntropyBase64Check(
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
# If the value size is less than this value the entropy evaluation gives an imprecise result
|
|
15
|
-
min_length = 12
|
|
16
|
-
|
|
17
|
-
def __init__(self, config: Config = None) -> None:
|
|
18
|
-
pass
|
|
7
|
+
class ValueEntropyBase64Check(ValueEntropyBaseCheck):
|
|
8
|
+
"""Base64 entropy check"""
|
|
19
9
|
|
|
20
10
|
@staticmethod
|
|
11
|
+
@cache
|
|
21
12
|
def get_min_data_entropy(x: int) -> float:
|
|
22
13
|
"""Returns minimal average entropy for size of random data. Precalculated round data is applied for speedup"""
|
|
23
|
-
if
|
|
24
|
-
y =
|
|
25
|
-
elif
|
|
26
|
-
y =
|
|
27
|
-
elif
|
|
28
|
-
y =
|
|
29
|
-
elif
|
|
30
|
-
y =
|
|
31
|
-
elif
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
# the entropy grows slowly after 60
|
|
38
|
-
y = 5.0
|
|
14
|
+
if 12 <= x < 18:
|
|
15
|
+
y = 0.915 * math.log2(x) - 0.047
|
|
16
|
+
elif 18 <= x < 35:
|
|
17
|
+
y = 0.767 * math.log2(x) + 0.5677
|
|
18
|
+
elif 35 <= x < 65:
|
|
19
|
+
y = 0.944 * math.log2(x) - 0.009 * x - 0.04
|
|
20
|
+
elif 65 <= x < 256:
|
|
21
|
+
y = 0.621 * math.log2(x) - 0.003 * x + 1.54
|
|
22
|
+
elif 256 <= x < 512:
|
|
23
|
+
y = 5.77
|
|
24
|
+
elif 512 <= x < 1024:
|
|
25
|
+
y = 5.89
|
|
26
|
+
elif 1024 <= x:
|
|
27
|
+
y = 5.94
|
|
39
28
|
else:
|
|
40
29
|
y = 0
|
|
41
30
|
return y
|
|
42
|
-
|
|
43
|
-
def run(self, line_data: LineData, target: AnalysisTarget) -> bool:
|
|
44
|
-
"""Run filter checks on received credential candidate data 'line_data'.
|
|
45
|
-
|
|
46
|
-
Args:
|
|
47
|
-
line_data: credential candidate data
|
|
48
|
-
target: multiline target from which line data was obtained
|
|
49
|
-
|
|
50
|
-
Return:
|
|
51
|
-
True, if need to filter candidate and False if left
|
|
52
|
-
|
|
53
|
-
"""
|
|
54
|
-
if '-' in line_data.value or '_' in line_data.value:
|
|
55
|
-
entropy = Util.get_shannon_entropy(line_data.value, Chars.BASE64URL_CHARS.value)
|
|
56
|
-
else:
|
|
57
|
-
entropy = Util.get_shannon_entropy(line_data.value, Chars.BASE64STD_CHARS.value)
|
|
58
|
-
min_entropy = ValueEntropyBase64Check.get_min_data_entropy(len(line_data.value))
|
|
59
|
-
return min_entropy > entropy or 0 == min_entropy
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
|
|
3
|
+
from credsweeper.config import Config
|
|
4
|
+
from credsweeper.credentials import LineData
|
|
5
|
+
from credsweeper.file_handler.analysis_target import AnalysisTarget
|
|
6
|
+
from credsweeper.filters import Filter
|
|
7
|
+
from credsweeper.utils import Util
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ValueEntropyBaseCheck(Filter):
|
|
11
|
+
"""Check that candidate value has minimal Shanon Entropy for appropriated base"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, config: Config = None) -> None:
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def get_min_data_entropy(x: int) -> float:
|
|
19
|
+
"""Returns minimal entropy for size of data"""
|
|
20
|
+
raise NotImplementedError()
|
|
21
|
+
|
|
22
|
+
def run(self, line_data: LineData, target: AnalysisTarget) -> bool:
|
|
23
|
+
"""Run filter checks on received credential candidate data 'line_data'.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
line_data: credential candidate data
|
|
27
|
+
target: multiline target from which line data was obtained
|
|
28
|
+
|
|
29
|
+
Return:
|
|
30
|
+
True, when need to filter candidate and False if left
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
entropy = Util.get_shannon_entropy(line_data.value)
|
|
34
|
+
min_entropy = self.get_min_data_entropy(len(line_data.value))
|
|
35
|
+
if min_entropy > entropy or 0 == min_entropy:
|
|
36
|
+
return True
|
|
37
|
+
return False
|
|
@@ -53,7 +53,7 @@ class ValueFilePathCheck(Filter):
|
|
|
53
53
|
break
|
|
54
54
|
else:
|
|
55
55
|
# all symbols are from base64 alphabet
|
|
56
|
-
entropy = Util.get_shannon_entropy(value
|
|
56
|
+
entropy = Util.get_shannon_entropy(value)
|
|
57
57
|
if 0 == min_entropy or min_entropy > entropy:
|
|
58
58
|
contains_unix_separator = 1 < value.count('/')
|
|
59
59
|
else:
|
|
@@ -7,9 +7,9 @@ from credsweeper.filters import Filter
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class ValueHexNumberCheck(Filter):
|
|
10
|
-
"""Check value if it a value
|
|
10
|
+
"""Check value if it is a value up to 64 bits hex representation"""
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
HEX_08_64_VALUE_REGEX = re.compile(r"^0x[0-9a-f]{1,16}$")
|
|
13
13
|
|
|
14
14
|
def __init__(self, config: Config = None) -> None:
|
|
15
15
|
pass
|
|
@@ -26,6 +26,6 @@ class ValueHexNumberCheck(Filter):
|
|
|
26
26
|
|
|
27
27
|
"""
|
|
28
28
|
value = line_data.value.lower()
|
|
29
|
-
if
|
|
29
|
+
if ValueHexNumberCheck.HEX_08_64_VALUE_REGEX.match(value):
|
|
30
30
|
return True
|
|
31
31
|
return False
|
|
@@ -15,14 +15,13 @@ class ValueJsonWebTokenCheck(Filter):
|
|
|
15
15
|
https://www.iana.org/assignments/jose/jose.xhtml
|
|
16
16
|
"""
|
|
17
17
|
header_keys = {
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"p2c", "iss", "sub", "aud", "b64", "ppt", "url", "nonce", "svt"
|
|
18
|
+
"kid", "x5u", "x5t", "x5t#S256", "typ", "cty", "crit", "alg", "enc", "zip", "jku", "jwk", "x5c", "epk", "apu",
|
|
19
|
+
"apv", "iv", "tag", "p2s", "p2c", "iss", "sub", "aud", "b64", "ppt", "url", "nonce", "svt"
|
|
21
20
|
}
|
|
22
21
|
payload_keys = {
|
|
23
22
|
"iss", "sub", "aud", "exp", "nbf", "iat", "jti", "kty", "use", "key_ops", "alg", "enc", "zip", "jku", "jwk",
|
|
24
|
-
"kid", "x5u", "x5c", "x5t", "x5t#S256", "
|
|
25
|
-
"
|
|
23
|
+
"kid", "x5u", "x5c", "x5t", "x5t#S256", "x", "y", "d", "n", "e", "p", "q", "dp", "dq", "qi", "oth", "k", "crv",
|
|
24
|
+
"ext", "crit", "keys", "id", "role", "token", "secret", "password", "nonce"
|
|
26
25
|
}
|
|
27
26
|
|
|
28
27
|
def __init__(self, config: Config = None) -> None:
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
1
3
|
from credsweeper.config import Config
|
|
2
4
|
from credsweeper.credentials import LineData
|
|
3
5
|
from credsweeper.file_handler.analysis_target import AnalysisTarget
|
|
@@ -9,6 +11,7 @@ class ValueStringTypeCheck(Filter):
|
|
|
9
11
|
|
|
10
12
|
If it is, then checks if line_data really have string literal declaration.
|
|
11
13
|
Comment rows in source files (start with //, /\*, etc) ignored.
|
|
14
|
+
Multiple bytes scenario allowed [123,23,54,67,78,89] or {0xae, 0x54, 0x55, 0xff}
|
|
12
15
|
|
|
13
16
|
True if:
|
|
14
17
|
|
|
@@ -20,6 +23,8 @@ class ValueStringTypeCheck(Filter):
|
|
|
20
23
|
False otherwise
|
|
21
24
|
"""
|
|
22
25
|
|
|
26
|
+
MULTIBYTE_PATTERN = re.compile(r"(\s*(0x)?[0-9a-f]{1,3}\s*,){8,80}", flags=re.IGNORECASE)
|
|
27
|
+
|
|
23
28
|
def __init__(self, config: Config) -> None:
|
|
24
29
|
self.check_for_literals = config.check_for_literals
|
|
25
30
|
|
|
@@ -37,10 +42,13 @@ class ValueStringTypeCheck(Filter):
|
|
|
37
42
|
if not self.check_for_literals or line_data.url_part:
|
|
38
43
|
return False
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
if ValueStringTypeCheck.MULTIBYTE_PATTERN.match(line_data.value):
|
|
46
|
+
return False
|
|
42
47
|
|
|
43
|
-
if line_data.is_source_file_with_quotes()
|
|
48
|
+
if line_data.is_source_file_with_quotes() \
|
|
49
|
+
and not line_data.is_comment() \
|
|
50
|
+
and not line_data.is_well_quoted_value \
|
|
51
|
+
and not line_data.is_quoted \
|
|
44
52
|
and line_data.separator and '=' in line_data.separator:
|
|
45
53
|
# heterogeneous code e.g. YAML in Python uses colon sign instead equals
|
|
46
54
|
return True
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from typing import Tuple
|
|
2
2
|
|
|
3
|
-
from credsweeper.config import Config
|
|
4
3
|
from credsweeper.filters.value_token_base_check import ValueTokenBaseCheck
|
|
5
4
|
|
|
6
5
|
|
|
@@ -21,9 +20,6 @@ class ValueTokenBase32Check(ValueTokenBaseCheck):
|
|
|
21
20
|
64: ((3.4805990476190476, 0.28572156450556774), (2.035756800745673, 0.18815721535870078)),
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
def __init__(self, config: Config = None) -> None:
|
|
25
|
-
super().__init__(config)
|
|
26
|
-
|
|
27
23
|
@staticmethod
|
|
28
24
|
def get_stat_range(size: int) -> Tuple[Tuple[float, float], Tuple[float, float]]:
|
|
29
25
|
"""Returns minimal, maximal for hop and deviation. Precalculated data is applied for speedup"""
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from typing import Tuple
|
|
2
2
|
|
|
3
|
-
from credsweeper.config import Config
|
|
4
3
|
from credsweeper.filters.value_token_base_check import ValueTokenBaseCheck
|
|
5
4
|
|
|
6
5
|
|
|
@@ -21,9 +20,6 @@ class ValueTokenBase36Check(ValueTokenBaseCheck):
|
|
|
21
20
|
64: ((3.7190009761904763, 0.30325954360127116), (2.1751172797904093, 0.1942582237461476)),
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
def __init__(self, config: Config = None) -> None:
|
|
25
|
-
super().__init__(config)
|
|
26
|
-
|
|
27
23
|
@staticmethod
|
|
28
24
|
def get_stat_range(size: int) -> Tuple[Tuple[float, float], Tuple[float, float]]:
|
|
29
25
|
"""Returns minimal, maximal for hop and deviation. Precalculated data is applied for speedup"""
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from typing import Tuple
|
|
2
2
|
|
|
3
|
-
from credsweeper.config import Config
|
|
4
3
|
from credsweeper.filters.value_token_base_check import ValueTokenBaseCheck
|
|
5
4
|
|
|
6
5
|
|
|
@@ -21,9 +20,6 @@ class ValueTokenBase64Check(ValueTokenBaseCheck):
|
|
|
21
20
|
64: ((3.7625271746031745, 0.31733579704946846), (2.257532519514275, 0.20571908142867643)),
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
def __init__(self, config: Config = None) -> None:
|
|
25
|
-
super().__init__(config)
|
|
26
|
-
|
|
27
23
|
@staticmethod
|
|
28
24
|
def get_stat_range(size: int) -> Tuple[Tuple[float, float], Tuple[float, float]]:
|
|
29
25
|
"""Returns minimal, maximal for hop and deviation. Precalculated data is applied for speedup"""
|
|
@@ -15,7 +15,7 @@ class FileExtension(WordIn):
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
def __init__(self, extensions: List[str]) -> None:
|
|
18
|
-
super().__init__(extensions)
|
|
18
|
+
super().__init__(words=extensions)
|
|
19
19
|
|
|
20
20
|
def __call__(self, candidates: List[Candidate]) -> np.ndarray:
|
|
21
21
|
extension_set = set([candidate.line_data_list[0].file_type.lower() for candidate in candidates])
|
|
@@ -6,10 +6,6 @@ from credsweeper.ml_model.features.feature import Feature
|
|
|
6
6
|
class MorphemeDense(Feature):
|
|
7
7
|
"""Feature calculates morphemes density for a value"""
|
|
8
8
|
|
|
9
|
-
def __init__(self) -> None:
|
|
10
|
-
"""Class initializer"""
|
|
11
|
-
super().__init__()
|
|
12
|
-
|
|
13
9
|
def extract(self, candidate: Candidate) -> float:
|
|
14
10
|
if value := candidate.line_data_list[0].value.lower():
|
|
15
11
|
morphemes_counter = 0
|
|
@@ -15,7 +15,7 @@ class RuleName(WordIn):
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
def __init__(self, rule_names: List[str]) -> None:
|
|
18
|
-
super().__init__(rule_names)
|
|
18
|
+
super().__init__(words=rule_names)
|
|
19
19
|
|
|
20
20
|
def __call__(self, candidates: List[Candidate]) -> np.ndarray:
|
|
21
21
|
candidate_rule_set = set(x.rule_name for x in candidates)
|
|
@@ -10,15 +10,6 @@ from credsweeper.ml_model.features.word_in import WordIn
|
|
|
10
10
|
class WordInPath(WordIn):
|
|
11
11
|
"""Categorical feature that corresponds to words in path (POSIX, lowercase)"""
|
|
12
12
|
|
|
13
|
-
def __init__(self, words: List[str]) -> None:
|
|
14
|
-
"""WordInPath constructor
|
|
15
|
-
|
|
16
|
-
Args:
|
|
17
|
-
words: list of predefined words - MUST BE IN LOWER CASE & POSIX
|
|
18
|
-
|
|
19
|
-
"""
|
|
20
|
-
super().__init__(words)
|
|
21
|
-
|
|
22
13
|
def __call__(self, candidates: List[Candidate]) -> np.ndarray:
|
|
23
14
|
# actually there must be one path because the candidates are grouped before
|
|
24
15
|
if file_path := candidates[0].line_data_list[0].path:
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
|
|
3
1
|
import numpy as np
|
|
4
2
|
|
|
5
3
|
from credsweeper.common.constants import ML_HUNK
|
|
@@ -10,15 +8,6 @@ from credsweeper.ml_model.features.word_in import WordIn
|
|
|
10
8
|
class WordInPostamble(WordIn):
|
|
11
9
|
"""Feature is true if line contains at least one word from predefined list."""
|
|
12
10
|
|
|
13
|
-
def __init__(self, words: List[str]) -> None:
|
|
14
|
-
"""Feature returns array of matching words
|
|
15
|
-
|
|
16
|
-
Args:
|
|
17
|
-
words: list of predefined words - MUST BE IN LOWER CASE
|
|
18
|
-
|
|
19
|
-
"""
|
|
20
|
-
super().__init__(words)
|
|
21
|
-
|
|
22
11
|
def extract(self, candidate: Candidate) -> np.ndarray:
|
|
23
12
|
"""Returns true if any words in a part of line after value"""
|
|
24
13
|
postamble_end = len(candidate.line_data_list[0].line) \
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
|
|
3
1
|
import numpy as np
|
|
4
2
|
|
|
5
3
|
from credsweeper.common.constants import ML_HUNK
|
|
@@ -10,15 +8,6 @@ from credsweeper.ml_model.features.word_in import WordIn
|
|
|
10
8
|
class WordInPreamble(WordIn):
|
|
11
9
|
"""Feature is true if line contains at least one word from predefined list."""
|
|
12
10
|
|
|
13
|
-
def __init__(self, words: List[str]) -> None:
|
|
14
|
-
"""Feature returns array of matching words
|
|
15
|
-
|
|
16
|
-
Args:
|
|
17
|
-
words: list of predefined words - MUST BE IN LOWER CASE
|
|
18
|
-
|
|
19
|
-
"""
|
|
20
|
-
super().__init__(words)
|
|
21
|
-
|
|
22
11
|
def extract(self, candidate: Candidate) -> np.ndarray:
|
|
23
12
|
"""Returns true if any words in line before variable or value"""
|
|
24
13
|
if 0 <= candidate.line_data_list[0].variable_start:
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
|
|
3
1
|
import numpy as np
|
|
4
2
|
|
|
5
3
|
from credsweeper.credentials import Candidate
|
|
@@ -9,15 +7,6 @@ from credsweeper.ml_model.features.word_in import WordIn
|
|
|
9
7
|
class WordInTransition(WordIn):
|
|
10
8
|
"""Feature is true if line contains at least one word from predefined list."""
|
|
11
9
|
|
|
12
|
-
def __init__(self, words: List[str]) -> None:
|
|
13
|
-
"""Feature returns array of matching words
|
|
14
|
-
|
|
15
|
-
Args:
|
|
16
|
-
words: list of predefined words - MUST BE IN LOWER CASE
|
|
17
|
-
|
|
18
|
-
"""
|
|
19
|
-
super().__init__(words)
|
|
20
|
-
|
|
21
10
|
def extract(self, candidate: Candidate) -> np.ndarray:
|
|
22
11
|
"""Returns true if any words between variable and value"""
|
|
23
12
|
if 0 <= candidate.line_data_list[0].variable_end < candidate.line_data_list[0].value_start:
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
|
|
3
1
|
import numpy as np
|
|
4
2
|
|
|
5
3
|
from credsweeper.credentials import Candidate
|
|
@@ -9,15 +7,6 @@ from credsweeper.ml_model.features.word_in import WordIn
|
|
|
9
7
|
class WordInValue(WordIn):
|
|
10
8
|
"""Feature returns true if candidate value contains at least one word from predefined list."""
|
|
11
9
|
|
|
12
|
-
def __init__(self, words: List[str]) -> None:
|
|
13
|
-
"""Feature is true if candidate value contains at least one predefined word.
|
|
14
|
-
|
|
15
|
-
Args:
|
|
16
|
-
words: list of predefined words - MUST BE IN LOWER CASE and SORTED (preferred)
|
|
17
|
-
|
|
18
|
-
"""
|
|
19
|
-
super().__init__(words)
|
|
20
|
-
|
|
21
10
|
def extract(self, candidate: Candidate) -> np.ndarray:
|
|
22
11
|
"""Returns array of matching words for first line"""
|
|
23
12
|
if value := candidate.line_data_list[0].value:
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
|
|
3
1
|
import numpy as np
|
|
4
2
|
|
|
5
3
|
from credsweeper.credentials import Candidate
|
|
@@ -9,15 +7,6 @@ from credsweeper.ml_model.features.word_in import WordIn
|
|
|
9
7
|
class WordInVariable(WordIn):
|
|
10
8
|
"""Feature returns array of words matching in variable"""
|
|
11
9
|
|
|
12
|
-
def __init__(self, words: List[str]) -> None:
|
|
13
|
-
"""Feature is true if candidate value contains at least one predefined word.
|
|
14
|
-
|
|
15
|
-
Args:
|
|
16
|
-
words: list of predefined words - MUST BE IN LOWER CASE
|
|
17
|
-
|
|
18
|
-
"""
|
|
19
|
-
super().__init__(words)
|
|
20
|
-
|
|
21
10
|
def extract(self, candidate: Candidate) -> np.ndarray:
|
|
22
11
|
"""Returns array of matching words for first line"""
|
|
23
12
|
if variable := candidate.line_data_list[0].variable:
|