sqreen-alt 1.10.0
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 +7 -0
- data/CODE_OF_CONDUCT.md +22 -0
- data/README.md +77 -0
- data/Rakefile +20 -0
- data/lib/sqreen-alt.rb +1 -0
- data/lib/sqreen.rb +68 -0
- data/lib/sqreen/attack_detected.html +2 -0
- data/lib/sqreen/binding_accessor.rb +288 -0
- data/lib/sqreen/ca.crt +72 -0
- data/lib/sqreen/call_countable.rb +67 -0
- data/lib/sqreen/callback_tree.rb +78 -0
- data/lib/sqreen/callbacks.rb +100 -0
- data/lib/sqreen/capped_queue.rb +23 -0
- data/lib/sqreen/condition_evaluator.rb +235 -0
- data/lib/sqreen/conditionable.rb +50 -0
- data/lib/sqreen/configuration.rb +168 -0
- data/lib/sqreen/context.rb +26 -0
- data/lib/sqreen/deliveries/batch.rb +84 -0
- data/lib/sqreen/deliveries/simple.rb +39 -0
- data/lib/sqreen/event.rb +16 -0
- data/lib/sqreen/events/attack.rb +61 -0
- data/lib/sqreen/events/remote_exception.rb +54 -0
- data/lib/sqreen/events/request_record.rb +62 -0
- data/lib/sqreen/exception.rb +34 -0
- data/lib/sqreen/frameworks.rb +40 -0
- data/lib/sqreen/frameworks/generic.rb +446 -0
- data/lib/sqreen/frameworks/rails.rb +148 -0
- data/lib/sqreen/frameworks/rails3.rb +36 -0
- data/lib/sqreen/frameworks/request_recorder.rb +69 -0
- data/lib/sqreen/frameworks/sinatra.rb +57 -0
- data/lib/sqreen/frameworks/sqreen_test.rb +26 -0
- data/lib/sqreen/instrumentation.rb +542 -0
- data/lib/sqreen/log.rb +119 -0
- data/lib/sqreen/metrics.rb +6 -0
- data/lib/sqreen/metrics/average.rb +39 -0
- data/lib/sqreen/metrics/base.rb +45 -0
- data/lib/sqreen/metrics/collect.rb +22 -0
- data/lib/sqreen/metrics/sum.rb +20 -0
- data/lib/sqreen/metrics_store.rb +96 -0
- data/lib/sqreen/middleware.rb +34 -0
- data/lib/sqreen/payload_creator.rb +137 -0
- data/lib/sqreen/performance_notifications.rb +86 -0
- data/lib/sqreen/performance_notifications/log.rb +36 -0
- data/lib/sqreen/performance_notifications/metrics.rb +36 -0
- data/lib/sqreen/performance_notifications/newrelic.rb +36 -0
- data/lib/sqreen/remote_command.rb +93 -0
- data/lib/sqreen/rule_attributes.rb +26 -0
- data/lib/sqreen/rule_callback.rb +108 -0
- data/lib/sqreen/rules.rb +126 -0
- data/lib/sqreen/rules_callbacks.rb +29 -0
- data/lib/sqreen/rules_callbacks/binding_accessor_matcher.rb +77 -0
- data/lib/sqreen/rules_callbacks/binding_accessor_metrics.rb +79 -0
- data/lib/sqreen/rules_callbacks/blacklist_ips.rb +44 -0
- data/lib/sqreen/rules_callbacks/count_http_codes.rb +40 -0
- data/lib/sqreen/rules_callbacks/crawler_user_agent_matches.rb +24 -0
- data/lib/sqreen/rules_callbacks/crawler_user_agent_matches_metrics.rb +24 -0
- data/lib/sqreen/rules_callbacks/custom_error.rb +64 -0
- data/lib/sqreen/rules_callbacks/execjs.rb +241 -0
- data/lib/sqreen/rules_callbacks/headers_insert.rb +22 -0
- data/lib/sqreen/rules_callbacks/inspect_rule.rb +25 -0
- data/lib/sqreen/rules_callbacks/matcher_rule.rb +138 -0
- data/lib/sqreen/rules_callbacks/rails_parameters.rb +14 -0
- data/lib/sqreen/rules_callbacks/record_request_context.rb +39 -0
- data/lib/sqreen/rules_callbacks/reflected_xss.rb +254 -0
- data/lib/sqreen/rules_callbacks/regexp_rule.rb +36 -0
- data/lib/sqreen/rules_callbacks/shell_env.rb +32 -0
- data/lib/sqreen/rules_callbacks/url_matches.rb +25 -0
- data/lib/sqreen/rules_callbacks/user_agent_matches.rb +22 -0
- data/lib/sqreen/rules_signature.rb +151 -0
- data/lib/sqreen/runner.rb +365 -0
- data/lib/sqreen/runtime_infos.rb +138 -0
- data/lib/sqreen/safe_json.rb +60 -0
- data/lib/sqreen/sdk.rb +22 -0
- data/lib/sqreen/serializer.rb +46 -0
- data/lib/sqreen/session.rb +317 -0
- data/lib/sqreen/shared_storage.rb +31 -0
- data/lib/sqreen/stats.rb +18 -0
- data/lib/sqreen/version.rb +5 -0
- metadata +148 -0
data/lib/sqreen/ca.crt
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
3
|
+
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
4
|
+
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
5
|
+
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
6
|
+
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
7
|
+
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
8
|
+
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
9
|
+
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
10
|
+
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
11
|
+
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
12
|
+
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
13
|
+
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
14
|
+
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
15
|
+
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
16
|
+
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
17
|
+
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
18
|
+
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
19
|
+
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
20
|
+
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
21
|
+
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
22
|
+
+OkuE6N36B9K
|
23
|
+
-----END CERTIFICATE-----
|
24
|
+
-----BEGIN CERTIFICATE-----
|
25
|
+
MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY
|
26
|
+
MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo
|
27
|
+
R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx
|
28
|
+
MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK
|
29
|
+
Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp
|
30
|
+
ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
|
31
|
+
AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9
|
32
|
+
AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA
|
33
|
+
ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0
|
34
|
+
7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W
|
35
|
+
kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI
|
36
|
+
mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G
|
37
|
+
A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ
|
38
|
+
KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1
|
39
|
+
6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl
|
40
|
+
4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K
|
41
|
+
oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj
|
42
|
+
UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU
|
43
|
+
AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk=
|
44
|
+
-----END CERTIFICATE-----
|
45
|
+
-----BEGIN CERTIFICATE-----
|
46
|
+
MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB
|
47
|
+
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
|
48
|
+
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
|
49
|
+
U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
|
50
|
+
ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
|
51
|
+
aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL
|
52
|
+
MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
|
53
|
+
ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln
|
54
|
+
biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
|
55
|
+
U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
|
56
|
+
aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1
|
57
|
+
nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex
|
58
|
+
t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz
|
59
|
+
SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG
|
60
|
+
BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+
|
61
|
+
rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/
|
62
|
+
NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
|
63
|
+
BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH
|
64
|
+
BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
|
65
|
+
aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv
|
66
|
+
MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE
|
67
|
+
p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y
|
68
|
+
5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK
|
69
|
+
WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ
|
70
|
+
4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N
|
71
|
+
hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
|
72
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
module Sqreen
|
5
|
+
# A module that will dynamically had call_counts to the pre/post/failing
|
6
|
+
# callbacks
|
7
|
+
module CallCountable
|
8
|
+
# Hook the necessary callback function
|
9
|
+
# The module being decorated is expected to have a
|
10
|
+
# record_observation & rulespack_id & rule_name method available (like RuleCallback)
|
11
|
+
#
|
12
|
+
# @param count [Hash] hash of callback names => count
|
13
|
+
def count_callback_calls(count)
|
14
|
+
base = self.class
|
15
|
+
@call_count_interval = 0
|
16
|
+
return if count.to_i == 0
|
17
|
+
@call_counts = {}
|
18
|
+
@call_count_interval = count
|
19
|
+
@call_count_names = {}
|
20
|
+
%w(pre post failing).each do |cb|
|
21
|
+
next unless base.method_defined?(cb)
|
22
|
+
@call_counts[cb] = 0
|
23
|
+
@call_count_names[cb] = "#{rulespack_id}/#{rule_name}/#{cb}".freeze
|
24
|
+
defd = base.instance_variable_defined?("@call_count_hooked_#{cb}")
|
25
|
+
next if defd && base.instance_variable_get("@call_count_hooked_#{cb}")
|
26
|
+
base.send(:alias_method, "#{cb}_without_count", cb)
|
27
|
+
base.send(:alias_method, cb, "#{cb}_with_count")
|
28
|
+
base.instance_variable_set("@call_count_hooked_#{cb}", true)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
PRE = 'pre'.freeze
|
32
|
+
POST = 'post'.freeze
|
33
|
+
FAILING = 'failing'.freeze
|
34
|
+
COUNT_CALLS = 'sqreen_call_counts'.freeze
|
35
|
+
|
36
|
+
def pre_with_count(inst, *args, &block)
|
37
|
+
ret = pre_without_count(inst, *args, &block)
|
38
|
+
count_calls('pre')
|
39
|
+
ret
|
40
|
+
end
|
41
|
+
|
42
|
+
def post_with_count(rv, inst, *args, &block)
|
43
|
+
ret = post_without_count(rv, inst, *args, &block)
|
44
|
+
count_calls('post')
|
45
|
+
ret
|
46
|
+
end
|
47
|
+
|
48
|
+
def failing_with_count(rv, inst, *args, &block)
|
49
|
+
ret = failing_without_count(rv, inst, *args, &block)
|
50
|
+
count_calls('failing')
|
51
|
+
ret
|
52
|
+
end
|
53
|
+
|
54
|
+
attr_reader :call_counts
|
55
|
+
attr_reader :call_count_interval
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
def count_calls(what)
|
60
|
+
return unless @call_count_interval > 0
|
61
|
+
new_value = (@call_counts[what] += 1)
|
62
|
+
return unless new_value % call_count_interval == 0
|
63
|
+
@call_counts[what] = 0
|
64
|
+
record_observation(COUNT_CALLS, @call_count_names[what], new_value)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
require 'sqreen/log'
|
5
|
+
|
6
|
+
module Sqreen
|
7
|
+
class CBTree
|
8
|
+
include Enumerable
|
9
|
+
# Callbacks tree:
|
10
|
+
# class
|
11
|
+
# methods
|
12
|
+
# position
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@by_class = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(cb)
|
19
|
+
@by_class[cb.klass] = {} unless @by_class[cb.klass]
|
20
|
+
|
21
|
+
cb_klass = @by_class[cb.klass]
|
22
|
+
unless cb_klass[cb.method]
|
23
|
+
cb_klass[cb.method] = { :pre => [], :post => [], :failing => [] }
|
24
|
+
end
|
25
|
+
|
26
|
+
methods = cb_klass[cb.method]
|
27
|
+
|
28
|
+
methods[:pre] << cb if cb.pre?
|
29
|
+
methods[:post] << cb if cb.post?
|
30
|
+
methods[:failing] << cb if cb.failing?
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove(cb)
|
34
|
+
types = @by_class[cb.klass][cb.method]
|
35
|
+
|
36
|
+
types[:pre].delete cb if cb.pre?
|
37
|
+
types[:post].delete cb if cb.post?
|
38
|
+
types[:failing].delete cb if cb.failing?
|
39
|
+
end
|
40
|
+
|
41
|
+
def get(klass, method, type = nil)
|
42
|
+
k = @by_class[klass]
|
43
|
+
unless k
|
44
|
+
log(format('Error: no cb registered for class %s (%s)', klass.inspect, klass.class))
|
45
|
+
log(inspect)
|
46
|
+
return []
|
47
|
+
end
|
48
|
+
cbs = k[method]
|
49
|
+
unless cbs
|
50
|
+
log(format('Error: no cbs registered for method %s.%s', klass, method))
|
51
|
+
log(inspect)
|
52
|
+
return []
|
53
|
+
end
|
54
|
+
|
55
|
+
if type.nil?
|
56
|
+
res = Set.new
|
57
|
+
cbs.values.each do |v|
|
58
|
+
res += v
|
59
|
+
end
|
60
|
+
return res.to_a
|
61
|
+
else
|
62
|
+
return cbs[type]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def each
|
67
|
+
@by_class.each do |_klass, values|
|
68
|
+
values.each do |_method, cbs|
|
69
|
+
cbs.values.each do |cb_ary|
|
70
|
+
cb_ary.each do |cb|
|
71
|
+
yield cb
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
require 'set'
|
5
|
+
require 'sqreen/shared_storage'
|
6
|
+
|
7
|
+
module Sqreen
|
8
|
+
|
9
|
+
class CB
|
10
|
+
# Callback class.
|
11
|
+
#
|
12
|
+
# Three methods can be defined:
|
13
|
+
# - pre(*args, &block)
|
14
|
+
# To be called prior to the hooked method.
|
15
|
+
# - post(return_value, *args, &block)
|
16
|
+
# To be called after the hooked method. The return_value argument is
|
17
|
+
# the value returned by the hooked method.
|
18
|
+
# - failing(exception, ...)
|
19
|
+
# To be called when the method raise
|
20
|
+
# The method pre, post and exception may return nil or a Hash.
|
21
|
+
# - nil: the original method is called and the callback has no further
|
22
|
+
# effect
|
23
|
+
# - { :status => :skip }: we skip the original method call
|
24
|
+
# - { :status => :raise}:
|
25
|
+
#
|
26
|
+
# - nil: the original return value is returned, as if coallback had no
|
27
|
+
# effect
|
28
|
+
# - { :status => :raise}:
|
29
|
+
# - { :status => :override }:
|
30
|
+
#
|
31
|
+
# - nil: reraise
|
32
|
+
# - { :status => :reraise }: reraise
|
33
|
+
# - { :status => :override }: eat exception
|
34
|
+
# - { :retry => :retry }: try the block again
|
35
|
+
#
|
36
|
+
# CB can also declare that they are whitelisted and should not be run at the moment.
|
37
|
+
|
38
|
+
attr_reader :klass, :method
|
39
|
+
|
40
|
+
def initialize(klass, method)
|
41
|
+
@method = method
|
42
|
+
@klass = klass
|
43
|
+
|
44
|
+
@has_pre = respond_to? :pre
|
45
|
+
@has_post = respond_to? :post
|
46
|
+
@has_failing = respond_to? :failing
|
47
|
+
|
48
|
+
raise(Sqreen::Exception, 'No callback provided') unless @has_pre || @has_post || @has_failing
|
49
|
+
end
|
50
|
+
|
51
|
+
def whitelisted?
|
52
|
+
false
|
53
|
+
end
|
54
|
+
|
55
|
+
def pre?
|
56
|
+
@has_pre
|
57
|
+
end
|
58
|
+
|
59
|
+
def post?
|
60
|
+
@has_post
|
61
|
+
end
|
62
|
+
|
63
|
+
def failing?
|
64
|
+
@has_failing
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
format('#<%s: %s.%s>', self.class, @klass, @method)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
# target_method, position, callback, callback class
|
72
|
+
|
73
|
+
class DefaultCB < CB
|
74
|
+
def pre(_inst, *args, &_block)
|
75
|
+
Sqreen.log.debug "<< #{@klass} #{@method} #{Thread.current}"
|
76
|
+
Sqreen.log.debug args.join ' '
|
77
|
+
# log params
|
78
|
+
end
|
79
|
+
|
80
|
+
def post(_rv, _inst, *_args, &_block)
|
81
|
+
# log "#{rv}"
|
82
|
+
Sqreen.log.debug ">> #{@klass} #{@method} #{Thread.current}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class RunWhenCalledCB < CB
|
87
|
+
def initialize(klass, method, &block)
|
88
|
+
super(klass, method)
|
89
|
+
|
90
|
+
raise 'missing block' unless block_given?
|
91
|
+
@block = block
|
92
|
+
end
|
93
|
+
|
94
|
+
def pre(_inst, *_args, &_block)
|
95
|
+
# FIXME: implement this removal
|
96
|
+
@remove_me = true
|
97
|
+
@block.call
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
module Sqreen
|
5
|
+
# A simple size limited queue.
|
6
|
+
# When trying to enqueue more than the capacity
|
7
|
+
# the older elements will get thrown
|
8
|
+
class CappedQueue < Queue
|
9
|
+
attr_reader :capacity
|
10
|
+
|
11
|
+
def initialize(capacity)
|
12
|
+
@capacity = capacity
|
13
|
+
super()
|
14
|
+
end
|
15
|
+
|
16
|
+
alias original_push push
|
17
|
+
|
18
|
+
def push(value)
|
19
|
+
pop until size < @capacity
|
20
|
+
original_push(value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,235 @@
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
3
|
+
|
4
|
+
require 'sqreen/binding_accessor'
|
5
|
+
require 'sqreen/exception'
|
6
|
+
|
7
|
+
module Sqreen
|
8
|
+
# Evaluate a condition, resolving literals using BindingAccessor.
|
9
|
+
#
|
10
|
+
# { "%and" => ["true", "true"] } -> true
|
11
|
+
# { "%or" => ["true", "false"] } -> true
|
12
|
+
# { "%and" => ["false", "true"] } -> false
|
13
|
+
#
|
14
|
+
# { "%equal" => ["coucou", "#.args[0]"] } -> "coucou" == args[0]
|
15
|
+
# { "%hash_val_include" => ["toto is a small guy", "#.request_params"] } ->
|
16
|
+
# true if one value of request params in included
|
17
|
+
# in the sentence 'toto is a small guy'.
|
18
|
+
#
|
19
|
+
# Combine expressions:
|
20
|
+
# { "%or" =>
|
21
|
+
# [
|
22
|
+
# { "%hash_val_include" => ["AAA", "#.request_params"] },
|
23
|
+
# { "%hash_val_include" => ["BBB", "#.request_params"] },
|
24
|
+
# ]
|
25
|
+
# }
|
26
|
+
# will return true if one of the request_params include either AAA or BBB.
|
27
|
+
#
|
28
|
+
class ConditionEvaluator
|
29
|
+
# Predicate: Is value deeply included in hash
|
30
|
+
# @params value [Object] object to find
|
31
|
+
# @params hash [Hash] Hash to search into
|
32
|
+
# @params min_value_size [Fixnum] to compare against
|
33
|
+
def self.hash_val_include?(value, hash, min_value_size, rem = 20)
|
34
|
+
return true if rem <= 0
|
35
|
+
vals = hash
|
36
|
+
vals = hash.values if hash.is_a?(Hash)
|
37
|
+
|
38
|
+
vals.any? do |hval|
|
39
|
+
case hval
|
40
|
+
when Hash, Array
|
41
|
+
ConditionEvaluator.hash_val_include?(value, hval,
|
42
|
+
min_value_size, rem - 1)
|
43
|
+
when NilClass
|
44
|
+
false
|
45
|
+
else
|
46
|
+
if hval.respond_to?(:empty?) && hval.empty?
|
47
|
+
false
|
48
|
+
else
|
49
|
+
v = hval.to_s
|
50
|
+
if v.size < min_value_size
|
51
|
+
false
|
52
|
+
else
|
53
|
+
ConditionEvaluator.str_include?(value.to_s, v)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Predicate: Is one of values deeply present in keys of hash
|
61
|
+
# @params value [Array] Array of objects to find
|
62
|
+
# @params hash [Hash] Hash to search into
|
63
|
+
# @params min_value_size [Fixnum] to compare against
|
64
|
+
def self.hash_key_include?(values, hash, min_value_size, rem = 10)
|
65
|
+
return true if rem <= 0
|
66
|
+
if hash.is_a?(Array)
|
67
|
+
return hash.any? do |v|
|
68
|
+
ConditionEvaluator.hash_key_include?(values, v, min_value_size, rem - 1)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
return false unless hash.is_a?(Hash)
|
73
|
+
|
74
|
+
hash.any? do |hkey, hval|
|
75
|
+
case hkey
|
76
|
+
when NilClass
|
77
|
+
false
|
78
|
+
else
|
79
|
+
if hkey.respond_to?(:empty?) && hkey.empty?
|
80
|
+
false
|
81
|
+
else
|
82
|
+
values.include?(hkey.to_s) || ConditionEvaluator.hash_key_include?(values, hval, min_value_size, rem - 1)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Test is a str contains what. Rencode if necessary
|
89
|
+
def self.str_include?(str, what)
|
90
|
+
str1 = if str.encoding != Encoding::UTF_8
|
91
|
+
str.encode(Encoding::UTF_8, :invalid => :replace,
|
92
|
+
:undef => :replace)
|
93
|
+
else
|
94
|
+
str
|
95
|
+
end
|
96
|
+
str2 = if what.encoding != Encoding::UTF_8
|
97
|
+
what.encode(Encoding::UTF_8, :invalid => :replace,
|
98
|
+
:undef => :replace)
|
99
|
+
else
|
100
|
+
what
|
101
|
+
end
|
102
|
+
str1.include?(str2)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Initialize evaluator
|
106
|
+
# @param cond [Hash] condition Hash
|
107
|
+
def initialize(cond)
|
108
|
+
unless cond == true || cond == false
|
109
|
+
unless cond.respond_to? :each
|
110
|
+
raise(Sqreen::Exception, "cond should be a Hash (was #{cond.class})")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
@raw = cond
|
114
|
+
@compiled = compile_expr(cond, 10)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Evaluate the condition
|
118
|
+
# @params *args: BindingAccessor evaluate arguments
|
119
|
+
def evaluate(*args)
|
120
|
+
evaluate_expr(@compiled, 10, *args)
|
121
|
+
end
|
122
|
+
|
123
|
+
protected
|
124
|
+
|
125
|
+
def compile_expr(exp, rem)
|
126
|
+
return exp if exp == true || exp == false
|
127
|
+
return true if exp.empty?
|
128
|
+
raise(Sqreen::Exception, 'too deep call detected') if rem <= 0
|
129
|
+
h = {}
|
130
|
+
exp.each do |op, values|
|
131
|
+
unless op.is_a? String
|
132
|
+
raise Sqreen::Exception, "op should be a String (was #{op.class})"
|
133
|
+
end
|
134
|
+
unless values.is_a?(Array)
|
135
|
+
raise Sqreen::Exception, "values should be an Array (was #{values.class})"
|
136
|
+
end
|
137
|
+
h[op] = values.map do |v|
|
138
|
+
case v
|
139
|
+
when Hash
|
140
|
+
compile_expr(v, rem - 1)
|
141
|
+
when 'true'
|
142
|
+
true
|
143
|
+
when 'false'
|
144
|
+
false
|
145
|
+
else
|
146
|
+
BindingAccessor.new(v.to_s)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
h
|
151
|
+
end
|
152
|
+
|
153
|
+
EQ_OPERATOR = '%equal'.freeze
|
154
|
+
NEQ_OPERATOR = '%not_equal'.freeze
|
155
|
+
GTE_OPERATOR = '%gte'.freeze
|
156
|
+
LTE_OPERATOR = '%lte'.freeze
|
157
|
+
GT_OPERATOR = '%gt'.freeze
|
158
|
+
LT_OPERATOR = '%lt'.freeze
|
159
|
+
HASH_INC_OPERATOR = '%hash_val_include'.freeze
|
160
|
+
HASH_KEY_OPERATOR = '%hash_key_include'.freeze
|
161
|
+
INC_OPERATOR = '%include'.freeze
|
162
|
+
OR_OPERATOR = '%or'.freeze
|
163
|
+
AND_OPERATOR = '%and'.freeze
|
164
|
+
|
165
|
+
OPERATORS_ARITY = {
|
166
|
+
HASH_INC_OPERATOR => 3,
|
167
|
+
HASH_KEY_OPERATOR => 3,
|
168
|
+
EQ_OPERATOR => 2,
|
169
|
+
NEQ_OPERATOR => 2,
|
170
|
+
INC_OPERATOR => 2,
|
171
|
+
GTE_OPERATOR => 2,
|
172
|
+
LTE_OPERATOR => 2,
|
173
|
+
GT_OPERATOR => 2,
|
174
|
+
LT_OPERATOR => 2,
|
175
|
+
}.freeze
|
176
|
+
|
177
|
+
def evaluate_expr(exp, rem, *args)
|
178
|
+
return exp if exp == true || exp == false
|
179
|
+
return true if exp.empty?
|
180
|
+
raise(Sqreen::Exception, 'too deep call detected') if rem <= 0
|
181
|
+
exp.all? do |op, values|
|
182
|
+
res = values.map do |v|
|
183
|
+
case v
|
184
|
+
when Hash
|
185
|
+
evaluate_expr(v, rem - 1, *args)
|
186
|
+
when true, false
|
187
|
+
v
|
188
|
+
else
|
189
|
+
v.resolve(*args)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
arity = OPERATORS_ARITY[op]
|
194
|
+
if !arity.nil? && res.size != arity
|
195
|
+
raise(Sqreen::Exception, "bad res #{res} (op #{op} wanted #{arity})")
|
196
|
+
end
|
197
|
+
bool = case op
|
198
|
+
when OR_OPERATOR
|
199
|
+
res.any?
|
200
|
+
when AND_OPERATOR
|
201
|
+
res.all?
|
202
|
+
when EQ_OPERATOR
|
203
|
+
res[0] == res[1]
|
204
|
+
when NEQ_OPERATOR
|
205
|
+
res[0] != res[1]
|
206
|
+
when GT_OPERATOR
|
207
|
+
res[0] > res[1]
|
208
|
+
when GTE_OPERATOR
|
209
|
+
res[0] >= res[1]
|
210
|
+
when LT_OPERATOR
|
211
|
+
res[0] < res[1]
|
212
|
+
when LTE_OPERATOR
|
213
|
+
res[0] <= res[1]
|
214
|
+
when INC_OPERATOR
|
215
|
+
unless res[0].respond_to?(:include?)
|
216
|
+
raise(Sqreen::Exception, "no include on res #{res[0].inspect}")
|
217
|
+
end
|
218
|
+
if res[0].is_a?(String)
|
219
|
+
ConditionEvaluator.str_include?(res[0], res[1])
|
220
|
+
else
|
221
|
+
res[0].include?(res[1])
|
222
|
+
end
|
223
|
+
when HASH_INC_OPERATOR
|
224
|
+
ConditionEvaluator.hash_val_include?(res[0], res[1], res[2])
|
225
|
+
when HASH_KEY_OPERATOR
|
226
|
+
ConditionEvaluator.hash_key_include?(res[0], res[1], res[2])
|
227
|
+
else
|
228
|
+
# FIXME: this should be check in compile
|
229
|
+
raise(Sqreen::Exception, "unknown op #{op})")
|
230
|
+
end
|
231
|
+
bool
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|