sqreen 1.8.5-java → 1.9.0-java
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/lib/sqreen/attack_detected.html +1 -1
- data/lib/sqreen/callbacks.rb +1 -27
- data/lib/sqreen/context.rb +4 -0
- data/lib/sqreen/deliveries/batch.rb +14 -10
- data/lib/sqreen/deliveries/simple.rb +3 -0
- data/lib/sqreen/events/request_record.rb +54 -0
- data/lib/sqreen/frameworks.rb +1 -1
- data/lib/sqreen/frameworks/generic.rb +32 -12
- data/lib/sqreen/frameworks/request_recorder.rb +68 -0
- data/lib/sqreen/payload_creator.rb +15 -37
- data/lib/sqreen/rule_callback.rb +31 -40
- data/lib/sqreen/rules.rb +1 -0
- data/lib/sqreen/rules_callbacks/execjs.rb +6 -4
- data/lib/sqreen/sdk.rb +11 -3
- data/lib/sqreen/session.rb +8 -2
- data/lib/sqreen/shared_storage.rb +32 -0
- data/lib/sqreen/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c748ad9e30a231437548b0b1b4b51262e6bb0fee
|
|
4
|
+
data.tar.gz: 020cb5707e74f39af595716a22a21714191a7c13
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 51aca80eec0d387bec06f9e43d24bcd6f2d7b9195bd94df3f92d38bc66f2a76213509ac969d7a9e8fe28957edbdc2b3298399cf5f5267701ffaa15f769ce1ce1
|
|
7
|
+
data.tar.gz: 950a8bc12d01a28cdaa6a3d0533d116a3573ce6cec4ce778943a9b65cb5868ff2772fe57fdfc7bcf37e24b14c67a6a927e0f7ade57b7b445d8fe6c77f3fae58f
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Sqreen has detected an attack.</title> <style>html,body,div,span,h1,a{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}body{background
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Sqreen has detected an attack.</title> <style>html, body, div, span, h1, a{margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline}body{background: -webkit-radial-gradient(26% 19%, circle, #fff, #f4f7f9); background: radial-gradient(circle at 26% 19%, #fff, #f4f7f9); display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; -webkit-box-align: center; -ms-flex-align: center; align-items: center; -ms-flex-line-pack: center; align-content: center; width: 100%; min-height: 100vh; line-height: 1}svg, h1, p{display: block}svg{margin: 0 auto 4vh}h1{font-family: sans-serif; font-weight: 300; font-size: 34px; color: #384886; line-height: normal}p{font-size: 18px; line-height: normal; color: #b8bccc; font-family: sans-serif; font-weight: 300}a{color: #b8bccc}.flex{text-align: center}</style></head><body> <div class="flex"> <svg xmlns="http://www.w3.org/2000/svg" width="230" height="250" viewBox="0 0 230 250" enable-background="new 0 0 230 250"> <style>.st0{opacity: 0.4; filter: url(#a);}.st1{fill: #FFFFFF;}.st2{fill: #B0ACFF;}.st3{fill: #4842B7;}.st4{fill: #1E0936;}</style> <filter id="a" width="151.7%" height="146%" x="-25.8%" y="-16%" filterUnits="objectBoundingBox"> <feOffset dy="14" in="SourceAlpha" result="shadowOffsetOuter1"/> <feGaussianBlur in="shadowOffsetOuter1" result="shadowBlurOuter1" stdDeviation="13"/> <feColorMatrix in="shadowBlurOuter1" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.05 0"/> </filter> <g class="st0"> <path id="b_2_" d="M202.6 34.9c-.2-1.2-.8-2.1-1.9-2.8-3.8-2-37.9-20.1-85.7-20.1-48.8 0-84.2 19.3-85.7 20.1-1 .6-1.6 1.6-1.8 2.7-14.8 123.2 84.7 176.3 85.7 176.8.6.3 1.2.4 1.8.4.6 0 1.2-.1 1.7-.4 1-.5 100.4-55 85.9-176.7z"/> </g> <path id="b_1_" d="M202.6 34.9c-.2-1.2-.8-2.1-1.9-2.8-3.8-2-37.9-20.1-85.7-20.1-48.8 0-84.2 19.3-85.7 20.1-1 .6-1.6 1.6-1.8 2.7-14.8 123.2 84.7 176.3 85.7 176.8.6.3 1.2.4 1.8.4.6 0 1.2-.1 1.7-.4 1-.5 100.4-55 85.9-176.7z" class="st1"/> <g id="nest-cmyk-indigo"> <ellipse id="sqreen" cx="115.5" cy="69.9" class="st2" rx="12.7" ry="12.7"/> <path id="app" d="M113.6 91.9V71.5L95.5 61.1v18l6.4-3.7c.5 1.1 1 2.2 1.7 3.2L97 82.3l16.6 9.6zm3.7 0l16.6-9.6-6.7-3.9c.7-1 1.3-2 1.7-3.2l6.4 3.7v-18l-18.1 10.5v20.5zM96.9 57.6l18.6 10.7L134 57.6 117.3 48v7.6c-.6-.1-1.2-.1-1.8-.1-.6 0-1.2 0-1.8.1V48l-16.8 9.6zm20.2-13.9l20.3 11.7c1 .6 1.6 1.7 1.6 2.8v23.5c0 1.2-.6 2.2-1.6 2.8l-20.3 11.7c-1 .6-2.3.6-3.3 0L93.5 84.5c-1-.6-1.6-1.7-1.6-2.8V58.2c0-1.2.6-2.2 1.6-2.8l20.3-11.7c1-.6 2.3-.6 3.3 0z" class="st3"/> </g> <path id="s" d="M74.6 113c-1.8-1-3.5-1.5-5.2-1.5-1.4 0-2.3.6-2.3 1.5 0 2.7 10.1.4 10.1 7.7 0 3.3-2.9 6-7.6 6-2.1 0-4.7-.5-6.4-1.4l-.1-.1c-.3-.2-.3-.5-.2-.8l1.2-2.7c.1-.3.5-.5.9-.3.1 0 .1.1.2.1 1.5.6 3.1 1 4.6 1 2.2 0 2.9-.6 2.9-1.7 0-3-10.1-.8-10.1-7.7 0-3.1 2.7-5.8 7-5.8 2.1 0 5 .7 6.9 1.8.1 0 .1.1.2.1.3.2.4.5.3.8l-1.2 2.7c-.1.3-.5.5-.9.3h-.3z" class="st4"/> <path id="q" d="M93.6 107.8h3.2c.4 0 .7.3.7.7v25.9c0 .4-.3.7-.7.7h-3.2c-.4 0-.7-.3-.7-.7v-9.1c-1.2.8-2.9 1.4-4.7 1.4-5.4 0-9.6-4.3-9.6-9.7 0-5.4 4.1-9.7 9.6-9.7 1.8 0 3.5.6 4.7 1.4v-.1c0-.5.3-.8.7-.8zm-.7 12.4v-6.5c-1.3-1.3-2.8-2.1-4.5-2.1-2.9 0-5.1 2.3-5.1 5.4s2.2 5.4 5.1 5.4c1.7-.1 3.2-.7 4.5-2.2z" class="st4"/> <path id="r" d="M112.5 107.8c-1-.4-2-.6-3-.6-1.8 0-3.5.6-4.9 1.4v-.2c0-.3-.2-.5-.5-.5h-3.4c-.3 0-.5.2-.5.5v17.8c0 .3.2.5.5.5h3.4c.3 0 .5-.2.5-.5v-12.6c1.1-1.2 2.8-1.9 4.6-1.9.4 0 .9 0 1.5.2.3.1.6-.1.7-.4l1.3-2.9c.1-.4 0-.7-.2-.8z" class="st4"/> <path id="e" d="M129 124.7c-1.7 1-4.2 2-6.7 2-6 0-10.3-4.4-10.3-9.9 0-5.3 3.7-9.6 9.4-9.6 5.2 0 8.4 4.4 8.4 9 0 .4 0 .9-.1 1.2 0 .3-.3.6-.7.6h-12.5c.5 2.8 2.8 4.5 5.8 4.5 1.7 0 3.4-.5 5.1-1.4.3-.2.6-.1.8.2l1.2 2.6c.1.2 0 .4-.2.6-.2.1-.2.2-.2.2zm-12.4-10h8.5c-.2-1.8-1.9-3.3-3.9-3.3-2.5-.1-4 1.4-4.6 3.3z" class="st4"/> <path id="e_1_" d="M148.7 124.7c-1.7 1-4.2 2-6.7 2-6 0-10.3-4.4-10.3-9.9 0-5.3 3.7-9.6 9.4-9.6 5.2 0 8.4 4.4 8.4 9 0 .4 0 .9-.1 1.2 0 .3-.3.6-.7.6h-12.5c.5 2.8 2.8 4.5 5.8 4.5 1.7 0 3.4-.5 5.1-1.4.3-.2.6-.1.8.2l1.2 2.6c.1.2 0 .4-.2.6-.2.1-.2.2-.2.2zm-12.4-10h8.5c-.2-1.8-1.9-3.3-3.9-3.3-2.5-.1-4 1.4-4.6 3.3z" class="st4"/> <path id="n" d="M151.5 108.5V126c0 .4.3.7.7.7h3.2c.4 0 .7-.3.7-.7v-12.5c1.1-1.2 2.8-1.9 4.6-1.9 2.9 0 4.5 1.6 4.5 4.7v9.7c0 .4.3.7.7.7h3.2c.4 0 .7-.3.7-.7v-10.2c0-5.2-2.9-8.5-8.8-8.5-1.8 0-3.5.6-4.9 1.4v-.1c0-.4-.3-.7-.7-.7h-3.2c-.4-.1-.7.2-.7.6z" class="st4"/> </svg> <h1>Uh Oh! Sqreen has detected an attack.</h1> <p>If you are the application owner, check the Sqreen <a href="https://my.sqreen.io/">dashboard</a> for more information.</p></div></body></html>
|
|
2
2
|
|
data/lib/sqreen/callbacks.rb
CHANGED
|
@@ -2,35 +2,9 @@
|
|
|
2
2
|
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
|
3
3
|
|
|
4
4
|
require 'set'
|
|
5
|
+
require 'sqreen/shared_storage'
|
|
5
6
|
|
|
6
7
|
module Sqreen
|
|
7
|
-
module SharedStorage
|
|
8
|
-
@@shared = {}
|
|
9
|
-
|
|
10
|
-
def self::get(key, default = nil)
|
|
11
|
-
h = @@shared[Thread.current]
|
|
12
|
-
return h.fetch(key, default) if h
|
|
13
|
-
default
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def self::set(key, obj)
|
|
17
|
-
main_key = Thread.current
|
|
18
|
-
@@shared[main_key] = {} unless @@shared.key? main_key
|
|
19
|
-
@@shared[main_key][key] = obj
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def self.clear
|
|
23
|
-
@@shared.delete(Thread.current)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def self.inc(value)
|
|
27
|
-
set(value, get(value, 0) + 1)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def self.dec(value)
|
|
31
|
-
set(value, get(value, 0) - 1)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
8
|
|
|
35
9
|
class CB
|
|
36
10
|
# Callback class.
|
data/lib/sqreen/context.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Sqreen
|
|
|
8
8
|
# Simple delivery method that batch event already seen in a batch
|
|
9
9
|
class Batch < Simple
|
|
10
10
|
attr_accessor :max_batch, :max_staleness
|
|
11
|
-
attr_accessor :current_batch
|
|
11
|
+
attr_accessor :current_batch, :first_seen
|
|
12
12
|
|
|
13
13
|
def initialize(session,
|
|
14
14
|
max_batch,
|
|
@@ -40,18 +40,17 @@ module Sqreen
|
|
|
40
40
|
|
|
41
41
|
def stale?
|
|
42
42
|
min = @first_seen.values.min
|
|
43
|
+
return false if min.nil?
|
|
43
44
|
(min + max_staleness) < Time.now
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
def post_batch_needed?(event)
|
|
47
|
-
key = event_key(event)
|
|
48
|
-
was = @first_seen[key]
|
|
49
48
|
now = Time.now
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
49
|
+
event_keys(event).map do |key|
|
|
50
|
+
was = @first_seen[key]
|
|
51
|
+
@first_seen[key] ||= now
|
|
52
|
+
was.nil? || current_batch.size > max_batch || (was + max_staleness) < now
|
|
53
|
+
end.any?
|
|
55
54
|
end
|
|
56
55
|
|
|
57
56
|
def post_batch
|
|
@@ -67,12 +66,17 @@ module Sqreen
|
|
|
67
66
|
self.max_staleness += rand(@original_max_staleness / 10)
|
|
68
67
|
end
|
|
69
68
|
|
|
69
|
+
def event_keys(event)
|
|
70
|
+
return [event_key(event)] unless event.is_a?(Sqreen::RequestRecord)
|
|
71
|
+
event.observed.fetch(:attacks, []).map { |e| "att-#{e[:rule_name]}" } + event.observed.fetch(:sqreen_exceptions, []).map { |e| "rex-#{e[:exception].class}" }
|
|
72
|
+
end
|
|
73
|
+
|
|
70
74
|
def event_key(event)
|
|
71
75
|
case event
|
|
72
76
|
when Sqreen::Attack
|
|
73
|
-
|
|
77
|
+
"att-#{event.type}"
|
|
74
78
|
when Sqreen::RemoteException
|
|
75
|
-
|
|
79
|
+
"rex-#{event.klass}"
|
|
76
80
|
end
|
|
77
81
|
end
|
|
78
82
|
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
|
3
3
|
|
|
4
4
|
require 'sqreen/events/remote_exception'
|
|
5
|
+
require 'sqreen/events/request_record'
|
|
5
6
|
|
|
6
7
|
module Sqreen
|
|
7
8
|
module Deliveries
|
|
@@ -19,6 +20,8 @@ module Sqreen
|
|
|
19
20
|
session.post_attack(event)
|
|
20
21
|
when Sqreen::RemoteException
|
|
21
22
|
session.post_sqreen_exception(event)
|
|
23
|
+
when Sqreen::RequestRecord
|
|
24
|
+
session.post_request_record(event)
|
|
22
25
|
else
|
|
23
26
|
session.post_event(event)
|
|
24
27
|
end
|
|
@@ -0,0 +1,54 @@
|
|
|
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 'json'
|
|
5
|
+
require 'sqreen/event'
|
|
6
|
+
|
|
7
|
+
module Sqreen
|
|
8
|
+
# When a request is deeemed worthy of being sent to the backend
|
|
9
|
+
class RequestRecord < Sqreen::Event
|
|
10
|
+
def observed
|
|
11
|
+
(payload && payload[:observed]) || {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_hash
|
|
15
|
+
res = { :version => '20171208' }
|
|
16
|
+
if payload[:observed]
|
|
17
|
+
res[:observed] = payload[:observed]
|
|
18
|
+
rulespack = nil
|
|
19
|
+
observed.fetch(:attacks, []).each do |att|
|
|
20
|
+
rulespack = att.delete(:rulespack_id) || rulespack
|
|
21
|
+
end
|
|
22
|
+
observed.fetch(:sqreen_exceptions, []).each do |exc|
|
|
23
|
+
excp = exc.delete(:exception)
|
|
24
|
+
if excp
|
|
25
|
+
exc[:message] = excp.message
|
|
26
|
+
exc[:klass] = excp.class.name
|
|
27
|
+
end
|
|
28
|
+
rulespack = exc.delete(:rulespack_id) || rulespack
|
|
29
|
+
end
|
|
30
|
+
res[:rulespack_id] = rulespack unless rulespack.nil?
|
|
31
|
+
if observed[:observations]
|
|
32
|
+
res[:observed][:observations] = observed[:observations].map do |cat, key, value, time|
|
|
33
|
+
{ :category => cat, :key => key, :value => value, :time => time }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
if observed[:sdk]
|
|
37
|
+
payload[:observed][:sdk] = observed[:sdk].map do |meth, time, *args|
|
|
38
|
+
{ :name => meth, :time => time, :args => args }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
res[:local] = payload['local'] if payload['local']
|
|
43
|
+
if payload['request']
|
|
44
|
+
res[:request] = payload['request']
|
|
45
|
+
res[:client_ip] = res[:request].delete(:client_ip) if res[:request][:client_ip]
|
|
46
|
+
else
|
|
47
|
+
res[:request] = {}
|
|
48
|
+
end
|
|
49
|
+
res[:request][:parameters] = payload['params'] if payload['params']
|
|
50
|
+
res[:request][:headers] = payload['headers'] if payload['headers']
|
|
51
|
+
res
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/sqreen/frameworks.rb
CHANGED
|
@@ -6,11 +6,13 @@ require 'sqreen/events/remote_exception'
|
|
|
6
6
|
require 'sqreen/callbacks'
|
|
7
7
|
require 'sqreen/exception'
|
|
8
8
|
require 'sqreen/log'
|
|
9
|
+
require 'sqreen/frameworks/request_recorder'
|
|
9
10
|
|
|
10
11
|
module Sqreen
|
|
11
12
|
module Frameworks
|
|
12
13
|
# This is the base class for framework specific code
|
|
13
14
|
class GenericFramework
|
|
15
|
+
include RequestRecorder
|
|
14
16
|
attr_accessor :sqreen_configuration
|
|
15
17
|
|
|
16
18
|
def initialize
|
|
@@ -19,6 +21,7 @@ module Sqreen
|
|
|
19
21
|
else
|
|
20
22
|
to_app_done(Process.pid)
|
|
21
23
|
end
|
|
24
|
+
clean_request_record
|
|
22
25
|
end
|
|
23
26
|
|
|
24
27
|
# What kind of database is this
|
|
@@ -40,13 +43,16 @@ module Sqreen
|
|
|
40
43
|
ENV['RACK_ENV'] == 'development'
|
|
41
44
|
end
|
|
42
45
|
|
|
46
|
+
PREFFERED_IP_HEADERS = %w(HTTP_X_FORWARDED_FOR HTTP_X_REAL_IP
|
|
47
|
+
HTTP_CLIENT_IP HTTP_X_FORWARDED
|
|
48
|
+
HTTP_X_CLUSTER_CLIENT_IP HTTP_FORWARDED_FOR
|
|
49
|
+
HTTP_FORWARDED HTTP_VIA).freeze
|
|
50
|
+
|
|
43
51
|
def ip_headers
|
|
44
52
|
req = request
|
|
45
53
|
return [] unless req
|
|
46
54
|
ips = []
|
|
47
|
-
|
|
48
|
-
HTTP_X_CLUSTER_CLIENT_IP HTTP_FORWARDED_FOR HTTP_FORWARDED HTTP_VIA
|
|
49
|
-
REMOTE_ADDR).each do |header|
|
|
55
|
+
(PREFFERED_IP_HEADERS + ['REMOTE_ADDR']).each do |header|
|
|
50
56
|
v = req.env[header]
|
|
51
57
|
ips << [header, v] unless v.nil?
|
|
52
58
|
end
|
|
@@ -71,16 +77,22 @@ module Sqreen
|
|
|
71
77
|
req = request
|
|
72
78
|
return nil unless req
|
|
73
79
|
# Look for an external address being forwarded
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
split_ips = []
|
|
81
|
+
PREFFERED_IP_HEADERS.each do |header_name|
|
|
82
|
+
forwarded = req.env[header_name]
|
|
83
|
+
ips = split_ip_addresses(forwarded)
|
|
84
|
+
lip = ips.find { |ip| (ip !~ TRUSTED_PROXIES) && valid_ip?(ip) }
|
|
85
|
+
split_ips << ips unless ips.empty?
|
|
86
|
+
return lip unless lip.nil?
|
|
87
|
+
end
|
|
78
88
|
# Else fall back to declared remote addr
|
|
79
89
|
r = req.env['REMOTE_ADDR']
|
|
80
90
|
# If this is localhost get the last hop before
|
|
81
|
-
if
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
if r.nil? || r =~ LOCALHOST
|
|
92
|
+
split_ips.each do |ips|
|
|
93
|
+
lip = ips.find { |ip| (ip !~ LOCALHOST) && valid_ip?(ip) }
|
|
94
|
+
return lip unless lip.nil?
|
|
95
|
+
end
|
|
84
96
|
end
|
|
85
97
|
r
|
|
86
98
|
end
|
|
@@ -124,10 +136,11 @@ module Sqreen
|
|
|
124
136
|
:verb => req.env['REQUEST_METHOD'],
|
|
125
137
|
:host => hostname,
|
|
126
138
|
:port => req.env['SERVER_PORT'],
|
|
127
|
-
:rport => req.env['REMOTE_PORT'],
|
|
128
139
|
:referer => req.env['HTTP_REFERER'],
|
|
129
140
|
:path => request_path,
|
|
130
|
-
:
|
|
141
|
+
:remote_port => req.env['REMOTE_PORT'],
|
|
142
|
+
:remote_ip => remote_addr,
|
|
143
|
+
:client_ip => client_ip,
|
|
131
144
|
}
|
|
132
145
|
end
|
|
133
146
|
|
|
@@ -221,6 +234,8 @@ module Sqreen
|
|
|
221
234
|
# Cleanup request context
|
|
222
235
|
def clean_request
|
|
223
236
|
return unless SharedStorage.dec(:stored_requests) <= 0
|
|
237
|
+
payload_creator = Sqreen::PayloadCreator.new(self)
|
|
238
|
+
close_request_record(Sqreen.queue, Sqreen.observations_queue, payload_creator)
|
|
224
239
|
SharedStorage.set(:request, nil)
|
|
225
240
|
end
|
|
226
241
|
|
|
@@ -327,6 +342,11 @@ module Sqreen
|
|
|
327
342
|
nil
|
|
328
343
|
end
|
|
329
344
|
|
|
345
|
+
def remote_addr
|
|
346
|
+
return nil unless request
|
|
347
|
+
request.env['REMOTE_ADDR']
|
|
348
|
+
end
|
|
349
|
+
|
|
330
350
|
protected
|
|
331
351
|
|
|
332
352
|
# Is this a whitelisted path?
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
|
2
|
+
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
|
3
|
+
require 'set'
|
|
4
|
+
require 'sqreen/shared_storage'
|
|
5
|
+
require 'sqreen/events/request_record'
|
|
6
|
+
|
|
7
|
+
module Sqreen
|
|
8
|
+
# Store event/observations that happened in this request
|
|
9
|
+
module RequestRecorder
|
|
10
|
+
def observed_items
|
|
11
|
+
SharedStorage.get(:observed_items)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def observed_items=(value)
|
|
15
|
+
SharedStorage.set(:observed_items, value)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def payload_requests
|
|
19
|
+
SharedStorage.get(:payload_requests)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def payload_requests=(value)
|
|
23
|
+
SharedStorage.set(:payload_requests, value)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def only_metric_observation
|
|
27
|
+
SharedStorage.get(:only_metric_observation)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def only_metric_observation=(value)
|
|
31
|
+
SharedStorage.set(:only_metric_observation, value)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def clean_request_record
|
|
35
|
+
self.only_metric_observation = true
|
|
36
|
+
self.payload_requests = Set.new
|
|
37
|
+
self.observed_items = Hash.new { |hash, key| hash[key] = [] }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def observe(what, data, accessors = [], report = true)
|
|
41
|
+
clean_request_record if observed_items.nil?
|
|
42
|
+
self.only_metric_observation = false if report
|
|
43
|
+
observed_items[what] << data
|
|
44
|
+
payload_requests.merge(accessors)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def close_request_record(queue, observations_queue, payload_creator)
|
|
48
|
+
if only_metric_observation
|
|
49
|
+
push_metrics(observations_queue, queue)
|
|
50
|
+
return clean_request_record
|
|
51
|
+
end
|
|
52
|
+
payload = payload_creator.payload(payload_requests)
|
|
53
|
+
payload[:observed] = observed_items
|
|
54
|
+
queue.push RequestRecord.new(payload)
|
|
55
|
+
clean_request_record
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
protected
|
|
59
|
+
|
|
60
|
+
def push_metrics(observations_queue, event_queue)
|
|
61
|
+
observed_items[:observations].each do |obs|
|
|
62
|
+
observations_queue.push obs
|
|
63
|
+
end
|
|
64
|
+
return unless observations_queue.size > MAX_OBS_QUEUE_LENGTH / 2
|
|
65
|
+
event_queue.push Sqreen::METRICS_EVENT
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
|
2
2
|
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
|
3
3
|
|
|
4
|
-
require 'sqreen/context'
|
|
5
4
|
require 'sqreen/runtime_infos'
|
|
6
5
|
require 'sqreen/events/remote_exception'
|
|
7
6
|
|
|
@@ -15,8 +14,9 @@ module Sqreen
|
|
|
15
14
|
# The payload will always be outputed as a
|
|
16
15
|
# Hash of section => subsection.
|
|
17
16
|
class PayloadCreator
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
attr_reader :framework
|
|
18
|
+
def initialize(framework)
|
|
19
|
+
@framework = framework
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def query=(keys)
|
|
@@ -30,24 +30,25 @@ module Sqreen
|
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
def payload(
|
|
33
|
+
def payload(query)
|
|
34
|
+
self.query = query
|
|
34
35
|
ret = {}
|
|
35
36
|
METHODS.each_key do |section|
|
|
36
|
-
ret = fill(section, ret, framework
|
|
37
|
+
ret = fill(section, ret, @framework)
|
|
37
38
|
end
|
|
38
39
|
ret
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
protected
|
|
42
43
|
|
|
43
|
-
def fill(key, base, framework
|
|
44
|
+
def fill(key, base, framework)
|
|
44
45
|
subsection = @sections[key]
|
|
45
46
|
return base if subsection.nil?
|
|
46
47
|
if subsection == true
|
|
47
|
-
return base.merge!(key => full_section(key, framework
|
|
48
|
+
return base.merge!(key => full_section(key, framework))
|
|
48
49
|
end
|
|
49
50
|
return base if subsection.empty?
|
|
50
|
-
base[key] = fields(key, framework
|
|
51
|
+
base[key] = fields(key, framework)
|
|
51
52
|
base
|
|
52
53
|
end
|
|
53
54
|
|
|
@@ -72,33 +73,25 @@ module Sqreen
|
|
|
72
73
|
'cookies' => 'cookies_params',
|
|
73
74
|
'rails' => 'rails_params',
|
|
74
75
|
},
|
|
75
|
-
'rule' => {},
|
|
76
76
|
'headers' => {},
|
|
77
|
-
'context' => {
|
|
78
|
-
'backtrace' => 'get_current_backtrace',
|
|
79
|
-
},
|
|
80
77
|
}.freeze
|
|
81
78
|
|
|
82
|
-
def section_object(section, framework
|
|
79
|
+
def section_object(section, framework)
|
|
83
80
|
return RuntimeInfos if section == 'local'
|
|
84
|
-
return rule if section == 'rule'
|
|
85
|
-
return Context.new if section == 'context'
|
|
86
81
|
return HeaderSection.new(framework) if section == 'headers'
|
|
87
82
|
framework
|
|
88
83
|
end
|
|
89
84
|
|
|
90
|
-
def full_section(section, framework
|
|
91
|
-
return section_rule(framework, rule) if section == 'rule'
|
|
92
|
-
return section_context(framework, rule) if section == 'context'
|
|
85
|
+
def full_section(section, framework)
|
|
93
86
|
# fast path prevent initializing a HeaderSection
|
|
94
87
|
return framework.ip_headers if section == 'headers'
|
|
95
|
-
so = section_object(section, framework
|
|
88
|
+
so = section_object(section, framework)
|
|
96
89
|
so.send(FULL_SECTIONS[section])
|
|
97
90
|
end
|
|
98
91
|
|
|
99
|
-
def fields(section, framework
|
|
92
|
+
def fields(section, framework)
|
|
100
93
|
out = {}
|
|
101
|
-
object = section_object(section, framework
|
|
94
|
+
object = section_object(section, framework)
|
|
102
95
|
remove = []
|
|
103
96
|
@sections[section].each do |key|
|
|
104
97
|
meth = METHODS[section][key]
|
|
@@ -119,21 +112,6 @@ module Sqreen
|
|
|
119
112
|
Sqreen::RemoteException.record(e)
|
|
120
113
|
end
|
|
121
114
|
|
|
122
|
-
def section_context(framework, rule)
|
|
123
|
-
obj = section_object('context', framework, rule)
|
|
124
|
-
{
|
|
125
|
-
'backtrace' => obj.get_current_backtrace,
|
|
126
|
-
}
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def section_rule(_framework, rule)
|
|
130
|
-
{
|
|
131
|
-
'name' => rule['name'],
|
|
132
|
-
'rulespack_id' => rule['rulespack_id'],
|
|
133
|
-
'test' => rule['test'],
|
|
134
|
-
}
|
|
135
|
-
end
|
|
136
|
-
|
|
137
115
|
# object that default to call on framework header
|
|
138
116
|
class HeaderSection
|
|
139
117
|
def initialize(framework)
|
|
@@ -141,7 +119,7 @@ module Sqreen
|
|
|
141
119
|
end
|
|
142
120
|
|
|
143
121
|
def [](value)
|
|
144
|
-
if %w
|
|
122
|
+
if %w[rack_client_ip rails_client_ip ip_headers].include?(value)
|
|
145
123
|
return @framework.send(value)
|
|
146
124
|
end
|
|
147
125
|
@framework.header(value)
|
data/lib/sqreen/rule_callback.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
|
3
3
|
|
|
4
4
|
require 'sqreen/callbacks'
|
|
5
|
+
require 'sqreen/context'
|
|
5
6
|
require 'sqreen/conditionable'
|
|
6
7
|
require 'sqreen/call_countable'
|
|
7
8
|
require 'sqreen/rule_attributes'
|
|
@@ -18,8 +19,9 @@ module Sqreen
|
|
|
18
19
|
include CallCountable
|
|
19
20
|
# If nothing was asked by the rule we will ask for all sections available
|
|
20
21
|
# These information will be pruned later when exporting in #to_hash
|
|
21
|
-
DEFAULT_PAYLOAD = PayloadCreator::METHODS.keys.freeze
|
|
22
|
+
DEFAULT_PAYLOAD = (PayloadCreator::METHODS.keys - ['local'] + ['context']).freeze
|
|
22
23
|
attr_reader :test
|
|
24
|
+
attr_reader :payload_tpl
|
|
23
25
|
attr_reader :block
|
|
24
26
|
attr_accessor :framework
|
|
25
27
|
|
|
@@ -32,8 +34,7 @@ module Sqreen
|
|
|
32
34
|
@test = rule_hash[Attrs::TEST] == true
|
|
33
35
|
@data = rule_hash[Attrs::DATA]
|
|
34
36
|
@rule = rule_hash
|
|
35
|
-
payload_tpl = @rule[Attrs::PAYLOAD] || DEFAULT_PAYLOAD
|
|
36
|
-
@payload_generator = PayloadCreator.new(payload_tpl)
|
|
37
|
+
@payload_tpl = @rule[Attrs::PAYLOAD] || DEFAULT_PAYLOAD
|
|
37
38
|
condition_callbacks(@rule[Attrs::CONDITIONS])
|
|
38
39
|
count_callback_calls(@rule[Attrs::CALL_COUNT_INTERVAL])
|
|
39
40
|
end
|
|
@@ -61,10 +62,19 @@ module Sqreen
|
|
|
61
62
|
|
|
62
63
|
# Record an attack event into Sqreen system
|
|
63
64
|
# @param infos [Hash] Additional information about request
|
|
64
|
-
def record_event(infos)
|
|
65
|
-
|
|
66
|
-
payload
|
|
67
|
-
|
|
65
|
+
def record_event(infos, at = Time.now.utc)
|
|
66
|
+
return unless framework
|
|
67
|
+
payload = {
|
|
68
|
+
:infos => infos,
|
|
69
|
+
:rulespack_id => rulespack_id,
|
|
70
|
+
:rule_name => rule_name,
|
|
71
|
+
:test => test,
|
|
72
|
+
:time => at,
|
|
73
|
+
}
|
|
74
|
+
if payload_tpl.include?('context')
|
|
75
|
+
payload[:backtrace] = Sqreen::Context.new.bt
|
|
76
|
+
end
|
|
77
|
+
framework.observe(:attacks, payload, payload_tpl)
|
|
68
78
|
end
|
|
69
79
|
|
|
70
80
|
# Record a metric observation
|
|
@@ -73,44 +83,25 @@ module Sqreen
|
|
|
73
83
|
# @param observation [Object] data observed
|
|
74
84
|
# @param at [Time] time when observation was made
|
|
75
85
|
def record_observation(category, key, observation, at = Time.now.utc)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
Sqreen.queue.push Sqreen::METRICS_EVENT
|
|
79
|
-
end
|
|
86
|
+
return unless framework
|
|
87
|
+
framework.observe(:observations, [category, key, observation, at], [], false)
|
|
80
88
|
end
|
|
81
89
|
|
|
82
90
|
# Record an exception that just occurred
|
|
83
91
|
# @param exception [Exception] Exception to send over
|
|
84
92
|
# @param infos [Hash] Additional contextual information
|
|
85
|
-
def record_exception(exception, infos = {})
|
|
86
|
-
|
|
87
|
-
payload
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
rescue => e
|
|
98
|
-
Sqreen.log.debug("No framework request params #{e}")
|
|
99
|
-
end
|
|
100
|
-
payload['time'] = Time.now.utc
|
|
101
|
-
payload['infos'] = infos
|
|
102
|
-
payload['backtrace'] = Sqreen::Context.new.bt
|
|
103
|
-
begin
|
|
104
|
-
payload['client_ip'] = framework.client_ip.to_s
|
|
105
|
-
rescue => e
|
|
106
|
-
Sqreen.log.debug("No framework client_ip #{e}")
|
|
107
|
-
end
|
|
108
|
-
begin
|
|
109
|
-
payload['headers'] = framework.ip_headers
|
|
110
|
-
rescue => e
|
|
111
|
-
Sqreen.log.debug("No framework ip_headers #{e}")
|
|
112
|
-
end
|
|
113
|
-
RemoteException.record(payload)
|
|
93
|
+
def record_exception(exception, infos = {}, at = Time.now.utc)
|
|
94
|
+
return unless framework
|
|
95
|
+
payload = {
|
|
96
|
+
:exception => exception,
|
|
97
|
+
:infos => infos,
|
|
98
|
+
:rulespack_id => rulespack_id,
|
|
99
|
+
:rule_name => rule_name,
|
|
100
|
+
:test => test,
|
|
101
|
+
:time => at,
|
|
102
|
+
:backtrace => exception.backtrace || Sqreen::Context.bt,
|
|
103
|
+
}
|
|
104
|
+
framework.observe(:sqreen_exceptions, payload)
|
|
114
105
|
end
|
|
115
106
|
end
|
|
116
107
|
end
|
data/lib/sqreen/rules.rb
CHANGED
|
@@ -19,6 +19,7 @@ module Sqreen
|
|
|
19
19
|
module Rules
|
|
20
20
|
# Exec js callbacks
|
|
21
21
|
class ExecJSCB < RuleCB
|
|
22
|
+
attr_accessor :restrict_max_depth
|
|
22
23
|
def initialize(klass, method, rule_hash)
|
|
23
24
|
super(klass, method, rule_hash)
|
|
24
25
|
callbacks = @rule[Attrs::CALLBACKS]
|
|
@@ -32,6 +33,7 @@ module Sqreen
|
|
|
32
33
|
|
|
33
34
|
build_runnable(callbacks)
|
|
34
35
|
@compiled = ExecJS.compile(@source)
|
|
36
|
+
@restrict_max_depth = 20
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
def pre?
|
|
@@ -97,12 +99,11 @@ module Sqreen
|
|
|
97
99
|
insert.reverse.each do |wh, ikey, ival|
|
|
98
100
|
case wh
|
|
99
101
|
when Array
|
|
100
|
-
wh << ival unless ival.empty?
|
|
102
|
+
wh << ival unless ival.respond_to?(:empty?) && ival.empty?
|
|
101
103
|
else
|
|
102
|
-
wh[ikey] = ival unless ival.empty?
|
|
104
|
+
wh[ikey] = ival unless ival.respond_to?(:empty?) && ival.empty?
|
|
103
105
|
end
|
|
104
106
|
end
|
|
105
|
-
|
|
106
107
|
new_obj
|
|
107
108
|
end
|
|
108
109
|
|
|
@@ -184,7 +185,8 @@ module Sqreen
|
|
|
184
185
|
arguments[haystack_idx] = ExecJSCB.hash_val_included(
|
|
185
186
|
arguments[needed_idx],
|
|
186
187
|
arguments[haystack_idx],
|
|
187
|
-
min_length.to_i
|
|
188
|
+
min_length.to_i,
|
|
189
|
+
@restrict_max_depth
|
|
188
190
|
)
|
|
189
191
|
end
|
|
190
192
|
|
data/lib/sqreen/sdk.rb
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
# Copyright (c) 2015 Sqreen. All Rights Reserved.
|
|
2
2
|
# Please refer to our terms for more information: https://www.sqreen.io/terms.html
|
|
3
3
|
|
|
4
|
+
# Sqreen Namespace
|
|
4
5
|
module Sqreen
|
|
5
6
|
# Sqreen SDK
|
|
6
7
|
class << self
|
|
7
8
|
# Authentication tracking method
|
|
8
|
-
def auth_track(is_logged_in, authentication_keys)
|
|
9
|
-
|
|
9
|
+
def auth_track(is_logged_in, authentication_keys); end
|
|
10
|
+
|
|
11
|
+
def signup_track(authentication_keys); end
|
|
10
12
|
|
|
11
|
-
def
|
|
13
|
+
def identify(authentication_keys, traits = {})
|
|
14
|
+
return unless Sqreen.framework
|
|
15
|
+
Sqreen.framework.observe(
|
|
16
|
+
:sdk,
|
|
17
|
+
[:identify, Time.now, authentication_keys, traits],
|
|
18
|
+
[], false
|
|
19
|
+
)
|
|
12
20
|
end
|
|
13
21
|
end
|
|
14
22
|
end
|
data/lib/sqreen/session.rb
CHANGED
|
@@ -5,6 +5,8 @@ require 'sqreen/log'
|
|
|
5
5
|
require 'sqreen/serializer'
|
|
6
6
|
require 'sqreen/runtime_infos'
|
|
7
7
|
require 'sqreen/events/remote_exception'
|
|
8
|
+
require 'sqreen/events/attack'
|
|
9
|
+
require 'sqreen/events/request_record'
|
|
8
10
|
require 'sqreen/exception'
|
|
9
11
|
require 'sqreen/safe_json'
|
|
10
12
|
|
|
@@ -159,8 +161,7 @@ module Sqreen
|
|
|
159
161
|
connect unless connected?
|
|
160
162
|
headers['X-Session-Key'] = @session_id if @session_id
|
|
161
163
|
headers['X-Sqreen-Time'] = Time.now.utc.to_f.to_s
|
|
162
|
-
headers['
|
|
163
|
-
headers['User-Agent'] = "Ruby/#{Sqreen::VERSION}"
|
|
164
|
+
headers['User-Agent'] = "sqreen-ruby/#{Sqreen::VERSION}"
|
|
164
165
|
headers['X-Sqreen-Beta'] = format('pid=%d;tid=%s;nb=%d;t=%f',
|
|
165
166
|
Process.pid,
|
|
166
167
|
thread_id,
|
|
@@ -263,6 +264,10 @@ module Sqreen
|
|
|
263
264
|
'dependencies' => dependencies)
|
|
264
265
|
end
|
|
265
266
|
|
|
267
|
+
def post_request_record(request_record)
|
|
268
|
+
resilient_post('request_record', request_record.to_hash)
|
|
269
|
+
end
|
|
270
|
+
|
|
266
271
|
# Post an exception to Sqreen for analysis
|
|
267
272
|
# @param exception [RemoteException] Exception and context to be sent over
|
|
268
273
|
def post_sqreen_exception(exception)
|
|
@@ -305,6 +310,7 @@ module Sqreen
|
|
|
305
310
|
case event
|
|
306
311
|
when Sqreen::RemoteException then 'sqreen_exception'
|
|
307
312
|
when Sqreen::Attack then 'attack'
|
|
313
|
+
when Sqreen::RequestRecord then 'request_record'
|
|
308
314
|
end
|
|
309
315
|
end
|
|
310
316
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
module SharedStorage
|
|
6
|
+
@@shared = {}
|
|
7
|
+
|
|
8
|
+
def self::get(key, default = nil)
|
|
9
|
+
h = @@shared[Thread.current]
|
|
10
|
+
return h.fetch(key, default) if h
|
|
11
|
+
default
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self::set(key, obj)
|
|
15
|
+
main_key = Thread.current
|
|
16
|
+
@@shared[main_key] = {} unless @@shared.key? main_key
|
|
17
|
+
@@shared[main_key][key] = obj
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.clear
|
|
21
|
+
@@shared.delete(Thread.current)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.inc(value)
|
|
25
|
+
set(value, get(value, 0) + 1)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.dec(value)
|
|
29
|
+
set(value, get(value, 0) - 1)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/sqreen/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sqreen
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.9.0
|
|
5
5
|
platform: java
|
|
6
6
|
authors:
|
|
7
7
|
- Sqreen
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2018-01-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: execjs
|
|
@@ -64,11 +64,13 @@ files:
|
|
|
64
64
|
- lib/sqreen/event.rb
|
|
65
65
|
- lib/sqreen/events/attack.rb
|
|
66
66
|
- lib/sqreen/events/remote_exception.rb
|
|
67
|
+
- lib/sqreen/events/request_record.rb
|
|
67
68
|
- lib/sqreen/exception.rb
|
|
68
69
|
- lib/sqreen/frameworks.rb
|
|
69
70
|
- lib/sqreen/frameworks/generic.rb
|
|
70
71
|
- lib/sqreen/frameworks/rails.rb
|
|
71
72
|
- lib/sqreen/frameworks/rails3.rb
|
|
73
|
+
- lib/sqreen/frameworks/request_recorder.rb
|
|
72
74
|
- lib/sqreen/frameworks/sinatra.rb
|
|
73
75
|
- lib/sqreen/frameworks/sqreen_test.rb
|
|
74
76
|
- lib/sqreen/instrumentation.rb
|
|
@@ -115,6 +117,7 @@ files:
|
|
|
115
117
|
- lib/sqreen/sdk.rb
|
|
116
118
|
- lib/sqreen/serializer.rb
|
|
117
119
|
- lib/sqreen/session.rb
|
|
120
|
+
- lib/sqreen/shared_storage.rb
|
|
118
121
|
- lib/sqreen/stats.rb
|
|
119
122
|
- lib/sqreen/version.rb
|
|
120
123
|
homepage: https://www.sqreen.io/
|