test_plugin.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. import zipfile
  2. from io import BytesIO
  3. from django.core.files.uploadedfile import SimpleUploadedFile
  4. from django.urls import reverse
  5. from sentry.testutils import RelayStoreHelper, TransactionTestCase
  6. from sentry.testutils.helpers.datetime import before_now, iso_format
  7. PROGUARD_UUID = "6dc7fdb0-d2fb-4c8e-9d6b-bb1aa98929b1"
  8. PROGUARD_SOURCE = b"""\
  9. org.slf4j.helpers.Util$ClassContextSecurityManager -> org.a.b.g$a:
  10. 65:65:void <init>() -> <init>
  11. 67:67:java.lang.Class[] getClassContext() -> a
  12. 69:69:java.lang.Class[] getExtraClassContext() -> a
  13. 65:65:void <init>(org.slf4j.helpers.Util$1) -> <init>
  14. """
  15. PROGUARD_INLINE_UUID = "d748e578-b3d1-5be5-b0e5-a42e8c9bf8e0"
  16. PROGUARD_INLINE_SOURCE = b"""\
  17. # compiler: R8
  18. # compiler_version: 2.0.74
  19. # min_api: 16
  20. # pg_map_id: 5b46fdc
  21. # common_typos_disable
  22. $r8$backportedMethods$utility$Objects$2$equals -> a:
  23. boolean equals(java.lang.Object,java.lang.Object) -> a
  24. $r8$twr$utility -> b:
  25. void $closeResource(java.lang.Throwable,java.lang.Object) -> a
  26. android.support.v4.app.RemoteActionCompatParcelizer -> android.support.v4.app.RemoteActionCompatParcelizer:
  27. 1:1:void <init>():11:11 -> <init>
  28. io.sentry.sample.-$$Lambda$r3Avcbztes2hicEObh02jjhQqd4 -> e.a.c.a:
  29. io.sentry.sample.MainActivity f$0 -> b
  30. io.sentry.sample.MainActivity -> io.sentry.sample.MainActivity:
  31. 1:1:void <init>():15:15 -> <init>
  32. 1:1:boolean onCreateOptionsMenu(android.view.Menu):60:60 -> onCreateOptionsMenu
  33. 1:1:boolean onOptionsItemSelected(android.view.MenuItem):69:69 -> onOptionsItemSelected
  34. 2:2:boolean onOptionsItemSelected(android.view.MenuItem):76:76 -> onOptionsItemSelected
  35. 1:1:void bar():54:54 -> t
  36. 1:1:void foo():44 -> t
  37. 1:1:void onClickHandler(android.view.View):40 -> t
  38. """
  39. PROGUARD_BUG_UUID = "071207ac-b491-4a74-957c-2c94fd9594f2"
  40. PROGUARD_BUG_SOURCE = b"x"
  41. class BasicResolvingIntegrationTest(RelayStoreHelper, TransactionTestCase):
  42. def test_basic_resolving(self):
  43. with self.feature("organizations:java-exception-value-deobfuscation"):
  44. url = reverse(
  45. "sentry-api-0-dsym-files",
  46. kwargs={
  47. "organization_slug": self.project.organization.slug,
  48. "project_slug": self.project.slug,
  49. },
  50. )
  51. self.login_as(user=self.user)
  52. out = BytesIO()
  53. f = zipfile.ZipFile(out, "w")
  54. f.writestr("proguard/%s.txt" % PROGUARD_UUID, PROGUARD_SOURCE)
  55. f.writestr("ignored-file.txt", b"This is just some stuff")
  56. f.close()
  57. response = self.client.post(
  58. url,
  59. {
  60. "file": SimpleUploadedFile(
  61. "symbols.zip", out.getvalue(), content_type="application/zip"
  62. )
  63. },
  64. format="multipart",
  65. )
  66. assert response.status_code == 201, response.content
  67. assert len(response.data) == 1
  68. event_data = {
  69. "user": {"ip_address": "31.172.207.97"},
  70. "extra": {},
  71. "project": self.project.id,
  72. "platform": "java",
  73. "debug_meta": {"images": [{"type": "proguard", "uuid": PROGUARD_UUID}]},
  74. "exception": {
  75. "values": [
  76. {
  77. "stacktrace": {
  78. "frames": [
  79. {
  80. "function": "a",
  81. "abs_path": None,
  82. "module": "org.a.b.g$a",
  83. "filename": None,
  84. "lineno": 67,
  85. },
  86. {
  87. "function": "a",
  88. "abs_path": None,
  89. "module": "org.a.b.g$a",
  90. "filename": None,
  91. "lineno": 69,
  92. },
  93. ]
  94. },
  95. "module": "org.a.b",
  96. "type": "g$a",
  97. "value": "Attempt to invoke virtual method 'org.a.b.g$a.a' on a null object reference",
  98. }
  99. ]
  100. },
  101. "timestamp": iso_format(before_now(seconds=1)),
  102. }
  103. event = self.post_and_retrieve_event(event_data)
  104. if not self.use_relay():
  105. # We measure the number of queries after an initial post,
  106. # because there are many queries polluting the array
  107. # before the actual "processing" happens (like, auth_user)
  108. with self.assertWriteQueries(
  109. {
  110. "nodestore_node": 2,
  111. "sentry_eventuser": 1,
  112. "sentry_groupedmessage": 1,
  113. "sentry_userreport": 1,
  114. }
  115. ):
  116. self.post_and_retrieve_event(event_data)
  117. exc = event.interfaces["exception"].values[0]
  118. bt = exc.stacktrace
  119. frames = bt.frames
  120. assert exc.type == "Util$ClassContextSecurityManager"
  121. assert (
  122. exc.value
  123. == "Attempt to invoke virtual method 'org.slf4j.helpers.Util$ClassContextSecurityManager.getExtraClassContext' on a null object reference"
  124. )
  125. assert exc.module == "org.slf4j.helpers"
  126. assert frames[0].function == "getClassContext"
  127. assert frames[0].module == "org.slf4j.helpers.Util$ClassContextSecurityManager"
  128. assert frames[1].function == "getExtraClassContext"
  129. assert frames[1].module == "org.slf4j.helpers.Util$ClassContextSecurityManager"
  130. assert event.culprit == (
  131. "org.slf4j.helpers.Util$ClassContextSecurityManager " "in getExtraClassContext"
  132. )
  133. def test_resolving_inline(self):
  134. url = reverse(
  135. "sentry-api-0-dsym-files",
  136. kwargs={
  137. "organization_slug": self.project.organization.slug,
  138. "project_slug": self.project.slug,
  139. },
  140. )
  141. self.login_as(user=self.user)
  142. out = BytesIO()
  143. f = zipfile.ZipFile(out, "w")
  144. f.writestr("proguard/%s.txt" % PROGUARD_INLINE_UUID, PROGUARD_INLINE_SOURCE)
  145. f.writestr("ignored-file.txt", b"This is just some stuff")
  146. f.close()
  147. response = self.client.post(
  148. url,
  149. {
  150. "file": SimpleUploadedFile(
  151. "symbols.zip", out.getvalue(), content_type="application/zip"
  152. )
  153. },
  154. format="multipart",
  155. )
  156. assert response.status_code == 201, response.content
  157. assert len(response.data) == 1
  158. event_data = {
  159. "user": {"ip_address": "31.172.207.97"},
  160. "extra": {},
  161. "project": self.project.id,
  162. "platform": "java",
  163. "debug_meta": {"images": [{"type": "proguard", "uuid": PROGUARD_INLINE_UUID}]},
  164. "exception": {
  165. "values": [
  166. {
  167. "stacktrace": {
  168. "frames": [
  169. {
  170. "function": "onClick",
  171. "abs_path": None,
  172. "module": "e.a.c.a",
  173. "filename": None,
  174. "lineno": 2,
  175. },
  176. {
  177. "function": "t",
  178. "abs_path": None,
  179. "module": "io.sentry.sample.MainActivity",
  180. "filename": "MainActivity.java",
  181. "lineno": 1,
  182. },
  183. ]
  184. },
  185. "module": "org.a.b",
  186. "type": "g$a",
  187. "value": "Oh no",
  188. }
  189. ]
  190. },
  191. "timestamp": iso_format(before_now(seconds=1)),
  192. }
  193. event = self.post_and_retrieve_event(event_data)
  194. if not self.use_relay():
  195. # We measure the number of queries after an initial post,
  196. # because there are many queries polluting the array
  197. # before the actual "processing" happens (like, auth_user)
  198. with self.assertWriteQueries(
  199. {
  200. "nodestore_node": 2,
  201. "sentry_eventuser": 1,
  202. "sentry_groupedmessage": 1,
  203. "sentry_userreport": 1,
  204. }
  205. ):
  206. self.post_and_retrieve_event(event_data)
  207. exc = event.interfaces["exception"].values[0]
  208. bt = exc.stacktrace
  209. frames = bt.frames
  210. assert len(frames) == 4
  211. assert frames[0].function == "onClick"
  212. assert frames[0].module == "io.sentry.sample.-$$Lambda$r3Avcbztes2hicEObh02jjhQqd4"
  213. assert frames[1].filename == "MainActivity.java"
  214. assert frames[1].module == "io.sentry.sample.MainActivity"
  215. assert frames[1].function == "onClickHandler"
  216. assert frames[1].lineno == 40
  217. assert frames[2].function == "foo"
  218. assert frames[2].lineno == 44
  219. assert frames[3].function == "bar"
  220. assert frames[3].lineno == 54
  221. assert frames[3].filename == "MainActivity.java"
  222. assert frames[3].module == "io.sentry.sample.MainActivity"
  223. def test_error_on_resolving(self):
  224. url = reverse(
  225. "sentry-api-0-dsym-files",
  226. kwargs={
  227. "organization_slug": self.project.organization.slug,
  228. "project_slug": self.project.slug,
  229. },
  230. )
  231. self.login_as(user=self.user)
  232. out = BytesIO()
  233. f = zipfile.ZipFile(out, "w")
  234. f.writestr("proguard/%s.txt" % PROGUARD_BUG_UUID, PROGUARD_BUG_SOURCE)
  235. f.close()
  236. response = self.client.post(
  237. url,
  238. {
  239. "file": SimpleUploadedFile(
  240. "symbols.zip", out.getvalue(), content_type="application/zip"
  241. )
  242. },
  243. format="multipart",
  244. )
  245. assert response.status_code == 201, response.content
  246. assert len(response.data) == 1
  247. event_data = {
  248. "user": {"ip_address": "31.172.207.97"},
  249. "extra": {},
  250. "project": self.project.id,
  251. "platform": "java",
  252. "debug_meta": {"images": [{"type": "proguard", "uuid": PROGUARD_BUG_UUID}]},
  253. "exception": {
  254. "values": [
  255. {
  256. "stacktrace": {
  257. "frames": [
  258. {
  259. "function": "a",
  260. "abs_path": None,
  261. "module": "org.a.b.g$a",
  262. "filename": None,
  263. "lineno": 67,
  264. },
  265. {
  266. "function": "a",
  267. "abs_path": None,
  268. "module": "org.a.b.g$a",
  269. "filename": None,
  270. "lineno": 69,
  271. },
  272. ]
  273. },
  274. "type": "RuntimeException",
  275. "value": "Oh no",
  276. }
  277. ]
  278. },
  279. "timestamp": iso_format(before_now(seconds=1)),
  280. }
  281. event = self.post_and_retrieve_event(event_data)
  282. assert len(event.data["errors"]) == 1
  283. assert event.data["errors"][0] == {
  284. "mapping_uuid": "071207ac-b491-4a74-957c-2c94fd9594f2",
  285. "type": "proguard_missing_lineno",
  286. }