actionpack_csi 2.3.5.p6

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 (429) hide show
  1. data/CHANGELOG +5184 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +409 -0
  4. data/RUNNING_UNIT_TESTS +24 -0
  5. data/Rakefile +160 -0
  6. data/install.rb +30 -0
  7. data/lib/action_controller/assertions/dom_assertions.rb +55 -0
  8. data/lib/action_controller/assertions/model_assertions.rb +21 -0
  9. data/lib/action_controller/assertions/response_assertions.rb +160 -0
  10. data/lib/action_controller/assertions/routing_assertions.rb +146 -0
  11. data/lib/action_controller/assertions/selector_assertions.rb +638 -0
  12. data/lib/action_controller/assertions/tag_assertions.rb +127 -0
  13. data/lib/action_controller/base.rb +1423 -0
  14. data/lib/action_controller/benchmarking.rb +107 -0
  15. data/lib/action_controller/caching/actions.rb +177 -0
  16. data/lib/action_controller/caching/fragments.rb +120 -0
  17. data/lib/action_controller/caching/pages.rb +152 -0
  18. data/lib/action_controller/caching/sweeper.rb +45 -0
  19. data/lib/action_controller/caching/sweeping.rb +55 -0
  20. data/lib/action_controller/caching.rb +71 -0
  21. data/lib/action_controller/cgi_ext/cookie.rb +112 -0
  22. data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
  23. data/lib/action_controller/cgi_ext/stdinput.rb +24 -0
  24. data/lib/action_controller/cgi_ext.rb +15 -0
  25. data/lib/action_controller/cgi_process.rb +77 -0
  26. data/lib/action_controller/cookies.rb +95 -0
  27. data/lib/action_controller/dispatcher.rb +133 -0
  28. data/lib/action_controller/failsafe.rb +86 -0
  29. data/lib/action_controller/filters.rb +680 -0
  30. data/lib/action_controller/flash.rb +171 -0
  31. data/lib/action_controller/headers.rb +33 -0
  32. data/lib/action_controller/helpers.rb +225 -0
  33. data/lib/action_controller/http_authentication.rb +309 -0
  34. data/lib/action_controller/integration.rb +692 -0
  35. data/lib/action_controller/layout.rb +286 -0
  36. data/lib/action_controller/middleware_stack.rb +119 -0
  37. data/lib/action_controller/middlewares.rb +14 -0
  38. data/lib/action_controller/mime_responds.rb +193 -0
  39. data/lib/action_controller/mime_type.rb +212 -0
  40. data/lib/action_controller/mime_types.rb +21 -0
  41. data/lib/action_controller/params_parser.rb +77 -0
  42. data/lib/action_controller/performance_test.rb +15 -0
  43. data/lib/action_controller/polymorphic_routes.rb +189 -0
  44. data/lib/action_controller/rack_lint_patch.rb +36 -0
  45. data/lib/action_controller/record_identifier.rb +104 -0
  46. data/lib/action_controller/reloader.rb +54 -0
  47. data/lib/action_controller/request.rb +493 -0
  48. data/lib/action_controller/request_forgery_protection.rb +113 -0
  49. data/lib/action_controller/rescue.rb +183 -0
  50. data/lib/action_controller/resources.rb +682 -0
  51. data/lib/action_controller/response.rb +239 -0
  52. data/lib/action_controller/routing/builder.rb +197 -0
  53. data/lib/action_controller/routing/optimisations.rb +130 -0
  54. data/lib/action_controller/routing/recognition_optimisation.rb +167 -0
  55. data/lib/action_controller/routing/route.rb +265 -0
  56. data/lib/action_controller/routing/route_set.rb +502 -0
  57. data/lib/action_controller/routing/routing_ext.rb +49 -0
  58. data/lib/action_controller/routing/segments.rb +343 -0
  59. data/lib/action_controller/routing.rb +388 -0
  60. data/lib/action_controller/session/abstract_store.rb +181 -0
  61. data/lib/action_controller/session/cookie_store.rb +221 -0
  62. data/lib/action_controller/session/mem_cache_store.rb +51 -0
  63. data/lib/action_controller/session_management.rb +54 -0
  64. data/lib/action_controller/status_codes.rb +88 -0
  65. data/lib/action_controller/streaming.rb +181 -0
  66. data/lib/action_controller/string_coercion.rb +29 -0
  67. data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
  68. data/lib/action_controller/templates/rescues/_trace.erb +26 -0
  69. data/lib/action_controller/templates/rescues/diagnostics.erb +11 -0
  70. data/lib/action_controller/templates/rescues/layout.erb +29 -0
  71. data/lib/action_controller/templates/rescues/missing_template.erb +2 -0
  72. data/lib/action_controller/templates/rescues/routing_error.erb +10 -0
  73. data/lib/action_controller/templates/rescues/template_error.erb +21 -0
  74. data/lib/action_controller/templates/rescues/unknown_action.erb +2 -0
  75. data/lib/action_controller/test_case.rb +209 -0
  76. data/lib/action_controller/test_process.rb +580 -0
  77. data/lib/action_controller/translation.rb +13 -0
  78. data/lib/action_controller/uploaded_file.rb +44 -0
  79. data/lib/action_controller/url_rewriter.rb +216 -0
  80. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  81. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  82. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
  83. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  84. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  85. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  86. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  87. data/lib/action_controller/verification.rb +130 -0
  88. data/lib/action_controller.rb +113 -0
  89. data/lib/action_pack/version.rb +9 -0
  90. data/lib/action_pack.rb +24 -0
  91. data/lib/action_view/base.rb +362 -0
  92. data/lib/action_view/erb/util.rb +44 -0
  93. data/lib/action_view/helpers/active_record_helper.rb +305 -0
  94. data/lib/action_view/helpers/asset_tag_helper.rb +694 -0
  95. data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
  96. data/lib/action_view/helpers/benchmark_helper.rb +54 -0
  97. data/lib/action_view/helpers/cache_helper.rb +39 -0
  98. data/lib/action_view/helpers/capture_helper.rb +136 -0
  99. data/lib/action_view/helpers/date_helper.rb +988 -0
  100. data/lib/action_view/helpers/debug_helper.rb +38 -0
  101. data/lib/action_view/helpers/form_helper.rb +1074 -0
  102. data/lib/action_view/helpers/form_options_helper.rb +600 -0
  103. data/lib/action_view/helpers/form_tag_helper.rb +487 -0
  104. data/lib/action_view/helpers/javascript_helper.rb +208 -0
  105. data/lib/action_view/helpers/number_helper.rb +308 -0
  106. data/lib/action_view/helpers/prototype_helper.rb +1305 -0
  107. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  108. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  109. data/lib/action_view/helpers/record_tag_helper.rb +58 -0
  110. data/lib/action_view/helpers/sanitize_helper.rb +259 -0
  111. data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
  112. data/lib/action_view/helpers/tag_helper.rb +150 -0
  113. data/lib/action_view/helpers/text_helper.rb +587 -0
  114. data/lib/action_view/helpers/translation_helper.rb +39 -0
  115. data/lib/action_view/helpers/url_helper.rb +639 -0
  116. data/lib/action_view/helpers.rb +59 -0
  117. data/lib/action_view/inline_template.rb +19 -0
  118. data/lib/action_view/locale/en.yml +117 -0
  119. data/lib/action_view/partials.rb +240 -0
  120. data/lib/action_view/paths.rb +69 -0
  121. data/lib/action_view/reloadable_template.rb +117 -0
  122. data/lib/action_view/renderable.rb +95 -0
  123. data/lib/action_view/renderable_partial.rb +47 -0
  124. data/lib/action_view/safe_buffer.rb +28 -0
  125. data/lib/action_view/template.rb +252 -0
  126. data/lib/action_view/template_error.rb +99 -0
  127. data/lib/action_view/template_handler.rb +34 -0
  128. data/lib/action_view/template_handlers/builder.rb +17 -0
  129. data/lib/action_view/template_handlers/erb.rb +22 -0
  130. data/lib/action_view/template_handlers/rjs.rb +13 -0
  131. data/lib/action_view/template_handlers.rb +48 -0
  132. data/lib/action_view/test_case.rb +162 -0
  133. data/lib/action_view.rb +58 -0
  134. data/lib/actionpack.rb +2 -0
  135. data/test/abstract_unit.rb +61 -0
  136. data/test/active_record_unit.rb +104 -0
  137. data/test/activerecord/active_record_store_test.rb +174 -0
  138. data/test/activerecord/render_partial_with_record_identification_test.rb +188 -0
  139. data/test/adv_attr_test.rb +20 -0
  140. data/test/controller/action_pack_assertions_test.rb +543 -0
  141. data/test/controller/addresses_render_test.rb +37 -0
  142. data/test/controller/assert_select_test.rb +734 -0
  143. data/test/controller/base_test.rb +217 -0
  144. data/test/controller/benchmark_test.rb +32 -0
  145. data/test/controller/caching_test.rb +729 -0
  146. data/test/controller/capture_test.rb +66 -0
  147. data/test/controller/content_type_test.rb +168 -0
  148. data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
  149. data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
  150. data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
  151. data/test/controller/cookie_test.rb +134 -0
  152. data/test/controller/deprecation/deprecated_base_methods_test.rb +32 -0
  153. data/test/controller/dispatcher_test.rb +144 -0
  154. data/test/controller/dom_assertions_test.rb +53 -0
  155. data/test/controller/failsafe_test.rb +60 -0
  156. data/test/controller/fake_controllers.rb +33 -0
  157. data/test/controller/fake_models.rb +19 -0
  158. data/test/controller/filter_params_test.rb +52 -0
  159. data/test/controller/filters_test.rb +885 -0
  160. data/test/controller/flash_test.rb +147 -0
  161. data/test/controller/header_test.rb +14 -0
  162. data/test/controller/helper_test.rb +224 -0
  163. data/test/controller/html-scanner/cdata_node_test.rb +15 -0
  164. data/test/controller/html-scanner/document_test.rb +148 -0
  165. data/test/controller/html-scanner/node_test.rb +89 -0
  166. data/test/controller/html-scanner/sanitizer_test.rb +274 -0
  167. data/test/controller/html-scanner/tag_node_test.rb +238 -0
  168. data/test/controller/html-scanner/text_node_test.rb +50 -0
  169. data/test/controller/html-scanner/tokenizer_test.rb +131 -0
  170. data/test/controller/http_basic_authentication_test.rb +113 -0
  171. data/test/controller/http_digest_authentication_test.rb +254 -0
  172. data/test/controller/integration_test.rb +483 -0
  173. data/test/controller/layout_test.rb +215 -0
  174. data/test/controller/logging_test.rb +46 -0
  175. data/test/controller/middleware_stack_test.rb +90 -0
  176. data/test/controller/mime_responds_test.rb +536 -0
  177. data/test/controller/mime_type_test.rb +93 -0
  178. data/test/controller/polymorphic_routes_test.rb +297 -0
  179. data/test/controller/rack_test.rb +311 -0
  180. data/test/controller/record_identifier_test.rb +139 -0
  181. data/test/controller/redirect_test.rb +285 -0
  182. data/test/controller/reloader_test.rb +124 -0
  183. data/test/controller/render_test.rb +1762 -0
  184. data/test/controller/request/json_params_parsing_test.rb +65 -0
  185. data/test/controller/request/multipart_params_parsing_test.rb +162 -0
  186. data/test/controller/request/query_string_parsing_test.rb +120 -0
  187. data/test/controller/request/test_request_test.rb +35 -0
  188. data/test/controller/request/url_encoded_params_parsing_test.rb +146 -0
  189. data/test/controller/request/xml_params_parsing_test.rb +103 -0
  190. data/test/controller/request_forgery_protection_test.rb +265 -0
  191. data/test/controller/request_test.rb +395 -0
  192. data/test/controller/rescue_test.rb +536 -0
  193. data/test/controller/resources_test.rb +1393 -0
  194. data/test/controller/routing_test.rb +2591 -0
  195. data/test/controller/selector_test.rb +628 -0
  196. data/test/controller/send_file_test.rb +171 -0
  197. data/test/controller/session/cookie_store_test.rb +216 -0
  198. data/test/controller/session/mem_cache_store_test.rb +127 -0
  199. data/test/controller/session/test_session_test.rb +58 -0
  200. data/test/controller/test_test.rb +700 -0
  201. data/test/controller/translation_test.rb +26 -0
  202. data/test/controller/url_rewriter_test.rb +385 -0
  203. data/test/controller/verification_test.rb +270 -0
  204. data/test/controller/view_paths_test.rb +141 -0
  205. data/test/controller/webservice_test.rb +273 -0
  206. data/test/fixtures/_top_level_partial.html.erb +1 -0
  207. data/test/fixtures/_top_level_partial_only.erb +1 -0
  208. data/test/fixtures/addresses/list.erb +1 -0
  209. data/test/fixtures/alternate_helpers/foo_helper.rb +3 -0
  210. data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
  211. data/test/fixtures/companies.yml +24 -0
  212. data/test/fixtures/company.rb +10 -0
  213. data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
  214. data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
  215. data/test/fixtures/content_type/render_default_for_rjs.rjs +1 -0
  216. data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
  217. data/test/fixtures/customers/_customer.html.erb +1 -0
  218. data/test/fixtures/db_definitions/sqlite.sql +49 -0
  219. data/test/fixtures/developer.rb +9 -0
  220. data/test/fixtures/developers/_developer.erb +1 -0
  221. data/test/fixtures/developers.yml +21 -0
  222. data/test/fixtures/developers_projects.yml +13 -0
  223. data/test/fixtures/failsafe/500.html +1 -0
  224. data/test/fixtures/fun/games/_game.erb +1 -0
  225. data/test/fixtures/fun/games/hello_world.erb +1 -0
  226. data/test/fixtures/fun/serious/games/_game.erb +1 -0
  227. data/test/fixtures/functional_caching/_partial.erb +3 -0
  228. data/test/fixtures/functional_caching/formatted_fragment_cached.html.erb +3 -0
  229. data/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs +6 -0
  230. data/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder +5 -0
  231. data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
  232. data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
  233. data/test/fixtures/functional_caching/inline_fragment_cached.html.erb +2 -0
  234. data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
  235. data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
  236. data/test/fixtures/helpers/abc_helper.rb +5 -0
  237. data/test/fixtures/helpers/fun/games_helper.rb +3 -0
  238. data/test/fixtures/helpers/fun/pdf_helper.rb +3 -0
  239. data/test/fixtures/layout_tests/abs_path_layout.rhtml +1 -0
  240. data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
  241. data/test/fixtures/layout_tests/alt/layouts/alt.rhtml +0 -0
  242. data/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml +1 -0
  243. data/test/fixtures/layout_tests/layouts/item.rhtml +1 -0
  244. data/test/fixtures/layout_tests/layouts/layout_test.rhtml +1 -0
  245. data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
  246. data/test/fixtures/layout_tests/layouts/third_party_template_library.mab +1 -0
  247. data/test/fixtures/layout_tests/views/hello.rhtml +1 -0
  248. data/test/fixtures/layouts/_column.html.erb +2 -0
  249. data/test/fixtures/layouts/block_with_layout.erb +3 -0
  250. data/test/fixtures/layouts/builder.builder +3 -0
  251. data/test/fixtures/layouts/default_html.html.erb +1 -0
  252. data/test/fixtures/layouts/partial_with_layout.erb +3 -0
  253. data/test/fixtures/layouts/standard.erb +1 -0
  254. data/test/fixtures/layouts/talk_from_action.erb +2 -0
  255. data/test/fixtures/layouts/xhr.html.erb +2 -0
  256. data/test/fixtures/layouts/yield.erb +2 -0
  257. data/test/fixtures/mascot.rb +3 -0
  258. data/test/fixtures/mascots/_mascot.html.erb +1 -0
  259. data/test/fixtures/mascots.yml +4 -0
  260. data/test/fixtures/multipart/binary_file +0 -0
  261. data/test/fixtures/multipart/boundary_problem_file +10 -0
  262. data/test/fixtures/multipart/bracketed_param +5 -0
  263. data/test/fixtures/multipart/empty +10 -0
  264. data/test/fixtures/multipart/hello.txt +1 -0
  265. data/test/fixtures/multipart/large_text_file +10 -0
  266. data/test/fixtures/multipart/mixed_files +0 -0
  267. data/test/fixtures/multipart/mona_lisa.jpg +0 -0
  268. data/test/fixtures/multipart/none +9 -0
  269. data/test/fixtures/multipart/single_parameter +5 -0
  270. data/test/fixtures/multipart/text_file +10 -0
  271. data/test/fixtures/override/test/hello_world.erb +1 -0
  272. data/test/fixtures/override2/layouts/test/sub.erb +1 -0
  273. data/test/fixtures/post_test/layouts/post.html.erb +1 -0
  274. data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
  275. data/test/fixtures/post_test/post/index.html.erb +1 -0
  276. data/test/fixtures/post_test/post/index.iphone.erb +1 -0
  277. data/test/fixtures/post_test/super_post/index.html.erb +1 -0
  278. data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
  279. data/test/fixtures/project.rb +3 -0
  280. data/test/fixtures/projects/_project.erb +1 -0
  281. data/test/fixtures/projects.yml +7 -0
  282. data/test/fixtures/public/404.html +1 -0
  283. data/test/fixtures/public/500.da.html +1 -0
  284. data/test/fixtures/public/500.html +1 -0
  285. data/test/fixtures/public/absolute/test.css +23 -0
  286. data/test/fixtures/public/absolute/test.js +63 -0
  287. data/test/fixtures/public/images/rails.png +0 -0
  288. data/test/fixtures/public/javascripts/application.js +1 -0
  289. data/test/fixtures/public/javascripts/bank.js +1 -0
  290. data/test/fixtures/public/javascripts/controls.js +1 -0
  291. data/test/fixtures/public/javascripts/dragdrop.js +1 -0
  292. data/test/fixtures/public/javascripts/effects.js +1 -0
  293. data/test/fixtures/public/javascripts/prototype.js +1 -0
  294. data/test/fixtures/public/javascripts/robber.js +1 -0
  295. data/test/fixtures/public/javascripts/subdir/subdir.js +1 -0
  296. data/test/fixtures/public/javascripts/version.1.0.js +1 -0
  297. data/test/fixtures/public/stylesheets/bank.css +1 -0
  298. data/test/fixtures/public/stylesheets/robber.css +1 -0
  299. data/test/fixtures/public/stylesheets/subdir/subdir.css +1 -0
  300. data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
  301. data/test/fixtures/quiz/questions/_question.html.erb +1 -0
  302. data/test/fixtures/replies/_reply.erb +1 -0
  303. data/test/fixtures/replies.yml +15 -0
  304. data/test/fixtures/reply.rb +7 -0
  305. data/test/fixtures/respond_to/all_types_with_layout.html.erb +1 -0
  306. data/test/fixtures/respond_to/all_types_with_layout.js.rjs +1 -0
  307. data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
  308. data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
  309. data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
  310. data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
  311. data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
  312. data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
  313. data/test/fixtures/respond_to/using_defaults.html.erb +1 -0
  314. data/test/fixtures/respond_to/using_defaults.js.rjs +1 -0
  315. data/test/fixtures/respond_to/using_defaults.xml.builder +1 -0
  316. data/test/fixtures/respond_to/using_defaults_with_type_list.html.erb +1 -0
  317. data/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs +1 -0
  318. data/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder +1 -0
  319. data/test/fixtures/scope/test/modgreet.erb +1 -0
  320. data/test/fixtures/shared.html.erb +1 -0
  321. data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
  322. data/test/fixtures/test/_counter.html.erb +1 -0
  323. data/test/fixtures/test/_customer.erb +1 -0
  324. data/test/fixtures/test/_customer_counter.erb +1 -0
  325. data/test/fixtures/test/_customer_greeting.erb +1 -0
  326. data/test/fixtures/test/_customer_with_var.erb +1 -0
  327. data/test/fixtures/test/_form.erb +1 -0
  328. data/test/fixtures/test/_from_helper.erb +1 -0
  329. data/test/fixtures/test/_hash_greeting.erb +1 -0
  330. data/test/fixtures/test/_hash_object.erb +2 -0
  331. data/test/fixtures/test/_hello.builder +1 -0
  332. data/test/fixtures/test/_labelling_form.erb +1 -0
  333. data/test/fixtures/test/_layout_for_block_with_args.html.erb +3 -0
  334. data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
  335. data/test/fixtures/test/_local_inspector.html.erb +1 -0
  336. data/test/fixtures/test/_one.html.erb +1 -0
  337. data/test/fixtures/test/_partial.erb +1 -0
  338. data/test/fixtures/test/_partial.html.erb +1 -0
  339. data/test/fixtures/test/_partial.js.erb +1 -0
  340. data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
  341. data/test/fixtures/test/_partial_only.erb +1 -0
  342. data/test/fixtures/test/_partial_with_only_html_version.html.erb +1 -0
  343. data/test/fixtures/test/_person.erb +2 -0
  344. data/test/fixtures/test/_raise.html.erb +1 -0
  345. data/test/fixtures/test/_two.html.erb +1 -0
  346. data/test/fixtures/test/action_talk_to_layout.erb +2 -0
  347. data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
  348. data/test/fixtures/test/capturing.erb +4 -0
  349. data/test/fixtures/test/content_for.erb +2 -0
  350. data/test/fixtures/test/content_for_concatenated.erb +3 -0
  351. data/test/fixtures/test/content_for_with_parameter.erb +2 -0
  352. data/test/fixtures/test/delete_with_js.rjs +2 -0
  353. data/test/fixtures/test/dont_pick_me +1 -0
  354. data/test/fixtures/test/dot.directory/render_file_with_ivar.erb +1 -0
  355. data/test/fixtures/test/enum_rjs_test.rjs +6 -0
  356. data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
  357. data/test/fixtures/test/formatted_xml_erb.builder +1 -0
  358. data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
  359. data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
  360. data/test/fixtures/test/greeting.erb +1 -0
  361. data/test/fixtures/test/greeting.js.rjs +1 -0
  362. data/test/fixtures/test/hello.builder +4 -0
  363. data/test/fixtures/test/hello_world.da.html.erb +1 -0
  364. data/test/fixtures/test/hello_world.erb +1 -0
  365. data/test/fixtures/test/hello_world.erb~ +1 -0
  366. data/test/fixtures/test/hello_world.pt-BR.html.erb +1 -0
  367. data/test/fixtures/test/hello_world_container.builder +3 -0
  368. data/test/fixtures/test/hello_world_from_rxml.builder +4 -0
  369. data/test/fixtures/test/hello_world_with_layout_false.erb +1 -0
  370. data/test/fixtures/test/hello_xml_world.builder +11 -0
  371. data/test/fixtures/test/hyphen-ated.erb +1 -0
  372. data/test/fixtures/test/implicit_content_type.atom.builder +2 -0
  373. data/test/fixtures/test/list.erb +1 -0
  374. data/test/fixtures/test/malformed/malformed.en.html.erb~ +1 -0
  375. data/test/fixtures/test/malformed/malformed.erb~ +1 -0
  376. data/test/fixtures/test/malformed/malformed.html.erb~ +1 -0
  377. data/test/fixtures/test/nested_layout.erb +3 -0
  378. data/test/fixtures/test/non_erb_block_content_for.builder +4 -0
  379. data/test/fixtures/test/potential_conflicts.erb +4 -0
  380. data/test/fixtures/test/render_explicit_html_template.js.rjs +1 -0
  381. data/test/fixtures/test/render_file_from_template.html.erb +1 -0
  382. data/test/fixtures/test/render_file_with_ivar.erb +1 -0
  383. data/test/fixtures/test/render_file_with_locals.erb +1 -0
  384. data/test/fixtures/test/render_implicit_html_template.js.rjs +1 -0
  385. data/test/fixtures/test/render_implicit_html_template_from_xhr_request.da.html.erb +1 -0
  386. data/test/fixtures/test/render_implicit_html_template_from_xhr_request.html.erb +1 -0
  387. data/test/fixtures/test/render_implicit_js_template_without_layout.js.erb +1 -0
  388. data/test/fixtures/test/render_to_string_test.erb +1 -0
  389. data/test/fixtures/test/sub_template_raise.html.erb +1 -0
  390. data/test/fixtures/test/template.erb +1 -0
  391. data/test/fixtures/test/update_element_with_capture.erb +9 -0
  392. data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
  393. data/test/fixtures/test/using_layout_around_block_with_args.html.erb +1 -0
  394. data/test/fixtures/test/utf8.html.erb +2 -0
  395. data/test/fixtures/topic.rb +3 -0
  396. data/test/fixtures/topics/_topic.html.erb +1 -0
  397. data/test/fixtures/topics.yml +22 -0
  398. data/test/template/active_record_helper_i18n_test.rb +44 -0
  399. data/test/template/active_record_helper_test.rb +302 -0
  400. data/test/template/asset_tag_helper_test.rb +771 -0
  401. data/test/template/atom_feed_helper_test.rb +315 -0
  402. data/test/template/benchmark_helper_test.rb +86 -0
  403. data/test/template/compiled_templates_test.rb +204 -0
  404. data/test/template/date_helper_i18n_test.rb +121 -0
  405. data/test/template/date_helper_test.rb +2485 -0
  406. data/test/template/erb_util_test.rb +24 -0
  407. data/test/template/form_helper_test.rb +1393 -0
  408. data/test/template/form_options_helper_i18n_test.rb +27 -0
  409. data/test/template/form_options_helper_test.rb +807 -0
  410. data/test/template/form_tag_helper_test.rb +344 -0
  411. data/test/template/javascript_helper_test.rb +106 -0
  412. data/test/template/number_helper_i18n_test.rb +69 -0
  413. data/test/template/number_helper_test.rb +132 -0
  414. data/test/template/prototype_helper_test.rb +639 -0
  415. data/test/template/raw_output_helper_test.rb +21 -0
  416. data/test/template/record_tag_helper_test.rb +58 -0
  417. data/test/template/render_test.rb +290 -0
  418. data/test/template/sanitize_helper_test.rb +57 -0
  419. data/test/template/scriptaculous_helper_test.rb +90 -0
  420. data/test/template/tag_helper_test.rb +98 -0
  421. data/test/template/template_test.rb +32 -0
  422. data/test/template/test_test.rb +54 -0
  423. data/test/template/text_helper_test.rb +543 -0
  424. data/test/template/translation_helper_test.rb +32 -0
  425. data/test/template/url_helper_test.rb +622 -0
  426. data/test/testing_sandbox.rb +15 -0
  427. data/test/view/safe_buffer_test.rb +36 -0
  428. data/test/view/test_case_test.rb +176 -0
  429. metadata +531 -0
@@ -0,0 +1,537 @@
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 |k,v2|
22
+ case k
23
+ when :count, :greater_than, :less_than
24
+ # keys are valid, and require no further processing
25
+ when :only
26
+ v[k] = Conditions.new(v2)
27
+ else
28
+ raise "illegal key #{k.inspect} => #{v2.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.keys.inject({}) do |h,k|
42
+ h[k.to_s] = hash[k]
43
+ h
44
+ end
45
+ end
46
+
47
+ def keys_to_symbols(hash)
48
+ hash.keys.inject({}) do |h,k|
49
+ raise "illegal key #{k.inspect}" unless k.respond_to?(:to_sym)
50
+ h[k.to_sym] = hash[k]
51
+ h
52
+ end
53
+ end
54
+ end
55
+
56
+ # The base class of all nodes, textual and otherwise, in an HTML document.
57
+ class Node #:nodoc:
58
+ # The array of children of this node. Not all nodes have children.
59
+ attr_reader :children
60
+
61
+ # The parent node of this node. All nodes have a parent, except for the
62
+ # root node.
63
+ attr_reader :parent
64
+
65
+ # The line number of the input where this node was begun
66
+ attr_reader :line
67
+
68
+ # The byte position in the input where this node was begun
69
+ attr_reader :position
70
+
71
+ # Create a new node as a child of the given parent.
72
+ def initialize(parent, line=0, pos=0)
73
+ @parent = parent
74
+ @children = []
75
+ @line, @position = line, pos
76
+ end
77
+
78
+ # Return a textual representation of the node.
79
+ def to_s
80
+ s = ""
81
+ @children.each { |child| s << child.to_s }
82
+ s
83
+ end
84
+
85
+ # Return false (subclasses must override this to provide specific matching
86
+ # behavior.) +conditions+ may be of any type.
87
+ def match(conditions)
88
+ false
89
+ end
90
+
91
+ # Search the children of this node for the first node for which #find
92
+ # returns non +nil+. Returns the result of the #find call that succeeded.
93
+ def find(conditions)
94
+ conditions = validate_conditions(conditions)
95
+ @children.each do |child|
96
+ node = child.find(conditions)
97
+ return node if node
98
+ end
99
+ nil
100
+ end
101
+
102
+ # Search for all nodes that match the given conditions, and return them
103
+ # as an array.
104
+ def find_all(conditions)
105
+ conditions = validate_conditions(conditions)
106
+
107
+ matches = []
108
+ matches << self if match(conditions)
109
+ @children.each do |child|
110
+ matches.concat child.find_all(conditions)
111
+ end
112
+ matches
113
+ end
114
+
115
+ # Returns +false+. Subclasses may override this if they define a kind of
116
+ # tag.
117
+ def tag?
118
+ false
119
+ end
120
+
121
+ def validate_conditions(conditions)
122
+ Conditions === conditions ? conditions : Conditions.new(conditions)
123
+ end
124
+
125
+ def ==(node)
126
+ return false unless self.class == node.class && children.size == node.children.size
127
+
128
+ equivalent = true
129
+
130
+ children.size.times do |i|
131
+ equivalent &&= children[i] == node.children[i]
132
+ end
133
+
134
+ equivalent
135
+ end
136
+
137
+ class <<self
138
+ def parse(parent, line, pos, content, strict=true)
139
+ if content !~ /^<\S/
140
+ Text.new(parent, line, pos, content)
141
+ else
142
+ scanner = StringScanner.new(content)
143
+
144
+ unless scanner.skip(/</)
145
+ if strict
146
+ raise "expected <"
147
+ else
148
+ return Text.new(parent, line, pos, content)
149
+ end
150
+ end
151
+
152
+ if scanner.skip(/!\[CDATA\[/)
153
+ unless scanner.skip_until(/\]\]>/)
154
+ if strict
155
+ raise "expected ]]> (got #{scanner.rest.inspect} for #{content})"
156
+ else
157
+ scanner.skip_until(/\Z/)
158
+ end
159
+ end
160
+
161
+ return CDATA.new(parent, line, pos, scanner.pre_match.gsub(/<!\[CDATA\[/, ''))
162
+ end
163
+
164
+ closing = ( scanner.scan(/\//) ? :close : nil )
165
+ return Text.new(parent, line, pos, content) unless name = scanner.scan(/[-:\w\x00-\x09\x0b-\x0c\x0e-\x1f]+/)
166
+ name.downcase!
167
+
168
+ unless closing
169
+ scanner.skip(/\s*/)
170
+ attributes = {}
171
+ while attr = scanner.scan(/[-\w:]+/)
172
+ value = true
173
+ if scanner.scan(/\s*=\s*/)
174
+ if delim = scanner.scan(/['"]/)
175
+ value = ""
176
+ while text = scanner.scan(/[^#{delim}\\]+|./)
177
+ case text
178
+ when "\\" then
179
+ value << text
180
+ value << scanner.getch
181
+ when delim
182
+ break
183
+ else value << text
184
+ end
185
+ end
186
+ else
187
+ value = scanner.scan(/[^\s>\/]+/)
188
+ end
189
+ end
190
+ attributes[attr.downcase] = value
191
+ scanner.skip(/\s*/)
192
+ end
193
+
194
+ closing = ( scanner.scan(/\//) ? :self : nil )
195
+ end
196
+
197
+ unless scanner.scan(/\s*>/)
198
+ if strict
199
+ raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})"
200
+ else
201
+ # throw away all text until we find what we're looking for
202
+ scanner.skip_until(/>/) or scanner.terminate
203
+ end
204
+ end
205
+
206
+ Tag.new(parent, line, pos, name, attributes, closing)
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ # A node that represents text, rather than markup.
213
+ class Text < Node #:nodoc:
214
+
215
+ attr_reader :content
216
+
217
+ # Creates a new text node as a child of the given parent, with the given
218
+ # content.
219
+ def initialize(parent, line, pos, content)
220
+ super(parent, line, pos)
221
+ @content = content
222
+ end
223
+
224
+ # Returns the content of this node.
225
+ def to_s
226
+ @content
227
+ end
228
+
229
+ # Returns +self+ if this node meets the given conditions. Text nodes support
230
+ # conditions of the following kinds:
231
+ #
232
+ # * if +conditions+ is a string, it must be a substring of the node's
233
+ # content
234
+ # * if +conditions+ is a regular expression, it must match the node's
235
+ # content
236
+ # * if +conditions+ is a hash, it must contain a <tt>:content</tt> key that
237
+ # is either a string or a regexp, and which is interpreted as described
238
+ # above.
239
+ def find(conditions)
240
+ match(conditions) && self
241
+ end
242
+
243
+ # Returns non-+nil+ if this node meets the given conditions, or +nil+
244
+ # otherwise. See the discussion of #find for the valid conditions.
245
+ def match(conditions)
246
+ case conditions
247
+ when String
248
+ @content == conditions
249
+ when Regexp
250
+ @content =~ conditions
251
+ when Hash
252
+ conditions = validate_conditions(conditions)
253
+
254
+ # Text nodes only have :content, :parent, :ancestor
255
+ unless (conditions.keys - [:content, :parent, :ancestor]).empty?
256
+ return false
257
+ end
258
+
259
+ match(conditions[:content])
260
+ else
261
+ nil
262
+ end
263
+ end
264
+
265
+ def ==(node)
266
+ return false unless super
267
+ content == node.content
268
+ end
269
+ end
270
+
271
+ # A CDATA node is simply a text node with a specialized way of displaying
272
+ # itself.
273
+ class CDATA < Text #:nodoc:
274
+ def to_s
275
+ "<![CDATA[#{super}]]>"
276
+ end
277
+ end
278
+
279
+ # A Tag is any node that represents markup. It may be an opening tag, a
280
+ # closing tag, or a self-closing tag. It has a name, and may have a hash of
281
+ # attributes.
282
+ class Tag < Node #:nodoc:
283
+
284
+ # Either +nil+, <tt>:close</tt>, or <tt>:self</tt>
285
+ attr_reader :closing
286
+
287
+ # Either +nil+, or a hash of attributes for this node.
288
+ attr_reader :attributes
289
+
290
+ # The name of this tag.
291
+ attr_reader :name
292
+
293
+ # Create a new node as a child of the given parent, using the given content
294
+ # to describe the node. It will be parsed and the node name, attributes and
295
+ # closing status extracted.
296
+ def initialize(parent, line, pos, name, attributes, closing)
297
+ super(parent, line, pos)
298
+ @name = name
299
+ @attributes = attributes
300
+ @closing = closing
301
+ end
302
+
303
+ # A convenience for obtaining an attribute of the node. Returns +nil+ if
304
+ # the node has no attributes.
305
+ def [](attr)
306
+ @attributes ? @attributes[attr] : nil
307
+ end
308
+
309
+ # Returns non-+nil+ if this tag can contain child nodes.
310
+ def childless?(xml = false)
311
+ return false if xml && @closing.nil?
312
+ !@closing.nil? ||
313
+ @name =~ /^(img|br|hr|link|meta|area|base|basefont|
314
+ col|frame|input|isindex|param)$/ox
315
+ end
316
+
317
+ # Returns a textual representation of the node
318
+ def to_s
319
+ if @closing == :close
320
+ "</#{@name}>"
321
+ else
322
+ s = "<#{@name}"
323
+ @attributes.each do |k,v|
324
+ s << " #{k}"
325
+ s << "=\"#{v}\"" if String === v
326
+ end
327
+ s << " /" if @closing == :self
328
+ s << ">"
329
+ @children.each { |child| s << child.to_s }
330
+ s << "</#{@name}>" if @closing != :self && !@children.empty?
331
+ s
332
+ end
333
+ end
334
+
335
+ # If either the node or any of its children meet the given conditions, the
336
+ # matching node is returned. Otherwise, +nil+ is returned. (See the
337
+ # description of the valid conditions in the +match+ method.)
338
+ def find(conditions)
339
+ match(conditions) && self || super
340
+ end
341
+
342
+ # Returns +true+, indicating that this node represents an HTML tag.
343
+ def tag?
344
+ true
345
+ end
346
+
347
+ # Returns +true+ if the node meets any of the given conditions. The
348
+ # +conditions+ parameter must be a hash of any of the following keys
349
+ # (all are optional):
350
+ #
351
+ # * <tt>:tag</tt>: the node name must match the corresponding value
352
+ # * <tt>:attributes</tt>: a hash. The node's values must match the
353
+ # corresponding values in the hash.
354
+ # * <tt>:parent</tt>: a hash. The node's parent must match the
355
+ # corresponding hash.
356
+ # * <tt>:child</tt>: a hash. At least one of the node's immediate children
357
+ # must meet the criteria described by the hash.
358
+ # * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
359
+ # meet the criteria described by the hash.
360
+ # * <tt>:descendant</tt>: a hash. At least one of the node's descendants
361
+ # must meet the criteria described by the hash.
362
+ # * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
363
+ # meet the criteria described by the hash.
364
+ # * <tt>:after</tt>: a hash. The node must be after any sibling meeting
365
+ # the criteria described by the hash, and at least one sibling must match.
366
+ # * <tt>:before</tt>: a hash. The node must be before any sibling meeting
367
+ # the criteria described by the hash, and at least one sibling must match.
368
+ # * <tt>:children</tt>: a hash, for counting children of a node. Accepts the
369
+ # keys:
370
+ # ** <tt>:count</tt>: either a number or a range which must equal (or
371
+ # include) the number of children that match.
372
+ # ** <tt>:less_than</tt>: the number of matching children must be less than
373
+ # this number.
374
+ # ** <tt>:greater_than</tt>: the number of matching children must be
375
+ # greater than this number.
376
+ # ** <tt>:only</tt>: another hash consisting of the keys to use
377
+ # to match on the children, and only matching children will be
378
+ # counted.
379
+ #
380
+ # Conditions are matched using the following algorithm:
381
+ #
382
+ # * if the condition is a string, it must be a substring of the value.
383
+ # * if the condition is a regexp, it must match the value.
384
+ # * if the condition is a number, the value must match number.to_s.
385
+ # * if the condition is +true+, the value must not be +nil+.
386
+ # * if the condition is +false+ or +nil+, the value must be +nil+.
387
+ #
388
+ # Usage:
389
+ #
390
+ # # test if the node is a "span" tag
391
+ # node.match :tag => "span"
392
+ #
393
+ # # test if the node's parent is a "div"
394
+ # node.match :parent => { :tag => "div" }
395
+ #
396
+ # # test if any of the node's ancestors are "table" tags
397
+ # node.match :ancestor => { :tag => "table" }
398
+ #
399
+ # # test if any of the node's immediate children are "em" tags
400
+ # node.match :child => { :tag => "em" }
401
+ #
402
+ # # test if any of the node's descendants are "strong" tags
403
+ # node.match :descendant => { :tag => "strong" }
404
+ #
405
+ # # test if the node has between 2 and 4 span tags as immediate children
406
+ # node.match :children => { :count => 2..4, :only => { :tag => "span" } }
407
+ #
408
+ # # get funky: test to see if the node is a "div", has a "ul" ancestor
409
+ # # and an "li" parent (with "class" = "enum"), and whether or not it has
410
+ # # a "span" descendant that contains # text matching /hello world/:
411
+ # node.match :tag => "div",
412
+ # :ancestor => { :tag => "ul" },
413
+ # :parent => { :tag => "li",
414
+ # :attributes => { :class => "enum" } },
415
+ # :descendant => { :tag => "span",
416
+ # :child => /hello world/ }
417
+ def match(conditions)
418
+ conditions = validate_conditions(conditions)
419
+ # check content of child nodes
420
+ if conditions[:content]
421
+ if children.empty?
422
+ return false unless match_condition("", conditions[:content])
423
+ else
424
+ return false unless children.find { |child| child.match(conditions[:content]) }
425
+ end
426
+ end
427
+
428
+ # test the name
429
+ return false unless match_condition(@name, conditions[:tag]) if conditions[:tag]
430
+
431
+ # test attributes
432
+ (conditions[:attributes] || {}).each do |key, value|
433
+ return false unless match_condition(self[key], value)
434
+ end
435
+
436
+ # test parent
437
+ return false unless parent.match(conditions[:parent]) if conditions[:parent]
438
+
439
+ # test children
440
+ return false unless children.find { |child| child.match(conditions[:child]) } if conditions[:child]
441
+
442
+ # test ancestors
443
+ if conditions[:ancestor]
444
+ return false unless catch :found do
445
+ p = self
446
+ throw :found, true if p.match(conditions[:ancestor]) while p = p.parent
447
+ end
448
+ end
449
+
450
+ # test descendants
451
+ if conditions[:descendant]
452
+ return false unless children.find do |child|
453
+ # test the child
454
+ child.match(conditions[:descendant]) ||
455
+ # test the child's descendants
456
+ child.match(:descendant => conditions[:descendant])
457
+ end
458
+ end
459
+
460
+ # count children
461
+ if opts = conditions[:children]
462
+ matches = children.select do |c|
463
+ (c.kind_of?(HTML::Tag) and (c.closing == :self or ! c.childless?))
464
+ end
465
+
466
+ matches = matches.select { |c| c.match(opts[:only]) } if opts[:only]
467
+ opts.each do |key, value|
468
+ next if key == :only
469
+ case key
470
+ when :count
471
+ if Integer === value
472
+ return false if matches.length != value
473
+ else
474
+ return false unless value.include?(matches.length)
475
+ end
476
+ when :less_than
477
+ return false unless matches.length < value
478
+ when :greater_than
479
+ return false unless matches.length > value
480
+ else raise "unknown count condition #{key}"
481
+ end
482
+ end
483
+ end
484
+
485
+ # test siblings
486
+ if conditions[:sibling] || conditions[:before] || conditions[:after]
487
+ siblings = parent ? parent.children : []
488
+ self_index = siblings.index(self)
489
+
490
+ if conditions[:sibling]
491
+ return false unless siblings.detect do |s|
492
+ s != self && s.match(conditions[:sibling])
493
+ end
494
+ end
495
+
496
+ if conditions[:before]
497
+ return false unless siblings[self_index+1..-1].detect do |s|
498
+ s != self && s.match(conditions[:before])
499
+ end
500
+ end
501
+
502
+ if conditions[:after]
503
+ return false unless siblings[0,self_index].detect do |s|
504
+ s != self && s.match(conditions[:after])
505
+ end
506
+ end
507
+ end
508
+
509
+ true
510
+ end
511
+
512
+ def ==(node)
513
+ return false unless super
514
+ return false unless closing == node.closing && self.name == node.name
515
+ attributes == node.attributes
516
+ end
517
+
518
+ private
519
+ # Match the given value to the given condition.
520
+ def match_condition(value, condition)
521
+ case condition
522
+ when String
523
+ value && value == condition
524
+ when Regexp
525
+ value && value.match(condition)
526
+ when Numeric
527
+ value == condition.to_s
528
+ when true
529
+ !value.nil?
530
+ when false, nil
531
+ value.nil?
532
+ else
533
+ false
534
+ end
535
+ end
536
+ end
537
+ end
@@ -0,0 +1,173 @@
1
+ module HTML
2
+ class Sanitizer
3
+ def sanitize(text, options = {})
4
+ return text unless sanitizeable?(text)
5
+ tokenize(text, options).join
6
+ end
7
+
8
+ def sanitizeable?(text)
9
+ !(text.nil? || text.empty? || !text.index("<"))
10
+ end
11
+
12
+ protected
13
+ def tokenize(text, options)
14
+ tokenizer = HTML::Tokenizer.new(text)
15
+ result = []
16
+ while token = tokenizer.next
17
+ node = Node.parse(nil, 0, 0, token, false)
18
+ process_node node, result, options
19
+ end
20
+ result
21
+ end
22
+
23
+ def process_node(node, result, options)
24
+ result << node.to_s
25
+ end
26
+ end
27
+
28
+ class FullSanitizer < Sanitizer
29
+ def sanitize(text, options = {})
30
+ result = super
31
+ # strip any comments, and if they have a newline at the end (ie. line with
32
+ # only a comment) strip that too
33
+ result.gsub!(/<!--(.*?)-->[\n]?/m, "") if result
34
+ # Recurse - handle all dirty nested tags
35
+ result == text ? result : sanitize(result, options)
36
+ end
37
+
38
+ def process_node(node, result, options)
39
+ result << node.to_s if node.class == HTML::Text
40
+ end
41
+ end
42
+
43
+ class LinkSanitizer < FullSanitizer
44
+ cattr_accessor :included_tags, :instance_writer => false
45
+ self.included_tags = Set.new(%w(a href))
46
+
47
+ def sanitizeable?(text)
48
+ !(text.nil? || text.empty? || !((text.index("<a") || text.index("<href")) && text.index(">")))
49
+ end
50
+
51
+ protected
52
+ def process_node(node, result, options)
53
+ result << node.to_s unless node.is_a?(HTML::Tag) && included_tags.include?(node.name)
54
+ end
55
+ end
56
+
57
+ class WhiteListSanitizer < Sanitizer
58
+ [:protocol_separator, :uri_attributes, :allowed_attributes, :allowed_tags, :allowed_protocols, :bad_tags,
59
+ :allowed_css_properties, :allowed_css_keywords, :shorthand_css_properties].each do |attr|
60
+ class_inheritable_accessor attr, :instance_writer => false
61
+ end
62
+
63
+ # A regular expression of the valid characters used to separate protocols like
64
+ # the ':' in 'http://foo.com'
65
+ self.protocol_separator = /:|(&#0*58)|(&#x70)|(%|&#37;)3A/
66
+
67
+ # Specifies a Set of HTML attributes that can have URIs.
68
+ self.uri_attributes = Set.new(%w(href src cite action longdesc xlink:href lowsrc))
69
+
70
+ # Specifies a Set of 'bad' tags that the #sanitize helper will remove completely, as opposed
71
+ # to just escaping harmless tags like &lt;font&gt;
72
+ self.bad_tags = Set.new(%w(script))
73
+
74
+ # Specifies the default Set of tags that the #sanitize helper will allow unscathed.
75
+ self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
76
+ sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dt dd abbr
77
+ acronym a img blockquote del ins))
78
+
79
+ # Specifies the default Set of html attributes that the #sanitize helper will leave
80
+ # in the allowed tag.
81
+ self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
82
+
83
+ # Specifies the default Set of acceptable css properties that #sanitize and #sanitize_css will accept.
84
+ self.allowed_protocols = Set.new(%w(ed2k ftp http https irc mailto news gopher nntp telnet webcal xmpp callto
85
+ feed svn urn aim rsync tag ssh sftp rtsp afs))
86
+
87
+ # Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
88
+ self.allowed_css_properties = Set.new(%w(azimuth background-color border-bottom-color border-collapse
89
+ border-color border-left-color border-right-color border-top-color clear color cursor direction display
90
+ elevation float font font-family font-size font-style font-variant font-weight height letter-spacing line-height
91
+ overflow pause pause-after pause-before pitch pitch-range richness speak speak-header speak-numeral speak-punctuation
92
+ speech-rate stress text-align text-decoration text-indent unicode-bidi vertical-align voice-family volume white-space
93
+ width))
94
+
95
+ # Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
96
+ self.allowed_css_keywords = Set.new(%w(auto aqua black block blue bold both bottom brown center
97
+ collapse dashed dotted fuchsia gray green !important italic left lime maroon medium none navy normal
98
+ nowrap olive pointer purple red right solid silver teal top transparent underline white yellow))
99
+
100
+ # Specifies the default Set of allowed shorthand css properties for the #sanitize and #sanitize_css helpers.
101
+ self.shorthand_css_properties = Set.new(%w(background border margin padding))
102
+
103
+ # Sanitizes a block of css code. Used by #sanitize when it comes across a style attribute
104
+ def sanitize_css(style)
105
+ # disallow urls
106
+ style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ')
107
+
108
+ # gauntlet
109
+ if style !~ /^([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*$/ ||
110
+ style !~ /^(\s*[-\w]+\s*:\s*[^:;]*(;|$)\s*)*$/
111
+ return ''
112
+ end
113
+
114
+ clean = []
115
+ style.scan(/([-\w]+)\s*:\s*([^:;]*)/) do |prop,val|
116
+ if allowed_css_properties.include?(prop.downcase)
117
+ clean << prop + ': ' + val + ';'
118
+ elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
119
+ unless val.split().any? do |keyword|
120
+ !allowed_css_keywords.include?(keyword) &&
121
+ keyword !~ /^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/
122
+ end
123
+ clean << prop + ': ' + val + ';'
124
+ end
125
+ end
126
+ end
127
+ clean.join(' ')
128
+ end
129
+
130
+ protected
131
+ def tokenize(text, options)
132
+ options[:parent] = []
133
+ options[:attributes] ||= allowed_attributes
134
+ options[:tags] ||= allowed_tags
135
+ super
136
+ end
137
+
138
+ def process_node(node, result, options)
139
+ result << case node
140
+ when HTML::Tag
141
+ if node.closing == :close
142
+ options[:parent].shift
143
+ else
144
+ options[:parent].unshift node.name
145
+ end
146
+
147
+ process_attributes_for node, options
148
+
149
+ options[:tags].include?(node.name) ? node : nil
150
+ else
151
+ bad_tags.include?(options[:parent].first) ? nil : node.to_s.gsub(/</, "&lt;")
152
+ end
153
+ end
154
+
155
+ def process_attributes_for(node, options)
156
+ return unless node.attributes
157
+ node.attributes.keys.each do |attr_name|
158
+ value = node.attributes[attr_name].to_s
159
+
160
+ if !options[:attributes].include?(attr_name) || contains_bad_protocols?(attr_name, value)
161
+ node.attributes.delete(attr_name)
162
+ else
163
+ node.attributes[attr_name] = attr_name == 'style' ? sanitize_css(value) : CGI::escapeHTML(CGI::unescapeHTML(value))
164
+ end
165
+ end
166
+ end
167
+
168
+ def contains_bad_protocols?(attr_name, value)
169
+ uri_attributes.include?(attr_name) &&
170
+ (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(%|&#37;)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first))
171
+ end
172
+ end
173
+ end