perimeter_x 2.2.1 → 2.3.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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/changelog.md +10 -0
- data/lib/perimeter_x.rb +10 -5
- data/lib/perimeterx/utils/px_constants.rb +2 -1
- data/lib/perimeterx/utils/px_template_factory.rb +2 -3
- data/lib/perimeterx/utils/templates/block_template.mustache +32 -163
- data/lib/perimeterx/version.rb +1 -1
- data/px_metadata.json +28 -0
- data/readme.md +7 -2
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7321ac61fcf5c1b2b8a573eb23744d807e0af5e430a3468cb4704e8d36c7f8a9
|
4
|
+
data.tar.gz: 5bd64b4340535a52d2f91db99cabb673693dec8dbf1526dd00d2aa200743a444
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3037218a40b007fd7a0aaf93c6fe2e367b622042f2653c2e08e3c9a5d53d12d0107374d81e841d46e9320562d95c2ac4957040d7cb3a932c971ab2a1ff16aed2
|
7
|
+
data.tar.gz: 5de6563d962cee1d9ba90ac370ecb4a0a8679dfab9a1ab69b2dbcfaafe54d3ce05b12f31b8c57f1a5b129299e215dd0e8f7e488bbadf03308099104ec7f2ab60
|
data/LICENSE.txt
CHANGED
data/changelog.md
CHANGED
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
7
7
|
|
8
|
+
## [2.3.0] - 2022-04-10
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- Custom logo in block JSON response
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
|
16
|
+
- Updated block page to use new template
|
17
|
+
|
8
18
|
## [2.2.1] - 2020-09-27
|
9
19
|
### Fixed
|
10
20
|
- bypass_monitor_header type validation
|
data/lib/perimeter_x.rb
CHANGED
@@ -56,19 +56,22 @@ module PxModule
|
|
56
56
|
end
|
57
57
|
|
58
58
|
is_mobile = px_ctx.context[:cookie_origin] == 'header' ? '1' : '0'
|
59
|
-
action = px_ctx.context[:block_action][0,1]
|
59
|
+
action = px_ctx.context[:block_action][0,1]
|
60
|
+
block_script_uri = "/captcha.js?a=#{action}&u=#{px_ctx.context[:uuid]}&v=#{px_ctx.context[:vid]}&m=#{is_mobile}"
|
60
61
|
|
61
62
|
if px_config[:first_party_enabled]
|
62
63
|
px_template_object = {
|
63
64
|
js_client_src: "/#{px_config[:app_id][2..-1]}/init.js",
|
64
|
-
block_script: "/#{px_config[:app_id][2..-1]}/captcha/#{px_config[:app_id]}
|
65
|
-
host_url: "/#{px_config[:app_id][2..-1]}/xhr"
|
65
|
+
block_script: "/#{px_config[:app_id][2..-1]}/captcha/#{px_config[:app_id]}#{block_script_uri}",
|
66
|
+
host_url: "/#{px_config[:app_id][2..-1]}/xhr",
|
67
|
+
alt_block_script: "//#{PxModule::ALT_CAPTCHA_HOST}/#{px_config[:app_id]}#{block_script_uri}"
|
66
68
|
}
|
67
69
|
else
|
68
70
|
px_template_object = {
|
69
71
|
js_client_src: "//#{PxModule::CLIENT_HOST}/#{px_config[:app_id]}/main.min.js",
|
70
|
-
block_script: "//#{PxModule::CAPTCHA_HOST}/#{px_config[:app_id]}
|
71
|
-
host_url: "https://collector-#{px_config[:app_id]}.perimeterx.net"
|
72
|
+
block_script: "//#{PxModule::CAPTCHA_HOST}/#{px_config[:app_id]}#{block_script_uri}",
|
73
|
+
host_url: "https://collector-#{px_config[:app_id]}.perimeterx.net",
|
74
|
+
alt_block_script: "//#{PxModule::ALT_CAPTCHA_HOST}/#{px_config[:app_id]}#{block_script_uri}"
|
72
75
|
}
|
73
76
|
end
|
74
77
|
|
@@ -92,6 +95,8 @@ module PxModule
|
|
92
95
|
:vid => px_ctx.context[:vid],
|
93
96
|
:hostUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net",
|
94
97
|
:blockScript => px_template_object[:block_script],
|
98
|
+
:altBlockScript => px_template_object[:alt_block_script],
|
99
|
+
:customLogo => px_config[:custom_logo]
|
95
100
|
}
|
96
101
|
|
97
102
|
render :json => hash_json
|
@@ -36,11 +36,11 @@ module PxModule
|
|
36
36
|
PROP_APP_ID = :appId
|
37
37
|
PROP_VID = :vid
|
38
38
|
PROP_UUID = :uuid
|
39
|
-
PROP_LOGO_VISIBILITY = :logoVisibility
|
40
39
|
PROP_CUSTOM_LOGO = :customLogo
|
41
40
|
PROP_CSS_REF = :cssRef
|
42
41
|
PROP_JS_REF = :jsRef
|
43
42
|
PROP_BLOCK_SCRIPT = :blockScript
|
43
|
+
PROP_ALT_BLOCK_SCRIPT = :altBlockScript
|
44
44
|
PROP_JS_CLIENT_SRC = :jsClientSrc
|
45
45
|
PROP_HOST_URL = :hostUrl
|
46
46
|
PROP_FIRST_PARTY_ENABLED = :firstPartyEnabled
|
@@ -48,6 +48,7 @@ module PxModule
|
|
48
48
|
# Hosts
|
49
49
|
CLIENT_HOST = 'client.perimeterx.net'
|
50
50
|
CAPTCHA_HOST = 'captcha.px-cdn.net'
|
51
|
+
ALT_CAPTCHA_HOST = 'captcha.px-cloud.net'
|
51
52
|
|
52
53
|
VISIBLE = 'visible'
|
53
54
|
HIDDEN = 'hidden'
|
@@ -6,7 +6,7 @@ module PxModule
|
|
6
6
|
def self.get_template(px_ctx, px_config, px_template_object)
|
7
7
|
logger = px_config[:logger]
|
8
8
|
if (px_config[:challenge_enabled] && px_ctx.context[:block_action] == 'challenge')
|
9
|
-
logger.debug('PxTemplateFactory[get_template]: px
|
9
|
+
logger.debug('PxTemplateFactory[get_template]: px challenge triggered')
|
10
10
|
return px_ctx.context[:block_action_data].html_safe
|
11
11
|
end
|
12
12
|
|
@@ -23,15 +23,14 @@ module PxModule
|
|
23
23
|
Mustache.template_file = "#{File.dirname(__FILE__) }/templates/#{template_type}#{PxModule::TEMPLATE_EXT}"
|
24
24
|
|
25
25
|
view[PxModule::PROP_APP_ID] = px_config[:app_id]
|
26
|
-
view[PxModule::PROP_REF_ID] = px_ctx.context[:uuid]
|
27
26
|
view[PxModule::PROP_VID] = px_ctx.context[:vid]
|
28
27
|
view[PxModule::PROP_UUID] = px_ctx.context[:uuid]
|
29
28
|
view[PxModule::PROP_CUSTOM_LOGO] = px_config[:custom_logo]
|
30
29
|
view[PxModule::PROP_CSS_REF] = px_config[:css_ref]
|
31
30
|
view[PxModule::PROP_JS_REF] = px_config[:js_ref]
|
32
31
|
view[PxModule::PROP_HOST_URL] = px_template_object[:host_url]
|
33
|
-
view[PxModule::PROP_LOGO_VISIBILITY] = px_config[:custom_logo] ? PxModule::VISIBLE : PxModule::HIDDEN
|
34
32
|
view[PxModule::PROP_BLOCK_SCRIPT] = px_template_object[:block_script]
|
33
|
+
view[PxModule::PROP_ALT_BLOCK_SCRIPT] = px_template_object[:alt_block_script]
|
35
34
|
view[PxModule::PROP_JS_CLIENT_SRC] = px_template_object[:js_client_src]
|
36
35
|
view[PxModule::PROP_FIRST_PARTY_ENABLED] = px_ctx.context[:first_party_enabled]
|
37
36
|
|
@@ -3,173 +3,42 @@
|
|
3
3
|
<head>
|
4
4
|
<meta charset="utf-8">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
|
-
<
|
7
|
-
<
|
8
|
-
<style>
|
9
|
-
html, body {
|
10
|
-
margin: 0;
|
11
|
-
padding: 0;
|
12
|
-
font-family: 'Open Sans', sans-serif;
|
13
|
-
color: #000;
|
14
|
-
}
|
15
|
-
|
16
|
-
a {
|
17
|
-
color: #c5c5c5;
|
18
|
-
text-decoration: none;
|
19
|
-
}
|
20
|
-
|
21
|
-
.container {
|
22
|
-
align-items: center;
|
23
|
-
display: flex;
|
24
|
-
flex: 1;
|
25
|
-
justify-content: space-between;
|
26
|
-
flex-direction: column;
|
27
|
-
height: 100%;
|
28
|
-
}
|
29
|
-
|
30
|
-
.container > div {
|
31
|
-
width: 100%;
|
32
|
-
display: flex;
|
33
|
-
justify-content: center;
|
34
|
-
}
|
35
|
-
|
36
|
-
.container > div > div {
|
37
|
-
display: flex;
|
38
|
-
width: 80%;
|
39
|
-
}
|
40
|
-
|
41
|
-
.customer-logo-wrapper {
|
42
|
-
padding-top: 2rem;
|
43
|
-
flex-grow: 0;
|
44
|
-
background-color: #fff;
|
45
|
-
visibility: {{logoVisibility}};
|
46
|
-
}
|
47
|
-
|
48
|
-
.customer-logo {
|
49
|
-
border-bottom: 1px solid #000;
|
50
|
-
}
|
51
|
-
|
52
|
-
.customer-logo > img {
|
53
|
-
padding-bottom: 1rem;
|
54
|
-
max-height: 50px;
|
55
|
-
max-width: 100%;
|
56
|
-
}
|
57
|
-
|
58
|
-
.page-title-wrapper {
|
59
|
-
flex-grow: 2;
|
60
|
-
}
|
61
|
-
|
62
|
-
.page-title {
|
63
|
-
flex-direction: column-reverse;
|
64
|
-
}
|
65
|
-
|
66
|
-
.content-wrapper {
|
67
|
-
flex-grow: 5;
|
68
|
-
}
|
69
|
-
|
70
|
-
.content {
|
71
|
-
flex-direction: column;
|
72
|
-
}
|
73
|
-
|
74
|
-
.page-footer-wrapper {
|
75
|
-
align-items: center;
|
76
|
-
flex-grow: 0.2;
|
77
|
-
background-color: #000;
|
78
|
-
color: #c5c5c5;
|
79
|
-
font-size: 70%;
|
80
|
-
}
|
81
|
-
|
82
|
-
@media (min-width: 768px) {
|
83
|
-
html, body {
|
84
|
-
height: 100%;
|
85
|
-
}
|
86
|
-
}
|
87
|
-
</style>
|
88
|
-
<!-- Custom CSS -->
|
6
|
+
<meta name="description" content="px-captcha">
|
7
|
+
<title>Access to this page has been denied</title>
|
89
8
|
{{#cssRef}}
|
90
9
|
<link rel="stylesheet" type="text/css" href="{{{cssRef}}}"/>
|
91
10
|
{{/cssRef}}
|
92
11
|
</head>
|
93
|
-
|
94
12
|
<body>
|
95
|
-
<
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
<p>
|
112
|
-
Access to this page has been denied because we believe you are using automation tools to browse the
|
113
|
-
website.
|
114
|
-
</p>
|
115
|
-
<p>
|
116
|
-
This may happen as a result of the following:
|
117
|
-
</p>
|
118
|
-
<ul>
|
119
|
-
<li>
|
120
|
-
Javascript is disabled or blocked by an extension (ad blockers for example)
|
121
|
-
</li>
|
122
|
-
<li>
|
123
|
-
Your browser does not support cookies
|
124
|
-
</li>
|
125
|
-
</ul>
|
126
|
-
<p>
|
127
|
-
Please make sure that Javascript and cookies are enabled on your browser and that you are not blocking
|
128
|
-
them from loading.
|
129
|
-
</p>
|
130
|
-
<p>
|
131
|
-
Reference ID: #{{refId}}
|
132
|
-
</p>
|
133
|
-
</div>
|
134
|
-
</div>
|
135
|
-
<div class="page-footer-wrapper">
|
136
|
-
<div class="page-footer">
|
137
|
-
<p>
|
138
|
-
Powered by
|
139
|
-
<a href="https://www.perimeterx.com/whywasiblocked">PerimeterX</a>
|
140
|
-
, Inc.
|
141
|
-
</p>
|
142
|
-
</div>
|
143
|
-
</div>
|
144
|
-
</section>
|
145
|
-
<!-- Px -->
|
146
|
-
<script>
|
147
|
-
window._pxAppId = '{{appId}}';
|
148
|
-
window._pxJsClientSrc = '{{{jsClientSrc}}}';
|
149
|
-
window._pxFirstPartyEnabled = {{firstPartyEnabled}};
|
150
|
-
window._pxVid = '{{vid}}';
|
151
|
-
window._pxUuid = '{{uuid}}';
|
152
|
-
window._pxHostUrl = '{{{hostUrl}}}';
|
153
|
-
</script>
|
154
|
-
<script>
|
155
|
-
var s = document.createElement('script');
|
156
|
-
s.src = '{{{blockScript}}}';
|
157
|
-
var p = document.getElementsByTagName('head')[0];
|
158
|
-
p.insertBefore(s, null);
|
159
|
-
if ({{firstPartyEnabled}}) {
|
160
|
-
s.onerror = function () {
|
161
|
-
s = document.createElement('script');
|
162
|
-
var suffixIndex = '{{{blockScript}}}'.indexOf('captcha.js');
|
163
|
-
var temperedBlockScript = '{{{blockScript}}}'.substring(suffixIndex);
|
164
|
-
s.src = '//captcha.px-cdn.net/{{appId}}/' + temperedBlockScript;
|
165
|
-
p.parentNode.insertBefore(s, p);
|
13
|
+
<script>
|
14
|
+
window._pxVid = '{{vid}}';
|
15
|
+
window._pxUuid = '{{uuid}}';
|
16
|
+
window._pxAppId = '{{appId}}';
|
17
|
+
window._pxCustomLogo = '{{customLogo}}';
|
18
|
+
window._pxHostUrl = '{{hostUrl}}';
|
19
|
+
window._pxJsClientSrc = '{{jsClientSrc}}';
|
20
|
+
window._pxFirstPartyEnabled = {{firstPartyEnabled}};
|
21
|
+
var script = document.createElement('script');
|
22
|
+
script.src = '{{blockScript}}';
|
23
|
+
document.head.appendChild(script);
|
24
|
+
script.onerror = function () {
|
25
|
+
script = document.createElement('script');
|
26
|
+
script.src = '{{altBlockScript}}';
|
27
|
+
script.onerror = window._pxDisplayErrorMessage;
|
28
|
+
document.head.appendChild(script);
|
166
29
|
};
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
30
|
+
window._pxDisplayErrorMessage = function () {
|
31
|
+
var style = document.createElement('style');
|
32
|
+
style.innerText = '@import url(https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap);body{background-color:#fafbfc}@media (max-width:480px){body{background-color:#fff}}.px-captcha-error-container{position:fixed;height:328px;background-color:#fff;font-family:Roboto,sans-serif}.px-captcha-error-header{color:#f0f1f2;font-size:29px;margin:67px 0 33px;font-weight:500;line-height:.83;text-align:center}.px-captcha-error-message{color:#f0f1f2;font-size:18px;margin:0 0 29px;line-height:1.33;text-align:center}div.px-captcha-error-button{text-align:center;line-height:50px;width:253px;margin:auto;border-radius:25px;border:solid 1px #f0f1f2;font-size:20px;color:#f0f1f2}div.px-captcha-error-wrapper{margin:23px 0 0}div.px-captcha-error{margin:auto;text-align:center;width:400px;height:30px;font-size:12px;background-color:#fcf0f2;color:#ce0e2d}img.px-captcha-error{margin:6px 10px -2px 0}@media (min-width:620px){.px-captcha-error-container{width:528px;top:50%;left:50%;margin-top:-164px;margin-left:-264px;border-radius:3px;box-shadow:0 2px 9px -1px rgba(0,0,0,.13)}}@media (min-width:481px) and (max-width:620px){.px-captcha-error-container{width:85%;top:50%;left:50%;margin-top:-164px;margin-left:-42.5%;border-radius:3px;box-shadow:0 2px 9px -1px rgba(0,0,0,.13)}}@media (max-width:480px){.px-captcha-error-container{width:528px;top:50%;left:50%;margin-top:-164px;margin-left:-264px}}';
|
33
|
+
document.head.appendChild(style);
|
34
|
+
var div = document.createElement('div');
|
35
|
+
div.className = 'px-captcha-error-container';
|
36
|
+
div.innerHTML = '<div class="px-captcha-error-header">Before we continue...</div><div class="px-captcha-error-message">Press & Hold to confirm you are<br>a human (and not a bot).</div><div class="px-captcha-error-button">Press & Hold</div><div class="px-captcha-error-wrapper"><div class="px-captcha-error"><img class="px-captcha-error" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAQCAMAAADDGrRQAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABFUExURUdwTNYELOEGONQILd0AONwALtwEL+AAL9MFLfkJSNQGLdMJLdQJLdQGLdQKLtYFLNcELdUGLdcBL9gFL88OLdUFLNEOLglBhT4AAAAXdFJOUwC8CqgNIRgRoAS1dWWuR4RTjzgryZpYblfkcAAAAI9JREFUGNNdj+sWhCAIhAdvqGVa1r7/oy6RZ7eaH3D4ZACBIed9wlOOMtUnSrEmZ6cHa9YAIfsbCkWrdpi/c50Bk2CO9mNLdMAu03wJA3HpEnfpxbyOg6ruyx8JJi6KNstnslp1dbPd9GnqmuYq7mmcv1zjnbQw8cV0xzkqo+fX1zkjUOO7wnrInUTxJiruC3vtBNRoQQn2AAAAAElFTkSuQmCC">Please check your network connection or disable your ad-blocker.</div></div>';
|
37
|
+
document.body.appendChild(div);
|
38
|
+
};
|
39
|
+
</script>
|
40
|
+
{{#jsRef}}
|
41
|
+
<script src="{{{jsRef}}}"></script>
|
42
|
+
{{/jsRef}}
|
174
43
|
</body>
|
175
|
-
</html>
|
44
|
+
</html>
|
data/lib/perimeterx/version.rb
CHANGED
data/px_metadata.json
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
{
|
2
|
+
"version": "2.3.0",
|
3
|
+
"supported_features": [
|
4
|
+
"additional_activity_handler",
|
5
|
+
"advanced_blocking_response",
|
6
|
+
"batched_activities",
|
7
|
+
"block_activity",
|
8
|
+
"block_page_captcha",
|
9
|
+
"block_page_rate_limit",
|
10
|
+
"bypass_monitor_header",
|
11
|
+
"client_ip_extraction",
|
12
|
+
"cookie_v3",
|
13
|
+
"css_ref",
|
14
|
+
"custom_logo",
|
15
|
+
"logger",
|
16
|
+
"filter_by_route",
|
17
|
+
"first_party",
|
18
|
+
"js_ref",
|
19
|
+
"mobile_support",
|
20
|
+
"module_enable",
|
21
|
+
"module_mode",
|
22
|
+
"page_requested_activity",
|
23
|
+
"vid_extraction",
|
24
|
+
"risk_api",
|
25
|
+
"sensitive_headers",
|
26
|
+
"sensitive_routes"
|
27
|
+
]
|
28
|
+
}
|
data/readme.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
[PerimeterX](http://www.perimeterx.com) Ruby SDK
|
6
6
|
=============================================================
|
7
7
|
|
8
|
-
> Latest stable version: [v2.
|
8
|
+
> Latest stable version: [v2.3.0](https://rubygems.org/gems/perimeter_x)
|
9
9
|
|
10
10
|
Table of Contents
|
11
11
|
-----------------
|
@@ -31,6 +31,7 @@ Table of Contents
|
|
31
31
|
* [Update Configuration on Runtime](#update-config)
|
32
32
|
* [First Party](#first-party)
|
33
33
|
|
34
|
+
**[Additional Information](#additional-information)**
|
34
35
|
**[Contributing](#contributing)**
|
35
36
|
|
36
37
|
<a name="Usage"></a>
|
@@ -350,8 +351,12 @@ Default: true
|
|
350
351
|
params[:first_party_enabled] = false
|
351
352
|
```
|
352
353
|
|
354
|
+
<a name="additional_information"></a> Additional Information
|
355
|
+
------------------------------
|
356
|
+
### URI Delimiters
|
357
|
+
PerimeterX processes URI paths with general- and sub-delimiters according to RFC 3986. General delimiters (e.g., `?`, `#`) are used to separate parts of the URI. Sub-delimiters (e.g., `$`, `&`) are not used to split the URI as they are considered valid characters in the URI path.
|
353
358
|
|
354
|
-
<a name="contributing"></a
|
359
|
+
<a name="contributing"></a> Contributing
|
355
360
|
------------------------------
|
356
361
|
The following steps are welcome when contributing to our project.
|
357
362
|
###Fork/Clone
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: perimeter_x
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nitzan Goldfeder
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -191,6 +191,7 @@ files:
|
|
191
191
|
- lib/perimeterx/utils/templates/ratelimit.mustache
|
192
192
|
- lib/perimeterx/version.rb
|
193
193
|
- perimeter_x.gemspec
|
194
|
+
- px_metadata.json
|
194
195
|
- readme.md
|
195
196
|
homepage: https://www.perimeterx.com
|
196
197
|
licenses:
|
@@ -215,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
215
216
|
- !ruby/object:Gem::Version
|
216
217
|
version: '0'
|
217
218
|
requirements: []
|
218
|
-
rubygems_version: 3.0.3
|
219
|
+
rubygems_version: 3.0.3.1
|
219
220
|
signing_key:
|
220
221
|
specification_version: 4
|
221
222
|
summary: PerimeterX ruby implmentation
|