CVE-2021-25973: Incorrect Resource Transfer Between Spheres (CWE-669)
value: In Publify, 9.0.0.pre1 to 9.2.4 are vulnerable to Improper Access Control. “guest” role users can self-register even when the admin does not allow. This happens due to front-end restriction only.
CWE description: The product does not properly transfer a resource/behavior to another sphere, or improperly imports a resource/behavior from another sphere, in a manner that provides unintended control over that resource.
CVSS v3 base score: 6.5
Published: 2021-11-02T07:15Z
Repository: https://github.com/publify/publify
Language: Ruby
Files changed: publify_core/Manifest.txt, publify_core/app/controllers/users/registrations_controller.rb, publify_core/config/routes.rb, publify_core/spec/features/signup_spec.rb
Commit date: 2021-10-10 15:08:32 +0200
Commit message: Disallow registration rather than hiding it

Previously, the allow_signup setting only hid the signup link from the
login page. With this change, the registration functionality itself is
also disabled.

Diff: diff --git a/publify_core/Manifest.txt b/publify_core/Manifest.txt
index cf17a52b7..34492c696 100644
--- a/publify_core/Manifest.txt
+++ b/publify_core/Manifest.txt
@@ -140,10 +140,11 @@ app/controllers/notes_controller.rb
 app/controllers/setup_controller.rb
 app/controllers/tags_controller.rb
 app/controllers/text_controller.rb
 app/controllers/textfilter_controller.rb
 app/controllers/theme_controller.rb
+app/controllers/users/registrations_controller.rb
 app/controllers/xml_controller.rb
 app/helpers/admin/base_helper.rb
 app/helpers/admin/feedback_helper.rb
 app/helpers/articles_helper.rb
 app/helpers/authors_helper.rb
diff --git a/publify_core/app/controllers/users/registrations_controller.rb b/publify_core/app/controllers/users/registrations_controller.rb
new file mode 100644
index 000000000..22711bfd7
--- /dev/null
+++ b/publify_core/app/controllers/users/registrations_controller.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class Users::RegistrationsController < Devise::RegistrationsController
+  include BlogHelper
+  before_action :require_signup_allowed
+
+  private
+
+  def require_signup_allowed
+    render plain: "Not found", status: :not_found unless this_blog.allow_signup?
+  end
+end
diff --git a/publify_core/config/routes.rb b/publify_core/config/routes.rb
index 3224c1a51..bf3215f5b 100644
--- a/publify_core/config/routes.rb
+++ b/publify_core/config/routes.rb
@@ -1,9 +1,10 @@
 # frozen_string_literal: true
 
 Rails.application.routes.draw do
-  devise_for :users
+  devise_for :users, controllers: { registrations: "users/registrations" }
+
   # TODO: use only in archive sidebar. See how made other system
   get ":year/:month", to: "articles#index", year: /\d{4}/, month: /\d{1,2}/,
                       as: "articles_by_month", format: false
   get ":year/:month/page/:page", to: "articles#index", year: /\d{4}/, month: /\d{1,2}/,
                                  as: "articles_by_month_page", format: false
diff --git a/publify_core/spec/features/signup_spec.rb b/publify_core/spec/features/signup_spec.rb
index ec89fcb2e..5265f9873 100644
--- a/publify_core/spec/features/signup_spec.rb
+++ b/publify_core/spec/features/signup_spec.rb
@@ -2,13 +2,10 @@
 
 require "rails_helper"
 
 RSpec.feature "Signing up", type: :feature do
   before do
-    stub_request(:get,
-                 "http://www.google.com/search?output=rss&q=link:www.example.com&tbm=blg").
-      to_return(status: 200, body: "", headers: {})
     load Rails.root.join("db/seeds.rb")
     Blog.first.update(blog_name: "Awesome!",
                       base_url: "http://www.example.com/",
                       allow_signup: 1)
     create :user, :as_admin, login: "admin", password: "a-secret"
@@ -38,8 +35,20 @@ RSpec.feature "Signing up", type: :feature do
     fill_in :user_password, with: "hush-hush"
     find("input[type=submit]").click
     expect(page).to have_text I18n.t!("devise.sessions.signed_in")
 
     # Confirm proper setting fo user properties
-    expect(User.last.email).to eq "hello@hello.com"
+    u = User.last
+    expect(u.email).to eq "hello@hello.com"
+    expect(u.profile).to eq "contributor"
+  end
+
+  scenario "Disallow account sign-up link with a blog setting" do
+    Blog.first.update(allow_signup: 0)
+    visit admin_dashboard_path
+    expect(page).not_to have_link I18n.t("accounts.create_account")
+
+    visit new_user_registration_path
+
+    expect(page.status_code).to eq 404
   end
 end

Vulnerable code:   devise_for :users
    stub_request(:get,
                 "http://www.google.com/search?output=rss&q=link:www.example.com&tbm=blg").
      to_return(status: 200, body: "", headers: {})
    expect(User.last.email).to eq "hello@hello.com"
Fixed code: app/controllers/users/registrations_controller.rb
# frozen_string_literal: true

class Users::RegistrationsController < Devise::RegistrationsController
  include BlogHelper
  before_action :require_signup_allowed

  private

  def require_signup_allowed
    render plain: "Not found", status: :not_found unless this_blog.allow_signup?
  end
end
  devise_for :users, controllers: { registrations: "users/registrations" }

    u = User.last
    expect(u.email).to eq "hello@hello.com"
    expect(u.profile).to eq "contributor"
  end

  scenario "Disallow account sign-up link with a blog setting" do
    Blog.first.update(allow_signup: 0)
    visit admin_dashboard_path
    expect(page).not_to have_link I18n.t("accounts.create_account")

    visit new_user_registration_path

    expect(page.status_code).to eq 404