Module: Threatstack::Instrumentation
- Includes:
- Constants
- Defined in:
- lib/instrumentation/rails.rb,
lib/instrumentation/common.rb,
lib/instrumentation/kernel.rb,
lib/instrumentation/instrumenter.rb
Defined Under Namespace
Modules: TSKernel, TSRails
Classes: Instrumenter
Constant Summary
collapse
- @@logger =
Threatstack::Utils::TSLogger.create 'CommonInstrumentation'
- @@submitter =
Threatstack::Jobs::EventSubmitter.instance
Constants included
from Constants
Constants::AGENT_ID, Constants::AGENT_INSTANCE_ID, Constants::AGENT_NAME, Constants::APPSEC_BASE_URL, Constants::APPSEC_EVENTS_URL, Constants::ATTACK, Constants::AWS_METADATA_URL, Constants::BLOCK_SQLI, Constants::BLOCK_XSS, Constants::CGI_VARIABLES, Constants::DEPENDENCIES, Constants::DETECTED_NOT_BLOCKED, Constants::DISABLED, Constants::DROP_FIELDS, Constants::ENVIRONMENT, Constants::EVENTS_PER_REQ, Constants::INSTRUMENTATION, Constants::IPV4, Constants::IPV6, Constants::JOB_INTERVAL, Constants::LOG_COLORS, Constants::LOG_LEVEL, Constants::MANUAL_INIT, Constants::REDACTED, Constants::REQUEST_BLOCKED, Constants::RUBY, Constants::SQLI, Constants::TRUTHY, Constants::XSS
Class Method Summary
collapse
-
.check_parameters(params, location, request, headers, backtrace, include_payload = true) ⇒ Object
-
.check_sqli_payload(param, name = nil) ⇒ Object
-
.check_xss_payload(param, name = nil) ⇒ Object
-
.const_exist?(name) ⇒ Boolean
-
.create_attack_event(payload, type, location, request, headers, backtrace) ⇒ Object
-
.create_instrumentation_event(module_name, method_name, file_path, line_num, arguments) ⇒ Object
-
.drop_sensitive_fields(obj) ⇒ Object
-
.flatten(obj) ⇒ Object
-
.resolve_const(name) ⇒ Object
Methods included from Constants
env, is_truthy
Class Method Details
.check_parameters(params, location, request, headers, backtrace, include_payload = true) ⇒ Object
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
# File 'lib/instrumentation/common.rb', line 145
def self.check_parameters(params, location, request, , backtrace, include_payload = true)
if params.nil? || !params.is_a?(Hash)
@@logger.debug "Skipping param check for: #{params}"
return nil
end
flattened = flatten params
sqli_found, xss_found = false, false
flattened.each do |key, val|
sqli_found = check_sqli_payload(val, key) unless sqli_found
xss_found = check_xss_payload(val, key) unless xss_found
end
payload = include_payload ? params : nil
create_attack_event(payload, SQLI, location, request, , backtrace) if sqli_found
create_attack_event(payload, XSS, location, request, , backtrace) if xss_found
{ :sqli => sqli_found, :xss => xss_found }
end
|
.check_sqli_payload(param, name = nil) ⇒ Object
124
125
126
127
128
129
130
131
132
|
# File 'lib/instrumentation/common.rb', line 124
def self.check_sqli_payload(param, name = nil)
if param.nil? || !param.kind_of?(String)
@@logger.debug "SQLI Check skipped for: #{name}" unless name.nil?
return false
end
match = (Libinjection.libinjection_sqli(param, param.length, '') === 1 ? true : false)
@@logger.send(match ? :warn : :debug, "SQLI Check #{match ? 'positive' : 'negative'} for: #{name}") unless name.nil?
match
end
|
.check_xss_payload(param, name = nil) ⇒ Object
134
135
136
137
138
139
140
141
142
143
|
# File 'lib/instrumentation/common.rb', line 134
def self.check_xss_payload(param, name = nil)
if param.nil? || !param.kind_of?(String)
@@logger.debug "XSS Check skipped for: #{name}" unless name.nil?
return false
end
match = (Libinjection.libinjection_xss(param, param.length) === 1 ? true : false)
@@logger.send(match ? :warn : :debug, "XSS Check #{match ? 'positive' : 'negative'} for: #{name}") unless name.nil?
match
end
|
.const_exist?(name) ⇒ Boolean
55
56
57
58
59
|
# File 'lib/instrumentation/common.rb', line 55
def self.const_exist?(name)
resolve_const(name) && true
rescue NameError, ArgumentError
false
end
|
.create_attack_event(payload, type, location, request, headers, backtrace) ⇒ Object
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
# File 'lib/instrumentation/common.rb', line 30
def self.create_attack_event(payload, type, location, request, , backtrace)
is_blocked = (type == SQLI && BLOCK_SQLI) || (type == XSS && BLOCK_XSS)
data = {
:timestamp => Time.now.utc.strftime('%FT%T.%3NZ'),
:module_name => AGENT_NAME,
:request_ip => request.remote_ip,
:request_headers => ,
:request_url => request.path,
:request_method => request.request_method,
:attack_message => is_blocked ? REQUEST_BLOCKED : DETECTED_NOT_BLOCKED,
:attack_stack => backtrace,
:attack_details => {
:details => [{ :signature => nil, :value => drop_sensitive_fields(payload) }],
:in => location,
:type => type,
:isBlocked => is_blocked,
:action => 'process_action'
}
}
@@logger.debug "Creating attack event with data: #{data}"
@@submitter.queue_event Threatstack::Events::AttackEvent.new(drop_sensitive_fields(data))
end
|
.create_instrumentation_event(module_name, method_name, file_path, line_num, arguments) ⇒ Object
17
18
19
20
21
22
23
24
25
26
27
28
|
# File 'lib/instrumentation/common.rb', line 17
def self.create_instrumentation_event(module_name, method_name, file_path, line_num, arguments)
data = {
:module_name => module_name,
:method_name => method_name,
:file_path => file_path,
:line_num => line_num,
:arguments => drop_sensitive_fields(arguments)
}
@@logger.debug "Creating instrumentation event with data: #{data}"
@@submitter.queue_event Threatstack::Events::InstrumentationEvent.new(drop_sensitive_fields(data))
end
|
.drop_sensitive_fields(obj) ⇒ Object
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
# File 'lib/instrumentation/common.rb', line 95
def self.drop_sensitive_fields(obj)
return obj if DROP_FIELDS.nil?
if obj.is_a?(Hash)
obj.each_with_object({}) do |(k, v), h|
if DROP_FIELDS[k] || DROP_FIELDS[k.to_s]
h[k] = REDACTED
next
end
if v.is_a?(Hash) || v.is_a?(Array)
h[k] = drop_sensitive_fields(v)
else
h[k] = v
end
end
elsif obj.is_a?(Array)
obj.each_with_object([]) do |v, arr|
if v.is_a?(Hash) || v.is_a?(Array)
arr.push drop_sensitive_fields(v)
else
arr.push v
end
end
else
obj
end
end
|
.flatten(obj) ⇒ Object
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
# File 'lib/instrumentation/common.rb', line 67
def self.flatten(obj)
if obj.is_a?(Hash)
obj.each_with_object({}) do |(k, v), h|
if v.is_a?(Hash) || v.is_a?(Array)
flatten(v).map do |h_k, h_v|
h["#{k}.#{h_k}".to_sym] = h_v
end
else
h[k] = v
end
end
elsif obj.is_a?(Array)
h = {}
obj.each_with_index do |v, index|
if v.is_a?(Hash) || v.is_a?(Array)
flatten(v).map do |h_k, h_v|
h["#{index}.#{h_k}".to_sym] = h_v
end
else
h[index.to_s] = v
end
end
h
else
obj
end
end
|
.resolve_const(name) ⇒ Object
61
62
63
64
65
|
# File 'lib/instrumentation/common.rb', line 61
def self.resolve_const(name)
raise ArgumentError if name.nil? || name.empty?
name.to_s.split('::').inject(Object) { |a, e| a.const_get(e) }
end
|