actionableItemsUtils.tsx 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. import type {EventErrorData} from 'sentry/components/events/errorItem';
  2. import findBestThread from 'sentry/components/events/interfaces/threads/threadSelector/findBestThread';
  3. import getThreadException from 'sentry/components/events/interfaces/threads/threadSelector/getThreadException';
  4. import ExternalLink from 'sentry/components/links/externalLink';
  5. import type {HttpProcessingErrors} from 'sentry/constants/eventErrors';
  6. import {
  7. CocoaProcessingErrors,
  8. GenericSchemaErrors,
  9. JavascriptProcessingErrors,
  10. NativeProcessingErrors,
  11. ProguardProcessingErrors,
  12. } from 'sentry/constants/eventErrors';
  13. import {t, tct} from 'sentry/locale';
  14. import type {DebugFile} from 'sentry/types/debugFiles';
  15. import type {Image} from 'sentry/types/debugImage';
  16. import type {Event, ExceptionValue, Thread} from 'sentry/types/event';
  17. import {EntryType} from 'sentry/types/event';
  18. import type {Project} from 'sentry/types/project';
  19. import {defined} from 'sentry/utils';
  20. import {trackAnalytics} from 'sentry/utils/analytics';
  21. import {useApiQuery} from 'sentry/utils/queryClient';
  22. import useOrganization from 'sentry/utils/useOrganization';
  23. import {semverCompare} from 'sentry/utils/versions/semverCompare';
  24. const MINIFIED_DATA_JAVA_EVENT_REGEX_MATCH =
  25. /^(([\w\$]\.[\w\$]{1,2})|([\w\$]{2}\.[\w\$]\.[\w\$]))(\.|$)/g;
  26. export type ActionableItemTypes =
  27. | JavascriptProcessingErrors
  28. | HttpProcessingErrors
  29. | GenericSchemaErrors
  30. | ProguardProcessingErrors
  31. | NativeProcessingErrors;
  32. export const ActionableItemWarning = [
  33. ProguardProcessingErrors.PROGUARD_MISSING_LINENO,
  34. NativeProcessingErrors.NATIVE_MISSING_OPTIONALLY_BUNDLED_DSYM,
  35. GenericSchemaErrors.FUTURE_TIMESTAMP,
  36. GenericSchemaErrors.CLOCK_DRIFT,
  37. GenericSchemaErrors.PAST_TIMESTAMP,
  38. GenericSchemaErrors.VALUE_TOO_LONG,
  39. GenericSchemaErrors.INVALID_DATA,
  40. GenericSchemaErrors.INVALID_ATTRIBUTE,
  41. GenericSchemaErrors.MISSING_ATTRIBUTE,
  42. ];
  43. interface BaseActionableItem {
  44. data: any;
  45. message: string;
  46. type: ActionableItemTypes;
  47. }
  48. interface ProguardMissingLineNoError extends BaseActionableItem {
  49. type: ProguardProcessingErrors.PROGUARD_MISSING_LINENO;
  50. }
  51. interface ProguardMissingMappingError extends BaseActionableItem {
  52. type: ProguardProcessingErrors.PROGUARD_MISSING_MAPPING;
  53. }
  54. interface NativeMissingOptionalBundledDSYMError extends BaseActionableItem {
  55. type: NativeProcessingErrors.NATIVE_MISSING_OPTIONALLY_BUNDLED_DSYM;
  56. }
  57. interface NativeMissingDSYMError extends BaseActionableItem {
  58. type: NativeProcessingErrors.NATIVE_MISSING_DSYM;
  59. }
  60. interface NativeBadDSYMError extends BaseActionableItem {
  61. type: NativeProcessingErrors.NATIVE_BAD_DSYM;
  62. }
  63. interface JSMissingSourcesContentError extends BaseActionableItem {
  64. type: JavascriptProcessingErrors.JS_MISSING_SOURCES_CONTENT;
  65. }
  66. interface FetchGenericError extends BaseActionableItem {
  67. type: HttpProcessingErrors.FETCH_GENERIC_ERROR;
  68. }
  69. interface RestrictedIpError extends BaseActionableItem {
  70. type: HttpProcessingErrors.RESTRICTED_IP;
  71. }
  72. interface SecurityViolationError extends BaseActionableItem {
  73. type: HttpProcessingErrors.SECURITY_VIOLATION;
  74. }
  75. interface FutureTimestampError extends BaseActionableItem {
  76. type: GenericSchemaErrors.FUTURE_TIMESTAMP;
  77. }
  78. interface ClockDriftError extends BaseActionableItem {
  79. type: GenericSchemaErrors.CLOCK_DRIFT;
  80. }
  81. interface PastTimestampError extends BaseActionableItem {
  82. type: GenericSchemaErrors.PAST_TIMESTAMP;
  83. }
  84. interface ValueTooLongError extends BaseActionableItem {
  85. type: GenericSchemaErrors.VALUE_TOO_LONG;
  86. }
  87. interface InvalidDataError extends BaseActionableItem {
  88. type: GenericSchemaErrors.INVALID_DATA;
  89. }
  90. interface InvalidEnvironmentError extends BaseActionableItem {
  91. type: GenericSchemaErrors.INVALID_ENVIRONMENT;
  92. }
  93. interface InvalidAttributeError extends BaseActionableItem {
  94. type: GenericSchemaErrors.INVALID_ATTRIBUTE;
  95. }
  96. export type ActionableItemErrors =
  97. | ProguardMissingLineNoError
  98. | ProguardMissingMappingError
  99. | NativeMissingOptionalBundledDSYMError
  100. | NativeMissingDSYMError
  101. | NativeBadDSYMError
  102. | JSMissingSourcesContentError
  103. | FetchGenericError
  104. | RestrictedIpError
  105. | SecurityViolationError
  106. | FutureTimestampError
  107. | ClockDriftError
  108. | PastTimestampError
  109. | ValueTooLongError
  110. | InvalidDataError
  111. | InvalidEnvironmentError
  112. | InvalidAttributeError;
  113. export function shouldErrorBeShown(error: EventErrorData, event: Event) {
  114. if (
  115. error.type === CocoaProcessingErrors.COCOA_INVALID_DATA &&
  116. event.sdk?.name === 'sentry.cocoa' &&
  117. error.data?.name === 'contexts.trace.sampled' &&
  118. semverCompare(event.sdk?.version || '', '8.7.4') === -1
  119. ) {
  120. // The Cocoa SDK sends wrong values for contexts.trace.sampled before 8.7.4
  121. return false;
  122. }
  123. // Hide unactionable source context errors that happen in flutter web: https://github.com/getsentry/sentry-dart/issues/1764
  124. if (
  125. event.sdk?.name === 'sentry.dart.flutter' &&
  126. error.type === JavascriptProcessingErrors.JS_MISSING_SOURCES_CONTENT
  127. ) {
  128. const source: string | undefined = error.data?.source;
  129. if (
  130. source &&
  131. (source.includes('org-dartlang-sdk:///dart-sdk/lib/_internal') ||
  132. source.includes('flutter/packages/flutter/lib'))
  133. ) {
  134. return false;
  135. }
  136. }
  137. return true;
  138. }
  139. function isDataMinified(str: string | null) {
  140. if (!str) {
  141. return false;
  142. }
  143. return MINIFIED_DATA_JAVA_EVENT_REGEX_MATCH.test(str);
  144. }
  145. const hasThreadOrExceptionMinifiedFrameData = (
  146. definedEvent: Event,
  147. bestThread?: Thread
  148. ) => {
  149. if (!bestThread) {
  150. const exceptionValues: Array<ExceptionValue> =
  151. definedEvent.entries?.find(e => e.type === EntryType.EXCEPTION)?.data?.values ?? [];
  152. return exceptionValues.some(exceptionValue =>
  153. exceptionValue.stacktrace?.frames?.some(frame => isDataMinified(frame.module))
  154. );
  155. }
  156. const threadExceptionValues = getThreadException(definedEvent, bestThread)?.values;
  157. return threadExceptionValues
  158. ? threadExceptionValues.some(threadExceptionValue =>
  159. threadExceptionValue.stacktrace?.frames?.some(frame =>
  160. isDataMinified(frame.module)
  161. )
  162. )
  163. : bestThread?.stacktrace?.frames?.some(frame => isDataMinified(frame.module));
  164. };
  165. export const useFetchProguardMappingFiles = ({
  166. event,
  167. isShare,
  168. project,
  169. }: {
  170. event: Event;
  171. isShare: boolean;
  172. project: Project;
  173. }): {proguardErrors: EventErrorData[]; proguardErrorsLoading: boolean} => {
  174. const organization = useOrganization();
  175. const hasEventErrorsProGuardMissingMapping = event.errors?.find(
  176. error => error.type === 'proguard_missing_mapping'
  177. );
  178. const debugImages = event.entries?.find(e => e.type === EntryType.DEBUGMETA)?.data
  179. .images as undefined | Array<Image>;
  180. // When debugImages contains a 'proguard' entry, it must always be only one entry
  181. const proGuardImage = debugImages?.find(debugImage => debugImage?.type === 'proguard');
  182. const proGuardImageUuid = proGuardImage?.uuid;
  183. const shouldFetch =
  184. defined(proGuardImageUuid) &&
  185. event.platform === 'java' &&
  186. !hasEventErrorsProGuardMissingMapping &&
  187. !isShare;
  188. const {
  189. data: proguardMappingFiles,
  190. isSuccess,
  191. isLoading,
  192. } = useApiQuery<DebugFile[]>(
  193. [
  194. `/projects/${organization.slug}/${project.slug}/files/dsyms/`,
  195. {
  196. query: {
  197. query: proGuardImageUuid,
  198. file_formats: 'proguard',
  199. },
  200. },
  201. ],
  202. {
  203. staleTime: Infinity,
  204. enabled: shouldFetch,
  205. retry: false,
  206. }
  207. );
  208. function getProguardErrorsFromMappingFiles(): EventErrorData[] {
  209. if (isShare) {
  210. return [];
  211. }
  212. if (shouldFetch) {
  213. if (!isSuccess || proguardMappingFiles.length > 0) {
  214. return [];
  215. }
  216. return [
  217. {
  218. type: 'proguard_missing_mapping',
  219. message: t('A proguard mapping file was missing.'),
  220. data: {mapping_uuid: proGuardImageUuid},
  221. },
  222. ];
  223. }
  224. const threads: Array<Thread> =
  225. event.entries?.find(e => e.type === EntryType.THREADS)?.data?.values ?? [];
  226. const bestThread = findBestThread(threads);
  227. const hasThreadOrExceptionMinifiedData = hasThreadOrExceptionMinifiedFrameData(
  228. event,
  229. bestThread
  230. );
  231. if (hasThreadOrExceptionMinifiedData) {
  232. return [
  233. {
  234. type: 'proguard_potentially_misconfigured_plugin',
  235. message: tct(
  236. 'Some frames appear to be minified. Did you configure the [plugin]?',
  237. {
  238. plugin: (
  239. <ExternalLink
  240. href="https://docs.sentry.io/platforms/android/proguard/#gradle"
  241. onClick={() => {
  242. trackAnalytics('issue_error_banner.proguard_misconfigured.clicked', {
  243. organization,
  244. group: event?.groupID,
  245. });
  246. }}
  247. >
  248. Sentry Gradle Plugin
  249. </ExternalLink>
  250. ),
  251. }
  252. ),
  253. },
  254. ];
  255. }
  256. return [];
  257. }
  258. return {
  259. proguardErrorsLoading: shouldFetch && isLoading,
  260. proguardErrors: getProguardErrorsFromMappingFiles(),
  261. };
  262. };