vidibus-xss 0.1.14 → 0.1.15
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/vidibus/xss/extensions/controller.rb +24 -13
- data/public/javascripts/vidibus.js +51 -0
- data/public/javascripts/vidibus.loader.js +75 -70
- data/public/javascripts/vidibus.xss.js +87 -105
- data/vidibus-xss.gemspec +52 -27
- metadata +147 -16
- data/.gitignore +0 -21
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.15
|
@@ -8,7 +8,7 @@ module Vidibus
|
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
10
|
included do
|
11
|
-
helper_method :url_for, :xss_request?, :fullpath_url
|
11
|
+
helper_method :url_for, :xss_request?, :fullpath_url, :render_xss_string, :extract_xss
|
12
12
|
respond_to :html, :xss
|
13
13
|
rescue_from ActionController::RoutingError, :with => :rescue_404
|
14
14
|
end
|
@@ -140,13 +140,13 @@ module Vidibus
|
|
140
140
|
resources
|
141
141
|
end
|
142
142
|
|
143
|
-
#
|
143
|
+
# Extracts XSS hash of resources and content from given content string.
|
144
144
|
# If html content is given, the method tries to extract title,
|
145
145
|
# stylesheets and javascripts from head and content from body.
|
146
146
|
# TODO: Allow script blocks! Add them to body?
|
147
147
|
# TODO: Allow style blocks?
|
148
148
|
# TODO: Check for html content
|
149
|
-
def
|
149
|
+
def extract_xss(content)
|
150
150
|
dom = Nokogiri::HTML(content)
|
151
151
|
{
|
152
152
|
:resources => extract_xss_javascripts(dom) + extract_xss_stylesheets(dom),
|
@@ -183,9 +183,18 @@ module Vidibus
|
|
183
183
|
render_xss(:callback => data)
|
184
184
|
end
|
185
185
|
|
186
|
+
# Controller method for rendering xss content.
|
187
|
+
def render_xss(options = {})
|
188
|
+
xss = render_xss_string(options)
|
189
|
+
xss_access_control_headers
|
190
|
+
self.content_type = Mime::XSS
|
191
|
+
self.status = 200 # force success status
|
192
|
+
self.response_body = xss
|
193
|
+
end
|
194
|
+
|
186
195
|
# Main method for rendering XSS.
|
187
196
|
# Renders given XSS resources and content to string and sets it as response_body.
|
188
|
-
def
|
197
|
+
def render_xss_string(options = {})
|
189
198
|
resources = options.delete(:resources)
|
190
199
|
content = options.delete(:content)
|
191
200
|
path = options.delete(:get)
|
@@ -210,7 +219,7 @@ module Vidibus
|
|
210
219
|
|
211
220
|
# render load invocations of XSS resources
|
212
221
|
if resources and resources.any?
|
213
|
-
xss << %(
|
222
|
+
xss << %(xssLoader.load(#{resources.to_json},'#{params[:scope]}');)
|
214
223
|
defer = true
|
215
224
|
end
|
216
225
|
|
@@ -232,13 +241,9 @@ module Vidibus
|
|
232
241
|
# wait until resources have been loaded, before rendering XSS content
|
233
242
|
if defer
|
234
243
|
function_name = "rx#{xss_random_string}"
|
235
|
-
xss_content = %(var #{function_name}=function(){if(
|
244
|
+
xss_content = %(var #{function_name}=function(){if(xssLoader.complete){#{xss_content}}else{window.setTimeout('#{function_name}()',100);}};#{function_name}();)
|
236
245
|
end
|
237
246
|
xss << xss_content
|
238
|
-
xss_access_control_headers
|
239
|
-
self.content_type = Mime::XSS
|
240
|
-
self.status = 200 # force success status
|
241
|
-
self.response_body = xss
|
242
247
|
end
|
243
248
|
|
244
249
|
# Generates random string for current cycle.
|
@@ -305,15 +310,21 @@ module Vidibus
|
|
305
310
|
|
306
311
|
# embed xss.get
|
307
312
|
if path = options[:path]
|
308
|
-
|
309
|
-
|
313
|
+
template = options[:template]
|
314
|
+
if template === false
|
315
|
+
xss = {}
|
316
|
+
else
|
317
|
+
template ||= "layouts/#{get_layout(:xss)}"
|
318
|
+
content = render_to_string(:template => template)
|
319
|
+
xss = extract_xss(content)
|
320
|
+
end
|
310
321
|
xss[:get] = "/#{path}"
|
311
322
|
xss.delete(:content) # Ensure that not content will be embedded!
|
312
323
|
|
313
324
|
# embed xss content
|
314
325
|
else
|
315
326
|
content = render_to_string(*args, &block)
|
316
|
-
xss =
|
327
|
+
xss = extract_xss(content)
|
317
328
|
end
|
318
329
|
|
319
330
|
render_xss(xss)
|
@@ -29,3 +29,54 @@ jQuery(function ($) {
|
|
29
29
|
vidibus.csrf.data[vidibus.csrf.param] = encodeURIComponent(vidibus.csrf.token);
|
30
30
|
}
|
31
31
|
});
|
32
|
+
|
33
|
+
|
34
|
+
/**
|
35
|
+
* Implement ajax handler.
|
36
|
+
* This is the default handler provided in rails.js with some extensions.
|
37
|
+
*/
|
38
|
+
$(function($) {
|
39
|
+
$.fn.extend({
|
40
|
+
|
41
|
+
/**
|
42
|
+
* Handles execution of remote calls firing overridable events along the way.
|
43
|
+
*/
|
44
|
+
callAjax: function(url, method, data) {
|
45
|
+
var el = this,
|
46
|
+
method = method || el.attr('method') || el.attr('data-method') || 'GET',
|
47
|
+
dataType = el.attr('data-type') || 'script';
|
48
|
+
if (!url) url = el.attr('action') || el.attr('href') || el.attr('data-url');
|
49
|
+
if (url === undefined) {
|
50
|
+
throw "No URL specified for remote call (action or href must be present).";
|
51
|
+
} else {
|
52
|
+
if (el.triggerAndReturn('ajax:before')) {
|
53
|
+
data = data || el.is('form') ? el.serializeArray() : {};
|
54
|
+
if (method == 'delete') {
|
55
|
+
data['_method'] = method;
|
56
|
+
method = 'POST';
|
57
|
+
}
|
58
|
+
$.extend(data, vidibus.csrf.data());
|
59
|
+
$.ajax({
|
60
|
+
url: url,
|
61
|
+
data: data,
|
62
|
+
dataType: dataType,
|
63
|
+
type: method.toUpperCase(),
|
64
|
+
beforeSend: function(xhr) {
|
65
|
+
el.trigger('ajax:loading', xhr);
|
66
|
+
},
|
67
|
+
success: function(data, status, xhr) {
|
68
|
+
el.trigger('ajax:success', [data, status, xhr]);
|
69
|
+
},
|
70
|
+
complete: function(xhr) {
|
71
|
+
el.trigger('ajax:complete', xhr);
|
72
|
+
},
|
73
|
+
error: function(xhr, status, error) {
|
74
|
+
el.trigger('ajax:failure', [xhr, status, error]);
|
75
|
+
}
|
76
|
+
});
|
77
|
+
}
|
78
|
+
el.trigger('ajax:after');
|
79
|
+
}
|
80
|
+
}
|
81
|
+
});
|
82
|
+
});
|
@@ -1,13 +1,13 @@
|
|
1
1
|
// Basic loader for stylesheets and javascripts.
|
2
|
-
|
3
|
-
|
2
|
+
var xssLoader = {
|
3
|
+
|
4
4
|
complete: true, // indicates that loading has been finished
|
5
5
|
queue: [], // holds resources that are queued to load
|
6
6
|
loading: undefined, // holds resource that is currently being loaded
|
7
7
|
preloaded: undefined, // holds resources that are included in consumer base file
|
8
8
|
loaded: {}, // holds resources that are currently loaded
|
9
9
|
unused: {}, // holds resources that are loaded, but not required anymore
|
10
|
-
|
10
|
+
|
11
11
|
/**
|
12
12
|
* Load resources.
|
13
13
|
*/
|
@@ -19,98 +19,98 @@ vidibus.loader = {
|
|
19
19
|
$(resources).each(function() {
|
20
20
|
var resource = this,
|
21
21
|
src = resource.src,
|
22
|
-
name =
|
23
|
-
|
22
|
+
name = xssLoader.resourceName(src);
|
23
|
+
|
24
24
|
resource.name = name;
|
25
|
-
resource.scopes = {}
|
25
|
+
resource.scopes = {};
|
26
26
|
resource.scopes[scope] = true;
|
27
|
-
|
27
|
+
|
28
28
|
// remove current file, because it is used
|
29
|
-
delete
|
29
|
+
delete xssLoader.unused[name];
|
30
30
|
|
31
31
|
// skip files that have already been loaded
|
32
|
-
if (
|
33
|
-
|
32
|
+
if (xssLoader.loaded[name]) {
|
33
|
+
xssLoader.loaded[name].scopes[scope] = true; // add current scope
|
34
34
|
return; // continue
|
35
|
-
} else if (
|
35
|
+
} else if (xssLoader.preloaded[name]) {
|
36
36
|
return; // continue
|
37
37
|
}
|
38
|
-
|
39
|
-
|
38
|
+
|
39
|
+
xssLoader.loaded[name] = resource;
|
40
40
|
switch (resource.type) {
|
41
|
-
|
41
|
+
|
42
42
|
// load css file directly
|
43
43
|
case 'text/css':
|
44
|
-
var element = document.createElement("link");
|
45
|
-
element.rel = 'stylesheet';
|
46
|
-
element.href = src;
|
44
|
+
var element = document.createElement("link");
|
45
|
+
element.rel = 'stylesheet';
|
46
|
+
element.href = src;
|
47
47
|
element.media = resource.media || 'all';
|
48
48
|
element.type = 'text/css';
|
49
|
-
|
49
|
+
xssLoader.appendToHead(element);
|
50
50
|
break;
|
51
|
-
|
51
|
+
|
52
52
|
// push script file to loading queue
|
53
53
|
case 'text/javascript':
|
54
|
-
|
54
|
+
xssLoader.queue.push(resource);
|
55
55
|
break;
|
56
|
-
|
57
|
-
default: console.log('
|
56
|
+
|
57
|
+
default: console.log('xssLoader.load: unsupported resource type: '+resource.type);
|
58
58
|
}
|
59
59
|
});
|
60
|
-
|
60
|
+
|
61
61
|
this.loadQueue(true);
|
62
62
|
this.unloadUnused(scope);
|
63
63
|
},
|
64
|
-
|
64
|
+
|
65
65
|
/**
|
66
66
|
* Returns file name of resource.
|
67
67
|
*/
|
68
68
|
resourceName: function(url) {
|
69
69
|
return url.match(/\/([^\/\?]+)(\?.*)*$/)[1];
|
70
70
|
},
|
71
|
-
|
71
|
+
|
72
72
|
/**
|
73
73
|
* Returns list of static resources.
|
74
74
|
*/
|
75
75
|
initStaticResources: function() {
|
76
|
-
if (
|
77
|
-
|
76
|
+
if (xssLoader.preloaded === undefined) {
|
77
|
+
xssLoader.preloaded = {};
|
78
78
|
var $resource, src, name;
|
79
79
|
$('script[src],link[href]',$('head')).each(function() {
|
80
80
|
$resource = $(this);
|
81
81
|
src = $resource.attr('src') || $resource.attr('href');
|
82
|
-
name =
|
83
|
-
|
82
|
+
name = xssLoader.resourceName(src);
|
83
|
+
xssLoader.preloaded[name] = src;
|
84
84
|
});
|
85
85
|
}
|
86
86
|
},
|
87
|
-
|
87
|
+
|
88
88
|
/**
|
89
89
|
* Loads resources in queue.
|
90
90
|
*/
|
91
91
|
loadQueue: function(start) {
|
92
|
-
|
92
|
+
|
93
93
|
// Reduce queue if this method is called as callback.
|
94
|
-
if(start
|
95
|
-
|
94
|
+
if(start !== true) {
|
95
|
+
xssLoader.queue.shift();
|
96
96
|
}
|
97
|
-
|
98
|
-
var resource =
|
99
|
-
|
97
|
+
|
98
|
+
var resource = xssLoader.queue[0];
|
99
|
+
|
100
100
|
// return if file is currently loading
|
101
101
|
if (resource) {
|
102
|
-
if (resource
|
102
|
+
if (resource === xssLoader.loading) {
|
103
103
|
// console.log('CURRENTLY LOADING: '+resource.src);
|
104
104
|
return;
|
105
105
|
}
|
106
|
-
|
107
|
-
|
106
|
+
xssLoader.loading = resource;
|
107
|
+
xssLoader.loadScript(resource.src, xssLoader.loadQueue);
|
108
108
|
} else {
|
109
|
-
|
110
|
-
|
109
|
+
xssLoader.loading = undefined;
|
110
|
+
xssLoader.complete = true;
|
111
111
|
}
|
112
112
|
},
|
113
|
-
|
113
|
+
|
114
114
|
/**
|
115
115
|
* Loads script src.
|
116
116
|
*/
|
@@ -121,66 +121,71 @@ vidibus.loader = {
|
|
121
121
|
} else {
|
122
122
|
// IE
|
123
123
|
element.onreadystatechange = function() {
|
124
|
-
if (this.readyState
|
125
|
-
|
124
|
+
if (this.readyState === 'loaded') {
|
125
|
+
callback.call(this);
|
126
|
+
}
|
127
|
+
};
|
126
128
|
}
|
127
129
|
element.type = 'text/javascript';
|
128
130
|
element.src = src;
|
129
|
-
|
131
|
+
xssLoader.appendToHead(element);
|
130
132
|
element = null;
|
131
133
|
},
|
132
|
-
|
134
|
+
|
133
135
|
/**
|
134
136
|
* Detects unused resources and removes them.
|
135
137
|
*/
|
136
138
|
unloadUnused: function(scope) {
|
137
139
|
var name, resources = [];
|
138
|
-
for(name in
|
140
|
+
for(name in xssLoader.unused) {
|
141
|
+
if (xssLoader.unused.hasOwnProperty(name)) {
|
142
|
+
// Remove dependency for given scope.
|
143
|
+
if (xssLoader.unused[name].scopes[scope]) {
|
144
|
+
delete xssLoader.unused[name].scopes[scope];
|
145
|
+
}
|
139
146
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
// Unload resource if it has no dependencies left.
|
146
|
-
if ($.isEmptyObject(vidibus.loader.unused[name].scopes)) {
|
147
|
-
resources.push(vidibus.loader.unused[name]);
|
147
|
+
// Unload resource if it has no dependencies left.
|
148
|
+
if ($.isEmptyObject(xssLoader.unused[name].scopes)) {
|
149
|
+
resources.push(xssLoader.unused[name]);
|
150
|
+
}
|
148
151
|
}
|
149
152
|
}
|
150
|
-
|
151
|
-
|
153
|
+
xssLoader.unload(resources);
|
154
|
+
xssLoader.unused = {};
|
152
155
|
},
|
153
|
-
|
156
|
+
|
154
157
|
/**
|
155
158
|
* Removes resources given in list.
|
156
159
|
*/
|
157
160
|
unload: function(resources) {
|
158
|
-
var src,
|
161
|
+
var src, resource;
|
159
162
|
$(resources).each(function() {
|
160
163
|
resource = this;
|
161
164
|
src = resource.src;
|
162
|
-
|
163
|
-
// console.log('REMOVE UNUSED RESOURCE: '+src);
|
164
|
-
|
165
165
|
switch (resource.type) {
|
166
|
-
case "text/css":
|
166
|
+
case "text/css":
|
167
167
|
$('link[href="'+src+'"]').remove();
|
168
168
|
break;
|
169
|
-
|
170
|
-
case "text/javascript":
|
169
|
+
|
170
|
+
case "text/javascript":
|
171
171
|
$('script[src="'+src+'"]').remove();
|
172
172
|
break;
|
173
|
-
|
174
|
-
default: console.log('
|
173
|
+
|
174
|
+
default: console.log('xssLoader.unload: unsupported resource type: '+resource.type);
|
175
175
|
}
|
176
|
-
delete
|
176
|
+
delete xssLoader.loaded[resource.name];
|
177
177
|
});
|
178
178
|
},
|
179
|
-
|
179
|
+
|
180
180
|
/**
|
181
181
|
* Appends given element to document head.
|
182
182
|
*/
|
183
183
|
appendToHead: function(element) {
|
184
|
-
|
184
|
+
$("head")[0].appendChild(element);
|
185
185
|
}
|
186
186
|
};
|
187
|
+
|
188
|
+
// Maintain compatibility
|
189
|
+
if (typeof vidibus !== "undefined") {
|
190
|
+
vidibus.loader = xssLoader;
|
191
|
+
}
|
@@ -18,9 +18,14 @@ vidibus.xss = {
|
|
18
18
|
initialized: {}, // holds true for every scope that has been initialized
|
19
19
|
fileExtension: 'xss', // use 'xss' as file extension
|
20
20
|
loadedUrls: {}, // store urls currently loaded in each scope
|
21
|
+
pathPrefix: '', // set a fixed prefix for all paths
|
22
|
+
|
23
|
+
ready: function() {
|
24
|
+
// TODO: implement real event handler
|
25
|
+
},
|
21
26
|
|
22
27
|
/**
|
23
|
-
* Detects scope of script block to be executed.
|
28
|
+
* Detects scope of script block to be executed.
|
24
29
|
* Must be called from embedding page.
|
25
30
|
*/
|
26
31
|
detectScope: function() {
|
@@ -30,50 +35,60 @@ vidibus.xss = {
|
|
30
35
|
$detector.remove();
|
31
36
|
return $scope;
|
32
37
|
},
|
33
|
-
|
38
|
+
|
34
39
|
/**
|
35
40
|
* Usage:
|
36
|
-
* vidibus.xss.embed('<div>Some HTML</div>', $('#scope'),
|
41
|
+
* vidibus.xss.embed('<div>Some HTML</div>', $('#scope') [, ".some element"]);
|
37
42
|
*/
|
38
|
-
embed: function(html, $scope,
|
43
|
+
embed: function(html, $scope, selector) {
|
39
44
|
html = this.transformPaths(html, $scope); // Transform local paths before embedding html into page!
|
40
|
-
|
45
|
+
if (selector) {
|
46
|
+
$(selector, $scope).html(html);
|
47
|
+
} else {
|
48
|
+
$scope.html(html);
|
49
|
+
}
|
41
50
|
this.setUrls($scope);
|
42
51
|
this.setActions($scope);
|
52
|
+
this.ready();
|
43
53
|
},
|
44
|
-
|
54
|
+
|
45
55
|
/**
|
46
56
|
* Calls given path for given scope. If a third parameter is set to true,
|
47
57
|
* the location will be loaded, even if it is currently loaded.
|
48
|
-
*
|
58
|
+
*
|
49
59
|
* Usage:
|
50
60
|
* vidibus.xss.get('path', $('#scope') [, true]);
|
51
|
-
*
|
61
|
+
*
|
52
62
|
* If no host is provided, host of scope will be used.
|
53
63
|
*/
|
54
64
|
get: function(path, $scope, reload) {
|
55
|
-
//
|
65
|
+
// escape query parts
|
56
66
|
path = path.replace("?", "%3F").replace("&", "%26").replace("=", "%3D");
|
57
67
|
|
68
|
+
// remove path prefix
|
69
|
+
if (typeof xssPathPrefix !== "undefined" && xssPathPrefix) {
|
70
|
+
path = path.replace(xssPathPrefix,'');
|
71
|
+
}
|
72
|
+
|
58
73
|
var scopeId = $scope[0].id,
|
59
74
|
params = scopeId+'='+this.getPath(path),
|
60
75
|
keepCurrentLocation = this.initialized[scopeId] ? 0 : 1,
|
61
76
|
location = $.param.fragment(String(document.location), params, keepCurrentLocation),
|
62
77
|
reloadScope = {};
|
63
|
-
|
78
|
+
|
64
79
|
this.initialized[scopeId] = true;
|
65
80
|
window.location.href = location; // write history
|
66
81
|
reloadScope[scopeId] = reload;
|
67
82
|
this.loadUrl(location, reloadScope);
|
68
83
|
},
|
69
|
-
|
84
|
+
|
70
85
|
/**
|
71
86
|
* Redirect to given path while forcing reloading.
|
72
87
|
*/
|
73
88
|
redirect: function(path, $scope) {
|
74
|
-
this.get(path, $scope, true)
|
89
|
+
this.get(path, $scope, true);
|
75
90
|
},
|
76
|
-
|
91
|
+
|
77
92
|
/**
|
78
93
|
* Handles callback action.
|
79
94
|
*
|
@@ -83,41 +98,41 @@ vidibus.xss = {
|
|
83
98
|
* Accepts actions depending on status:
|
84
99
|
* (redirect) to: Performs redirect to location.
|
85
100
|
*/
|
86
|
-
callback: function(data, $scope) {
|
87
|
-
if (data.status
|
101
|
+
callback: function(data, $scope) {
|
102
|
+
if (data.status === "redirect") {
|
88
103
|
this.redirect(data.to, $scope);
|
89
104
|
}
|
90
105
|
},
|
91
|
-
|
106
|
+
|
92
107
|
/**
|
93
108
|
* Sets host for given scope.
|
94
109
|
*/
|
95
110
|
setHost: function(host, $scope) {
|
96
111
|
$scope.attr('data-host', host);
|
97
112
|
},
|
98
|
-
|
113
|
+
|
99
114
|
/**
|
100
115
|
* Returns host for given scope.
|
101
116
|
*/
|
102
117
|
getHost: function($scope) {
|
103
118
|
return $scope.attr('data-host');
|
104
119
|
},
|
105
|
-
|
120
|
+
|
106
121
|
/**
|
107
122
|
* Turns given path into an absolute url.
|
108
123
|
*/
|
109
124
|
getUrl: function(path, host) {
|
110
|
-
if (path.match(/https?:\/\//)) { return path }
|
125
|
+
if (path.match(/https?:\/\//)) { return path; }
|
111
126
|
return host + path;
|
112
127
|
},
|
113
|
-
|
128
|
+
|
114
129
|
/**
|
115
130
|
* Returns relative path from url.
|
116
131
|
*/
|
117
132
|
getPath: function(url) {
|
118
|
-
return url.replace(/https?:\/\/[^\/]+/,'')
|
133
|
+
return url.replace(/https?:\/\/[^\/]+/,'');
|
119
134
|
},
|
120
|
-
|
135
|
+
|
121
136
|
/**
|
122
137
|
* Rewrites links to absolute urls.
|
123
138
|
*/
|
@@ -129,20 +144,20 @@ vidibus.xss = {
|
|
129
144
|
var href = $(this).attr('href');
|
130
145
|
$(this).attr('href', vidibus.xss.getUrl(href, host));
|
131
146
|
});
|
132
|
-
|
147
|
+
|
133
148
|
// Rewrite forms
|
134
149
|
$('form[action]', $scope).each(function(e) {
|
135
150
|
var action = $(this).attr('action');
|
136
151
|
$(this).attr('href', vidibus.xss.getUrl(action, host));
|
137
152
|
});
|
138
153
|
},
|
139
|
-
|
154
|
+
|
140
155
|
/**
|
141
156
|
* Set xss actions for interactive elements.
|
142
157
|
*/
|
143
158
|
setActions: function($scope) {
|
144
159
|
var host = this.getHost($scope);
|
145
|
-
|
160
|
+
|
146
161
|
// Set action for GET links
|
147
162
|
// TODO: Allow links to be flagged as "external"
|
148
163
|
$('a[href^='+host+']:not([data-method],[data-remote])', $scope).bind('click.xss', function(e) {
|
@@ -150,7 +165,7 @@ vidibus.xss = {
|
|
150
165
|
vidibus.xss.get(href, $scope);
|
151
166
|
e.preventDefault();
|
152
167
|
});
|
153
|
-
|
168
|
+
|
154
169
|
// Set action non-GET links
|
155
170
|
// TODO: Remove bindings from links that match current host only: a[data-method][href^='+host+']:not([data-remote])
|
156
171
|
$('a[data-method]:not([data-remote])').die('click').unbind('click'); // remove bindings
|
@@ -161,9 +176,9 @@ vidibus.xss = {
|
|
161
176
|
$(this).callAjax(url);
|
162
177
|
e.preventDefault();
|
163
178
|
});
|
164
|
-
|
179
|
+
|
165
180
|
// Set form action
|
166
|
-
$('form[action]').die('submit').unbind('submit') // remove bindings
|
181
|
+
$('form[action]').die('submit').unbind('submit'); // remove bindings
|
167
182
|
$('form[action][href^='+host+']', $scope).submit(function(e) {
|
168
183
|
var $form = $(this),
|
169
184
|
path = $form.attr('action');
|
@@ -172,7 +187,7 @@ vidibus.xss = {
|
|
172
187
|
return false;
|
173
188
|
});
|
174
189
|
},
|
175
|
-
|
190
|
+
|
176
191
|
/**
|
177
192
|
* Modifies paths within given html string.
|
178
193
|
* This method must be called before embedding html snippet into the page
|
@@ -180,13 +195,17 @@ vidibus.xss = {
|
|
180
195
|
*/
|
181
196
|
transformPaths: function(html, $scope) {
|
182
197
|
var match, url;
|
183
|
-
while (
|
198
|
+
while (true) {
|
199
|
+
match = html.match(/src="((?!http)[^"]+)"/);
|
200
|
+
if (!match) {
|
201
|
+
break;
|
202
|
+
}
|
184
203
|
url = vidibus.xss.buildUrl(match[1], $scope);
|
185
|
-
html = html.replace(match[0], 'src="'+url+'"')
|
204
|
+
html = html.replace(match[0], 'src="'+url+'"');
|
186
205
|
}
|
187
206
|
return html;
|
188
207
|
},
|
189
|
-
|
208
|
+
|
190
209
|
/**
|
191
210
|
* Load XSS sources from given url.
|
192
211
|
* If url is empty, the current location will be used.
|
@@ -194,24 +213,26 @@ vidibus.xss = {
|
|
194
213
|
loadUrl: function(url, reload) {
|
195
214
|
var scope, $scope, path, loaded, params = $.deparam.fragment();
|
196
215
|
for(scope in params) {
|
197
|
-
|
216
|
+
if (params.hasOwnProperty(scope)) {
|
217
|
+
path = params[scope];
|
198
218
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
219
|
+
// don't reload locations that have already been loaded
|
220
|
+
loaded = this.loadedUrls[scope];
|
221
|
+
if((!reload || !reload[scope]) && loaded && loaded === path) {
|
222
|
+
continue;
|
223
|
+
}
|
204
224
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
225
|
+
$scope = $('#'+scope);
|
226
|
+
if($scope[0] === undefined) {
|
227
|
+
console.log('Scope not found: '+scope);
|
228
|
+
} else {
|
229
|
+
this.loadedUrls[scope] = path;
|
230
|
+
this.loadData(path, $scope);
|
231
|
+
}
|
211
232
|
}
|
212
233
|
}
|
213
234
|
},
|
214
|
-
|
235
|
+
|
215
236
|
/**
|
216
237
|
* Load relative path into given scope.
|
217
238
|
* Transforms scope host and path into a XSS location.
|
@@ -225,7 +246,7 @@ vidibus.xss = {
|
|
225
246
|
type: 'GET'
|
226
247
|
});
|
227
248
|
},
|
228
|
-
|
249
|
+
|
229
250
|
/**
|
230
251
|
* Tranforms local paths to absolute ones.
|
231
252
|
*/
|
@@ -236,27 +257,32 @@ vidibus.xss = {
|
|
236
257
|
if(params) {
|
237
258
|
params = params.split("&");
|
238
259
|
} else {
|
239
|
-
params = []
|
260
|
+
params = [];
|
261
|
+
}
|
262
|
+
|
263
|
+
// prepend path prefix
|
264
|
+
if (typeof xssPathPrefix !== "undefined" && xssPathPrefix && !path.match(xssPathPrefix)) {
|
265
|
+
path = xssPathPrefix + path;
|
240
266
|
}
|
241
267
|
|
242
268
|
var host = this.getHost($scope),
|
243
269
|
scope = $scope.attr('id'),
|
244
|
-
url = this.getUrl(path, host)
|
270
|
+
url = this.getUrl(path, host);
|
245
271
|
|
246
|
-
if (!url.match(/\.[a-z]+((\?|\#).*)?$/)) url += '.xss';
|
247
|
-
if (url.indexOf('.xss') > -1 && params.toString().indexOf('scope='+scope)
|
272
|
+
if (!url.match(/\.[a-z]+((\?|\#).*)?$/)) { url += '.xss'; }
|
273
|
+
if (url.indexOf('.xss') > -1 && params.toString().indexOf('scope='+scope) === -1) {
|
248
274
|
params.push('scope='+scope);
|
249
275
|
}
|
250
|
-
|
276
|
+
|
251
277
|
// add cache buster
|
252
278
|
if (uncached) {
|
253
279
|
var d = new Date();
|
254
280
|
params.push(d.getTime());
|
255
281
|
}
|
256
|
-
|
282
|
+
|
257
283
|
// append params
|
258
284
|
if (params.length) {
|
259
|
-
if (url.indexOf('?')
|
285
|
+
if (url.indexOf('?') === -1) { url += '?'; }
|
260
286
|
url += params.join('&');
|
261
287
|
}
|
262
288
|
|
@@ -266,13 +292,13 @@ vidibus.xss = {
|
|
266
292
|
|
267
293
|
/**
|
268
294
|
* Detect changes to document's location.
|
269
|
-
*
|
295
|
+
*
|
270
296
|
*/
|
271
297
|
$(function($){
|
272
|
-
|
298
|
+
|
273
299
|
// Detect changes of document.location and trigger loading.
|
274
300
|
$(window).bind('hashchange', function(e) {
|
275
|
-
if (
|
301
|
+
if (xssLoader.complete) {
|
276
302
|
vidibus.xss.loadUrl();
|
277
303
|
}
|
278
304
|
return false;
|
@@ -285,54 +311,10 @@ $(function($){
|
|
285
311
|
});
|
286
312
|
|
287
313
|
/**
|
288
|
-
*
|
289
|
-
* This is the default handler provided in rails.js extended to accept delete method.
|
314
|
+
* Extend XHR
|
290
315
|
*/
|
291
316
|
$(function($) {
|
292
|
-
|
293
|
-
|
294
|
-
/**
|
295
|
-
* Handles execution of remote calls firing overridable events along the way.
|
296
|
-
*/
|
297
|
-
callAjax: function(url) {
|
298
|
-
var el = this,
|
299
|
-
method = el.attr('method') || el.attr('data-method') || 'GET',
|
300
|
-
dataType = el.attr('data-type') || 'script';
|
301
|
-
if (!url) url = el.attr('action') || el.attr('href');
|
302
|
-
if (url === undefined) {
|
303
|
-
throw "No URL specified for remote call (action or href must be present).";
|
304
|
-
} else {
|
305
|
-
if (el.triggerAndReturn('ajax:before')) {
|
306
|
-
var data = el.is('form') ? el.serializeArray() : {};
|
307
|
-
if (method == 'delete') {
|
308
|
-
data['_method'] = method;
|
309
|
-
method = 'POST';
|
310
|
-
}
|
311
|
-
$.extend(data, vidibus.csrf.data());
|
312
|
-
$.ajax({
|
313
|
-
url: url,
|
314
|
-
data: data,
|
315
|
-
dataType: dataType,
|
316
|
-
type: method.toUpperCase(),
|
317
|
-
beforeSend: function(xhr) {
|
318
|
-
el.trigger('ajax:loading', xhr);
|
319
|
-
},
|
320
|
-
success: function(data, status, xhr) {
|
321
|
-
el.trigger('ajax:success', [data, status, xhr]);
|
322
|
-
},
|
323
|
-
complete: function(xhr) {
|
324
|
-
el.trigger('ajax:complete', xhr);
|
325
|
-
},
|
326
|
-
error: function(xhr, status, error) {
|
327
|
-
el.trigger('ajax:failure', [xhr, status, error]);
|
328
|
-
}
|
329
|
-
});
|
330
|
-
}
|
331
|
-
el.trigger('ajax:after');
|
332
|
-
}
|
333
|
-
}
|
334
|
-
});
|
335
|
-
|
317
|
+
|
336
318
|
/**
|
337
319
|
* Extend xhr object to send credentials and force XMLHttpRequest.
|
338
320
|
*/
|
@@ -341,17 +323,17 @@ $(function($) {
|
|
341
323
|
try {
|
342
324
|
xhr.withCredentials = "true";
|
343
325
|
} catch(e) {
|
344
|
-
alert('Cannot set xhr with credentials:\n'+e)
|
326
|
+
alert('Cannot set xhr with credentials:\n'+e);
|
345
327
|
}
|
346
328
|
};
|
347
|
-
|
329
|
+
|
348
330
|
/**
|
349
331
|
* Extends xhr on beforeSend by binding to Rails' ajax:loading event.
|
350
332
|
*/
|
351
333
|
$("body").bind('ajax:loading', function(e, xhr) {
|
352
334
|
extendXhr(xhr);
|
353
335
|
});
|
354
|
-
|
336
|
+
|
355
337
|
/**
|
356
338
|
* Try to send xhr request withCredentials.
|
357
339
|
* Unfortunately, this has to be set after the connection has been opened.
|
data/vidibus-xss.gemspec
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# Generated by jeweler
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{vidibus-xss}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.15"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Andre Pankratz"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-01-11}
|
13
13
|
s.description = %q{Drop-in XSS support for remote applications.}
|
14
14
|
s.email = %q{andre@vidibus.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -17,32 +17,30 @@ Gem::Specification.new do |s|
|
|
17
17
|
]
|
18
18
|
s.files = [
|
19
19
|
".bundle/config",
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
"vidibus-xss.gemspec"
|
20
|
+
".rspec",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"MIT-LICENSE",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"app/controllers/xss_controller.rb",
|
28
|
+
"config/routes.rb",
|
29
|
+
"lib/vidibus-xss.rb",
|
30
|
+
"lib/vidibus/xss.rb",
|
31
|
+
"lib/vidibus/xss/extensions.rb",
|
32
|
+
"lib/vidibus/xss/extensions/controller.rb",
|
33
|
+
"lib/vidibus/xss/extensions/string.rb",
|
34
|
+
"lib/vidibus/xss/extensions/view.rb",
|
35
|
+
"lib/vidibus/xss/mime_type.rb",
|
36
|
+
"public/javascripts/jquery.ba-bbq.js",
|
37
|
+
"public/javascripts/vidibus.js",
|
38
|
+
"public/javascripts/vidibus.loader.js",
|
39
|
+
"public/javascripts/vidibus.xss.js",
|
40
|
+
"spec/spec_helper.rb",
|
41
|
+
"vidibus-xss.gemspec"
|
43
42
|
]
|
44
43
|
s.homepage = %q{http://github.com/vidibus/vidibus-xss}
|
45
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
46
44
|
s.require_paths = ["lib"]
|
47
45
|
s.rubygems_version = %q{1.3.7}
|
48
46
|
s.summary = %q{Drop-in XSS support for remote applications.}
|
@@ -58,15 +56,42 @@ Gem::Specification.new do |s|
|
|
58
56
|
s.add_runtime_dependency(%q<rails>, ["~> 3.0.0"])
|
59
57
|
s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
|
60
58
|
s.add_runtime_dependency(%q<vidibus-routing_error>, [">= 0"])
|
59
|
+
s.add_runtime_dependency(%q<vidibus-uuid>, [">= 0"])
|
60
|
+
s.add_runtime_dependency(%q<jeweler>, [">= 0"])
|
61
|
+
s.add_runtime_dependency(%q<rake>, [">= 0"])
|
62
|
+
s.add_runtime_dependency(%q<rspec>, ["~> 2.0.0.beta.20"])
|
63
|
+
s.add_runtime_dependency(%q<rr>, [">= 0"])
|
64
|
+
s.add_runtime_dependency(%q<relevance-rcov>, [">= 0"])
|
65
|
+
s.add_runtime_dependency(%q<rails>, ["~> 3.0.0"])
|
66
|
+
s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
|
67
|
+
s.add_runtime_dependency(%q<vidibus-routing_error>, [">= 0"])
|
61
68
|
else
|
62
69
|
s.add_dependency(%q<rails>, ["~> 3.0.0"])
|
63
70
|
s.add_dependency(%q<nokogiri>, [">= 0"])
|
64
71
|
s.add_dependency(%q<vidibus-routing_error>, [">= 0"])
|
72
|
+
s.add_dependency(%q<vidibus-uuid>, [">= 0"])
|
73
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
74
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
75
|
+
s.add_dependency(%q<rspec>, ["~> 2.0.0.beta.20"])
|
76
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
77
|
+
s.add_dependency(%q<relevance-rcov>, [">= 0"])
|
78
|
+
s.add_dependency(%q<rails>, ["~> 3.0.0"])
|
79
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
80
|
+
s.add_dependency(%q<vidibus-routing_error>, [">= 0"])
|
65
81
|
end
|
66
82
|
else
|
67
83
|
s.add_dependency(%q<rails>, ["~> 3.0.0"])
|
68
84
|
s.add_dependency(%q<nokogiri>, [">= 0"])
|
69
85
|
s.add_dependency(%q<vidibus-routing_error>, [">= 0"])
|
86
|
+
s.add_dependency(%q<vidibus-uuid>, [">= 0"])
|
87
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
88
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
89
|
+
s.add_dependency(%q<rspec>, ["~> 2.0.0.beta.20"])
|
90
|
+
s.add_dependency(%q<rr>, [">= 0"])
|
91
|
+
s.add_dependency(%q<relevance-rcov>, [">= 0"])
|
92
|
+
s.add_dependency(%q<rails>, ["~> 3.0.0"])
|
93
|
+
s.add_dependency(%q<nokogiri>, [">= 0"])
|
94
|
+
s.add_dependency(%q<vidibus-routing_error>, [">= 0"])
|
70
95
|
end
|
71
96
|
end
|
72
97
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vidibus-xss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 5
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 15
|
10
|
+
version: 0.1.15
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Andre Pankratz
|
@@ -15,13 +15,14 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-01-11 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
|
-
|
22
|
+
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
|
24
|
+
name: rails
|
25
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
25
26
|
none: false
|
26
27
|
requirements:
|
27
28
|
- - ~>
|
@@ -32,12 +33,26 @@ dependencies:
|
|
32
33
|
- 0
|
33
34
|
- 0
|
34
35
|
version: 3.0.0
|
35
|
-
|
36
|
-
version_requirements: *id001
|
36
|
+
requirement: *id001
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
38
40
|
name: nokogiri
|
41
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 3
|
47
|
+
segments:
|
48
|
+
- 0
|
49
|
+
version: "0"
|
50
|
+
requirement: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
type: :runtime
|
39
53
|
prerelease: false
|
40
|
-
|
54
|
+
name: vidibus-routing_error
|
55
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
41
56
|
none: false
|
42
57
|
requirements:
|
43
58
|
- - ">="
|
@@ -46,12 +61,86 @@ dependencies:
|
|
46
61
|
segments:
|
47
62
|
- 0
|
48
63
|
version: "0"
|
64
|
+
requirement: *id003
|
65
|
+
- !ruby/object:Gem::Dependency
|
49
66
|
type: :runtime
|
50
|
-
|
67
|
+
prerelease: false
|
68
|
+
name: vidibus-uuid
|
69
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 3
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
requirement: *id004
|
51
79
|
- !ruby/object:Gem::Dependency
|
52
|
-
|
80
|
+
type: :runtime
|
81
|
+
prerelease: false
|
82
|
+
name: jeweler
|
83
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
requirement: *id005
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
type: :runtime
|
95
|
+
prerelease: false
|
96
|
+
name: rake
|
97
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
hash: 3
|
103
|
+
segments:
|
104
|
+
- 0
|
105
|
+
version: "0"
|
106
|
+
requirement: *id006
|
107
|
+
- !ruby/object:Gem::Dependency
|
108
|
+
type: :runtime
|
109
|
+
prerelease: false
|
110
|
+
name: rspec
|
111
|
+
version_requirements: &id007 !ruby/object:Gem::Requirement
|
112
|
+
none: false
|
113
|
+
requirements:
|
114
|
+
- - ~>
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
hash: 62196427
|
117
|
+
segments:
|
118
|
+
- 2
|
119
|
+
- 0
|
120
|
+
- 0
|
121
|
+
- beta
|
122
|
+
- 20
|
123
|
+
version: 2.0.0.beta.20
|
124
|
+
requirement: *id007
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
type: :runtime
|
127
|
+
prerelease: false
|
128
|
+
name: rr
|
129
|
+
version_requirements: &id008 !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
hash: 3
|
135
|
+
segments:
|
136
|
+
- 0
|
137
|
+
version: "0"
|
138
|
+
requirement: *id008
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
type: :runtime
|
53
141
|
prerelease: false
|
54
|
-
|
142
|
+
name: relevance-rcov
|
143
|
+
version_requirements: &id009 !ruby/object:Gem::Requirement
|
55
144
|
none: false
|
56
145
|
requirements:
|
57
146
|
- - ">="
|
@@ -60,8 +149,51 @@ dependencies:
|
|
60
149
|
segments:
|
61
150
|
- 0
|
62
151
|
version: "0"
|
152
|
+
requirement: *id009
|
153
|
+
- !ruby/object:Gem::Dependency
|
63
154
|
type: :runtime
|
64
|
-
|
155
|
+
prerelease: false
|
156
|
+
name: rails
|
157
|
+
version_requirements: &id010 !ruby/object:Gem::Requirement
|
158
|
+
none: false
|
159
|
+
requirements:
|
160
|
+
- - ~>
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
hash: 7
|
163
|
+
segments:
|
164
|
+
- 3
|
165
|
+
- 0
|
166
|
+
- 0
|
167
|
+
version: 3.0.0
|
168
|
+
requirement: *id010
|
169
|
+
- !ruby/object:Gem::Dependency
|
170
|
+
type: :runtime
|
171
|
+
prerelease: false
|
172
|
+
name: nokogiri
|
173
|
+
version_requirements: &id011 !ruby/object:Gem::Requirement
|
174
|
+
none: false
|
175
|
+
requirements:
|
176
|
+
- - ">="
|
177
|
+
- !ruby/object:Gem::Version
|
178
|
+
hash: 3
|
179
|
+
segments:
|
180
|
+
- 0
|
181
|
+
version: "0"
|
182
|
+
requirement: *id011
|
183
|
+
- !ruby/object:Gem::Dependency
|
184
|
+
type: :runtime
|
185
|
+
prerelease: false
|
186
|
+
name: vidibus-routing_error
|
187
|
+
version_requirements: &id012 !ruby/object:Gem::Requirement
|
188
|
+
none: false
|
189
|
+
requirements:
|
190
|
+
- - ">="
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
hash: 3
|
193
|
+
segments:
|
194
|
+
- 0
|
195
|
+
version: "0"
|
196
|
+
requirement: *id012
|
65
197
|
description: Drop-in XSS support for remote applications.
|
66
198
|
email: andre@vidibus.com
|
67
199
|
executables: []
|
@@ -72,7 +204,6 @@ extra_rdoc_files:
|
|
72
204
|
- README.rdoc
|
73
205
|
files:
|
74
206
|
- .bundle/config
|
75
|
-
- .gitignore
|
76
207
|
- .rspec
|
77
208
|
- Gemfile
|
78
209
|
- Gemfile.lock
|
@@ -100,8 +231,8 @@ homepage: http://github.com/vidibus/vidibus-xss
|
|
100
231
|
licenses: []
|
101
232
|
|
102
233
|
post_install_message:
|
103
|
-
rdoc_options:
|
104
|
-
|
234
|
+
rdoc_options: []
|
235
|
+
|
105
236
|
require_paths:
|
106
237
|
- lib
|
107
238
|
required_ruby_version: !ruby/object:Gem::Requirement
|