erb_lint 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/erb_lint/linters/deprecated_classes.rb +40 -117
- data/lib/erb_lint/linters/erb_safety.rb +35 -0
- metadata +31 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a211bea7ec247bcf1d79ede8f37db1b6b7d1b8c
|
4
|
+
data.tar.gz: 362ca7b4ffb1f184818889166cb572198c1def7e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45c2ff7d03c2ec43e37562887ad9cdc9f396b0084bf290036a848aa820fed661a0f99a49d120be62100aa15c640fa4374f5937c9accb0066a06028632ac56e8f
|
7
|
+
data.tar.gz: 16279ef10a1f004fe97309dde027de0bf244ac0a9e047b9a0ff04be73d5d9d71494e1e2ab08d584b9123ee96f35a05525ff7b8ff09d005d3f75f000c009eacbf
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'better_html'
|
4
|
+
|
3
5
|
module ERBLint
|
4
6
|
module Linters
|
5
7
|
# Checks for deprecated classes in the start tags of HTML elements.
|
@@ -22,144 +24,65 @@ module ERBLint
|
|
22
24
|
@addendum = config.fetch('addendum', '')
|
23
25
|
end
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
def lint_lines(lines)
|
27
|
+
def lint_file(file_content)
|
28
28
|
errors = []
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
errors.push(*generate_errors(class_name, index + 1))
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
29
|
+
iterator = build_iterator(file_content)
|
30
|
+
each_class_name_with_line(iterator) do |class_name, line|
|
31
|
+
errors.push(*generate_errors(class_name, line))
|
32
|
+
end
|
33
|
+
each_texthtml_script(iterator) do |text_node|
|
34
|
+
errors.push(*lint_file(text_node.content))
|
39
35
|
end
|
40
36
|
errors
|
41
37
|
end
|
42
38
|
|
43
39
|
private
|
44
40
|
|
45
|
-
def
|
46
|
-
|
47
|
-
suggestion = " #{violated_rule[:suggestion]}".rstrip
|
48
|
-
message = "Deprecated class `%s` detected matching the pattern `%s`.%s #{@addendum}".strip
|
49
|
-
{
|
50
|
-
line: line_number,
|
51
|
-
message: format(message, class_name, violated_rule[:class_expr], suggestion)
|
52
|
-
}
|
53
|
-
end
|
41
|
+
def build_iterator(file_content)
|
42
|
+
BetterHtml::NodeIterator.new(file_content, template_language: :html)
|
54
43
|
end
|
55
44
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
45
|
+
def each_class_name_with_line(iterator)
|
46
|
+
each_element_with_index(iterator) do |element, _index|
|
47
|
+
klass = element.find_attr('class')
|
48
|
+
next unless klass
|
49
|
+
klass.value_without_quotes.split(' ').each do |class_name|
|
50
|
+
yield class_name, klass.name_parts.first.location.line
|
51
|
+
end
|
59
52
|
end
|
60
53
|
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Provides methods and classes for finding HTML start tags and their attributes.
|
64
|
-
module StartTagHelper
|
65
|
-
# These patterns cover a superset of the W3 HTML5 specification.
|
66
|
-
# Additional cases not included in the spec include those that are still rendered by some browsers.
|
67
|
-
|
68
|
-
# Attribute Patterns
|
69
|
-
# https://www.w3.org/TR/html5/syntax.html#syntax-attributes
|
70
|
-
|
71
|
-
# attribute names must be non empty and can't contain a certain set of special characters
|
72
|
-
ATTRIBUTE_NAME_PATTERN = %r{[^\s"'>\/=]+}
|
73
|
-
|
74
|
-
ATTRIBUTE_VALUE_PATTERN = %r{
|
75
|
-
"([^"]*)" | # double-quoted value
|
76
|
-
'([^']*)' | # single-quoted value
|
77
|
-
([^\s"'=<>`]+) # unquoted non-empty value without special characters
|
78
|
-
}x
|
79
|
-
|
80
|
-
# attributes can be empty or have an attribute value
|
81
|
-
ATTRIBUTE_PATTERN = %r{
|
82
|
-
#{ATTRIBUTE_NAME_PATTERN} # attribute name
|
83
|
-
(
|
84
|
-
\s*=\s* # any whitespace around equals sign
|
85
|
-
(#{ATTRIBUTE_VALUE_PATTERN}) # attribute value
|
86
|
-
)? # attributes can be empty or have an assignemnt.
|
87
|
-
}x
|
88
|
-
|
89
|
-
# Start tag Patterns
|
90
|
-
# https://www.w3.org/TR/html5/syntax.html#syntax-start-tag
|
91
|
-
|
92
|
-
TAG_NAME_PATTERN = /[A-Za-z0-9]+/ # maybe add _ < ? etc later since it gets interpreted by some browsers
|
93
54
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
(
|
98
|
-
\s+ # required whitespace between tag name and first attribute and between attributes
|
99
|
-
#{ATTRIBUTE_PATTERN} # attributes
|
100
|
-
)*
|
101
|
-
)? # having an attribute block is optional
|
102
|
-
\/?> # void or foreign elements can have a slash before tag close
|
103
|
-
}x
|
104
|
-
|
105
|
-
# Represents and provides an interface for a start tag found in the HTML.
|
106
|
-
class StartTag
|
107
|
-
attr_accessor :tag_name, :attributes
|
108
|
-
|
109
|
-
def initialize(tag_name, attributes)
|
110
|
-
@tag_name = tag_name
|
111
|
-
@attributes = attributes
|
55
|
+
def each_element_with_index(iterator)
|
56
|
+
iterator.nodes.each_with_index do |node, index|
|
57
|
+
yield node, index if node.element?
|
112
58
|
end
|
113
59
|
end
|
114
60
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
@attribute_name = attribute_name
|
122
|
-
@value = value
|
123
|
-
end
|
61
|
+
def each_texthtml_script(iterator)
|
62
|
+
each_element_with_index(iterator) do |element, index|
|
63
|
+
type = element.find_attr('type')
|
64
|
+
next unless type
|
65
|
+
next unless 'text/html' == type.value_without_quotes
|
66
|
+
next_node = iterator.nodes[index + 1]
|
124
67
|
|
125
|
-
|
126
|
-
ATTR_NAME_CLASS_PATTERN.match(@attribute_name)
|
68
|
+
yield next_node if next_node&.text?
|
127
69
|
end
|
128
70
|
end
|
129
71
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
tag_name = start_tag_matching_group[1]
|
139
|
-
|
140
|
-
# attributes_string can be nil if there is no space after the tag name (and therefore no attributes).
|
141
|
-
attributes_string = start_tag_matching_group[2] || ''
|
142
|
-
|
143
|
-
attribute_list = attributes(attributes_string)
|
144
|
-
|
145
|
-
StartTag.new(tag_name, attribute_list)
|
146
|
-
end
|
72
|
+
def generate_errors(class_name, line_number)
|
73
|
+
violated_rules(class_name).map do |violated_rule|
|
74
|
+
suggestion = " #{violated_rule[:suggestion]}".rstrip
|
75
|
+
message = "Deprecated class `%s` detected matching the pattern `%s`.%s #{@addendum}".strip
|
76
|
+
{
|
77
|
+
line: line_number,
|
78
|
+
message: format(message, class_name, violated_rule[:class_expr], suggestion)
|
79
|
+
}
|
147
80
|
end
|
81
|
+
end
|
148
82
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
attributes_string.scan(/(#{ATTRIBUTE_PATTERN})/).map do |attribute_matching_group|
|
153
|
-
entire_string = attribute_matching_group[0]
|
154
|
-
value_with_equal_sign = attribute_matching_group[1] || '' # This can be nil if attribute is empty
|
155
|
-
name = entire_string.sub(value_with_equal_sign, '')
|
156
|
-
|
157
|
-
# The 3 captures [3..5] are the possibilities specified in ATTRIBUTE_VALUE_PATTERN
|
158
|
-
possible_value_formats = attribute_matching_group[3..5]
|
159
|
-
value = possible_value_formats.reduce { |a, e| a.nil? ? e : a }
|
160
|
-
|
161
|
-
Attribute.new(name, value)
|
162
|
-
end
|
83
|
+
def violated_rules(class_name)
|
84
|
+
@deprecated_ruleset.select do |deprecated_rule|
|
85
|
+
/\A#{deprecated_rule[:class_expr]}\z/.match(class_name)
|
163
86
|
end
|
164
87
|
end
|
165
88
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'better_html'
|
4
|
+
require 'better_html/test_helper/safe_erb_tester'
|
5
|
+
|
6
|
+
module ERBLint
|
7
|
+
module Linters
|
8
|
+
# Detect unsafe ruby interpolations into javascript.
|
9
|
+
class ErbSafety < Linter
|
10
|
+
include LinterRegistry
|
11
|
+
include BetterHtml::TestHelper::SafeErbTester
|
12
|
+
|
13
|
+
def initialize(config)
|
14
|
+
end
|
15
|
+
|
16
|
+
def lint_file(file_content)
|
17
|
+
errors = []
|
18
|
+
tester = Tester.new(file_content)
|
19
|
+
tester.errors.each do |error|
|
20
|
+
errors << format_error(error)
|
21
|
+
end
|
22
|
+
errors
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def format_error(error)
|
28
|
+
{
|
29
|
+
line: error.token.location.line,
|
30
|
+
message: error.message
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: erb_lint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Chan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-10-
|
11
|
+
date: 2017-10-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: html_tokenizer
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: better_html
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.0.5
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.0.5
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: rspec
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -49,6 +77,7 @@ files:
|
|
49
77
|
- lib/erb_lint/linter.rb
|
50
78
|
- lib/erb_lint/linter_registry.rb
|
51
79
|
- lib/erb_lint/linters/deprecated_classes.rb
|
80
|
+
- lib/erb_lint/linters/erb_safety.rb
|
52
81
|
- lib/erb_lint/linters/final_newline.rb
|
53
82
|
- lib/erb_lint/runner.rb
|
54
83
|
homepage: https://github.com/justinthec/erb-lint
|