mirror of
https://github.com/zulip/zulip.git
synced 2025-10-24 08:33:43 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e724c1ec6 | ||
|
|
e2d303c1bb | ||
|
|
d3091a6096 | ||
|
|
313bcfd02a | ||
|
|
09bfd485e9 | ||
|
|
576ae9cc9f | ||
|
|
300447ddd9 | ||
|
|
f8149b0d5a | ||
|
|
b579dad7d9 | ||
|
|
fdfabb800d | ||
|
|
2c4156678c | ||
|
|
0a87276a27 | ||
|
|
19aed43817 |
4
.github/workflows/cancel-previous-runs.yml
vendored
4
.github/workflows/cancel-previous-runs.yml
vendored
@@ -22,6 +22,7 @@ jobs:
|
||||
# so this is required.
|
||||
- name: Get workflow IDs.
|
||||
id: workflow_ids
|
||||
continue-on-error: true # Don't fail this job on failure
|
||||
env:
|
||||
# This is in <owner>/<repo> format e.g. zulip/zulip
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
@@ -35,7 +36,8 @@ jobs:
|
||||
ids=$(node -e "$script")
|
||||
echo "::set-output name=ids::$ids"
|
||||
|
||||
- uses: styfle/cancel-workflow-action@0.4.1
|
||||
- uses: styfle/cancel-workflow-action@0.9.0
|
||||
continue-on-error: true # Don't fail this job on failure
|
||||
with:
|
||||
workflow_id: ${{ steps.workflow_ids.outputs.ids }}
|
||||
access_token: ${{ github.token }}
|
||||
|
||||
1
.github/workflows/codeql-analysis.yml
vendored
1
.github/workflows/codeql-analysis.yml
vendored
@@ -4,6 +4,7 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
CodeQL:
|
||||
if: ${{!github.event.repository.private}}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
65
.github/workflows/production-suite.yml
vendored
65
.github/workflows/production-suite.yml
vendored
@@ -30,6 +30,8 @@ defaults:
|
||||
|
||||
jobs:
|
||||
production_build:
|
||||
# This job builds a release tarball from the current commit, which
|
||||
# will be used for all of the following install/upgrade tests.
|
||||
name: Bionic production build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -106,6 +108,9 @@ jobs:
|
||||
run: tools/ci/send-failure-message
|
||||
|
||||
production_install:
|
||||
# This job installs the server release tarball built above on a
|
||||
# range of platforms, and does some basic health checks on the
|
||||
# resulting installer Zulip server.
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -208,3 +213,63 @@ jobs:
|
||||
env:
|
||||
ZULIP_BOT_KEY: ${{ secrets.ZULIP_BOT_KEY }}
|
||||
run: /tmp/send-failure-message
|
||||
|
||||
production_upgrade:
|
||||
# The production upgrade job starts with a container with a
|
||||
# previous Zulip release installed, and attempts to upgrade it to
|
||||
# the release tarball built for the current commit being tested.
|
||||
#
|
||||
# This is intended to catch bugs that result in the upgrade
|
||||
# process failing.
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# Base images are built using `tools/ci/Dockerfile.prod.template`.
|
||||
# The comments at the top explain how to build and upload these images.
|
||||
- docker_image: zulip/ci:buster-3.4
|
||||
name: 3.4 Version Upgrade
|
||||
is_focal: true
|
||||
os: buster
|
||||
|
||||
name: ${{ matrix.name }}
|
||||
container:
|
||||
image: ${{ matrix.docker_image }}
|
||||
options: --init
|
||||
runs-on: ubuntu-latest
|
||||
needs: production_build
|
||||
|
||||
steps:
|
||||
- name: Download built production tarball
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: production-tarball
|
||||
path: /tmp
|
||||
|
||||
- name: Add required permissions and setup
|
||||
run: |
|
||||
# This is the GitHub Actions specific cache directory the
|
||||
# the current github user must be able to access for the
|
||||
# cache action to work. It is owned by root currently.
|
||||
sudo chmod -R 0777 /__w/_temp/
|
||||
|
||||
# Since actions/download-artifact@v2 loses all the permissions
|
||||
# of the tarball uploaded by the upload artifact fix those.
|
||||
chmod +x /tmp/production-upgrade
|
||||
chmod +x /tmp/production-verify
|
||||
chmod +x /tmp/send-failure-message
|
||||
|
||||
- name: Upgrade production
|
||||
run: sudo /tmp/production-upgrade
|
||||
|
||||
# TODO: We should be running production-verify here, but it
|
||||
# doesn't pass yet.
|
||||
#
|
||||
# - name: Verify install
|
||||
# run: sudo /tmp/production-verify
|
||||
|
||||
- name: Report status
|
||||
if: failure()
|
||||
env:
|
||||
ZULIP_BOT_KEY: ${{ secrets.ZULIP_BOT_KEY }}
|
||||
run: /tmp/send-failure-message
|
||||
|
||||
@@ -7,6 +7,12 @@ up-to-date list of raw changes.
|
||||
|
||||
## Zulip 4.x series
|
||||
|
||||
### 4.7 -- 2021-10-04
|
||||
|
||||
- CVE-2021-41115: Prevent organization administrators from affecting
|
||||
the server with a regular expression denial-of-service attack
|
||||
through linkifier patterns.
|
||||
|
||||
### 4.6 -- 2021-09-23
|
||||
|
||||
- Documented official support for Debian 11 Bullseye, now that it is
|
||||
|
||||
@@ -412,11 +412,64 @@ instructions for other supported platforms.
|
||||
|
||||
### Upgrading from Debian Buster to Debian Bullseye
|
||||
|
||||
We expect to have tested documentation for upgrading from Buster to
|
||||
Bullseye available soon. See [this chat.zulip.org
|
||||
thread][bullseye-discussion-thread]) for the current status of that work.
|
||||
1. Upgrade your server to the latest Zulip `4.x` release.
|
||||
|
||||
[bullseye-discussion-thread]: https://chat.zulip.org/#narrow/stream/3-backend/topic/Upgrade.20to.20bullseye
|
||||
2. As the Zulip user, stop the Zulip server and run the following
|
||||
to back up the system:
|
||||
|
||||
```bash
|
||||
supervisorctl stop all
|
||||
/home/zulip/deployments/current/manage.py backup --output=/home/zulip/release-upgrade.backup.tar.gz
|
||||
```
|
||||
|
||||
3. Follow [Debian's instructions to upgrade the OS][bullseye-upgrade].
|
||||
|
||||
[bullseye-upgrade]: https://www.debian.org/releases/bullseye/amd64/release-notes/ch-upgrading.html
|
||||
|
||||
When prompted for you how to upgrade configuration
|
||||
files for services that Zulip manages like Redis, PostgreSQL,
|
||||
Nginx, and memcached, the best choice is `N` to keep the
|
||||
currently installed version. But it's not important; the next
|
||||
step will re-install Zulip's configuration in any case.
|
||||
|
||||
4. As root, run the following steps to regenerate configurations
|
||||
for services used by Zulip:
|
||||
|
||||
```bash
|
||||
apt remove upstart -y
|
||||
/home/zulip/deployments/current/scripts/zulip-puppet-apply -f
|
||||
```
|
||||
|
||||
5. Reinstall the current version of Zulip, which among other things
|
||||
will recompile Zulip's Python module dependencies for your new
|
||||
version of Python:
|
||||
|
||||
```bash
|
||||
rm -rf /srv/zulip-venv-cache/*
|
||||
/home/zulip/deployments/current/scripts/lib/upgrade-zulip-stage-2 \
|
||||
/home/zulip/deployments/current/ --ignore-static-assets
|
||||
```
|
||||
|
||||
This will finish by restarting your Zulip server; you should now
|
||||
be able to navigate to its URL and confirm everything is working
|
||||
correctly.
|
||||
|
||||
6. Debian Bullseye has a different version of the low-level glibc
|
||||
library, which affects how PostgreSQL orders text data (known as
|
||||
"collations"); this corrupts database indexes that rely on
|
||||
collations. Regenerate the affected indexes by running:
|
||||
|
||||
```bash
|
||||
/home/zulip/deployments/current/scripts/setup/reindex-textual-data --force
|
||||
```
|
||||
|
||||
7. As root, finish by verifying the contents of the full-text indexes:
|
||||
|
||||
```bash
|
||||
/home/zulip/deployments/current/manage.py audit_fts_indexes
|
||||
```
|
||||
|
||||
8. As an additional step, you can also [upgrade the postgresql version](#upgrading-postgresql).
|
||||
|
||||
### Upgrading from Debian Stretch to Debian Buster
|
||||
|
||||
@@ -424,7 +477,13 @@ thread][bullseye-discussion-thread]) for the current status of that work.
|
||||
only upgrade to Zulip 3.0 and newer after completing this process,
|
||||
since newer releases don't support Ubuntu Debian Stretch.
|
||||
|
||||
2. Same as for Bionic to Focal.
|
||||
2. As the Zulip user, stop the Zulip server and run the following
|
||||
to back up the system:
|
||||
|
||||
```bash
|
||||
supervisorctl stop all
|
||||
/home/zulip/deployments/current/manage.py backup --output=/home/zulip/release-upgrade.backup.tar.gz
|
||||
```
|
||||
|
||||
3. Follow [Debian's instructions to upgrade the OS][debian-upgrade-os].
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ async function test_delete_linkifier(page: Page): Promise<void> {
|
||||
async function test_add_invalid_linkifier_pattern(page: Page): Promise<void> {
|
||||
await page.waitForSelector(".admin-linkifier-form", {visible: true});
|
||||
await common.fill_form(page, "form.admin-linkifier-form", {
|
||||
pattern: "a$",
|
||||
pattern: "(foo",
|
||||
url_format_string: "https://trac.example.com/ticket/%(id)s",
|
||||
});
|
||||
await page.click("form.admin-linkifier-form button.button");
|
||||
@@ -50,7 +50,7 @@ async function test_add_invalid_linkifier_pattern(page: Page): Promise<void> {
|
||||
await page.waitForSelector("div#admin-linkifier-pattern-status", {visible: true});
|
||||
assert.strictEqual(
|
||||
await common.get_text_from_selector(page, "div#admin-linkifier-pattern-status"),
|
||||
"Failed: Invalid linkifier pattern. Valid characters are [ a-zA-Z_#=/:+!-].",
|
||||
"Failed: Bad regular expression: missing ): (foo",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -83,8 +83,8 @@ async function test_edit_invalid_linkifier(page: Page): Promise<void> {
|
||||
await page.click(".linkifier_row .edit");
|
||||
await page.waitForFunction(() => document.activeElement === $("#linkifier-edit-form-modal")[0]);
|
||||
await common.fill_form(page, "form.linkifier-edit-form", {
|
||||
pattern: "####",
|
||||
url_format_string: "####",
|
||||
pattern: "#(?P<id>d????)",
|
||||
url_format_string: "????",
|
||||
});
|
||||
await page.click(".submit-linkifier-info-change");
|
||||
|
||||
@@ -96,7 +96,7 @@ async function test_edit_invalid_linkifier(page: Page): Promise<void> {
|
||||
);
|
||||
assert.strictEqual(
|
||||
edit_linkifier_pattern_status,
|
||||
"Failed: Invalid linkifier pattern. Valid characters are [ a-zA-Z_#=/:+!-].",
|
||||
"Failed: Bad regular expression: bad repetition operator: ????",
|
||||
);
|
||||
|
||||
const edit_linkifier_format_status_selector = "div#edit-linkifier-format-status";
|
||||
|
||||
@@ -184,3 +184,6 @@ pyuca
|
||||
|
||||
# Handle connection retries with exponential backoff
|
||||
backoff
|
||||
|
||||
# Non-backtracking regular expressions
|
||||
google-re2
|
||||
|
||||
@@ -408,6 +408,13 @@ gitlint==0.15.1 \
|
||||
--hash=sha256:4b22916dcbdca381244aee6cb8d8743756cfd98f27e7d1f02e78733f07c3c21c \
|
||||
--hash=sha256:7ebdb8e7d333e577e956225cbc3ad8e0e96d05e638e6d461c9b66b784f9d2ac4
|
||||
# via -r requirements/dev.in
|
||||
google-re2==0.2.20210901 \
|
||||
--hash=sha256:028bb86b1612cbd7947e7493a92f1dc33c8b95b31a57017c392811650b62bf5d \
|
||||
--hash=sha256:10020ad49a6217534c5b82543ca4ee56c3317981afb68a8c737c212f222564f5 \
|
||||
--hash=sha256:542b9b18f01d06375ffab6a0e2ed809e3de7ddedde3382b6d24a98aced57c7a5 \
|
||||
--hash=sha256:676fa9ee54e3fb70f290526fc0f4d78d1e5a4add701b5547494eaf7c68c72247 \
|
||||
--hash=sha256:a1175c7e5b5ad12462279184099b7b95f72b25fab4d141289224c7a3ffdc4a06
|
||||
# via -r requirements/common.in
|
||||
h2==2.6.2 \
|
||||
--hash=sha256:93cbd1013a2218539af05cdf9fc37b786655b93bbc94f5296b7dabd1c5cadf41 \
|
||||
--hash=sha256:af35878673c83a44afbc12b13ac91a489da2819b5dc1e11768f3c2406f740fe9
|
||||
|
||||
@@ -259,6 +259,13 @@ django[argon2]==3.2.2 \
|
||||
future==0.18.2 \
|
||||
--hash=sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d
|
||||
# via python-twitter
|
||||
google-re2==0.2.20210901 \
|
||||
--hash=sha256:028bb86b1612cbd7947e7493a92f1dc33c8b95b31a57017c392811650b62bf5d \
|
||||
--hash=sha256:10020ad49a6217534c5b82543ca4ee56c3317981afb68a8c737c212f222564f5 \
|
||||
--hash=sha256:542b9b18f01d06375ffab6a0e2ed809e3de7ddedde3382b6d24a98aced57c7a5 \
|
||||
--hash=sha256:676fa9ee54e3fb70f290526fc0f4d78d1e5a4add701b5547494eaf7c68c72247 \
|
||||
--hash=sha256:a1175c7e5b5ad12462279184099b7b95f72b25fab4d141289224c7a3ffdc4a06
|
||||
# via -r requirements/common.in
|
||||
h2==2.6.2 \
|
||||
--hash=sha256:93cbd1013a2218539af05cdf9fc37b786655b93bbc94f5296b7dabd1c5cadf41 \
|
||||
--hash=sha256:af35878673c83a44afbc12b13ac91a489da2819b5dc1e11768f3c2406f740fe9
|
||||
|
||||
@@ -37,6 +37,7 @@ cp -a \
|
||||
tools/ci/success-http-headers.template.debian.txt \
|
||||
tools/ci/production-install \
|
||||
tools/ci/production-verify \
|
||||
tools/ci/production-upgrade \
|
||||
tools/ci/production-upgrade-pg \
|
||||
tools/ci/production-extract-tarball \
|
||||
tools/ci/send-failure-message \
|
||||
|
||||
41
tools/ci/production-upgrade
Normal file
41
tools/ci/production-upgrade
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
# Given a Zulip production environment that had been installed with a
|
||||
# previous version of Zulip, upgrade it to the commit being tested.
|
||||
# This takes as input the tarball generated by production-build.
|
||||
set -e
|
||||
set -x
|
||||
|
||||
# Structurally, this script should just call upgrade-zulip. However,
|
||||
# because of a set of issues that result in the previously installed
|
||||
# GitHub Actions Docker containers not actually working on boot, we
|
||||
# need to do some preparatory steps. It is a goal to delete these
|
||||
# steps.
|
||||
|
||||
# Reinstall rabbitmq-server.
|
||||
#
|
||||
# * For rabbitmq-server, we likely need to do this to work around the
|
||||
# hostname changing on reboot causing RabbitMQ to not boot.
|
||||
sudo apt-get -y purge rabbitmq-server
|
||||
sudo apt-get -y install rabbitmq-server
|
||||
|
||||
# Start the postgresql service.
|
||||
sudo service postgresql start
|
||||
|
||||
# Starting the rabbitmq-server
|
||||
if ! sudo service rabbitmq-server start; then
|
||||
echo
|
||||
echo "Starting rabbitmq-server failed. Trying again:"
|
||||
sudo service rabbitmq-server start
|
||||
fi
|
||||
|
||||
# Start the supervisor
|
||||
sudo service supervisor start
|
||||
|
||||
# Zulip releases before 2.1.8/3.5/4.4 have a bug in their
|
||||
# `upgrade-zulip` scripts, resulting in them exiting with status 0
|
||||
# unconditionally. We work around that by running
|
||||
# scripts/lib/upgrade-zulip instead.
|
||||
UPGRADE_SCRIPT=/home/zulip/deployments/current/scripts/lib/upgrade-zulip
|
||||
|
||||
# Execute the upgrade.
|
||||
sudo "$UPGRADE_SCRIPT" /tmp/zulip-server-test.tar.gz
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
|
||||
ZULIP_VERSION = "4.6"
|
||||
ZULIP_VERSION = "4.7"
|
||||
|
||||
# Add information on number of commits and commit hash to version, if available
|
||||
zulip_git_version_file = os.path.join(
|
||||
@@ -14,7 +14,7 @@ ZULIP_VERSION = lines.pop(0).strip()
|
||||
ZULIP_MERGE_BASE = lines.pop(0).strip()
|
||||
|
||||
LATEST_MAJOR_VERSION = "4.0"
|
||||
LATEST_RELEASE_VERSION = "4.6"
|
||||
LATEST_RELEASE_VERSION = "4.7"
|
||||
LATEST_RELEASE_ANNOUNCEMENT = "https://blog.zulip.com/2021/05/13/zulip-4-0-released/"
|
||||
|
||||
# Versions of the desktop app below DESKTOP_MINIMUM_VERSION will be
|
||||
@@ -47,4 +47,4 @@ API_FEATURE_LEVEL = 65
|
||||
# historical commits sharing the same major version, in which case a
|
||||
# minor version bump suffices.
|
||||
|
||||
PROVISION_VERSION = "146.0"
|
||||
PROVISION_VERSION = "146.1"
|
||||
|
||||
@@ -37,6 +37,7 @@ import markdown.inlinepatterns
|
||||
import markdown.postprocessors
|
||||
import markdown.treeprocessors
|
||||
import markdown.util
|
||||
import re2
|
||||
import requests
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
@@ -1779,7 +1780,9 @@ class MarkdownListPreprocessor(markdown.preprocessors.Preprocessor):
|
||||
# Name for the outer capture group we use to separate whitespace and
|
||||
# other delimiters from the actual content. This value won't be an
|
||||
# option in user-entered capture groups.
|
||||
BEFORE_CAPTURE_GROUP = "linkifier_before_match"
|
||||
OUTER_CAPTURE_GROUP = "linkifier_actual_match"
|
||||
AFTER_CAPTURE_GROUP = "linkifier_after_match"
|
||||
|
||||
|
||||
def prepare_linkifier_pattern(source: str) -> str:
|
||||
@@ -1787,31 +1790,45 @@ def prepare_linkifier_pattern(source: str) -> str:
|
||||
whitespace, or opening delimiters, won't match if there are word
|
||||
characters directly after, and saves what was matched as
|
||||
OUTER_CAPTURE_GROUP."""
|
||||
return fr"""(?<![^\s'"\(,:<])(?P<{OUTER_CAPTURE_GROUP}>{source})(?!\w)"""
|
||||
return fr"""(?P<{BEFORE_CAPTURE_GROUP}>^|\s|['"\(,:<])(?P<{OUTER_CAPTURE_GROUP}>{source})(?P<{AFTER_CAPTURE_GROUP}>$|[^\pL\pN])"""
|
||||
|
||||
|
||||
# Given a regular expression pattern, linkifies groups that match it
|
||||
# using the provided format string to construct the URL.
|
||||
class LinkifierPattern(markdown.inlinepatterns.Pattern):
|
||||
class LinkifierPattern(markdown.inlinepatterns.InlineProcessor):
|
||||
"""Applied a given linkifier to the input"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
source_pattern: str,
|
||||
format_string: str,
|
||||
markdown_instance: Optional[markdown.Markdown] = None,
|
||||
md: markdown.Markdown,
|
||||
) -> None:
|
||||
self.pattern = prepare_linkifier_pattern(source_pattern)
|
||||
self.format_string = format_string
|
||||
markdown.inlinepatterns.Pattern.__init__(self, self.pattern, markdown_instance)
|
||||
# Do not write errors to stderr (this still raises exceptions)
|
||||
options = re2.Options()
|
||||
options.log_errors = False
|
||||
|
||||
def handleMatch(self, m: Match[str]) -> Union[Element, str]:
|
||||
self.md = md
|
||||
self.compiled_re = re2.compile(prepare_linkifier_pattern(source_pattern), options=options)
|
||||
self.format_string = format_string
|
||||
|
||||
def handleMatch( # type: ignore[override] # supertype incompatible with supersupertype
|
||||
self, m: Match[str], data: str
|
||||
) -> Union[Tuple[Element, int, int], Tuple[None, None, None]]:
|
||||
db_data = self.md.zulip_db_data
|
||||
return url_to_a(
|
||||
url = url_to_a(
|
||||
db_data,
|
||||
self.format_string % m.groupdict(),
|
||||
markdown.util.AtomicString(m.group(OUTER_CAPTURE_GROUP)),
|
||||
)
|
||||
if isinstance(url, str):
|
||||
return None, None, None
|
||||
|
||||
return (
|
||||
url,
|
||||
m.start(2),
|
||||
m.end(2),
|
||||
)
|
||||
|
||||
|
||||
class UserMentionPattern(markdown.inlinepatterns.InlineProcessor):
|
||||
@@ -2336,11 +2353,19 @@ def topic_links(linkifiers_key: int, topic_name: str) -> List[Dict[str, str]]:
|
||||
matches: List[Dict[str, Union[str, int]]] = []
|
||||
linkifiers = linkifiers_for_realm(linkifiers_key)
|
||||
|
||||
options = re2.Options()
|
||||
options.log_errors = False
|
||||
for linkifier in linkifiers:
|
||||
raw_pattern = linkifier["pattern"]
|
||||
url_format_string = linkifier["url_format"]
|
||||
pattern = prepare_linkifier_pattern(raw_pattern)
|
||||
for m in re.finditer(pattern, topic_name):
|
||||
try:
|
||||
pattern = re2.compile(prepare_linkifier_pattern(raw_pattern), options=options)
|
||||
except re2.error:
|
||||
# An invalid regex shouldn't be possible here, and logging
|
||||
# here on an invalid regex would spam the logs with every
|
||||
# message sent; simply move on.
|
||||
continue
|
||||
for m in pattern.finditer(topic_name):
|
||||
match_details = m.groupdict()
|
||||
match_text = match_details["linkifier_actual_match"]
|
||||
# We format the linkifier's url string using the matched text.
|
||||
|
||||
39
zerver/migrations/0359_re2_linkifiers.py
Normal file
39
zerver/migrations/0359_re2_linkifiers.py
Normal file
@@ -0,0 +1,39 @@
|
||||
import re2
|
||||
from django.db import migrations
|
||||
from django.db.backends.postgresql.schema import DatabaseSchemaEditor
|
||||
from django.db.migrations.state import StateApps
|
||||
|
||||
|
||||
def delete_re2_invalid(apps: StateApps, schema_editor: DatabaseSchemaEditor) -> None:
|
||||
options = re2.Options()
|
||||
options.log_errors = False
|
||||
|
||||
RealmFilter = apps.get_model("zerver", "RealmFilter")
|
||||
found_errors = False
|
||||
for linkifier in RealmFilter.objects.all():
|
||||
try:
|
||||
re2.compile(linkifier.pattern, options=options)
|
||||
except re2.error:
|
||||
if not found_errors:
|
||||
print()
|
||||
found_errors = True
|
||||
print(
|
||||
f"Deleting linkifier {linkifier.id} in realm {linkifier.realm.string_id} which is not compatible with new re2 engine:"
|
||||
)
|
||||
print(f" {linkifier.pattern} -> {linkifier.url_format_string}")
|
||||
linkifier.delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("zerver", "0325_alter_realmplayground_unique_together"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
delete_re2_invalid,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
elidable=True,
|
||||
)
|
||||
]
|
||||
@@ -19,6 +19,7 @@ from typing import (
|
||||
)
|
||||
|
||||
import django.contrib.auth
|
||||
import re2
|
||||
from bitfield import BitField
|
||||
from bitfield.types import BitHandler
|
||||
from django.conf import settings
|
||||
@@ -906,19 +907,19 @@ post_delete.connect(flush_realm_emoji, sender=RealmEmoji)
|
||||
|
||||
|
||||
def filter_pattern_validator(value: str) -> None:
|
||||
regex = re.compile(r"^(?:(?:[\w\-#_= /:]*|[+]|[!])(\(\?P<\w+>.+\)))+$")
|
||||
error_msg = _("Invalid linkifier pattern. Valid characters are {}.").format(
|
||||
"[ a-zA-Z_#=/:+!-]",
|
||||
)
|
||||
|
||||
if not regex.match(str(value)):
|
||||
raise ValidationError(error_msg)
|
||||
|
||||
try:
|
||||
re.compile(value)
|
||||
except re.error:
|
||||
# Regex is invalid
|
||||
raise ValidationError(error_msg)
|
||||
# Do not write errors to stderr (this still raises exceptions)
|
||||
options = re2.Options()
|
||||
options.log_errors = False
|
||||
|
||||
re2.compile(value, options=options)
|
||||
except re2.error as e:
|
||||
if len(e.args) >= 1:
|
||||
if isinstance(e.args[0], str): # nocoverage
|
||||
raise ValidationError(_("Bad regular expression: {}").format(e.args[0]))
|
||||
if isinstance(e.args[0], bytes):
|
||||
raise ValidationError(_("Bad regular expression: {}").format(e.args[0].decode()))
|
||||
raise ValidationError(_("Unknown regular expression error")) # nocoverage
|
||||
|
||||
|
||||
def filter_format_validator(value: str) -> None:
|
||||
|
||||
@@ -1401,26 +1401,25 @@ class MarkdownTest(ZulipTestCase):
|
||||
url_format_string = r"https://trac.example.com/ticket/%(id)s"
|
||||
linkifier_1 = RealmFilter(
|
||||
realm=realm,
|
||||
pattern=r"(?P<id>ABC\-[0-9]+)(?![A-Z0-9-])",
|
||||
pattern=r"(?P<id>ABC\-[0-9]+)",
|
||||
url_format_string=url_format_string,
|
||||
)
|
||||
linkifier_1.save()
|
||||
self.assertEqual(
|
||||
linkifier_1.__str__(),
|
||||
r"<RealmFilter(zulip): (?P<id>ABC\-[0-9]+)(?![A-Z0-9-])"
|
||||
" https://trac.example.com/ticket/%(id)s>",
|
||||
r"<RealmFilter(zulip): (?P<id>ABC\-[0-9]+) https://trac.example.com/ticket/%(id)s>",
|
||||
)
|
||||
|
||||
url_format_string = r"https://other-trac.example.com/ticket/%(id)s"
|
||||
linkifier_2 = RealmFilter(
|
||||
realm=realm,
|
||||
pattern=r"(?P<id>[A-Z][A-Z0-9]*\-[0-9]+)(?![A-Z0-9-])",
|
||||
pattern=r"(?P<id>[A-Z][A-Z0-9]*\-[0-9]+)",
|
||||
url_format_string=url_format_string,
|
||||
)
|
||||
linkifier_2.save()
|
||||
self.assertEqual(
|
||||
linkifier_2.__str__(),
|
||||
r"<RealmFilter(zulip): (?P<id>[A-Z][A-Z0-9]*\-[0-9]+)(?![A-Z0-9-])"
|
||||
r"<RealmFilter(zulip): (?P<id>[A-Z][A-Z0-9]*\-[0-9]+)"
|
||||
" https://other-trac.example.com/ticket/%(id)s>",
|
||||
)
|
||||
|
||||
|
||||
@@ -27,17 +27,13 @@ class RealmFilterTest(ZulipTestCase):
|
||||
result = self.client_post("/json/realm/filters", info=data)
|
||||
self.assert_json_error(result, "This field cannot be blank.")
|
||||
|
||||
data["pattern"] = "$a"
|
||||
data["pattern"] = "(foo"
|
||||
result = self.client_post("/json/realm/filters", info=data)
|
||||
self.assert_json_error(
|
||||
result, "Invalid linkifier pattern. Valid characters are [ a-zA-Z_#=/:+!-]."
|
||||
)
|
||||
self.assert_json_error(result, "Bad regular expression: missing ): (foo")
|
||||
|
||||
data["pattern"] = r"ZUL-(?P<id>\d++)"
|
||||
data["pattern"] = r"ZUL-(?P<id>\d????)"
|
||||
result = self.client_post("/json/realm/filters", info=data)
|
||||
self.assert_json_error(
|
||||
result, "Invalid linkifier pattern. Valid characters are [ a-zA-Z_#=/:+!-]."
|
||||
)
|
||||
self.assert_json_error(result, "Bad regular expression: bad repetition operator: ????")
|
||||
|
||||
data["pattern"] = r"ZUL-(?P<id>\d+)"
|
||||
data["url_format_string"] = "$fgfg"
|
||||
@@ -154,13 +150,11 @@ class RealmFilterTest(ZulipTestCase):
|
||||
)
|
||||
|
||||
data = {
|
||||
"pattern": r"ZUL-(?P<id>\d++)",
|
||||
"pattern": r"ZUL-(?P<id>\d????)",
|
||||
"url_format_string": "https://realm.com/my_realm_filter/%(id)s",
|
||||
}
|
||||
result = self.client_patch(f"/json/realm/filters/{linkifier_id}", info=data)
|
||||
self.assert_json_error(
|
||||
result, "Invalid linkifier pattern. Valid characters are [ a-zA-Z_#=/:+!-]."
|
||||
)
|
||||
self.assert_json_error(result, "Bad regular expression: bad repetition operator: ????")
|
||||
|
||||
data["pattern"] = r"ZUL-(?P<id>\d+)"
|
||||
data["url_format_string"] = "$fgfg"
|
||||
|
||||
Reference in New Issue
Block a user