CVE-2023-0569 (published 2023-01-29T17:15Z)

Weak Password Requirements in GitHub repository publify/publify prior to 9.2.10.

CVSS v3 base score: 6.5, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-521
cwe_name: Weak Password Requirements
description: The product does not require that users should have strong passwords, which makes it easier for attackers to compromise user accounts.
extended_description: Authentication mechanisms often rely on a memorized secret (also known as a password) to provide an assertion of identity for a user of a system. It is therefore important that this password be of sufficient complexity and impractical for an adversary to guess. The specific requirements around how complex a password needs to be depends on the type of system being protected. Selecting the correct password requirements and enforcing them through implementation are critical to the overall success of the authentication mechanism.
         

Fix information:
repo_url: https://github.com/publify/publify
commit_details: repo_url: https://github.com/publify/publify
author: Matijs van Zuijlen
author_date: 2022-08-19 23:41:41+02:00
msg: Require user passwords to be strong

This adds validation of passwords using the zxcvbn password strength
estimation gem.
repository: repo_url: https://github.com/publify/publify
repo_name: publify/publify
description: A self hosted Web publishing platform on Rails.
repo_language: Less
owner: publify
file_changes: filename: user.rb
change_type: ModificationType.MODIFY
diff: @@ -12,7 +12,7 @@ class User < ApplicationRecord
   # Include default devise modules. Others available are:
   # :confirmable, :lockable, :timeoutable and :omniauthable
   devise :database_authenticatable, :registerable,
-         :recoverable, :rememberable, :trackable, :validatable
+         :recoverable, :rememberable, :trackable, :validatable, :zxcvbnable
   include ConfigManager
   include StringLengthLimit
 

programming_language: Ruby
filename: publify_core.rb
change_type: ModificationType.MODIFY
diff: @@ -2,6 +2,7 @@
 
 require "devise"
 require "devise-i18n"
+require "devise_zxcvbn"
 
 require "publify_core/version"
 require "publify_core/engine"

programming_language: Ruby
filename: factories.rb
change_type: ModificationType.MODIFY
diff: @@ -21,7 +21,7 @@ FactoryBot.define do
     notify_via_email { false }
     notify_on_new_articles { false }
     notify_on_comments { false }
-    password { "top-secret" }
+    password { "top-Secret12!$#" }
     state { "active" }
     profile { User::CONTRIBUTOR }
 

programming_language: Ruby
filename: publify_core.gemspec
change_type: ModificationType.MODIFY
diff: @@ -28,6 +28,7 @@ Gem::Specification.new do |s|
   s.add_dependency "carrierwave", "~> 2.0"
   s.add_dependency "devise", "~> 4.7.1"
   s.add_dependency "devise-i18n", "~> 1.2"
+  s.add_dependency "devise_zxcvbn", "~> 6.0"
   s.add_dependency "dynamic_form", "~> 1.1.4"
   s.add_dependency "fog-aws", "~> 3.2"
   s.add_dependency "fog-core", "~> 2.2"

programming_language: Ruby
filename: users_controller_spec.rb
change_type: ModificationType.MODIFY
diff: @@ -7,6 +7,7 @@ describe Admin::UsersController, type: :controller do
   let(:admin) { create(:user, :as_admin) }
   let(:publisher) { create(:user, :as_publisher) }
   let(:contributor) { create(:user, :as_contributor) }
+  let(:strong_password) { "fhnehnhfiiuh" }
 
   render_views
 
@@ -48,8 +49,8 @@ describe Admin::UsersController, type: :controller do
     before do
       sign_in admin
       post :create, params: { user: { login: "errand", email: "corey@test.com",
-                                      password: "testpass",
-                                      password_confirmation: "testpass",
+                                      password: strong_password,
+                                      password_confirmation: strong_password,
                                       profile: User::CONTRIBUTOR,
                                       text_filter_name: "markdown",
                                       nickname: "fooo", firstname: "bar" } }
@@ -70,8 +71,9 @@ describe Admin::UsersController, type: :controller do
     it "redirects to index" do
       post :update, params: { id: contributor.id,
                               user: { login: "errand",
-                                      email: "corey@test.com", password: "testpass",
-                                      password_confirmation: "testpass" } }
+                                      email: "corey@test.com",
+                                      password: strong_password,
+                                      password_confirmation: strong_password } }
       expect(response).to redirect_to(action: "index")
     end
 

programming_language: Ruby
filename: setup_controller_spec.rb
change_type: ModificationType.MODIFY
diff: @@ -3,6 +3,8 @@
 require "rails_helper"
 
 describe SetupController, type: :controller do
+  let(:strong_password) { "fhnehnhfiiuh" }
+
   describe "#index" do
     describe "when no blog is configured" do
       before do
@@ -35,7 +37,7 @@ describe SetupController, type: :controller do
         before do
           ActionMailer::Base.deliveries.clear
           post :create, params: { setting: { blog_name: "Foo", email: "foo@bar.net",
-                                             password: "foo123bar" } }
+                                             password: strong_password } }
         end
 
         it "correctly initializes blog and users" do
@@ -64,13 +66,13 @@ describe SetupController, type: :controller do
       describe "when passing incorrect parameters" do
         it "empty blog name should raise an error" do
           post :create, params: { setting: { blog_name: "", email: "foo@bar.net",
-                                             password: "foobar123" } }
+                                             password: strong_password } }
           expect(response).to redirect_to(action: "index")
         end
 
         it "empty email should raise an error" do
           post :create, params: { setting: { blog_name: "Foo", email: "",
-                                             password: "foobar123" } }
+                                             password: strong_password } }
           expect(response).to redirect_to(action: "index")
         end
 
@@ -79,6 +81,12 @@ describe SetupController, type: :controller do
                                              password: "" } }
           expect(response).to redirect_to(action: "index")
         end
+
+        it "weak password should raise an error" do
+          post :create, params: { setting: { blog_name: "Foo", email: "foo@bar.net",
+                                             password: "foo123bar" } }
+          expect(response).to redirect_to(action: "index")
+        end
       end
     end
 

programming_language: Ruby
filename: login_spec.rb
change_type: ModificationType.MODIFY
diff: @@ -3,19 +3,21 @@
 require "rails_helper"
 
 RSpec.feature "Logging in", type: :feature do
+  let(:strong_password) { "fhnehnhfiiuh" }
+
   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/"
-    create :user, :as_admin, login: "admin", password: "a-secret"
+    create :user, :as_admin, login: "admin", password: strong_password
   end
 
   scenario "Admin logs in" do
     visit "/admin"
     fill_in :user_login, with: "admin"
-    fill_in :user_password, with: "a-secret"
+    fill_in :user_password, with: strong_password
 
     click_button I18n.t("devise.sessions.new.sign_in")
 

programming_language: Ruby
filename: reset_password_spec.rb
change_type: ModificationType.MODIFY
diff: @@ -6,7 +6,7 @@ RSpec.feature "Logging in", type: :feature do
   before do
     load Rails.root.join("db/seeds.rb")
     Blog.first.update blog_name: "Awesome!", base_url: "http://www.example.com/"
-    create :user, :as_admin, login: "admin", password: "forget-me"
+    create :user, :as_admin, login: "admin", password: "Fo_rgEt-1m5e2"
   end
 
   scenario "Admin resets password" do
@@ -23,8 +23,8 @@ RSpec.feature "Logging in", type: :feature do
 
     visit url
 
-    fill_in :user_password, with: "a-secret"
-    fill_in :user_password_confirmation, with: "a-secret"
+    fill_in :user_password, with: "a5-SeCre1T4"
+    fill_in :user_password_confirmation, with: "a5-SeCre1T4"
 
     click_button I18n.t("devise.passwords.edit.change_my_password")
     expect(page).to have_text I18n.t("devise.passwords.updated")

programming_language: Ruby
filename: setup_spec.rb
change_type: ModificationType.MODIFY
diff: @@ -3,6 +3,8 @@
 require "rails_helper"
 
 RSpec.feature "Blog setup", type: :feature do
+  let(:strong_password) { "fhnehnhfiiuh" }
+
   before do
     stub_request(:get,
                  "http://www.google.com/search?output=rss&q=link:www.example.com&tbm=blg").
@@ -18,7 +20,7 @@ RSpec.feature "Blog setup", type: :feature do
     # Set up the blog
     fill_in :setting_blog_name, with: "Awesome blog"
     fill_in :setting_email, with: "foo@bar.com"
-    fill_in :setting_password, with: "test1234"
+    fill_in :setting_password, with: strong_password
     click_button I18n.t!("generic.save")
 
     # Confirm set up success
@@ -38,7 +40,7 @@ RSpec.feature "Blog setup", type: :feature do
 
     visit admin_dashboard_path
     fill_in :user_login, with: "admin"
-    fill_in :user_password, with: "test1234"
+    fill_in :user_password, with: strong_password
     find("input[type=submit]").click
     expect(page).to have_text I18n.t!("admin.dashboard.index.welcome_back",
                                       user_name: "admin")

programming_language: Ruby
filename: signup_spec.rb
change_type: ModificationType.MODIFY
diff: @@ -3,12 +3,14 @@
 require "rails_helper"
 
 RSpec.feature "Signing up", type: :feature do
+  let(:strong_password) { "whhefwhfwefhiu" }
+
   before do
     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"
+    create :user, :as_admin, login: "admin"
   end
 
   scenario "User signs up for an account" do
@@ -18,8 +20,8 @@ RSpec.feature "Signing up", type: :feature do
     # Create account
     fill_in :user_login, with: "hello"
     fill_in :user_email, with: "hello@hello.com"
-    fill_in :user_password, with: "hush-hush"
-    fill_in :user_password_confirmation, with: "hush-hush"
+    fill_in :user_password, with: strong_password
+    fill_in :user_password_confirmation, with: strong_password
     click_button I18n.t!("devise.registrations.new.sign_up")
 
     # Confirm creation success
@@ -32,7 +34,7 @@ RSpec.feature "Signing up", type: :feature do
     # Confirm ability to sign in
     visit admin_dashboard_path
     fill_in :user_login, with: "hello"
-    fill_in :user_password, with: "hush-hush"
+    fill_in :user_password, with: strong_password
     find("input[type=submit]").click
     expect(page).to have_text I18n.t!("devise.sessions.signed_in")
 

programming_language: Ruby
filename: user_spec.rb
change_type: ModificationType.MODIFY
diff: @@ -98,6 +98,11 @@ describe User, type: :model do
 
       expect(bar).not_to allow_value("foo@foo.com").for(:email)
     end
+
+    it "requires a strong password" do
+      nearly_valid_user = build(:user)
+      expect(nearly_valid_user).not_to allow_value("password01").for(:password)
+    end
   end
 
   describe "#initialize" do

programming_language: Ruby