The python:3.14.7-slim base image ships libpcre2-8-0
10.46-1~deb13u2, which is affected by CVE-2026-103111 (HIGH):
an out-of-bounds write triggered by a crafted regular expression.
Explicitly install libpcre2-8-0 in the base stage so apt pulls
the patched 10.46-1~deb13u3 from trixie-security. All stages
(builder, development, production) inherit the fix.
This line can be dropped once an upstream python slim image
ships the patched package.
Address the following HIGH severity CVEs reported by Trivy on the
backend image:
* Django 5.2.16 → 5.2.17
- CVE-2026-15307 — remote code execution via GeoDjango spatial
lookups.
* urllib3 2.7.0 → 2.8.0
- CVE-2026-97687 — traffic interception via HTTPS proxy TLS
configuration override.
- CVE-2026-97689 — denial of service via unbounded memory
allocation in the chunk parser.
The trivy scan fails on HIGH vulnerabilities found in the Debian 13
base images (util-linux, acl, ncurses, systemd and perl-base). None
of them has a fixed version available yet, so there is nothing we
can upgrade to clear them.
List these CVEs in a shared .github/.trivyignore and pass it to
every image scan, so the scan stays blocking for any new HIGH or
CRITICAL vulnerability. Remove the entries once Debian ships a fix.
Instead of referencing the shared CI repo on `main`, pin it to the
initial tagged version `v0.0.1`, so the CI behavior is stable and
does not silently change when the shared repo is updated.
Get the shellcheck CI job to pass again by addressing the issues it
flagged across our shell scripts.
Note: I am not 100% sure of every fix applied here. Reviewers should
feel free to challenge specific changes and suggest better ones
where relevant.
Get the spellcheck CI job to pass again by:
* Fixing the actual spelling issues it caught in the project.
* Excluding generated files from the scan, since they are not
written by us.
* Excluding translation files, whose content is not necessarily in
English and would trigger false positives.
Wire Menshen into the CI to scan the GitHub Actions we use and flag
vulnerable ones, following the same approach as other projects that
recently adopted it.
Note: I am not fully sure about the current setup. Reviewers should
feel free to adjust the configuration or the integration point as
they see fit.
The Crowdin workflows were not used by the project and had turned
into dead CI code.
Remove them to reduce noise and keep the CI configuration limited
to what is actually running.
First iteration of a migration toward a centralized repository
containing our shared CI logic.
Goals:
* Manage GitHub Actions version upgrades in one place.
* Make it easier to audit what actually runs in CI from a security
perspective.
* Avoid duplicating CI logic across projects and having each
repository slowly diverge over time.
* Centralize as many of our custom GitHub Actions as possible,
including some that still live in the old `numerique-gouv`
organization.
* Centralize the Renovate configuration alongside the workflows.
Inspired by the Accounts project, which recently simplified and
reorganized its CI setup.
This PR starts moving meet's CI to the shared repository so we can
validate the approach on a real project. For now, reusable
workflows are pinned to `main`; once we agree on the structure and
content of the central repository, they should be pinned to a
specific commit SHA instead.
Address the following CVEs in util-linux, reported by Cyberwatch on
the agents image. The python:3.14.6-slim tag is no longer rebuilt
and still ships util-linux 2.41-5. Bump the base image to
python:3.14.7-slim, which ships the patched 2.41.5-0+deb13u1
(DSA-6442-1), to cover them all:
* CVE-2026-53612 (7.0) — TOCTOU in mount post-mount ownership/mode
changes.
* CVE-2026-53613 (7.0) — TOCTOU in mount via ancestor directory swap.
* CVE-2026-53614 (7.0) — SUID mount(8) nosuid/noexec bypass via
`LIBMOUNT_FORCE_MOUNT2`.
* CVE-2026-13595 (5.3) — flaw in the libblkid library.
* CVE-2026-27456 (4.7) — TOCTOU in SUID mount(8) when setting up
loop devices.
Permissions on the recording panel checkboxes were not properly
enforced. A user with only partial access to some recording modes
could still tick a mode's checkbox and, for example, launch a
transcription from the recording panel even though they were not
authorized to.
Gate each checkbox on the user's actual permissions so that only
authorized modes can be started from the panel.
The per-minute room creation throttle absorbs bursts but does not stop
a compromised account from steadily creating rooms over hours or days.
Add RoomCreationDailyUserRateThrottle, a per-user throttle with its own
"room_creation_daily" scope, applied to room creation only alongside
the existing short-term throttle. It defaults to 1000 rooms per day and
is configurable via ROOM_CREATION_DAILY_THROTTLE_RATES.
Tests use a controllable clock and patch rates with monkeypatch so they
are restored after each test.
Add throttling on the endpoint used to generate meeting links, so a
compromised authenticated account cannot silently generate thousands
of links without hitting any suspicious errors or alerts.
The limits are set high enough not to affect legitimate usage, while
capping the damage a leaked account can do.
The CHANGELOG entries ended up in the wrong order after a rebase
performed while merging a recent PR.
Restore the intended organization of the entries so the file reads
correctly again.
Bump PyJWT from 2.13.0 to 2.14.0 to address the following CVEs
reported by Trivy:
* CVE-2026-102268 (CRITICAL)
* CVE-2026-102266 (HIGH) — authentication bypass via empty HMAC
key acceptance.
* CVE-2026-102267 (HIGH) — verification key substitution via
unvalidated JWKS redirects.
* CVE-2026-102271 (HIGH) — authentication bypass via acceptance
of DER public keys as HMAC.
* CVE-2026-102272 (HIGH) — token forgery via improper Unicode
byte-order mark handling.
* CVE-2026-102273 (HIGH) — token forgery via acceptance of
public JWK containers as HMAC.
Since posthog-js 1.356.0, feature flags are reloaded every 5
minutes by default while the tab is visible. Meet sessions are
long-lived visible tabs, so this multiplied the number of `/flags`
requests we send.
Restore the pre-1.356.0 behavior: only (re)load flags on init and
on identity
Add a `purge_inactive_rooms` management command which permanently
deletes the rooms that were not started for
`ROOM_INACTIVITY_DELETION_DAYS` days. Rooms never started are aged from
their creation date.
Important points :
- The setting is unset by default, which disables the purge.
- Rooms holding a recording their users may still access are kept,
- It refuses to run while unregistered rooms are allowed, as the link
of a purged room would turn into a public unregistered room,
Rooms pile up in database with no way to tell the ones still in use from
the abandoned ones. Record on the room the last time LiveKit reported it
as started, in a new `last_started_at` field which stays NULL until the
first time the room is started on a LiveKit node.
The migration stamps existing rooms with the migration time to avoid
deleting preexisting rooms.
Garage has no web console, so inspecting recordings, transcripts and
summaries locally meant writing aws-cli commands by hand.
For each of recordings, transcripts and summaries:
- `make <folder>-list` lists the files from the most recent, and
- `make <folder>-download-latest` downloads the latest one into
data/<folder>.
Only files with the folder's expected extensions are kept, so the Egress
manifests stored next to recordings are skipped.
The media ingresses and their ExternalName services defaulted to the
MinIO service of the development stack, which is now Garage.
Self-hosted relying on these defaults must set serviceMedia*.host and the
upstream-vhost annotations explicitly, see UPGRADE.md.
The development stacks now run Garage instead of MinIO which is
deprecated.
Garage is a bit stricter than MinIO:
- key IDs and secrets must be at least 8 and 16 characters long
- requests must be signed for its region, so every development env now sets
AWS_S3_REGION_NAME=local;
- cross-origin requests are denied unless the bucket CORS rules allow
them, so a one-shot aws-cli container allows the frontend origin to
upload files straight to the bucket.
Switch from the minio client to the boto3 python client, in the metadata
collector agent and the summary service. AWS_S3_REGION_NAME is passed
as-is to boto3, the region is not looked up from the bucket.
In the dev stack, configure the backend to call the summary service
using its v2 API by default.
Also strip the trailing `/` from the task endpoint, which was
triggering a redirect on every call.
The organization now provides default GitHub templates for pull
requests and issues at the org level, so individual repositories no
longer need to duplicate the same Markdown files.
Delete the local templates so this project uses the organization
defaults, reducing the amount of code we maintain and centralizing
the practice across repositories.
Address the following CVEs reported by Trivy on the LiveKit agent
image against `libssl3t64` 3.5.7-1~deb13u2:
* CVE-2026-63073 — CRITICAL (CVSS 9.8)
* CVE-2026-63072
Bump `libssl3t64` to the patched version to pick up both fixes.
Address the following MEDIUM severity CVEs reported against
`djangorestframework` 3.17.1:
* CVE-2026-73228 (CVSS 5.3)
* CVE-2026-73229 (CVSS 4.3)
Bump `djangorestframework` to the patched version to pick up the
fixes.
Bump `libexpat` from 2.8.4-r0 to 2.8.5-r0 to address the following
HIGH severity CVE, reported by Trivy on the frontend image
(alpine 3.24.1):
* CVE-2026-93990 — expat: XML injection via malformed UTF-16
input.
https://avd.aquasec.com/nvd/cve-2026-93990
Most call sites of `update_metadata` already wrap the call in a
try/except that logs the failure at info level.
Remove the warning log inside `update_metadata` itself to avoid
redundant logs, without losing any information.
`EGRESS_ABORTED` and `EGRESS_FAILED` events were previously ignored, leaving
recordings indefinitely in `ACTIVE` state and potentially blocking subsequent
recordings with 409 errors. Add handling and logging for failed and aborted
egresses, discarding failed recordings while preserving the existing behavior
for savable recordings.
Rename `handle_complete` to `handle_savable` to reflect that it handles both
`EGRESS_COMPLETE` and `EGRESS_LIMIT_REACHED`.
Slight refactor to separate LiveKit event handling from recording concerns as
part of a general separation concern to allow for future SFU swapping.
NB:
- FAILED recordings are currently discarded although exploitable media files
may exist
- There is a theoretical hole: if stop observes EGRESS_FAILED before the
egress_ended webhook is processed, the recording is immediately marked as
FAILED. Since only ACTIVE and STOPPED recordings are savable, the webhook
then skips the failure notification and LiveKit error log. In that rare race
condition, participants may therefore not see the failure toast. We accept
this trade-off for now, as this should be very infrequent.
- Another theoretical hole: There is a short race window where the user
clicks stop while the limit-reached status is being processed. Since the user
explicitly requested the stop, we consider skipping the limit notification
acceptable and do not handle this case.
fix(recording): log aborted worker events at info level
When a user starts a recording or a transcription while no track is
published yet, the recording stays in a "starting" state until an
appropriate track is available.
Show an explicit message on start explaining that the recording
will remain in "starting" state until a track of the required type
is published. The expected track type depends on the recording
type (audio-only vs. audio + video).
This situation was generating a lot of support requests, with users
asking why the recording did not actually start.