contrast-agent 3.8.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 (500) hide show
  1. checksums.yaml +7 -0
  2. data/.clang-format +5 -0
  3. data/.dockerignore +10 -0
  4. data/.gitignore +58 -0
  5. data/.gitmodules +6 -0
  6. data/.rspec +6 -0
  7. data/.simplecov +4 -0
  8. data/Gemfile +7 -0
  9. data/LICENSE.txt +12 -0
  10. data/Rakefile +15 -0
  11. data/exe/contrast_service +29 -0
  12. data/ext/build_funchook.rb +48 -0
  13. data/ext/cs__assess_active_record_named/cs__active_record_named.c +47 -0
  14. data/ext/cs__assess_active_record_named/cs__active_record_named.h +10 -0
  15. data/ext/cs__assess_active_record_named/extconf.rb +2 -0
  16. data/ext/cs__assess_array/cs__assess_array.c +38 -0
  17. data/ext/cs__assess_array/cs__assess_array.h +9 -0
  18. data/ext/cs__assess_array/extconf.rb +2 -0
  19. data/ext/cs__assess_basic_object/cs__assess_basic_object.c +50 -0
  20. data/ext/cs__assess_basic_object/cs__assess_basic_object.h +17 -0
  21. data/ext/cs__assess_basic_object/extconf.rb +2 -0
  22. data/ext/cs__assess_fiber_track/cs__assess_fiber_track.c +86 -0
  23. data/ext/cs__assess_fiber_track/cs__assess_fiber_track.h +34 -0
  24. data/ext/cs__assess_fiber_track/extconf.rb +2 -0
  25. data/ext/cs__assess_hash/cs__assess_hash.c +64 -0
  26. data/ext/cs__assess_hash/cs__assess_hash.h +24 -0
  27. data/ext/cs__assess_hash/extconf.rb +2 -0
  28. data/ext/cs__assess_kernel/cs__assess_kernel.c +36 -0
  29. data/ext/cs__assess_kernel/cs__assess_kernel.h +10 -0
  30. data/ext/cs__assess_kernel/extconf.rb +2 -0
  31. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +47 -0
  32. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +18 -0
  33. data/ext/cs__assess_marshal_module/extconf.rb +2 -0
  34. data/ext/cs__assess_module/cs__assess_module.c +78 -0
  35. data/ext/cs__assess_module/cs__assess_module.h +25 -0
  36. data/ext/cs__assess_module/extconf.rb +2 -0
  37. data/ext/cs__assess_regexp/cs__assess_regexp.c +48 -0
  38. data/ext/cs__assess_regexp/cs__assess_regexp.h +22 -0
  39. data/ext/cs__assess_regexp/extconf.rb +2 -0
  40. data/ext/cs__assess_regexp_track/cs__assess_regexp_track.c +63 -0
  41. data/ext/cs__assess_regexp_track/cs__assess_regexp_track.h +29 -0
  42. data/ext/cs__assess_regexp_track/extconf.rb +2 -0
  43. data/ext/cs__assess_string/cs__assess_string.c +38 -0
  44. data/ext/cs__assess_string/cs__assess_string.h +19 -0
  45. data/ext/cs__assess_string/extconf.rb +2 -0
  46. data/ext/cs__assess_string_interpolation26/cs__assess_string_interpolation26.c +31 -0
  47. data/ext/cs__assess_string_interpolation26/cs__assess_string_interpolation26.h +13 -0
  48. data/ext/cs__assess_string_interpolation26/extconf.rb +2 -0
  49. data/ext/cs__common/cs__common.c +60 -0
  50. data/ext/cs__common/cs__common.h +28 -0
  51. data/ext/cs__common/extconf.rb +20 -0
  52. data/ext/cs__contrast_patch/cs__contrast_patch.c +445 -0
  53. data/ext/cs__contrast_patch/cs__contrast_patch.h +196 -0
  54. data/ext/cs__contrast_patch/extconf.rb +2 -0
  55. data/ext/cs__protect_kernel/cs__protect_kernel.c +37 -0
  56. data/ext/cs__protect_kernel/cs__protect_kernel.h +11 -0
  57. data/ext/cs__protect_kernel/extconf.rb +2 -0
  58. data/ext/cs__scope/cs__scope.c +96 -0
  59. data/ext/cs__scope/cs__scope.h +33 -0
  60. data/ext/cs__scope/extconf.rb +2 -0
  61. data/ext/extconf_common.rb +49 -0
  62. data/funchook/LICENSE +360 -0
  63. data/funchook/Makefile +29 -0
  64. data/funchook/Makefile.in +29 -0
  65. data/funchook/README.md +121 -0
  66. data/funchook/appveyor.yml +42 -0
  67. data/funchook/autogen.sh +3 -0
  68. data/funchook/autom4te.cache/output.0 +4976 -0
  69. data/funchook/autom4te.cache/requests +78 -0
  70. data/funchook/autom4te.cache/traces.0 +364 -0
  71. data/funchook/config.guess +1530 -0
  72. data/funchook/config.log +490 -0
  73. data/funchook/config.status +1016 -0
  74. data/funchook/config.sub +1773 -0
  75. data/funchook/configure +4976 -0
  76. data/funchook/configure.ac +59 -0
  77. data/funchook/distorm/COPYING +26 -0
  78. data/funchook/distorm/MANIFEST +25 -0
  79. data/funchook/distorm/MANIFEST.in +4 -0
  80. data/funchook/distorm/README.md +12 -0
  81. data/funchook/distorm/disOps/disOps.py +795 -0
  82. data/funchook/distorm/disOps/x86db.py +404 -0
  83. data/funchook/distorm/disOps/x86header.py +247 -0
  84. data/funchook/distorm/disOps/x86sets.py +1664 -0
  85. data/funchook/distorm/examples/cs/TestdiStorm/Program.cs +79 -0
  86. data/funchook/distorm/examples/cs/TestdiStorm/Properties/AssemblyInfo.cs +36 -0
  87. data/funchook/distorm/examples/cs/TestdiStorm/TestdiStorm.csproj +69 -0
  88. data/funchook/distorm/examples/cs/distorm-net.sln +26 -0
  89. data/funchook/distorm/examples/cs/distorm-net/CodeInfo.cs +23 -0
  90. data/funchook/distorm/examples/cs/distorm-net/DecodedInst.cs +15 -0
  91. data/funchook/distorm/examples/cs/distorm-net/DecodedResult.cs +14 -0
  92. data/funchook/distorm/examples/cs/distorm-net/DecomposedInst.cs +36 -0
  93. data/funchook/distorm/examples/cs/distorm-net/DecomposedResult.cs +14 -0
  94. data/funchook/distorm/examples/cs/distorm-net/Opcodes.cs +1268 -0
  95. data/funchook/distorm/examples/cs/distorm-net/Opcodes.tt +37 -0
  96. data/funchook/distorm/examples/cs/distorm-net/Operand.cs +25 -0
  97. data/funchook/distorm/examples/cs/distorm-net/Properties/AssemblyInfo.cs +36 -0
  98. data/funchook/distorm/examples/cs/distorm-net/diStorm3.cs +411 -0
  99. data/funchook/distorm/examples/cs/distorm-net/distorm-net.csproj +80 -0
  100. data/funchook/distorm/examples/cs/readme +3 -0
  101. data/funchook/distorm/examples/ddk/README +48 -0
  102. data/funchook/distorm/examples/ddk/distorm.ini +11 -0
  103. data/funchook/distorm/examples/ddk/dummy.c +15 -0
  104. data/funchook/distorm/examples/ddk/main.c +91 -0
  105. data/funchook/distorm/examples/ddk/makefile +1 -0
  106. data/funchook/distorm/examples/ddk/sources +10 -0
  107. data/funchook/distorm/examples/java/Makefile +23 -0
  108. data/funchook/distorm/examples/java/distorm/src/Main.java +43 -0
  109. data/funchook/distorm/examples/java/distorm/src/diStorm3/CodeInfo.java +27 -0
  110. data/funchook/distorm/examples/java/distorm/src/diStorm3/DecodedInst.java +32 -0
  111. data/funchook/distorm/examples/java/distorm/src/diStorm3/DecodedResult.java +11 -0
  112. data/funchook/distorm/examples/java/distorm/src/diStorm3/DecomposedInst.java +89 -0
  113. data/funchook/distorm/examples/java/distorm/src/diStorm3/DecomposedResult.java +11 -0
  114. data/funchook/distorm/examples/java/distorm/src/diStorm3/OpcodeEnum.java +131 -0
  115. data/funchook/distorm/examples/java/distorm/src/diStorm3/Opcodes.java +1123 -0
  116. data/funchook/distorm/examples/java/distorm/src/diStorm3/Operand.java +24 -0
  117. data/funchook/distorm/examples/java/distorm/src/diStorm3/distorm3.java +41 -0
  118. data/funchook/distorm/examples/java/jdistorm.c +405 -0
  119. data/funchook/distorm/examples/java/jdistorm.h +40 -0
  120. data/funchook/distorm/examples/java/jdistorm.sln +20 -0
  121. data/funchook/distorm/examples/java/jdistorm.vcproj +208 -0
  122. data/funchook/distorm/examples/linux/Makefile +15 -0
  123. data/funchook/distorm/examples/linux/main.c +181 -0
  124. data/funchook/distorm/examples/tests/Makefile +15 -0
  125. data/funchook/distorm/examples/tests/main.cpp +42 -0
  126. data/funchook/distorm/examples/tests/main.py +66 -0
  127. data/funchook/distorm/examples/tests/test_distorm3.py +1672 -0
  128. data/funchook/distorm/examples/tests/tests.sln +20 -0
  129. data/funchook/distorm/examples/tests/tests.vcxproj +82 -0
  130. data/funchook/distorm/examples/tests/tests.vcxproj.filters +22 -0
  131. data/funchook/distorm/examples/win32/disasm.sln +25 -0
  132. data/funchook/distorm/examples/win32/disasm.vcxproj +201 -0
  133. data/funchook/distorm/examples/win32/disasm.vcxproj.filters +14 -0
  134. data/funchook/distorm/examples/win32/main.cpp +163 -0
  135. data/funchook/distorm/include/distorm.h +482 -0
  136. data/funchook/distorm/include/mnemonics.h +301 -0
  137. data/funchook/distorm/make/linux/Makefile +28 -0
  138. data/funchook/distorm/make/mac/Makefile +24 -0
  139. data/funchook/distorm/make/win32/cdistorm.vcxproj +239 -0
  140. data/funchook/distorm/make/win32/cdistorm.vcxproj.filters +80 -0
  141. data/funchook/distorm/make/win32/distorm.sln +25 -0
  142. data/funchook/distorm/make/win32/resource.h +14 -0
  143. data/funchook/distorm/make/win32/resource.rc +99 -0
  144. data/funchook/distorm/python/distorm3/__init__.py +957 -0
  145. data/funchook/distorm/python/distorm3/sample.py +51 -0
  146. data/funchook/distorm/setup.cfg +10 -0
  147. data/funchook/distorm/setup.py +266 -0
  148. data/funchook/distorm/src/config.h +169 -0
  149. data/funchook/distorm/src/decoder.c +641 -0
  150. data/funchook/distorm/src/decoder.h +33 -0
  151. data/funchook/distorm/src/distorm.c +413 -0
  152. data/funchook/distorm/src/instructions.c +597 -0
  153. data/funchook/distorm/src/instructions.h +463 -0
  154. data/funchook/distorm/src/insts.c +7939 -0
  155. data/funchook/distorm/src/insts.h +64 -0
  156. data/funchook/distorm/src/mnemonics.c +284 -0
  157. data/funchook/distorm/src/operands.c +1290 -0
  158. data/funchook/distorm/src/operands.h +28 -0
  159. data/funchook/distorm/src/prefix.c +368 -0
  160. data/funchook/distorm/src/prefix.h +64 -0
  161. data/funchook/distorm/src/textdefs.c +172 -0
  162. data/funchook/distorm/src/textdefs.h +57 -0
  163. data/funchook/distorm/src/wstring.c +47 -0
  164. data/funchook/distorm/src/wstring.h +35 -0
  165. data/funchook/distorm/src/x86defs.h +82 -0
  166. data/funchook/include/funchook.h +123 -0
  167. data/funchook/install-sh +527 -0
  168. data/funchook/src/Makefile +70 -0
  169. data/funchook/src/Makefile.in +70 -0
  170. data/funchook/src/__strerror.h +109 -0
  171. data/funchook/src/config.h +101 -0
  172. data/funchook/src/config.h.in +100 -0
  173. data/funchook/src/decoder.o +0 -0
  174. data/funchook/src/distorm.o +0 -0
  175. data/funchook/src/funchook.c +440 -0
  176. data/funchook/src/funchook.o +0 -0
  177. data/funchook/src/funchook_internal.h +155 -0
  178. data/funchook/src/funchook_io.c +182 -0
  179. data/funchook/src/funchook_io.h +64 -0
  180. data/funchook/src/funchook_io.o +0 -0
  181. data/funchook/src/funchook_syscall.S +134 -0
  182. data/funchook/src/funchook_syscall.o +0 -0
  183. data/funchook/src/funchook_unix.c +480 -0
  184. data/funchook/src/funchook_unix.o +0 -0
  185. data/funchook/src/funchook_windows.c +397 -0
  186. data/funchook/src/funchook_x86.c +622 -0
  187. data/funchook/src/funchook_x86.o +0 -0
  188. data/funchook/src/instructions.o +0 -0
  189. data/funchook/src/insts.o +0 -0
  190. data/funchook/src/libfunchook.so +0 -0
  191. data/funchook/src/mnemonics.o +0 -0
  192. data/funchook/src/operands.o +0 -0
  193. data/funchook/src/os_func.c +115 -0
  194. data/funchook/src/os_func.h +75 -0
  195. data/funchook/src/os_func.o +0 -0
  196. data/funchook/src/os_func_unix.c +94 -0
  197. data/funchook/src/os_func_unix.o +0 -0
  198. data/funchook/src/os_func_windows.c +32 -0
  199. data/funchook/src/prefix.o +0 -0
  200. data/funchook/src/printf_base.c +1688 -0
  201. data/funchook/src/printf_base.h +46 -0
  202. data/funchook/src/printf_base.o +0 -0
  203. data/funchook/src/textdefs.o +0 -0
  204. data/funchook/src/wstring.o +0 -0
  205. data/funchook/test/Makefile +43 -0
  206. data/funchook/test/Makefile.in +43 -0
  207. data/funchook/test/funchook_test +0 -0
  208. data/funchook/test/libfunchook_test.c +25 -0
  209. data/funchook/test/libfunchook_test.so +0 -0
  210. data/funchook/test/libfunchook_test2.c +18 -0
  211. data/funchook/test/suffix.list +600 -0
  212. data/funchook/test/test_main.c +430 -0
  213. data/funchook/test/test_main.o +0 -0
  214. data/funchook/test/x86_64_test.S +10 -0
  215. data/funchook/test/x86_64_test.o +0 -0
  216. data/funchook/test/x86_test.S +339 -0
  217. data/funchook/win32/config.h +1 -0
  218. data/funchook/win32/funchook.sln +52 -0
  219. data/funchook/win32/funchook.vcxproj +188 -0
  220. data/funchook/win32/funchook.vcxproj.filters +84 -0
  221. data/funchook/win32/funchook_test.vcxproj +170 -0
  222. data/funchook/win32/funchook_test.vcxproj.filters +22 -0
  223. data/funchook/win32/funchook_test_dll.vcxproj +184 -0
  224. data/funchook/win32/funchook_test_dll.vcxproj.filters +30 -0
  225. data/funchook/win32/funchook_test_exe.def +3 -0
  226. data/lib/contrast-agent.rb +8 -0
  227. data/lib/contrast.rb +57 -0
  228. data/lib/contrast/agent.rb +80 -0
  229. data/lib/contrast/agent/assess.rb +45 -0
  230. data/lib/contrast/agent/assess/adjusted_span.rb +25 -0
  231. data/lib/contrast/agent/assess/class_reverter.rb +82 -0
  232. data/lib/contrast/agent/assess/contrast_event.rb +398 -0
  233. data/lib/contrast/agent/assess/frozen_properties.rb +41 -0
  234. data/lib/contrast/agent/assess/insulator.rb +53 -0
  235. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +78 -0
  236. data/lib/contrast/agent/assess/policy/patcher.rb +85 -0
  237. data/lib/contrast/agent/assess/policy/policy.rb +116 -0
  238. data/lib/contrast/agent/assess/policy/policy_node.rb +289 -0
  239. data/lib/contrast/agent/assess/policy/policy_scanner.rb +44 -0
  240. data/lib/contrast/agent/assess/policy/preshift.rb +94 -0
  241. data/lib/contrast/agent/assess/policy/propagation_method.rb +260 -0
  242. data/lib/contrast/agent/assess/policy/propagation_node.rb +127 -0
  243. data/lib/contrast/agent/assess/policy/propagator.rb +35 -0
  244. data/lib/contrast/agent/assess/policy/propagator/append.rb +54 -0
  245. data/lib/contrast/agent/assess/policy/propagator/base.rb +37 -0
  246. data/lib/contrast/agent/assess/policy/propagator/center.rb +73 -0
  247. data/lib/contrast/agent/assess/policy/propagator/custom.rb +36 -0
  248. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +62 -0
  249. data/lib/contrast/agent/assess/policy/propagator/insert.rb +55 -0
  250. data/lib/contrast/agent/assess/policy/propagator/keep.rb +26 -0
  251. data/lib/contrast/agent/assess/policy/propagator/next.rb +42 -0
  252. data/lib/contrast/agent/assess/policy/propagator/prepend.rb +50 -0
  253. data/lib/contrast/agent/assess/policy/propagator/remove.rb +76 -0
  254. data/lib/contrast/agent/assess/policy/propagator/replace.rb +27 -0
  255. data/lib/contrast/agent/assess/policy/propagator/reverse.rb +38 -0
  256. data/lib/contrast/agent/assess/policy/propagator/select.rb +86 -0
  257. data/lib/contrast/agent/assess/policy/propagator/splat.rb +60 -0
  258. data/lib/contrast/agent/assess/policy/propagator/split.rb +49 -0
  259. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +169 -0
  260. data/lib/contrast/agent/assess/policy/propagator/trim.rb +81 -0
  261. data/lib/contrast/agent/assess/policy/rewriter_patch.rb +79 -0
  262. data/lib/contrast/agent/assess/policy/source_method.rb +209 -0
  263. data/lib/contrast/agent/assess/policy/source_node.rb +62 -0
  264. data/lib/contrast/agent/assess/policy/trigger_method.rb +209 -0
  265. data/lib/contrast/agent/assess/policy/trigger_node.rb +198 -0
  266. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +77 -0
  267. data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +31 -0
  268. data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +40 -0
  269. data/lib/contrast/agent/assess/properties.rb +392 -0
  270. data/lib/contrast/agent/assess/rule.rb +18 -0
  271. data/lib/contrast/agent/assess/rule/base.rb +72 -0
  272. data/lib/contrast/agent/assess/rule/csrf.rb +66 -0
  273. data/lib/contrast/agent/assess/rule/csrf/csrf_action.rb +28 -0
  274. data/lib/contrast/agent/assess/rule/csrf/csrf_applicator.rb +69 -0
  275. data/lib/contrast/agent/assess/rule/csrf/csrf_watcher.rb +132 -0
  276. data/lib/contrast/agent/assess/rule/provider.rb +21 -0
  277. data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +62 -0
  278. data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +73 -0
  279. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +121 -0
  280. data/lib/contrast/agent/assess/rule/redos.rb +68 -0
  281. data/lib/contrast/agent/assess/rule/response_scanning_rule.rb +47 -0
  282. data/lib/contrast/agent/assess/rule/response_watcher.rb +36 -0
  283. data/lib/contrast/agent/assess/rule/watcher.rb +36 -0
  284. data/lib/contrast/agent/assess/tag.rb +151 -0
  285. data/lib/contrast/agent/at_exit_hook.rb +33 -0
  286. data/lib/contrast/agent/class_reopener.rb +195 -0
  287. data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +26 -0
  288. data/lib/contrast/agent/deadzone/policy/policy.rb +57 -0
  289. data/lib/contrast/agent/disable_reaction.rb +24 -0
  290. data/lib/contrast/agent/exclusion_matcher.rb +190 -0
  291. data/lib/contrast/agent/feature_state.rb +379 -0
  292. data/lib/contrast/agent/inventory/policy/policy.rb +32 -0
  293. data/lib/contrast/agent/inventory/policy/trigger_node.rb +22 -0
  294. data/lib/contrast/agent/logger_manager.rb +116 -0
  295. data/lib/contrast/agent/middleware.rb +352 -0
  296. data/lib/contrast/agent/module_data.rb +16 -0
  297. data/lib/contrast/agent/patching/policy/after_load_patch.rb +37 -0
  298. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +58 -0
  299. data/lib/contrast/agent/patching/policy/method_policy.rb +94 -0
  300. data/lib/contrast/agent/patching/policy/module_policy.rb +116 -0
  301. data/lib/contrast/agent/patching/policy/patch.rb +312 -0
  302. data/lib/contrast/agent/patching/policy/patch_status.rb +192 -0
  303. data/lib/contrast/agent/patching/policy/patcher.rb +310 -0
  304. data/lib/contrast/agent/patching/policy/policy.rb +138 -0
  305. data/lib/contrast/agent/patching/policy/policy_node.rb +80 -0
  306. data/lib/contrast/agent/patching/policy/policy_unpatcher.rb +28 -0
  307. data/lib/contrast/agent/patching/policy/trigger_node.rb +81 -0
  308. data/lib/contrast/agent/protect/policy/policy.rb +37 -0
  309. data/lib/contrast/agent/protect/policy/trigger_node.rb +23 -0
  310. data/lib/contrast/agent/protect/rule.rb +58 -0
  311. data/lib/contrast/agent/protect/rule/base.rb +300 -0
  312. data/lib/contrast/agent/protect/rule/base_service.rb +88 -0
  313. data/lib/contrast/agent/protect/rule/cmd_injection.rb +156 -0
  314. data/lib/contrast/agent/protect/rule/csrf.rb +118 -0
  315. data/lib/contrast/agent/protect/rule/csrf/csrf_evaluator.rb +103 -0
  316. data/lib/contrast/agent/protect/rule/csrf/csrf_token_injector.rb +85 -0
  317. data/lib/contrast/agent/protect/rule/default_scanner.rb +300 -0
  318. data/lib/contrast/agent/protect/rule/deserialization.rb +193 -0
  319. data/lib/contrast/agent/protect/rule/http_method_tampering.rb +80 -0
  320. data/lib/contrast/agent/protect/rule/no_sqli.rb +101 -0
  321. data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +40 -0
  322. data/lib/contrast/agent/protect/rule/path_traversal.rb +143 -0
  323. data/lib/contrast/agent/protect/rule/sqli.rb +101 -0
  324. data/lib/contrast/agent/protect/rule/sqli/default_sql_scanner.rb +16 -0
  325. data/lib/contrast/agent/protect/rule/sqli/mysql_sql_scanner.rb +38 -0
  326. data/lib/contrast/agent/protect/rule/sqli/postgres_sql_scanner.rb +22 -0
  327. data/lib/contrast/agent/protect/rule/sqli/sqlite_sql_scanner.rb +19 -0
  328. data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +20 -0
  329. data/lib/contrast/agent/protect/rule/xss.rb +24 -0
  330. data/lib/contrast/agent/protect/rule/xxe.rb +120 -0
  331. data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +82 -0
  332. data/lib/contrast/agent/railtie.rb +30 -0
  333. data/lib/contrast/agent/reaction_processor.rb +47 -0
  334. data/lib/contrast/agent/request.rb +493 -0
  335. data/lib/contrast/agent/request_context.rb +225 -0
  336. data/lib/contrast/agent/require_state.rb +61 -0
  337. data/lib/contrast/agent/response.rb +215 -0
  338. data/lib/contrast/agent/rewriter.rb +244 -0
  339. data/lib/contrast/agent/scope.rb +28 -0
  340. data/lib/contrast/agent/service_heartbeat.rb +37 -0
  341. data/lib/contrast/agent/settings_state.rb +148 -0
  342. data/lib/contrast/agent/socket_client.rb +125 -0
  343. data/lib/contrast/agent/thread.rb +26 -0
  344. data/lib/contrast/agent/tracepoint_hook.rb +51 -0
  345. data/lib/contrast/agent/version.rb +8 -0
  346. data/lib/contrast/api.rb +17 -0
  347. data/lib/contrast/api/.gitkeep +0 -0
  348. data/lib/contrast/api/connection_status.rb +49 -0
  349. data/lib/contrast/api/socket.rb +43 -0
  350. data/lib/contrast/api/speedracer.rb +206 -0
  351. data/lib/contrast/api/tcp_socket.rb +31 -0
  352. data/lib/contrast/api/unix_socket.rb +25 -0
  353. data/lib/contrast/common_agent_configuration.rb +86 -0
  354. data/lib/contrast/components/agent.rb +85 -0
  355. data/lib/contrast/components/app_context.rb +188 -0
  356. data/lib/contrast/components/assess.rb +67 -0
  357. data/lib/contrast/components/config.rb +135 -0
  358. data/lib/contrast/components/contrast_service.rb +113 -0
  359. data/lib/contrast/components/heap_dump.rb +34 -0
  360. data/lib/contrast/components/interface.rb +178 -0
  361. data/lib/contrast/components/inventory.rb +23 -0
  362. data/lib/contrast/components/logger.rb +92 -0
  363. data/lib/contrast/components/protect.rb +38 -0
  364. data/lib/contrast/components/sampling.rb +41 -0
  365. data/lib/contrast/components/scope.rb +106 -0
  366. data/lib/contrast/components/settings.rb +140 -0
  367. data/lib/contrast/config.rb +33 -0
  368. data/lib/contrast/config/agent_configuration.rb +24 -0
  369. data/lib/contrast/config/application_configuration.rb +27 -0
  370. data/lib/contrast/config/assess_configuration.rb +22 -0
  371. data/lib/contrast/config/assess_rules_configuration.rb +18 -0
  372. data/lib/contrast/config/base_configuration.rb +105 -0
  373. data/lib/contrast/config/default_value.rb +16 -0
  374. data/lib/contrast/config/exception_configuration.rb +21 -0
  375. data/lib/contrast/config/heap_dump_configuration.rb +23 -0
  376. data/lib/contrast/config/inventory_configuration.rb +20 -0
  377. data/lib/contrast/config/logger_configuration.rb +20 -0
  378. data/lib/contrast/config/protect_configuration.rb +20 -0
  379. data/lib/contrast/config/protect_rule_configuration.rb +37 -0
  380. data/lib/contrast/config/protect_rules_configuration.rb +30 -0
  381. data/lib/contrast/config/root_configuration.rb +26 -0
  382. data/lib/contrast/config/ruby_configuration.rb +39 -0
  383. data/lib/contrast/config/sampling_configuration.rb +22 -0
  384. data/lib/contrast/config/server_configuration.rb +23 -0
  385. data/lib/contrast/config/service_configuration.rb +22 -0
  386. data/lib/contrast/configuration.rb +214 -0
  387. data/lib/contrast/core_extensions/assess.rb +51 -0
  388. data/lib/contrast/core_extensions/assess/array.rb +58 -0
  389. data/lib/contrast/core_extensions/assess/assess_extension.rb +145 -0
  390. data/lib/contrast/core_extensions/assess/basic_object.rb +15 -0
  391. data/lib/contrast/core_extensions/assess/erb.rb +42 -0
  392. data/lib/contrast/core_extensions/assess/exec_trigger.rb +48 -0
  393. data/lib/contrast/core_extensions/assess/fiber.rb +125 -0
  394. data/lib/contrast/core_extensions/assess/hash.rb +22 -0
  395. data/lib/contrast/core_extensions/assess/kernel.rb +95 -0
  396. data/lib/contrast/core_extensions/assess/module.rb +14 -0
  397. data/lib/contrast/core_extensions/assess/regexp.rb +206 -0
  398. data/lib/contrast/core_extensions/assess/string.rb +75 -0
  399. data/lib/contrast/core_extensions/assess/tilt_template_trigger.rb +73 -0
  400. data/lib/contrast/core_extensions/delegator.rb +14 -0
  401. data/lib/contrast/core_extensions/eval_trigger.rb +52 -0
  402. data/lib/contrast/core_extensions/inventory.rb +22 -0
  403. data/lib/contrast/core_extensions/inventory/datastores.rb +37 -0
  404. data/lib/contrast/core_extensions/module.rb +42 -0
  405. data/lib/contrast/core_extensions/object.rb +27 -0
  406. data/lib/contrast/core_extensions/protect.rb +20 -0
  407. data/lib/contrast/core_extensions/protect/applies_command_injection_rule.rb +70 -0
  408. data/lib/contrast/core_extensions/protect/applies_deserialization_rule.rb +58 -0
  409. data/lib/contrast/core_extensions/protect/applies_no_sqli_rule.rb +81 -0
  410. data/lib/contrast/core_extensions/protect/applies_path_traversal_rule.rb +119 -0
  411. data/lib/contrast/core_extensions/protect/applies_sqli_rule.rb +63 -0
  412. data/lib/contrast/core_extensions/protect/applies_xxe_rule.rb +141 -0
  413. data/lib/contrast/core_extensions/protect/kernel.rb +30 -0
  414. data/lib/contrast/core_extensions/protect/psych.rb +7 -0
  415. data/lib/contrast/core_extensions/thread.rb +31 -0
  416. data/lib/contrast/internal_exception.rb +8 -0
  417. data/lib/contrast/rails_extensions/assess/action_controller_inheritance.rb +48 -0
  418. data/lib/contrast/rails_extensions/assess/active_record.rb +32 -0
  419. data/lib/contrast/rails_extensions/assess/active_record_named.rb +61 -0
  420. data/lib/contrast/rails_extensions/assess/configuration.rb +26 -0
  421. data/lib/contrast/rails_extensions/buffer.rb +30 -0
  422. data/lib/contrast/rails_extensions/rack.rb +45 -0
  423. data/lib/contrast/security_exception.rb +14 -0
  424. data/lib/contrast/sinatra_extensions/assess/cookie.rb +26 -0
  425. data/lib/contrast/sinatra_extensions/inventory/sinatra_base.rb +59 -0
  426. data/lib/contrast/tasks/service.rb +95 -0
  427. data/lib/contrast/utils/assess/sampling_util.rb +96 -0
  428. data/lib/contrast/utils/assess/tracking_util.rb +39 -0
  429. data/lib/contrast/utils/boolean_util.rb +33 -0
  430. data/lib/contrast/utils/cache.rb +69 -0
  431. data/lib/contrast/utils/class_util.rb +58 -0
  432. data/lib/contrast/utils/comment_range.rb +19 -0
  433. data/lib/contrast/utils/data_store_util.rb +23 -0
  434. data/lib/contrast/utils/duck_utils.rb +58 -0
  435. data/lib/contrast/utils/env_configuration_item.rb +52 -0
  436. data/lib/contrast/utils/environment_util.rb +152 -0
  437. data/lib/contrast/utils/freeze_util.rb +36 -0
  438. data/lib/contrast/utils/gemfile_reader.rb +191 -0
  439. data/lib/contrast/utils/hash_digest.rb +148 -0
  440. data/lib/contrast/utils/heap_dump_util.rb +113 -0
  441. data/lib/contrast/utils/invalid_configuration_util.rb +88 -0
  442. data/lib/contrast/utils/inventory_util.rb +126 -0
  443. data/lib/contrast/utils/io_util.rb +61 -0
  444. data/lib/contrast/utils/object_share.rb +117 -0
  445. data/lib/contrast/utils/operating_environment.rb +38 -0
  446. data/lib/contrast/utils/os.rb +49 -0
  447. data/lib/contrast/utils/path_util.rb +151 -0
  448. data/lib/contrast/utils/performs_logging.rb +152 -0
  449. data/lib/contrast/utils/preflight_util.rb +13 -0
  450. data/lib/contrast/utils/prevent_serialization.rb +52 -0
  451. data/lib/contrast/utils/rack_assess_session_cookie.rb +104 -0
  452. data/lib/contrast/utils/rails_assess_configuration.rb +95 -0
  453. data/lib/contrast/utils/random_util.rb +22 -0
  454. data/lib/contrast/utils/resource_loader.rb +23 -0
  455. data/lib/contrast/utils/ruby_ast_rewriter.rb +74 -0
  456. data/lib/contrast/utils/scope_util.rb +99 -0
  457. data/lib/contrast/utils/service_response_util.rb +116 -0
  458. data/lib/contrast/utils/service_sender_util.rb +98 -0
  459. data/lib/contrast/utils/sha256_builder.rb +69 -0
  460. data/lib/contrast/utils/sinatra_helper.rb +49 -0
  461. data/lib/contrast/utils/stack_trace_utils.rb +209 -0
  462. data/lib/contrast/utils/string_utils.rb +72 -0
  463. data/lib/contrast/utils/tag_util.rb +139 -0
  464. data/lib/contrast/utils/thread_tracker.rb +54 -0
  465. data/lib/contrast/utils/timer.rb +78 -0
  466. data/resources/assess/policy.json +1673 -0
  467. data/resources/csrf/inject.js +44 -0
  468. data/resources/deadzone/policy.json +55 -0
  469. data/resources/factory-bot-spec/spec_helper.rb +30 -0
  470. data/resources/inventory/policy.json +110 -0
  471. data/resources/protect/policy.json +417 -0
  472. data/resources/rubocops/kernel/catch_cop.rb +37 -0
  473. data/resources/rubocops/kernel/require_cop.rb +37 -0
  474. data/resources/rubocops/kernel/require_relative_cop.rb +33 -0
  475. data/resources/rubocops/module/autoload_cop.rb +37 -0
  476. data/resources/rubocops/module/const_defined_cop.rb +37 -0
  477. data/resources/rubocops/module/const_get_cop.rb +37 -0
  478. data/resources/rubocops/module/const_set_cop.rb +37 -0
  479. data/resources/rubocops/module/constants_cop.rb +37 -0
  480. data/resources/rubocops/module/name_cop.rb +37 -0
  481. data/resources/rubocops/object/class_cop.rb +37 -0
  482. data/resources/rubocops/object/freeze_cop.rb +37 -0
  483. data/resources/rubocops/object/frozen_cop.rb +37 -0
  484. data/resources/rubocops/object/is_a_cop.rb +37 -0
  485. data/resources/rubocops/object/method_cop.rb +37 -0
  486. data/resources/rubocops/object/respond_to_cop.rb +37 -0
  487. data/resources/rubocops/object/singleton_class_cop.rb +37 -0
  488. data/resources/rubocops/regexp/spelling_cop.rb +44 -0
  489. data/resources/rubocops/thread/new_cop.rb +39 -0
  490. data/resources/ruby-spec/ancestors_spec.rb +70 -0
  491. data/resources/ruby-spec/modulo_spec.rb +831 -0
  492. data/resources/ruby-spec/parameters_spec.rb +261 -0
  493. data/resources/ruby-spec/ruby_spec_spec_helper.rb +35 -0
  494. data/resources/test_marker.txt +1 -0
  495. data/ruby-agent.gemspec +129 -0
  496. data/service_executables/.gitkeep +0 -0
  497. data/service_executables/VERSION +1 -0
  498. data/service_executables/linux/contrast-service +0 -0
  499. data/service_executables/mac/contrast-service +0 -0
  500. metadata +945 -0
@@ -0,0 +1,118 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ cs__scoped_require 'rack'
5
+ cs__scoped_require 'contrast/utils/object_share'
6
+ cs__scoped_require 'contrast/agent/request'
7
+ cs__scoped_require 'contrast/agent/response'
8
+ cs__scoped_require 'contrast/utils/stack_trace_utils'
9
+ cs__scoped_require 'contrast/utils/timer'
10
+
11
+ module Contrast
12
+ module Agent
13
+ module Protect
14
+ module Rule
15
+ # The Ruby implementation of the Protect Cross-Site Request Forgery
16
+ # rule.
17
+ class Csrf < Contrast::Agent::Protect::Rule::Base
18
+ NAME = 'csrf'
19
+
20
+ def name
21
+ NAME
22
+ end
23
+
24
+ def stream_safe?
25
+ false
26
+ end
27
+
28
+ def request_evaluator
29
+ @_request_evaluator ||= Contrast::Agent::Protect::Rule::Csrf::CsrfEvaluator.new
30
+ end
31
+
32
+ def token_injector
33
+ @_token_injector ||= Contrast::Agent::Protect::Rule::Csrf::CsrfTokenInjector.new
34
+ end
35
+
36
+ BLOCK_MESSAGE = 'CSRF rule triggered. Request blocked.'
37
+
38
+ def prefilter context
39
+ return unless enabled? && mode != :NO_ACTION
40
+
41
+ request = context.request
42
+ return if request_evaluator.can_ignore_check?(request)
43
+
44
+ parameters = request.parameters
45
+ return unless parameters&.any?
46
+
47
+ tokens = parameters[token_injector.token_name]
48
+ expected_token = token_injector.get_expected_token(context)
49
+ return if valid_token?(expected_token, tokens)
50
+
51
+ # unexpected token is interpreted as an attack with a match
52
+ ia_result = Contrast::Api::Settings::InputAnalysisResult.new
53
+ ia_result.input_type = :BODY
54
+ ia_result.value = build_attack_string(context.request)
55
+
56
+ result = build_attack_with_match(
57
+ context,
58
+ ia_result,
59
+ nil,
60
+ expected_token,
61
+ details: build_details(expected_token, tokens))
62
+
63
+ append_to_activity(context, result)
64
+
65
+ raise Contrast::SecurityException.new(self, BLOCK_MESSAGE) if blocked?
66
+ end
67
+
68
+ def valid_token? expected, actual_tokens
69
+ actual_tokens&.include?(expected)
70
+ end
71
+
72
+ def postfilter context
73
+ return unless enabled? && POSTFILTER_MODES.include?(mode)
74
+
75
+ token_injector.do_injection(context)
76
+ end
77
+
78
+ def build_sample context, evaluation, _url, **kwargs
79
+ sample = build_base_sample(context, evaluation)
80
+ sample.csrf = kwargs[:details]
81
+ sample
82
+ end
83
+
84
+ # Build a subclass of the RaspRuleSample using the query string and the
85
+ # evaluation
86
+ def build_details expected, actual_tokens
87
+ details = Contrast::Api::Dtm::CsrfDetails.new
88
+ details.name = Contrast::Utils::StringUtils.protobuf_safe_string(token_injector.token_name)
89
+ details.expected = Contrast::Utils::StringUtils.protobuf_safe_string(expected)
90
+ presented = build_presented_token(actual_tokens)
91
+ details.presented = Contrast::Utils::StringUtils.protobuf_safe_string(presented)
92
+ details
93
+ end
94
+
95
+ def build_presented_token actual_tokens
96
+ return Contrast::Utils::ObjectShare::EMPTY_STRING unless actual_tokens&.any?
97
+
98
+ actual_tokens.first
99
+ end
100
+
101
+ # Convert the request parameters into an attack string
102
+ def build_attack_string request
103
+ arr = []
104
+ request.dtm.normalized_request_params.each_with_index do |(name, value), index|
105
+ arr << Contrast::Utils::ObjectShare::AND unless index.zero?
106
+ arr += [name.to_s, Contrast::Utils::ObjectShare::EQUALS, value.values.to_s]
107
+ end
108
+ if arr.empty?
109
+ request.query_string
110
+ else
111
+ arr.join
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,103 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ cs__scoped_require 'contrast/utils/object_share'
5
+ cs__scoped_require 'contrast/components/interface'
6
+ cs__scoped_require 'uri'
7
+
8
+ # This class is used by the CSRF rule to determine if the given Request is
9
+ # susceptible to CSRF / if the rule applies to it.
10
+ class Contrast::Agent::Protect::Rule::Csrf::CsrfEvaluator # rubocop:disable Style/ClassAndModuleChildren
11
+ include Contrast::Components::Interface
12
+ access_component :logging
13
+
14
+ def can_ignore_check? request
15
+ if !form_submittable_method?(request)
16
+ logger.debug("Ignoring method #{ request.request_method } for URI #{ request.uri }")
17
+ true
18
+ elsif ajax_request?(request)
19
+ logger.debug("Ignoring Ajax request for URI #{ request.uri }")
20
+ true
21
+ elsif empty_post?(request)
22
+ logger.debug("Ignoring empty POST request for URI #{ request.uri }")
23
+ true
24
+ elsif !form_content_type?(request)
25
+ logger.debug("Ignoring POST request with Content-Type #{ request.content_type } for URI #{ request.uri }")
26
+ true
27
+ elsif request.static_request?
28
+ logger.debug("Ignoring static request for URI #{ request.uri }")
29
+ true
30
+ elsif origin_is_referer?(request)
31
+ logger.debug("Ignoring equivalent origin-referer for URI #{ request.uri }")
32
+ true
33
+ elsif login_page?(request)
34
+ logger.debug("Ignoring possible login page for URI #{ request.uri }")
35
+ true
36
+ else
37
+ false
38
+ end
39
+ rescue StandardError => e
40
+ logger.warn(e, 'Unable to determine if CSRF can be ignored')
41
+ end
42
+
43
+ POST_METHOD = 'POST'
44
+ def form_submittable_method? request
45
+ POST_METHOD == request.request_method
46
+ end
47
+
48
+ REQUESTED_WITH = 'X-REQUESTED-WITH'
49
+ def ajax_request? request
50
+ requested_with = request.header_value(REQUESTED_WITH)
51
+ !requested_with.nil? && !requested_with.empty?
52
+ end
53
+
54
+ USER_AGENT = 'USER-AGENT'
55
+ def empty_user_agent? request
56
+ agent = request.header_value(USER_AGENT)
57
+ agent.nil? || agent.empty?
58
+ end
59
+
60
+ def empty_post? request
61
+ request.request_body_str.empty? && (request.query_string.nil? || request.query_string.empty?)
62
+ end
63
+
64
+ FORM_CONTENT_TYPES = %w[text/plain multipart/form-data application/x-www-form-urlencoded].cs__freeze
65
+
66
+ def form_content_type? request
67
+ content_type = request.content_type
68
+ return true if content_type.nil? || content_type.empty?
69
+
70
+ content_type.start_with?(*FORM_CONTENT_TYPES)
71
+ end
72
+
73
+ ORIGIN = 'ORIGIN'
74
+ REFERER = 'REFERER'
75
+ def origin_is_referer? request
76
+ origin = request.header_value(ORIGIN)
77
+ referer = request.header_value(REFERER)
78
+ return false unless origin && referer
79
+ return false if origin.empty? || referer.empty?
80
+
81
+ origin = trim_origin(origin)
82
+ referer = URI(referer).host
83
+ origin == referer
84
+ end
85
+
86
+ HTTP = 'http://'
87
+ HTTPS = 'https://'
88
+ def trim_origin origin_header
89
+ origin_header = if origin_header.to_s.start_with?(HTTP, HTTPS)
90
+ URI(origin_header).host
91
+ else
92
+ idx = origin_header.index(Contrast::Utils::ObjectShare::COLON)
93
+ idx.nil? ? origin_header : origin_header[0, idx]
94
+ end
95
+ origin_header
96
+ end
97
+
98
+ LOGIN_MARKERS = %w[login auth j_security verify validate sessions].cs__freeze
99
+ def login_page? request
100
+ url = request.url.downcase
101
+ LOGIN_MARKERS.any? { |marker| url.index(marker) }
102
+ end
103
+ end
@@ -0,0 +1,85 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ cs__scoped_require 'contrast/utils/object_share'
5
+ cs__scoped_require 'contrast/utils/random_util'
6
+
7
+ # This class is used by the CSRF rule to inject the Contrast CSRF token into
8
+ # the Response's body, allowing for the detection of CSRF attacks.
9
+ class Contrast::Agent::Protect::Rule::Csrf::CsrfTokenInjector # rubocop:disable Style/ClassAndModuleChildren
10
+ TKN_NAME = 'cs_csrf_tkn'
11
+ def token_name
12
+ TKN_NAME
13
+ end
14
+
15
+ SESSION_TOKEN_PREFIX = '__CONTRAST__'
16
+ def token_key
17
+ @_token_key ||= SESSION_TOKEN_PREFIX + token_name
18
+ end
19
+
20
+ def javascript
21
+ @_javascript ||= build_javascript
22
+ end
23
+
24
+ SCRIPT_LOC = File.join('csrf', 'inject.js').cs__freeze
25
+ NAME_MARKER = '!TOKEN_NAME!'
26
+ VALUE_MARKER = '!TOKEN_VALUE!'
27
+ def build_javascript
28
+ script = Contrast::Utils::ResourceLoader.load(SCRIPT_LOC)
29
+ script&.sub!(NAME_MARKER, TKN_NAME)
30
+ script
31
+ end
32
+
33
+ CSRF_TOKEN_LENGTH = 8
34
+ def get_expected_token context
35
+ cookies = context.request.request_cookies
36
+ token = cookies[token_key]
37
+ return token if token
38
+ return unless context.response
39
+
40
+ build_token(context)
41
+ end
42
+
43
+ def build_token context
44
+ token = Contrast::Utils::RandomUtil.secure_random_string(CSRF_TOKEN_LENGTH)
45
+ context&.response&.set_header(token_key, token)
46
+ token
47
+ end
48
+
49
+ CONTENT_TYPE = 'CONTENT-TYPE'
50
+
51
+ def wedge_token? response
52
+ return true unless response
53
+
54
+ content_type = response.header(CONTENT_TYPE)
55
+ return true unless content_type
56
+
57
+ content_type = content_type.to_s
58
+ !content_type.start_with?(*DATA_CONTENT_TYPES)
59
+ end
60
+
61
+ END_BODY_TAG = %r{</body>}i.cs__freeze
62
+ def do_injection context
63
+ return unless wedge_token?(context&.response)
64
+
65
+ body_string = context&.response&.body
66
+ return unless body_string
67
+
68
+ index = body_string.index(END_BODY_TAG)
69
+ return unless index
70
+
71
+ injection = get_expected_token(context)
72
+ return unless injection
73
+
74
+ injection = javascript.sub(VALUE_MARKER, injection)
75
+ body_string.insert(index, injection)
76
+ context&.response&.update_body(body_string)
77
+ end
78
+
79
+ DATA_CONTENT_TYPES = [
80
+ 'image/', 'audio/', 'video/',
81
+ 'application/json', 'application/xml',
82
+ 'application/octet', 'application/force',
83
+ 'text/json', 'text/xml'
84
+ ].cs__freeze
85
+ end
@@ -0,0 +1,300 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ # The base class used to determine if a user input crosses a token boundary or
5
+ # state, indicating a successful attack using SQL or NoSQL Injection.
6
+ #
7
+ # @deprecated RUBY-356: this class and those that extend it are being phased out
8
+ # in favor of the more performant code in the Service.
9
+ class Contrast::Agent::Protect::Rule::DefaultScanner # rubocop:disable Style/ClassAndModuleChildren
10
+ # Potential states
11
+ # :STATE_INSIDE_TOKEN
12
+ # :STATE_INSIDE_NUMBER
13
+ # :STATE_EXPECTING_TOKEN
14
+ # :STATE_INSIDE_DOUBLEQUOTE
15
+ # :STATE_INSIDE_SINGLEQUOTE
16
+ # :STATE_INSIDE_STRING_ESCAPE_BLOCK
17
+ # :STATE_INSIDE_LINE_COMMENT # inside a comment that will continue to the end of the line
18
+ # :STATE_INSIDE_BLOCK_COMMENT # inside a commend that will end with a closing tag
19
+ # :STATE_SKIP_NEXT_CHARACTER
20
+
21
+ def crosses_boundary query, index, input
22
+ last_boundary = 0
23
+ token_boundaries(query).each do |boundary|
24
+ if boundary > index
25
+ return last_boundary, boundary if boundary < index + input.length
26
+
27
+ break
28
+ end
29
+ last_boundary = boundary
30
+ end
31
+ nil
32
+ end
33
+
34
+ def token_boundaries query
35
+ @_token_boundaries ||= scan_token_boundaries(query)
36
+ end
37
+
38
+ CANARY = "select * from tbl where bar='bar';#' and pwd='sec3r3t'; # CANARY"
39
+ def scan_token_boundaries query
40
+ boundaries = []
41
+ return boundaries unless query && !query.empty?
42
+
43
+ state = :STATE_EXPECTING_TOKEN
44
+ index = 0
45
+
46
+ while index < query.length
47
+ char = query[index]
48
+
49
+ previous_state = state
50
+
51
+ state = process_state(boundaries, state, char, index, query)
52
+
53
+ case state
54
+ when :STATE_SKIP_NEXT_CHARACTER
55
+ index += 1
56
+ state = previous_state
57
+ when :STATE_INSIDE_STRING_ESCAPE_BLOCK
58
+ index = find_escape_sequence_boundary(query, index + 1)
59
+ state = previous_state
60
+ when :STATE_INSIDE_BLOCK_COMMENT
61
+ index = find_block_comment_boundary(query, index + 2)
62
+ index += 1
63
+ state = previous_state
64
+ boundaries << index
65
+ when :STATE_INSIDE_LINE_COMMENT
66
+ index = find_new_line_boundary(query, index + 1)
67
+ state = previous_state
68
+ boundaries << index
69
+ end
70
+ index += 1
71
+ end
72
+ boundaries
73
+ end
74
+
75
+ def process_state boundaries, current_state, char, index, query
76
+ case current_state
77
+ when :STATE_EXPECTING_TOKEN
78
+ process_expecting_token(boundaries, char, index, query)
79
+ when :STATE_INSIDE_NUMBER
80
+ process_number(boundaries, char, index, query)
81
+ when :STATE_INSIDE_TOKEN
82
+ process_inside_token(boundaries, char, index, query)
83
+ when :STATE_INSIDE_DOUBLEQUOTE
84
+ process_double_quote(boundaries, char, index, query)
85
+ when :STATE_INSIDE_SINGLEQUOTE
86
+ process_single_quote(boundaries, char, index, query)
87
+ end
88
+ end
89
+
90
+ def process_expecting_token boundaries, char, index, query
91
+ if char == Contrast::Utils::ObjectShare::SINGLE_QUOTE
92
+ boundaries << index
93
+ :STATE_INSIDE_SINGLEQUOTE
94
+ elsif char == Contrast::Utils::ObjectShare::DOUBLE_QUOTE
95
+ boundaries << index
96
+ :STATE_INSIDE_DOUBLEQUOTE
97
+ elsif char.match?(Contrast::Utils::ObjectShare::DIGIT_REGEXP)
98
+ boundaries << index
99
+ :STATE_INSIDE_NUMBER
100
+ elsif start_line_comment?(char, index, query)
101
+ boundaries << index
102
+ :STATE_INSIDE_LINE_COMMENT
103
+ elsif start_block_comment?(char, index, query)
104
+ boundaries << index
105
+ :STATE_INSIDE_BLOCK_COMMENT
106
+ elsif char.match?(Contrast::Utils::ObjectShare::NOT_WHITE_SPACE_REGEXP)
107
+ boundaries << index
108
+ :STATE_INSIDE_TOKEN
109
+ else
110
+ :STATE_EXPECTING_TOKEN
111
+ end
112
+ end
113
+
114
+ def process_inside_token boundaries, char, index, query
115
+ if char == Contrast::Utils::ObjectShare::SINGLE_QUOTE
116
+ boundaries << index
117
+ :STATE_INSIDE_SINGLEQUOTE
118
+ elsif char == Contrast::Utils::ObjectShare::DOUBLE_QUOTE
119
+ boundaries << index
120
+ :STATE_INSIDE_DOUBLEQUOTE
121
+ elsif start_line_comment?(char, index, query)
122
+ boundaries << index
123
+ :STATE_INSIDE_LINE_COMMENT
124
+ elsif start_block_comment?(char, index, query)
125
+ boundaries << index
126
+ :STATE_INSIDE_BLOCK_COMMENT
127
+ elsif operator?(char)
128
+ boundaries << index
129
+ :STATE_EXPECTING_TOKEN
130
+ elsif char.match?(Contrast::Utils::ObjectShare::WHITE_SPACE_REGEXP)
131
+ boundaries << index
132
+ :STATE_EXPECTING_TOKEN
133
+ else
134
+ :STATE_INSIDE_TOKEN
135
+ end
136
+ end
137
+
138
+ def process_number boundaries, char, index, _query
139
+ if char.match?(Contrast::Utils::ObjectShare::DIGIT_REGEXP) || char == Contrast::Utils::ObjectShare::PERIOD
140
+ :STATE_INSIDE_NUMBER
141
+ else
142
+ boundaries << index
143
+ :STATE_EXPECTING_TOKEN
144
+ end
145
+ end
146
+
147
+ def process_double_quote boundaries, char, index, query
148
+ if escape_char?(char)
149
+ :STATE_SKIP_NEXT_CHARACTER
150
+ elsif escape_sequence_start?(char)
151
+ :STATE_INSIDE_STRING_ESCAPE_BLOCK
152
+ elsif char == Contrast::Utils::ObjectShare::DOUBLE_QUOTE
153
+ if double_quote_escape_in_double_quote? && double_quote?(query, index + 1)
154
+ :STATE_SKIP_NEXT_CHARACTER
155
+ else
156
+ boundaries << index
157
+ :STATE_EXPECTING_TOKEN
158
+ end
159
+ else
160
+ :STATE_INSIDE_DOUBLEQUOTE
161
+ end
162
+ end
163
+
164
+ def process_single_quote boundaries, char, index, query
165
+ if escape_char?(char)
166
+ :STATE_SKIP_NEXT_CHARACTER
167
+ elsif escape_sequence_start?(char)
168
+ :STATE_INSIDE_STRING_ESCAPE_BLOCK
169
+ elsif char == Contrast::Utils::ObjectShare::SINGLE_QUOTE
170
+ if singe_quote_escape_in_singe_quote? && single_quote?(query, index + 1)
171
+ :STATE_SKIP_NEXT_CHARACTER
172
+ else
173
+ boundaries << index
174
+ :STATE_EXPECTING_TOKEN
175
+ end
176
+ else
177
+ :STATE_INSIDE_SINGLEQUOTE
178
+ end
179
+ end
180
+
181
+ def double_quote? query, index
182
+ return false unless index >= 0 && index < query.length
183
+
184
+ query[index] == Contrast::Utils::ObjectShare::DOUBLE_QUOTE
185
+ end
186
+
187
+ def single_quote? query, index
188
+ return false unless index >= 0 && index < query.length
189
+
190
+ query[index] == Contrast::Utils::ObjectShare::SINGLE_QUOTE
191
+ end
192
+
193
+ def find_escape_sequence_boundary query, index
194
+ idx = index
195
+ while idx < query.length
196
+ char = query[idx]
197
+ break if escape_sequence_end?(char)
198
+
199
+ idx += 1
200
+ end
201
+ idx
202
+ end
203
+
204
+ def find_block_comment_boundary query, index
205
+ idx = index
206
+ while idx < query.length
207
+ char = query[idx]
208
+ break if end_block_comment?(char, idx, query)
209
+
210
+ idx += 1
211
+ end
212
+ idx
213
+ end
214
+
215
+ def find_new_line_boundary query, index
216
+ idx = index
217
+ while idx < query.length
218
+ char = query[idx]
219
+ break if char == Contrast::Utils::ObjectShare::NEW_LINE
220
+ break if char == Contrast::Utils::ObjectShare::RETURN
221
+
222
+ idx += 1
223
+ end
224
+ idx
225
+ end
226
+
227
+ OPERATOR_PATTERN = %r{[+=*^/%><!-]}.cs__freeze
228
+ def operator? char
229
+ char.match?(OPERATOR_PATTERN)
230
+ end
231
+
232
+ # @note: Any class extending this module should override these methods as needed
233
+ # Are the current and subsequent characters both '-' ?
234
+ def start_line_comment? char, index, query
235
+ return false unless char == Contrast::Utils::ObjectShare::DASH
236
+ return false unless (query.length - 2) >= index
237
+
238
+ query[index + 1] == Contrast::Utils::ObjectShare::DASH
239
+ end
240
+
241
+ # Is the current character / sequence of characters the start of a block comment
242
+ # We assume '/*' starts the comment by default
243
+ def start_block_comment? char, index, query
244
+ return false unless char == Contrast::Utils::ObjectShare::SLASH
245
+ return false unless (query.length - 2) >= index
246
+
247
+ query[index + 1] == Contrast::Utils::ObjectShare::ASTERISK
248
+ end
249
+
250
+ # Is the current character / sequence of characters the end of a block comment
251
+ # We assume '*/' ends the comment by default
252
+ def end_block_comment? char, index, query
253
+ return false unless char == Contrast::Utils::ObjectShare::ASTERISK
254
+ return false unless (query.length - 2) >= index
255
+
256
+ query[index + 1] == Contrast::Utils::ObjectShare::SLASH
257
+ end
258
+
259
+ # Indicates if '""' inside of double quotes is the equivalent of '\"'
260
+ # We assume no by default
261
+ def double_quote_escape_in_double_quote?
262
+ false
263
+ end
264
+
265
+ # Indicates if "''" inside of single quotes is the equivalent of "\'"
266
+ # We assume yes by default
267
+ def singe_quote_escape_in_singe_quote?
268
+ true
269
+ end
270
+
271
+ # Does this language let the user redefine the escape character
272
+ # We assume no by default
273
+ def redefines_escape_char?
274
+ false
275
+ end
276
+
277
+ # Is the character provided an escape character?
278
+ # By default, we'll assume
279
+ def escape_char? char
280
+ char == Contrast::Utils::ObjectShare::BACK_SLASH
281
+ end
282
+
283
+ # Does this language support string escape sequences?
284
+ # We assume no by default
285
+ def support_string_escape_sequence?
286
+ false
287
+ end
288
+
289
+ # Is this the start of a string escape sequence?
290
+ # Since escape sequences aren't supported, the answer is always false
291
+ def escape_sequence_start? _char
292
+ false
293
+ end
294
+
295
+ # Is this the end of a string escape sequence?
296
+ # Since escape sequences aren't supported, the answer is always false
297
+ def escape_sequence_end? _char
298
+ false
299
+ end
300
+ end