File php-composer2-CVE-2025-67746.patch of Package php-composer2.42546

From 5db1876a76fdef76d3c4f8a27995c434c7a43e71 Mon Sep 17 00:00:00 2001
From: Jordi Boggiano <j.boggiano@seld.be>
Date: Tue, 30 Dec 2025 13:18:16 +0100
Subject: [PATCH] Merge commit from fork

---
Index: SRC/src/Composer/Advisory/Auditor.php
===================================================================
--- SRC.orig/src/Composer/Advisory/Auditor.php
+++ SRC/src/Composer/Advisory/Auditor.php
@@ -272,7 +272,7 @@ $row[] = $advisory->ignoreReason ?? 'Non
 $io->getTable()
 ->setHorizontal()
 ->setHeaders($headers)
-->addRow($row)
+->addRow(ConsoleIO::sanitize($row))
 ->setColumnWidth(1, 80)
 ->setColumnMaxWidth(1, 80)
 ->render();
@@ -341,7 +341,7 @@ $table = $io->getTable()
 
 foreach ($packages as $pkg) {
 $replacement = $pkg->getReplacementPackage() !== null ? $pkg->getReplacementPackage() : 'none';
-$table->addRow([$this->getPackageNameWithLink($pkg), $replacement]);
+$table->addRow(ConsoleIO::sanitize([$this->getPackageNameWithLink($pkg), $replacement]));
 }
 
 $table->render();
Index: SRC/src/Composer/IO/ConsoleIO.php
===================================================================
--- SRC.orig/src/Composer/IO/ConsoleIO.php
+++ SRC/src/Composer/IO/ConsoleIO.php
@@ -12,6 +12,7 @@
 
 namespace Composer\IO;
 
+use Composer\Pcre\Preg;
 use Composer\Question\StrictConfirmationQuestion;
 use Symfony\Component\Console\Helper\HelperSet;
 use Symfony\Component\Console\Helper\ProgressBar;
@@ -120,6 +121,8 @@ return $this->output->isDebug();
 
 public function write($messages, bool $newline = true, int $verbosity = self::NORMAL)
 {
+$messages = self::sanitize($messages);
+
 $this->doWrite($messages, $newline, false, $verbosity);
 }
 
@@ -128,6 +131,8 @@ $this->doWrite($messages, $newline, fals
 
 public function writeError($messages, bool $newline = true, int $verbosity = self::NORMAL)
 {
+$messages = self::sanitize($messages);
+
 $this->doWrite($messages, $newline, true, $verbosity);
 }
 
@@ -252,7 +257,7 @@ public function ask($question, $default
 {
 
 $helper = $this->helperSet->get('question');
-$question = new Question($question, $default);
+$question = new Question(self::sanitize($question), is_string($default) ? self::sanitize($default) : $default);
 
 return $helper->ask($this->input, $this->getErrorOutput(), $question);
 }
@@ -264,7 +269,7 @@ public function askConfirmation($questio
 {
 
 $helper = $this->helperSet->get('question');
-$question = new StrictConfirmationQuestion($question, $default);
+$question = new StrictConfirmationQuestion(self::sanitize($question), is_string($default) ? self::sanitize($default) : $default);
 
 return $helper->ask($this->input, $this->getErrorOutput(), $question);
 }
@@ -276,7 +281,7 @@ public function askAndValidate($question
 {
 
 $helper = $this->helperSet->get('question');
-$question = new Question($question, $default);
+$question = new Question(self::sanitize($question), is_string($default) ? self::sanitize($default) : $default);
 $question->setValidator($validator);
 $question->setMaxAttempts($attempts);
 
@@ -290,7 +295,7 @@ public function askAndHideAnswer($questi
 {
 
 $helper = $this->helperSet->get('question');
-$question = new Question($question);
+$question = new Question(self::sanitize($question));
 $question->setHidden(true);
 
 return $helper->ask($this->input, $this->getErrorOutput(), $question);
@@ -303,7 +308,7 @@ public function select($question, $choic
 {
 
 $helper = $this->helperSet->get('question');
-$question = new ChoiceQuestion($question, $choices, $default);
+$question = new ChoiceQuestion(self::sanitize($question), self::sanitize($choices), is_string($default) ? self::sanitize($default) : $default);
 $question->setMaxAttempts($attempts ?: null); 
 $question->setErrorMessage($errorMessage);
 $question->setMultiselect($multiselect);
@@ -342,4 +347,35 @@ return $this->output->getErrorOutput();
 
 return $this->output;
 }
+
+    /**
+     * Sanitize string to remove control characters
+     *
+     * If $allowNewlines is true, \x0A (\n) and \x0D\x0A (\r\n) are let through. Single \r are still sanitized away to prevent overwriting whole lines.
+     *
+     * All other control chars (except NULL bytes) as well as ANSI escape sequences are removed.
+     *
+     * @param string|iterable<string> $messages
+     * @return string|array<string>
+     * @phpstan-return ($messages is string ? string : array<string>)
+     */
+    public static function sanitize($messages, bool $allowNewlines = true)
+    {
+        // Match ANSI escape sequences:
+        // - CSI (Control Sequence Introducer): ESC [ params intermediate final
+        // - OSC (Operating System Command): ESC ] ... ESC \ or BEL
+        // - Other ESC sequences: ESC followed by any character
+        $escapePattern = '\x1B\[[\x30-\x3F]*[\x20-\x2F]*[\x40-\x7E]|\x1B\].*?(?:\x1B\\\\|\x07)|\x1B.';
+        $pattern = $allowNewlines ? "{{$escapePattern}|[\x01-\x09\x0B\x0C\x0E-\x1A]|\r(?!\n)}u" : "{{$escapePattern}|[\x01-\x1A]}u";
+        if (is_string($messages)) {
+            return Preg::replace($pattern, '', $messages);
+        }
+
+        $sanitized = [];
+        foreach ($messages as $key => $message) {
+            $sanitized[$key] = Preg::replace($pattern, '', $message);
+        }
+
+        return $sanitized;
+    }
 }
openSUSE Build Service is sponsored by