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,73 @@
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 Assess
7
+ module Rule
8
+ module Provider
9
+ # Determine if there are any passwords hardcoded into the sourcecode
10
+ # of the application. A constant is a password if:
11
+ # 1) the name contains a PASSWORD_FIELD_NAMES value
12
+ # 2) the name does not contain a NON_PASSWORD_PARTIAL_NAMES value
13
+ # 3) the value is a String
14
+ # 4) the value is not solely alphanumeric and '.' or '_' * note that
15
+ # mixing the characters counts as a violation of this rule
16
+ class HardcodedPassword
17
+ include Contrast::Agent::Assess::Rule::Provider::HardcodedValueRule
18
+
19
+ NAME = 'hardcoded-password'
20
+ def rule_id
21
+ NAME
22
+ end
23
+
24
+ # These are names, determined by the security team (Matt & Ar), that
25
+ # indicate a field is likely to be a password or secret token of some
26
+ # sort.
27
+ PASSWORD_FIELD_NAMES = %w[PASSWORD PASSKEY PASSPHRASE SECRET].cs__freeze
28
+
29
+ # These are markers whose presence indicates that a field is more
30
+ # likely to be a descriptor or requirement than an actual password.
31
+ # We should ignore fields that contain them.
32
+ NON_PASSWORD_PARTIAL_NAMES = %w[DATE FORGOT FORM ENCODE PATTERN PREFIX PROP SUFFIX URL BASE FILE URI].cs__freeze
33
+
34
+ # If the constant looks like a password and it doesn't look like a
35
+ # password descriptor, it passes for this rule
36
+ def name_passes? constant_string
37
+ PASSWORD_FIELD_NAMES.any? { |name| constant_string.index(name) } &&
38
+ NON_PASSWORD_PARTIAL_NAMES.none? { |name| constant_string.index(name) }
39
+ end
40
+
41
+ # If the value is a string, it passes for this rule
42
+ def value_type_passes? value
43
+ value.is_a?(String)
44
+ end
45
+
46
+ # If the value probably isn't a property name, it passes for this
47
+ # rule
48
+ def value_passes? value
49
+ !probably_property_name?(value)
50
+ end
51
+
52
+ # If a field name matches an expected password field, we'll check it's
53
+ # value to see if it looks like a placeholder. For our purposes,
54
+ # placeholders will be any non-empty String conforming to the patterns
55
+ # below. We do combine the patterns with [\._] as in Ruby these two
56
+ # characters are probably more likely to appear together in a
57
+ # default placeholder than in a password. Note this is opposite of
58
+ # the behavior in Java
59
+ PROPERTY_NAME_PATTERN = /^[a-z]+[\.\_][\.\_a-z]*[a-z]+$/.cs__freeze
60
+ def probably_property_name? value
61
+ value.match?(PROPERTY_NAME_PATTERN)
62
+ end
63
+
64
+ REDACTED_MARKER = ' = "**REDACTED**"'
65
+ def redacted_marker
66
+ REDACTED_MARKER
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,121 @@
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/components/interface'
5
+ cs__scoped_require 'contrast/core_extensions/module'
6
+
7
+ module Contrast
8
+ module Agent
9
+ module Assess
10
+ module Rule
11
+ module Provider
12
+ # Hardcoded rules detect if any secret value has been written directly into the
13
+ # sourcecode of the application. To use this base class, a provider must
14
+ # implement four methods
15
+ # 1) name_passes? : does the constant name match a given value set
16
+ # 2) value_type_passes? : does the type of the value of the constant match the
17
+ # type given
18
+ # 3) value_passes? : does the value of the constant match a given value set
19
+ # 4) redacted_marker : the value to plug in for the obfuscated value
20
+ module HardcodedValueRule
21
+ include Contrast::Components::Interface
22
+ access_component :contrast_service, :logging, :analysis
23
+
24
+ def disabled?
25
+ !ASSESS.enabled? || ASSESS.rule_disabled?(rule_id)
26
+ end
27
+
28
+ COMMON_CONSTANTS = %i[
29
+ CONTRAST_ASSESS_POLICY_STATUS
30
+ VERSION
31
+ ].cs__freeze
32
+ def analyze clazz
33
+ return if disabled?
34
+
35
+ # we only want the constants explicitly defined in this class, not
36
+ # those of its ancestor(s)
37
+ constants = clazz.cs__constants(false)
38
+
39
+ # if there are no constants, let's just leave
40
+ return unless constants&.any?
41
+
42
+ constants.each do |constant|
43
+ next if COMMON_CONSTANTS.include?(constant)
44
+
45
+ # if this class autoloads its constant, get the hell away from it
46
+ # I mean it! Don't even think about it.
47
+ #
48
+ # Autoload means this constant (usually [always?] a class or
49
+ # module) won't be required until something in the application
50
+ # tries to load it. We CANNOT be that thing. We'll just have to
51
+ # wait until it's loaded, at which point we'll be handed it
52
+ # again.
53
+ next if clazz.cs__autoload?(constant)
54
+
55
+ # constant comes to us as a symbol. that sucks. we need to do
56
+ # some string methods on it, so stringify it.
57
+ constant_string = constant.to_s
58
+
59
+ # if this is another class or a module, move on
60
+ next unless constant_name?(constant_string)
61
+
62
+ next unless name_passes?(constant_string)
63
+
64
+ value = clazz.cs__const_get(constant, false)
65
+
66
+ # if the constant isn't holding a string, skip it
67
+ next unless value_type_passes?(value)
68
+
69
+ # if it looks like a placeholder / pointer to a config, skip it
70
+ next unless value_passes?(value)
71
+
72
+ report_finding(clazz, constant_string)
73
+ end
74
+ end
75
+
76
+ # Constants can be variable or classes defined in the given
77
+ # class. We ONLY want the variables, which should be defined in
78
+ # the MACRO_CASE (upper case & underscore format)
79
+ CONSTANT_NAME_PATTERN = /^[A-Z_]+$/.cs__freeze
80
+ def constant_name? constant
81
+ constant.match?(CONSTANT_NAME_PATTERN)
82
+ end
83
+
84
+ # The name of the field
85
+ CONSTANT_NAME_KEY = 'name'
86
+ # The code line, recreated, with the password obfuscated
87
+ CODE_SOURCE_KEY = 'codeSource'
88
+ # The constant name
89
+ SOURCE_KEY = 'source'
90
+
91
+ def report_finding clazz, constant_string
92
+ class_name = clazz.cs__name
93
+
94
+ finding = Contrast::Api::Dtm::Finding.new
95
+ finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(rule_id)
96
+ finding.session_id = Contrast::Agent::FeatureState.instance.current_session_id
97
+
98
+ finding.version = Contrast::Agent::Assess::Policy::TriggerMethod::CURRENT_FINDING_VERSION
99
+
100
+ finding.properties[SOURCE_KEY] = Contrast::Utils::StringUtils.protobuf_safe_string(class_name)
101
+ finding.properties[CONSTANT_NAME_KEY] = Contrast::Utils::StringUtils.protobuf_safe_string(constant_string)
102
+ finding.properties[CODE_SOURCE_KEY] = Contrast::Utils::StringUtils.protobuf_safe_string(constant_string + redacted_marker)
103
+
104
+ hash = Contrast::Utils::HashDigest.generate_class_scanning_hash(finding)
105
+ finding.hash_code = Contrast::Utils::StringUtils.protobuf_safe_string(hash)
106
+ finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
107
+
108
+ activity = Contrast::Api::Dtm::Activity.new
109
+ activity.finding_tags = Contrast::Utils::StringUtils.protobuf_safe_string(ASSESS.tags)
110
+ activity.findings << finding
111
+
112
+ CONTRAST_SERVICE.queue_message activity
113
+ rescue StandardError => e
114
+ logger.error(e, 'Unable to build a finding for Hardcoded Rule')
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,68 @@
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 Assess
7
+ module Rule
8
+ # A regexp is only vulnerable to REDOS if it's going to run
9
+ # with pathologically bad performance.
10
+ # We report a vulnerability if the regexp is liable to run
11
+ # with quadratic time for some input.
12
+ # This vastly errs on the side of false positives.
13
+ class Redos < Contrast::Agent::Assess::Rule::Base
14
+ class << self
15
+ NAME = 'redos'
16
+ def name
17
+ NAME
18
+ end
19
+
20
+ def regexp_complexity_check context, trigger_node, source, object, ret, invoked, *args
21
+ # we can arrive here either from:
22
+ # regexp =~ string
23
+ # string =~ regexp
24
+ # regexp.match string
25
+ #
26
+ # so object/args[0] can be string/regexp or regexp/string.
27
+ regexp = object.is_a?(Regexp) ? object : args[0]
28
+ string = object.is_a?(String) ? object : args[0]
29
+
30
+ # (1) regexp must be exploitable
31
+ return unless regexp_vulnerable?(regexp)
32
+
33
+ # (2) regexp must evaluate against user input
34
+ if trigger_node.violated?(string) # rubocop:disable Style/GuardClause
35
+ Contrast::Agent::Assess::Policy::TriggerMethod.build_finding(context, trigger_node, source, object, ret, invoked + 1, args)
36
+ end
37
+ end
38
+
39
+ protected
40
+
41
+ VULNERABLE_PATTERN = /[\[(].*?[\[(].*?[\])][*+?].*?[\])][*+?]/.cs__freeze
42
+
43
+ # Does the regexp
44
+ # https://bitbucket.org/contrastsecurity/assess-specifications/src/master/rules/dataflow/redos.md
45
+ def regexp_vulnerable? regexp
46
+ # A pattern is considered vulnerable if it has 2 or more levels of nested multi-matching.
47
+ # A level is defined as any set of opening and closing control characters immediately followed by a multi match control character.
48
+ # A control character is defined as one of the OPENING_CHARS, CLOSING_CHARS,
49
+ # or MULTI_MATCH_CHARS that is not immediately preceded by an escaping \ character.
50
+ # OPENING_CHARS are ( and [ CLOSING_CHARS are ) and ] MULTI_MATCH_CHARS are +, *, and ?
51
+
52
+ # Nota bene about Regexp#to_s: it doesn't necessarily give you the original Regexp back
53
+ # (in the sense of `my_str == Regexp.new(my_str).to_s`), it gives you a Regexp that
54
+ # will have the same functional characteristics as the original.
55
+ # Regexp#inspect gives you a "more nicely formatted" version than #to_s.
56
+ # Regexp#source will give you the original source.
57
+ # TODO RUBY-683, would we ever get a hit on one but not the other?
58
+
59
+ # Use #match? because it doesn't fill out global variables
60
+ # in the way match or =~ do.
61
+ VULNERABLE_PATTERN.match? regexp.source
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,47 @@
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 Assess
7
+ module Rule
8
+ # Those rules which function by scanning the Response body in order to
9
+ # detect vulnerabilities. These rules should each have their own
10
+ # Contrast::Agent::Assess::RuleResponseWatcher.
11
+ #
12
+ # Note: Most have been moved to the Service, as they typically watch
13
+ # the Request or Response bodies, parsing out vulnerabilities
14
+ # therein. CSRF is an exception to this as the rule requires a change
15
+ # to the Response body to function.
16
+ class ResponseScanningRule < Contrast::Agent::Assess::Rule::Base
17
+ def watcher
18
+ # raise(
19
+ # NotImplementedError,
20
+ # 'A child rule should have overridden the watcher method')
21
+ end
22
+
23
+ def stream_safe?
24
+ false
25
+ end
26
+
27
+ def generate_hash finding
28
+ Contrast::Utils::HashDigest.generate_response_hash(finding)
29
+ end
30
+
31
+ def postfilter context
32
+ findings = watcher.postfilter(context) if watcher && context
33
+ return unless findings
34
+
35
+ if findings.is_a?(Array)
36
+ findings.each do |finding|
37
+ send_report(finding) if finding
38
+ end
39
+ else
40
+ send_report(findings)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,36 @@
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 Assess
7
+ module Rule
8
+ # A watcher focused on the Response body, parsing out vulnerabilities
9
+ # therein.
10
+ #
11
+ # Note: Most have been moved to the Service, as they typically watch
12
+ # the Request or Response bodies, parsing out vulnerabilities
13
+ # therein. CSRF is an exception to this as the rule requires a change
14
+ # to the Response body to function.
15
+ class ResponseWatcher < Contrast::Agent::Assess::Rule::Watcher
16
+ def postfilter context
17
+ return unless supports?(context)
18
+ return unless vulnerable?(context)
19
+
20
+ build_finding(context)
21
+ end
22
+
23
+ def vulnerable? _context
24
+ raise(
25
+ NotImplementedError,
26
+ 'A child rule should have overridden the vulnerable? method')
27
+ end
28
+
29
+ def build_finding _context
30
+ Contrast::Api::Dtm::Finding.new
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,36 @@
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 Assess
7
+ module Rule
8
+ # Watchers are how those Rules which do not act on dataflow function.
9
+ #
10
+ # Note: Most have been moved to the Service, as they typically watch
11
+ # the Request or Response bodies, parsing out vulnerabilities
12
+ # therein. CSRF is an exception to this as the rule requires a change
13
+ # to the Response body to function.
14
+ class Watcher
15
+ def supports? context
16
+ return false if context.request.static_request?
17
+ return false unless context.response
18
+ return false if undesired_response_code? context.response.response_code
19
+ return false if undesired_response_type? context.response.content_type
20
+
21
+ true
22
+ end
23
+
24
+ UNDESIRED_RESPONSE_CODES = [301, 302, 307, 404, 410, 500].cs__freeze
25
+ def undesired_response_code? code
26
+ UNDESIRED_RESPONSE_CODES.include?(code)
27
+ end
28
+
29
+ def undesired_response_type? _type
30
+ false
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,151 @@
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 Assess
7
+ # A Tag represents a range in a given piece of data. It is used by the
8
+ # Agent to determine if a vulnerable dataflow has occurred.
9
+ class Tag
10
+ attr_reader :length, # length of tagged text within string
11
+ :start_idx, # start of range
12
+ :end_idx # end of range (calculated from start + length)
13
+
14
+ # Initialize a new tag
15
+ # length : the length of the string described with this tag
16
+ # start_idx : (default: 0) the starting position in the string for this tag
17
+ def initialize length, start_idx = 0
18
+ update_range(start_idx, start_idx + length)
19
+ end
20
+
21
+ # Return true if the tag covers the given position in the string
22
+ def covers? idx
23
+ idx >= start_idx && idx < end_idx
24
+ end
25
+
26
+ # Return true if the tag is above the given position in the string
27
+ def above? idx
28
+ idx < start_idx
29
+ end
30
+
31
+ # Return the range that this tag covers
32
+ def range
33
+ start_idx...end_idx
34
+ end
35
+
36
+ # Return if a given tag overlaps this one
37
+ def overlaps? other
38
+ return true if @start_idx < other.start_idx && @end_idx >= other.start_idx # we start below other & end in it
39
+ return true if @start_idx >= other.start_idx && @end_idx <= other.end_idx # we start and end in other
40
+ return true if @start_idx <= other.end_idx && @end_idx > other.end_idx # we start in other & end above it
41
+ end
42
+
43
+ def shift idx
44
+ update_range(@start_idx + idx, @end_idx + idx)
45
+ end
46
+
47
+ def shift_end idx
48
+ update_range(@start_idx, @end_idx + idx)
49
+ end
50
+
51
+ def update_start start_idx
52
+ update_range(start_idx, @end_idx)
53
+ end
54
+
55
+ def update_end end_idx
56
+ update_range(@start_idx, end_idx)
57
+ end
58
+
59
+ def repurpose start_idx, end_idx
60
+ update_range(start_idx, end_idx)
61
+ end
62
+
63
+ # Given a tag, merge its ranges with this one
64
+ # such that the lowest start and highest end
65
+ # become the values of this tag
66
+ #
67
+ # Returns true if the other tag was merged into
68
+ # this tag
69
+ def merge other
70
+ return unless overlaps?(other)
71
+
72
+ start = other.start_idx < @start_idx ? other.start_idx : @start_idx
73
+ finish = other.end_idx > @end_idx ? other.end_idx : @end_idx
74
+ update_range(start, finish)
75
+ end
76
+
77
+ # Modification to tracked String can change the position and length of the tracked tag
78
+ # shift : negative value moves left
79
+ def copy_modified shift
80
+ start = start_idx + shift
81
+ # Tags cannot start below 0
82
+ new_start_idx = start >= 0 ? start : 0
83
+ # If a tag were to go negative, cut off the negative portion from length
84
+ new_length = start >= 0 ? length : (length + start)
85
+ Contrast::Agent::Assess::Tag.new(new_length, new_start_idx)
86
+ end
87
+
88
+ def str_val
89
+ @_str_val ||= "[#{ start_idx },#{ end_idx }]"
90
+ end
91
+ alias_method :to_s, :str_val
92
+
93
+ BELOW = 'BELOW'
94
+ LOW_SPAN = 'LOW_SPAN'
95
+ WITHIN = 'WITHIN'
96
+ WITHOUT = 'WITHOUT'
97
+ HIGH_SPAN = 'HIGH_SPAN'
98
+ ABOVE = 'ABOVE'
99
+
100
+ # The tag is ______ the range
101
+ # rrrrrrr == self.range, the range of the tag
102
+ def compare_range start, stop
103
+ # r starts and stops below
104
+ # rrrrrrrrrrrrr
105
+ # start stop
106
+ return BELOW if @start_idx < start && @end_idx <= start
107
+ # r starts below and finishes within
108
+ # rrrrrrrrrrrrr
109
+ # start stop
110
+ return LOW_SPAN if @start_idx < start && @end_idx > start && @end_idx <= stop
111
+ # r is between start and stop
112
+ # rrrrrrrrrrrrrrr
113
+ # start stop
114
+ return WITHIN if @start_idx >= start && @start_idx < stop && @end_idx <= stop
115
+ # r starts below and finishes above stop
116
+ # rrrrrrrrrrrrrrrrrrrrrrrr
117
+ # start stop
118
+ return WITHOUT if @start_idx < start && @end_idx > stop
119
+ # r starts within and finishes above stop
120
+ # rrrrrrrrrrrrr
121
+ # start stop
122
+ return HIGH_SPAN if @start_idx >= start && @start_idx < stop && @end_idx > stop
123
+
124
+ # starts and stops above
125
+ # rrrrrrrrrrrrr
126
+ # start stop
127
+ ABOVE
128
+ end
129
+
130
+ private
131
+
132
+ # Update range should be how start and end index of tags are changed,
133
+ # as it includes validation
134
+ #
135
+ # Note that we allow start_idx == end_idx b/c this is how we determine
136
+ # if a tag range is 'covered' in trigger detection
137
+ ERROR_NEGATIVE_START = 'Unable to set start idx negative'
138
+ ERROR_END_BEFORE_START = 'Unable to set start idx after end idx'
139
+ def update_range start_idx, end_idx
140
+ raise(ArgumentError, ERROR_NEGATIVE_START) if start_idx.negative?
141
+ raise(ArgumentError, ERROR_END_BEFORE_START) if end_idx < start_idx
142
+
143
+ @start_idx = start_idx
144
+ @end_idx = end_idx
145
+ @length = @end_idx - @start_idx
146
+ @_str_val = nil
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end