mixpanel 4.0.9 → 4.1.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.
- 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
|