-
-
Notifications
You must be signed in to change notification settings - Fork 150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
612 Introduced RECAP Search Alerts sweep index #4127
Conversation
- Test filter out queries and hits based on fields that matched.
for more information, see https://pre-commit.ci
- Added tests to assert nested child documents in case alerts.
{% endif %} | ||
{% if doc.plain_text %} | ||
{% contains_highlights doc.plain_text.0 True as highlighted %} | ||
<span style="display: block; margin-top: 5px;">{% if highlighted %}… {% endif %}{{ doc.plain_text|render_string_or_list|safe|underscore_to_space }} …</span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Detected a segment of a Flask template where autoescaping is explicitly disabled with '| safe' filter. This allows rendering of raw HTML in this segment. Ensure no user data is rendered here, otherwise this is a cross-site scripting (XSS) vulnerability.
Ignore this finding from template-unescaped-with-safe.<a href="https://www.courtlistener.com{% if doc.absolute_url %}{{ doc.absolute_url }}{% else %}{{ result.docket_absolute_url }}#minute-entry-{{ doc.docket_entry_id }}{% endif %}" class="visitable">{% if doc.short_description %}{{ doc.short_description|render_string_or_list|safe }}<span class="gray"> — </span>{% endif %}Document #{% if doc.document_number %}{{ doc.document_number }}{% endif %}{% if doc.attachment_number %}, Attachment #{{ doc.attachment_number }}{% endif %} | ||
</a> | ||
{% if doc.description %} | ||
<span style="display: block; margin-top: 5px;">Description: {{ doc.description|render_string_or_list|safe }}</span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Detected a segment of a Flask template where autoescaping is explicitly disabled with '| safe' filter. This allows rendering of raw HTML in this segment. Ensure no user data is rendered here, otherwise this is a cross-site scripting (XSS) vulnerability.
Ignore this finding from template-unescaped-with-safe.{% for doc in result.child_docs %} | ||
{% with doc=doc|get_attrdict:"_source" %} | ||
<li> | ||
<a href="https://www.courtlistener.com{% if doc.absolute_url %}{{ doc.absolute_url }}{% else %}{{ result.docket_absolute_url }}#minute-entry-{{ doc.docket_entry_id }}{% endif %}" class="visitable">{% if doc.short_description %}{{ doc.short_description|render_string_or_list|safe }}<span class="gray"> — </span>{% endif %}Document #{% if doc.document_number %}{{ doc.document_number }}{% endif %}{% if doc.attachment_number %}, Attachment #{{ doc.attachment_number }}{% endif %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Detected a segment of a Flask template where autoescaping is explicitly disabled with '| safe' filter. This allows rendering of raw HTML in this segment. Ensure no user data is rendered here, otherwise this is a cross-site scripting (XSS) vulnerability.
Ignore this finding from template-unescaped-with-safe.{{ forloop.counter }}. {{ result|get_highlight:"caseName"|safe }} | ||
({% if result.court_id != 'scotus' %}{{ result|get_highlight:"court_citation_string"|nbsp|safe }} {% endif %}{% if type == 'o' %}{{ result.dateFiled|date:"Y" }}{% elif type == 'oa' %}{{ result.dateArgued|date:"Y" }}{% endif %}) | ||
({% if result.court_id != 'scotus' %}{{ result|get_highlight:"court_citation_string"|nbsp|safe }} {% endif %}{% if type == 'o' %}{{ result.dateFiled|date:"Y" }}{% elif type == 'oa' %}{{ result.dateArgued|date:"Y" }}{% elif type == 'r' %}{{ result.dateFiled|date:"Y" }}{% endif %}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Detected a segment of a Flask template where autoescaping is explicitly disabled with '| safe' filter. This allows rendering of raw HTML in this segment. Ensure no user data is rendered here, otherwise this is a cross-site scripting (XSS) vulnerability.
Ignore this finding from template-unescaped-with-safe.
Semgrep found 1 Detected a segment of a Flask template where autoescaping is explicitly disabled with '| safe' filter. This allows rendering of raw HTML in this segment. Ensure no user data is rendered here, otherwise this is a cross-site scripting (XSS) vulnerability. Ignore this finding from template-unescaped-with-safe. |
…ing the Re Index API
- Enabled RECAP Search alerts UI behind a waffle. - Added alert frequency estimation for RECAP
Semgrep found 6
Class RECAPSweepDocument inherits from both |
…ets + RD hits - Fixed RECAP MLY and WLY scheduled alerts content.
…dex. - Ensure document timestamps get updated on partial updates.
fc5720e
to
d102664
Compare
…CAP_CHILD_HITS_PER_RESULT value
child_total_query = child_docs_count_query.extra( | ||
size=0, track_total_hits=True | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
QuerySet.extra' does not provide safeguards against SQL injection and requires very careful use. SQL injection can lead to critical data being stolen by attackers. Instead of using '.extra', use the Django ORM and parameterized queries such as People.objects.get(name='Bob')
.
main_doc_count_query = main_doc_count_query.extra( | ||
size=0, track_total_hits=True | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
QuerySet.extra' does not provide safeguards against SQL injection and requires very careful use. SQL injection can lead to critical data being stolen by attackers. Instead of using '.extra', use the Django ORM and parameterized queries such as People.objects.get(name='Bob')
.
…ies returning values change
…n HL to filter out RD hits
Just confirming that the RECAP Search Alerts UI will be controlled by |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@albertisfu Here are some suggestions for refactoring that I believe would improve the readability of this PR
cl/search/management/commands/cl_index_parent_and_child_docs.py
Outdated
Show resolved
Hide resolved
…rogress This commit introduces a new dataclass to store and manage information related to running Elasticsearch tasks. The dataclass includes properties for task completion status, created and total document counts.
dds a new dataclass to encapsulate common Elasticsearch queries used throughout the codebase. This centralizes query definitions, making it easier to maintain and update them. Updates the `build_es_base_query` method to return an instance of `EsMainQueries` instead of a tuple. This ensures consistent query structure and simplifies future modifications.
Thanks, @ERosendo, for the changes applied. They look good! @mlissner, this is ready for merging. I've already created the waffle The remaining task once this is merged is to set up a cron job for the A good time to schedule the cron job would be at midnight, but we also need to consider the scheduling of the |
IN IT GOES! THANK YOU BOTH! For the sweep cronjob, we can just not do that until you get back, right? It'll just mean that certain alerts don't send, which I think is fine for the moment? If that's the case, let's just make a new issue for that, and we can put that on your list for your return. |
Eh, actually, I take that back. I'll try to get this done next week! |
This PR introduces the Sweep index approach (discussed in #612) to send RECAP Search Alerts that might have been missed by the percolator approach during the day.
The
cl_send_recap_alerts
command will perform the following tasks:Remove the
RECAPSweepDocument
index from the previous day and recreate it to get a clean index (I'll create a follow-up issue to apply the logic to store the last 7-14 days indices for debugging purposes).Index all documents added/changed during the day from the main RECAP index to the
RECAPSweepDocument
. The indexing process uses the ES re-index API with a custom query for efficiency. The documents included in the re-index are:This ensures that every document that should be included in the day's alerts is indexed.
Considering that the re-index process can take considerable time depending on the number of documents, it is scheduled as an ES task. This task retrieves an ID to monitor its progress. Initially, the process will wait for one minute after the task is scheduled. Depending on the task's progress, the estimated waiting time will dynamically change before checking the task status again, repeating the process until the task is completed. If after 10 failed tries of getting the task status (possible due to a ES cluster overhead), the process is aborted and a error is logged so we can take an action manually.
Some variables are stored in Redis to make the command resumable in case of a failure or if the Pod dies:
alert_sweep:re_index_completed
: If the command fails after the re-index process is completed, this step can be skipped when the command is resumed.alert_sweep:query_date
: This stores the date from which we are sending alerts. The idea is that the command can be started during the day we are sending alerts, and in case it is close to 00:00 of the next day, it will still know that the alerts belong to the previous day in the event of a failure occurring after midnight.alert_sweep:task_id
: In case of failure during the ES re-index process, monitoring can continue from the previously scheduled task instead of starting a new one.Send RT and Daily alerts. After the re-index process is completed, Real-time and Daily alerts are sent. The process is the same for both rates:
Get alerts for each user.
Filter out RT alerts for non-members.
Search each query alert against the
RECAPSweepDocument
and retrieve the hits.Each query is limited to retrieving up to
SCHEDULED_ALERT_HITS_LIMIT
hits, defaulting to 20 (the current max number of hits in ES OA Search Alerts).Hits are processed to ensure only the correct hits are included and no hits/alerts are duplicated:
description
orplain_text
.case_name
anddocument_number
filter, or a text query matchingcase_name
andplain_text
simultaneously.The reason to differentiate alert types is to avoid sending alerts incorrectly based on matched content.
In practice, we only need to differentiate Docket-only alerts from RECAP-only or Cross-object alerts. If a hit in an alert doesn't include RD fields in the query or filters and the hit doesn't match RD highlights (Docket-only), we need to ensure the matched Docket was added/updated during the day. This ensures the alert should be triggered and no child hits are included. To confirm that, the Docket
date_modified
must belong to the same day, indicating the Docket was added or modified that day. We want to avoid cases where a Docket is indexed due to one of its RECAPDocuments being updated independently.For RECAP-only and Cross-object alerts, RECAPDocuments are matched as inner hits. The filtering process confirms that the query contains a child field as a filter or within the text query using advanced syntax, or if a child field is highlighted. If true, the child hit is included in the alert.
An additional filter checks if the Docket hit or the RECAPDocument hit has already triggered the same alert. We keep two sets per alert:
alert_hits:id.d
stores Dockets that have triggered an alert.alert_hits:id.r
stores RECAPDocuments that have triggered the alert.For Docket-only alerts, we check if the Docket hit ID is already within
alert_hits:id.d
. If so, the hit is excluded from the alert. For RECAP-only or Cross-object alerts, we check if the RD hits are withinalert_hits:id.r
. Only RDs not in the set are included in the alert. If all RD hits have previously triggered the alert, the hit is omitted from the alert.Finally, after filtering hits and child hits, alert emails are sent, along with their related webhooks.
WLY and MLY rates:
ScheduledAlertHit
to be sent according to their rate by thecl_send_scheduled_alerts
command.The Alerts UI is enabled for RECAP Search behind a waffle flag:
Here are some examples of alert emails:
Docket-only alert: Only the Docket is included in the alert with no child hits.
RECAPDocument-only alert: The docket fields are shown with the RECAPDocument nested below the docket fields.
Cross-object alert: Here we can see how multiple cases are includes in the alert. In this case, the cross-object alert matched a hit by its
case_name
and also matched a RD belonging to the case that included the keywords within the document description. The second hit only matched the case by itscase_name
with no RDs matched.Also you can notice the
View Additional Results
for this case is shown in the first case.This is because the original search matched more
RECAPDocuments
due to thecase_name
being indexed into each RD, which is the behavior in the frontend.In the alert, we filter out the RDs that actually matched the alert.
One question here: should we keep the
View Additional Results
button as in the frontend, or only show it if there are still 5 RDs matched after filtering? It's important to note that when clicking that button in the frontend, more results can be shown since it includes RDs filtered from the alert.Notes and additional questions: