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,81 @@
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 Policy
8
+ module Propagator
9
+ # This class is specifically for String#tr(_s) propagation
10
+ #
11
+ # Disclaimer: there may be a better way, but we're
12
+ # in a 'get it work' state. hopefully, we'll be in
13
+ # a 'get it right' state soon.
14
+ class Trim
15
+ class << self
16
+ def tr_tagger patcher, preshift, ret, _block
17
+ return ret unless ret && !ret.empty?
18
+
19
+ source = preshift.object
20
+ args = preshift.args
21
+ ret.cs__copy_from(source)
22
+ replace_string = args[1]
23
+ source_chars = source.chars
24
+ # if the replace string is empty, then there's a bunch of deletes. this
25
+ # functions the same as the Removal propagation.
26
+ if replace_string == Contrast::Utils::ObjectShare::EMPTY_STRING
27
+ Contrast::Agent::Assess::Policy::Propagator::Remove.handle_removal(source_chars, ret)
28
+ else
29
+ remove_ranges = []
30
+ ret_chars = ret.chars
31
+ curr_span = nil
32
+ source_chars.each_with_index do |char, idx|
33
+ if ret_chars[idx] == char
34
+ next unless curr_span
35
+
36
+ curr_span.stop = idx
37
+ remove_ranges << curr_span
38
+ curr_span = nil
39
+ else
40
+ curr_span ||= Contrast::Agent::Assess::AdjustedSpan.new(idx)
41
+ end
42
+ end
43
+ # account for the last char being different
44
+ if curr_span
45
+ curr_span.stop = source_chars.length
46
+ remove_ranges << curr_span
47
+ end
48
+ ret.cs__properties.delete_tags_at_ranges(remove_ranges, false)
49
+ end
50
+
51
+ ret.cs__properties.build_event(
52
+ patcher,
53
+ ret,
54
+ source,
55
+ ret,
56
+ args,
57
+ 1)
58
+ ret
59
+ end
60
+
61
+ def tr_s_tagger patcher, preshift, ret, _block
62
+ return unless ret && !ret.empty?
63
+
64
+ source = preshift.object
65
+ args = preshift.args
66
+ source.cs__splat_tags(ret)
67
+ ret.cs__properties.build_event(
68
+ patcher,
69
+ ret,
70
+ source,
71
+ ret,
72
+ args)
73
+ ret
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,79 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ cs__scoped_require 'contrast/utils/object_share'
5
+ cs__scoped_require 'contrast/agent/patching/policy/patch_status'
6
+ cs__scoped_require 'contrast/agent/module_data'
7
+ cs__scoped_require 'contrast/agent/rewriter'
8
+ cs__scoped_require 'contrast/components/interface'
9
+
10
+ module Contrast
11
+ module Agent
12
+ module Assess
13
+ module Policy
14
+ # This is our interface from the Patcher to the Rewriter
15
+ # functionality
16
+ #
17
+ # TODO: RUBY-534 remove w/ EOL of 2.5
18
+ module RewriterPatch
19
+ include Contrast::Components::Interface
20
+ access_component :agent, :analysis, :logging
21
+
22
+ class << self
23
+ def rewrite_interpolations
24
+ return unless ASSESS.enabled?
25
+ return unless AGENT.rewrite_interpolation?
26
+
27
+ logger.debug_with_time("\t\tRunning Assess interpolation rewrite") do
28
+ ObjectSpace.each_object(Module) do |mod|
29
+ rewrite_interpolation(mod)
30
+ end
31
+ end
32
+ end
33
+
34
+ # Rails is being a jerk, again. It passes in a class before it is
35
+ # done being defined. There is a state where the files have been
36
+ # loaded, but the class definition is not complete, so the
37
+ # methods of the class are not defined despite the class existing
38
+ #
39
+ # To get around this, we have those methods tell us the class
40
+ # isn't ready
41
+ def mid_defining? mod
42
+ mod.instance_variable_defined?(:@cs__defining_class) &&
43
+ mod.instance_variable_get(:@cs__defining_class)
44
+ end
45
+
46
+ def rewrite_interpolation mod, redo_rewrite = false
47
+ return unless ASSESS.enabled?
48
+ return unless AGENT.rewrite_interpolation?
49
+ return unless AGENT.interpolation_enabled?
50
+ return if mod.cs__frozen?
51
+ return if mod.singleton_class?
52
+ return if mid_defining?(mod)
53
+
54
+ status = Contrast::Agent::Patching::Policy::PatchStatus.get_status(mod)
55
+ return if (status&.rewritten? || status&.rewriting?) && !redo_rewrite
56
+
57
+ module_name = mod.cs__name
58
+ return unless module_name
59
+
60
+ if module_name.start_with?(Contrast::Utils::ObjectShare::CONTRAST_MODULE_START, Contrast::Utils::ObjectShare::ANONYMOUS_CLASS_MARKER)
61
+ status.no_rewrite!
62
+ return
63
+
64
+ end
65
+ module_data = Contrast::Agent::ModuleData.new(mod, module_name)
66
+ logger.debug_with_time("\t\t\tRewriting #{ module_name }") do
67
+ Contrast::Agent::Rewriter.rewrite_class(module_data, redo_rewrite)
68
+ end
69
+ rescue StandardError => e
70
+ logger.error(
71
+ e,
72
+ "Unable to patch assess into the module #{ mod }")
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,209 @@
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 class is responsible for the origination of traces. A Source is any method
5
+ # that returns an untrusted value. In general, these sources are request methods
6
+ # as the request object is user controlled.
7
+ #
8
+ # Going forward, we may add in Dynamic sources (determined at runtime) based on
9
+ # database configs or other variables (used for Stored XSS or other persisted
10
+ # vulnerability detection rules)
11
+ cs__scoped_require 'set'
12
+
13
+ cs__scoped_require 'contrast/utils/object_share'
14
+ cs__scoped_require 'contrast/utils/sha256_builder'
15
+ cs__scoped_require 'contrast/agent/assess/adjusted_span'
16
+
17
+ cs__scoped_require 'contrast/components/interface'
18
+
19
+ module Contrast
20
+ module Agent
21
+ module Assess
22
+ module Policy
23
+ # This class controls the actions we take on Sources, as determined by
24
+ # our Assess policy. It indicates what actions we should take in order
25
+ # to mark data as User Input and treat it as untrusted, starting the
26
+ # dataflows used in Assess vulnerability detection.
27
+ module SourceMethod
28
+ include Contrast::Components::Interface
29
+ access_component :logging, :analysis
30
+
31
+ def self.determine_target source_node, object, ret, args
32
+ source_target = source_node.targets[0]
33
+ case source_target
34
+ when Contrast::Utils::ObjectShare::RETURN_KEY
35
+ ret
36
+ when Contrast::Utils::ObjectShare::OBJECT_KEY
37
+ object
38
+ else
39
+ if source_target.is_a?(Integer)
40
+ args[source_target]
41
+ # If this isn't an index param, it's a named one. R.I.P.
42
+ else
43
+ arg = nil
44
+ args.each do |search|
45
+ next unless search.is_a?(Hash)
46
+
47
+ arg = search[source_target]
48
+ break if arg
49
+ end
50
+ arg
51
+ end
52
+ end
53
+ end
54
+
55
+ # This is called from within our woven proc. It will be called as if it
56
+ # were inline in the Rack application.
57
+ def self.source_patchers method_policy, object, ret, args
58
+ return if method_policy.source_node.nil?
59
+
60
+ current_context = Contrast::Agent::REQUEST_TRACKER.current
61
+ return unless current_context&.analyze_request? && ASSESS.enabled?
62
+
63
+ replaced_return = nil
64
+ source_node = method_policy.source_node
65
+
66
+ target = determine_target(source_node, object, ret, args)
67
+
68
+ # We don't propagate to frozen things that haven't been tracked
69
+ # before. By definition, something that is a source has not
70
+ # previously been tracked; therefore, we can break out early.
71
+ if target.cs__frozen?
72
+ # That being said, we don't have enough context to know if we
73
+ # can make this assumption and still function, so we'll allow for
74
+ # source tracking of frozen things by a common config setting.
75
+ #
76
+ # Rails' StrongParameters make a case for this to be default
77
+ # behavior
78
+ return replaced_return unless ASSESS.track_frozen_sources?
79
+
80
+ # If we're tracking the frozen target, we need to unfreeze
81
+ # (dup) it to track and then freeze that result. For
82
+ # simplicities sake, we ONLY do this if the return is the
83
+ # target (I don't want to have to deal with unfreezing self)
84
+ return replaced_return unless source_node.targets[0] == Contrast::Utils::ObjectShare::RETURN_KEY
85
+
86
+ restore_frozen_state = true
87
+ ret = Contrast::Utils::FreezeUtil.unfreeze_dup(ret)
88
+ target = ret
89
+ end
90
+
91
+ SourceMethod.cs__apply_source(current_context, source_node, target, object, ret, *args)
92
+
93
+ ret.cs__freeze if restore_frozen_state
94
+ ret
95
+ end
96
+
97
+ # This is our method that actually taints the object our source_node
98
+ # targets.
99
+ def self.cs__apply_source context, source_node, target, object, ret, *args
100
+ return unless context
101
+
102
+ source_node_source = source_node.sources[0]
103
+ source_name = case source_node_source
104
+ when nil
105
+ nil
106
+ when Contrast::Utils::ObjectShare::RETURN_KEY
107
+ ret
108
+ when Contrast::Utils::ObjectShare::OBJECT_KEY
109
+ self
110
+ else
111
+ args[source_node_source]
112
+ end
113
+ _cs__apply_source context, source_node, target, object, ret, source_node.type, source_name, 0, *args
114
+ end
115
+
116
+ # I lied above. We had to figure out what the target of the source was.
117
+ # Now that we know, we'll actually tag it.
118
+ def self._cs__apply_source context, source_node, target, object, ret, source_type, source_name = nil, invoked = 0, *args
119
+ return unless context && source_node && target
120
+
121
+ # We know we only work on certain things.
122
+ # Skip if this isn't one of them
123
+ if Contrast::Utils::DuckUtils.quacks_to?(target, :cs__properties)
124
+
125
+ # don't apply second source -- probably needs tuning later if we
126
+ # use more than 'UNTRUSTED' in our sources
127
+ return if target.cs__tracked? || target.cs__frozen?
128
+
129
+ # otherwise for each tag this source_node applies, create a tag range
130
+ # on the target object
131
+ # I realize this looping is counter-intuitive from the above
132
+ # message, that's why we're revisiting.
133
+ source_node.tags.each do |tag|
134
+ length = Contrast::Utils::StringUtils.ret_length(target)
135
+ target.cs__properties.add_tag(tag, Contrast::Agent::Assess::AdjustedSpan.new(0, length))
136
+ target.cs__properties.add_properties(source_node.properties)
137
+ logger.debug(nil, "Source #{ source_node.id } detected: #{ target.__id__ } tagged with #{ tag }")
138
+ end
139
+
140
+ # make a representation of this method that TeamServer can render
141
+ target.cs__properties.build_event(source_node, target, object, ret, args, invoked, source_type, source_name)
142
+
143
+ # While we don't taint hashes themselves, we may taint the things
144
+ # they hold. Let's pass their keys and values back to ourselves and
145
+ # try again
146
+ elsif Contrast::Utils::DuckUtils.quacks_like_tracked_hash?(target)
147
+ source_key_type = invoked.zero? ? key_type(source_type) : source_type
148
+ invoked += 1
149
+ to_replace = []
150
+ target.each_pair do |key, value|
151
+ # We only do this for Strings b/c of the way Hash lookup works.
152
+ # To replace another object would break hash lookup and,
153
+ # therefore, the application
154
+ if ASSESS.track_frozen_sources? &&
155
+ key.is_a?(String) &&
156
+ Contrast::Utils::DuckUtils.quacks_to?(target, :delete)
157
+ key = Contrast::Utils::FreezeUtil.unfreeze_dup(key)
158
+ to_replace << key
159
+ end
160
+ _cs__apply_source(context, source_node, key, object, ret, source_key_type, key, invoked, *args)
161
+ _cs__apply_source(context, source_node, value, object, ret, source_type, key, invoked, *args)
162
+ end
163
+
164
+ # Hash is designed to keep one instance of the string key in it.
165
+ # We need to remove the existing one and replace it with our new
166
+ # tracked one.
167
+ to_replace.each do |key|
168
+ key.cs__freeze
169
+ value = target[key]
170
+ target.delete(key)
171
+ target[key] = value
172
+ end
173
+ # While we don't taint arrays themselves, we may taint the things
174
+ # they hold. Let's pass their keys and values back to ourselves and
175
+ # try again
176
+ elsif Contrast::Utils::DuckUtils.quacks_like_tracked_enumerable?(target)
177
+ invoked += 1
178
+ target.each { |value| _cs__apply_source(context, source_node, value, object, ret, source_type, source_name, invoked, *args) }
179
+ end
180
+ rescue StandardError => e
181
+ logger.warn(e, "Unable to apply source for source_node #{ source_node.id }")
182
+ end
183
+
184
+ # Silly helper method so that TeamServer can properly mark up
185
+ # the source of this trace, if this source ends up in a trigger
186
+ PARAMETER_TYPE = 'PARAMETER'
187
+ PARAMETER_KEY_TYPE = 'PARAMETER_KEY'
188
+ HEADER_TYPE = 'HEADER'
189
+ HEADER_KEY_TYPE = 'HEADER_KEY'
190
+ COOKIE_TYPE = 'COOKIE'
191
+ COOKIE_KEY_TYPE = 'COOKIE_KEY'
192
+
193
+ def self.key_type source_type
194
+ case source_type
195
+ when PARAMETER_TYPE
196
+ PARAMETER_KEY_TYPE
197
+ when HEADER_TYPE
198
+ HEADER_KEY_TYPE
199
+ when COOKIE_TYPE
200
+ COOKIE_KEY_TYPE
201
+ else
202
+ source_type
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,62 @@
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 Policy
8
+ # This class functions to translate our policy.json into an actionable
9
+ # Ruby object, allowing for dynamic patching over hardcoded patching,
10
+ # specifically for those methods which result in the source of
11
+ # untrusted data (indicate points in the application where user
12
+ # controlled input is accessed).
13
+ class SourceNode < PolicyNode
14
+ attr_accessor :type
15
+
16
+ DB_SOURCE_TYPE = 'TAINTED_DATABASE'
17
+ def self.build_dynamic_source _id, dynamic_source
18
+ dynamic_source_hash = {
19
+ JSON_CLASS_NAME => dynamic_source.class_name,
20
+ JSON_METHOD_NAME => dynamic_source.method_name,
21
+ JSON_INSTANCE_METHOD => dynamic_source.instance_method,
22
+ JSON_TYPE => DB_SOURCE_TYPE,
23
+ JSON_METHOD_VISIBILITY => 'public',
24
+ JSON_TARGET => dynamic_source.target,
25
+ JSON_PROPERTIES => dynamic_source.properties
26
+ }
27
+ Contrast::Agent::Assess::Policy::SourceNode.new(dynamic_source_hash)
28
+ end
29
+
30
+ JSON_TYPE = 'type'
31
+ JSON_SOURCE_NAME = 'source_name'
32
+ SOURCE_TAG = 'UNTRUSTED'
33
+ def initialize source_hash = {}
34
+ super(source_hash)
35
+ @type = source_hash[JSON_TYPE]
36
+ @tags << SOURCE_TAG
37
+ end
38
+
39
+ SOURCE = 'Source'
40
+ def node_class
41
+ SOURCE
42
+ end
43
+
44
+ # This is confusing. Sources are Creation action but
45
+ # Propagation type. Oh and also Type refers to input type,
46
+ # like parameter, so we have to call this node_type. :-/
47
+ def node_type
48
+ :TYPE_PROPAGATION
49
+ end
50
+
51
+ # Standard validation + TS trace version two rules:
52
+ # Must have source and type
53
+ def validate
54
+ super
55
+ raise(ArgumentError, "Source #{ id } did not have a proper target. Unable to create.") unless targets&.any?
56
+ raise(ArgumentError, "Source #{ id } did not have a proper type. Unable to create.") unless type
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,209 @@
1
+ # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ cs__scoped_require 'contrast/utils/object_share'
5
+ cs__scoped_require 'contrast/utils/sha256_builder'
6
+ cs__scoped_require 'contrast/agent/assess/policy/trigger_validation/trigger_validation'
7
+
8
+ cs__scoped_require 'contrast/components/interface'
9
+
10
+ module Contrast
11
+ module Agent
12
+ module Assess
13
+ module Policy
14
+ # A trigger method is one which can perform a dangerous action, as
15
+ # described by the Contrast::Agent::Assess::Policy::TriggerNode class.
16
+ # Each such method will call to this module just after invocation in
17
+ # order to determine if the call was done safely. In those cases where
18
+ # it was not, a Finding report is issued to the Service
19
+ module TriggerMethod
20
+ include Contrast::Components::Interface
21
+ access_component :logging, :analysis
22
+
23
+ # The level of TeamServer compliance our traces meet
24
+ CURRENT_FINDING_VERSION = 2
25
+
26
+ def self.settings
27
+ Contrast::Agent::FeatureState.instance
28
+ end
29
+
30
+ def self.apply_trigger_rule trigger_node, object, ret, args
31
+ return if trigger_node.nil?
32
+
33
+ current_context = Contrast::Agent::REQUEST_TRACKER.current
34
+ return unless current_context&.analyze_request? && ASSESS.enabled?
35
+
36
+ if trigger_node.sources&.any?
37
+ trigger_node.sources.each do |marker|
38
+ source = determine_source(marker, object, ret, args)
39
+ TriggerMethod.cs__apply_trigger(current_context,
40
+ trigger_node,
41
+ source,
42
+ object,
43
+ ret,
44
+ 1,
45
+ *args)
46
+ end
47
+ else
48
+ TriggerMethod.cs__apply_trigger(current_context,
49
+ trigger_node,
50
+ nil,
51
+ object,
52
+ ret,
53
+ 1,
54
+ *args)
55
+ end
56
+ end
57
+
58
+ def self.cs__apply_trigger context, trigger_node, source, object, ret, invoked, *args
59
+ return unless context && trigger_node
60
+ return if trigger_node.rule_disabled?
61
+ return if trigger_node.dataflow? && source.nil?
62
+
63
+ invoked += 1
64
+ if trigger_node.regexp_rule?
65
+ apply_regexp_rule(context, trigger_node, source, object, ret, invoked, *args)
66
+ elsif trigger_node.custom_trigger?
67
+ trigger_node.apply_custom_trigger(context, trigger_node, source, object, ret, invoked, *args)
68
+ elsif trigger_node.dataflow?
69
+ apply_dataflow_rule(context, trigger_node, source, object, ret, invoked, *args)
70
+ else # trigger rule - just calling the method is dangerous
71
+ build_finding(context, trigger_node, source, object, ret, invoked, *args)
72
+ end
73
+ rescue StandardError => e
74
+ logger.warn(e, 'Unable to apply trigger.')
75
+ end
76
+
77
+ # Given the marker from the trigger_node (the pointer indicating the entity
78
+ # from which the taint originated), return the entity on which this
79
+ # trigger needs to operate.
80
+ #
81
+ # In an effort to speed up this lookup, we've changed the marker for
82
+ # parameters to be implicit - if it is not a return or an object, it
83
+ # must be a parameter, which we can reference either by index or by
84
+ # name.
85
+ def self.determine_source marker, object, ret, args
86
+ case marker
87
+ when Contrast::Utils::ObjectShare::RETURN_KEY
88
+ ret
89
+ when Contrast::Utils::ObjectShare::OBJECT_KEY
90
+ object
91
+ else # 'P'
92
+ if marker.is_a?(Integer)
93
+ args[marker]
94
+ else
95
+ arg = nil
96
+ args.each do |search|
97
+ next unless search.is_a?(Hash)
98
+
99
+ arg = search[marker]
100
+ break if arg
101
+ end
102
+ arg
103
+ end
104
+ end
105
+ end
106
+
107
+ def self.apply_regexp_rule context, trigger_node, source, object, ret, invoked, *args
108
+ return unless source.is_a?(String)
109
+ return if trigger_node.good_value && source.match?(trigger_node.good_value)
110
+ return if trigger_node.bad_value && source !~ trigger_node.bad_value
111
+
112
+ invoked += 1
113
+ build_finding(context, trigger_node, source, object, ret, invoked, *args)
114
+ end
115
+
116
+ def self.apply_dataflow_rule context, trigger_node, source, object, ret, invoked, *args
117
+ return unless source
118
+
119
+ invoked += 1
120
+ if Contrast::Utils::DuckUtils.quacks_to?(source, :cs__properties)
121
+ return unless source.cs__tracked?
122
+ return unless trigger_node.violated?(source)
123
+
124
+ build_finding(context, trigger_node, source, object, ret, invoked, *args)
125
+ elsif Contrast::Utils::DuckUtils.quacks_like_tracked_hash?(source)
126
+ invoked += 2 # the each & the block
127
+ source.each_pair do |key, value|
128
+ apply_dataflow_rule(context, trigger_node, key, object, ret, invoked, *args)
129
+ apply_dataflow_rule(context, trigger_node, value, object, ret, invoked, *args)
130
+ end
131
+ elsif Contrast::Utils::DuckUtils.quacks_like_tracked_enumerable?(source)
132
+ invoked += 2 # the each & the block
133
+ source.each do |value|
134
+ apply_dataflow_rule(context, trigger_node, value, object, ret, invoked, *args)
135
+ end
136
+ else
137
+ logger.warn(
138
+ nil,
139
+ "Target is a #{ source.cs__class.name } -- not sure how to inspect: #{ trigger_node.inspect }")
140
+ logger.debug(nil, source.to_s[0..99])
141
+ end
142
+ end
143
+
144
+ def self.build_finding context, trigger_node, source, object, ret, invoked, *args
145
+ return unless Contrast::Agent::Assess::Policy::TriggerValidation.valid?(trigger_node, object, ret, args)
146
+
147
+ request = context.request
148
+ env = request.env
149
+ return if defined?(ActionController::Live) &&
150
+ env &&
151
+ env['action_controller.instance'].cs__class.included_modules.include?(ActionController::Live)
152
+
153
+ finding = Contrast::Api::Dtm::Finding.new
154
+ finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(trigger_node.rule_id)
155
+ finding.session_id = Contrast::Agent::FeatureState.instance.current_session_id
156
+ finding.version = CURRENT_FINDING_VERSION
157
+
158
+ build_from_source(finding, source)
159
+ trigger_event = Contrast::Agent::Assess::ContrastEvent.new(trigger_node, source, object, ret, args, invoked + 1).to_dtm_event
160
+ finding.events << trigger_event
161
+ build_hash(finding, source)
162
+ build_tags(context)
163
+ finding.routes << context.route if context.route
164
+ context.activity.findings << finding
165
+ logger.debug(nil, "Trigger #{ trigger_node.id } detected: #{ source.__id__ } triggered #{ trigger_node.rule_id }")
166
+ rescue StandardError => e
167
+ logger.error(e, "Unable to build a finding for #{ trigger_node.id }")
168
+ end
169
+
170
+ def self.build_from_source finding, source
171
+ return unless source
172
+ return unless Contrast::Utils::DuckUtils.quacks_to?(
173
+ source,
174
+ :cs__properties)
175
+ return unless source.cs__properties
176
+
177
+ # events could technically be nil, but we would have failed
178
+ # the rule check before getting here. not worth the nil check
179
+ source.cs__properties.events.each do |event|
180
+ finding.events << event.to_dtm_event
181
+ end
182
+
183
+ # Google::Protobuf::Map doesn't support merge!, so we have to do this
184
+ # long form
185
+ source_props = source.cs__properties.properties
186
+ return unless source_props
187
+
188
+ source_props.each_pair do |key, value|
189
+ key = Contrast::Utils::StringUtils.force_utf8(key)
190
+ finding.properties[key] = Contrast::Utils::StringUtils.force_utf8(value)
191
+ end
192
+ end
193
+
194
+ def self.build_hash finding, source
195
+ hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source)
196
+ finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash_code)
197
+ finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
198
+ end
199
+
200
+ def self.build_tags context
201
+ return unless ASSESS.tags
202
+
203
+ context.activity.finding_tags = Contrast::Utils::StringUtils.force_utf8(ASSESS.tags)
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end