VYPR
Critical severityNVD Advisory· Published Jan 14, 2023· Updated Apr 7, 2025

Improper Input Validation in publify/publify

CVE-2023-0299

Description

Improper Input Validation in GitHub repository publify/publify prior to 9.2.10.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
publify_coreRubyGems
< 9.2.109.2.10

Affected products

1

Patches

2
ca46da283572

Validate lengths of string attributes

https://github.com/publify/publifyMatijs van ZuijlenAug 14, 2022via ghsa
20 files changed · +177 2
  • publify_core/app/models/blog.rb+3 0 modified
    @@ -9,6 +9,8 @@
     #
     class Blog < ApplicationRecord
       include ConfigManager
    +  include StringLengthLimit
    +
       include Rails.application.routes.url_helpers
     
       has_many :contents
    @@ -139,6 +141,7 @@ class Blog < ApplicationRecord
     
       validate :permalink_has_identifier
       # validates :base_url, presence: true
    +  validates_default_string_length :base_url
     
       # Find the Blog that matches a specific base URL. If no Blog object is found
       # that matches, then grab the first blog. If *that* fails, then create a new
    
  • publify_core/app/models/concerns/string_length_limit.rb+17 0 added
    @@ -0,0 +1,17 @@
    +# frozen_string_literal: true
    +
    +module StringLengthLimit
    +  # Default string length limit for model attributes. When running on MySQL,
    +  # this is equal to the default string length in the database as set by Rails.
    +  STRING_LIMIT = 255
    +
    +  extend ActiveSupport::Concern
    +
    +  class_methods do
    +    def validates_default_string_length(*names)
    +      names.each do |name|
    +        validates name, length: { maximum: STRING_LIMIT }
    +      end
    +    end
    +  end
    +end
    
  • publify_core/app/models/content.rb+4 0 modified
    @@ -5,6 +5,7 @@
     
     class Content < ApplicationRecord
       include ContentBase
    +  include StringLengthLimit
     
       belongs_to :user, optional: true, touch: true
       belongs_to :blog
    @@ -38,6 +39,9 @@ class Content < ApplicationRecord
     
       serialize :whiteboard
     
    +  validates_default_string_length :title, :author, :permalink, :name,
    +                                  :post_type, :text_filter_name
    +
       def author=(user)
         if user.respond_to?(:login)
           self[:author] = user.login
    
  • publify_core/app/models/feedback.rb+6 0 modified
    @@ -10,10 +10,16 @@ class Feedback < ApplicationRecord
     
       include PublifyGuid
       include ContentBase
    +  include StringLengthLimit
     
       validate :feedback_allowed, on: :create
       validates :article, presence: true
     
    +  validates_default_string_length :title, :author, :email, :url, :blog_name,
    +                                  :user_agent, :text_filter_name
    +
    +  validates :ip, length: { maximum: 40 }
    +
       before_save :correct_url, :classify_content
       before_create :create_guid
     
    
  • publify_core/app/models/ping.rb+3 0 modified
    @@ -1,5 +1,8 @@
     # frozen_string_literal: true
     
     class Ping < ApplicationRecord
    +  include StringLengthLimit
    +
       belongs_to :article
    +  validates_default_string_length :url
     end
    
  • publify_core/app/models/post_type.rb+4 0 modified
    @@ -1,9 +1,13 @@
     # frozen_string_literal: true
     
     class PostType < ApplicationRecord
    +  include StringLengthLimit
    +
       validates :name, uniqueness: true
       validates :name, presence: true
       validate :name_is_not_read
    +  validates_default_string_length :name, :permalink, :description
    +
       before_save :sanitize_title
     
       def name_is_not_read
    
  • publify_core/app/models/redirect.rb+4 0 modified
    @@ -1,13 +1,17 @@
     # frozen_string_literal: true
     
     class Redirect < ApplicationRecord
    +  include StringLengthLimit
    +
       belongs_to :content, optional: true, touch: true
       belongs_to :blog
     
       validates :from_path, uniqueness: true
       validates :to_path, presence: true
       validates :blog, presence: true
     
    +  validates_default_string_length :from_path, :to_path
    +
       def full_to_path
         path = to_path
         # FIXME: Unify HTTP URI matchers
    
  • publify_core/app/models/resource.rb+3 0 modified
    @@ -4,9 +4,12 @@
     require "carrierwave/orm/activerecord"
     
     class Resource < ApplicationRecord
    +  include StringLengthLimit
       belongs_to :blog
       belongs_to :content, optional: true
     
       mount_uploader :upload, ResourceUploader
       validates :upload, presence: true
    +
    +  validates_default_string_length :mime
     end
    
  • publify_core/app/models/tag.rb+3 0 modified
    @@ -1,12 +1,15 @@
     # frozen_string_literal: true
     
     class Tag < ApplicationRecord
    +  include StringLengthLimit
    +
       belongs_to :blog
       has_and_belongs_to_many :contents, order: "created_at DESC"
     
       validates :name, uniqueness: { scope: :blog_id }
       validates :blog, presence: true
       validates :name, presence: true
    +  validates_default_string_length :display_name
     
       before_validation :ensure_naming_conventions
     
    
  • publify_core/app/models/user.rb+2 0 modified
    @@ -14,12 +14,14 @@ class User < ApplicationRecord
       devise :database_authenticatable, :registerable,
              :recoverable, :rememberable, :trackable, :validatable
       include ConfigManager
    +  include StringLengthLimit
     
       before_validation :set_default_profile
     
       validates :login, uniqueness: true
       validates :email, :login, presence: true
       validates :login, length: { in: 3..40 }
    +  validates_default_string_length :email, :text_filter_name
     
       belongs_to :resource, optional: true
       has_many :notifications, foreign_key: "notify_user_id"
    
  • publify_core/Manifest.txt+1 0 modified
    @@ -160,6 +160,7 @@ app/models/article.rb
     app/models/article/factory.rb
     app/models/blog.rb
     app/models/comment.rb
    +app/models/concerns/string_length_limit.rb
     app/models/config_manager.rb
     app/models/content.rb
     app/models/content_base.rb
    
  • publify_core/spec/models/blog_spec.rb+4 0 modified
    @@ -113,6 +113,10 @@
       describe "validations" do
         let(:blog) { described_class.new }
     
    +    it "requires base url to not be too long" do
    +      expect(blog).to validate_length_of(:base_url).is_at_most(255)
    +    end
    +
         it "requires blog name to not be too long" do
           expect(blog).to validate_length_of(:blog_name).is_at_most(256)
         end
    
  • publify_core/spec/models/content_spec.rb+28 0 modified
    @@ -144,4 +144,32 @@
           it { expect(content.author_name).to eq(author.login) }
         end
       end
    +
    +  describe "validations" do
    +    let(:content) { described_class.new }
    +
    +    it "requires title to not be too long" do
    +      expect(content).to validate_length_of(:title).is_at_most(255)
    +    end
    +
    +    it "requires author to not be too long" do
    +      expect(content).to validate_length_of(:author).is_at_most(255)
    +    end
    +
    +    it "requires permalink to not be too long" do
    +      expect(content).to validate_length_of(:permalink).is_at_most(255)
    +    end
    +
    +    it "requires name to not be too long" do
    +      expect(content).to validate_length_of(:name).is_at_most(255)
    +    end
    +
    +    it "requires post_type to not be too long" do
    +      expect(content).to validate_length_of(:post_type).is_at_most(255)
    +    end
    +
    +    it "requires text_filter_name to not be too long" do
    +      expect(content).to validate_length_of(:text_filter_name).is_at_most(255)
    +    end
    +  end
     end
    
  • publify_core/spec/models/feedback_spec.rb+36 0 modified
    @@ -178,4 +178,40 @@ def classify
           assert !@comment.published?
         end
       end
    +
    +  describe "validations" do
    +    let(:feedback) { described_class.new }
    +
    +    it "requires title to not be too long" do
    +      expect(feedback).to validate_length_of(:title).is_at_most(255)
    +    end
    +
    +    it "requires author to not be too long" do
    +      expect(feedback).to validate_length_of(:author).is_at_most(255)
    +    end
    +
    +    it "requires email to not be too long" do
    +      expect(feedback).to validate_length_of(:email).is_at_most(255)
    +    end
    +
    +    it "requires url to not be too long" do
    +      expect(feedback).to validate_length_of(:url).is_at_most(255)
    +    end
    +
    +    it "requires ip to not be too long" do
    +      expect(feedback).to validate_length_of(:ip).is_at_most(40)
    +    end
    +
    +    it "requires blog_name to not be too long" do
    +      expect(feedback).to validate_length_of(:blog_name).is_at_most(255)
    +    end
    +
    +    it "requires user_agent to not be too long" do
    +      expect(feedback).to validate_length_of(:user_agent).is_at_most(255)
    +    end
    +
    +    it "requires text_filter_name to not be too long" do
    +      expect(feedback).to validate_length_of(:text_filter_name).is_at_most(255)
    +    end
    +  end
     end
    
  • publify_core/spec/models/ping_spec.rb+7 0 modified
    @@ -3,4 +3,11 @@
     require "rails_helper"
     
     describe Ping, type: :model do
    +  describe "validations" do
    +    let(:ping) { described_class.new }
    +
    +    it "requires url to not be too long" do
    +      expect(ping).to validate_length_of(:url).is_at_most(255)
    +    end
    +  end
     end
    
  • publify_core/spec/models/post_type_spec.rb+16 0 modified
    @@ -29,4 +29,20 @@
         expect(test_type).not_to be_valid
         expect(test_type.errors[:name]).to eq(["has already been taken"])
       end
    +
    +  describe "validations" do
    +    let(:post_type) { described_class.new }
    +
    +    it "requires name to not be too long" do
    +      expect(post_type).to validate_length_of(:name).is_at_most(255)
    +    end
    +
    +    it "requires permalink to not be too long" do
    +      expect(post_type).to validate_length_of(:permalink).is_at_most(255)
    +    end
    +
    +    it "requires description to not be too long" do
    +      expect(post_type).to validate_length_of(:description).is_at_most(255)
    +    end
    +  end
     end
    
  • publify_core/spec/models/redirect_spec.rb+12 0 modified
    @@ -20,4 +20,16 @@
           expect(redirect.from_url).to eq "#{blog.base_url}/right/here"
         end
       end
    +
    +  describe "validations" do
    +    let(:redirect) { described_class.new }
    +
    +    it "requires from_path to not be too long" do
    +      expect(redirect).to validate_length_of(:from_path).is_at_most(255)
    +    end
    +
    +    it "requires to_path to not be too long" do
    +      expect(redirect).to validate_length_of(:to_path).is_at_most(255)
    +    end
    +  end
     end
    
  • publify_core/spec/models/resource_spec.rb+8 0 modified
    @@ -33,4 +33,12 @@
           expect(img_resource.upload_url(:thumb)).to eq "/files/resource/1/thumb_testfile.png"
         end
       end
    +
    +  describe "validations" do
    +    let(:resource) { described_class.new }
    +
    +    it "requires mime to not be too long" do
    +      expect(resource).to validate_length_of(:mime).is_at_most(255)
    +    end
    +  end
     end
    
  • publify_core/spec/models/tag_spec.rb+8 2 modified
    @@ -12,9 +12,15 @@
         expect(test_tag.errors[:name]).to eq(["has already been taken"])
       end
     
    -  describe "validation" do
    +  describe "validations" do
    +    let(:tag) { described_class.new }
    +
         it "requires name to be present" do
    -      expect(blog.tags.build(name: "")).not_to be_valid
    +      expect(tag).to validate_presence_of(:name)
    +    end
    +
    +    it "requires display_name to not be too long" do
    +      expect(tag).to validate_length_of(:display_name).is_at_most(255)
         end
       end
     
    
  • publify_core/spec/models/user_spec.rb+8 0 modified
    @@ -45,6 +45,10 @@
       describe "validations" do
         let(:user) { described_class.new }
     
    +    it "requires email to not be too long" do
    +      expect(user).to validate_length_of(:email).is_at_most(255)
    +    end
    +
         it "requires first name to not be too long" do
           expect(user).to validate_length_of(:firstname).is_at_most(256)
         end
    @@ -73,6 +77,10 @@
           expect(user).to validate_presence_of(:login)
         end
     
    +    it "requires text_filter_name to not be too long" do
    +      expect(user).to validate_length_of(:text_filter_name).is_at_most(255)
    +    end
    +
         it "does not allow duplicate logins when updating a user" do
           create :user, login: "foo"
           bar = create :user, login: "bar"
    
34f6e9c98e0e

Validate lengths of string attributes

https://github.com/publify/publify_coreMatijs van ZuijlenAug 14, 2022via ghsa
20 files changed · +177 2
  • app/models/blog.rb+3 0 modified
    @@ -9,6 +9,8 @@
     #
     class Blog < ApplicationRecord
       include ConfigManager
    +  include StringLengthLimit
    +
       include Rails.application.routes.url_helpers
     
       has_many :contents
    @@ -139,6 +141,7 @@ class Blog < ApplicationRecord
     
       validate :permalink_has_identifier
       # validates :base_url, presence: true
    +  validates_default_string_length :base_url
     
       # Find the Blog that matches a specific base URL. If no Blog object is found
       # that matches, then grab the first blog. If *that* fails, then create a new
    
  • app/models/concerns/string_length_limit.rb+17 0 added
    @@ -0,0 +1,17 @@
    +# frozen_string_literal: true
    +
    +module StringLengthLimit
    +  # Default string length limit for model attributes. When running on MySQL,
    +  # this is equal to the default string length in the database as set by Rails.
    +  STRING_LIMIT = 255
    +
    +  extend ActiveSupport::Concern
    +
    +  class_methods do
    +    def validates_default_string_length(*names)
    +      names.each do |name|
    +        validates name, length: { maximum: STRING_LIMIT }
    +      end
    +    end
    +  end
    +end
    
  • app/models/content.rb+4 0 modified
    @@ -5,6 +5,7 @@
     
     class Content < ApplicationRecord
       include ContentBase
    +  include StringLengthLimit
     
       belongs_to :user, optional: true, touch: true
       belongs_to :blog
    @@ -38,6 +39,9 @@ class Content < ApplicationRecord
     
       serialize :whiteboard
     
    +  validates_default_string_length :title, :author, :permalink, :name,
    +                                  :post_type, :text_filter_name
    +
       def author=(user)
         if user.respond_to?(:login)
           self[:author] = user.login
    
  • app/models/feedback.rb+6 0 modified
    @@ -10,10 +10,16 @@ class Feedback < ApplicationRecord
     
       include PublifyGuid
       include ContentBase
    +  include StringLengthLimit
     
       validate :feedback_allowed, on: :create
       validates :article, presence: true
     
    +  validates_default_string_length :title, :author, :email, :url, :blog_name,
    +                                  :user_agent, :text_filter_name
    +
    +  validates :ip, length: { maximum: 40 }
    +
       before_save :correct_url, :classify_content
       before_create :create_guid
     
    
  • app/models/ping.rb+3 0 modified
    @@ -1,5 +1,8 @@
     # frozen_string_literal: true
     
     class Ping < ApplicationRecord
    +  include StringLengthLimit
    +
       belongs_to :article
    +  validates_default_string_length :url
     end
    
  • app/models/post_type.rb+4 0 modified
    @@ -1,9 +1,13 @@
     # frozen_string_literal: true
     
     class PostType < ApplicationRecord
    +  include StringLengthLimit
    +
       validates :name, uniqueness: true
       validates :name, presence: true
       validate :name_is_not_read
    +  validates_default_string_length :name, :permalink, :description
    +
       before_save :sanitize_title
     
       def name_is_not_read
    
  • app/models/redirect.rb+4 0 modified
    @@ -1,13 +1,17 @@
     # frozen_string_literal: true
     
     class Redirect < ApplicationRecord
    +  include StringLengthLimit
    +
       belongs_to :content, optional: true, touch: true
       belongs_to :blog
     
       validates :from_path, uniqueness: true
       validates :to_path, presence: true
       validates :blog, presence: true
     
    +  validates_default_string_length :from_path, :to_path
    +
       def full_to_path
         path = to_path
         # FIXME: Unify HTTP URI matchers
    
  • app/models/resource.rb+3 0 modified
    @@ -4,9 +4,12 @@
     require "carrierwave/orm/activerecord"
     
     class Resource < ApplicationRecord
    +  include StringLengthLimit
       belongs_to :blog
       belongs_to :content, optional: true
     
       mount_uploader :upload, ResourceUploader
       validates :upload, presence: true
    +
    +  validates_default_string_length :mime
     end
    
  • app/models/tag.rb+3 0 modified
    @@ -1,12 +1,15 @@
     # frozen_string_literal: true
     
     class Tag < ApplicationRecord
    +  include StringLengthLimit
    +
       belongs_to :blog
       has_and_belongs_to_many :contents, order: "created_at DESC"
     
       validates :name, uniqueness: { scope: :blog_id }
       validates :blog, presence: true
       validates :name, presence: true
    +  validates_default_string_length :display_name
     
       before_validation :ensure_naming_conventions
     
    
  • app/models/user.rb+2 0 modified
    @@ -14,12 +14,14 @@ class User < ApplicationRecord
       devise :database_authenticatable, :registerable,
              :recoverable, :rememberable, :trackable, :validatable
       include ConfigManager
    +  include StringLengthLimit
     
       before_validation :set_default_profile
     
       validates :login, uniqueness: true
       validates :email, :login, presence: true
       validates :login, length: { in: 3..40 }
    +  validates_default_string_length :email, :text_filter_name
     
       belongs_to :resource, optional: true
       has_many :notifications, foreign_key: "notify_user_id"
    
  • Manifest.txt+1 0 modified
    @@ -160,6 +160,7 @@ app/models/article.rb
     app/models/article/factory.rb
     app/models/blog.rb
     app/models/comment.rb
    +app/models/concerns/string_length_limit.rb
     app/models/config_manager.rb
     app/models/content.rb
     app/models/content_base.rb
    
  • spec/models/blog_spec.rb+4 0 modified
    @@ -113,6 +113,10 @@
       describe "validations" do
         let(:blog) { described_class.new }
     
    +    it "requires base url to not be too long" do
    +      expect(blog).to validate_length_of(:base_url).is_at_most(255)
    +    end
    +
         it "requires blog name to not be too long" do
           expect(blog).to validate_length_of(:blog_name).is_at_most(256)
         end
    
  • spec/models/content_spec.rb+28 0 modified
    @@ -144,4 +144,32 @@
           it { expect(content.author_name).to eq(author.login) }
         end
       end
    +
    +  describe "validations" do
    +    let(:content) { described_class.new }
    +
    +    it "requires title to not be too long" do
    +      expect(content).to validate_length_of(:title).is_at_most(255)
    +    end
    +
    +    it "requires author to not be too long" do
    +      expect(content).to validate_length_of(:author).is_at_most(255)
    +    end
    +
    +    it "requires permalink to not be too long" do
    +      expect(content).to validate_length_of(:permalink).is_at_most(255)
    +    end
    +
    +    it "requires name to not be too long" do
    +      expect(content).to validate_length_of(:name).is_at_most(255)
    +    end
    +
    +    it "requires post_type to not be too long" do
    +      expect(content).to validate_length_of(:post_type).is_at_most(255)
    +    end
    +
    +    it "requires text_filter_name to not be too long" do
    +      expect(content).to validate_length_of(:text_filter_name).is_at_most(255)
    +    end
    +  end
     end
    
  • spec/models/feedback_spec.rb+36 0 modified
    @@ -178,4 +178,40 @@ def classify
           assert !@comment.published?
         end
       end
    +
    +  describe "validations" do
    +    let(:feedback) { described_class.new }
    +
    +    it "requires title to not be too long" do
    +      expect(feedback).to validate_length_of(:title).is_at_most(255)
    +    end
    +
    +    it "requires author to not be too long" do
    +      expect(feedback).to validate_length_of(:author).is_at_most(255)
    +    end
    +
    +    it "requires email to not be too long" do
    +      expect(feedback).to validate_length_of(:email).is_at_most(255)
    +    end
    +
    +    it "requires url to not be too long" do
    +      expect(feedback).to validate_length_of(:url).is_at_most(255)
    +    end
    +
    +    it "requires ip to not be too long" do
    +      expect(feedback).to validate_length_of(:ip).is_at_most(40)
    +    end
    +
    +    it "requires blog_name to not be too long" do
    +      expect(feedback).to validate_length_of(:blog_name).is_at_most(255)
    +    end
    +
    +    it "requires user_agent to not be too long" do
    +      expect(feedback).to validate_length_of(:user_agent).is_at_most(255)
    +    end
    +
    +    it "requires text_filter_name to not be too long" do
    +      expect(feedback).to validate_length_of(:text_filter_name).is_at_most(255)
    +    end
    +  end
     end
    
  • spec/models/ping_spec.rb+7 0 modified
    @@ -3,4 +3,11 @@
     require "rails_helper"
     
     describe Ping, type: :model do
    +  describe "validations" do
    +    let(:ping) { described_class.new }
    +
    +    it "requires url to not be too long" do
    +      expect(ping).to validate_length_of(:url).is_at_most(255)
    +    end
    +  end
     end
    
  • spec/models/post_type_spec.rb+16 0 modified
    @@ -29,4 +29,20 @@
         expect(test_type).not_to be_valid
         expect(test_type.errors[:name]).to eq(["has already been taken"])
       end
    +
    +  describe "validations" do
    +    let(:post_type) { described_class.new }
    +
    +    it "requires name to not be too long" do
    +      expect(post_type).to validate_length_of(:name).is_at_most(255)
    +    end
    +
    +    it "requires permalink to not be too long" do
    +      expect(post_type).to validate_length_of(:permalink).is_at_most(255)
    +    end
    +
    +    it "requires description to not be too long" do
    +      expect(post_type).to validate_length_of(:description).is_at_most(255)
    +    end
    +  end
     end
    
  • spec/models/redirect_spec.rb+12 0 modified
    @@ -20,4 +20,16 @@
           expect(redirect.from_url).to eq "#{blog.base_url}/right/here"
         end
       end
    +
    +  describe "validations" do
    +    let(:redirect) { described_class.new }
    +
    +    it "requires from_path to not be too long" do
    +      expect(redirect).to validate_length_of(:from_path).is_at_most(255)
    +    end
    +
    +    it "requires to_path to not be too long" do
    +      expect(redirect).to validate_length_of(:to_path).is_at_most(255)
    +    end
    +  end
     end
    
  • spec/models/resource_spec.rb+8 0 modified
    @@ -33,4 +33,12 @@
           expect(img_resource.upload_url(:thumb)).to eq "/files/resource/1/thumb_testfile.png"
         end
       end
    +
    +  describe "validations" do
    +    let(:resource) { described_class.new }
    +
    +    it "requires mime to not be too long" do
    +      expect(resource).to validate_length_of(:mime).is_at_most(255)
    +    end
    +  end
     end
    
  • spec/models/tag_spec.rb+8 2 modified
    @@ -12,9 +12,15 @@
         expect(test_tag.errors[:name]).to eq(["has already been taken"])
       end
     
    -  describe "validation" do
    +  describe "validations" do
    +    let(:tag) { described_class.new }
    +
         it "requires name to be present" do
    -      expect(blog.tags.build(name: "")).not_to be_valid
    +      expect(tag).to validate_presence_of(:name)
    +    end
    +
    +    it "requires display_name to not be too long" do
    +      expect(tag).to validate_length_of(:display_name).is_at_most(255)
         end
       end
     
    
  • spec/models/user_spec.rb+8 0 modified
    @@ -45,6 +45,10 @@
       describe "validations" do
         let(:user) { described_class.new }
     
    +    it "requires email to not be too long" do
    +      expect(user).to validate_length_of(:email).is_at_most(255)
    +    end
    +
         it "requires first name to not be too long" do
           expect(user).to validate_length_of(:firstname).is_at_most(256)
         end
    @@ -73,6 +77,10 @@
           expect(user).to validate_presence_of(:login)
         end
     
    +    it "requires text_filter_name to not be too long" do
    +      expect(user).to validate_length_of(:text_filter_name).is_at_most(255)
    +    end
    +
         it "does not allow duplicate logins when updating a user" do
           create :user, login: "foo"
           bar = create :user, login: "bar"
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.