camaleon_cms 2.7.4 → 2.7.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of camaleon_cms might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db5860768212ca0703d01e3d2b16a691d5972917905bab1740cf16a1fb79ab58
4
- data.tar.gz: cd0b90107912e5281d49d4a0070734a9db641deadb27f9671d27a13a297c8c71
3
+ metadata.gz: db43005ef92c135dec7d3ef6871aeac761dd01d904e3f5707bee706f2dd65aa3
4
+ data.tar.gz: e99b9643fba7ab169ede5758bedfd1b8c7c569c7972363782f79ec91770da3da
5
5
  SHA512:
6
- metadata.gz: bfc9b376433413768f22e2b191f0f977697c8789004ef91eab78edd683ae22914a4a194c1e8568fa4d074a3eb5f7773b1d492298121b8afb64731c87bd390f8b
7
- data.tar.gz: 99f90a12596bc5d24a8dd73555cba291d3140f84f4c5373931b6b62a8a79301126020c7de1a82b4fea7b4c5019062bbfc3573ad5e614584ab8777fc7e6767a2a
6
+ metadata.gz: 7e4d609bf25dfbfe2c994f1393dbfe7bc9aa060f07e10bd6446f3273fbecc1646868a1efcae9b14dd32dd206c64a986e0210ea1199c3f1e2cda680953824ada2
7
+ data.tar.gz: fb39191112e373c198578073e5b317bd94749b4ef2956afd2e3baa07a98e91d3a30af9c1f5c9e98822f9338a8ab5f86aea6be405335a71794c55317bc68e285e
@@ -9,18 +9,18 @@ window.cama_init_posttype_form = function() {
9
9
 
10
10
  item.parent().siblings().find('input').prop('disabled', $(this).is(':checked'))
11
11
  if ($(this).is(':checked'))
12
- return item.prop('checked', true).prop('disabled', false)
12
+ item.prop('checked', true).prop('disabled', false)
13
13
  else
14
- return item.prop('disabled', true)
14
+ item.prop('disabled', true)
15
15
  }).trigger('change')
16
16
 
17
17
  form.find('[name="meta[has_picture]"]').change(function() {
18
18
  const items = form.find('.picture_settings input')
19
19
 
20
20
  if ($(this).is(':checked'))
21
- return items.prop('disabled', false)
21
+ items.prop('disabled', false)
22
22
  else
23
- return items.prop('disabled', true)
23
+ items.prop('disabled', true)
24
24
  }).trigger('change')
25
25
 
26
26
  // toggle single and multiple categories checkbox
@@ -28,9 +28,10 @@ window.cama_init_posttype_form = function() {
28
28
  'input:checkbox[name="meta[has_category]"], input:checkbox[name="meta[has_single_category]"]'
29
29
  )
30
30
 
31
- return catChecks.change(
31
+ catChecks.change(
32
32
  function() {
33
- if ($(this).is(':checked')) return catChecks.not(this).prop('checked', false)
33
+ if ($(this).is(':checked'))
34
+ catChecks.not(this).prop('checked', false)
34
35
  }
35
36
  ).filter(':checked').trigger('change')
36
37
  }
@@ -1,9 +1,3 @@
1
- /*
2
- * decaffeinate suggestions:
3
- * DS102: Remove unnecessary code created because of implicit returns
4
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md
5
- */
6
-
7
1
  /* eslint-env jquery */
8
2
  $(function() {
9
3
  const panel = $('#menu_content')
@@ -21,11 +15,11 @@ $(function() {
21
15
  return
22
16
 
23
17
  panel.find('#menu_reoreder_loading').show()
24
- return $.post(listPanel.attr('data-reorder_url'), data, function(res) {
18
+ $.post(listPanel.attr('data-reorder_url'), data, function(res) {
25
19
  lastData = data
26
20
  panel.find('#menu_reoreder_loading').hide()
27
21
  if (res)
28
- return alert(res)
22
+ alert(res)
29
23
  })
30
24
  })
31
25
 
@@ -33,13 +27,10 @@ $(function() {
33
27
  const saveMenu = function(data) {
34
28
  showLoading()
35
29
 
36
- return $.post(
37
- listPanel.attr('data-url'),
38
- data,
39
- function(res) {
40
- listPanel.children('.dd-list').append($(res).children())
41
- return hideLoading()
42
- })
30
+ $.post(listPanel.attr('data-url'), data, function(res) {
31
+ listPanel.children('.dd-list').append($(res).children())
32
+ hideLoading()
33
+ })
43
34
  }
44
35
 
45
36
  // add menu items (non-external)
@@ -48,7 +39,7 @@ $(function() {
48
39
  let flag = false
49
40
  $(this).closest('.panel').find('input:checkbox:checked').each(function() {
50
41
  flag = true
51
- return data.items.push({ id: $(this).val(), kind: $(this).closest('.class_type').attr('data-type') })
42
+ data.items.push({ id: $(this).val(), kind: $(this).closest('.class_type').attr('data-type') })
52
43
  }).prop('checked', false)
53
44
 
54
45
  if (!flag)
@@ -64,7 +55,7 @@ $(function() {
64
55
  let flag = false
65
56
  $(this).closest('.panel').find('input:checkbox:checked').each(function() {
66
57
  flag = true
67
- return data.custom_items.push({ url: $(this).val(), kind: $(this).attr('data-kind'), label: $(this).attr('data-label') })
58
+ data.custom_items.push({ url: $(this).val(), kind: $(this).attr('data-kind'), label: $(this).attr('data-label') })
68
59
  }).prop('checked', false)
69
60
 
70
61
  if (!flag)
@@ -93,10 +84,10 @@ $(function() {
93
84
  title: link.attr('data-original-title') || link.attr('title'),
94
85
  url: link.attr('href'),
95
86
  mode: 'ajax',
96
- callback(modal) {
87
+ callback: (modal) => {
97
88
  const form = modal.find('form')
98
89
  init_form_validations(form)
99
- return form.submit(function() {
90
+ form.submit(function() {
100
91
  if (!form.valid())
101
92
  return false
102
93
 
@@ -104,7 +95,7 @@ $(function() {
104
95
  $.post(form.attr('action'), form.serialize(), function(res) {
105
96
  link.closest('li').replaceWith($(res).html())
106
97
  modal.modal('hide')
107
- return hideLoading()
98
+ hideLoading()
108
99
  })
109
100
  return false
110
101
  })
@@ -122,39 +113,39 @@ $(function() {
122
113
  showLoading()
123
114
  $.get(link.attr('href'), function() {
124
115
  link.closest('.dd-item').remove()
125
- return hideLoading()
116
+ hideLoading()
126
117
  })
127
118
  return false
128
119
  })
129
120
 
130
121
  // new menu
131
122
  panel.find('.new_menu_link, .edit_menu_link').ajax_modal({
132
- callback(modal) {
123
+ callback: (modal) => {
133
124
  const form = modal.find('form')
134
125
 
135
- return setTimeout(() => init_form_validations(form), 1000)
126
+ setTimeout(() => init_form_validations(form), 1000)
136
127
  }
137
128
  })
138
129
 
139
130
  // menus list - change dropdown
140
- panel.find('#menu_items #switch_nav_menuForm select').change(function() {
131
+ panel.find('#menu_items #switch_nav_menu_form select').change(function() {
141
132
  if (!$(this).val())
142
133
  return
143
134
 
144
- return $(this).closest('form').submit()
135
+ $(this).closest('form').submit()
145
136
  })
146
137
 
147
138
  // custom fields
148
- return listPanel.on('click', '.custom_settings_link', function() {
139
+ listPanel.on('click', '.custom_settings_link', function() {
149
140
  const link = $(this)
150
141
  open_modal({
151
142
  title: link.attr('data-original-title') || link.attr('title'),
152
143
  url: link.attr('href'),
153
144
  mode: 'ajax',
154
- callback(modal) {
145
+ callback: (modal) => {
155
146
  const form = modal.find('form')
156
147
  init_form_validations(form)
157
- return form.submit(function() {
148
+ form.submit(function() {
158
149
  if (!form.valid())
159
150
  return false
160
151
 
@@ -164,7 +155,7 @@ $(function() {
164
155
  alert(res)
165
156
 
166
157
  modal.modal('hide')
167
- return hideLoading()
158
+ hideLoading()
168
159
  })
169
160
  return false
170
161
  })
@@ -6,15 +6,6 @@ module CamaleonCms
6
6
  skip_before_action :verify_authenticity_token, only: :upload, raise: false
7
7
  before_action :init_media_vars, except: :download_private_file
8
8
 
9
- LOCALHOST_DOMAIN_MATCHER = /
10
- localhost|
11
- 127\.0\.0\.1|
12
- 0\.0\.0\.0|
13
- 0x7f\.0x0\.0x0\.0x1| # hex encoding
14
- 0177\.0\.0\.01| # octal encoding
15
- 2130706433 # dword encoding
16
- /x.freeze
17
-
18
9
  # render media section
19
10
  def index
20
11
  authorize! :manage, :media
@@ -71,13 +62,13 @@ module CamaleonCms
71
62
  cama_uploader.delete_file(params[:folder].gsub('//', '/'))
72
63
  render plain: ''
73
64
  when 'crop_url'
74
- unless params[:url].start_with?('data:')
75
- params[:url] = (params[:url].start_with?('http') ? '' : current_site.the_url(locale: nil)) + params[:url]
76
- end
77
- r = if local_url?(params[:url])
78
- { error: t('camaleon_cms.admin.media.local_upload_denied') }
65
+ user_url = params[:url].to_s
66
+ user_url = "#{current_site.the_url(locale: nil)}#{user_url}" unless user_url.start_with?('data:', 'http')
67
+ url_validation_result = UserUrlValidator.validate(user_url)
68
+ r = if url_validation_result.is_a?(Array)
69
+ { error: url_validation_result.join(', ') }
79
70
  else
80
- cama_tmp_upload(params[:url], formats: params[:formats], name: params[:name])
71
+ cama_tmp_upload(user_url, formats: params[:formats], name: params[:name])
81
72
  end
82
73
  if r[:error].present?
83
74
  render plain: helpers.sanitize(r[:error])
@@ -91,10 +82,6 @@ module CamaleonCms
91
82
  end
92
83
  end
93
84
 
94
- def local_url?(url)
95
- url.try :match?, LOCALHOST_DOMAIN_MATCHER
96
- end
97
-
98
85
  # upload files from media uploader
99
86
  def upload(settings = {})
100
87
  params[:dimension] = nil if params[:skip_auto_crop].present?
@@ -70,7 +70,8 @@ module CamaleonCms
70
70
 
71
71
  # send email test
72
72
  def test_email
73
- CamaleonCms::HtmlMailer.sender(params[:email], 'Test', { content: 'Test content' }).deliver_now
73
+ data = { content: 'Test content', current_site: current_site, url_base: cama_root_url }
74
+ CamaleonCms::HtmlMailer.sender(params[:email], 'Test', data).deliver_now
74
75
  head :ok
75
76
  rescue StandardError => e
76
77
  render inline: e.message, status: 502
@@ -29,7 +29,8 @@ module CamaleonCms
29
29
  # return a child category from this category with id (integer) or by slug (string)
30
30
  def the_category(slug_or_id)
31
31
  return object.categories.where(id: slug_or_id).first if slug_or_id.is_a?(Integer)
32
- return object.categories.find_by_slug(slug_or_id) if slug_or_id.is_a?(String)
32
+
33
+ object.categories.find_by_slug(slug_or_id) if slug_or_id.is_a?(String)
33
34
  end
34
35
 
35
36
  # ---------------------
@@ -70,7 +70,8 @@ module CamaleonCms
70
70
  # return a category from this post_type with id (integer) or by slug (string)
71
71
  def the_category(slug_or_id)
72
72
  return the_categories.where(id: slug_or_id).first if slug_or_id.is_a?(Integer)
73
- return the_categories.find_by_slug(slug_or_id) if slug_or_id.is_a?(String)
73
+
74
+ the_categories.find_by_slug(slug_or_id) if slug_or_id.is_a?(String)
74
75
  end
75
76
 
76
77
  # return all post_tags for the post_type (active_record) filtered by permissions + hidden posts + roles + etc...
@@ -80,7 +80,8 @@ module CamaleonCms
80
80
  # slug_or_id: string => return all main categories of the post_type with slug = slug_or_id
81
81
  def the_categories(slug_or_id = nil)
82
82
  return the_post_type(slug_or_id).the_categories if slug_or_id.present?
83
- return object.categories unless slug_or_id.present?
83
+
84
+ object.categories unless slug_or_id.present?
84
85
  end
85
86
 
86
87
  # return the category object with id or slug = slug_or_id from this site
@@ -58,8 +58,11 @@ module CamaleonCms
58
58
  prepend_view_path(Rails.root.join(views_dir).to_s)
59
59
 
60
60
  theme = @current_site.get_theme
61
- lookup_context.prefixes.prepend("themes/#{theme.slug}") if theme.settings['gem_mode']
62
- lookup_context.prefixes.prepend("themes/#{theme.slug}/views") unless theme.settings['gem_mode']
61
+ if theme.settings && theme.settings['gem_mode']
62
+ lookup_context.prefixes.prepend("themes/#{theme.slug}")
63
+ else
64
+ lookup_context.prefixes.prepend("themes/#{theme.slug}/views")
65
+ end
63
66
  lookup_context.use_camaleon_partial_prefixes = true
64
67
  ((data[:files] || []) + (data[:attachments] || [])).each do |attach|
65
68
  if File.exist?(attach) && !File.directory?(attach)
@@ -8,9 +8,10 @@ module CamaleonCms
8
8
  return unless ptype.present? # only for posts that belongs to a post type model
9
9
 
10
10
  posts = ptype.site.posts
11
- .where("(#{slug_array.map do |s|
12
- "#{CamaleonCms::Post.table_name}.slug LIKE '%-->#{s}<!--%'"
13
- end.join(' OR ')} ) OR #{CamaleonCms::Post.table_name}.slug = ?", record.slug)
11
+ .where(
12
+ "(#{slug_array.map { |s| "#{CamaleonCms::Post.table_name}.slug LIKE '%-->#{s}<!--%'" }
13
+ .join(' OR ')} ) OR #{CamaleonCms::Post.table_name}.slug = ?", record.slug
14
+ )
14
15
  .where.not(id: record.id)
15
16
  .where.not(status: %i[draft draft_child trash])
16
17
  if posts.size.positive?
@@ -0,0 +1,207 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2011-present GitLab B.V.
4
+ #
5
+ # See https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/url_blocker.rb
6
+ #
7
+ # Portions of this software are licensed under the "MIT Expat" license as defined below.
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ # of this software and associated documentation files (the "Software"), to deal
11
+ # in the Software without restriction, including without limitation the rights
12
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ # copies of the Software, and to permit persons to whom the Software is
14
+ # furnished to do so, subject to the following conditions:
15
+ #
16
+ # The above copyright notice and this permission notice shall be included in all
17
+ # copies or substantial portions of the Software.
18
+ #
19
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ # SOFTWARE.
26
+
27
+ # require 'resolv'
28
+ require 'ipaddress'
29
+ require 'addressable/uri'
30
+
31
+ module CamaleonCms
32
+ class UserUrlValidator
33
+ LOCAL_IPS = %w[0.0.0.0 ::].freeze
34
+
35
+ def self.validate(...)
36
+ new.validate(...)
37
+ end
38
+
39
+ def initialize
40
+ @errors = []
41
+ end
42
+
43
+ # Validates the given url according to the constraints specified by the received arguments.
44
+ #
45
+ # allow_localhost - Registers error if URL resolves to a localhost IP address and argument is false.
46
+ # allow_local_network - Registers error if URL resolves to a link-local address and argument is false.
47
+ # enforce_user - Registers error if URL user doesn't start with alphanumeric characters and argument is true.
48
+ # enforce_sanitizing - Registers error if URL includes any HTML/CSS/JS tags and argument is true.
49
+ #
50
+ # Returns an array with [<uri>, <original-hostname>].
51
+ def validate(url, allow_localhost: false, allow_local_network: false, enforce_user: true, enforce_sanitizing: true)
52
+ return invalid_url unless url.present?
53
+
54
+ # Param url can be a string, URI or Addressable::URI
55
+ return invalid_url unless (uri = parse_url(url))
56
+
57
+ validate_uri(uri: uri, enforce_sanitizing: enforce_sanitizing, enforce_user: enforce_user)
58
+ return @errors if @errors.any?
59
+
60
+ address_info = get_address_info(uri)
61
+ return @errors if @errors.any?
62
+
63
+ validate_local_request(
64
+ address_info: address_info,
65
+ allow_localhost: allow_localhost,
66
+ allow_local_network: allow_local_network
67
+ )
68
+
69
+ @errors.empty? || @errors
70
+ end
71
+
72
+ private
73
+
74
+ def validate_uri(uri:, enforce_sanitizing:, enforce_user:)
75
+ validate_html_tags(uri) if enforce_sanitizing
76
+
77
+ validate_user(uri.user) if enforce_user
78
+ validate_hostname(uri.hostname)
79
+ end
80
+
81
+ # @param uri [Addressable::URI]
82
+ # @return [Array<Addrinfo>] addrinfo object for the URI
83
+ def get_address_info(uri)
84
+ Addrinfo.getaddrinfo(uri.hostname, get_port(uri), nil, :STREAM).map do |addr|
85
+ addr.ipv6_v4mapped? ? addr.ipv6_to_ipv4 : addr
86
+ end
87
+ rescue ArgumentError => e
88
+ # Addrinfo.getaddrinfo errors if the domain exceeds 1024 characters.
89
+ @errors << I18n.t('camaleon_cms.admin.validate.hostname_long') if e.message.include?('hostname too long')
90
+
91
+ @errors << "#{e.message}: #{I18n.t('camaleon_cms.admin.validate.url')}" if @errors.blank?
92
+ rescue SocketError
93
+ @errors << I18n.t('camaleon_cms.admin.validate.host_invalid')
94
+ end
95
+
96
+ def validate_local_request(address_info:, allow_localhost:, allow_local_network:)
97
+ return if allow_local_network && allow_localhost
98
+
99
+ unless allow_localhost
100
+ validate_localhost(address_info)
101
+ validate_loopback(address_info)
102
+ end
103
+
104
+ return if allow_local_network
105
+
106
+ validate_local_network(address_info)
107
+ validate_link_local(address_info)
108
+ validate_shared_address(address_info)
109
+ validate_limited_broadcast_address(address_info)
110
+ end
111
+
112
+ def get_port(uri)
113
+ uri.port || uri.default_port
114
+ end
115
+
116
+ def validate_html_tags(uri)
117
+ uri_str = uri.to_s
118
+ sanitized_uri = ActionController::Base.helpers.sanitize(uri_str, tags: [])
119
+ @errors << I18n.t('camaleon_cms.admin.validate.html_tags') unless sanitized_uri == uri_str
120
+ end
121
+
122
+ # @param [String, Addressable::URI, #to_str] url The URL string to parse
123
+ # @return [Addressable::URI, nil] URI object based on the parsed string, or `nil` if the `url` is invalid
124
+ def parse_url(url)
125
+ invalid = nil
126
+ uri = Addressable::URI.parse(url).tap do |parsed_url|
127
+ invalid = true if multiline_blocked?(parsed_url)
128
+ end
129
+ return if invalid
130
+
131
+ uri
132
+ rescue Addressable::URI::InvalidURIError, URI::InvalidURIError
133
+ nil
134
+ end
135
+
136
+ def multiline_blocked?(parsed_url)
137
+ url = parsed_url.to_s
138
+
139
+ return true if url =~ /[\n\r]/
140
+ # Google Cloud Storage uses a multi-line, encoded Signature query string
141
+ return false if %w[http https].include?(parsed_url.scheme&.downcase)
142
+
143
+ CGI.unescape(url) =~ /[\n\r]/
144
+ end
145
+
146
+ def validate_user(value)
147
+ return if value.blank?
148
+ return if value =~ /\A\p{Alnum}/
149
+
150
+ @errors << I18n.t('camaleon_cms.admin.validate.username_alphanumeric')
151
+ end
152
+
153
+ def validate_hostname(value)
154
+ return if value.blank?
155
+ return if IPAddress.valid?(value)
156
+ return if value =~ /\A\p{Alnum}/
157
+
158
+ @errors << I18n.t('camaleon_cms.admin.validate.host_or_ip_invalid')
159
+ end
160
+
161
+ def validate_localhost(addrs_info)
162
+ return if (Socket.ip_address_list.map(&:ip_address).concat(LOCAL_IPS) & addrs_info.map(&:ip_address)).empty?
163
+
164
+ @errors << I18n.t('camaleon_cms.admin.validate.no_localhost_requests')
165
+ end
166
+
167
+ def validate_loopback(addrs_info)
168
+ return unless addrs_info.any? { |addr| addr.ipv4_loopback? || addr.ipv6_loopback? }
169
+
170
+ @errors << I18n.t('camaleon_cms.admin.validate.no_loopback_requests')
171
+ end
172
+
173
+ def validate_local_network(addrs_info)
174
+ return unless addrs_info.any? { |addr| addr.ipv4_private? || addr.ipv6_sitelocal? || addr.ipv6_unique_local? }
175
+
176
+ @errors << I18n.t('camaleon_cms.admin.validate.no_local_net_requests')
177
+ end
178
+
179
+ def validate_link_local(addrs_info)
180
+ netmask = IPAddr.new('169.254.0.0/16')
181
+ return unless addrs_info.any? { |addr| addr.ipv6_linklocal? || netmask.include?(addr.ip_address) }
182
+
183
+ @errors << I18n.t('camaleon_cms.admin.validate.no_link_local_net_requests')
184
+ end
185
+
186
+ def validate_shared_address(addrs_info)
187
+ netmask = IPAddr.new('100.64.0.0/10')
188
+ return unless addrs_info.any? { |addr| netmask.include?(addr.ip_address) }
189
+
190
+ @errors << I18n.t('camaleon_cms.admin.validate.no_shared_address_requests')
191
+ end
192
+
193
+ # Registers an error if any IP in `addrs_info` is the limited broadcast address.
194
+ # https://datatracker.ietf.org/doc/html/rfc919#section-7
195
+ def validate_limited_broadcast_address(addrs_info)
196
+ blocked_ips = ['255.255.255.255']
197
+
198
+ return if (blocked_ips & addrs_info.map(&:ip_address)).empty?
199
+
200
+ @errors << I18n.t('camaleon_cms.admin.validate.no_limited_broadcast_address_requests')
201
+ end
202
+
203
+ def invalid_url
204
+ @errors << I18n.t('camaleon_cms.admin.validate.url')
205
+ end
206
+ end
207
+ end
@@ -212,7 +212,6 @@ en:
212
212
  reload: 'Reload'
213
213
  clear_cache: 'Clear Cache'
214
214
  name_required: 'File name is required'
215
- local_upload_denied: 'Cannot upload from localhost'
216
215
  menus:
217
216
  menus: Menus
218
217
  link_url: 'Link URL'
@@ -684,6 +683,16 @@ en:
684
683
  required: 'This field is required.'
685
684
  remote: 'Please fix this field.'
686
685
  email: 'Please enter a valid email address.'
686
+ host_invalid: 'Host cannot be resolved or invalid'
687
+ host_or_ip_invalid: 'Hostname or IP address invalid'
688
+ hostname_long: 'Host is too long (maximum is 1024 characters)'
689
+ html_tags: 'HTML/CSS/JS tags are not allowed'
690
+ no_localhost_requests: 'Requests to localhost are not allowed'
691
+ no_loopback_requests: 'Requests to loopback addresses are not allowed'
692
+ no_local_net_requests: 'Requests to the local network are not allowed'
693
+ no_link_local_net_requests: 'Requests to the link local network are not allowed'
694
+ no_shared_address_requests: 'Requests to the shared address space are not allowed'
695
+ no_limited_broadcast_address_requests: 'Requests to the limited broadcast address are not allowed'
687
696
  url: 'Please enter a valid URL.'
688
697
  date: 'Please enter a valid date.'
689
698
  dateiso: 'Please enter a valid date ( ISO ).'
@@ -697,6 +706,7 @@ en:
697
706
  range: 'Please enter a value between {0} and {1}.'
698
707
  max: 'Please enter a value less than or equal to {0}.'
699
708
  min: 'Please enter a value greater than or equal to {0}.'
709
+ username_alphanumeric: 'Username needs to start with an alphanumeric character'
700
710
  widgets:
701
711
  create_widget: 'Create Widget'
702
712
  create_sidebar: 'Create Sidebar'
@@ -1,3 +1,3 @@
1
1
  module CamaleonCms
2
- VERSION = '2.7.4'.freeze
2
+ VERSION = '2.7.5'.freeze
3
3
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: camaleon_cms
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.4
4
+ version: 2.7.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Owen Peredo Diaz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-11 00:00:00.000000000 Z
11
+ date: 2023-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: addressable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bcrypt
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -86,6 +100,20 @@ dependencies:
86
100
  - - ">="
87
101
  - !ruby/object:Gem::Version
88
102
  version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: ipaddress
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :runtime
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
89
117
  - !ruby/object:Gem::Dependency
90
118
  name: jquery-rails
91
119
  requirement: !ruby/object:Gem::Requirement
@@ -766,6 +794,7 @@ files:
766
794
  - app/uploaders/camaleon_cms_uploader.rb
767
795
  - app/validators/camaleon_cms/post_uniq_validator.rb
768
796
  - app/validators/camaleon_cms/uniq_validator.rb
797
+ - app/validators/camaleon_cms/user_url_validator.rb
769
798
  - app/views/camaleon_cms/404.html.erb
770
799
  - app/views/camaleon_cms/500.html.erb
771
800
  - app/views/camaleon_cms/_flash_messages.html.erb
@@ -1103,7 +1132,7 @@ requirements:
1103
1132
  - rails >= 6.0
1104
1133
  - ruby >= 2.7
1105
1134
  - imagemagick
1106
- rubygems_version: 3.4.10
1135
+ rubygems_version: 3.5.1
1107
1136
  signing_key:
1108
1137
  specification_version: 4
1109
1138
  summary: Camaleon is a CMS for Ruby on Rails as an alternative to Wordpress.