actionpack 3.2.22.5 → 5.2.4

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.
Files changed (271) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +279 -603
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -297
  5. data/lib/abstract_controller/asset_paths.rb +4 -2
  6. data/lib/abstract_controller/base.rb +82 -52
  7. data/lib/abstract_controller/caching/fragments.rb +166 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +117 -103
  10. data/lib/abstract_controller/collector.rb +18 -7
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +65 -38
  13. data/lib/abstract_controller/logger.rb +3 -2
  14. data/lib/abstract_controller/railties/routes_helpers.rb +5 -3
  15. data/lib/abstract_controller/rendering.rb +77 -129
  16. data/lib/abstract_controller/translation.rb +21 -3
  17. data/lib/abstract_controller/url_for.rb +9 -7
  18. data/lib/abstract_controller.rb +12 -13
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/base.rb +81 -40
  22. data/lib/action_controller/caching.rb +22 -62
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +30 -18
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +190 -47
  27. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  28. data/lib/action_controller/metal/cookies.rb +3 -3
  29. data/lib/action_controller/metal/data_streaming.rb +40 -65
  30. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  31. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  32. data/lib/action_controller/metal/exceptions.rb +19 -12
  33. data/lib/action_controller/metal/flash.rb +42 -9
  34. data/lib/action_controller/metal/force_ssl.rb +79 -19
  35. data/lib/action_controller/metal/head.rb +35 -10
  36. data/lib/action_controller/metal/helpers.rb +31 -21
  37. data/lib/action_controller/metal/http_authentication.rb +182 -134
  38. data/lib/action_controller/metal/implicit_render.rb +62 -8
  39. data/lib/action_controller/metal/instrumentation.rb +28 -26
  40. data/lib/action_controller/metal/live.rb +312 -0
  41. data/lib/action_controller/metal/mime_responds.rb +159 -163
  42. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  43. data/lib/action_controller/metal/params_wrapper.rb +146 -93
  44. data/lib/action_controller/metal/redirecting.rb +80 -56
  45. data/lib/action_controller/metal/renderers.rb +119 -47
  46. data/lib/action_controller/metal/rendering.rb +89 -32
  47. data/lib/action_controller/metal/request_forgery_protection.rb +373 -41
  48. data/lib/action_controller/metal/rescue.rb +9 -16
  49. data/lib/action_controller/metal/streaming.rb +39 -45
  50. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  51. data/lib/action_controller/metal/testing.rb +8 -29
  52. data/lib/action_controller/metal/url_for.rb +43 -32
  53. data/lib/action_controller/metal.rb +112 -106
  54. data/lib/action_controller/railtie.rb +56 -18
  55. data/lib/action_controller/railties/helpers.rb +24 -0
  56. data/lib/action_controller/renderer.rb +117 -0
  57. data/lib/action_controller/template_assertions.rb +11 -0
  58. data/lib/action_controller/test_case.rb +402 -347
  59. data/lib/action_controller.rb +31 -30
  60. data/lib/action_dispatch/http/cache.rb +133 -34
  61. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  62. data/lib/action_dispatch/http/filter_parameters.rb +40 -24
  63. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  64. data/lib/action_dispatch/http/headers.rb +117 -16
  65. data/lib/action_dispatch/http/mime_negotiation.rb +98 -33
  66. data/lib/action_dispatch/http/mime_type.rb +198 -146
  67. data/lib/action_dispatch/http/mime_types.rb +22 -7
  68. data/lib/action_dispatch/http/parameter_filter.rb +61 -49
  69. data/lib/action_dispatch/http/parameters.rb +94 -51
  70. data/lib/action_dispatch/http/rack_cache.rb +4 -3
  71. data/lib/action_dispatch/http/request.rb +262 -117
  72. data/lib/action_dispatch/http/response.rb +400 -86
  73. data/lib/action_dispatch/http/upload.rb +66 -29
  74. data/lib/action_dispatch/http/url.rb +232 -60
  75. data/lib/action_dispatch/journey/formatter.rb +189 -0
  76. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  77. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  78. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  79. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  80. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  81. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  82. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  83. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  84. data/lib/action_dispatch/journey/parser.rb +199 -0
  85. data/lib/action_dispatch/journey/parser.y +50 -0
  86. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  87. data/lib/action_dispatch/journey/path/pattern.rb +199 -0
  88. data/lib/action_dispatch/journey/route.rb +203 -0
  89. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  90. data/lib/action_dispatch/journey/router.rb +156 -0
  91. data/lib/action_dispatch/journey/routes.rb +82 -0
  92. data/lib/action_dispatch/journey/scanner.rb +64 -0
  93. data/lib/action_dispatch/journey/visitors.rb +268 -0
  94. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  95. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  96. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  97. data/lib/action_dispatch/journey.rb +7 -0
  98. data/lib/action_dispatch/middleware/callbacks.rb +17 -13
  99. data/lib/action_dispatch/middleware/cookies.rb +494 -162
  100. data/lib/action_dispatch/middleware/debug_exceptions.rb +176 -53
  101. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  102. data/lib/action_dispatch/middleware/exception_wrapper.rb +103 -38
  103. data/lib/action_dispatch/middleware/executor.rb +21 -0
  104. data/lib/action_dispatch/middleware/flash.rb +128 -91
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +43 -16
  106. data/lib/action_dispatch/middleware/reloader.rb +6 -83
  107. data/lib/action_dispatch/middleware/remote_ip.rb +151 -49
  108. data/lib/action_dispatch/middleware/request_id.rb +19 -15
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +38 -34
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +14 -9
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +94 -44
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -4
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +36 -61
  114. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  115. data/lib/action_dispatch/middleware/stack.rb +33 -41
  116. data/lib/action_dispatch/middleware/static.rb +92 -48
  117. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +134 -5
  128. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  136. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  138. data/lib/action_dispatch/railtie.rb +29 -8
  139. data/lib/action_dispatch/request/session.rb +234 -0
  140. data/lib/action_dispatch/request/utils.rb +78 -0
  141. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  142. data/lib/action_dispatch/routing/inspector.rb +225 -0
  143. data/lib/action_dispatch/routing/mapper.rb +1329 -582
  144. data/lib/action_dispatch/routing/polymorphic_routes.rb +237 -94
  145. data/lib/action_dispatch/routing/redirection.rb +120 -50
  146. data/lib/action_dispatch/routing/route_set.rb +545 -322
  147. data/lib/action_dispatch/routing/routes_proxy.rb +37 -7
  148. data/lib/action_dispatch/routing/url_for.rb +103 -34
  149. data/lib/action_dispatch/routing.rb +66 -99
  150. data/lib/action_dispatch/system_test_case.rb +147 -0
  151. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  152. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  153. data/lib/action_dispatch/system_testing/server.rb +31 -0
  154. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  157. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  158. data/lib/action_dispatch/testing/assertions/response.rb +53 -42
  159. data/lib/action_dispatch/testing/assertions/routing.rb +79 -74
  160. data/lib/action_dispatch/testing/assertions.rb +15 -9
  161. data/lib/action_dispatch/testing/integration.rb +361 -207
  162. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  163. data/lib/action_dispatch/testing/test_process.rb +28 -19
  164. data/lib/action_dispatch/testing/test_request.rb +30 -33
  165. data/lib/action_dispatch/testing/test_response.rb +35 -11
  166. data/lib/action_dispatch.rb +42 -32
  167. data/lib/action_pack/gem_version.rb +17 -0
  168. data/lib/action_pack/version.rb +7 -7
  169. data/lib/action_pack.rb +4 -2
  170. metadata +116 -175
  171. data/lib/abstract_controller/layouts.rb +0 -423
  172. data/lib/abstract_controller/view_paths.rb +0 -96
  173. data/lib/action_controller/caching/actions.rb +0 -185
  174. data/lib/action_controller/caching/fragments.rb +0 -127
  175. data/lib/action_controller/caching/pages.rb +0 -187
  176. data/lib/action_controller/caching/sweeping.rb +0 -97
  177. data/lib/action_controller/deprecated/integration_test.rb +0 -2
  178. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  179. data/lib/action_controller/deprecated.rb +0 -3
  180. data/lib/action_controller/metal/compatibility.rb +0 -65
  181. data/lib/action_controller/metal/hide_actions.rb +0 -41
  182. data/lib/action_controller/metal/rack_delegation.rb +0 -26
  183. data/lib/action_controller/metal/responder.rb +0 -286
  184. data/lib/action_controller/metal/session_management.rb +0 -14
  185. data/lib/action_controller/middleware.rb +0 -39
  186. data/lib/action_controller/railties/paths.rb +0 -25
  187. data/lib/action_controller/record_identifier.rb +0 -85
  188. data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
  189. data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
  190. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
  191. data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
  192. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
  193. data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
  194. data/lib/action_controller/vendor/html-scanner.rb +0 -20
  195. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  196. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  197. data/lib/action_dispatch/middleware/head.rb +0 -18
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -75
  199. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  200. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
  201. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
  202. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
  203. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
  204. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
  205. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
  206. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
  207. data/lib/action_dispatch/testing/assertions/dom.rb +0 -37
  208. data/lib/action_dispatch/testing/assertions/selector.rb +0 -435
  209. data/lib/action_dispatch/testing/assertions/tag.rb +0 -138
  210. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  211. data/lib/action_view/asset_paths.rb +0 -142
  212. data/lib/action_view/base.rb +0 -220
  213. data/lib/action_view/buffers.rb +0 -43
  214. data/lib/action_view/context.rb +0 -36
  215. data/lib/action_view/flows.rb +0 -79
  216. data/lib/action_view/helpers/active_model_helper.rb +0 -50
  217. data/lib/action_view/helpers/asset_paths.rb +0 -7
  218. data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
  219. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  220. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  221. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  222. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  223. data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
  224. data/lib/action_view/helpers/cache_helper.rb +0 -64
  225. data/lib/action_view/helpers/capture_helper.rb +0 -203
  226. data/lib/action_view/helpers/controller_helper.rb +0 -25
  227. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  228. data/lib/action_view/helpers/date_helper.rb +0 -1062
  229. data/lib/action_view/helpers/debug_helper.rb +0 -40
  230. data/lib/action_view/helpers/form_helper.rb +0 -1486
  231. data/lib/action_view/helpers/form_options_helper.rb +0 -658
  232. data/lib/action_view/helpers/form_tag_helper.rb +0 -685
  233. data/lib/action_view/helpers/javascript_helper.rb +0 -110
  234. data/lib/action_view/helpers/number_helper.rb +0 -622
  235. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  236. data/lib/action_view/helpers/record_tag_helper.rb +0 -111
  237. data/lib/action_view/helpers/rendering_helper.rb +0 -92
  238. data/lib/action_view/helpers/sanitize_helper.rb +0 -259
  239. data/lib/action_view/helpers/tag_helper.rb +0 -167
  240. data/lib/action_view/helpers/text_helper.rb +0 -426
  241. data/lib/action_view/helpers/translation_helper.rb +0 -91
  242. data/lib/action_view/helpers/url_helper.rb +0 -693
  243. data/lib/action_view/helpers.rb +0 -60
  244. data/lib/action_view/locale/en.yml +0 -160
  245. data/lib/action_view/log_subscriber.rb +0 -28
  246. data/lib/action_view/lookup_context.rb +0 -258
  247. data/lib/action_view/path_set.rb +0 -101
  248. data/lib/action_view/railtie.rb +0 -55
  249. data/lib/action_view/renderer/abstract_renderer.rb +0 -41
  250. data/lib/action_view/renderer/partial_renderer.rb +0 -415
  251. data/lib/action_view/renderer/renderer.rb +0 -61
  252. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
  253. data/lib/action_view/renderer/template_renderer.rb +0 -95
  254. data/lib/action_view/template/error.rb +0 -128
  255. data/lib/action_view/template/handlers/builder.rb +0 -26
  256. data/lib/action_view/template/handlers/erb.rb +0 -125
  257. data/lib/action_view/template/handlers.rb +0 -50
  258. data/lib/action_view/template/resolver.rb +0 -298
  259. data/lib/action_view/template/text.rb +0 -30
  260. data/lib/action_view/template.rb +0 -337
  261. data/lib/action_view/test_case.rb +0 -246
  262. data/lib/action_view/testing/resolvers.rb +0 -49
  263. data/lib/action_view.rb +0 -84
  264. data/lib/sprockets/assets.rake +0 -99
  265. data/lib/sprockets/bootstrap.rb +0 -37
  266. data/lib/sprockets/compressors.rb +0 -83
  267. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  268. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  269. data/lib/sprockets/helpers.rb +0 -6
  270. data/lib/sprockets/railtie.rb +0 -62
  271. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,532 +0,0 @@
1
- require 'strscan'
2
-
3
- module HTML #:nodoc:
4
-
5
- class Conditions < Hash #:nodoc:
6
- def initialize(hash)
7
- super()
8
- hash = { :content => hash } unless Hash === hash
9
- hash = keys_to_symbols(hash)
10
- hash.each do |k,v|
11
- case k
12
- when :tag, :content then
13
- # keys are valid, and require no further processing
14
- when :attributes then
15
- hash[k] = keys_to_strings(v)
16
- when :parent, :child, :ancestor, :descendant, :sibling, :before,
17
- :after
18
- hash[k] = Conditions.new(v)
19
- when :children
20
- hash[k] = v = keys_to_symbols(v)
21
- v.each do |key,value|
22
- case key
23
- when :count, :greater_than, :less_than
24
- # keys are valid, and require no further processing
25
- when :only
26
- v[key] = Conditions.new(value)
27
- else
28
- raise "illegal key #{key.inspect} => #{value.inspect}"
29
- end
30
- end
31
- else
32
- raise "illegal key #{k.inspect} => #{v.inspect}"
33
- end
34
- end
35
- update hash
36
- end
37
-
38
- private
39
-
40
- def keys_to_strings(hash)
41
- Hash[hash.keys.map {|k| [k.to_s, hash[k]]}]
42
- end
43
-
44
- def keys_to_symbols(hash)
45
- Hash[hash.keys.map do |k|
46
- raise "illegal key #{k.inspect}" unless k.respond_to?(:to_sym)
47
- [k.to_sym, hash[k]]
48
- end]
49
- end
50
- end
51
-
52
- # The base class of all nodes, textual and otherwise, in an HTML document.
53
- class Node #:nodoc:
54
- # The array of children of this node. Not all nodes have children.
55
- attr_reader :children
56
-
57
- # The parent node of this node. All nodes have a parent, except for the
58
- # root node.
59
- attr_reader :parent
60
-
61
- # The line number of the input where this node was begun
62
- attr_reader :line
63
-
64
- # The byte position in the input where this node was begun
65
- attr_reader :position
66
-
67
- # Create a new node as a child of the given parent.
68
- def initialize(parent, line=0, pos=0)
69
- @parent = parent
70
- @children = []
71
- @line, @position = line, pos
72
- end
73
-
74
- # Return a textual representation of the node.
75
- def to_s
76
- @children.join()
77
- end
78
-
79
- # Return false (subclasses must override this to provide specific matching
80
- # behavior.) +conditions+ may be of any type.
81
- def match(conditions)
82
- false
83
- end
84
-
85
- # Search the children of this node for the first node for which #find
86
- # returns non +nil+. Returns the result of the #find call that succeeded.
87
- def find(conditions)
88
- conditions = validate_conditions(conditions)
89
- @children.each do |child|
90
- node = child.find(conditions)
91
- return node if node
92
- end
93
- nil
94
- end
95
-
96
- # Search for all nodes that match the given conditions, and return them
97
- # as an array.
98
- def find_all(conditions)
99
- conditions = validate_conditions(conditions)
100
-
101
- matches = []
102
- matches << self if match(conditions)
103
- @children.each do |child|
104
- matches.concat child.find_all(conditions)
105
- end
106
- matches
107
- end
108
-
109
- # Returns +false+. Subclasses may override this if they define a kind of
110
- # tag.
111
- def tag?
112
- false
113
- end
114
-
115
- def validate_conditions(conditions)
116
- Conditions === conditions ? conditions : Conditions.new(conditions)
117
- end
118
-
119
- def ==(node)
120
- return false unless self.class == node.class && children.size == node.children.size
121
-
122
- equivalent = true
123
-
124
- children.size.times do |i|
125
- equivalent &&= children[i] == node.children[i]
126
- end
127
-
128
- equivalent
129
- end
130
-
131
- class <<self
132
- def parse(parent, line, pos, content, strict=true)
133
- if content !~ /^<\S/
134
- Text.new(parent, line, pos, content)
135
- else
136
- scanner = StringScanner.new(content)
137
-
138
- unless scanner.skip(/</)
139
- if strict
140
- raise "expected <"
141
- else
142
- return Text.new(parent, line, pos, content)
143
- end
144
- end
145
-
146
- if scanner.skip(/!\[CDATA\[/)
147
- unless scanner.skip_until(/\]\]>/)
148
- if strict
149
- raise "expected ]]> (got #{scanner.rest.inspect} for #{content})"
150
- else
151
- scanner.skip_until(/\Z/)
152
- end
153
- end
154
-
155
- return CDATA.new(parent, line, pos, scanner.pre_match.gsub(/<!\[CDATA\[/, ''))
156
- end
157
-
158
- closing = ( scanner.scan(/\//) ? :close : nil )
159
- return Text.new(parent, line, pos, content) unless name = scanner.scan(/[^\s!>\/]+/)
160
- name.downcase!
161
-
162
- unless closing
163
- scanner.skip(/\s*/)
164
- attributes = {}
165
- while attr = scanner.scan(/[-\w:]+/)
166
- value = true
167
- if scanner.scan(/\s*=\s*/)
168
- if delim = scanner.scan(/['"]/)
169
- value = ""
170
- while text = scanner.scan(/[^#{delim}\\]+|./)
171
- case text
172
- when "\\" then
173
- value << text
174
- break if scanner.eos?
175
- value << scanner.getch
176
- when delim
177
- break
178
- else value << text
179
- end
180
- end
181
- else
182
- value = scanner.scan(/[^\s>\/]+/)
183
- end
184
- end
185
- attributes[attr.downcase] = value
186
- scanner.skip(/\s*/)
187
- end
188
-
189
- closing = ( scanner.scan(/\//) ? :self : nil )
190
- end
191
-
192
- unless scanner.scan(/\s*>/)
193
- if strict
194
- raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})"
195
- else
196
- # throw away all text until we find what we're looking for
197
- scanner.skip_until(/>/) or scanner.terminate
198
- end
199
- end
200
-
201
- Tag.new(parent, line, pos, name, attributes, closing)
202
- end
203
- end
204
- end
205
- end
206
-
207
- # A node that represents text, rather than markup.
208
- class Text < Node #:nodoc:
209
-
210
- attr_reader :content
211
-
212
- # Creates a new text node as a child of the given parent, with the given
213
- # content.
214
- def initialize(parent, line, pos, content)
215
- super(parent, line, pos)
216
- @content = content
217
- end
218
-
219
- # Returns the content of this node.
220
- def to_s
221
- @content
222
- end
223
-
224
- # Returns +self+ if this node meets the given conditions. Text nodes support
225
- # conditions of the following kinds:
226
- #
227
- # * if +conditions+ is a string, it must be a substring of the node's
228
- # content
229
- # * if +conditions+ is a regular expression, it must match the node's
230
- # content
231
- # * if +conditions+ is a hash, it must contain a <tt>:content</tt> key that
232
- # is either a string or a regexp, and which is interpreted as described
233
- # above.
234
- def find(conditions)
235
- match(conditions) && self
236
- end
237
-
238
- # Returns non-+nil+ if this node meets the given conditions, or +nil+
239
- # otherwise. See the discussion of #find for the valid conditions.
240
- def match(conditions)
241
- case conditions
242
- when String
243
- @content == conditions
244
- when Regexp
245
- @content =~ conditions
246
- when Hash
247
- conditions = validate_conditions(conditions)
248
-
249
- # Text nodes only have :content, :parent, :ancestor
250
- unless (conditions.keys - [:content, :parent, :ancestor]).empty?
251
- return false
252
- end
253
-
254
- match(conditions[:content])
255
- else
256
- nil
257
- end
258
- end
259
-
260
- def ==(node)
261
- return false unless super
262
- content == node.content
263
- end
264
- end
265
-
266
- # A CDATA node is simply a text node with a specialized way of displaying
267
- # itself.
268
- class CDATA < Text #:nodoc:
269
- def to_s
270
- "<![CDATA[#{super}]]>"
271
- end
272
- end
273
-
274
- # A Tag is any node that represents markup. It may be an opening tag, a
275
- # closing tag, or a self-closing tag. It has a name, and may have a hash of
276
- # attributes.
277
- class Tag < Node #:nodoc:
278
-
279
- # Either +nil+, <tt>:close</tt>, or <tt>:self</tt>
280
- attr_reader :closing
281
-
282
- # Either +nil+, or a hash of attributes for this node.
283
- attr_reader :attributes
284
-
285
- # The name of this tag.
286
- attr_reader :name
287
-
288
- # Create a new node as a child of the given parent, using the given content
289
- # to describe the node. It will be parsed and the node name, attributes and
290
- # closing status extracted.
291
- def initialize(parent, line, pos, name, attributes, closing)
292
- super(parent, line, pos)
293
- @name = name
294
- @attributes = attributes
295
- @closing = closing
296
- end
297
-
298
- # A convenience for obtaining an attribute of the node. Returns +nil+ if
299
- # the node has no attributes.
300
- def [](attr)
301
- @attributes ? @attributes[attr] : nil
302
- end
303
-
304
- # Returns non-+nil+ if this tag can contain child nodes.
305
- def childless?(xml = false)
306
- return false if xml && @closing.nil?
307
- !@closing.nil? ||
308
- @name =~ /^(img|br|hr|link|meta|area|base|basefont|
309
- col|frame|input|isindex|param)$/ox
310
- end
311
-
312
- # Returns a textual representation of the node
313
- def to_s
314
- if @closing == :close
315
- "</#{@name}>"
316
- else
317
- s = "<#{@name}"
318
- @attributes.each do |k,v|
319
- s << " #{k}"
320
- s << "=\"#{v}\"" if String === v
321
- end
322
- s << " /" if @closing == :self
323
- s << ">"
324
- @children.each { |child| s << child.to_s }
325
- s << "</#{@name}>" if @closing != :self && !@children.empty?
326
- s
327
- end
328
- end
329
-
330
- # If either the node or any of its children meet the given conditions, the
331
- # matching node is returned. Otherwise, +nil+ is returned. (See the
332
- # description of the valid conditions in the +match+ method.)
333
- def find(conditions)
334
- match(conditions) && self || super
335
- end
336
-
337
- # Returns +true+, indicating that this node represents an HTML tag.
338
- def tag?
339
- true
340
- end
341
-
342
- # Returns +true+ if the node meets any of the given conditions. The
343
- # +conditions+ parameter must be a hash of any of the following keys
344
- # (all are optional):
345
- #
346
- # * <tt>:tag</tt>: the node name must match the corresponding value
347
- # * <tt>:attributes</tt>: a hash. The node's values must match the
348
- # corresponding values in the hash.
349
- # * <tt>:parent</tt>: a hash. The node's parent must match the
350
- # corresponding hash.
351
- # * <tt>:child</tt>: a hash. At least one of the node's immediate children
352
- # must meet the criteria described by the hash.
353
- # * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
354
- # meet the criteria described by the hash.
355
- # * <tt>:descendant</tt>: a hash. At least one of the node's descendants
356
- # must meet the criteria described by the hash.
357
- # * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
358
- # meet the criteria described by the hash.
359
- # * <tt>:after</tt>: a hash. The node must be after any sibling meeting
360
- # the criteria described by the hash, and at least one sibling must match.
361
- # * <tt>:before</tt>: a hash. The node must be before any sibling meeting
362
- # the criteria described by the hash, and at least one sibling must match.
363
- # * <tt>:children</tt>: a hash, for counting children of a node. Accepts the
364
- # keys:
365
- # ** <tt>:count</tt>: either a number or a range which must equal (or
366
- # include) the number of children that match.
367
- # ** <tt>:less_than</tt>: the number of matching children must be less than
368
- # this number.
369
- # ** <tt>:greater_than</tt>: the number of matching children must be
370
- # greater than this number.
371
- # ** <tt>:only</tt>: another hash consisting of the keys to use
372
- # to match on the children, and only matching children will be
373
- # counted.
374
- #
375
- # Conditions are matched using the following algorithm:
376
- #
377
- # * if the condition is a string, it must be a substring of the value.
378
- # * if the condition is a regexp, it must match the value.
379
- # * if the condition is a number, the value must match number.to_s.
380
- # * if the condition is +true+, the value must not be +nil+.
381
- # * if the condition is +false+ or +nil+, the value must be +nil+.
382
- #
383
- # Usage:
384
- #
385
- # # test if the node is a "span" tag
386
- # node.match :tag => "span"
387
- #
388
- # # test if the node's parent is a "div"
389
- # node.match :parent => { :tag => "div" }
390
- #
391
- # # test if any of the node's ancestors are "table" tags
392
- # node.match :ancestor => { :tag => "table" }
393
- #
394
- # # test if any of the node's immediate children are "em" tags
395
- # node.match :child => { :tag => "em" }
396
- #
397
- # # test if any of the node's descendants are "strong" tags
398
- # node.match :descendant => { :tag => "strong" }
399
- #
400
- # # test if the node has between 2 and 4 span tags as immediate children
401
- # node.match :children => { :count => 2..4, :only => { :tag => "span" } }
402
- #
403
- # # get funky: test to see if the node is a "div", has a "ul" ancestor
404
- # # and an "li" parent (with "class" = "enum"), and whether or not it has
405
- # # a "span" descendant that contains # text matching /hello world/:
406
- # node.match :tag => "div",
407
- # :ancestor => { :tag => "ul" },
408
- # :parent => { :tag => "li",
409
- # :attributes => { :class => "enum" } },
410
- # :descendant => { :tag => "span",
411
- # :child => /hello world/ }
412
- def match(conditions)
413
- conditions = validate_conditions(conditions)
414
- # check content of child nodes
415
- if conditions[:content]
416
- if children.empty?
417
- return false unless match_condition("", conditions[:content])
418
- else
419
- return false unless children.find { |child| child.match(conditions[:content]) }
420
- end
421
- end
422
-
423
- # test the name
424
- return false unless match_condition(@name, conditions[:tag]) if conditions[:tag]
425
-
426
- # test attributes
427
- (conditions[:attributes] || {}).each do |key, value|
428
- return false unless match_condition(self[key], value)
429
- end
430
-
431
- # test parent
432
- return false unless parent.match(conditions[:parent]) if conditions[:parent]
433
-
434
- # test children
435
- return false unless children.find { |child| child.match(conditions[:child]) } if conditions[:child]
436
-
437
- # test ancestors
438
- if conditions[:ancestor]
439
- return false unless catch :found do
440
- p = self
441
- throw :found, true if p.match(conditions[:ancestor]) while p = p.parent
442
- end
443
- end
444
-
445
- # test descendants
446
- if conditions[:descendant]
447
- return false unless children.find do |child|
448
- # test the child
449
- child.match(conditions[:descendant]) ||
450
- # test the child's descendants
451
- child.match(:descendant => conditions[:descendant])
452
- end
453
- end
454
-
455
- # count children
456
- if opts = conditions[:children]
457
- matches = children.select do |c|
458
- (c.kind_of?(HTML::Tag) and (c.closing == :self or ! c.childless?))
459
- end
460
-
461
- matches = matches.select { |c| c.match(opts[:only]) } if opts[:only]
462
- opts.each do |key, value|
463
- next if key == :only
464
- case key
465
- when :count
466
- if Integer === value
467
- return false if matches.length != value
468
- else
469
- return false unless value.include?(matches.length)
470
- end
471
- when :less_than
472
- return false unless matches.length < value
473
- when :greater_than
474
- return false unless matches.length > value
475
- else raise "unknown count condition #{key}"
476
- end
477
- end
478
- end
479
-
480
- # test siblings
481
- if conditions[:sibling] || conditions[:before] || conditions[:after]
482
- siblings = parent ? parent.children : []
483
- self_index = siblings.index(self)
484
-
485
- if conditions[:sibling]
486
- return false unless siblings.detect do |s|
487
- s != self && s.match(conditions[:sibling])
488
- end
489
- end
490
-
491
- if conditions[:before]
492
- return false unless siblings[self_index+1..-1].detect do |s|
493
- s != self && s.match(conditions[:before])
494
- end
495
- end
496
-
497
- if conditions[:after]
498
- return false unless siblings[0,self_index].detect do |s|
499
- s != self && s.match(conditions[:after])
500
- end
501
- end
502
- end
503
-
504
- true
505
- end
506
-
507
- def ==(node)
508
- return false unless super
509
- return false unless closing == node.closing && self.name == node.name
510
- attributes == node.attributes
511
- end
512
-
513
- private
514
- # Match the given value to the given condition.
515
- def match_condition(value, condition)
516
- case condition
517
- when String
518
- value && value == condition
519
- when Regexp
520
- value && value.match(condition)
521
- when Numeric
522
- value == condition.to_s
523
- when true
524
- !value.nil?
525
- when false, nil
526
- value.nil?
527
- else
528
- false
529
- end
530
- end
531
- end
532
- end
@@ -1,177 +0,0 @@
1
- require 'set'
2
- require 'cgi'
3
- require 'active_support/core_ext/class/attribute'
4
-
5
- module HTML
6
- class Sanitizer
7
- def sanitize(text, options = {})
8
- return text unless sanitizeable?(text)
9
- tokenize(text, options).join
10
- end
11
-
12
- def sanitizeable?(text)
13
- !(text.nil? || text.empty? || !text.index("<"))
14
- end
15
-
16
- protected
17
- def tokenize(text, options)
18
- tokenizer = HTML::Tokenizer.new(text)
19
- result = []
20
- while token = tokenizer.next
21
- node = Node.parse(nil, 0, 0, token, false)
22
- process_node node, result, options
23
- end
24
- result
25
- end
26
-
27
- def process_node(node, result, options)
28
- result << node.to_s
29
- end
30
- end
31
-
32
- class FullSanitizer < Sanitizer
33
- def sanitize(text, options = {})
34
- result = super
35
- # strip any comments, and if they have a newline at the end (ie. line with
36
- # only a comment) strip that too
37
- result = result.gsub(/<!--(.*?)-->[\n]?/m, "") if (result && result =~ /<!--(.*?)-->[\n]?/m)
38
- # Recurse - handle all dirty nested tags
39
- result == text ? result : sanitize(result, options)
40
- end
41
-
42
- def process_node(node, result, options)
43
- result << node.to_s if node.class == HTML::Text
44
- end
45
- end
46
-
47
- class LinkSanitizer < FullSanitizer
48
- cattr_accessor :included_tags, :instance_writer => false
49
- self.included_tags = Set.new(%w(a href))
50
-
51
- def sanitizeable?(text)
52
- !(text.nil? || text.empty? || !((text.index("<a") || text.index("<href")) && text.index(">")))
53
- end
54
-
55
- protected
56
- def process_node(node, result, options)
57
- result << node.to_s unless node.is_a?(HTML::Tag) && included_tags.include?(node.name)
58
- end
59
- end
60
-
61
- class WhiteListSanitizer < Sanitizer
62
- [:protocol_separator, :uri_attributes, :allowed_attributes, :allowed_tags, :allowed_protocols, :bad_tags,
63
- :allowed_css_properties, :allowed_css_keywords, :shorthand_css_properties].each do |attr|
64
- class_attribute attr, :instance_writer => false
65
- end
66
-
67
- # A regular expression of the valid characters used to separate protocols like
68
- # the ':' in 'http://foo.com'
69
- self.protocol_separator = /:|(&#0*58)|(&#x70)|(&#x0*3a)|(%|&#37;)3A/i
70
-
71
- # Specifies a Set of HTML attributes that can have URIs.
72
- self.uri_attributes = Set.new(%w(href src cite action longdesc xlink:href lowsrc))
73
-
74
- # Specifies a Set of 'bad' tags that the #sanitize helper will remove completely, as opposed
75
- # to just escaping harmless tags like &lt;font&gt;
76
- self.bad_tags = Set.new(%w(script))
77
-
78
- # Specifies the default Set of tags that the #sanitize helper will allow unscathed.
79
- self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
80
- sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr
81
- acronym a img blockquote del ins))
82
-
83
- # Specifies the default Set of html attributes that the #sanitize helper will leave
84
- # in the allowed tag.
85
- self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
86
-
87
- # Specifies the default Set of acceptable css properties that #sanitize and #sanitize_css will accept.
88
- self.allowed_protocols = Set.new(%w(ed2k ftp http https irc mailto news gopher nntp telnet webcal xmpp callto
89
- feed svn urn aim rsync tag ssh sftp rtsp afs))
90
-
91
- # Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
92
- self.allowed_css_properties = Set.new(%w(azimuth background-color border-bottom-color border-collapse
93
- border-color border-left-color border-right-color border-top-color clear color cursor direction display
94
- elevation float font font-family font-size font-style font-variant font-weight height letter-spacing line-height
95
- overflow pause pause-after pause-before pitch pitch-range richness speak speak-header speak-numeral speak-punctuation
96
- speech-rate stress text-align text-decoration text-indent unicode-bidi vertical-align voice-family volume white-space
97
- width))
98
-
99
- # Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
100
- self.allowed_css_keywords = Set.new(%w(auto aqua black block blue bold both bottom brown center
101
- collapse dashed dotted fuchsia gray green !important italic left lime maroon medium none navy normal
102
- nowrap olive pointer purple red right solid silver teal top transparent underline white yellow))
103
-
104
- # Specifies the default Set of allowed shorthand css properties for the #sanitize and #sanitize_css helpers.
105
- self.shorthand_css_properties = Set.new(%w(background border margin padding))
106
-
107
- # Sanitizes a block of css code. Used by #sanitize when it comes across a style attribute
108
- def sanitize_css(style)
109
- # disallow urls
110
- style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ')
111
-
112
- # gauntlet
113
- if style !~ /\A([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*\z/ ||
114
- style !~ /\A(\s*[-\w]+\s*:\s*[^:;]*(;|$)\s*)*\z/
115
- return ''
116
- end
117
-
118
- clean = []
119
- style.scan(/([-\w]+)\s*:\s*([^:;]*)/) do |prop,val|
120
- if allowed_css_properties.include?(prop.downcase)
121
- clean << prop + ': ' + val + ';'
122
- elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
123
- unless val.split().any? do |keyword|
124
- !allowed_css_keywords.include?(keyword) &&
125
- keyword !~ /\A(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)\z/
126
- end
127
- clean << prop + ': ' + val + ';'
128
- end
129
- end
130
- end
131
- clean.join(' ')
132
- end
133
-
134
- protected
135
- def tokenize(text, options)
136
- options[:parent] = []
137
- options[:attributes] ||= allowed_attributes
138
- options[:tags] ||= allowed_tags
139
- super
140
- end
141
-
142
- def process_node(node, result, options)
143
- result << case node
144
- when HTML::Tag
145
- if node.closing == :close
146
- options[:parent].shift
147
- else
148
- options[:parent].unshift node.name
149
- end
150
-
151
- process_attributes_for node, options
152
-
153
- options[:tags].include?(node.name) ? node : nil
154
- else
155
- bad_tags.include?(options[:parent].first) ? nil : node.to_s.gsub(/</, "&lt;")
156
- end
157
- end
158
-
159
- def process_attributes_for(node, options)
160
- return unless node.attributes
161
- node.attributes.keys.each do |attr_name|
162
- value = node.attributes[attr_name].to_s
163
-
164
- if !options[:attributes].include?(attr_name) || contains_bad_protocols?(attr_name, value)
165
- node.attributes.delete(attr_name)
166
- else
167
- node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(CGI::unescapeHTML(value))
168
- end
169
- end
170
- end
171
-
172
- def contains_bad_protocols?(attr_name, value)
173
- uri_attributes.include?(attr_name) &&
174
- (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(&#x0*3a)|(%|&#37;)3A/i && !allowed_protocols.include?(value.split(protocol_separator).first.downcase.strip))
175
- end
176
- end
177
- end