Conversation
|
@sentry review |
| if ( | ||
| comparison.enriched_pull is None | ||
| or comparison.enriched_pull.provider_pull is None | ||
| ): | ||
| log.debug( |
There was a problem hiding this comment.
Potential bug: Bypassing `comparison.enriched_pull.provider_pull` validation with `force_notify=True` can lead to `AttributeError` when accessing `provider_pull` attributes (e.g., `["author"]`, `["head"]`) without null checks, causing notification process crashes.
- Description: When "force_notify=True", the validation for
comparison.enriched_pull.provider_pullbeingNoneis bypassed. This allows code execution to proceed even ifprovider_pullisNone. Later, methods likeget_upgrade_messageinapps/worker/services/notification/notifiers/checks/base.py(e.g., line 59:author_username = comparison.enriched_pull.provider_pull["author"].get("username")) and_create_upgrade_messageinapps/worker/services/notification/notifiers/comment/__init__.py(e.g., line 391) directly accessprovider_pull["author"]orprovider_pull["head"]without null checks. Ifcomparison.enriched_pull.provider_pullisNone, attempting to access["author"]or["head"]will raise anAttributeError: 'NoneType' object has no attribute '__getitem__', leading to an unhandled exception and a crash in the notification process. - Suggested fix: Reinstate the
comparison.enriched_pull.provider_pull is Nonevalidation whenforce_notify=Trueor add explicit null checks forcomparison.enriched_pull.provider_pullbefore accessing its attributes in methods likeget_upgrade_messageand_create_upgrade_message.
severity: 0.9, confidence: 0.95
Did we get this right? 👍 / 👎 to inform future reviews.
| for condition in self.notify_conditions: | ||
| condition_result = condition.check_condition( | ||
| notifier=self, comparison=comparison | ||
| ) | ||
| return default_result.merge(side_effect_result) | ||
| if condition_result == False: |
There was a problem hiding this comment.
Potential bug: Bypassing `comparison.pull` and `comparison.enriched_pull.provider_pull` validation in the comment notifier with `force_notify=True` can cause `AttributeError` when accessing their attributes, leading to crashes.
- Description: When "force_notify=True", the comment notifier bypasses validation checks like
ComparisonHasPull(ensuringcomparison.pull is not None) andPullRequestInProvider(ensuringcomparison.enriched_pull.provider_pull is not None). This allows execution to reachbuild_message()(e.g., line 427) wherecomparison.pull.is_first_coverage_pullis accessed, orcomparison.enriched_pull.provider_pull(line 428) is accessed directly. Similarly,pull.pullid(line 134) andcomparison.enriched_pull.provider_pull["author"]in_create_upgrade_message()are accessed without null checks. If the bypassed objects areNone, these accesses will raise anAttributeError(e.g.,'NoneType' object has no attribute 'is_first_coverage_pull'), leading to an unhandled exception and a crash in the notification process. - Suggested fix: Reinstate
ComparisonHasPullandPullRequestInProvidervalidations in the comment notifier whenforce_notify=True, or add explicit null checks forcomparison.pullandcomparison.enriched_pull.provider_pullbefore accessing their attributes.
severity: 0.9, confidence: 0.95
Did we get this right? 👍 / 👎 to inform future reviews.
| if not force_notify: | ||
| if comparison.pull is None or (): | ||
| log.debug( | ||
| "Falling back to commit_status: Not a pull request", | ||
| extra={ | ||
| "notifier": self.name, | ||
| "repoid": comparison.head.commit.repoid, | ||
| "notifier_title": self.title, | ||
| "commit": comparison.head.commit, | ||
| }, | ||
| ) | ||
| return NotificationResult( | ||
| notification_attempted=False, | ||
| notification_successful=None, | ||
| explanation="no_pull_request", | ||
| data_sent=None, | ||
| data_received=None, | ||
| ) | ||
| if ( | ||
| comparison.enriched_pull is None | ||
| or comparison.enriched_pull.provider_pull is None | ||
| ): | ||
| log.debug( | ||
| "Falling back to commit_status: Pull request not in provider", | ||
| extra={ | ||
| "notifier": self.name, | ||
| "repoid": comparison.head.commit.repoid, | ||
| "notifier_title": self.title, | ||
| "commit": comparison.head.commit, | ||
| }, | ||
| ) | ||
| return NotificationResult( | ||
| notification_attempted=False, | ||
| notification_successful=None, | ||
| explanation="pull_request_not_in_provider", | ||
| data_sent=None, | ||
| data_received=None, | ||
| ) | ||
| if comparison.pull and comparison.pull.state != "open": | ||
| log.debug( | ||
| "Falling back to commit_status: Pull request closed", | ||
| extra={ | ||
| "notifier": self.name, | ||
| "repoid": comparison.head.commit.repoid, | ||
| "notifier_title": self.title, | ||
| "commit": comparison.head.commit, | ||
| }, | ||
| ) | ||
| return NotificationResult( | ||
| notification_attempted=False, | ||
| notification_successful=None, | ||
| explanation="pull_request_closed", | ||
| data_sent=None, | ||
| data_received=None, | ||
| ) | ||
| # Check branches config for this status before sending the check | ||
| if not self.can_we_set_this_status(comparison): | ||
| return NotificationResult( | ||
| notification_attempted=False, | ||
| notification_successful=None, | ||
| explanation="not_fit_criteria", | ||
| data_sent=None, | ||
| ) | ||
| if not self.required_builds(comparison): | ||
| return NotificationResult( | ||
| notification_attempted=False, | ||
| notification_successful=None, | ||
| explanation="need_more_builds", | ||
| data_sent=None, | ||
| data_received=None, | ||
| ) | ||
| # Check for existing statuses for this commit. If so, retain | ||
| # statuses and don't do a check as well | ||
| statuses = comparison.get_existing_statuses() | ||
| status_title = self.get_status_external_name() | ||
| if statuses and statuses.get(status_title): | ||
| log.debug( | ||
| "Falling back to commit_status: Status already exists for this commit", | ||
| extra={ | ||
| "notifier": self.name, | ||
| "repoid": comparison.head.commit.repoid, | ||
| "notifier_title": self.title, | ||
| "status_title": status_title, | ||
| "commit": comparison.head.commit, | ||
| }, | ||
| ) | ||
| return NotificationResult( | ||
| notification_attempted=False, | ||
| notification_successful=None, | ||
| explanation="preexisting_commit_status", | ||
| data_sent=None, | ||
| data_received=None, | ||
| ) | ||
|
|
||
| payload = None | ||
| try: | ||
| with nullcontext(): |
There was a problem hiding this comment.
Deep nesting of conditional logic makes the code hard to read and maintain. Consider extracting the validation logic into separate methods to improve readability and testability.
| if not force_notify: | |
| if comparison.pull is None or (): | |
| log.debug( | |
| "Falling back to commit_status: Not a pull request", | |
| extra={ | |
| "notifier": self.name, | |
| "repoid": comparison.head.commit.repoid, | |
| "notifier_title": self.title, | |
| "commit": comparison.head.commit, | |
| }, | |
| ) | |
| return NotificationResult( | |
| notification_attempted=False, | |
| notification_successful=None, | |
| explanation="no_pull_request", | |
| data_sent=None, | |
| data_received=None, | |
| ) | |
| if ( | |
| comparison.enriched_pull is None | |
| or comparison.enriched_pull.provider_pull is None | |
| ): | |
| log.debug( | |
| "Falling back to commit_status: Pull request not in provider", | |
| extra={ | |
| "notifier": self.name, | |
| "repoid": comparison.head.commit.repoid, | |
| "notifier_title": self.title, | |
| "commit": comparison.head.commit, | |
| }, | |
| ) | |
| return NotificationResult( | |
| notification_attempted=False, | |
| notification_successful=None, | |
| explanation="pull_request_not_in_provider", | |
| data_sent=None, | |
| data_received=None, | |
| ) | |
| if comparison.pull and comparison.pull.state != "open": | |
| log.debug( | |
| "Falling back to commit_status: Pull request closed", | |
| extra={ | |
| "notifier": self.name, | |
| "repoid": comparison.head.commit.repoid, | |
| "notifier_title": self.title, | |
| "commit": comparison.head.commit, | |
| }, | |
| ) | |
| return NotificationResult( | |
| notification_attempted=False, | |
| notification_successful=None, | |
| explanation="pull_request_closed", | |
| data_sent=None, | |
| data_received=None, | |
| ) | |
| # Check branches config for this status before sending the check | |
| if not self.can_we_set_this_status(comparison): | |
| return NotificationResult( | |
| notification_attempted=False, | |
| notification_successful=None, | |
| explanation="not_fit_criteria", | |
| data_sent=None, | |
| ) | |
| if not self.required_builds(comparison): | |
| return NotificationResult( | |
| notification_attempted=False, | |
| notification_successful=None, | |
| explanation="need_more_builds", | |
| data_sent=None, | |
| data_received=None, | |
| ) | |
| # Check for existing statuses for this commit. If so, retain | |
| # statuses and don't do a check as well | |
| statuses = comparison.get_existing_statuses() | |
| status_title = self.get_status_external_name() | |
| if statuses and statuses.get(status_title): | |
| log.debug( | |
| "Falling back to commit_status: Status already exists for this commit", | |
| extra={ | |
| "notifier": self.name, | |
| "repoid": comparison.head.commit.repoid, | |
| "notifier_title": self.title, | |
| "status_title": status_title, | |
| "commit": comparison.head.commit, | |
| }, | |
| ) | |
| return NotificationResult( | |
| notification_attempted=False, | |
| notification_successful=None, | |
| explanation="preexisting_commit_status", | |
| data_sent=None, | |
| data_received=None, | |
| ) | |
| payload = None | |
| try: | |
| with nullcontext(): | |
| if not force_notify: | |
| validation_result = self._validate_notification_conditions(comparison) | |
| if validation_result: | |
| return validation_result | |
| # Then implement: | |
| def _validate_notification_conditions(self, comparison) -> NotificationResult | None: | |
| # Extract all validation logic here | |
| pass |
Did we get this right? 👍 / 👎 to inform future reviews.
| force_notify = getattr(comparison.context, "force_notify", False) | ||
|
|
||
| repository_service = self.repository_service | ||
| title = self.get_status_external_name() | ||
| head_commit_sha = comparison.head.commit.commitid | ||
| head_report = comparison.head.report | ||
| state = payload["state"] | ||
| message = payload["message"] | ||
| url = payload["url"] | ||
| if self.status_already_exists(comparison, title, state, message): | ||
|
|
There was a problem hiding this comment.
The logic for handling force_notify in send_notification duplicates the check from maybe_send_notification. This creates a potential inconsistency where the behavior could differ between the two methods.
| force_notify = getattr(comparison.context, "force_notify", False) | |
| repository_service = self.repository_service | |
| title = self.get_status_external_name() | |
| head_commit_sha = comparison.head.commit.commitid | |
| head_report = comparison.head.report | |
| state = payload["state"] | |
| message = payload["message"] | |
| url = payload["url"] | |
| if self.status_already_exists(comparison, title, state, message): | |
| # Remove the duplicate force_notify check here since maybe_send_notification already handles it appropriately |
Did we get this right? 👍 / 👎 to inform future reviews.
| } | ||
| else: | ||
| ci_results = None |
There was a problem hiding this comment.
When force_notify=True and CI results cannot be fetched, setting ci_results = None could lead to unexpected behavior downstream. Consider logging this decision more prominently or adding explicit handling for this case.
| } | |
| else: | |
| ci_results = None | |
| else: | |
| log.warning( | |
| "Force notify enabled: proceeding without CI results due to client error", | |
| extra={"repoid": repoid, "commit": commitid} | |
| ) | |
| ci_results = None |
Did we get this right? 👍 / 👎 to inform future reviews.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #349 +/- ##
==========================================
- Coverage 94.30% 94.29% -0.01%
==========================================
Files 1247 1247
Lines 45835 45874 +39
Branches 1455 1455
==========================================
+ Hits 43223 43258 +35
- Misses 2307 2311 +4
Partials 305 305
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
07ca5c6 to
e0c23ba
Compare
e0c23ba to
c5854b4
Compare
No description provided.