Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package perl-Mojolicious for 
openSUSE:Factory checked in at 2026-07-15 22:14:36
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/perl-Mojolicious (Old)
 and      /work/SRC/openSUSE:Factory/.perl-Mojolicious.new.1991 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "perl-Mojolicious"

Wed Jul 15 22:14:36 2026 rev:186 rq:1365998 version:9.480.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/perl-Mojolicious/perl-Mojolicious.changes        
2026-07-07 21:08:17.240846688 +0200
+++ 
/work/SRC/openSUSE:Factory/.perl-Mojolicious.new.1991/perl-Mojolicious.changes  
    2026-07-15 22:15:34.047180178 +0200
@@ -1,0 +2,11 @@
+Wed Jul 15 07:26:15 UTC 2026 - Tina Müller <[email protected]>
+
+- updated to 9.480.0 (9.48)
+   see /usr/share/doc/packages/perl-Mojolicious/Changes
+
+  9.48  2026-07-14
+    - Fixed a security issue where CSRF tokens were vulnerable to BREACH 
attacks. Tokens are now masked with a fresh
+      random value on every request, instead of being reused for the whole 
lifetime of a session.
+      CVE-2026-15747 bsc#1271431
+
+-------------------------------------------------------------------

Old:
----
  Mojolicious-9.47.tar.gz

New:
----
  Mojolicious-9.48.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ perl-Mojolicious.spec ++++++
--- /var/tmp/diff_new_pack.ihFLV9/_old  2026-07-15 22:15:34.791205423 +0200
+++ /var/tmp/diff_new_pack.ihFLV9/_new  2026-07-15 22:15:34.791205423 +0200
@@ -18,10 +18,10 @@
 
 %define cpan_name Mojolicious
 Name:           perl-Mojolicious
-Version:        9.470.0
+Version:        9.480.0
 Release:        0
-# 9.47 -> normalize -> 9.470.0
-%define cpan_version 9.47
+# 9.48 -> normalize -> 9.480.0
+%define cpan_version 9.48
 License:        Artistic-2.0
 Summary:        Real-time web framework
 URL:            https://metacpan.org/release/%{cpan_name}

++++++ Mojolicious-9.47.tar.gz -> Mojolicious-9.48.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-9.47/Changes new/Mojolicious-9.48/Changes
--- old/Mojolicious-9.47/Changes        2026-07-05 16:00:40.000000000 +0200
+++ new/Mojolicious-9.48/Changes        2026-07-14 14:52:03.000000000 +0200
@@ -1,4 +1,8 @@
 
+9.48  2026-07-14
+  - Fixed a security issue where CSRF tokens were vulnerable to BREACH 
attacks. Tokens are now masked with a fresh
+    random value on every request, instead of being reused for the whole 
lifetime of a session.
+
 9.47  2026-07-05
   - Added support for the QUERY HTTP request method from RFC 10008.
   - Added query and query_p methods to Mojo::UserAgent.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-9.47/META.json 
new/Mojolicious-9.48/META.json
--- old/Mojolicious-9.47/META.json      2026-07-05 16:01:49.000000000 +0200
+++ new/Mojolicious-9.48/META.json      2026-07-14 14:53:10.000000000 +0200
@@ -64,6 +64,6 @@
          "web" : "https://web.libera.chat/#mojo";
       }
    },
-   "version" : "9.47",
+   "version" : "9.48",
    "x_serialization_backend" : "JSON::PP version 4.16"
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-9.47/META.yml 
new/Mojolicious-9.48/META.yml
--- old/Mojolicious-9.47/META.yml       2026-07-05 16:01:49.000000000 +0200
+++ new/Mojolicious-9.48/META.yml       2026-07-14 14:53:10.000000000 +0200
@@ -35,5 +35,5 @@
   homepage: https://mojolicious.org
   license: https://opensource.org/license/artistic-2-0
   repository: https://github.com/mojolicious/mojo.git
-version: '9.47'
+version: '9.48'
 x_serialization_backend: 'CPAN::Meta::YAML version 0.020'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Mojolicious-9.47/lib/Mojolicious/Guides/Rendering.pod 
new/Mojolicious-9.48/lib/Mojolicious/Guides/Rendering.pod
--- old/Mojolicious-9.47/lib/Mojolicious/Guides/Rendering.pod   2025-07-03 
14:07:39.000000000 +0200
+++ new/Mojolicious-9.48/lib/Mojolicious/Guides/Rendering.pod   2026-07-14 
14:51:25.000000000 +0200
@@ -1007,6 +1007,10 @@
 For Ajax requests and the like, you can also generate a token directly with 
the helper
 L<Mojolicious::Plugin::DefaultHelpers/"csrf_token">, and then pass it along 
with the C<X-CSRF-Token> request header.
 
+The token is masked with a fresh random value on every request, so its value 
changes each time even though it stays
+bound to the same session. This protects against BREACH attacks, which could 
otherwise recover a static token from
+compressed responses.
+
 =head1 ADVANCED
 
 Less commonly used and more powerful features.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Mojolicious-9.47/lib/Mojolicious/Plugin/DefaultHelpers.pm 
new/Mojolicious-9.48/lib/Mojolicious/Plugin/DefaultHelpers.pm
--- old/Mojolicious-9.47/lib/Mojolicious/Plugin/DefaultHelpers.pm       
2026-06-04 18:58:27.000000000 +0200
+++ new/Mojolicious-9.48/lib/Mojolicious/Plugin/DefaultHelpers.pm       
2026-07-14 14:51:25.000000000 +0200
@@ -95,7 +95,17 @@
   return (blessed $e && $e->isa('Mojo::Exception')) ? $e : 
Mojo::Exception->new($e);
 }
 
-sub _csrf_token { $_[0]->session->{csrf_token} ||= unpack "H*", 
random_bytes(20) }
+sub _csrf_token {
+  my $c = shift;
+
+  # Generate token and store it in the session
+  my $session = $c->session;
+  $session->{csrf_token} ||= unpack "H*", random_bytes(20);
+
+  # Mask token to protect against BREACH attacks
+  my $mask = random_bytes(20);
+  return unpack("H*", $mask) . unpack("H*", $mask ^ pack("H*", 
$session->{csrf_token}));
+}
 
 sub _current_route {
   return '' unless my $route = shift->match->endpoint;
@@ -474,7 +484,8 @@
 
   %= csrf_token
 
-Get CSRF token from L</"session">, and generate one if none exists.
+Get CSRF token from L</"session">, and generate one if none exists. The token 
is masked with a fresh random value on
+every call to protect against BREACH attacks.
 
 =head2 current_route
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Mojolicious-9.47/lib/Mojolicious/Validator/Validation.pm 
new/Mojolicious-9.48/lib/Mojolicious/Validator/Validation.pm
--- old/Mojolicious-9.47/lib/Mojolicious/Validator/Validation.pm        
2023-03-08 19:43:31.000000000 +0100
+++ new/Mojolicious-9.48/lib/Mojolicious/Validator/Validation.pm        
2026-07-14 14:51:25.000000000 +0200
@@ -3,6 +3,7 @@
 
 use Carp ();
 use Mojo::DynamicMethods -dispatch;
+use Mojo::Util   qw(secure_compare);
 use Scalar::Util ();
 
 has [qw(csrf_token topic validator)];
@@ -39,7 +40,7 @@
 sub csrf_protect {
   my $self  = shift;
   my $token = $self->input->{csrf_token};
-  $self->error(csrf_token => ['csrf_protect']) unless $token && $token eq 
($self->csrf_token // '');
+  $self->error(csrf_token => ['csrf_protect']) unless _csrf_valid($token, 
$self->csrf_token);
   return $self;
 }
 
@@ -88,6 +89,16 @@
   return $self->error($name => ['required']);
 }
 
+sub _csrf_valid {
+  my ($token, $csrf) = @_;
+  return undef unless defined $token && !ref $token && $token =~ 
/^[0-9a-f]{80}$/;
+
+  # Unmask token to protect against BREACH attacks
+  my $mask   = pack 'H*', substr $token, 0, 40;
+  my $masked = pack 'H*', substr $token, 40;
+  return secure_compare unpack('H*', $mask ^ $masked), $csrf // '';
+}
+
 1;
 
 =encoding utf8
@@ -165,7 +176,8 @@
 
   $v = $v->csrf_protect;
 
-Validate C<csrf_token> and protect from cross-site request forgery.
+Validate C<csrf_token> and protect from cross-site request forgery. Tokens are 
masked with a random value to protect
+against BREACH attacks.
 
 =head2 error
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-9.47/lib/Mojolicious.pm 
new/Mojolicious-9.48/lib/Mojolicious.pm
--- old/Mojolicious-9.47/lib/Mojolicious.pm     2026-06-04 19:10:03.000000000 
+0200
+++ new/Mojolicious-9.48/lib/Mojolicious.pm     2026-07-05 16:02:24.000000000 
+0200
@@ -57,7 +57,7 @@
 has validator => sub { Mojolicious::Validator->new };
 
 our $CODENAME = 'Waffle';
-our $VERSION  = '9.47';
+our $VERSION  = '9.48';
 
 sub BUILD_DYNAMIC {
   my ($class, $method, $dyn_methods) = @_;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-9.47/t/mojo/collection.t 
new/Mojolicious-9.48/t/mojo/collection.t
--- old/Mojolicious-9.47/t/mojo/collection.t    2023-03-08 19:43:32.000000000 
+0100
+++ new/Mojolicious-9.48/t/mojo/collection.t    2026-07-05 17:48:39.000000000 
+0200
@@ -133,8 +133,8 @@
 
 subtest 'sort' => sub {
   my $collection = c(2, 5, 4, 1);
-  is_deeply $collection->sort->to_array,                          [1, 2, 4, 
5], 'right order';
-  is_deeply $collection->sort(sub { $b cmp $a })->to_array,       [5, 4, 2, 
1], 'right order';
+  is_deeply $collection->sort->to_array, [1, 2, 4, 5], 'right order';
+  is_deeply $collection->sort(sub { $b    cmp $a })->to_array,    [5, 4, 2, 
1], 'right order';
   is_deeply $collection->sort(sub { $_[1] cmp $_[0] })->to_array, [5, 4, 2, 
1], 'right order';
   $collection = c(qw(Test perl Mojo));
   is_deeply $collection->sort(sub { uc(shift) cmp uc(shift) })->to_array, 
[qw(Mojo perl Test)], 'right order';
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Mojolicious-9.47/t/mojolicious/validation_lite_app.t 
new/Mojolicious-9.48/t/mojolicious/validation_lite_app.t
--- old/Mojolicious-9.47/t/mojolicious/validation_lite_app.t    2025-02-25 
01:50:18.000000000 +0100
+++ new/Mojolicious-9.48/t/mojolicious/validation_lite_app.t    2026-07-14 
14:51:25.000000000 +0200
@@ -309,23 +309,35 @@
 };
 
 subtest 'CSRF protection' => sub {
+
+  # Masked token with an all-zero mask, so it unmasks to the session token
+  my $session = 'a' x 40;
+  my $token   = ('0' x 40) . $session;
+
   my $v = $t->app->validation->input({foo => 'bar'})->csrf_protect;
   ok $v->has_data,  'has data';
   ok $v->has_error, 'has error';
   is_deeply $v->error('csrf_token'), ['csrf_protect'], 'right error';
-  $v = $t->app->validation->input({csrf_token => 'abc'});
+  $v = $t->app->validation->input({csrf_token => $token});
   ok $v->has_data,                'has data';
   ok $v->csrf_protect->has_error, 'has error';
   ok $v->has_data,                'has data';
   is_deeply $v->error('csrf_token'), ['csrf_protect'], 'right error';
-  $v = $t->app->validation->input({csrf_token => 'abc', foo => 
'bar'})->csrf_token('cba')->csrf_protect;
+  $v = $t->app->validation->input({csrf_token => $token, foo => 
'bar'})->csrf_token('b' x 40)->csrf_protect;
+  ok $v->has_error, 'has error';
+  is_deeply $v->error('csrf_token'), ['csrf_protect'], 'right error';
+  $v = $t->app->validation->input({csrf_token => $session, foo => 
'bar'})->csrf_token($session)->csrf_protect;
+  ok $v->has_error, 'has error';
+  is_deeply $v->error('csrf_token'), ['csrf_protect'], 'right error';
+  (my $tampered = $token) =~ s/.$/f/;
+  $v = $t->app->validation->input({csrf_token => $tampered, foo => 
'bar'})->csrf_token($session)->csrf_protect;
   ok $v->has_error, 'has error';
   is_deeply $v->error('csrf_token'), ['csrf_protect'], 'right error';
-  $v = $t->app->validation->input({csrf_token => 'abc', foo => 
'bar'})->csrf_token('abc')->csrf_protect;
+  $v = $t->app->validation->input({csrf_token => $token, foo => 
'bar'})->csrf_token($session)->csrf_protect;
   ok !$v->has_error,                'no error';
   ok $v->required('foo')->is_valid, 'valid';
   is_deeply $v->output, {foo => 'bar'}, 'right result';
-  $v = $t->app->validation->input({csrf_token => ['abc', 
'abc']})->csrf_token('abc')->csrf_protect;
+  $v = $t->app->validation->input({csrf_token => [$token, 
$token]})->csrf_token($session)->csrf_protect;
   ok $v->has_error, 'has error';
   is_deeply $v->error('csrf_token'), ['csrf_protect'], 'right error';
 };
@@ -418,8 +430,22 @@
     ->element_count_is('[name=csrf_token]', 2)
     ->element_count_is('form',              2)
     ->element_exists('form > input[name=csrf_token] + input[type=submit]');
-  is $t->tx->res->dom->find('[name=csrf_token]')->[0]->val, 
$t->tx->res->dom->find('[name=csrf_token]')->[1]->val,
-    'same token';
+  isnt $t->tx->res->dom->find('[name=csrf_token]')->[0]->val, 
$t->tx->res->dom->find('[name=csrf_token]')->[1]->val,
+    'different masked tokens';
+};
+
+subtest 'Different masked CSRF token for every request' => sub {
+  my $first  = $t->ua->get('/forgery')->res->dom->at('[name=csrf_token]')->val;
+  my $second = $t->ua->get('/forgery')->res->dom->at('[name=csrf_token]')->val;
+  isnt $first, $second, 'different tokens';
+  like $first,  qr/^[0-9a-f]{80}$/, 'token is masked';
+  like $second, qr/^[0-9a-f]{80}$/, 'token is masked';
+  $t->post_ok('/forgery' => form => {csrf_token => $first, foo => 'bar'})
+    ->status_is(200)
+    ->content_unlike(qr/Wrong or missing CSRF token!/);
+  $t->post_ok('/forgery' => form => {csrf_token => $second, foo => 'bar'})
+    ->status_is(200)
+    ->content_unlike(qr/Wrong or missing CSRF token!/);
 };
 
 subtest 'Correct CSRF token (header)' => sub {

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.ihFLV9/_old  2026-07-15 22:15:35.267221574 +0200
+++ /var/tmp/diff_new_pack.ihFLV9/_new  2026-07-15 22:15:35.271221709 +0200
@@ -1,6 +1,6 @@
-mtime: 1783329162
-commit: bce999112d433396c90604c7f31c3e545b3032dbab35a011adba07ea9689cc17
+mtime: 1784127264
+commit: c4d329c531d49a3eac7b713f705ebdb8e0d4bfb55b2b81a27b789623c08d24f4
 url: https://src.opensuse.org/perl/perl-Mojolicious
-revision: bce999112d433396c90604c7f31c3e545b3032dbab35a011adba07ea9689cc17
+revision: c4d329c531d49a3eac7b713f705ebdb8e0d4bfb55b2b81a27b789623c08d24f4
 projectscmsync: https://src.opensuse.org/perl/_ObsPrj
 

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-07-15 16:54:24.000000000 +0200
@@ -0,0 +1 @@
+.osc

Reply via email to