test_server 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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'
|