Skip to content

perf(notificator): drop unknown CVEs queue#2419

Merged
jdobes merged 1 commit into
RedHatInsights:masterfrom
jdobes:optimize_notifications_3
Jul 16, 2026
Merged

perf(notificator): drop unknown CVEs queue#2419
jdobes merged 1 commit into
RedHatInsights:masterfrom
jdobes:optimize_notifications_3

Conversation

@jdobes

@jdobes jdobes commented Jul 15, 2026

Copy link
Copy Markdown
Member

this is a trade-off to keep memory usage reasonable after the number of notifications heavily increased

system-level notifications for unknown CVEs now fire immediately only for the -all event type

account-level notifications will be sent when CVEs receives metadata

without CVE metadata we can't categorize is CVE is fresh, what's the CVSS score or severity

RHINENG-28638

Secure Coding Practices Checklist GitHub Link

Secure Coding Checklist

  • Input Validation
  • Output Encoding
  • Authentication and Password Management
  • Session Management
  • Access Control
  • Cryptographic Practices
  • Error Handling and Logging
  • Data Protection
  • Communication Security
  • System Configuration
  • Database Security
  • File Management
  • Memory Management
  • General Coding Practices

Summary by Sourcery

Simplify CVE notification handling by removing the separate unknown-CVE queue and adjusting logic for CVEs without public_date.

New Features:

  • Send immediate system-level notifications (system-cve-all) for CVEs that exist without a known public_date.

Enhancements:

  • Load all CVE metadata into the in-memory cache and treat CVEs without public_date as non-fresh for account-level notifications.
  • Normalize notification payloads by emitting a default epoch publish_date when CVE public_date is missing.
  • Improve logging when CVEs are (re)fetched to aid operability and debugging.

Documentation:

  • Update notificator README to describe single-queue behavior and how CVEs without public_date are handled, including the system-cve-all-only notifications.
  • Clarify CVE cache behavior in documentation to reflect caching of all CVEs and lazy fetching of missing entries.

Tests:

  • Adjust notificator and queue tests to remove dependence on the unknown CVE queue and verify the new behavior for CVEs without public_date, including system-cve-all batching and notification counts.

this is a trade-off to keep memory usage reasonable after the number of notifications heavily increased
system-level notifications for unknown CVEs now fire immediately only for the -all event type
account-level notifications will be sent when CVEs receives metadata
without CVE metadata we can't categorize is CVE is fresh, what's the CVSS score or severity

RHINENG-28638
@sourcery-ai

sourcery-ai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR removes the separate "unknown CVEs" queue and treats CVEs without public_date as immediately notifiable only for system-cve-all, while tightening CVE existence/freshness logic and updating tests and docs accordingly.

Sequence diagram for CVE notification flow after removing unknown CVEs queue

sequenceDiagram
    actor Evaluator
    participant Notificator
    participant NotificatorConditions
    participant QueueProcessor
    participant NotificationsPlatform

    Evaluator->>Notificator: _process_new_sys_vulns(new_sys_vulns, rh_account_id, org_id, inventory_id, display_name)
    loop for each sys_vuln_id
        Notificator->>NotificatorConditions: cve_exists(cve_id)
        alt cve_id not in cve_map or public_date is None
            NotificatorConditions->>NotificatorConditions: _fetch_cve(cve_id)
        end
        NotificatorConditions-->>Notificator: cve_exists result
        alt cve_exists is True
            Notificator->>NotificatorConditions: make_events_for_cve(cve_id)
            NotificatorConditions-->>Notificator: events (e.g. system-cve-all when public_date is None)
            alt events is not empty
                Notificator->>QueueProcessor: notif_cves_queue[sys_vuln_id] = QueueItem(..., events)
            end
        end
    end

    loop every period
        QueueProcessor->>QueueProcessor: _process_normal_queue()
        loop for each sys_vuln_id in notif_cves_queue
            QueueProcessor->>QueueProcessor: _create_notif_events(cve_id)
            QueueProcessor->>NotificationsPlatform: notifications_topic.send(events)
        end
    end
Loading

File-Level Changes

Change Details Files
Remove the dedicated unknown CVEs queue and process all CVEs through a single queue with simplified semantics.
  • Deleted unkno_cves_queue state from NotificatorQueue and related queue-processing logic.
  • Stopped populating unkno_cves_queue from notificator when CVE metadata is incomplete.
  • Ensured mitigated-vulns processing only cleans up the normal notification queue.
notificator/notificator_queue.py
notificator/notificator.py
tests/notificator_tests/test_notificator_queue.py
tests/notificator_tests/test_notificator.py
Adjust CVE metadata handling so CVEs without public_date are still cached and can trigger only system-cve-all notifications, with safe defaults for missing publish_date.
  • Updated _create_notif_events to emit a default epoch publish_date when metadata has no public_date.
  • Loaded all CVEs into the cve_map regardless of public_date and relaxed _fetch_cve query filters.
  • Changed cve_exists and is_fresh_cve to treat CVEs with null public_date as non-existent/non-fresh for eligibility logic while still allowing system-level notifications.
  • Logged CVE refetches to aid observability.
notificator/notificator_queue.py
notificator/notificator.py
Update tests and documentation to reflect the new behavior for unknown CVEs and the single-queue design.
  • Rewrote the unknown-CVE queue test to simulate a CVE with null public_date and assert only system-cve-all notification is sent in a single Kafka batch.
  • Removed assertions and setup related to unkno_cves_queue in existing queue tests.
  • Clarified README to describe a single queue and the behavior for CVEs without public_date, and updated the cache description.
  • Cleaned up test comments and expectations around queue emptiness and mitigated vulnerabilities.
tests/notificator_tests/test_notificator_queue.py
tests/notificator_tests/test_notificator.py
notificator/README.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@jdobes
jdobes requested a review from pindo696 July 15, 2026 15:52

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 4 issues, and left some high level feedback:

  • Using datetime.fromtimestamp(0, tz=timezone.utc) as a synthetic publish_date for CVEs without public_date may be misleading downstream; consider omitting the field or explicitly indicating an unknown/null value in the payload instead of an epoch placeholder.
  • The updated cve_exists will repeatedly refetch CVEs with public_date is None on every check; consider caching this "known but not yet published" state to avoid unnecessary DB round-trips until the public date is actually populated.
  • The new (Re)fetched CVE log is at info level and may become noisy in production for frequently re-checked CVEs; consider lowering this to debug or rate-limiting it.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Using `datetime.fromtimestamp(0, tz=timezone.utc)` as a synthetic `publish_date` for CVEs without `public_date` may be misleading downstream; consider omitting the field or explicitly indicating an unknown/null value in the payload instead of an epoch placeholder.
- The updated `cve_exists` will repeatedly refetch CVEs with `public_date is None` on every check; consider caching this "known but not yet published" state to avoid unnecessary DB round-trips until the public date is actually populated.
- The new `(Re)fetched CVE` log is at `info` level and may become noisy in production for frequently re-checked CVEs; consider lowering this to `debug` or rate-limiting it.

## Individual Comments

### Comment 1
<location path="notificator/notificator_queue.py" line_range="114-115" />
<code_context>
                     "known_exploit": cve["exploits"],
                     "has_rule": cve["is_rule"],
-                    "publish_date": cve["public_date"].isoformat(),
+                    "publish_date": (
+                        cve["public_date"].isoformat() if cve["public_date"] else datetime.fromtimestamp(0, tz=timezone.utc).isoformat()
+                    ),
                 },
</code_context>
<issue_to_address>
**suggestion:** Using Unix epoch as a fallback publish_date may be misleading downstream.

This fallback makes it impossible to distinguish a truly early CVE from one with missing `public_date`, which can affect sorting, freshness checks, and analytics. Instead, either omit `publish_date` when it's `None`, propagate `None`, or add a separate sentinel/flag so downstream code can explicitly handle unknown dates.

Suggested implementation:

```python
                    "impact_id": cve["impact_id"],
                    "known_exploit": cve["exploits"],
                    "has_rule": cve["is_rule"],
                    "publish_date": (
                        cve["public_date"].isoformat() if cve["public_date"] is not None else None
                    ),

```

Downstream code that consumes `publish_date` should be updated (if not already robust) to:
1. Treat `None`/`null` as "unknown publish date".
2. Adjust any sort/analytics logic that previously assumed `publish_date` is always a string ISO timestamp so it explicitly handles the `None` case instead of silently treating it as an early date.
</issue_to_address>

### Comment 2
<location path="notificator/notificator.py" line_range="93-95" />
<code_context>
                 cve["is_rule"] = cve_row[6]
                 cve["public_date"] = cve_row[7]
                 self.cve_map[cve_row[0]] = cve
+                LOGGER.info("(Re)fetched CVE: %s", cve["cve"])
                 return True
         return False
</code_context>
<issue_to_address>
**suggestion (performance):** Logging every (re)fetched CVE at info level may generate excessive log volume.

If `_fetch_cve` runs frequently or over many CVEs, this info-level log per item could flood logs and hurt usability/storage. Consider lowering to debug or aggregating/rate-limiting these messages while retaining necessary visibility.

```suggestion
                self.cve_map[cve_row[0]] = cve
                LOGGER.debug("(Re)fetched CVE: %s", cve["cve"])
                return True
```
</issue_to_address>

### Comment 3
<location path="tests/notificator_tests/test_notificator_queue.py" line_range="92-101" />
<code_context>
+        def _send_kafka_notif_mock(org_id, inventory_id, display_name, event_type, events, *_, **__):
</code_context>
<issue_to_address>
**suggestion (testing):** Strengthen the unknown-CVE test by asserting the number of events sent in the batched message

Since `msgs.append((inventory_id, event_type, len(events)))` already records the batch size, you can also assert `msgs[0][2] == 1` to verify that the batched event count is exactly one for unknown CVEs, not just that a single `SYSTEM_ALL_NOTIFICATION` message was sent.

Suggested implementation:

```python
        msgs = []

        def _send_kafka_notif_mock(org_id, inventory_id, display_name, event_type, events, *_, **__):
            msgs.append((inventory_id, event_type, len(events)))

```

```python
        assert len(msgs) == 1
        # Verify that the batched event count is exactly one for unknown CVEs
        assert msgs[0][2] == 1

```

I assumed the unknown-CVE test already asserts `len(msgs) == 1` after invoking the notificator with the `_send_kafka_notif_mock`. If the assertion currently has a different form (for example, checking the full tuple contents or indexing differently into `msgs`), you should:
1. Locate the test case that exercises unknown CVEs and uses `msgs` populated via `_send_kafka_notif_mock`.
2. Add `assert msgs[0][2] == 1` (or the equivalent index, if the tuple structure differs) right next to the existing assertions to ensure the batch size for the unknown CVE notification is exactly one.
3. If `msgs` is structured differently (e.g., more fields in the tuple), adjust the index `2` to point to the stored `len(events)` value.
</issue_to_address>

### Comment 4
<location path="notificator/README.md" line_range="31" />
<code_context>
 ```
-There are two queues, the unknown cves queue and known cves queue.
-Queue resolver process runs every 1 minute and checks both queues, for normal cve queue:
+Queue resolver process runs every 1 minute and checks the queue:
 * Checks if customer was already notified about this CVE.

</code_context>
<issue_to_address>
**nitpick (typo):** Consider rephrasing "every 1 minute" to the more natural "every minute".

"Every 1 minute" is understandable but a bit awkward; "every minute" is more natural and keeps the same meaning.

```suggestion
Queue resolver process runs every minute and checks the queue:
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread notificator/notificator_queue.py
Comment thread notificator/notificator.py
Comment thread tests/notificator_tests/test_notificator_queue.py
Comment thread notificator/README.md
@jdobes
jdobes enabled auto-merge (squash) July 15, 2026 16:01
@jdobes
jdobes merged commit 326c07d into RedHatInsights:master Jul 16, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants