Redmine uses SHA256 instead of MD5 for file digests #783

This commit is contained in:
Karel Picman 2017-11-01 14:18:21 +01:00
parent ed43639ec0
commit edc17a32f0
6 changed files with 58 additions and 13 deletions

View File

@ -20,7 +20,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'tmpdir' require 'tmpdir'
require 'digest/md5' require 'digest'
require 'csv' require 'csv'
module DmsfHelper module DmsfHelper
@ -42,16 +42,13 @@ module DmsfHelper
def self.sanitize_filename(filename) def self.sanitize_filename(filename)
# get only the filename, not the whole path # get only the filename, not the whole path
just_filename = File.basename(filename.gsub('\\\\', '/')) just_filename = File.basename(filename.gsub('\\\\', '/'))
# replace all non alphanumeric, hyphens or periods with underscore # replace all non alphanumeric, hyphens or periods with underscore
just_filename = just_filename.gsub(/[^\w\.\-]/,'_') just_filename = just_filename.gsub(/[^\w\.\-]/,'_')
unless just_filename =~ %r{^[a-zA-Z0-9_\.\-]*$} unless just_filename =~ %r{^[a-zA-Z0-9_\.\-]*$}
# keep the extension if any # keep the extension if any
extension = $1 if just_filename =~ %r{(\.[a-zA-Z0-9]+)$} extension = $1 if just_filename =~ %r{(\.[a-zA-Z0-9]+)$}
just_filename = Digest::MD5.hexdigest(just_filename) << extension just_filename = Digest::SHA256.hexdigest(just_filename) << extension
end end
just_filename just_filename
end end

View File

@ -19,7 +19,7 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'digest/md5' require 'digest'
class DmsfFileRevision < ActiveRecord::Base class DmsfFileRevision < ActiveRecord::Base
@ -290,7 +290,7 @@ class DmsfFileRevision < ActiveRecord::Base
def self.create_digest(path) def self.create_digest(path)
begin begin
Digest::MD5.file(path).hexdigest Digest::SHA256.file(path).hexdigest
rescue Exception => e rescue Exception => e
Rails.logger.error e.message Rails.logger.error e.message
0 0
@ -301,9 +301,17 @@ class DmsfFileRevision < ActiveRecord::Base
self.digest = DmsfFileRevision.create_digest(self.disk_file) self.digest = DmsfFileRevision.create_digest(self.disk_file)
end end
# Returns either MD5 or SHA256 depending on the way self.digest was computed
def digest_type
self.digest.size < 64 ? 'MD5' : 'SHA256' if digest.present?
end
def tooltip def tooltip
if self.description.present?
text = self.description
else
text = '' text = ''
text = self.description if self.description.present? end
if self.comment.present? if self.comment.present?
text += ' / ' if text.present? text += ' / ' if text.present?
text += self.comment text += self.comment

View File

@ -162,8 +162,8 @@
</div> </div>
<% if revision.digest.present? %> <% if revision.digest.present? %>
<div class="status attribute"> <div class="status attribute">
<%= content_tag :div, 'MD5', :class => 'label' %> <%= content_tag :div, l(:field_digest), :class => 'label' %>
<%= content_tag :div, revision.digest, :class => 'value wiki' %> <%= content_tag :div, "#{revision.digest_type}: #{revision.digest}", :class => 'value wiki' %>
</div> </div>
<% end %> <% end %>
<%= render 'dmsf/custom_fields', :object => revision %> <%= render 'dmsf/custom_fields', :object => revision %>

View File

@ -0,0 +1,28 @@
# encoding: utf-8
#
# Redmine plugin for Document Management System "Features"
#
# Copyright (C) 2011-17 Karel Pičman <karel.picman@kontron.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class ChangeRevisionDigestLimitTo64 < ActiveRecord::Migration
def up
change_column :dmsf_file_revisions, :digest, :string, limit: 64
end
def down
change_column :dmsf_file_revisions, :digest, :string, limit: 40
end
end

View File

@ -20,7 +20,7 @@
desc <<-END_DESC desc <<-END_DESC
DMSF maintenance task DMSF maintenance task
* Create missing MD5 digest for all file revisions * Create missing checksum for all file revisions
Available options: Available options:
*dry_run - test, no changes to the database *dry_run - test, no changes to the database

View File

@ -29,13 +29,16 @@ class DmsfFileRevisionTest < RedmineDmsf::Test::UnitTest
@revision1 = DmsfFileRevision.find_by_id 1 @revision1 = DmsfFileRevision.find_by_id 1
@revision2 = DmsfFileRevision.find_by_id 2 @revision2 = DmsfFileRevision.find_by_id 2
@revision5 = DmsfFileRevision.find_by_id 5 @revision5 = DmsfFileRevision.find_by_id 5
@revision8 = DmsfFileRevision.find_by_id 8
@wf1 = DmsfWorkflow.find_by_id 1 @wf1 = DmsfWorkflow.find_by_id 1
Setting.plugin_redmine_dmsf['dmsf_storage_directory'] = File.expand_path '../../fixtures/files', __FILE__
end end
def test_truth def test_truth
assert_kind_of DmsfFileRevision, @revision1 assert_kind_of DmsfFileRevision, @revision1
assert_kind_of DmsfFileRevision, @revision2 assert_kind_of DmsfFileRevision, @revision2
assert_kind_of DmsfFileRevision, @revision5 assert_kind_of DmsfFileRevision, @revision5
assert_kind_of DmsfFileRevision, @revision8
assert_kind_of DmsfWorkflow, @wf1 assert_kind_of DmsfWorkflow, @wf1
end end
@ -54,7 +57,16 @@ class DmsfFileRevisionTest < RedmineDmsf::Test::UnitTest
end end
def test_create_digest def test_create_digest
assert_equal @revision5.create_digest, 0, "MD5 should be 0, if the file is missing" assert @revision1.create_digest.length > 40
assert_equal @revision8.create_digest, 0, 'Digest should be 0, if the file is missing'
end
def test_digest_type
# Old type MD5
assert_equal 'MD5', @revision1.digest_type
# New type SHA256
@revision1.create_digest
assert_equal 'SHA256', @revision1.digest_type
end end
def test_new_storage_filename def test_new_storage_filename