mixpanel 4.0.9 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -0
- data/lib/mixpanel/tracker.rb +72 -1
- data/mixpanel.gemspec +1 -1
- data/spec/mixpanel/tracker_spec.rb +18 -0
- metadata +2 -2
data/README.md
CHANGED
@@ -4,6 +4,8 @@ This Gem will not be maintained anymore, there is an Official gem being develope
|
|
4
4
|
|
5
5
|
We will merge PR for bugs for a little period of time, but no new features will be added.
|
6
6
|
|
7
|
+
# Upgrade to 4.1.0 to avoid XSS Vulnerability
|
8
|
+
|
7
9
|
[Official Gem repository](https://github.com/mixpanel/mixpanel-ruby)
|
8
10
|
|
9
11
|
## Table of Contents
|
data/lib/mixpanel/tracker.rb
CHANGED
@@ -35,7 +35,10 @@ module Mixpanel
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def append(type, *args)
|
38
|
-
|
38
|
+
js_args = args.collect do |arg|
|
39
|
+
escape_object_for_js(arg).to_json
|
40
|
+
end
|
41
|
+
queue << [type, js_args]
|
39
42
|
end
|
40
43
|
|
41
44
|
protected
|
@@ -86,5 +89,73 @@ module Mixpanel
|
|
86
89
|
0
|
87
90
|
end
|
88
91
|
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
# Recursively escape anything in a primitive, array, or hash, in
|
96
|
+
# preparation for jsonifying it
|
97
|
+
def escape_object_for_js(object, i = 0)
|
98
|
+
|
99
|
+
if object.kind_of? Hash
|
100
|
+
# Recursive case
|
101
|
+
Hash.new.tap do |h|
|
102
|
+
object.each do |k, v|
|
103
|
+
h[escape_object_for_js(k, i + 1)] = escape_object_for_js(v, i + 1)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
elsif object.kind_of? Enumerable
|
108
|
+
# Recursive case
|
109
|
+
object.map do |elt|
|
110
|
+
escape_object_for_js(elt, i + 1)
|
111
|
+
end
|
112
|
+
|
113
|
+
elsif object.respond_to? :iso8601
|
114
|
+
# Base case - safe object
|
115
|
+
object.iso8601
|
116
|
+
|
117
|
+
elsif object.kind_of?(Numeric)
|
118
|
+
# Base case - safe object
|
119
|
+
object
|
120
|
+
|
121
|
+
elsif [true, false, nil].member?(object)
|
122
|
+
# Base case - safe object
|
123
|
+
object
|
124
|
+
|
125
|
+
else
|
126
|
+
# Base case - use string sanitizer from ActiveSupport
|
127
|
+
escape_javascript(object.to_s)
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# All this code borrowed from rails/action_pack - ActionView::Helpers::JavascriptHelper
|
133
|
+
|
134
|
+
JS_ESCAPE_MAP = {
|
135
|
+
'\\' => '\\\\',
|
136
|
+
'</' => '<\/',
|
137
|
+
"\r\n" => '\n',
|
138
|
+
"\n" => '\n',
|
139
|
+
"\r" => '\n',
|
140
|
+
'"' => '\\"',
|
141
|
+
"'" => "\\'"
|
142
|
+
}
|
143
|
+
|
144
|
+
JS_ESCAPE_MAP["\342\200\250".force_encoding(Encoding::UTF_8).encode!] = '
'
|
145
|
+
JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] = '
'
|
146
|
+
|
147
|
+
# Escapes carriage returns and single and double quotes for JavaScript segments.
|
148
|
+
#
|
149
|
+
# Also available through the alias j(). This is particularly helpful in JavaScript
|
150
|
+
# responses, like:
|
151
|
+
#
|
152
|
+
# $('some_element').replaceWith('<%=j render 'some/element_template' %>');
|
153
|
+
def escape_javascript(javascript)
|
154
|
+
if javascript
|
155
|
+
javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] }
|
156
|
+
else
|
157
|
+
''
|
158
|
+
end
|
159
|
+
end
|
89
160
|
end
|
90
161
|
end
|
data/mixpanel.gemspec
CHANGED
@@ -2,7 +2,7 @@ files = ['README.md', 'LICENSE', 'Rakefile', 'mixpanel.gemspec', '{spec,lib}/**/
|
|
2
2
|
|
3
3
|
spec = Gem::Specification.new do |s|
|
4
4
|
s.name = "mixpanel"
|
5
|
-
s.version = "4.0
|
5
|
+
s.version = "4.1.0"
|
6
6
|
s.rubyforge_project = "mixpanel"
|
7
7
|
s.description = "Simple lib to track events in Mixpanel service. It can be used in any rack based framework."
|
8
8
|
s.author = "Alvaro Gil"
|
@@ -146,6 +146,24 @@ describe Mixpanel::Tracker do
|
|
146
146
|
mixpanel_queue_should_include(@mixpanel, "track", "Sign up", props)
|
147
147
|
end
|
148
148
|
|
149
|
+
it "should sanitize property values" do
|
150
|
+
@mixpanel.append_track("Sign up", {:referer => "</script><script>alert('XSS');</script>"})
|
151
|
+
@mixpanel.queue.size.should == 1
|
152
|
+
enqueued = @mixpanel.queue.first
|
153
|
+
properties_json = enqueued[1][1]
|
154
|
+
properties_json.should_not match(%r|</script>|)
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should be able to sanitize complex objects" do
|
158
|
+
properties = {'object' => ['foo', {2 => 1, 1 => ['bar', Time.now, nil, {'xss' => "</script><script>alert('XSS');</script>"}]}]}
|
159
|
+
@mixpanel.append_track("Sign up", properties)
|
160
|
+
@mixpanel.queue.size.should == 1
|
161
|
+
enqueued = @mixpanel.queue.first
|
162
|
+
properties_json = enqueued[1][1]
|
163
|
+
properties_json.should_not match(%r|</script>|)
|
164
|
+
end
|
165
|
+
|
166
|
+
|
149
167
|
it "should give direct access to queue" do
|
150
168
|
@mixpanel.append_track("Sign up", {:referer => 'http://example.com'})
|
151
169
|
@mixpanel.queue.size.should == 1
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mixpanel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0
|
4
|
+
version: 4.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-11-
|
12
|
+
date: 2013-11-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|