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