test_server 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -3
- data/Gemfile +13 -5
- data/README.md +16 -0
- data/Rakefile +17 -3
- data/app/assets/javascripts/test_server/file_uploader.js.coffee +3 -0
- data/app/assets/stylesheets/application.scss +31 -0
- data/app/assets/stylesheets/test_server/file_uploader.css.scss +3 -0
- data/app/controllers/test_server/file_uploader_controller.rb +42 -0
- data/app/helpers/test_server/file_uploader_helper.rb +2 -0
- data/app/models/test_server/file_upload.rb +41 -0
- data/app/views/layouts/application.html.haml +10 -0
- data/app/views/test_server/dashboard/show.html.haml +1 -0
- data/app/views/test_server/file_uploader/_form.html.haml +27 -0
- data/app/views/test_server/file_uploader/_overview.html.haml +1 -0
- data/app/views/test_server/file_uploader/index.html.haml +1 -0
- data/app/views/test_server/file_uploader/result.html.haml +40 -0
- data/app/views/test_server/file_uploader/upload.html.haml +1 -0
- data/config/routes.rb +8 -2
- data/lib/test_server/checksum.rb +21 -0
- data/lib/test_server/checksum_calculator.rb +27 -0
- data/lib/test_server/command_runner.rb +58 -0
- data/lib/test_server/exceptions.rb +3 -0
- data/lib/test_server/file_size.rb +14 -0
- data/lib/test_server/filetype_detector.rb +21 -0
- data/lib/test_server/locales/en.yml +28 -1
- data/lib/test_server/main.rb +6 -1
- data/lib/test_server/md5_calculator.rb +17 -0
- data/lib/test_server/permitted_params.rb +25 -7
- data/lib/test_server/sha256_calculator.rb +17 -0
- data/lib/test_server/uploaded_file.rb +58 -0
- data/lib/test_server/version.rb +1 -1
- data/lib/test_server/virus_detector.rb +43 -0
- data/lib/test_server.rb +15 -0
- data/public/assets/{manifest-7c069da388f8a976fadfaab958b9feb7.json → manifest-1a3c45fcd482cca628c8cdc72631eb98.json} +0 -0
- data/spec/controllers/test_server/file_uploader_controller_spec.rb +26 -0
- data/spec/encoder_spec.rb +3 -3
- data/spec/features/fetch_data_via_javascript_spec.rb +18 -18
- data/spec/filetype_detector_spec.rb +31 -0
- data/spec/fixtures/file_uploader/file.bin +0 -0
- data/spec/permitted_params_spec.rb +1 -1
- data/spec/sha256_calculator_spec.rb +36 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/commands.rb +15 -0
- data/spec/support/debugging.rb +6 -1
- data/spec/support/eicar.rb +15 -0
- data/spec/support/fixtures.rb +16 -0
- data/spec/support/rails.rb +2 -0
- data/spec/uploaded_file_spec.rb +167 -0
- data/spec/views/errors/not_found.html.haml_spec.rb +1 -1
- data/spec/views/test_server/file_uploader/index.html.haml_spec.rb +32 -0
- data/spec/views/test_server/file_uploader/result.html.haml_spec.rb +58 -0
- data/spec/views/test_server/file_uploader/upload.html.haml_spec.rb +12 -0
- data/spec/views/test_server/reflector/client_ip_address.html.haml_spec.rb +1 -1
- data/spec/virus_detector_spec.rb +45 -0
- data/test_server.gemspec +2 -3
- metadata +48 -19
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module TestServer
|
3
|
+
class ChecksumCalculator
|
4
|
+
private
|
5
|
+
|
6
|
+
attr_reader :engine
|
7
|
+
|
8
|
+
public
|
9
|
+
|
10
|
+
def use(file)
|
11
|
+
file.checksum = compute_checksum(file)
|
12
|
+
rescue StandardError => err
|
13
|
+
Rails.logger.error "#{err.class}: #{err.message}"
|
14
|
+
|
15
|
+
checksum = OpenStruct.new
|
16
|
+
checksum.value = "An error occured while determine checksum for \"#{file.name}\"."
|
17
|
+
|
18
|
+
file.checksum = checksum
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def compute_checksum(file)
|
24
|
+
raise MethodNotImplemented, JSON.dump(method: :compute_checksum)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'timeout'
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
module TestServer
|
6
|
+
class CommandRunner
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
attr_reader :timeout
|
11
|
+
|
12
|
+
public
|
13
|
+
|
14
|
+
def initialize(timeout:)
|
15
|
+
@timeout = timeout
|
16
|
+
end
|
17
|
+
|
18
|
+
def run(command)
|
19
|
+
# stdout, stderr pipes
|
20
|
+
rout, wout = IO.pipe
|
21
|
+
rerr, werr = IO.pipe
|
22
|
+
|
23
|
+
result = OpenStruct.new
|
24
|
+
|
25
|
+
begin
|
26
|
+
result.pid = Process.spawn(command, :out => wout, :err => werr)
|
27
|
+
_, status = Timeout::timeout(timeout) { Process.wait2(result.pid) }
|
28
|
+
|
29
|
+
result.status = status.exitstatus
|
30
|
+
rescue Timeout::Error
|
31
|
+
Process.kill 'KILL', result.pid
|
32
|
+
result.status = 99
|
33
|
+
end
|
34
|
+
|
35
|
+
# close write ends so we could read them
|
36
|
+
wout.close
|
37
|
+
werr.close
|
38
|
+
|
39
|
+
result.stdout = rout.readlines
|
40
|
+
result.stderr = rerr.readlines
|
41
|
+
|
42
|
+
# dispose the read ends of the pipes
|
43
|
+
rout.close
|
44
|
+
rerr.close
|
45
|
+
|
46
|
+
result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module TestServer
|
52
|
+
module RunCommand
|
53
|
+
def run_command(command, timeout: 5)
|
54
|
+
runner = CommandRunner.new(timeout: timeout)
|
55
|
+
runner.run(command)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -41,5 +41,8 @@ module TestServer
|
|
41
41
|
|
42
42
|
# raised if developer misses to define action for controller in parameter list
|
43
43
|
class ActionNotDefinedInParameterList < InternalError; end
|
44
|
+
|
45
|
+
# raised if developer misses to define role
|
46
|
+
class RoleNotDefined < InternalError; end
|
44
47
|
end
|
45
48
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module TestServer
|
3
|
+
class FiletypeDetector
|
4
|
+
private
|
5
|
+
|
6
|
+
attr_reader :engine
|
7
|
+
|
8
|
+
public
|
9
|
+
|
10
|
+
def initialize(engine: FileMagic.new)
|
11
|
+
@engine = engine
|
12
|
+
end
|
13
|
+
|
14
|
+
def use(file)
|
15
|
+
file.filetype = engine.file(file.path)
|
16
|
+
rescue StandardError => err
|
17
|
+
Rails.logger.error "#{err.class}: #{err.message}"
|
18
|
+
file.filetype = "An error occured while determine file type for \"#{file.name}\"."
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -49,6 +49,7 @@ en:
|
|
49
49
|
reset: 'Reset'
|
50
50
|
close: 'Close'
|
51
51
|
clone: 'Clone'
|
52
|
+
upload: 'Upload'
|
52
53
|
dialogs:
|
53
54
|
notification:
|
54
55
|
title: Notification
|
@@ -105,6 +106,33 @@ en:
|
|
105
106
|
index:
|
106
107
|
title: Overview for Reflectors
|
107
108
|
link: Overview
|
109
|
+
file_uploader:
|
110
|
+
title: File Uploader
|
111
|
+
link: File Uploader
|
112
|
+
index:
|
113
|
+
title: Overview for File Uploads
|
114
|
+
link: Overview
|
115
|
+
result:
|
116
|
+
title: "Information about %{file}"
|
117
|
+
link: "%{file}"
|
118
|
+
fields:
|
119
|
+
file: File
|
120
|
+
checksum: Checksums
|
121
|
+
filename: Filename
|
122
|
+
virus: Virus ID
|
123
|
+
filetype: Filetype
|
124
|
+
size: Size
|
125
|
+
upload:
|
126
|
+
title: Upload file
|
127
|
+
link: Upload file
|
128
|
+
fields:
|
129
|
+
file: File
|
130
|
+
virus_scan: Scan for viruses
|
131
|
+
filetype_detection: Detect filetype
|
132
|
+
checksum_calculation: Calculate Checksums
|
133
|
+
spinner:
|
134
|
+
text: Waiting for upload to finish...
|
135
|
+
|
108
136
|
static:
|
109
137
|
link: Static
|
110
138
|
title: Static content
|
@@ -134,4 +162,3 @@ en:
|
|
134
162
|
status: Status
|
135
163
|
spinner:
|
136
164
|
text: Waiting for results...
|
137
|
-
|
data/lib/test_server/main.rb
CHANGED
@@ -14,7 +14,12 @@ module TestServer
|
|
14
14
|
TestServer.ui_logger.info "Activating debug mode."
|
15
15
|
|
16
16
|
require 'pry'
|
17
|
-
|
17
|
+
if RUBY_VERSION < '2.0.0'
|
18
|
+
require 'debugger'
|
19
|
+
else
|
20
|
+
require 'byebug'
|
21
|
+
end
|
22
|
+
|
18
23
|
rescue LoadError
|
19
24
|
TestServer.ui_logger.error "You tried to enable debug-mode, but either 'pry'- or 'debugger'-gem are not installed. Please fix that before using the debug-switch again."
|
20
25
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module TestServer
|
3
|
+
class MD5Calculator < ChecksumCalculator
|
4
|
+
private
|
5
|
+
|
6
|
+
attr_reader :engine
|
7
|
+
|
8
|
+
def compute_checksum(file)
|
9
|
+
Checksum.new(
|
10
|
+
algorithm: :md5,
|
11
|
+
prefix: 'MD5',
|
12
|
+
engine: Digest::MD5,
|
13
|
+
data: File.read(file.path),
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -13,7 +13,9 @@ module TestServer
|
|
13
13
|
@params = params
|
14
14
|
|
15
15
|
@database = {
|
16
|
+
# controller name
|
16
17
|
dashboard: {
|
18
|
+
# action: role1, role2, ...
|
17
19
|
show: [],
|
18
20
|
},
|
19
21
|
reflector: {
|
@@ -40,6 +42,11 @@ module TestServer
|
|
40
42
|
xhr: [:generatable, :repeatable],
|
41
43
|
index: [],
|
42
44
|
},
|
45
|
+
file_uploader: {
|
46
|
+
upload: [],
|
47
|
+
result: [:uploadable],
|
48
|
+
index: [],
|
49
|
+
},
|
43
50
|
}
|
44
51
|
end
|
45
52
|
|
@@ -47,8 +54,8 @@ module TestServer
|
|
47
54
|
name = name.to_sym
|
48
55
|
action = action.to_sym
|
49
56
|
|
50
|
-
raise Exceptions::ControllerNotDefinedInParameterList unless database.key? name
|
51
|
-
raise Exceptions::ActionNotDefinedInParameterList
|
57
|
+
raise Exceptions::ControllerNotDefinedInParameterList, JSON.dump(name: name) unless database.key? name
|
58
|
+
raise Exceptions::ActionNotDefinedInParameterList, JSON.dump(action: action) unless database[name].key? action
|
52
59
|
|
53
60
|
generate_params(defaults, params_for(database[name][action]))
|
54
61
|
end
|
@@ -65,15 +72,22 @@ module TestServer
|
|
65
72
|
|
66
73
|
private
|
67
74
|
|
75
|
+
def validate_role(role)
|
76
|
+
raise Exceptions::RoleNotDefined, JSON.dump(role: role, required_method: :"#{role}_params") unless self.respond_to? :"#{role}_params", true
|
77
|
+
end
|
78
|
+
|
79
|
+
def determine_attributes(role)
|
80
|
+
send :"#{role}_params"
|
81
|
+
end
|
82
|
+
|
68
83
|
def params_for(*roles)
|
69
84
|
roles = roles.flatten
|
70
85
|
attributes = Set.new
|
71
86
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
attributes = attributes + generatable_params if roles.include? :generatable
|
87
|
+
roles.each do |r|
|
88
|
+
validate_role r
|
89
|
+
attributes += determine_attributes(r)
|
90
|
+
end
|
77
91
|
|
78
92
|
attributes
|
79
93
|
end
|
@@ -107,5 +121,9 @@ module TestServer
|
|
107
121
|
def cachable_params
|
108
122
|
[:expires]
|
109
123
|
end
|
124
|
+
|
125
|
+
def uploadable_params
|
126
|
+
[:authenticity_token, :file, :upload, { test_server_file_upload: [ :uploaded_file, :virus_scan, :filetype_detection, :checksum_calculation ] }, :utf8]
|
127
|
+
end
|
110
128
|
end
|
111
129
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module TestServer
|
3
|
+
class Sha256Calculator < ChecksumCalculator
|
4
|
+
private
|
5
|
+
|
6
|
+
attr_reader :engine
|
7
|
+
|
8
|
+
def compute_checksum(file)
|
9
|
+
Checksum.new(
|
10
|
+
algorithm: :sha256,
|
11
|
+
prefix: 'SHA256',
|
12
|
+
engine: Digest::SHA256,
|
13
|
+
data: File.read(file.path),
|
14
|
+
)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module TestServer
|
3
|
+
class UploadedFile
|
4
|
+
private
|
5
|
+
|
6
|
+
attr_reader :uploaded_file
|
7
|
+
|
8
|
+
public
|
9
|
+
|
10
|
+
attr_accessor :filetype, :virus_id, :checksum
|
11
|
+
|
12
|
+
def initialize(uploaded_file)
|
13
|
+
if uploaded_file.respond_to? :tempfile
|
14
|
+
@uploaded_file = uploaded_file
|
15
|
+
else
|
16
|
+
@uploaded_file = OpenStruct.new
|
17
|
+
end
|
18
|
+
|
19
|
+
@checksum = []
|
20
|
+
end
|
21
|
+
|
22
|
+
def checksum=(sum)
|
23
|
+
@checksum << sum
|
24
|
+
end
|
25
|
+
|
26
|
+
def path
|
27
|
+
uploaded_file.path.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def name
|
31
|
+
File.basename(uploaded_file.original_filename.to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_filetype?
|
35
|
+
!filetype.blank?
|
36
|
+
end
|
37
|
+
|
38
|
+
def contains_virus?
|
39
|
+
!virus_id.blank?
|
40
|
+
end
|
41
|
+
|
42
|
+
def has_checksum?
|
43
|
+
!checksum.blank?
|
44
|
+
end
|
45
|
+
|
46
|
+
def size
|
47
|
+
formula = lambda { |number| uploaded_file.size.to_f / (2**number) }
|
48
|
+
|
49
|
+
result = {}
|
50
|
+
result[:b] = FileSize.new(suffix: 'B', value: uploaded_file.size.to_s)
|
51
|
+
result[:kib] = FileSize.new(suffix: 'KiB', value: '%.2f' % formula.call(10))
|
52
|
+
result[:mib] = FileSize.new(suffix: 'MiB', value: '%.2f' % formula.call(20))
|
53
|
+
result[:gib] = FileSize.new(suffix: 'KiB', value: '%.2f' % formula.call(30))
|
54
|
+
|
55
|
+
result
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/test_server/version.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module TestServer
|
3
|
+
class VirusDetector
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
attr_reader :command, :timeout
|
8
|
+
|
9
|
+
public
|
10
|
+
|
11
|
+
def initialize(command: which('clamdscan'))
|
12
|
+
@command = command
|
13
|
+
@timeout = 60
|
14
|
+
end
|
15
|
+
|
16
|
+
def use(file)
|
17
|
+
file.virus_id = scan(file)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
include FeduxOrg::Stdlib::Command::Which
|
23
|
+
include TestServer::RunCommand
|
24
|
+
|
25
|
+
def scan(file)
|
26
|
+
return "Scan commands \"clamdscan\" and \"clamscan\" not found. File \"#{file.name}\" could not be scanned." if command.blank?
|
27
|
+
|
28
|
+
result = run_command("#{command} #{file.path}", timeout: timeout)
|
29
|
+
return "Timeout \"#{timeout}\" reached before finishing scan." if result.status == 99
|
30
|
+
return "Scan failed due to error: #{extract_error(result.stdout)}" if result.stdout.first['ERROR']
|
31
|
+
|
32
|
+
extract_virus_id(result.stdout)
|
33
|
+
end
|
34
|
+
|
35
|
+
def extract_error(data)
|
36
|
+
data.first.chomp.split(/:/).last
|
37
|
+
end
|
38
|
+
|
39
|
+
def extract_virus_id(data)
|
40
|
+
data.first.scan(/^[^:]+:\s([[:alnum:]-]+)\s[[:alpha:]]+/).flatten.first
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/test_server.rb
CHANGED
@@ -17,6 +17,9 @@ require 'base64'
|
|
17
17
|
require 'thor'
|
18
18
|
require 'zlib'
|
19
19
|
require 'digest/sha1'
|
20
|
+
require 'timeout'
|
21
|
+
require 'filemagic'
|
22
|
+
require 'securerandom'
|
20
23
|
|
21
24
|
require 'active_support/core_ext/kernel/reporting'
|
22
25
|
require 'active_support/core_ext/object/blank'
|
@@ -24,6 +27,8 @@ require 'active_support/core_ext/array/access'
|
|
24
27
|
require 'active_support/core_ext/hash/keys'
|
25
28
|
require 'active_support/core_ext/string/inflections'
|
26
29
|
|
30
|
+
require 'fedux_org/stdlib/command/which'
|
31
|
+
|
27
32
|
require 'timeout'
|
28
33
|
require 'logger'
|
29
34
|
require 'pager'
|
@@ -68,6 +73,16 @@ require 'test_server/server_commands/puma'
|
|
68
73
|
require 'test_server/server_commands/rackup'
|
69
74
|
require 'test_server/server'
|
70
75
|
|
76
|
+
require 'test_server/command_runner'
|
77
|
+
require 'test_server/file_size'
|
78
|
+
require 'test_server/uploaded_file'
|
79
|
+
require 'test_server/virus_detector'
|
80
|
+
require 'test_server/filetype_detector'
|
81
|
+
require 'test_server/checksum'
|
82
|
+
require 'test_server/checksum_calculator'
|
83
|
+
require 'test_server/sha256_calculator'
|
84
|
+
require 'test_server/md5_calculator'
|
85
|
+
|
71
86
|
require 'test_server/cli/helper'
|
72
87
|
require 'test_server/cli/reload'
|
73
88
|
require 'test_server/cli/main'
|