fdlint 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +8 -0
- data/Gemfile.lock +14 -0
- data/README.md +68 -0
- data/Rakefile +92 -0
- data/bin/fdlint +17 -0
- data/lib/base_parser.rb +143 -0
- data/lib/cmd_runner.rb +145 -0
- data/lib/context.rb +31 -0
- data/lib/css/parser.rb +186 -0
- data/lib/css/reader.rb +30 -0
- data/lib/css/rule/check_compression_rule.rb +48 -0
- data/lib/css/rule/checklist.rb +45 -0
- data/lib/css/struct.rb +111 -0
- data/lib/encoding_error.rb +6 -0
- data/lib/file_validator.rb +38 -0
- data/lib/helper/code_type.rb +50 -0
- data/lib/helper/color_string.rb +44 -0
- data/lib/helper/file_reader.rb +22 -0
- data/lib/helper/strenc.rb +65 -0
- data/lib/html/parser.rb +212 -0
- data/lib/html/query.rb +96 -0
- data/lib/html/rule/check_tag_rule.rb +80 -0
- data/lib/html/struct.rb +291 -0
- data/lib/js/expr/expr.rb +66 -0
- data/lib/js/expr/left_hand.rb +63 -0
- data/lib/js/expr/operate.rb +92 -0
- data/lib/js/expr/primary.rb +166 -0
- data/lib/js/parser.rb +116 -0
- data/lib/js/rule/all.rb +35 -0
- data/lib/js/rule/checklist.rb +41 -0
- data/lib/js/rule/file_checker.rb +42 -0
- data/lib/js/rule/helper.rb +96 -0
- data/lib/js/rule/no_global.rb +87 -0
- data/lib/js/stat/if.rb +25 -0
- data/lib/js/stat/iter.rb +85 -0
- data/lib/js/stat/stat.rb +117 -0
- data/lib/js/stat/switch.rb +65 -0
- data/lib/js/stat/try.rb +28 -0
- data/lib/js/stat/var.rb +40 -0
- data/lib/js/struct.rb +248 -0
- data/lib/log_entry.rb +49 -0
- data/lib/node.rb +28 -0
- data/lib/parse_error.rb +13 -0
- data/lib/parser_visitable.rb +138 -0
- data/lib/position_info.rb +46 -0
- data/lib/printer/base_printer.rb +24 -0
- data/lib/printer/console_printer.rb +66 -0
- data/lib/printer/nocolor_printer.rb +27 -0
- data/lib/printer/vim_printer.rb +19 -0
- data/lib/rule.rb +241 -0
- data/lib/rule_helper.rb +14 -0
- data/lib/runner.rb +225 -0
- data/rules.d/css.rule +127 -0
- data/rules.d/html.dtd.rule +22 -0
- data/rules.d/html.prop.rule +51 -0
- data/rules.d/html.tag.rule +136 -0
- data/rules.d/js.file.rule +13 -0
- data/rules.d/js.jquery.rule +56 -0
- data/rules.d/js.mergefile.rule +71 -0
- data/rules.d/js.rule +84 -0
- data/test/all_tests.rb +84 -0
- data/test/cli/cli_test.rb +70 -0
- data/test/cli/log_level_test.rb +51 -0
- data/test/cli/output_format_test.rb +47 -0
- data/test/cli/type_test.rb +77 -0
- data/test/css/mac_line_end_support_test.rb +38 -0
- data/test/css/parser_test.rb +276 -0
- data/test/css/rule/check_encoding_test.rb +66 -0
- data/test/css/rule/check_list_rule_test.rb +167 -0
- data/test/css/rule/compression_test.rb +53 -0
- data/test/css/rule/file_name_test.rb +76 -0
- data/test/fixtures/css/broken.css +4 -0
- data/test/fixtures/css/cbu/36.css +52 -0
- data/test/fixtures/css/cbu/china_top.css +324 -0
- data/test/fixtures/css/cbu/default-merge.css +3 -0
- data/test/fixtures/css/cbu/default.css +13 -0
- data/test/fixtures/css/cbu/diy-merge.css +25 -0
- data/test/fixtures/css/cbu/fns-v1.css +27 -0
- data/test/fixtures/css/cbu/index_v0.1.css +12 -0
- data/test/fixtures/css/cbu/merge.css +11 -0
- data/test/fixtures/css/cbu/min.css +2 -0
- data/test/fixtures/css/cbu/my_home_admin.css +126 -0
- data/test/fixtures/css/cbu/nav.css +95 -0
- data/test/fixtures/css/cbu/pic_list.css +386 -0
- data/test/fixtures/css/cbu/quote-edit.css +18 -0
- data/test/fixtures/css/cbu/selloffer.shopwindow.css +1 -0
- data/test/fixtures/css/cbu/v1.css +9 -0
- data/test/fixtures/css/css3.css +30 -0
- data/test/fixtures/css/empty-min.css +0 -0
- data/test/fixtures/css/empty.css +0 -0
- data/test/fixtures/css/font-family.css +4 -0
- data/test/fixtures/css/gb-good.css +14 -0
- data/test/fixtures/css/gb_using_star.css +4 -0
- data/test/fixtures/css/import.css +18 -0
- data/test/fixtures/css/mac-line-sep-err-min.css +1 -0
- data/test/fixtures/css/mac-line-sep-err.css +1 -0
- data/test/fixtures/css/mac-line-sep-good-min.css +1 -0
- data/test/fixtures/css/mac-line-sep-good.css +1 -0
- data/test/fixtures/css/multi-encoding-in-a-file.css +0 -0
- data/test/fixtures/css/simple.css +1 -0
- data/test/fixtures/css/using_expr.css +8 -0
- data/test/fixtures/css/using_hack.css +21 -0
- data/test/fixtures/css/using_id.css +1 -0
- data/test/fixtures/css/using_star.css +4 -0
- data/test/fixtures/css/utf8_good.css +6 -0
- data/test/fixtures/css/utf8_good_declaring_charset.css +7 -0
- data/test/fixtures/css/utf8_using_star.css +5 -0
- data/test/fixtures/html/1-1.html +120 -0
- data/test/fixtures/html/1-2.html +120 -0
- data/test/fixtures/html/cms.html +373 -0
- data/test/fixtures/html/css_out_of_head.html +9 -0
- data/test/fixtures/html/fdev-template.html +22 -0
- data/test/fixtures/html/google.com.html +33 -0
- data/test/fixtures/html/mixed_log_levels.html +4 -0
- data/test/fixtures/html/mixed_types.html +13 -0
- data/test/fixtures/html/no_dtd.html +6 -0
- data/test/fixtures/html/readme.html +94 -0
- data/test/fixtures/html/review.board.html +163 -0
- data/test/fixtures/html/syntax_err.html +3 -0
- data/test/fixtures/html/train/detail/345/233/276/346/226/207/347/273/223/345/220/210.html +208 -0
- data/test/fixtures/html/train/detail/347/232/204Flash.html +212 -0
- data/test/fixtures/html/train/detail/347/232/204Vedio.html +212 -0
- data/test/fixtures/html/train/index.html +37 -0
- data/test/fixtures/html/train/test.html +1 -0
- data/test/fixtures/html/train//344/277/256/346/224/271/344/270/200/347/272/247/345/210/206/347/261/273.html +112 -0
- data/test/fixtures/html/train//344/277/256/346/224/271/345/255/220/345/210/206/347/261/273.html +108 -0
- data/test/fixtures/html/train//344/277/256/346/224/271/350/257/276/347/250/213.html +195 -0
- data/test/fixtures/html/train//345/215/232/345/256/242/350/256/276/347/275/256.html +142 -0
- data/test/fixtures/html/train//346/265/217/350/247/210/350/256/260/345/275/225.html +191 -0
- data/test/fixtures/html/train//346/267/273/345/212/240/344/270/200/347/272/247/345/210/206/347/261/273.html +113 -0
- data/test/fixtures/html/train//346/267/273/345/212/240/345/255/220/345/210/206/347/261/273.html +112 -0
- data/test/fixtures/html/train//346/267/273/345/212/240/350/257/276/347/250/213.html +195 -0
- data/test/fixtures/html/train//347/231/273/345/275/225.html +20 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/345/210/206/347/261/273.html +210 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/345/217/215/351/246/210.html +222 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/350/257/276/347/250/213.html +284 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/350/264/246/346/210/267.html +107 -0
- data/test/fixtures/html/train//347/275/221/344/270/212/345/237/271/350/256/255home/351/241/265.html +354 -0
- data/test/fixtures/html/train//347/275/221/345/225/206/345/237/271/350/256/255list/351/241/265.html +255 -0
- data/test/fixtures/html/train//350/256/276/347/275/256/351/246/226/351/241/265/346/216/250/350/215/220.html +168 -0
- data/test/fixtures/html/train//350/257/264/346/230/216.txt +3 -0
- data/test/fixtures/html/train//351/246/226/351/241/265/345/271/277/345/221/212/350/256/276/347/275/256.html +297 -0
- data/test/fixtures/html/unescaped.html +2 -0
- data/test/fixtures/html/view.vm +916 -0
- data/test/fixtures/js/jquery-1.7.js +9300 -0
- data/test/fixtures/js/scope-test.js +22 -0
- data/test/helper.rb +41 -0
- data/test/html/mixed_type_test.rb +35 -0
- data/test/html/parser/parse_comment_test.rb +47 -0
- data/test/html/parser/parse_dtd_test.rb +46 -0
- data/test/html/parser/parse_script_tag_test.rb +55 -0
- data/test/html/parser/parse_with_auto_close_tag_test.rb +41 -0
- data/test/html/parser/parse_with_diff_case_test.rb +38 -0
- data/test/html/parser/parse_with_emtpy_test.rb +22 -0
- data/test/html/parser/parse_with_multi_children_test.rb +27 -0
- data/test/html/parser/parse_with_multi_line_test.rb +41 -0
- data/test/html/parser/parse_with_prop_test.rb +88 -0
- data/test/html/parser/parse_with_script_tag_test.rb +26 -0
- data/test/html/parser/parse_with_selfclosing_test.rb +39 -0
- data/test/html/parser/parse_with_simple_tag_test.rb +44 -0
- data/test/html/parser/parse_with_simple_tree_test.rb +40 -0
- data/test/html/parser/parse_with_style_tag_test.rb +22 -0
- data/test/html/parser/parse_with_text_test.rb +45 -0
- data/test/html/parser_test.rb +52 -0
- data/test/html/query_test.rb +52 -0
- data/test/html/rule/check_block_level_element_test.rb +52 -0
- data/test/html/rule/check_button_test.rb +45 -0
- data/test/html/rule/check_class_count_test.rb +36 -0
- data/test/html/rule/check_css_in_head_test.rb +53 -0
- data/test/html/rule/check_dtd_test.rb +46 -0
- data/test/html/rule/check_form_element_name_test.rb +49 -0
- data/test/html/rule/check_head_contain_meta_and_title_test.rb +52 -0
- data/test/html/rule/check_html_template_test.rb +103 -0
- data/test/html/rule/check_hyperlink_with_target_test.rb +40 -0
- data/test/html/rule/check_hyperlink_with_title_test.rb +43 -0
- data/test/html/rule/check_id_n_class_downcase_test.rb +40 -0
- data/test/html/rule/check_img_with_alt_prop_test.rb +33 -0
- data/test/html/rule/check_no_import_css_test.rb +36 -0
- data/test/html/rule/check_prop_have_value_test.rb +32 -0
- data/test/html/rule/check_prop_seperator_test.rb +32 -0
- data/test/html/rule/check_style_prop_test.rb +30 -0
- data/test/html/rule/check_tag_closed_test.rb +59 -0
- data/test/html/rule/check_tag_downcase_test.rb +51 -0
- data/test/html/rule/check_unescape_char_test.rb +35 -0
- data/test/html/rule/check_unique_import_test.rb +56 -0
- data/test/html/rule_test.rb +62 -0
- data/test/js/expr/expr.rb +57 -0
- data/test/js/expr/left_hand.rb +25 -0
- data/test/js/expr/operate.rb +145 -0
- data/test/js/expr/primary.rb +89 -0
- data/test/js/parser_test.rb +98 -0
- data/test/js/rule/alert_check_test.rb +37 -0
- data/test/js/rule/all_test.rb +23 -0
- data/test/js/rule/base_test.rb +34 -0
- data/test/js/rule/file_checker_test.rb +131 -0
- data/test/js/rule/jq_check_test.rb +90 -0
- data/test/js/rule/nest_try_catch_test.rb +71 -0
- data/test/js/rule/new_object_and_new_array_test.rb +38 -0
- data/test/js/rule/no_eval_test.rb +34 -0
- data/test/js/rule/no_global_test.rb +88 -0
- data/test/js/rule/private_method_check_test.rb +58 -0
- data/test/js/rule/semicolon_test.rb +63 -0
- data/test/js/rule/stat_if_with_brace_test.rb +68 -0
- data/test/js/rule/stat_if_with_muti_else_test.rb +68 -0
- data/test/js/rule/use_strict_equal_test.rb +44 -0
- data/test/js/rule_test.rb +47 -0
- data/test/js/stat/if.rb +26 -0
- data/test/js/stat/iter.rb +115 -0
- data/test/js/stat/stat.rb +91 -0
- data/test/js/stat/switch.rb +37 -0
- data/test/js/stat/try.rb +32 -0
- data/test/js/stat/var.rb +38 -0
- data/test/parser_visitable_test.rb +102 -0
- data/test/position_info_test.rb +66 -0
- data/test/rule_dsl/dsl_basic_test.rb +91 -0
- data/test/rule_dsl/importing_test.rb +48 -0
- data/test/runner/log_level_test.rb +58 -0
- metadata +317 -0
@@ -0,0 +1,166 @@
|
|
1
|
+
module XRay
|
2
|
+
module JS
|
3
|
+
module Expr
|
4
|
+
|
5
|
+
module Primary
|
6
|
+
|
7
|
+
R_IDENTIFY = /[a-zA-Z_$][\w$]*/
|
8
|
+
|
9
|
+
RESERVED_WORDS = %w(
|
10
|
+
break case catch continue debugger default delete do else
|
11
|
+
finally for function if in instanceof new return switch this
|
12
|
+
throw try typeof var void while with
|
13
|
+
class const enum export extends import super
|
14
|
+
implements interface let package private protected public static yield
|
15
|
+
null true false
|
16
|
+
)
|
17
|
+
|
18
|
+
R_THIS_NULL_BOOLEAN = /(?:this|null|true|false)\b/
|
19
|
+
R_HEX = /0[xX]/
|
20
|
+
R_NUMBERIC = /[+-]?(?:\d|(?:[.]\d))/
|
21
|
+
R_STRING = /['"]/
|
22
|
+
R_REGEXP = /\/[^\/]/
|
23
|
+
|
24
|
+
def parse_expr_primary
|
25
|
+
log "parse expr primary"
|
26
|
+
if check /\(/
|
27
|
+
parse_expr_parentheses
|
28
|
+
|
29
|
+
elsif check /\[/
|
30
|
+
parse_expr_array
|
31
|
+
|
32
|
+
elsif check /\{/
|
33
|
+
parse_expr_object
|
34
|
+
|
35
|
+
# literal
|
36
|
+
elsif check R_THIS_NULL_BOOLEAN
|
37
|
+
parse_expr_literal_this_null_boolean
|
38
|
+
|
39
|
+
elsif check R_HEX
|
40
|
+
parse_expr_literal_hex
|
41
|
+
|
42
|
+
elsif check R_NUMBERIC
|
43
|
+
parse_expr_literal_number
|
44
|
+
|
45
|
+
elsif check R_STRING
|
46
|
+
parse_expr_literal_string
|
47
|
+
|
48
|
+
elsif check R_REGEXP
|
49
|
+
parse_expr_literal_regexp
|
50
|
+
#~
|
51
|
+
|
52
|
+
else
|
53
|
+
parse_expr_identifier
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_expr_parentheses
|
58
|
+
log 'parse expr parentheses'
|
59
|
+
|
60
|
+
pos = skip /\(/
|
61
|
+
expr = parse_expression
|
62
|
+
skip /\)/
|
63
|
+
|
64
|
+
create_expression 'parentheses', expr, pos
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_expr_array
|
68
|
+
log 'parse expr array'
|
69
|
+
|
70
|
+
pos = skip /\[/
|
71
|
+
elms = batch(:parse_expr_assignment, /\]/, /,/)
|
72
|
+
skip /\]/
|
73
|
+
|
74
|
+
create_expression 'array', Elements.new(elms), pos
|
75
|
+
end
|
76
|
+
|
77
|
+
def parse_expr_object
|
78
|
+
log 'parse expr object'
|
79
|
+
|
80
|
+
pos = skip /\{/
|
81
|
+
elms = batch(:parse_expr_object_item, /\}/, /,/)
|
82
|
+
skip /\}/
|
83
|
+
|
84
|
+
create_expression 'object', Elements.new(elms), pos
|
85
|
+
end
|
86
|
+
|
87
|
+
def parse_expr_object_item
|
88
|
+
log 'parse expr object item'
|
89
|
+
|
90
|
+
name = if check R_STRING
|
91
|
+
parse_expr_literal_string
|
92
|
+
elsif check R_NUMBERIC
|
93
|
+
parse_expr_literal_number
|
94
|
+
else
|
95
|
+
parse_expr_identifier
|
96
|
+
end
|
97
|
+
|
98
|
+
skip /:/
|
99
|
+
value = parse_expr_assignment
|
100
|
+
|
101
|
+
Expression.new ':', name, value
|
102
|
+
end
|
103
|
+
|
104
|
+
def parse_expr_identifier
|
105
|
+
log 'parse expr identifier'
|
106
|
+
|
107
|
+
id = scan(R_IDENTIFY)
|
108
|
+
RESERVED_WORDS.include?(id.text) ?
|
109
|
+
parse_error("identifier can not be reserved word: #{id}") : id
|
110
|
+
|
111
|
+
create_expression 'id', id
|
112
|
+
end
|
113
|
+
|
114
|
+
def parse_expr_literal_this_null_boolean
|
115
|
+
log 'parse expr literal this null boolean'
|
116
|
+
expr = scan /this|null|true|false/
|
117
|
+
type = expr.text.gsub(/true|false/, 'boolean')
|
118
|
+
|
119
|
+
create_expression type, expr
|
120
|
+
end
|
121
|
+
|
122
|
+
def parse_expr_literal_string
|
123
|
+
log 'parse expr literal string'
|
124
|
+
|
125
|
+
expr = if check /'/
|
126
|
+
scan /'(?:(?:\\')|(?:\\\n)|[^'\n])*'/
|
127
|
+
elsif check /"/
|
128
|
+
scan /"(?:(?:\\")|(?:\\\n)|[^"\n])*"/
|
129
|
+
else
|
130
|
+
raise 'assert false'
|
131
|
+
end
|
132
|
+
|
133
|
+
create_expression 'string', expr
|
134
|
+
end
|
135
|
+
|
136
|
+
def parse_expr_literal_hex
|
137
|
+
log 'parse expr literal hex'
|
138
|
+
expr = scan /0[xX][0-9a-fA-F]+/
|
139
|
+
create_expression 'number', expr
|
140
|
+
end
|
141
|
+
|
142
|
+
def parse_expr_literal_number
|
143
|
+
log 'parse expr literal number'
|
144
|
+
expr = scan /[+-]?(?:(?:\d*[.]\d+)|(?:\d+))(?:[eE][+-]?\d+)?/
|
145
|
+
create_expression 'number', expr
|
146
|
+
end
|
147
|
+
|
148
|
+
def parse_expr_literal_regexp
|
149
|
+
log 'parse expr literal regexp'
|
150
|
+
expr = scan %r{/(?:(?:\\.)|(?:\[(?:\\.|[^\[\]])+\])|[^/\\])+/[a-z]*}
|
151
|
+
create_expression 'regexp', expr
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def create_expression(*args)
|
157
|
+
expr = PrimaryExpression.new *args
|
158
|
+
log " #{expr}"
|
159
|
+
expr
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
data/lib/js/parser.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require_relative '../base_parser'
|
2
|
+
require_relative 'struct'
|
3
|
+
|
4
|
+
require_relative 'expr/expr'
|
5
|
+
require_relative 'stat/stat'
|
6
|
+
|
7
|
+
|
8
|
+
module XRay
|
9
|
+
module JS
|
10
|
+
|
11
|
+
class Parser < XRay::BaseParser
|
12
|
+
|
13
|
+
include Expr::Expr
|
14
|
+
include Stat::Stat
|
15
|
+
|
16
|
+
attr_reader :singleline_comments, :mutiline_comments
|
17
|
+
|
18
|
+
def initialize(js, logger)
|
19
|
+
super js, logger
|
20
|
+
@singleline_comments, @mutiline_comments = [], []
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_program
|
24
|
+
log 'parse program'
|
25
|
+
parse_comments
|
26
|
+
Program.new parse_source_elements
|
27
|
+
end
|
28
|
+
|
29
|
+
alias_method :parse, :parse_program
|
30
|
+
|
31
|
+
def parse_source_element
|
32
|
+
log 'parse source_element'
|
33
|
+
|
34
|
+
check(/function\b/) ? parse_function_declaration : parse_statement
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_function_declaration(skip_name = false)
|
38
|
+
log 'parse function declaration'
|
39
|
+
|
40
|
+
pos = skip /function/
|
41
|
+
|
42
|
+
name = (skip_name && check(/\(/)) ? nil : parse_function_name
|
43
|
+
skip /\(/
|
44
|
+
params = parse_function_parameters
|
45
|
+
skip /\)\s*\{/
|
46
|
+
body = parse_source_elements true
|
47
|
+
skip /\}/
|
48
|
+
|
49
|
+
FunctionDeclaraion.new name, params, body, pos
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_function_name
|
53
|
+
parse_expr_identifier
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_function_parameters
|
57
|
+
Elements.new batch(:parse_expr_identifier, /\)/, /,/)
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_singleline_comment
|
61
|
+
log 'parse singleline comment'
|
62
|
+
comment = raw_scan /\/\/.*/
|
63
|
+
log " #{comment}"
|
64
|
+
comment
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_mutiline_comment
|
68
|
+
log 'parse mutiline comment'
|
69
|
+
comment = raw_scan /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//
|
70
|
+
log " #{comment}"
|
71
|
+
comment
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
def create_element(klass, *args)
|
77
|
+
elm = klass.new *args
|
78
|
+
log " #{elm.text} #{elm.position}"
|
79
|
+
elm
|
80
|
+
end
|
81
|
+
|
82
|
+
#override
|
83
|
+
def after_scan(pattern)
|
84
|
+
parse_comments
|
85
|
+
end
|
86
|
+
|
87
|
+
def after_skip(pattern)
|
88
|
+
parse_comments
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def parse_source_elements(inner = false)
|
94
|
+
elms = batch(:parse_source_element) do
|
95
|
+
skip_empty
|
96
|
+
inner ? !check(/\}/) : !eos?
|
97
|
+
end
|
98
|
+
Elements.new elms
|
99
|
+
end
|
100
|
+
|
101
|
+
def parse_comments
|
102
|
+
while true
|
103
|
+
if check /\/\//
|
104
|
+
@singleline_comments << parse_singleline_comment
|
105
|
+
elsif check /\/\*/
|
106
|
+
@mutiline_comments << parse_mutiline_comment
|
107
|
+
else
|
108
|
+
break
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
data/lib/js/rule/all.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module XRay
|
4
|
+
module JS
|
5
|
+
module Rule
|
6
|
+
|
7
|
+
class All < SimpleDelegator
|
8
|
+
|
9
|
+
attr_reader :rules
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@rules = load_sub_rules.collect do |name|
|
13
|
+
klass = Rule.const_get name
|
14
|
+
klass.method(:initialize).arity >= 1 ? klass.new(options) :
|
15
|
+
klass.new
|
16
|
+
end
|
17
|
+
|
18
|
+
super @rules
|
19
|
+
end
|
20
|
+
|
21
|
+
def load_sub_rules
|
22
|
+
Dir.glob File.expand_path( '*.rb', File.dirname(__FILE__) ) do |file|
|
23
|
+
require file
|
24
|
+
end
|
25
|
+
XRay::JS::Rule.constants.select { |c| c.to_s.end_with? 'Rule' }
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require_relative 'helper'
|
3
|
+
require_relative '../../rule'
|
4
|
+
|
5
|
+
module XRay
|
6
|
+
module JS
|
7
|
+
module Rule
|
8
|
+
|
9
|
+
class ChecklistRule
|
10
|
+
|
11
|
+
include XRay::JS::Rule::Helper, XRay::Rule
|
12
|
+
|
13
|
+
def visit_statement(stat)
|
14
|
+
check_js_statement stat
|
15
|
+
end
|
16
|
+
|
17
|
+
def visit_stat_if(stat)
|
18
|
+
check_js_stat_if stat
|
19
|
+
end
|
20
|
+
|
21
|
+
def visit_expr_member(expr)
|
22
|
+
check_js_expr_member expr
|
23
|
+
end
|
24
|
+
|
25
|
+
def visit_expr_new(expr)
|
26
|
+
check_js_expr_new expr
|
27
|
+
end
|
28
|
+
|
29
|
+
def visit_expr_equal(expr)
|
30
|
+
check_js_expr_equal expr
|
31
|
+
end
|
32
|
+
|
33
|
+
def visit_stat_try(stat)
|
34
|
+
check_js_stat_try stat
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative '../../helper/file_reader'
|
4
|
+
require_relative 'helper'
|
5
|
+
require_relative '../../rule'
|
6
|
+
|
7
|
+
|
8
|
+
module XRay
|
9
|
+
module JS
|
10
|
+
module Rule
|
11
|
+
|
12
|
+
class FileChecker
|
13
|
+
|
14
|
+
include Helper, XRay::Rule
|
15
|
+
|
16
|
+
def check_file(path)
|
17
|
+
src = readfile(path)
|
18
|
+
ret = results_to_logs check_js_file( path, src )
|
19
|
+
|
20
|
+
if merge_file? path
|
21
|
+
pathes = grep_import_js_path( src )
|
22
|
+
ret.concat results_to_logs( check_js_merge_file( path, src, pathes ) )
|
23
|
+
ret.concat results_to_logs( check_merge_imports( pathes, path ) )
|
24
|
+
end
|
25
|
+
ret
|
26
|
+
end
|
27
|
+
|
28
|
+
def check_merge_imports( pathes, path )
|
29
|
+
ret = []
|
30
|
+
pathes.each do |import|
|
31
|
+
r = check_js_merge_importing import, pathes, path
|
32
|
+
ret.concat r
|
33
|
+
end
|
34
|
+
ret
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module XRay
|
2
|
+
module JS
|
3
|
+
module Rule
|
4
|
+
|
5
|
+
module Helper
|
6
|
+
def find_expr_member(expr)
|
7
|
+
while expr.is_a?(Expression) && !yield(expr)
|
8
|
+
expr = expr.left
|
9
|
+
end
|
10
|
+
expr && expr.is_a?(Expression) && yield(expr) ? expr : nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def has_doc_comment?(body)
|
14
|
+
body =~ /^\s*\/\*\*[^*]*\*+([^\/*][^*]*\*+)*\//
|
15
|
+
end
|
16
|
+
|
17
|
+
def readfile(path)
|
18
|
+
text, encoding = XRay::Helper::FileReader.readfile path
|
19
|
+
text
|
20
|
+
end
|
21
|
+
|
22
|
+
def merge_file?(path)
|
23
|
+
return path =~ /\w+-merge([-_]?\d+)?\.js$/
|
24
|
+
end
|
25
|
+
|
26
|
+
def min_file?(path)
|
27
|
+
return path =~ /\w+-min\.js$/
|
28
|
+
end
|
29
|
+
|
30
|
+
def func_file?(path)
|
31
|
+
!merge_file?(path) and !min_file?(path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def grep_import_js_path(body)
|
35
|
+
lines = body.split /\n/
|
36
|
+
pattern = /(ImportJavscript\s*\.\s*url\s*\(\s*['"]?([^'"]+)['"]?\s*\)\s*;?)/
|
37
|
+
|
38
|
+
pathes = []
|
39
|
+
lines.each_with_index do |line, index|
|
40
|
+
line.scan(pattern) do |text, url|
|
41
|
+
pathes << {
|
42
|
+
:text => text,
|
43
|
+
:url => url,
|
44
|
+
:row => index + 1,
|
45
|
+
:col => Regexp.last_match.begin(0) + 1
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
pathes
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def scope(path)
|
54
|
+
parts = path.split(/\\\//)
|
55
|
+
if parts.include? 'global'
|
56
|
+
:global
|
57
|
+
elsif parts.include? 'page'
|
58
|
+
:page
|
59
|
+
elsif parts.include? 'lib' or parts.include? 'sys'
|
60
|
+
:lib
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def global_scope?( path )
|
65
|
+
scope(path) == :global
|
66
|
+
end
|
67
|
+
|
68
|
+
def page_scope?( path )
|
69
|
+
scope(path) == :page
|
70
|
+
end
|
71
|
+
|
72
|
+
def lib_scope?( path )
|
73
|
+
scope(path) == :lib
|
74
|
+
end
|
75
|
+
|
76
|
+
def relative?( path )
|
77
|
+
path !~ %r(^https?://)
|
78
|
+
end
|
79
|
+
|
80
|
+
def app( path )
|
81
|
+
sep = /(?:\\|\/)/
|
82
|
+
path[/#{sep}app#{sep}(.*?)#{sep}(?:.+#{sep})*(global|page|module)(?:\\|\/)/, 1]
|
83
|
+
end
|
84
|
+
|
85
|
+
def results_to_logs( results )
|
86
|
+
results.map do |r|
|
87
|
+
msg, level, row, column = *r
|
88
|
+
LogEntry.new(msg, level, row || 0, column || 0)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module XRay
|
4
|
+
module JS
|
5
|
+
module Rule
|
6
|
+
|
7
|
+
class NoGlobalRule
|
8
|
+
|
9
|
+
def visit_stat_var(stat)
|
10
|
+
unless @scope_index > 0
|
11
|
+
['不允许使用全局变量', :error]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def visit_stat_var_declaration(dec)
|
16
|
+
scope = current_scope
|
17
|
+
scope << dec.left.text
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def visit_function_name(name)
|
22
|
+
current_scope << name.text
|
23
|
+
['不允许申明全局函数', :error] if @scope_index == 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def visit_function_parameters(params)
|
27
|
+
@scope_index = (@scope_index || 0) + 1
|
28
|
+
scope = current_scope(true)
|
29
|
+
params.each do |param|
|
30
|
+
scope << param.text
|
31
|
+
end
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def visit_stat_for(stat)
|
36
|
+
first = stat.condition.first
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
|
40
|
+
def visit_function_declaration(fun)
|
41
|
+
@scope_index -= 1
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def visit_expr_assignment(expr)
|
46
|
+
if expr.type == '='
|
47
|
+
id = find_assignment_id(expr.left)
|
48
|
+
|
49
|
+
if id && use_id_global?(id.text)
|
50
|
+
['禁止使用未定义的变量(或全局变量)', :error]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def current_scope(empty = false)
|
58
|
+
@scopes ||= []
|
59
|
+
@scope_index ||= 0
|
60
|
+
@scopes[@scope_index] = [] if empty
|
61
|
+
@scopes[@scope_index] ||= []
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_assignment_id(expr)
|
65
|
+
expr.type == 'id' ? expr :
|
66
|
+
expr.type == '.' && expr.left.text == 'window' &&
|
67
|
+
expr.right.type == 'id' ? expr.right : nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def use_id_global?(id)
|
71
|
+
white_list = %w(ImportJavscript)
|
72
|
+
|
73
|
+
return false if white_list.include? id
|
74
|
+
return true unless @scopes && @scope_index
|
75
|
+
|
76
|
+
@scope_index.downto(0) do |index|
|
77
|
+
scope = @scopes[index]
|
78
|
+
return false if scope && scope.find { |name| name == id}
|
79
|
+
end
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/js/stat/if.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module XRay
|
2
|
+
module JS
|
3
|
+
module Stat
|
4
|
+
|
5
|
+
module If
|
6
|
+
def parse_stat_if
|
7
|
+
log 'parse stat if'
|
8
|
+
|
9
|
+
pos = skip /if\s*\(/
|
10
|
+
condition = parse_expression
|
11
|
+
skip /\)/
|
12
|
+
|
13
|
+
true_part = parse_statement
|
14
|
+
false_part = if check /else\b/
|
15
|
+
skip /else/
|
16
|
+
parse_statement
|
17
|
+
end
|
18
|
+
|
19
|
+
create_element IfStatement, condition, true_part, false_part, pos
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/js/stat/iter.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
module XRay
|
2
|
+
module JS
|
3
|
+
module Stat
|
4
|
+
|
5
|
+
module Iter
|
6
|
+
|
7
|
+
def parse_stat_dowhile
|
8
|
+
log 'parse stat dowhile'
|
9
|
+
|
10
|
+
pos = skip /do/
|
11
|
+
body = parse_statement
|
12
|
+
|
13
|
+
skip /while\s*\(/
|
14
|
+
condition = parse_expression
|
15
|
+
skip /\)/
|
16
|
+
|
17
|
+
create_element DowhileStatement, body, condition, pos
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_stat_while
|
21
|
+
log 'parse stat while'
|
22
|
+
|
23
|
+
pos = skip /while\s*\(/
|
24
|
+
condition = parse_expression
|
25
|
+
skip /\)/
|
26
|
+
body = parse_statement
|
27
|
+
|
28
|
+
create_element WhileStatement, condition, body, pos
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse_stat_for
|
32
|
+
log 'parse stat for'
|
33
|
+
|
34
|
+
pos = skip /for/
|
35
|
+
|
36
|
+
con_pos = skip /\(/
|
37
|
+
first, is_var = parse_stat_for_con_first
|
38
|
+
type, second, third = parse_stat_for_con_other(first, is_var)
|
39
|
+
skip /\)/
|
40
|
+
|
41
|
+
condition = create_element ForConditionElement, type, first, second, third, con_pos
|
42
|
+
body = parse_statement
|
43
|
+
create_element ForStatement, condition, body, pos
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def parse_stat_for_con_first
|
49
|
+
self.expr_operate_not_in = true
|
50
|
+
first = if check /var\b/
|
51
|
+
skip /var/
|
52
|
+
is_var = true
|
53
|
+
parse_stat_var_declarationlist
|
54
|
+
elsif !check(/;/)
|
55
|
+
parse_expression
|
56
|
+
end
|
57
|
+
self.expr_operate_not_in = false
|
58
|
+
[first, is_var]
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse_stat_for_con_other(first, is_var)
|
62
|
+
if check /in\b/
|
63
|
+
if is_var && first.length != 1 || !is_var && !first.left_hand?
|
64
|
+
parse_error('first expression of for-condtion error')
|
65
|
+
end
|
66
|
+
|
67
|
+
skip /in/
|
68
|
+
type = is_var ? 'forvarin' : 'forin'
|
69
|
+
second = parse_expression
|
70
|
+
else
|
71
|
+
skip /;/
|
72
|
+
type = is_var ? 'forvar' : 'fordefault'
|
73
|
+
second = check(/;/) ? nil : parse_expression
|
74
|
+
skip /;/
|
75
|
+
|
76
|
+
third = check(/\)/) ? nil : parse_expression
|
77
|
+
end
|
78
|
+
[type, second, third]
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|