heitt 0.4.5 → 0.4.6
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.
- checksums.yaml +4 -4
- data/bin/heitt +29 -6
- data/lib/heitt/analyzer.rb +156 -0
- data/lib/heitt/database.rb +276 -1782
- data/lib/heitt/formatter.rb +125 -0
- data/lib/heitt/grouper.rb +22 -0
- data/lib/heitt/profiles.rb +184 -0
- data/lib/heitt/scanner.rb +65 -0
- data/lib/heitt/utils.rb +57 -0
- data/lib/heitt/version.rb +1 -1
- data/lib/heitt.rb +11 -362
- metadata +7 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
require_relative 'utils'
|
|
3
|
+
|
|
4
|
+
module HEITT
|
|
5
|
+
module Formatter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def self.tree(groups, verbose: false, extended: false, show_regex_match: false)
|
|
9
|
+
result = ""
|
|
10
|
+
|
|
11
|
+
#Filter out groups with extended candidates as true
|
|
12
|
+
visible_groups = groups.select do |group|
|
|
13
|
+
has_non_extended = group[:candidates].any? {|c| !c[:extended] || extended}
|
|
14
|
+
has_non_regex = group[:candidates].any? {|c| c[:confidence] != "regex-match" || show_regex_match}
|
|
15
|
+
has_non_extended && has_non_regex
|
|
16
|
+
end
|
|
17
|
+
#Renumber after filtering
|
|
18
|
+
renumbered_groups= visible_groups.each_with_index.map { |group, index| group.merge(cluster_id: index + 1) }
|
|
19
|
+
|
|
20
|
+
root = {
|
|
21
|
+
text: "#{HEITT::Color.colorize("\n\n[", :bold, :blue)}#{HEITT::Color.colorize("CLUSTERED HASHES", :green)}#{HEITT::Color.colorize("]", :bold, :blue)}",
|
|
22
|
+
children: renumbered_groups.map do |group|
|
|
23
|
+
{
|
|
24
|
+
text: HEITT::Color.colorize("HASH CLUSTER #{group[:cluster_id]}", :magenta, :bold),
|
|
25
|
+
children: group[:hashes].map{|h| {text: h, children: []}}
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
result += render_tree([root])
|
|
31
|
+
|
|
32
|
+
renumbered_groups.each do |group|
|
|
33
|
+
result += "#{HEITT::Color.colorize("\n\n[", :bold, :blue)}#{HEITT::Color.colorize("HASH CLUSTER #{group[:cluster_id]}", :white, :bold)}#{HEITT::Color.colorize("]\n", :bold, :blue)}"#, children: []}
|
|
34
|
+
candidate_nodes = (group[:candidates]).each_with_index.map do |candidate, idx|
|
|
35
|
+
next if candidate.nil?
|
|
36
|
+
next if candidate[:name].nil?
|
|
37
|
+
next if candidate[:extended] && !extended
|
|
38
|
+
next if candidate[:confidence] == "regex-match" && !show_regex_match
|
|
39
|
+
confidence = candidate[:confidence] ? " — CONFIDENCE: #{candidate[:confidence].upcase}" : ""
|
|
40
|
+
|
|
41
|
+
children = [
|
|
42
|
+
{text: "Hashcat Mode: #{candidate[:hashcat] || "--"}", children: []},
|
|
43
|
+
{text: "John Format: #{candidate[:john] || "--"}", children: []}
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
if verbose
|
|
47
|
+
if candidate[:description] && !candidate[:description].empty?
|
|
48
|
+
children << {text: "Description: #{candidate[:description]}", children: []}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if candidate[:notes] && !candidate[:notes].empty?
|
|
52
|
+
children << {text: "Notes:", children: candidate[:notes].map {|note| {text: note, children: []}}}
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
{
|
|
56
|
+
text: "#{HEITT::Color.colorize("[", :bold, :blue)}#{HEITT::Color.colorize("CANDIDATE #{idx + 1}: ", :bold, :cyan)}#{HEITT::Color.colorize("#{candidate[:name]}#{confidence}", :bold, :cyan)}#{HEITT::Color.colorize("]", :bold, :blue)}",
|
|
57
|
+
children: children
|
|
58
|
+
}
|
|
59
|
+
end.compact
|
|
60
|
+
result += render_tree(candidate_nodes, "", false, false) unless candidate_nodes.nil? || candidate_nodes.empty?
|
|
61
|
+
end
|
|
62
|
+
result
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def self.json(groups, extended: false, show_regex_match: false)
|
|
66
|
+
visible_groups = groups.select do |group|
|
|
67
|
+
has_non_extended = group[:candidates].any? {|c| c[:extended] || extended}
|
|
68
|
+
has_non_regex = group[:candidates].any? {|c| c[:confidence] != "regex-match" || show_regex_match}
|
|
69
|
+
has_non_extended && has_non_regex
|
|
70
|
+
end
|
|
71
|
+
#Renumber after filtering
|
|
72
|
+
renumbered_groups = visible_groups.each_with_index.map { |group, index| group.merge(cluster_id: index+1)}
|
|
73
|
+
|
|
74
|
+
JSON.pretty_generate(
|
|
75
|
+
renumbered_groups.map do |group|
|
|
76
|
+
visible_candidates = group[:candidates].select do |c|
|
|
77
|
+
(!c[:extended] || extended) && (c[:confidence] != "regex-match" || show_regex_match)
|
|
78
|
+
end
|
|
79
|
+
{
|
|
80
|
+
cluster_id: group[:cluster_id],
|
|
81
|
+
count: group[:count],
|
|
82
|
+
hashes: group[:hashes],
|
|
83
|
+
candidates: visible_candidates.map do |candidate|
|
|
84
|
+
{
|
|
85
|
+
name: candidate[:name],
|
|
86
|
+
hashcat: candidate[:hashcat],
|
|
87
|
+
john: candidate[:john],
|
|
88
|
+
confidence: candidate[:confidence],
|
|
89
|
+
description: candidate[:description]
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
}
|
|
93
|
+
end
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
def self.render_tree(items, prefix = "", parent_is_last=true, is_root=true)
|
|
99
|
+
result = ""
|
|
100
|
+
|
|
101
|
+
items.each_with_index do |node, i|
|
|
102
|
+
is_last_item = (i == items.length - 1)
|
|
103
|
+
|
|
104
|
+
line = if is_root
|
|
105
|
+
"#{node[:text]}\n"
|
|
106
|
+
else
|
|
107
|
+
"#{HEITT::Color.colorize(prefix, :blue)}#{HEITT::Color.colorize((is_last_item ? '└── ' : '├── '), :blue)}#{node[:text]}\n"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
child_prefix = if is_root
|
|
111
|
+
""
|
|
112
|
+
else
|
|
113
|
+
"#{HEITT::Color.colorize(prefix, :bold, :blue)}#{HEITT::Color.colorize((is_last_item ? " " : "│ "), :bold, :blue)}"
|
|
114
|
+
end
|
|
115
|
+
result += line
|
|
116
|
+
result += render_tree(node[:children], child_prefix, is_last_item, false) if node[:children].any?
|
|
117
|
+
|
|
118
|
+
if is_last_item && !is_root and !node[:children].any?
|
|
119
|
+
result += "#{HEITT::Color.colorize(prefix, :bold, :blue)} \n"
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
result
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require_relative 'utils'
|
|
2
|
+
module HEITT
|
|
3
|
+
module Grouper
|
|
4
|
+
|
|
5
|
+
def self.group(results)
|
|
6
|
+
clusters = {}
|
|
7
|
+
|
|
8
|
+
clusters = results.group_by {|r| r[:candidates].first[:name]}
|
|
9
|
+
groups = clusters.each_with_index.map do |(name, group), index|
|
|
10
|
+
hashes = group.map {|r| r[:hash]}
|
|
11
|
+
{
|
|
12
|
+
cluster_id: index+1,
|
|
13
|
+
hashes: hashes,
|
|
14
|
+
candidates: group.first[:candidates],
|
|
15
|
+
count: hashes.size
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
HEITT::Logger.debug("Hashes grouped successfully") unless groups.empty? || groups.nil?
|
|
19
|
+
groups
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
module HEITT
|
|
2
|
+
PROFILES = {
|
|
3
|
+
"CRC-16-CCITT" => {
|
|
4
|
+
description: "Cyclic Redundancy Check 16-bit Consultative Commitee for International Telegraph and Telephone",
|
|
5
|
+
notes: ["Used for error detection in communication and storage systems", "Data Integrity and verification", "Memory checks integrity", "Not cryptographic"],
|
|
6
|
+
context: ["checksum", "telecom", "bluetooth"],
|
|
7
|
+
common_sources: ["V.41", "X.25", "HDLC", "Bluetooth"]
|
|
8
|
+
},
|
|
9
|
+
"CRC-16" => {
|
|
10
|
+
description: "Cyclic Redundancy Check 16-bit — 4 hexadecimal chars, basic checksum",
|
|
11
|
+
notes: ["Error detection in data transmission", "Data storage integrity checks", "Not cryptographic", "Low collision resistance"],
|
|
12
|
+
context: ["checksum", "networking"],
|
|
13
|
+
prefixes: ["crc-16"],
|
|
14
|
+
common_sources: ["file verification", "network protocols", "embedded systems"]
|
|
15
|
+
},
|
|
16
|
+
"FCS-16" => {
|
|
17
|
+
description: "Frame Check Sequence 6-bit — 4 hexadecimal chars, data link layer",
|
|
18
|
+
notes: ["Not cryptographic"],
|
|
19
|
+
prefixes: ["fcs-16"],
|
|
20
|
+
context: ["checksum", "networking"],
|
|
21
|
+
common_sources: ["Ethernet frames", "PPP"]
|
|
22
|
+
},
|
|
23
|
+
"Adler-32" => {
|
|
24
|
+
description: "Adler-32 checksum — 8 hex chars, zlib compression",
|
|
25
|
+
common_sources: ["zlib", "PNG files", "RSYNC"],
|
|
26
|
+
context: ["checksum", "compression"]
|
|
27
|
+
},
|
|
28
|
+
"CRC-32B" => {
|
|
29
|
+
description: "CRC-32 IEEE 802.3 variant — 8 hex chars, Ethernet standard" ,
|
|
30
|
+
notes: ["Not cryptographic"],
|
|
31
|
+
common_sources: ["Ethernet", "MPEG-2", "PKZIP"],
|
|
32
|
+
context: ["checksum", "networking"]
|
|
33
|
+
},
|
|
34
|
+
"FCS-32" => {
|
|
35
|
+
description: "Frame Check Sequence 32-bit — 8 hex chars, advanced networking",
|
|
36
|
+
common_sources: ["advanced networking protocols"],
|
|
37
|
+
context: ["checksum", "networking"]
|
|
38
|
+
},
|
|
39
|
+
"GHash-32-3" => {
|
|
40
|
+
description: "G-Hash 32-bit 3-round — 8 hex chars, experimental hash",
|
|
41
|
+
common_sources: ["research", "academic"],
|
|
42
|
+
context: ["experimental"]
|
|
43
|
+
},
|
|
44
|
+
"GHash-32-5" => {
|
|
45
|
+
description: "G-Hash 32-bit 5-round — 8 hex chars, experimental hash",
|
|
46
|
+
common_sources: ["research", "academic"],
|
|
47
|
+
context: ["experimental"]
|
|
48
|
+
},
|
|
49
|
+
"FNV-132" => {
|
|
50
|
+
description: "Fowler-Noll-Vo hash 32-bit — 8 hex chars, fast non-crypto hash",
|
|
51
|
+
common_sources: ["DNS", "database indexing", "hash tables"],
|
|
52
|
+
context: ["checksum", "programming"]
|
|
53
|
+
},
|
|
54
|
+
"Fletcher-32" => {
|
|
55
|
+
description: "Fletcher's checksum 32-bit — 8 hex chars, error detection",
|
|
56
|
+
common_sources: ["OSTA UDF", "ISO/IEC 8473-1"],
|
|
57
|
+
context: ["checksum", "storage"]
|
|
58
|
+
},
|
|
59
|
+
"Joaat" => {
|
|
60
|
+
description: "Jenkins one-at-a-time hash — 8 hex chars, simple string hash",
|
|
61
|
+
common_sources: ["Perl", "Apache", "various applications"],
|
|
62
|
+
context: ["programming", "hashing"]
|
|
63
|
+
},
|
|
64
|
+
"ELF-32" => {
|
|
65
|
+
description: "ELF-32 hash for object files — 8 hex chars, Unix/Linux object files",
|
|
66
|
+
context: ["executable", "system"],
|
|
67
|
+
mime_types: ["application/octet-stream"]
|
|
68
|
+
},
|
|
69
|
+
"XOR-32" => {
|
|
70
|
+
description: "Simple XOR-based 32-bit hash — 8 hex chars, basic XOR operation",
|
|
71
|
+
common_sources: ["simple applications", "embedded systems"],
|
|
72
|
+
context: ["basic", "embedded"]
|
|
73
|
+
},
|
|
74
|
+
"CRC-24" => {
|
|
75
|
+
description: "Cyclic Redundancy Check 24-bits — 6 hexadecimal chars, OpenPGP standard",
|
|
76
|
+
notes: ["Not cryptographic"],
|
|
77
|
+
context: ["checksum"],
|
|
78
|
+
common_sources: ["OpenPGP", "RFID", "some file formats"]
|
|
79
|
+
},
|
|
80
|
+
"CRC-32" => {
|
|
81
|
+
description: "Cyclic Redundancy Check 32-bit — 8 hex chars, most common checksum",
|
|
82
|
+
notes: ["Not cryptographic"]
|
|
83
|
+
},
|
|
84
|
+
"DES(Unix)" => {
|
|
85
|
+
description: "DES-based Unix crypt — 13 chars, traditional Unix passwords",
|
|
86
|
+
notes: ["Only 8 char passwords", "weak salt"],
|
|
87
|
+
common_sources: ["/etc/passwd", "old Unix systems"],
|
|
88
|
+
context: ["unix", "legacy"]
|
|
89
|
+
},
|
|
90
|
+
"DEScrypt" => {
|
|
91
|
+
description: "DES crypt implementation — 13 chars",
|
|
92
|
+
notes: ["Traditional Unix password hashing"],
|
|
93
|
+
common_sources: ["old Unix/Linux"],
|
|
94
|
+
context: ["unix", "legacy"]
|
|
95
|
+
},
|
|
96
|
+
"MySQL323" => {
|
|
97
|
+
description: "MySQL 3.23 password hash — 16 chars typical, but can be padded to 32 (hexadecimals)",
|
|
98
|
+
notes: ["Used in old MySQL databases", "Can be broken in seconds", "Susceptible to rainbow tables", "Limited to 8 character passwords", "Deprecated since MySQL 4.1"]
|
|
99
|
+
},
|
|
100
|
+
"DES(Oracle)" => {
|
|
101
|
+
description: "Oracle DES-based hash — 16 hex chars, Oracle specific"
|
|
102
|
+
},
|
|
103
|
+
"Half MD5" => {
|
|
104
|
+
description: "First half of MD5 hash — 16 hex chars, MD5 truncated",
|
|
105
|
+
notes: ["Weaker than full MD5"]
|
|
106
|
+
},
|
|
107
|
+
"FNV-164" => {
|
|
108
|
+
description: "Fowler-Noll-Vo hash 64-bit — 16 hex chars, 64-bit version",
|
|
109
|
+
notes: ["Not cryptographic"]
|
|
110
|
+
},
|
|
111
|
+
"CRC-64" => {
|
|
112
|
+
description: "Cyclic Redundancy Check 64-bit — 16 hex chars, ISO 3309",
|
|
113
|
+
notes: ["Not cryptographic"]
|
|
114
|
+
},
|
|
115
|
+
"CRC-96(ZIP)" => {
|
|
116
|
+
description: "CRC-96 used in some ZIP variants — 24 hex chars, extended CRC",
|
|
117
|
+
notes: ["Not cryptographic", "For some archive formats"]
|
|
118
|
+
},
|
|
119
|
+
"Crypt16" => {
|
|
120
|
+
description: "Extended crypt16 implementation",
|
|
121
|
+
characteristics: "24 chars, extended DES crypt",
|
|
122
|
+
notes: ["Rarely used", "Used by some Unix variants"]
|
|
123
|
+
},
|
|
124
|
+
"BigCrypt" => {
|
|
125
|
+
description: "Extended DES crypt — 13+ chars, extended length",
|
|
126
|
+
notes: ["Rarely used", "Used in some Unix variants"],
|
|
127
|
+
common_sources: ["some Unix variants"],
|
|
128
|
+
context: ["unix", "extended"]
|
|
129
|
+
},
|
|
130
|
+
"MD5" => {
|
|
131
|
+
description: "MD5 cryptographic hash function",
|
|
132
|
+
characteristics: "32 chars, hexadecimal, unsalted",
|
|
133
|
+
notes: ["Used as checksum to verify data or file integrity", "MD5 is cryptographically broken as it is vulnerable to collision attacks"],
|
|
134
|
+
context: ["web", "checksum", "legacy", "password", "hash", "md5"],
|
|
135
|
+
prefixes: ["md5", "hash", "checksum", "password"],
|
|
136
|
+
file_types: ["shadow", "htpasswd", "logs"],
|
|
137
|
+
mime_types: ["text/plain", "text/x-passwd"],
|
|
138
|
+
common_sources: ["web applications", "file integrity checks", "checksums", "legacy systems"]
|
|
139
|
+
},
|
|
140
|
+
"MD4" => {
|
|
141
|
+
characteristics: "32 chars, legacy Microsoft systems",
|
|
142
|
+
prefixes: ["hash"],
|
|
143
|
+
context: ["hash"],
|
|
144
|
+
common_sources: ["Old Windows systems", "legacy applications"]
|
|
145
|
+
},
|
|
146
|
+
"LM" => {
|
|
147
|
+
description: "Windows LAN Manager hash",
|
|
148
|
+
characteristics: "16 hex chars, all uppercase, split password",
|
|
149
|
+
notes: ["Mainly found in Windows SAM files(legacy Windows)", "Very weak", "no lowercase", "split passwords"],
|
|
150
|
+
common_sources: ["Windows SAM", "legacy Windows systems"],
|
|
151
|
+
context: ["windows", "SAM"]
|
|
152
|
+
},
|
|
153
|
+
"NTLM" => {
|
|
154
|
+
description: "Windows NTLM authentication hash",
|
|
155
|
+
characteristics: "32 chars, Windows authentication, based on MD4",
|
|
156
|
+
notes: ["Hashcat Mode: 5600 (NetNTLMv2) - if network captured", "Hashcat Mode: 5500 (NetNTLMv1/NetNTLMv1+ESS) - legacy versions", "John Format: netntlm (for network hashes)", "John Format: netntlmv2 (v2 hashes)"],
|
|
157
|
+
context: ["windows", "SAM", "LSASS", "nt", "ntlm"],
|
|
158
|
+
prefixes: ["nt"],
|
|
159
|
+
file_types: ["ntds", "logs"],
|
|
160
|
+
mime_types: ["text/plain", "application/octet-stream"],
|
|
161
|
+
common_sources: ["Windows SAM", "Active Directory", "LSASS memory"]
|
|
162
|
+
},
|
|
163
|
+
"SHA-1" => {
|
|
164
|
+
description: "SHA-1 cryptographic hash function",
|
|
165
|
+
characteristics: "40 chars, hexadecimal, unsalted",
|
|
166
|
+
notes: ["Used for file verification", "found in git commits and legacy certificates"],
|
|
167
|
+
prefixes: ["sha1", "hash"],
|
|
168
|
+
context: ["sha1", "hash"]
|
|
169
|
+
},
|
|
170
|
+
"RIPEMD-160" => {
|
|
171
|
+
characteristics: "40 chars, Bitcoin addresses, digital signatures",
|
|
172
|
+
notes: ["Rarely used for passwords"]
|
|
173
|
+
},
|
|
174
|
+
"Android PIN" => {
|
|
175
|
+
description: "Android PIN/Password hash",
|
|
176
|
+
characteristics: "40 chars hash + 16 chars salt, SHA1 + MD5",
|
|
177
|
+
notes: ["found in android gesture.key files"]
|
|
178
|
+
},
|
|
179
|
+
"SHA-512 Crypt" => {
|
|
180
|
+
characteristics: "$6$ prefix, includes salt, 96-106 chars",
|
|
181
|
+
notes: ["Industry standard for modern Linux systems"]
|
|
182
|
+
},
|
|
183
|
+
}
|
|
184
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
require 'strscan'
|
|
2
|
+
require 'set'
|
|
3
|
+
require_relative 'analyzer'
|
|
4
|
+
require_relative 'database'
|
|
5
|
+
require_relative 'profiles'
|
|
6
|
+
|
|
7
|
+
module HEITT
|
|
8
|
+
module Scanner
|
|
9
|
+
|
|
10
|
+
def self.scan(input, database: HEITT::DATABASE, profiles: HEITT::PROFILES, min_entropy: 3.5)
|
|
11
|
+
text = File.exist?(File.expand_path(input)) ? File.read(File.expand_path(input)) : input
|
|
12
|
+
context_scores = HEITT::Analyzer.analyze(text, profiles: profiles)#database: database)
|
|
13
|
+
found = {}
|
|
14
|
+
#seen = {}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
database.each do |entry|
|
|
18
|
+
regex = get_regex(entry)
|
|
19
|
+
modes = get_modes(entry)
|
|
20
|
+
next unless regex && modes && !modes.empty?
|
|
21
|
+
pattern = regex.is_a?(Regexp) ? regex : Regexp.new(regex)
|
|
22
|
+
scanner = StringScanner.new(text)
|
|
23
|
+
|
|
24
|
+
while scanner.scan_until(pattern)
|
|
25
|
+
matched = scanner.matched
|
|
26
|
+
next unless matched.length < 8 || HEITT::Analyzer.high_entropy?(matched, min_entropy)
|
|
27
|
+
offset = scanner.pos - matched.length
|
|
28
|
+
HEITT::Logger.debug("Extracting prefix..")
|
|
29
|
+
delim_prefix = HEITT::Analyzer.extract_prefix(text, offset)
|
|
30
|
+
HEITT::Logger.debug("Extracted prefix: #{delim_prefix.length <= 1 ? "NULL" : delim_prefix}")
|
|
31
|
+
|
|
32
|
+
candidates = HEITT::Analyzer.score_candidates(modes, delim_prefix, context_scores)
|
|
33
|
+
#score = candidates.first[:score]
|
|
34
|
+
|
|
35
|
+
found[matched] ||= {hash: matched, candidates: []}
|
|
36
|
+
found[matched][:candidates].concat(candidates)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
found.each_value do |result|
|
|
41
|
+
result[:candidates] = result[:candidates]
|
|
42
|
+
.group_by {|c| c[:name]}
|
|
43
|
+
.map {|name, dupes| dupes.max_by {|c| c[:score]}}
|
|
44
|
+
.sort_by {|c| -c[:score]}
|
|
45
|
+
|
|
46
|
+
# Re-assign confidence based on final merged scores
|
|
47
|
+
scores_hash = result[:candidates].map {|c| [c[:name], c[:score]]}.to_h
|
|
48
|
+
confidences = Analyzer.assign_confidence(scores_hash)
|
|
49
|
+
result[:candidates] = result[:candidates].map {|c| c.merge(confidence: confidences[c[:name]])}
|
|
50
|
+
end
|
|
51
|
+
found.values
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def self.get_regex(entry)
|
|
57
|
+
entry[:extract_regex] || entry[:regex] || entry[:pattern] || entry[:regexp]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.get_modes(entry)
|
|
61
|
+
entry[:modes] || entry[:algorithms] || entry[:hashes] ||
|
|
62
|
+
entry[:candidates] || entry[:types] || entry[:hashtypes]
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/heitt/utils.rb
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
require 'colorize'
|
|
3
|
+
|
|
4
|
+
module HEITT
|
|
5
|
+
module Logger
|
|
6
|
+
@debug = false
|
|
7
|
+
#LEVELS = { debug: 0, verbose: 1, info: 2, warn: 3, error: 4 }
|
|
8
|
+
#@level = :warn
|
|
9
|
+
|
|
10
|
+
#def self.set_level(lvl)
|
|
11
|
+
# @level = lvl
|
|
12
|
+
#end
|
|
13
|
+
def self.enable_debug
|
|
14
|
+
@debug = true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.disable_debug
|
|
18
|
+
@debug = false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.debug_enabled?
|
|
22
|
+
@debug
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.debug(msg)
|
|
26
|
+
log("[DEBUG] #{msg}", :cyan)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.warn(msg)
|
|
30
|
+
log("[WARN] #{msg}", :yellow)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.error(msg)
|
|
34
|
+
log("[ERROR] #{msg}", :red)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
def self.log(msg, color)
|
|
39
|
+
return unless @debug
|
|
40
|
+
$stderr.puts HEITT::Color.colorize(msg, color)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
module Color
|
|
46
|
+
def self.colorize(text, color, *styles)
|
|
47
|
+
return text unless STDOUT.isatty #&& !(defined?(Flags) && Flags.no_color)
|
|
48
|
+
|
|
49
|
+
colored = text.colorize(color)
|
|
50
|
+
styles.each do |style|
|
|
51
|
+
colored = colored.send(style)
|
|
52
|
+
end
|
|
53
|
+
colored
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
#private_constant :Color
|
|
57
|
+
end
|