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,193 @@
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
+ module Contrast
5
+ module Agent
6
+ module Protect
7
+ module Rule
8
+ # This class handles our implementation of the Untrusted
9
+ # Deserialization Protect rule.
10
+ class Deserialization < Contrast::Agent::Protect::Rule::Base
11
+ # The TeamServer recognized name of this rule
12
+ NAME = 'untrusted-deserialization'
13
+
14
+ # The rule specific reason for raising a security exception.
15
+ BLOCK_MESSAGE = 'Untrusted Deserialization rule triggered. Deserialization blocked.'
16
+
17
+ # Gadgets that map to ERB modules
18
+ ERB_GADGETS = %w[
19
+ object:ERB
20
+ ].cs__freeze
21
+
22
+ # Gadgets that map to ActionDispatch modules
23
+ ACTION_DISPATCH_GADGETS = %w[
24
+ object:ActionDispatch::Routing::RouteSet::NamedRouteCollection
25
+ ].cs__freeze
26
+
27
+ # Gadgets that map to Arel Modules
28
+ AREL_GADGETS = %w[
29
+ string:Arel::Nodes::SqlLiteral
30
+ object:Arel::Nodes
31
+ ].cs__freeze
32
+
33
+ # Used to indicate to TeamServer the gadget is an ERB module
34
+ ERB = 'ERB'
35
+ # Used to indicate to TeamServer the gadget is an ActionDispatch
36
+ # module
37
+ DISPATCH = 'ActionDispatch'
38
+ # Used to indicate to TeamServer the gadget is an Arel module
39
+ AREL = 'Arel'
40
+
41
+ # Return the TeamServer understood id / name of this rule.
42
+ # @return [String] the TeamServer understood id / name of this rule.
43
+ def name
44
+ NAME
45
+ end
46
+
47
+ # Return the specific blocking message for this rule.
48
+ # @return [String] the reason for the raised security exception.
49
+ def block_message
50
+ BLOCK_MESSAGE
51
+ end
52
+
53
+ # Per the spec, this rule applies regardless of input. Only the mode
54
+ # of the rule and code exclusions apply at this point.
55
+ # @return [Boolean] should the rule apply to this call.
56
+ def infilter? _context
57
+ return false unless enabled?
58
+ return false if protect_excluded_by_code?
59
+
60
+ true
61
+ end
62
+
63
+ # Determine if the input being deserialized is an attack and take
64
+ # appropriate actions.
65
+ # @param context [Contrast::Agent::RequestContext] the request
66
+ # context in which this attack is occurring.
67
+ # @param serialized_input [String] the string being deserialized.
68
+ # @raise [Contrast::SecurityException] if attack detected while in
69
+ # block mode.
70
+ def infilter context, serialized_input
71
+ return unless infilter?(context)
72
+
73
+ gadget = find_gadget(serialized_input)
74
+ # If there isn't a gadget, this isn't an attack, so we have nothing
75
+ # to do here. Let the application carry on business as usual.
76
+ return unless gadget
77
+
78
+ ia_result = build_evaluation(serialized_input)
79
+ kwargs = { GADGET_TYPE: gadget }
80
+ result = build_attack_with_match(context, ia_result, nil, serialized_input, **kwargs)
81
+ append_to_activity(context, result)
82
+
83
+ raise Contrast::SecurityException.new(self, block_message) if blocked?
84
+ end
85
+
86
+ # Determine if the issued command was called while we're
87
+ # deserializing. If we are, treat this as an attack.
88
+ # @param gadget_command [String] the command being executed during
89
+ # the deserialization call.
90
+ # @raise [Contrast::SecurityException] if attack detected while in
91
+ # block mode.
92
+ def check_command_scope gadget_command
93
+ return unless deserializing?
94
+
95
+ context = Contrast::Agent::REQUEST_TRACKER.current
96
+ kwargs = { COMMAND_SCOPE: true }
97
+ ia_result = build_evaluation(gadget_command)
98
+ result = build_attack_with_match(context, ia_result, nil, gadget_command, **kwargs)
99
+ append_to_activity(context, result)
100
+ raise Contrast::SecurityException.new(self, BLOCK_MESSAGE) if blocked?
101
+ end
102
+
103
+ # I don't know a better way to do this without introducing another
104
+ # scope.
105
+ # The policy files are designed around using the Module names, not
106
+ # the file names, but stack is built using filenames. These are the
107
+ # files and methods we patch for this rule.
108
+ #
109
+ # There's not real test for this since I can't figure out how to get
110
+ # a command to execute from within the .load methods
111
+ # Assuming someone else does, this should just work (tested with
112
+ # Screener and the combination "%w[erb.rb `result']")
113
+ DESERIALIZER_STACK = [
114
+ %w[/psych.rb: `load'],
115
+ %w[/marshal.rb: `load']
116
+ ].cs__freeze
117
+ # We're considered deserializing if the call stack includes a
118
+ # reference to the file in which a serializer is defined and
119
+ # the method of that serializer responsible for deserializing.
120
+ # @return [Boolean] if the caller indicates deserialization.
121
+ def deserializing?
122
+ Kernel.caller.product(DESERIALIZER_STACK).find do |(frame, (class_signature_form, method_signature_form))|
123
+ frame.end_with?(method_signature_form) && frame.include?(class_signature_form)
124
+ end
125
+ end
126
+
127
+ protected
128
+
129
+ # Build the RaspRuleSample for the detected Deserialization attack.
130
+ # @param context [Contrast::Agent::RequestContext] the request
131
+ # context in which this attack is occurring.
132
+ # @param input_analysis_result [Contrast::Api::Settings::InputAnalysisResult]
133
+ # the result of the analysis done by this rule.
134
+ # @param _candidate_string [nil] unused.
135
+ # @param kwargs [Hash, nil] Hash of inputs used by this rule to flesh
136
+ # out the report to TeamServer in order to provide specific
137
+ # information for this rule.
138
+ # @return [Contrast::Api::Dtm::RaspRuleSample] the information needed
139
+ # to render this attack event in TeamServer.
140
+ def build_sample context, input_analysis_result, _candidate_string, **kwargs
141
+ sample = build_base_sample(context, input_analysis_result)
142
+ sample.untrusted_deserialization = Contrast::Api::Dtm::UntrustedDeserializationDetails.new
143
+
144
+ deserializer = kwargs[:GADGET_TYPE]
145
+ sample.untrusted_deserialization.deserializer = Contrast::Utils::StringUtils.protobuf_safe_string(deserializer)
146
+
147
+ command = !!kwargs[:COMMAND_SCOPE]
148
+ sample.untrusted_deserialization.command = command
149
+
150
+ sample
151
+ end
152
+
153
+ private
154
+
155
+ # Used to name this input since input analysis isn't done for this
156
+ # rule
157
+ INPUT_NAME = 'Serialized Gadget'
158
+ # We know that this attack happened, so the result is always matched
159
+ # and the level is always critical. Only variable is the Gadget
160
+ # supplied by the attacker.
161
+ # @param gadget_string [String] the input to be deserialized in which
162
+ # the gadget exists or the command that resulted from deserializing
163
+ # an input not detected in the initial infilter.
164
+ # @return [Contrast::Api::Settings::InputAnalysisResult] the result
165
+ # of the analysis done by this rule.
166
+ def build_evaluation gadget_string
167
+ ia_result = Contrast::Api::Settings::InputAnalysisResult.new
168
+ ia_result.rule_id = name
169
+ ia_result.input_type = :UNKNOWN
170
+ ia_result.key = INPUT_NAME
171
+ ia_result.value = Contrast::Utils::StringUtils.protobuf_safe_string(gadget_string)
172
+ ia_result
173
+ end
174
+
175
+ # Find the gadget within the serialized input, if such a gadget
176
+ # exists.
177
+ # @param serialized_input [String] the string being deserialized in
178
+ # which the gadget may exist.
179
+ # @return [String, nil] the gadget, if found.
180
+ def find_gadget serialized_input
181
+ if ERB_GADGETS.any? { |gadget| serialized_input.index(gadget) }
182
+ ERB
183
+ elsif ACTION_DISPATCH_GADGETS.any? { |gadget| serialized_input.index(gadget) }
184
+ DISPATCH
185
+ elsif AREL_GADGETS.any? { |gadget| serialized_input.index(gadget) }
186
+ AREL
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,80 @@
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
+ module Contrast
5
+ module Agent
6
+ module Protect
7
+ module Rule
8
+ # The Ruby implementation of the Protect HTTP Method Tampering rule.
9
+ class HttpMethodTampering < Contrast::Agent::Protect::Rule::BaseService
10
+ NAME = 'method-tampering'
11
+ STANDARD_METHODS = %w[GET HEAD POST PUT DELETE CONNECT OPTIONS TRACE PATCH].cs__freeze
12
+
13
+ def name
14
+ NAME
15
+ end
16
+
17
+ def postfilter context
18
+ return unless enabled? && POSTFILTER_MODES.include?(mode)
19
+
20
+ logger.debug("#{ name } postfilter...")
21
+ return if normal_request?(context)
22
+
23
+ # The only way to be here in postfilter with a result is if the rule mode was MONITOR
24
+ ia_results = gather_ia_results(context)
25
+ return if ia_results.empty?
26
+
27
+ # does the status code start with 4 or 5? Rails responds with 404 (but java is checking 501)
28
+ response_code = context&.response&.response_code
29
+ return unless response_code
30
+
31
+ method = ia_results.first.value
32
+ result = if response_code.to_s.start_with?('4', '5')
33
+ build_attack_without_match(
34
+ context,
35
+ nil,
36
+ nil,
37
+ method: method,
38
+ response_code: response_code)
39
+ else
40
+ build_attack_with_match(
41
+ context,
42
+ nil,
43
+ nil,
44
+ nil,
45
+ method: method,
46
+ response_code: response_code)
47
+ end
48
+ append_to_activity(context, result) if result
49
+ end
50
+
51
+ protected
52
+
53
+ def build_sample context, evaluation, _candidate_string, **kwargs
54
+ sample = build_base_sample(context, evaluation)
55
+ sample.user_input.value = kwargs[:method]
56
+
57
+ sample.method_tampering = Contrast::Api::Dtm::HttpMethodTamperingDetails.new
58
+ sample.method_tampering.method = Contrast::Utils::StringUtils.protobuf_safe_string(kwargs[:method])
59
+ code = kwargs[:response_code] || -1
60
+ sample.method_tampering.response_code = code.to_i
61
+ sample
62
+ end
63
+
64
+ def build_user_input _evaluation
65
+ input = Contrast::Api::Dtm::UserInput.new
66
+ input.input_type = :METHOD
67
+ input
68
+ end
69
+
70
+ private
71
+
72
+ def normal_request? context
73
+ method = context.request.request_method
74
+ context.request.static_request? || method.nil? || STANDARD_METHODS.include?(method.upcase)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,101 @@
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
+ # This could be useful for making patterns maybe?
5
+ # https://github.com/cr0hn/nosqlinjection_wordlists
6
+ # https://www.owasp.org/index.php/Testing_for_NoSQL_injection
7
+ module Contrast
8
+ module Agent
9
+ module Protect
10
+ module Rule
11
+ # The Ruby implementation of the Protect NoSQL Injection rule.
12
+ class NoSqli < Contrast::Agent::Protect::Rule::BaseService
13
+ NAME = 'nosql-injection'
14
+ BLOCK_MESSAGE = 'NoSQLi rule triggered. Response blocked.'
15
+
16
+ def name
17
+ NAME
18
+ end
19
+
20
+ def block_message
21
+ BLOCK_MESSAGE
22
+ end
23
+
24
+ def infilter context, database, query_string
25
+ return nil unless infilter?(context)
26
+
27
+ result = find_attacker(context, query_string, database: database)
28
+ return nil unless result
29
+
30
+ append_to_activity(context, result)
31
+
32
+ raise Contrast::SecurityException.new(self, BLOCK_MESSAGE) if blocked?
33
+ end
34
+
35
+ def build_attack_with_match context, input_analysis_result, result, query_string, **kwargs
36
+ return result if mode == :NO_ACTION || mode == :PERMIT
37
+
38
+ attack_string = input_analysis_result.value
39
+ regexp = Regexp.new(Regexp.escape(attack_string), Regexp::IGNORECASE)
40
+ return nil unless query_string.match?(regexp)
41
+
42
+ scanner = select_scanner
43
+ ss = StringScanner.new(query_string)
44
+ length = attack_string.length
45
+ while ss.scan_until(regexp)
46
+ # the pos of StringScanner is at the end of the regexp (input string),
47
+ # we need the beginning
48
+ idx = ss.pos - attack_string.length
49
+ last_boundary, boundary = scanner.crosses_boundary(query_string, idx, input_analysis_result.value)
50
+ next unless last_boundary && boundary
51
+
52
+ kwargs[:start_idx] = idx
53
+ kwargs[:end_idx] = idx + length
54
+ kwargs[:boundary_overrun_idx] = boundary
55
+ kwargs[:input_boundary_idx] = last_boundary
56
+
57
+ result ||= build_attack_result(context)
58
+ update_successful_attack_response(context, input_analysis_result, result, query_string)
59
+ append_sample(context, input_analysis_result, result, query_string, **kwargs)
60
+ end
61
+
62
+ result
63
+ end
64
+
65
+ protected
66
+
67
+ def find_attacker context, potential_attack_string, **kwargs
68
+ if potential_attack_string
69
+ # We need the query hash to be a JSON string to match on JSON input attacks
70
+ begin
71
+ potential_attack_string = JSON.generate(potential_attack_string).to_s
72
+ rescue JSON::GeneratorError
73
+ logger.debug("Error in JSON::generate from input #{ potential_attack_string }")
74
+ end
75
+ end
76
+ super(context, potential_attack_string, **kwargs)
77
+ end
78
+
79
+ def build_sample context, input_analysis_result, candidate_string, **kwargs
80
+ input = input_analysis_result.value
81
+
82
+ sample = build_base_sample(context, input_analysis_result)
83
+ sample.no_sqli = Contrast::Api::Dtm::NoSqlInjectionDetails.new
84
+ sample.no_sqli.query = Contrast::Utils::StringUtils.protobuf_safe_string(candidate_string)
85
+ sample.no_sqli.start_idx = sample.no_sqli.query.index(input).to_i
86
+ sample.no_sqli.boundary_overrun_idx = sample.no_sqli.start_idx + input.length
87
+ sample.no_sqli.input_boundary_idx = kwargs[:boundary].to_i
88
+ sample.no_sqli.input_boundary_idx = kwargs[:last_boundary].to_i
89
+ sample
90
+ end
91
+
92
+ private
93
+
94
+ def select_scanner
95
+ @_select_scanner ||= Contrast::Agent::Protect::Rule::NoSqli::MongoNoSqlScanner.new
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,40 @@
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
+ module Contrast
5
+ module Agent
6
+ module Protect
7
+ module Rule
8
+ class NoSqli
9
+ # The Mongo specific NoSQL scanner, used by the NoSQLI rule to
10
+ # determine if a NoSQL attack was performed against a Mongo database.
11
+ #
12
+ # @deprecated RUBY-356
13
+ class MongoNoSqlScanner < Contrast::Agent::Protect::Rule::DefaultScanner
14
+ # Is the current & next character '//' or are the current and
15
+ # subsequent characters '<--' ?
16
+ def start_line_comment? char, index, query
17
+ if char == Contrast::Utils::ObjectShare::SLASH &&
18
+ query[index + 1] == Contrast::Utils::ObjectShare::SLASH
19
+ return true
20
+ end
21
+
22
+ char == Contrast::Utils::ObjectShare::LEFT_ANGLE &&
23
+ query[index + 1] == Contrast::Utils::ObjectShare::DASH &&
24
+ query[index + 2] == Contrast::Utils::ObjectShare::DASH
25
+ end
26
+
27
+ def start_block_comment? _char, _index, _query
28
+ false
29
+ end
30
+
31
+ # Indicates if '""' inside of double quotes is the equivalent of '\"'
32
+ def double_quote_escape_in_double_quote?
33
+ true
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,143 @@
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/stack_trace_utils'
5
+ cs__scoped_require 'contrast/components/interface'
6
+
7
+ module Contrast
8
+ module Agent
9
+ module Protect
10
+ module Rule
11
+ # This class handles our implementation of the Path Traversal
12
+ # Protect rule.
13
+ class PathTraversal < Contrast::Agent::Protect::Rule::BaseService
14
+ include Contrast::Components::Interface
15
+ access_component :logging, :agent
16
+
17
+ NAME = 'path-traversal'
18
+ SYSTEM_PATHS = %w[
19
+ /proc/self
20
+ etc/passwd
21
+ etc/shadow
22
+ etc/hosts
23
+ etc/groups
24
+ etc/gshadow
25
+ ntuser.dat
26
+ /Windows/win.ini
27
+ /windows/system32/
28
+ /windows/repair/
29
+ ].cs__freeze
30
+
31
+ KNOWN_SECURITY_BYPASS_MARKERS = ['::$DATA', '::$Index', '', '\x00'].cs__freeze
32
+
33
+ def name
34
+ NAME
35
+ end
36
+
37
+ def infilter context, method, path
38
+ return unless infilter?(context)
39
+
40
+ result = find_attacker(context, path)
41
+ return unless result
42
+
43
+ append_to_activity(context, result)
44
+ return unless blocked?
45
+
46
+ raise Contrast::SecurityException.new(
47
+ self,
48
+ "Path Traversal rule triggered. Call to File.#{ method } blocked.")
49
+ end
50
+
51
+ protected
52
+
53
+ def find_attacker context, path
54
+ attack_result = nil
55
+ attack_result = super(context, path) if infilter?(context)
56
+ attack_result = check_rep_features(context, path, attack_result)
57
+ attack_result
58
+ end
59
+
60
+ # Build a subclass of the RaspRuleSample using the query string and the
61
+ # evaluation
62
+ def build_sample context, input_analysis_result, path, **_kwargs
63
+ sample = build_base_sample(context, input_analysis_result)
64
+ sample.path_traversal = Contrast::Api::Dtm::PathTraversalDetails.new
65
+ path ||= input_analysis_result.value
66
+ sample.path_traversal.path = Contrast::Utils::StringUtils.protobuf_safe_string(path)
67
+ sample
68
+ end
69
+
70
+ private
71
+
72
+ # Build a subclass of the RaspRuleSample if the sample matches
73
+ # TODO: SPEED-? delete me when implemented in Speedracer.
74
+ # this implementation duplicates logic that is moving to Speedracer
75
+ # the reason it is still here is Speedracer doesn't yet have a method to correlate
76
+ # and (update) attack results that are generated because the evaluation results
77
+ # from REP, it can only forward the attack results are generated by the agent.
78
+ def build_rep_sample context, path
79
+ sample = build_base_sample(context, nil)
80
+ sample.path_traversal_semantic = Contrast::Api::Dtm::PathTraversalSemanticAnalysisDetails.new
81
+ path = Contrast::Utils::StringUtils.protobuf_safe_string(path)
82
+ sample.path_traversal_semantic.path = path
83
+
84
+ if custom_code_access_sysfile_enabled? && custom_code_accessing_system_file?(path)
85
+ sample.path_traversal_semantic.findings << :CUSTOM_CODE_ACCESSING_SYSTEM_FILES
86
+ return sample
87
+ end
88
+
89
+ if common_file_exploits_enabled? && contains_known_attack_signatures?(path)
90
+ sample.path_traversal_semantic.findings << :COMMON_FILE_EXPLOITS
91
+ return sample
92
+ end
93
+
94
+ nil
95
+ end
96
+
97
+ def check_rep_features context, path, attack_result
98
+ rep_sample = build_rep_sample(context, path)
99
+ if rep_sample
100
+ attack_result = build_attack_result(context) if attack_result.nil?
101
+ build_attack_with_match(context, nil, attack_result, path)
102
+ end
103
+ attack_result
104
+ end
105
+
106
+ def custom_code_access_sysfile_enabled?
107
+ AGENT.report_custom_code_sysfile_access?
108
+ end
109
+
110
+ def custom_code_accessing_system_file? input
111
+ system_file?(input) && Contrast::Utils::StackTraceUtils.custom_code_context?
112
+ end
113
+
114
+ def system_file? path
115
+ return false unless path
116
+
117
+ SYSTEM_PATHS.any? { |sys_path| sys_path.include?(path) }
118
+ end
119
+
120
+ def common_file_exploits_enabled?
121
+ false
122
+ end
123
+
124
+ def contains_known_attack_signatures? input
125
+ utf8 = Contrast::Utils::StringUtils.force_utf8(input)
126
+ _ = CGI.unescape(utf8)
127
+ # TODO: RUBY-318 implement REP for known attack signatures
128
+ # try:
129
+ # realpath = os.path.realpath(unescaped).lower().rstrip('/')
130
+ # except ValueError as e:
131
+ # return 'embedded null byte' == str(e)
132
+ # except TypeError as e:
133
+ # return 'NUL' in str(e) or 'null byte' in str(e) or (PY34 and 'embedded NUL character' == str(e))
134
+ # except Exception as e:
135
+ # return 'null byte' in str(e).lower()
136
+ # return return any([bypass_markers.lower().rstrip('/') in realpath for bypass_markers in PathTraversalREPMixin.KNOWN_SECURITY_BYPASS_MARKERS])
137
+ false
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end