Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:aegisub
User: [email protected]
Usertags: pu

Hi,

[ Reason ]
Request to upload aegisub to Trixie p-u to address CVE-2026-92705.

[ Impact ]
- CVE-2026-92705: Aegisub: Arbitrary code execution when opening a
crafted ASS subtitle file

[ Tests ]
Manually tested to be working: a warning dialog is opened for user
confirmation when an ASS subtitle file references an automation script
to be loaded automatically.

[ Risks ]
Patch contains changes to add input sanitization and open warning when
loading automation scripts. Code changes are local and should not affect
other functions.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]
1 patch combining all 3 upstream patches referenced in the CVE, plus
adaptions required for older versions.

There is also a trivial change to debian-branch in d/gbp.conf for
tooling.

Please allow me to upload aegisub/3.2.2+dfsg-7.3+deb13u1 to Trixie p-u.

-- 
Regards,
Xiyue Deng
diff -Nru aegisub-3.2.2+dfsg/debian/changelog aegisub-3.2.2+dfsg/debian/changelog
--- aegisub-3.2.2+dfsg/debian/changelog	2025-03-28 06:58:39.000000000 -0700
+++ aegisub-3.2.2+dfsg/debian/changelog	2026-10-30 17:54:05.000000000 -0700
@@ -1,3 +1,11 @@
+aegisub (3.2.2+dfsg-7.3+deb13u1) trixie-security; urgency=medium
+
+  * Non-maintainer upload
+  * Add patch for fixing CVE-2026-92705 (Closes: #1150511)
+  * Update debian-branch in d/gbp.conf
+
+ -- Xiyue Deng <[email protected]>  Fri, 30 Oct 2026 17:54:05 -0700
+
 aegisub (3.2.2+dfsg-7.3) unstable; urgency=medium
 
   * Non-maintainer upload.
diff -Nru aegisub-3.2.2+dfsg/debian/gbp.conf aegisub-3.2.2+dfsg/debian/gbp.conf
--- aegisub-3.2.2+dfsg/debian/gbp.conf	2025-03-28 06:58:39.000000000 -0700
+++ aegisub-3.2.2+dfsg/debian/gbp.conf	2026-10-30 17:54:05.000000000 -0700
@@ -1,2 +1,4 @@
 [DEFAULT]
+debian-branch = debian/trixie
+
 pristine-tar = True
diff -Nru aegisub-3.2.2+dfsg/debian/patches/CVE-2026-92705.patch aegisub-3.2.2+dfsg/debian/patches/CVE-2026-92705.patch
--- aegisub-3.2.2+dfsg/debian/patches/CVE-2026-92705.patch	1969-12-31 16:00:00.000000000 -0800
+++ aegisub-3.2.2+dfsg/debian/patches/CVE-2026-92705.patch	2026-10-30 17:54:05.000000000 -0700
@@ -0,0 +1,214 @@
+Description: Fix for CVE-2026-92705
+ Adapted patches from upstream for fixing CVE-2026-92705
+Author: Xiyue Deng <[email protected]>
+Origin: upstream
+Bug: https://github.com/TypesettingTools/Aegisub/security/advisories/GHSA-67hq-5vgg-6f23
+Bug-Debian: https://bugs.debian.org/1150511
+Last-Update: 2026-10-30
+---
+This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
+--- a/libaegisub/common/fs.cpp
++++ b/libaegisub/common/fs.cpp
+@@ -97,7 +97,7 @@
+ 	}
+ 
+ 	bool HasExtension(path const& p, std::string const& ext) {
+-		auto filename = p.filename().string();
++		std::string filename = p.filename().string().c_str();	// Be consistent with std::filesystem functions which use C strings internally
+ 		if (filename.size() < ext.size() + 1) return false;
+ 		if (filename[filename.size() - ext.size() - 1] != '.') return false;
+ 		return boost::iends_with(filename, ext);
+--- a/src/ass_parser.cpp
++++ b/src/ass_parser.cpp
+@@ -29,6 +29,7 @@
+ #include <algorithm>
+ #include <boost/algorithm/string/case_conv.hpp>
+ #include <boost/algorithm/string/predicate.hpp>
++#include <boost/algorithm/string/replace.hpp>
+ #include <boost/algorithm/string/trim.hpp>
+ #include <boost/lexical_cast.hpp>
+ #include <boost/regex.hpp>
+@@ -165,7 +166,9 @@
+ 		target->Info.push_back(*new AssInfo(std::move(key), std::move(value)));
+ }
+ 
+-void AssParser::ParseMetadataLine(std::string const& data) {
++void AssParser::ParseMetadataLine(std::string const& rawdata) {
++	std::string data = SanitizeLine(rawdata);
++
+ 	size_t pos = data.find(':');
+ 	if (pos == data.npos) return;
+ 
+@@ -196,7 +199,9 @@
+ 		attach = agi::make_unique<AssAttachment>(data, AssEntryGroup::GRAPHIC);
+ }
+ 
+-void AssParser::ParseExtradataLine(std::string const &data) {
++void AssParser::ParseExtradataLine(std::string const &rawdata) {
++	std::string data = SanitizeLine(rawdata);
++
+ 	static const boost::regex matcher("Data:[[:space:]]*(\\d+),([^,]+),(.)(.*)");
+ 	boost::match_results<std::string::const_iterator> mr;
+ 
+@@ -223,6 +228,12 @@
+ 	}
+ }
+ 
++std::string AssParser::SanitizeLine(std::string const& data) {
++	std::string result = data;
++	boost::replace_all(result, std::string("\0", 1), "\uFFFD");		// Unicode replacement character
++	return result;
++}
++
+ void AssParser::AddLine(std::string const& data) {
+ 	// Special-case for attachments since a line could theoretically be both a
+ 	// valid attachment data line and a valid section header, and if an
+--- a/src/ass_parser.h
++++ b/src/ass_parser.h
+@@ -35,6 +35,8 @@
+ 	void ParseGraphicsLine(std::string const& data);
+ 	void ParseExtradataLine(std::string const &data);
+ 	void UnknownLine(std::string const&) { }
++
++	std::string SanitizeLine(std::string const& data);
+ public:
+ 	AssParser(AssFile *target, int version);
+ 	~AssParser();
+--- a/src/auto4_base.cpp
++++ b/src/auto4_base.cpp
+@@ -33,6 +33,7 @@
+ #include "ass_style.h"
+ #include "compat.h"
+ #include "dialog_progress.h"
++#include "format.h"
+ #include "include/aegisub/context.h"
+ #include "options.h"
+ #include "string_codec.h"
+@@ -44,13 +45,17 @@
+ #include <libaegisub/path.h>
+ #include <libaegisub/make_unique.h>
+ 
++#include <boost/algorithm/string/join.hpp>
+ #include <boost/algorithm/string/replace.hpp>
+ #include <boost/algorithm/string/trim.hpp>
+ #include <boost/tokenizer.hpp>
+ #include <future>
++#include <ranges>
++#include <iterator>
+ 
+ #include <wx/dcmemory.h>
+ #include <wx/log.h>
++#include <wx/msgdlg.h>
+ #include <wx/sizer.h>
+ 
+ #ifdef __WINDOWS__
+@@ -362,21 +367,23 @@
+ 
+ 	void LocalScriptManager::Reload()
+ 	{
++		bool was_empty = scripts.empty();
+ 		scripts.clear();
+ 
+ 		auto const& local_scripts = context->ass->Properties.automation_scripts;
+-		if (local_scripts.empty()) {
+-			ScriptsChanged();
+-			return;
+-		}
++
++		// Tuples of (raw script path, base path, resolved path)
++		using PathTuple = std::tuple<std::string, agi::fs::path, agi::fs::path>;
++		std::vector<PathTuple> resolvedpaths;
+ 
+ 		auto autobasefn(OPT_GET("Path/Automation/Base")->GetString());
+ 
+ 		boost::char_separator<char> sep("|");
+ 		for (auto const& cur : boost::tokenizer<boost::char_separator<char>>(local_scripts, sep)) {
+-			auto trimmed = boost::trim_copy(cur);
+-			char first_char = trimmed[0];
+-			trimmed.erase(0, 1);
++			auto const tok = boost::trim_copy(cur);
++			if (tok.size() == 0) continue;
++			char first_char = tok[0];
++			std::string trimmed(tok.begin() + 1, tok.end());
+ 
+ 			agi::fs::path basepath;
+ 			if (first_char == '~') {
+@@ -390,11 +397,54 @@
+ 				continue;
+ 			}
+ 			auto sfname = basepath/trimmed;
++
++			resolvedpaths.emplace_back(tok, basepath, sfname);
++		}
++
++		if (resolvedpaths.empty()) {
++			if (!was_empty)
++				ScriptsChanged();
++			return;
++		}
++
++		std::vector<std::string> resolvedpaths_log;
++		std::transform(resolvedpaths.begin(), resolvedpaths.end(),
++			       std::back_inserter(resolvedpaths_log),
++			       [](PathTuple const &paths) {
++				 return from_wx(fmt_tl(
++				     "    %s (%s)", to_wx(std::get<0>(paths)),
++				     std::get<2>(paths).wstring()));
++			       });
++		wxMessageDialog dlg(
++				nullptr,
++				fmt_tl(
++					"The subtitle file you have opened contains local automation scripts. "
++					"Do you want to load the associated automation scripts?\n"
++					"\n"
++					"List of scripts:\n\n%s\n\n"
++					"You should only load associated automation scripts if you trust their authors! "
++					"They are not required to view or edit the subtitle file."
++					, boost::algorithm::join(resolvedpaths_log, "\n"))
++				, _("Load associated automation scripts?")
++				, wxICON_WARNING | wxOK | wxCANCEL | wxCANCEL_DEFAULT | wxCENTRE
++			);
++		dlg.SetOKCancelLabels(_("Trust the authors && load scripts"), _("No"));
++		auto response = dlg.ShowModal();
++
++		if (response != wxID_OK) {
++			if (!was_empty)
++				ScriptsChanged();
++			else
++				SaveLoadedList();
++			return;
++		}
++
++		for (auto const& [tok, basepath, sfname] : resolvedpaths) {
+ 			if (agi::fs::FileExists(sfname))
+ 				scripts.emplace_back(Automation4::ScriptFactory::CreateFromFile(sfname, true));
+ 			else {
+-				wxLogWarning("Automation Script referenced could not be found.\nFilename specified: %c%s\nSearched relative to: %s\nResolved filename: %s",
+-					first_char, to_wx(trimmed), basepath.wstring(), sfname.wstring());
++				wxLogWarning(fmt_tl("Automation Script referenced could not be found.\nFilename specified: %s\nSearched relative to: %s\nResolved filename: %s",
++					to_wx(tok), basepath.wstring(), sfname.wstring()));
+ 			}
+ 		}
+ 
+--- a/src/format.h
++++ b/src/format.h
+@@ -14,6 +14,8 @@
+ //
+ // Aegisub Project http://www.aegisub.org/
+ 
++#pragma once
++
+ #include <libaegisub/format.h>
+ 
+ #include <wx/string.h>
+--- a/libaegisub/include/libaegisub/format.h
++++ b/libaegisub/include/libaegisub/format.h
+@@ -14,6 +14,8 @@
+ //
+ // Aegisub Project http://www.aegisub.org/
+ 
++#pragma once
++
+ #include <libaegisub/fs_fwd.h>
+ 
+ #include <boost/interprocess/streams/vectorstream.hpp>
diff -Nru aegisub-3.2.2+dfsg/debian/patches/series aegisub-3.2.2+dfsg/debian/patches/series
--- aegisub-3.2.2+dfsg/debian/patches/series	2025-03-28 06:58:39.000000000 -0700
+++ aegisub-3.2.2+dfsg/debian/patches/series	2026-10-30 17:54:05.000000000 -0700
@@ -29,3 +29,4 @@
 fix_boost183.patch
 aegisub-add-support-for-loong64.patch
 icu76.patch
+CVE-2026-92705.patch

Attachment: signature.asc
Description: PGP signature

Reply via email to