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.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +22 -0
  3. data/README.md +77 -0
  4. data/Rakefile +20 -0
  5. data/lib/sqreen-alt.rb +1 -0
  6. data/lib/sqreen.rb +68 -0
  7. data/lib/sqreen/attack_detected.html +2 -0
  8. data/lib/sqreen/binding_accessor.rb +288 -0
  9. data/lib/sqreen/ca.crt +72 -0
  10. data/lib/sqreen/call_countable.rb +67 -0
  11. data/lib/sqreen/callback_tree.rb +78 -0
  12. data/lib/sqreen/callbacks.rb +100 -0
  13. data/lib/sqreen/capped_queue.rb +23 -0
  14. data/lib/sqreen/condition_evaluator.rb +235 -0
  15. data/lib/sqreen/conditionable.rb +50 -0
  16. data/lib/sqreen/configuration.rb +168 -0
  17. data/lib/sqreen/context.rb +26 -0
  18. data/lib/sqreen/deliveries/batch.rb +84 -0
  19. data/lib/sqreen/deliveries/simple.rb +39 -0
  20. data/lib/sqreen/event.rb +16 -0
  21. data/lib/sqreen/events/attack.rb +61 -0
  22. data/lib/sqreen/events/remote_exception.rb +54 -0
  23. data/lib/sqreen/events/request_record.rb +62 -0
  24. data/lib/sqreen/exception.rb +34 -0
  25. data/lib/sqreen/frameworks.rb +40 -0
  26. data/lib/sqreen/frameworks/generic.rb +446 -0
  27. data/lib/sqreen/frameworks/rails.rb +148 -0
  28. data/lib/sqreen/frameworks/rails3.rb +36 -0
  29. data/lib/sqreen/frameworks/request_recorder.rb +69 -0
  30. data/lib/sqreen/frameworks/sinatra.rb +57 -0
  31. data/lib/sqreen/frameworks/sqreen_test.rb +26 -0
  32. data/lib/sqreen/instrumentation.rb +542 -0
  33. data/lib/sqreen/log.rb +119 -0
  34. data/lib/sqreen/metrics.rb +6 -0
  35. data/lib/sqreen/metrics/average.rb +39 -0
  36. data/lib/sqreen/metrics/base.rb +45 -0
  37. data/lib/sqreen/metrics/collect.rb +22 -0
  38. data/lib/sqreen/metrics/sum.rb +20 -0
  39. data/lib/sqreen/metrics_store.rb +96 -0
  40. data/lib/sqreen/middleware.rb +34 -0
  41. data/lib/sqreen/payload_creator.rb +137 -0
  42. data/lib/sqreen/performance_notifications.rb +86 -0
  43. data/lib/sqreen/performance_notifications/log.rb +36 -0
  44. data/lib/sqreen/performance_notifications/metrics.rb +36 -0
  45. data/lib/sqreen/performance_notifications/newrelic.rb +36 -0
  46. data/lib/sqreen/remote_command.rb +93 -0
  47. data/lib/sqreen/rule_attributes.rb +26 -0
  48. data/lib/sqreen/rule_callback.rb +108 -0
  49. data/lib/sqreen/rules.rb +126 -0
  50. data/lib/sqreen/rules_callbacks.rb +29 -0
  51. data/lib/sqreen/rules_callbacks/binding_accessor_matcher.rb +77 -0
  52. data/lib/sqreen/rules_callbacks/binding_accessor_metrics.rb +79 -0
  53. data/lib/sqreen/rules_callbacks/blacklist_ips.rb +44 -0
  54. data/lib/sqreen/rules_callbacks/count_http_codes.rb +40 -0
  55. data/lib/sqreen/rules_callbacks/crawler_user_agent_matches.rb +24 -0
  56. data/lib/sqreen/rules_callbacks/crawler_user_agent_matches_metrics.rb +24 -0
  57. data/lib/sqreen/rules_callbacks/custom_error.rb +64 -0
  58. data/lib/sqreen/rules_callbacks/execjs.rb +241 -0
  59. data/lib/sqreen/rules_callbacks/headers_insert.rb +22 -0
  60. data/lib/sqreen/rules_callbacks/inspect_rule.rb +25 -0
  61. data/lib/sqreen/rules_callbacks/matcher_rule.rb +138 -0
  62. data/lib/sqreen/rules_callbacks/rails_parameters.rb +14 -0
  63. data/lib/sqreen/rules_callbacks/record_request_context.rb +39 -0
  64. data/lib/sqreen/rules_callbacks/reflected_xss.rb +254 -0
  65. data/lib/sqreen/rules_callbacks/regexp_rule.rb +36 -0
  66. data/lib/sqreen/rules_callbacks/shell_env.rb +32 -0
  67. data/lib/sqreen/rules_callbacks/url_matches.rb +25 -0
  68. data/lib/sqreen/rules_callbacks/user_agent_matches.rb +22 -0
  69. data/lib/sqreen/rules_signature.rb +151 -0
  70. data/lib/sqreen/runner.rb +365 -0
  71. data/lib/sqreen/runtime_infos.rb +138 -0
  72. data/lib/sqreen/safe_json.rb +60 -0
  73. data/lib/sqreen/sdk.rb +22 -0
  74. data/lib/sqreen/serializer.rb +46 -0
  75. data/lib/sqreen/session.rb +317 -0
  76. data/lib/sqreen/shared_storage.rb +31 -0
  77. data/lib/sqreen/stats.rb +18 -0
  78. data/lib/sqreen/version.rb +5 -0
  79. metadata +148 -0
@@ -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