zendesk_apps_support 4.28.0 → 4.29.4
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 +5 -5
- data/README.md +1 -1
- data/config/locales/en.yml +5 -1
- data/config/locales/translations/zendesk_apps_support.yml +4 -0
- data/lib/zendesk_apps_support/location.rb +2 -1
- data/lib/zendesk_apps_support/package.rb +19 -19
- data/lib/zendesk_apps_support/validations/manifest.rb +64 -35
- data/lib/zendesk_apps_support/validations/requests.rb +4 -54
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8a0de7b7d7fcdd4588d5eff4a44ca2b7359630094c9660705f6964867de0324f
|
4
|
+
data.tar.gz: c2184021707a952289f78987019a8aab46fcebe51f094c8e0b5cba28aff57381
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c523f4bc63ff062085caebc36d05d708fd475661e338811d2ab435c011499333189a55965a3bb1b7b070f16d6ce2103210caf96edaafd13b264fcc19fdd5749e
|
7
|
+
data.tar.gz: b16330afa2508ad09407910ba8887c8e2f7304306d8141493059b71773783189426482b2731e126a0b5464dfa79dbbf19de4f86b912d0b7d47d9085a86e7b51a
|
data/README.md
CHANGED
@@ -22,7 +22,7 @@ This project uses Rspec, which can be run with `bundle exec rake`.
|
|
22
22
|
|
23
23
|
## Contribute
|
24
24
|
* Put up a PR into the master branch.
|
25
|
-
* CC and get two +1 from @zendesk/vegemite.
|
25
|
+
* CC and get two +1 from @zendesk/dingo, @zendesk/wattle, or @zendesk/vegemite.
|
26
26
|
|
27
27
|
## Bugs
|
28
28
|
Bugs can be reported as an issue here on github, or submitted to support@zendesk.com. By mentioning this project it will assigned to the right team.
|
data/config/locales/en.yml
CHANGED
@@ -62,7 +62,7 @@ en:
|
|
62
62
|
other: Unsupported MIME types detected in %{file_names}.
|
63
63
|
multiple_channel_integrations: Specifying multiple channel integrations
|
64
64
|
in requirements.json is not supported.
|
65
|
-
|
65
|
+
oauth_parameter_required: "Please upgrade to our new oauth format. Learn more: %{link}"
|
66
66
|
invalid_cr_schema_keys:
|
67
67
|
one: 'Custom resources schema contains an invalid key: %{invalid_keys}'
|
68
68
|
other: 'Custom resources schema contains invalid keys: %{invalid_keys}'
|
@@ -78,6 +78,8 @@ en:
|
|
78
78
|
missing:
|
79
79
|
one: 'Missing required oauth field in manifest: %{missing_keys}'
|
80
80
|
other: 'Missing required oauth fields in manifest: %{missing_keys}'
|
81
|
+
too_many_oauth_parameters: 'Too many parameters with type ''oauth'': one
|
82
|
+
permitted'
|
81
83
|
missing_source: Could not find app.js
|
82
84
|
style_in_template: "<style> tag in %{template}. Use an app.css file instead."
|
83
85
|
no_code_for_ifo_notemplate: Javascripts, stylesheets, and templates are
|
@@ -139,6 +141,8 @@ en:
|
|
139
141
|
invalid_v1_location:
|
140
142
|
one: "%{invalid_locations} is an invalid location in framework v1."
|
141
143
|
other: "%{invalid_locations} are invalid locations in framework v1."
|
144
|
+
oauth_parameter_cannot_be_secure: oauth parameter cannot be set to be
|
145
|
+
secure.
|
142
146
|
warning:
|
143
147
|
app_build:
|
144
148
|
deprecated_version: You are targeting a deprecated version of the framework.
|
@@ -399,3 +399,7 @@ parts:
|
|
399
399
|
key: "txt.apps.admin.error.app_build.invalid_v1_location.other"
|
400
400
|
title: "The locations listed are not available in framework v1."
|
401
401
|
value: "%{invalid_locations} are invalid locations in framework v1."
|
402
|
+
- translation:
|
403
|
+
key: "txt.apps.admin.error.app_build.oauth_parameter_cannot_be_secure"
|
404
|
+
title: "oauth parameter cannot be set to be secure."
|
405
|
+
value: "oauth parameter cannot be set to be secure."
|
@@ -64,7 +64,8 @@ module ZendeskAppsSupport
|
|
64
64
|
Location.new(id: 22, name: 'note_editor', product_code: Product::SELL.code, visible: true),
|
65
65
|
Location.new(id: 23, name: 'call_log_editor', product_code: Product::SELL.code, visible: true),
|
66
66
|
Location.new(id: 24, name: 'email_editor', product_code: Product::SELL.code, visible: true),
|
67
|
-
Location.new(id: 25, name: 'top_bar', product_code: Product::SELL.code, visible: true)
|
67
|
+
Location.new(id: 25, name: 'top_bar', product_code: Product::SELL.code, visible: true),
|
68
|
+
Location.new(id: 26, name: 'visit_editor', product_code: Product::SELL.code, visible: true)
|
68
69
|
].freeze
|
69
70
|
end
|
70
71
|
end
|
@@ -40,10 +40,10 @@ module ZendeskAppsSupport
|
|
40
40
|
errors << Validations::Source.call(self)
|
41
41
|
errors << Validations::Translations.call(self, skip_marketplace_translations: skip_marketplace_translations)
|
42
42
|
errors << Validations::Requirements.call(self)
|
43
|
-
errors << Validations::Requests.call(self)
|
44
43
|
|
45
44
|
# only adds warnings
|
46
45
|
Validations::SecureSettings.call(self)
|
46
|
+
Validations::Requests.call(self)
|
47
47
|
|
48
48
|
unless manifest.requirements_only? || manifest.marketing_only? || manifest.iframe_only?
|
49
49
|
errors << Validations::Templates.call(self)
|
@@ -278,6 +278,24 @@ module ZendeskAppsSupport
|
|
278
278
|
end
|
279
279
|
end
|
280
280
|
|
281
|
+
def location_icons
|
282
|
+
Hash.new { |h, k| h[k] = {} }.tap do |location_icons|
|
283
|
+
manifest.location_options.each do |location_options|
|
284
|
+
# no location information in the manifest
|
285
|
+
next unless location_options.location
|
286
|
+
|
287
|
+
product = location_options.location.product
|
288
|
+
location_name = location_options.location.name
|
289
|
+
# the location on the product does not support icons
|
290
|
+
next unless LOCATIONS_WITH_ICONS_PER_PRODUCT.fetch(product, []).include?(location_name)
|
291
|
+
|
292
|
+
host = location_options.location.product.name
|
293
|
+
product_directory = manifest.products.count > 1 ? "#{host}/" : ''
|
294
|
+
location_icons[host][location_name] = build_location_icons_hash(location_name, product_directory)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
281
299
|
private
|
282
300
|
|
283
301
|
def generate_logo_hash(products)
|
@@ -321,24 +339,6 @@ module ZendeskAppsSupport
|
|
321
339
|
has_file?('assets/banner.png')
|
322
340
|
end
|
323
341
|
|
324
|
-
def location_icons
|
325
|
-
Hash.new { |h, k| h[k] = {} }.tap do |location_icons|
|
326
|
-
manifest.location_options.each do |location_options|
|
327
|
-
# no location information in the manifest
|
328
|
-
next unless location_options.location
|
329
|
-
|
330
|
-
product = location_options.location.product
|
331
|
-
location_name = location_options.location.name
|
332
|
-
# the location on the product does not support icons
|
333
|
-
next unless LOCATIONS_WITH_ICONS_PER_PRODUCT.fetch(product, []).include?(location_name)
|
334
|
-
|
335
|
-
host = location_options.location.product.name
|
336
|
-
product_directory = manifest.products.count > 1 ? "#{host}/" : ''
|
337
|
-
location_icons[host][location_name] = build_location_icons_hash(location_name, product_directory)
|
338
|
-
end
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
342
|
def build_location_icons_hash(location, product_directory)
|
343
343
|
inactive_png = "icon_#{location}_inactive.png"
|
344
344
|
if has_file?("assets/#{product_directory}icon_#{location}.svg")
|
@@ -10,6 +10,7 @@ module ZendeskAppsSupport
|
|
10
10
|
REQUIRED_MANIFEST_FIELDS = RUBY_TO_JSON.select { |k| %i[author default_locale].include? k }.freeze
|
11
11
|
OAUTH_REQUIRED_FIELDS = %w[client_id client_secret authorize_uri access_token_uri].freeze
|
12
12
|
PARAMETER_TYPES = ZendeskAppsSupport::Manifest::Parameter::TYPES
|
13
|
+
OAUTH_MANIFEST_LINK = 'https://developer.zendesk.com/apps/docs/developer-guide/manifest#oauth'
|
13
14
|
|
14
15
|
class << self
|
15
16
|
def call(package)
|
@@ -33,51 +34,71 @@ module ZendeskAppsSupport
|
|
33
34
|
def collate_manifest_errors(package)
|
34
35
|
manifest = package.manifest
|
35
36
|
|
36
|
-
errors = [
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
errors = [
|
38
|
+
missing_keys_error(manifest),
|
39
|
+
type_checks(manifest),
|
40
|
+
oauth_error(manifest),
|
41
|
+
default_locale_error(manifest, package),
|
42
|
+
validate_parameters(manifest),
|
43
|
+
if manifest.requirements_only? || manifest.marketing_only?
|
44
|
+
[ ban_location(manifest),
|
45
|
+
ban_framework_version(manifest) ]
|
46
|
+
else
|
47
|
+
[ validate_location(package),
|
48
|
+
missing_framework_version(manifest),
|
49
|
+
invalid_version_error(manifest) ]
|
50
|
+
end,
|
51
|
+
ban_no_template(manifest)
|
52
|
+
]
|
53
|
+
errors.flatten.compact
|
54
|
+
end
|
41
55
|
|
56
|
+
def validate_location(package)
|
57
|
+
manifest = package.manifest
|
58
|
+
[
|
59
|
+
missing_location_error(package),
|
60
|
+
invalid_location_error(package),
|
61
|
+
invalid_v1_location(package),
|
62
|
+
location_framework_mismatch(manifest)
|
63
|
+
]
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate_parameters(manifest)
|
42
67
|
if manifest.marketing_only?
|
43
|
-
|
68
|
+
marketing_only_errors(manifest)
|
44
69
|
else
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
70
|
+
[
|
71
|
+
parameters_error(manifest),
|
72
|
+
invalid_hidden_parameter_error(manifest),
|
73
|
+
invalid_type_error(manifest),
|
74
|
+
too_many_oauth_parameters(manifest),
|
75
|
+
oauth_cannot_be_secure(manifest),
|
76
|
+
name_as_parameter_name_error(manifest)
|
77
|
+
]
|
50
78
|
end
|
79
|
+
end
|
51
80
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
errors << invalid_location_error(package)
|
58
|
-
errors << invalid_v1_location(package)
|
59
|
-
errors << missing_framework_version(manifest)
|
60
|
-
errors << location_framework_mismatch(manifest)
|
61
|
-
errors << invalid_version_error(manifest)
|
81
|
+
def oauth_cannot_be_secure(manifest)
|
82
|
+
manifest.parameters.map do |parameter|
|
83
|
+
if parameter.type == 'oauth' && parameter.secure
|
84
|
+
return ValidationError.new('oauth_parameter_cannot_be_secure')
|
85
|
+
end
|
62
86
|
end
|
63
|
-
|
64
|
-
errors << ban_no_template(manifest) if manifest.iframe_only?
|
65
|
-
|
66
|
-
errors.flatten.compact
|
67
87
|
end
|
68
88
|
|
69
89
|
def marketing_only_errors(manifest)
|
70
|
-
[
|
71
|
-
|
72
|
-
|
73
|
-
|
90
|
+
[
|
91
|
+
ban_parameters(manifest),
|
92
|
+
private_marketing_app_error(manifest)
|
93
|
+
]
|
74
94
|
end
|
75
95
|
|
76
96
|
def type_checks(manifest)
|
77
|
-
errors = [
|
78
|
-
|
79
|
-
|
80
|
-
|
97
|
+
errors = [
|
98
|
+
boolean_error(manifest),
|
99
|
+
string_error(manifest),
|
100
|
+
no_template_format_error(manifest)
|
101
|
+
]
|
81
102
|
unless manifest.experiments.is_a?(Hash)
|
82
103
|
errors << ValidationError.new(
|
83
104
|
:unacceptable_hash,
|
@@ -152,6 +173,7 @@ module ZendeskAppsSupport
|
|
152
173
|
end
|
153
174
|
|
154
175
|
def ban_no_template(manifest)
|
176
|
+
return unless manifest.iframe_only?
|
155
177
|
no_template_migration_link = 'https://developer.zendesk.com/apps/docs/apps-v2/manifest#location'
|
156
178
|
if manifest.no_template? || !manifest.no_template_locations.empty?
|
157
179
|
ValidationError.new(:no_template_deprecated_in_v2, link: no_template_migration_link)
|
@@ -176,14 +198,21 @@ module ZendeskAppsSupport
|
|
176
198
|
|
177
199
|
def oauth_error(manifest)
|
178
200
|
return unless manifest.oauth
|
179
|
-
|
201
|
+
oauth_errors = []
|
180
202
|
missing = OAUTH_REQUIRED_FIELDS.select do |key|
|
181
203
|
manifest.oauth[key].nil? || manifest.oauth[key].empty?
|
182
204
|
end
|
183
205
|
|
184
206
|
if missing.any?
|
185
|
-
|
207
|
+
oauth_errors << \
|
208
|
+
ValidationError.new('oauth_keys.missing', missing_keys: missing.join(', '), count: missing.length)
|
209
|
+
end
|
210
|
+
|
211
|
+
unless manifest.parameters.any? { |param| param.type == 'oauth' }
|
212
|
+
oauth_errors << ValidationError.new('oauth_parameter_required',
|
213
|
+
link: OAUTH_MANIFEST_LINK)
|
186
214
|
end
|
215
|
+
oauth_errors
|
187
216
|
end
|
188
217
|
|
189
218
|
def parameters_error(manifest)
|
@@ -7,62 +7,25 @@ module ZendeskAppsSupport
|
|
7
7
|
module Validations
|
8
8
|
module Requests
|
9
9
|
class << self
|
10
|
-
IP_ADDRESS = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
|
11
|
-
|
12
10
|
def call(package)
|
13
|
-
errors = []
|
14
11
|
files = package.js_files + package.html_files
|
15
|
-
private_app = package.manifest.private?
|
16
12
|
|
17
13
|
files.each do |file|
|
18
14
|
file_content = file.read
|
19
15
|
|
20
16
|
http_protocol_urls = find_address_containing_http(file_content)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
)
|
26
|
-
end
|
27
|
-
|
28
|
-
ip_addresses = file_content.scan(IP_ADDRESS)
|
29
|
-
next unless ip_addresses.any?
|
30
|
-
|
31
|
-
ip_validation_messages = ip_validation_messages(
|
32
|
-
file.relative_path,
|
33
|
-
ip_addresses,
|
34
|
-
private_app
|
17
|
+
next unless http_protocol_urls.any?
|
18
|
+
package.warnings << insecure_http_requests_warning(
|
19
|
+
http_protocol_urls,
|
20
|
+
file.relative_path
|
35
21
|
)
|
36
|
-
|
37
|
-
validation_group = private_app ? package.warnings : errors
|
38
|
-
validation_group << ip_validation_messages
|
39
22
|
end
|
40
23
|
|
41
24
|
package.warnings.flatten!
|
42
|
-
errors
|
43
25
|
end
|
44
26
|
|
45
27
|
private
|
46
28
|
|
47
|
-
def ip_validation_messages(file_path, ip_addresses, private_app)
|
48
|
-
ip_addresses.each_with_object([]) do |ip_address, messages|
|
49
|
-
ip_type_string = ip_type_string(ip_address)
|
50
|
-
next unless ip_type_string
|
51
|
-
|
52
|
-
string_params = {
|
53
|
-
type: ip_type_string, uri: ip_address, file: file_path
|
54
|
-
}
|
55
|
-
validation_message =
|
56
|
-
if private_app
|
57
|
-
I18n.t('txt.apps.admin.error.app_build.blocked_request', string_params)
|
58
|
-
else
|
59
|
-
ValidationError.new(:blocked_request, string_params)
|
60
|
-
end
|
61
|
-
|
62
|
-
messages << validation_message
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
29
|
def insecure_http_requests_warning(http_protocol_urls, relative_path)
|
67
30
|
http_protocol_urls = http_protocol_urls.join(
|
68
31
|
I18n.t('txt.apps.admin.error.app_build.listing_comma')
|
@@ -75,19 +38,6 @@ module ZendeskAppsSupport
|
|
75
38
|
)
|
76
39
|
end
|
77
40
|
|
78
|
-
def ip_type_string(ip_address)
|
79
|
-
block_type =
|
80
|
-
case IPAddress.parse(ip_address)
|
81
|
-
when proc(&:private?) then 'private'
|
82
|
-
when proc(&:loopback?) then 'loopback'
|
83
|
-
when proc(&:link_local?) then 'link_local'
|
84
|
-
end
|
85
|
-
|
86
|
-
block_type && I18n.t("txt.apps.admin.error.app_build.blocked_request_#{block_type}")
|
87
|
-
rescue ArgumentError
|
88
|
-
nil # Ignore numbers which are not an IP address
|
89
|
-
end
|
90
|
-
|
91
41
|
def find_address_containing_http(file_content)
|
92
42
|
file_content.scan(URI.regexp(['http'])).map(&:compact).map(&:last)
|
93
43
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zendesk_apps_support
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.29.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James A. Rosen
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2020-
|
14
|
+
date: 2020-07-22 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: i18n
|
@@ -320,7 +320,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
320
320
|
version: 1.3.6
|
321
321
|
requirements: []
|
322
322
|
rubyforge_project:
|
323
|
-
rubygems_version: 2.
|
323
|
+
rubygems_version: 2.7.10
|
324
324
|
signing_key:
|
325
325
|
specification_version: 4
|
326
326
|
summary: Support to help you develop Zendesk Apps.
|