class MaterialExportItem < ApplicationRecord VALID_PROFILES = ['legacy_drive'].freeze belongs_to :material belongs_to :created_by_user, class_name: 'User', optional: true validates :profile, presence: true, inclusion: { in: VALID_PROFILES } validates :export_path, presence: true, uniqueness: { scope: :profile } validates :material_id, uniqueness: { scope: :profile } validate :export_path_must_be_relative_safe_path scope :enabled, -> { where(enabled: true) } private def export_path_must_be_relative_safe_path return if export_path.blank? if export_path.start_with?('/') errors.add(:export_path, '絶対パスは使へません.') end if export_path.match?(/\A[A-Za-z]:\//) errors.add(:export_path, '絶対パスは使へません.') end if export_path.include?('\\') errors.add(:export_path, '/ 区切りで指定してください.') end if export_path.include?("\0") errors.add(:export_path, 'NUL は使へません.') end parts = export_path.split('/') if export_path.include?('//') errors.add(:export_path, '空の path segment は使へません.') end if parts.any? { |part| part.in?(['.', '..']) } errors.add(:export_path, '.. は使へません.') end if export_path.end_with?('/') errors.add(:export_path, 'directory path は使へません.') end end end