From 96ff27d1fb7a11a5bbf2eb2c846c8f0cc74705ef Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 1 Jun 2025 13:16:04 +0700 Subject: [PATCH 01/55] Ensure media files are passed relative to the media folder (#4041) We were (partially) doing this for MpvManager, but not for Windows' SimpleMpvPlayer. By passing a media file starting with a special scheme, a malicious actor could have caused a file to be written to the filesystem on Windows. Thanks once again to Michael Lappas for the report. --- pylib/anki/media.py | 2 +- pylib/anki/sound.py | 10 ++++++++++ qt/aqt/sound.py | 10 +++------- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/pylib/anki/media.py b/pylib/anki/media.py index 5d8653a97..8ba5d432c 100644 --- a/pylib/anki/media.py +++ b/pylib/anki/media.py @@ -76,7 +76,7 @@ class MediaManager(DeprecatedNamesMixin): return self.col._backend.strip_av_tags(text) def _extract_filenames(self, text: str) -> list[str]: - "This only exists do support a legacy function; do not use." + "This only exists to support a legacy function; do not use." out = self.col._backend.extract_av_tags(text=text, question_side=True) return [ x.filename diff --git a/pylib/anki/sound.py b/pylib/anki/sound.py index 3d375f716..99a8906f3 100644 --- a/pylib/anki/sound.py +++ b/pylib/anki/sound.py @@ -9,10 +9,13 @@ These can be accessed via eg card.question_av_tags() from __future__ import annotations +import os import re from dataclasses import dataclass from typing import Union +from anki import hooks + @dataclass class TTSTag: @@ -38,6 +41,13 @@ class SoundOrVideoTag: filename: str + def path(self, media_folder: str) -> str: + "Prepend the media folder to the filename." + # Ensure filename doesn't reference parent folder + filename = os.path.basename(self.filename) + filename = hooks.media_file_filter(filename) + return os.path.join(media_folder, filename) + # note this does not include image tags, which are handled with HTML. AVTag = Union[SoundOrVideoTag, TTSTag] diff --git a/qt/aqt/sound.py b/qt/aqt/sound.py index 11f957a84..5ee281e56 100644 --- a/qt/aqt/sound.py +++ b/qt/aqt/sound.py @@ -23,7 +23,6 @@ from markdown import markdown import aqt import aqt.mpv import aqt.qt -from anki import hooks from anki.cards import Card from anki.sound import AV_REF_RE, AVTag, SoundOrVideoTag from anki.utils import is_lin, is_mac, is_win, namedtmp @@ -327,7 +326,7 @@ class SimpleProcessPlayer(Player): # pylint: disable=abstract-method def _play(self, tag: AVTag) -> None: assert isinstance(tag, SoundOrVideoTag) self._process = subprocess.Popen( - self.args + ["--", tag.filename], + self.args + ["--", tag.path(self._media_folder)], env=self.env, cwd=self._media_folder, stdout=subprocess.DEVNULL, @@ -453,8 +452,7 @@ class MpvManager(MPV, SoundOrVideoPlayer): def play(self, tag: AVTag, on_done: OnDoneCallback) -> None: assert isinstance(tag, SoundOrVideoTag) self._on_done = on_done - filename = hooks.media_file_filter(tag.filename) - path = os.path.join(self.media_folder, filename) + path = tag.path(self.media_folder) if self.mpv_version is None or self.mpv_version >= (0, 38, 0): self.command("loadfile", path, "replace", -1, "pause=no") @@ -506,10 +504,8 @@ class SimpleMplayerSlaveModePlayer(SimpleMplayerPlayer): def _play(self, tag: AVTag) -> None: assert isinstance(tag, SoundOrVideoTag) - filename = hooks.media_file_filter(tag.filename) - self._process = subprocess.Popen( - self.args + ["--", filename], + self.args + ["--", tag.path(self.media_folder)], env=self.env, cwd=self.media_folder, stdin=subprocess.PIPE, From f81a9bfdfb66fad607e32ed29cfb19b3015e716c Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 1 Jun 2025 13:16:28 +0700 Subject: [PATCH 02/55] Fix mpv being left around on abrupt termination (#4042) Closes #4015 --- qt/aqt/mpv.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/qt/aqt/mpv.py b/qt/aqt/mpv.py index 329a95538..74155814c 100644 --- a/qt/aqt/mpv.py +++ b/qt/aqt/mpv.py @@ -69,6 +69,7 @@ if is_win: # pylint: disable=import-error import pywintypes import win32file # pytype: disable=import-error + import win32job import win32pipe import winerror @@ -131,6 +132,22 @@ class MPVBase: def _start_process(self): """Start the mpv process.""" self._proc = subprocess.Popen(self.argv, env=self.popenEnv) + if is_win: + # Ensure mpv gets terminated if Anki closes abruptly. + self._job = win32job.CreateJobObject(None, "") + extended_info = win32job.QueryInformationJobObject( + self._job, win32job.JobObjectExtendedLimitInformation + ) + extended_info["BasicLimitInformation"][ + "LimitFlags" + ] = win32job.JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE + win32job.SetInformationJobObject( + self._job, + win32job.JobObjectExtendedLimitInformation, + extended_info, + ) + handle = self._proc._handle # pylint: disable=no-member + win32job.AssignProcessToJobObject(self._job, handle) def _stop_process(self): """Stop the mpv process.""" From 06c0e4c14a5c2d05a4b38b51a6ee956e2583baae Mon Sep 17 00:00:00 2001 From: Luc Mcgrady Date: Sun, 1 Jun 2025 07:18:35 +0100 Subject: [PATCH 03/55] Fix/CMRR style (#4043) --- ts/routes/deck-options/SimulatorModal.svelte | 56 +++++++++----------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/ts/routes/deck-options/SimulatorModal.svelte b/ts/routes/deck-options/SimulatorModal.svelte index 070bed4fa..64b712560 100644 --- a/ts/routes/deck-options/SimulatorModal.svelte +++ b/ts/routes/deck-options/SimulatorModal.svelte @@ -383,38 +383,34 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html {/if} -
-
- {tr.deckConfigComputeOptimalRetention()} - - - {#if optimalRetention} - {estimatedRetention(optimalRetention)} - {#if optimalRetention - $config.desiredRetention >= 0.01} - - {/if} - {/if} - +
+ {tr.deckConfigComputeOptimalRetention()} +
-
+ + + {#if optimalRetention} + {estimatedRetention(optimalRetention)} + {#if optimalRetention - $config.desiredRetention >= 0.01} + + {/if} + {/if} + + {#if computingRetention} +
{computeRetentionProgressString}
+ {/if} + + {#if false} + + + {/if}
{#if computingParams || checkingParams} {computeParamsProgressString} @@ -351,18 +383,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-
- - openHelpModal("rescheduleCardsOnChange")}> - - - - - {#if $fsrsReschedule} - - {/if} -
-