codeclimate-fede 0.85.23
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 +7 -0
- data/bin/check +18 -0
- data/bin/codeclimate +21 -0
- data/bin/prep-release +45 -0
- data/bin/release +41 -0
- data/bin/validate-release +18 -0
- data/config/engines.yml +322 -0
- data/lib/cc/analyzer.rb +50 -0
- data/lib/cc/analyzer/bridge.rb +106 -0
- data/lib/cc/analyzer/composite_container_listener.rb +21 -0
- data/lib/cc/analyzer/container.rb +208 -0
- data/lib/cc/analyzer/container/result.rb +74 -0
- data/lib/cc/analyzer/container_listener.rb +9 -0
- data/lib/cc/analyzer/engine.rb +125 -0
- data/lib/cc/analyzer/engine_output.rb +74 -0
- data/lib/cc/analyzer/engine_output_filter.rb +36 -0
- data/lib/cc/analyzer/engine_output_overrider.rb +31 -0
- data/lib/cc/analyzer/filesystem.rb +50 -0
- data/lib/cc/analyzer/formatters.rb +21 -0
- data/lib/cc/analyzer/formatters/formatter.rb +53 -0
- data/lib/cc/analyzer/formatters/html_formatter.rb +415 -0
- data/lib/cc/analyzer/formatters/json_formatter.rb +38 -0
- data/lib/cc/analyzer/formatters/plain_text_formatter.rb +101 -0
- data/lib/cc/analyzer/formatters/spinner.rb +35 -0
- data/lib/cc/analyzer/issue.rb +69 -0
- data/lib/cc/analyzer/issue_sorter.rb +30 -0
- data/lib/cc/analyzer/issue_validations.rb +26 -0
- data/lib/cc/analyzer/issue_validations/category_validation.rb +32 -0
- data/lib/cc/analyzer/issue_validations/check_name_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/content_validation.rb +21 -0
- data/lib/cc/analyzer/issue_validations/description_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/location_format_validation.rb +72 -0
- data/lib/cc/analyzer/issue_validations/other_locations_format_validation.rb +41 -0
- data/lib/cc/analyzer/issue_validations/path_existence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/path_is_file_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/path_presence_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/relative_path_validation.rb +32 -0
- data/lib/cc/analyzer/issue_validations/remediation_points_validation.rb +25 -0
- data/lib/cc/analyzer/issue_validations/severity_validation.rb +39 -0
- data/lib/cc/analyzer/issue_validations/type_validation.rb +15 -0
- data/lib/cc/analyzer/issue_validations/validation.rb +35 -0
- data/lib/cc/analyzer/issue_validator.rb +11 -0
- data/lib/cc/analyzer/location_description.rb +45 -0
- data/lib/cc/analyzer/logging_container_listener.rb +24 -0
- data/lib/cc/analyzer/measurement.rb +22 -0
- data/lib/cc/analyzer/measurement_validations.rb +16 -0
- data/lib/cc/analyzer/measurement_validations/name_validation.rb +23 -0
- data/lib/cc/analyzer/measurement_validations/type_validation.rb +15 -0
- data/lib/cc/analyzer/measurement_validations/validation.rb +27 -0
- data/lib/cc/analyzer/measurement_validations/value_validation.rb +21 -0
- data/lib/cc/analyzer/measurement_validator.rb +11 -0
- data/lib/cc/analyzer/mounted_path.rb +80 -0
- data/lib/cc/analyzer/raising_container_listener.rb +32 -0
- data/lib/cc/analyzer/source_buffer.rb +47 -0
- data/lib/cc/analyzer/source_extractor.rb +79 -0
- data/lib/cc/analyzer/source_fingerprint.rb +40 -0
- data/lib/cc/analyzer/statsd_container_listener.rb +51 -0
- data/lib/cc/analyzer/validator.rb +38 -0
- data/lib/cc/cli.rb +39 -0
- data/lib/cc/cli/analyze.rb +90 -0
- data/lib/cc/cli/analyze/engine_failure.rb +11 -0
- data/lib/cc/cli/command.rb +85 -0
- data/lib/cc/cli/console.rb +12 -0
- data/lib/cc/cli/engines.rb +5 -0
- data/lib/cc/cli/engines/engine_command.rb +15 -0
- data/lib/cc/cli/engines/install.rb +35 -0
- data/lib/cc/cli/engines/list.rb +18 -0
- data/lib/cc/cli/file_store.rb +42 -0
- data/lib/cc/cli/global_cache.rb +47 -0
- data/lib/cc/cli/global_config.rb +35 -0
- data/lib/cc/cli/help.rb +51 -0
- data/lib/cc/cli/output.rb +34 -0
- data/lib/cc/cli/prepare.rb +98 -0
- data/lib/cc/cli/runner.rb +75 -0
- data/lib/cc/cli/validate_config.rb +84 -0
- data/lib/cc/cli/version.rb +16 -0
- data/lib/cc/cli/version_checker.rb +107 -0
- data/lib/cc/config.rb +70 -0
- data/lib/cc/config/checks_adapter.rb +40 -0
- data/lib/cc/config/default_adapter.rb +54 -0
- data/lib/cc/config/engine.rb +41 -0
- data/lib/cc/config/engine_set.rb +47 -0
- data/lib/cc/config/json_adapter.rb +17 -0
- data/lib/cc/config/prepare.rb +92 -0
- data/lib/cc/config/validation/check_validator.rb +34 -0
- data/lib/cc/config/validation/engine_validator.rb +93 -0
- data/lib/cc/config/validation/fetch_validator.rb +78 -0
- data/lib/cc/config/validation/file_validator.rb +112 -0
- data/lib/cc/config/validation/hash_validations.rb +52 -0
- data/lib/cc/config/validation/json.rb +31 -0
- data/lib/cc/config/validation/prepare_validator.rb +40 -0
- data/lib/cc/config/validation/yaml.rb +66 -0
- data/lib/cc/config/yaml_adapter.rb +73 -0
- data/lib/cc/engine_registry.rb +74 -0
- data/lib/cc/resolv.rb +39 -0
- data/lib/cc/workspace.rb +39 -0
- data/lib/cc/workspace/exclusion.rb +34 -0
- data/lib/cc/workspace/path_tree.rb +49 -0
- data/lib/cc/workspace/path_tree/dir_node.rb +67 -0
- data/lib/cc/workspace/path_tree/file_node.rb +31 -0
- metadata +277 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
class Config
|
|
3
|
+
class ChecksAdapter
|
|
4
|
+
attr_reader :config
|
|
5
|
+
|
|
6
|
+
def initialize(data = {})
|
|
7
|
+
@config = data
|
|
8
|
+
|
|
9
|
+
return unless checks.present?
|
|
10
|
+
copy_qm_checks_config
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def copy_qm_checks_config
|
|
16
|
+
DefaultAdapter::ENGINES.keys.each do |name|
|
|
17
|
+
copy_checks(name)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def copy_checks(engine_name)
|
|
22
|
+
engine = config.fetch("plugins", {}).fetch(engine_name, {})
|
|
23
|
+
engine["config"] ||= {}
|
|
24
|
+
|
|
25
|
+
if engine["config"].is_a?(String)
|
|
26
|
+
engine["config"] = {
|
|
27
|
+
"file" => engine["config"],
|
|
28
|
+
"checks" => checks,
|
|
29
|
+
}
|
|
30
|
+
elsif engine["config"].is_a?(Hash)
|
|
31
|
+
engine["config"]["checks"] = checks
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def checks
|
|
36
|
+
config["checks"]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
class Config
|
|
3
|
+
class DefaultAdapter
|
|
4
|
+
# intentionally not sorted: we want them in a particular order
|
|
5
|
+
ENGINES = {
|
|
6
|
+
"structure".freeze => "stable".freeze,
|
|
7
|
+
"duplication".freeze => "stable".freeze,
|
|
8
|
+
}.freeze
|
|
9
|
+
|
|
10
|
+
EXCLUDE_PATTERNS = %w[
|
|
11
|
+
config/
|
|
12
|
+
db/
|
|
13
|
+
dist/
|
|
14
|
+
features/
|
|
15
|
+
**/node_modules/
|
|
16
|
+
script/
|
|
17
|
+
**/spec/
|
|
18
|
+
**/test/
|
|
19
|
+
**/tests/
|
|
20
|
+
Tests/
|
|
21
|
+
**/vendor/
|
|
22
|
+
**/*_test.go
|
|
23
|
+
**/*.d.ts
|
|
24
|
+
].freeze
|
|
25
|
+
|
|
26
|
+
attr_reader :config
|
|
27
|
+
|
|
28
|
+
def initialize(data = {})
|
|
29
|
+
@config = data
|
|
30
|
+
|
|
31
|
+
apply_default_excludes
|
|
32
|
+
apply_default_engines
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def apply_default_engines
|
|
38
|
+
config["plugins"] ||= {}
|
|
39
|
+
|
|
40
|
+
ENGINES.each do |name, channel|
|
|
41
|
+
config["plugins"][name] ||= {}
|
|
42
|
+
unless [true, false].include?(config["plugins"][name]["enabled"])
|
|
43
|
+
config["plugins"][name]["enabled"] = true
|
|
44
|
+
end
|
|
45
|
+
config["plugins"][name]["channel"] ||= channel
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def apply_default_excludes
|
|
50
|
+
config["exclude_patterns"] ||= EXCLUDE_PATTERNS
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
class Config
|
|
3
|
+
class Engine
|
|
4
|
+
DEFAULT_CHANNEL = "stable".freeze
|
|
5
|
+
|
|
6
|
+
attr_accessor :channel
|
|
7
|
+
attr_reader :name, :config, :exclude_patterns
|
|
8
|
+
attr_writer :enabled
|
|
9
|
+
|
|
10
|
+
def initialize(name, enabled: false, channel: nil, config: nil, exclude_patterns: [])
|
|
11
|
+
@name = name
|
|
12
|
+
@enabled = enabled
|
|
13
|
+
@channel = channel || DEFAULT_CHANNEL
|
|
14
|
+
@config = config || {}
|
|
15
|
+
@exclude_patterns = exclude_patterns
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def enabled?
|
|
19
|
+
@enabled
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def plugin?
|
|
23
|
+
!DefaultAdapter::ENGINES.keys.include?(name)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def container_label
|
|
27
|
+
@container_label ||= SecureRandom.uuid
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def hash
|
|
31
|
+
name.hash
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def eql?(other)
|
|
35
|
+
other.is_a?(self.class) && name.eql?(other.name)
|
|
36
|
+
end
|
|
37
|
+
alias_method :==, :eql?
|
|
38
|
+
alias_method :equal?, :eql?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
class Config
|
|
3
|
+
class EngineSet
|
|
4
|
+
attr_reader :engines
|
|
5
|
+
|
|
6
|
+
def initialize(data)
|
|
7
|
+
@data = data
|
|
8
|
+
@engines = []
|
|
9
|
+
|
|
10
|
+
build_set
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
attr_reader :data
|
|
16
|
+
|
|
17
|
+
def build_set
|
|
18
|
+
DefaultAdapter::ENGINES.keys.each do |name|
|
|
19
|
+
if (engine = extract_engine(name))
|
|
20
|
+
engines << engine
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
data.each do |name, engine_data|
|
|
25
|
+
engines << build_engine(name, engine_data)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def extract_engine(name)
|
|
30
|
+
if data[name]
|
|
31
|
+
engine_data = data.delete(name)
|
|
32
|
+
build_engine(name, engine_data)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def build_engine(name, data)
|
|
37
|
+
Config::Engine.new(
|
|
38
|
+
name,
|
|
39
|
+
enabled: data.fetch("enabled", true),
|
|
40
|
+
channel: data["channel"],
|
|
41
|
+
config: data,
|
|
42
|
+
exclude_patterns: data.fetch("exclude_patterns", []),
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
class Config
|
|
3
|
+
class JSONAdapter < Config
|
|
4
|
+
DEFAULT_PATH = ".codeclimate.json".freeze
|
|
5
|
+
|
|
6
|
+
attr_reader :config
|
|
7
|
+
|
|
8
|
+
def self.load(path = DEFAULT_PATH)
|
|
9
|
+
new(::JSON.parse(File.open(path).read))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize(json = {})
|
|
13
|
+
@config = json
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
class Config
|
|
3
|
+
class Prepare
|
|
4
|
+
attr_reader :fetch
|
|
5
|
+
|
|
6
|
+
def self.from_data(data)
|
|
7
|
+
if data.present?
|
|
8
|
+
fetch = Fetch.from_data(data.fetch("fetch", []))
|
|
9
|
+
|
|
10
|
+
new(fetch: fetch)
|
|
11
|
+
else
|
|
12
|
+
new
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def initialize(fetch: Fetch.new)
|
|
17
|
+
@fetch = fetch
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def merge(other)
|
|
21
|
+
Prepare.new(fetch: fetch.merge(other.fetch))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Fetch
|
|
25
|
+
def self.from_data(data)
|
|
26
|
+
new(data.map { |d| Entry.from_data(d) })
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize(entries = [])
|
|
30
|
+
@entries = Set.new(entries)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def each(&block)
|
|
34
|
+
entries.each(&block)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def merge(other)
|
|
38
|
+
Fetch.new(each.to_a | other.each.to_a)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
attr_reader :entries
|
|
44
|
+
|
|
45
|
+
class Entry
|
|
46
|
+
attr_reader :url, :path
|
|
47
|
+
|
|
48
|
+
def self.from_data(data)
|
|
49
|
+
case data
|
|
50
|
+
when String then new(data)
|
|
51
|
+
when Hash then new(data.fetch("url"), data["path"])
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def initialize(url, path = nil)
|
|
56
|
+
@url = url
|
|
57
|
+
@path = path || url.split("/").last
|
|
58
|
+
|
|
59
|
+
validate_path!
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Useful in specs
|
|
63
|
+
def ==(other)
|
|
64
|
+
other.is_a?(self.class) &&
|
|
65
|
+
other.url == url &&
|
|
66
|
+
other.path == path
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
# Duplicate a validation which has security implication. This should
|
|
72
|
+
# always be caught upstream, so raising loudly is fine.
|
|
73
|
+
def validate_path!
|
|
74
|
+
if path.blank?
|
|
75
|
+
raise ArgumentError, "path cannot be be blank"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
pathname = Pathname.new(path)
|
|
79
|
+
|
|
80
|
+
if pathname.absolute?
|
|
81
|
+
raise ArgumentError, "path cannot be absolute: #{path}"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
if pathname.cleanpath.to_s != pathname.to_s || path.include?("..")
|
|
85
|
+
raise ArgumentError, "path cannot point outside the current directory: #{path}"
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
class Config
|
|
3
|
+
module Validation
|
|
4
|
+
class CheckValidator
|
|
5
|
+
include HashValidations
|
|
6
|
+
|
|
7
|
+
attr_reader :errors, :warnings
|
|
8
|
+
|
|
9
|
+
def initialize(data)
|
|
10
|
+
@data = data
|
|
11
|
+
|
|
12
|
+
@errors = []
|
|
13
|
+
@warnings = []
|
|
14
|
+
|
|
15
|
+
validate
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
attr_reader :data
|
|
21
|
+
|
|
22
|
+
def validate
|
|
23
|
+
unless data.is_a?(Hash)
|
|
24
|
+
errors << "must be a hash"
|
|
25
|
+
return
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
validate_key_type("enabled", [TrueClass, FalseClass])
|
|
29
|
+
validate_key_type("config", Hash)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
module CC
|
|
2
|
+
class Config
|
|
3
|
+
module Validation
|
|
4
|
+
class EngineValidator
|
|
5
|
+
include HashValidations
|
|
6
|
+
|
|
7
|
+
RECOGNIZED_KEYS = %w[
|
|
8
|
+
enabled
|
|
9
|
+
channel
|
|
10
|
+
checks
|
|
11
|
+
config
|
|
12
|
+
exclude_fingerprints
|
|
13
|
+
exclude_patterns
|
|
14
|
+
].freeze
|
|
15
|
+
|
|
16
|
+
attr_reader :errors, :warnings
|
|
17
|
+
|
|
18
|
+
def initialize(data, legacy: false)
|
|
19
|
+
@data = data
|
|
20
|
+
@legacy = legacy
|
|
21
|
+
|
|
22
|
+
@errors = []
|
|
23
|
+
@warnings = []
|
|
24
|
+
|
|
25
|
+
validate
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def valid?
|
|
29
|
+
errors.none?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
attr_reader :data
|
|
35
|
+
|
|
36
|
+
def legacy?
|
|
37
|
+
@legacy
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def validate
|
|
41
|
+
validate_root
|
|
42
|
+
return unless data.is_a?(Hash)
|
|
43
|
+
|
|
44
|
+
validate_key_type("enabled", [TrueClass, FalseClass])
|
|
45
|
+
validate_key_type("channel", String)
|
|
46
|
+
validate_key_type("config", [String, Hash])
|
|
47
|
+
validate_key_type("exclude_patterns", Array)
|
|
48
|
+
if legacy?
|
|
49
|
+
validate_exclude_paths
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
validate_checks
|
|
53
|
+
validate_exclude_fingerprints
|
|
54
|
+
|
|
55
|
+
if legacy?
|
|
56
|
+
warn_unrecognized_keys(RECOGNIZED_KEYS + %w[exclude_paths])
|
|
57
|
+
else
|
|
58
|
+
warn_unrecognized_keys(RECOGNIZED_KEYS)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def validate_root
|
|
63
|
+
if !data.is_a?(Hash) && ![true, false].include?(data)
|
|
64
|
+
errors << "section must be a boolean or a hash"
|
|
65
|
+
return false
|
|
66
|
+
end
|
|
67
|
+
true
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def validate_checks
|
|
71
|
+
return unless validate_key_type("checks", Hash)
|
|
72
|
+
|
|
73
|
+
data.fetch("checks", {}).each do |_check_name, check_data|
|
|
74
|
+
validator = CheckValidator.new(check_data)
|
|
75
|
+
errors.push(*validator.errors)
|
|
76
|
+
warnings.push(*validator.warnings)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def validate_exclude_paths
|
|
81
|
+
validate_key_type("exclude_paths", [Array, String])
|
|
82
|
+
if data.key?("exclude_paths")
|
|
83
|
+
warnings << "'exclude_paths' has been deprecated, please use 'exclude_patterns' instead"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def validate_exclude_fingerprints
|
|
88
|
+
validate_key_type("exclude_fingerprints", Array)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
require "uri"
|
|
2
|
+
require "pathname"
|
|
3
|
+
|
|
4
|
+
module CC
|
|
5
|
+
class Config
|
|
6
|
+
module Validation
|
|
7
|
+
class FetchValidator
|
|
8
|
+
include HashValidations
|
|
9
|
+
|
|
10
|
+
attr_reader :errors, :warnings
|
|
11
|
+
|
|
12
|
+
def initialize(data)
|
|
13
|
+
@data = data
|
|
14
|
+
|
|
15
|
+
@errors = []
|
|
16
|
+
@warnings = []
|
|
17
|
+
|
|
18
|
+
validate
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
attr_reader :data
|
|
24
|
+
|
|
25
|
+
def validate
|
|
26
|
+
if data.is_a?(String)
|
|
27
|
+
validate_url(data)
|
|
28
|
+
elsif data.is_a?(Hash)
|
|
29
|
+
validate_fetch_hash
|
|
30
|
+
else
|
|
31
|
+
errors << "fetch section should be a string or a hash"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def validate_url(url)
|
|
36
|
+
unless valid_url?(url)
|
|
37
|
+
errors << "fetch section: invalid URL '#{url}'"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def valid_url?(url)
|
|
42
|
+
uri = URI.parse(url)
|
|
43
|
+
uri.is_a?(URI::HTTP) || uri.is_a?(URI::HTTPS)
|
|
44
|
+
rescue URI::InvalidURIError
|
|
45
|
+
false
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def validate_fetch_hash
|
|
49
|
+
if !data.key?("path") || !data.key?("url")
|
|
50
|
+
errors << "fetch section must include 'url' & 'path'"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
validate_key_type("path", String)
|
|
54
|
+
validate_key_type("url", String)
|
|
55
|
+
|
|
56
|
+
validate_path(data["path"])
|
|
57
|
+
validate_url(data["url"])
|
|
58
|
+
|
|
59
|
+
warn_unrecognized_keys(%w[path url])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def validate_path(path)
|
|
63
|
+
if path.nil? || path.length.zero?
|
|
64
|
+
errors << "fetch section's 'path' cannot be empty"
|
|
65
|
+
else
|
|
66
|
+
pathname = Pathname.new(path)
|
|
67
|
+
if pathname.absolute?
|
|
68
|
+
errors << "fetch section: absolute path '#{path}' is invalid"
|
|
69
|
+
end
|
|
70
|
+
if pathname.cleanpath.to_s != pathname.to_s || path.include?("..")
|
|
71
|
+
errors << "fetch section: relative path elements in '#{path}' are invalid"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|