Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package perl-Cpanel-JSON-XS for 
openSUSE:Factory checked in at 2026-06-03 20:28:20
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/perl-Cpanel-JSON-XS (Old)
 and      /work/SRC/openSUSE:Factory/.perl-Cpanel-JSON-XS.new.1937 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "perl-Cpanel-JSON-XS"

Wed Jun  3 20:28:20 2026 rev:42 rq:1356949 version:4.410.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/perl-Cpanel-JSON-XS/perl-Cpanel-JSON-XS.changes  
2025-09-09 20:30:48.526651628 +0200
+++ 
/work/SRC/openSUSE:Factory/.perl-Cpanel-JSON-XS.new.1937/perl-Cpanel-JSON-XS.changes
        2026-06-03 20:30:26.073142935 +0200
@@ -1,0 +2,14 @@
+Thu May 28 08:53:35 UTC 2026 - Tina Müller <[email protected]>
+
+- updated to 4.410.0 (4.41)
+   see /usr/share/doc/packages/perl-Cpanel-JSON-XS/Changes
+
+  4.41 2026-05-27 (rurban)
+       - Fix BOM-shift PV-corruption SIGABRT (CVE-2026-9516) (patch by Paul 
Johnson)
+          - Fix dupkeys_as_arrayref type confusion (CVE-2026-9334) (patch by 
Paul Johnson)
+          - Fix incr_parse single-quote string delimiter (GH #245, reported by
+            Paul Johnson)
+          - Fix a one-byte out-of-bounds heap read reachable via allow_barekey 
on
+            truncated input (GH #244, reported by Paul Johnson)
+
+-------------------------------------------------------------------

Old:
----
  Cpanel-JSON-XS-4.40.tar.gz

New:
----
  Cpanel-JSON-XS-4.41.tar.gz

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

Other differences:
------------------
++++++ perl-Cpanel-JSON-XS.spec ++++++
--- /var/tmp/diff_new_pack.Axe8ku/_old  2026-06-03 20:30:26.857175405 +0200
+++ /var/tmp/diff_new_pack.Axe8ku/_new  2026-06-03 20:30:26.861175571 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package perl-Cpanel-JSON-XS
 #
-# Copyright (c) 2025 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,10 +18,10 @@
 
 %define cpan_name Cpanel-JSON-XS
 Name:           perl-Cpanel-JSON-XS
-Version:        4.400.0
+Version:        4.410.0
 Release:        0
-# 4.40 -> normalize -> 4.400.0
-%define cpan_version 4.40
+# 4.41 -> normalize -> 4.410.0
+%define cpan_version 4.41
 License:        Artistic-1.0 OR GPL-1.0-or-later
 Summary:        CPanel fork of JSON::XS, fast and correct serializing
 URL:            https://metacpan.org/release/%{cpan_name}
@@ -32,6 +32,7 @@
 BuildRequires:  perl-macros
 BuildRequires:  perl(Time::Piece)
 Provides:       perl(Cpanel::JSON::XS) = %{version}
+Provides:       perl(Cpanel::JSON::XS::Type)
 %undefine       __perllib_provides
 %{perl_requires}
 

++++++ Cpanel-JSON-XS-4.40.tar.gz -> Cpanel-JSON-XS-4.41.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Cpanel-JSON-XS-4.40/.github/workflows/testsuite.yml 
new/Cpanel-JSON-XS-4.41/.github/workflows/testsuite.yml
--- old/Cpanel-JSON-XS-4.40/.github/workflows/testsuite.yml     2025-09-08 
13:53:39.000000000 +0200
+++ new/Cpanel-JSON-XS-4.41/.github/workflows/testsuite.yml     2026-05-27 
20:00:10.000000000 +0200
@@ -19,10 +19,10 @@
     runs-on: ubuntu-latest
 
     steps:
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # 
v5.0.0
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
       - run: perl -V
       - name: install cpan deps
-        uses: 
perl-actions/install-with-cpm@8b1a9840b26cc3885ae2889749a48629be2501b0 # v1.9
+        uses: 
perl-actions/install-with-cpm@b7eeb5ea204d9c9bd434e01162ae97629cf7fa36 # v2.4
         with:
           install: |
             Data::Dumper
@@ -85,13 +85,13 @@
           ]
 
     steps:
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # 
v5.0.0
-      - uses: 
shogo82148/actions-setup-perl@5796a908661aa68fc0a5b8f55c6791af2376d72e # v1.35.0
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
+      - uses: 
shogo82148/actions-setup-perl@a198315ec4e9244f206879ea7b63078003aec8a6 # v1.41.1
         with:
           perl-version: ${{ matrix.perl-version }}
       - run: perl -V
       - name: install cpan deps
-        uses: 
perl-actions/install-with-cpm@8b1a9840b26cc3885ae2889749a48629be2501b0 # v1.9
+        uses: 
perl-actions/install-with-cpm@b7eeb5ea204d9c9bd434e01162ae97629cf7fa36 # v2.4
         with:
           sudo: false
           install: |
@@ -118,7 +118,7 @@
         perl-version: [latest]
 
     steps:
-      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # 
v5.0.0
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
       - run: perl -V
       - run: perl Makefile.PL
       - run: make test
@@ -140,7 +140,7 @@
         perl-version: [latest]
 
     steps:
-      - uses: actions/checkout@master
+      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # 
v6.0.2
       - run: perl -V
       - run: perl Makefile.PL
       #- run: prove -vb t/*.t
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Cpanel-JSON-XS-4.40/Changes 
new/Cpanel-JSON-XS-4.41/Changes
--- old/Cpanel-JSON-XS-4.40/Changes     2025-09-08 13:53:39.000000000 +0200
+++ new/Cpanel-JSON-XS-4.41/Changes     2026-05-27 20:00:10.000000000 +0200
@@ -2,6 +2,14 @@
 
 TODO: http://stevehanov.ca/blog/index.php?id=104 compression
 
+4.41 2026-05-27 (rurban)
+       - Fix BOM-shift PV-corruption SIGABRT (CVE-2026-9516) (patch by Paul 
Johnson)
+        - Fix dupkeys_as_arrayref type confusion (CVE-2026-9334) (patch by 
Paul Johnson)
+        - Fix incr_parse single-quote string delimiter (GH #245, reported by
+          Paul Johnson)
+        - Fix a one-byte out-of-bounds heap read reachable via allow_barekey on
+          truncated input (GH #244, reported by Paul Johnson)
+
 4.40 2025-09-07 (rurban)
        - Fix CVE-2025-40929 overflow with overlong numbers, fuzzing only.
        - Detect more malformed numbers, with two decimal points.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Cpanel-JSON-XS-4.40/META.json 
new/Cpanel-JSON-XS-4.41/META.json
--- old/Cpanel-JSON-XS-4.40/META.json   2025-09-08 16:00:51.000000000 +0200
+++ new/Cpanel-JSON-XS-4.41/META.json   2026-05-27 20:00:40.000000000 +0200
@@ -100,7 +100,7 @@
          "url" : "https://github.com/rurban/Cpanel-JSON-XS";
       }
    },
-   "version" : "4.40",
+   "version" : "4.41",
    "x_contributors" : [
       "Ashley Willis <[email protected]>",
       "Chip Salzenberg <[email protected]>",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Cpanel-JSON-XS-4.40/META.yml 
new/Cpanel-JSON-XS-4.41/META.yml
--- old/Cpanel-JSON-XS-4.40/META.yml    2025-09-08 16:00:50.000000000 +0200
+++ new/Cpanel-JSON-XS-4.41/META.yml    2026-05-27 20:00:39.000000000 +0200
@@ -48,7 +48,7 @@
   bugtracker: https://github.com/rurban/Cpanel-JSON-XS/issues
   license: http://dev.perl.org/licenses/
   repository: https://github.com/rurban/Cpanel-JSON-XS
-version: '4.40'
+version: '4.41'
 x_contributors:
   - 'Ashley Willis <[email protected]>'
   - 'Chip Salzenberg <[email protected]>'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Cpanel-JSON-XS-4.40/SIGNATURE 
new/Cpanel-JSON-XS-4.41/SIGNATURE
--- old/Cpanel-JSON-XS-4.40/SIGNATURE   2025-09-08 16:00:52.000000000 +0200
+++ new/Cpanel-JSON-XS-4.41/SIGNATURE   2026-05-27 20:00:41.000000000 +0200
@@ -16,18 +16,18 @@
 
 SHA256 aac2b4bbaa7b93eaf72300f60e167a17e05adcd721087f735ba55d2900f31490 
.appveyor.yml
 SHA256 082201a3cbd62a55f2e58ffbb991c4b2bb806de0009bc9497ffcc07202f60855 
.github/FUNDING.yml
-SHA256 27d585f5789434eac4d99d9a66b143c3c76d27c7a622061922919e4f70f93887 
.github/workflows/testsuite.yml
+SHA256 a7081377424ff33494fd0e7fad1ce807657b29794dcca9dd238f30465579dad1 
.github/workflows/testsuite.yml
 SHA256 a3c34aba52e269e6cec558ecf9cff393138574189fdff26b183bee9cc2e0434f 
.travis.yml
 SHA256 c3f2a1a4f66382f796f71a571946722edba53cf3238152b26fd325f4c2f1a20f 
.whitesource
 SHA256 8de3540a3cd7ecc9a9dcb48975fe852c082fe17d4462f87bb72aa7cc47f083ad COPYING
-SHA256 ae4bb1db5a3fa099f35c9a5b79648fa98c3e17bc30d01fb66e55bb72344bed39 Changes
+SHA256 5f125d9df9f2de624ecb1de0f26f820e4b940c78d4fbbb52a22c849e57740e2e Changes
 SHA256 a5378ebe65273d49047a21e94af087f70a303793ffed2a695c800ed965ac185d 
MANIFEST
-SHA256 48ed49a7b028baa86cec0aa499e2448f68890cfa166db960b7fe316f85d16757 
META.json
-SHA256 0acb81669300bb982e04541b1063b424d334af2d2f7e0399fa596abd638161a4 
META.yml
+SHA256 2a1d33955bd96ad9effd5f0ec39d589ecb51b0c08a11c196567979ca8a85899e 
META.json
+SHA256 f7714a74c855e188aa85d5422a1438ff469796876a833a128cea8661684f641f 
META.yml
 SHA256 0fc7078919ff510137a654acc66bd68c971786cafd8ee621afae6d99321df4bb 
Makefile.PL
 SHA256 c62f5a06dffaa850fe7d55cad1c5ce3fbdb5504031b73b6043aa2974708c9293 README
-SHA256 5ff0b6a015d21a2344ec786a6f8faa7547346720cfa02a0526c9952b2211d903 XS.pm
-SHA256 5bf1c4bc302db55d0e23eeac0dc9c2b1f03bb76a6d6af0f87d496cbbf1527315 XS.xs
+SHA256 e7b813bc4beea51b9ad2bb35c5a97f22f103b01d1274e6f1b5b03711e857e927 XS.pm
+SHA256 1fd3cedb5e734964e9410b452c50858df494924994fcee092f0921e13aea6766 XS.xs
 SHA256 c95e4b970183cbd6d1ec9c88c37a80f12bd2b66ed6be3c008ffd578d2f622c01 
XS/Boolean.pm
 SHA256 20596259e7e399ed1984a469a9a907be878499406d5285a11f1ab98f93aff44f 
XS/Type.pm
 SHA256 2f34a530b7ce981b0df9aacd2b6944ccf74c4a7f8bb49fde57b342663e7feb26 
bin/cpanel_json_xs
@@ -46,7 +46,7 @@
 SHA256 e88b03d3c8c5c85d4fc2c086848efd7d0fd7b69f839cf0936a698af77a7a59b8 
t/09_pc_extra_number.t
 SHA256 f177821982876d02403298d44ffe4e2193fdcd70b76da252b055f3eab8dd3cdf 
t/104_sortby.t
 SHA256 e8bf435b08bfd00e6ab7f278c6ce68ef8691011b80615fa372961f2d807f5c76 
t/105_esc_slash.t
-SHA256 8549eb57bd2ac40cbe57d8441f9188210ef0894c76626e3052ac40302f4f52a4 
t/106_allow_barekey.t
+SHA256 d633b016ec1e792b84d4f53ac75166953a95298328185aaa08932dc7236a929b 
t/106_allow_barekey.t
 SHA256 16f34295a33f59b8fe7a4f70b701df03fc866d77eac300ca0503a98875675569 
t/107_allow_singlequote.t
 SHA256 f2047975a3b8392feb6a87d782ecc7746ae2117bde57f716cc90877c8850f2e0 
t/108_decode.t
 SHA256 e6f7738431bc8d77ad0b8ad2db9ab54426f7bbc86eb5f5794b1a4616f454baef 
t/109_encode.t
@@ -71,7 +71,7 @@
 SHA256 4f73fcceb31cfb06ac5110ec89107cc14302905061d28b0700d4673f654d5592 
t/16_tied.t
 SHA256 398e5ff51603a52de901f4c1934265601e226d05b88ac604dcd9e9d179a0344f 
t/17_relaxed.t
 SHA256 1585a6aecec5c73b7a6f70982b3bcc1edc1d63ca55467223ab0d6f0956254bc4 
t/18_json_checker.t
-SHA256 9f9006c1f925d9ace7d1621e1479c21f9b50459ab57aa0046209fed2b3e66530 
t/19_incr.t
+SHA256 d89efdc36f0e1b2be955c91f320ef73cebb9aa9bc63b0b309e31ed58590cf0ae 
t/19_incr.t
 SHA256 dde73ed3cfc0e28d064f61fc08871accf88b780aee06a3cb0040f59f04c1ff36 
t/20_faihu.t
 SHA256 56e11977ce3d544f8c8e62a38cbcc4f58f7f1d53b71918f803536acd62122713 
t/20_unknown.t
 SHA256 388f8e0f0e41c9921aedc67313f8b89bdd08b95ced0dba242986d3b76d9a1688 
t/21_evans.t
@@ -79,9 +79,9 @@
 SHA256 2a6506fb07b27b1fef52b251d3876d23bd572596ff487d37c2f6597be554836c 
t/23_array_ctx.t
 SHA256 a8dfccba0b60b0fc91812fcfd96656e993abb74970509926d738c67a58641f01 
t/24_freeze_recursion.t
 SHA256 d6e46428bf221ea9bca6f8c0a9d14ee76305d91e01d9946b570aac125d392ba8 
t/25_boolean.t
-SHA256 e7297f97fe3fea65c865658675b72e667b37b201e7fec8b8128f2006f8999d86 
t/26_duplicate.t
+SHA256 9f6e48c5519a1fcf8be484e8c231138b66054027ac9bdca7c97672864a13db35 
t/26_duplicate.t
 SHA256 814438975ce229d4ff0deb6a9aef967b7f088e03894b8b8e6ca1ccfb6d953117 
t/30_jsonspec.t
-SHA256 cf2181a691d5e1862d71e4e902928a0d899b9671e3633584efa8ae3f5cc0d174 
t/31_bom.t
+SHA256 bf3103d7dcb5e6fe0603d9ab640c51652eb1c41393a9b5fc06db80b6ae7a5c95 
t/31_bom.t
 SHA256 59c743137453c8c4e9e785a15dcd057b0209d5ce160d683d7ab416dc37a92b6d 
t/52_object.t
 SHA256 3b9ce402e2d0cae8a525df4beca05f2656ba5cf02b074d02fd690fe97773d2d7 
t/53_readonly.t
 SHA256 a08be137b59c9cd58410bc41969e1e9e9fa2159469523394b6bfd0c798c00908 
t/54_stringify.t
@@ -446,15 +446,15 @@
 SHA256 aca6f846869ab2e4881e807739086e1535b1438bd0e23d7a021360742736a6a9 
xt/pod.t
 -----BEGIN PGP SIGNATURE-----
 
-iQGzBAEBAwAdFiEEOKQWew22nknF9yFsuMKIZqsnp6IFAmi+4ZMACgkQuMKIZqsn
-p6KuoAwAr4PmFqtyelLfhw0SXAmFBDuysAAYlLKb/bq0vrWM8xsrhRnIYZF4bAPw
-eEeM8nxMigZ3Dn1lFnK90q2+qtsU324zOzH57cGOBCCy+chL3rOSTSmGHed4badZ
-G6ocNL4E+1NUHz3bkTmjflsjiScxemCgUz1VSRE+Ob57rWnm9mtsofdVRViDwcrb
-kiLH5WwyPZYtu4KByxGs64nijg6WE3M12dDsvrm1f0dKbq4Y+gCCDKUn6IreOfRo
-ylr4/be0jJeSKfH+2riJ33cNtO8JBl3o9//p1eXM3k4gQgujs1HDiJO64j+u+vbq
-gW94FpHzOmoffufp9izq5sidgXj1kfbonFXw2mB/odVzjsHh05HBqeAZ6s4aTWFL
-O3TAKYeZYrE3fqS5y2DIoSC6gCqIQiKUy/00loG2vq6vVzjF+DbetDBiNc0WDOop
-eDkxmipMmVTA+ZUQvWtQAUUhP42QcGv5Am2OeeieKB2SuQ1BHl5hRcz9PE5FGldR
-ZrWYP67e
-=Z+fz
+iQGzBAEBAwAdFiEEOKQWew22nknF9yFsuMKIZqsnp6IFAmoXMUkACgkQuMKIZqsn
+p6IH/AwAlP87Q9QalXZtyOrJ6NegS/x+OwWp6/GprVe4mp9Mqosy9WEfcjf2zt6v
+0qEHQSYW3UOxTg0pjcM4Aw/wJWWDZzZRn9EsSnX2Gc2OJOJdQyfobYQgMOFxdni+
+T9xuHGpjtwsqk2oOzRVptrXa1bxTfWn0AJgHc3VAj+m3J3wk7mA3amd7a+5J+bL/
+dR2tq6iAEgPzjplwzytHkfPyXH3YdePXSM7xbzT5agzxgxZKiol6mwwLPwZe24x+
+xf67L9ksOY8dZzKMb/cQW/1CjoqJzwobxMNfBk5+PG8FBa9npPSvModkdI3Ty4Rh
+DL7GB7AIyhyDp0aSFrfpEdhsoySJK6m1rfRdk5qpYZaSdC8XY5MtD2a/eP747Jw0
+yy5rx7h1UqOuZipxd+7BW4izyas9FW37o/AEQHAv8AWN0MYTdq38e2W7qLjRb+Bb
+/ShHY3PdwkWOsEsfm1hj08LTEMLY/6LksSEqSZuTGivPsI4PT3s/jQQ+BldhIXRv
+yn0x3SIH
+=PtUe
 -----END PGP SIGNATURE-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Cpanel-JSON-XS-4.40/XS.pm 
new/Cpanel-JSON-XS-4.41/XS.pm
--- old/Cpanel-JSON-XS-4.40/XS.pm       2025-09-08 13:53:39.000000000 +0200
+++ new/Cpanel-JSON-XS-4.41/XS.pm       2026-05-27 20:00:10.000000000 +0200
@@ -1,5 +1,5 @@
 package Cpanel::JSON::XS;
-our $VERSION = '4.40';
+our $VERSION = '4.41';
 our $XS_VERSION = $VERSION;
 # $VERSION = eval $VERSION;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Cpanel-JSON-XS-4.40/XS.xs 
new/Cpanel-JSON-XS-4.41/XS.xs
--- old/Cpanel-JSON-XS-4.40/XS.xs       2025-09-08 13:53:39.000000000 +0200
+++ new/Cpanel-JSON-XS-4.41/XS.xs       2026-05-27 20:00:10.000000000 +0200
@@ -366,7 +366,6 @@
 #define F_REQUIRE_TYPES   0x01000000UL
 #define F_TYPE_ALL_STRING 0x02000000UL
 #define F_DUPKEYS_AS_AREF 0x04000000UL
-#define F_DUPKEYS_FIRST   0x08000000UL /* internal only */
 #define F_HOOK            0x80000000UL /* some hooks exist, so slow-path 
processing */
 
 #define F_PRETTY    F_INDENT | F_SPACE_BEFORE | F_SPACE_AFTER
@@ -426,7 +425,9 @@
   INCR_M_BS,     /* inside backslash */
   INCR_M_C0,     /* inside comment in initial whitespace sequence */
   INCR_M_C1,     /* inside comment in other places */
-  INCR_M_JSON    /* outside anything, count nesting */
+  INCR_M_JSON,   /* outside anything, count nesting */
+  INCR_M_SQSTR,  /* inside single-quoted string (allow_singlequote) */
+  INCR_M_SQBS    /* inside backslash inside single-quoted string */
 };
 
 #define INCR_DONE(json) ((json)->incr_nest <= 0 && (json)->incr_mode == 
INCR_M_JSON)
@@ -3924,7 +3925,7 @@
   int allow_barekey = dec->json.flags & F_ALLOW_BAREKEY;
   int allow_dupkeys = dec->json.flags & F_ALLOW_DUPKEYS;
   int dupkeys_as_arrayref = dec->json.flags & F_DUPKEYS_AS_AREF;
-  int dupkeys_first = dec->json.flags & F_DUPKEYS_FIRST;
+  HV *dupkeys_seen = NULL;
   char endstr = '"';
 
   DEC_INC_DEPTH;
@@ -3978,9 +3979,11 @@
           for (;;)
             {
               /* the >= 0x80 is false on most architectures */
-              if (UNLIKELY(!is_bare &&
+              /* !*p must be checked before !is_bare: a bare-key scan has no
+                 bounds guard and would run one byte past dec->end otherwise */
+              if (UNLIKELY(!*p || (!is_bare &&
                   (p == e || *p < 0x20 || *(U8*)p >= 0x80 || *p == '\\'
-                   || allow_squote)))
+                   || allow_squote))))
                 {
                   /* slow path, back up and use decode_str */
                   /* utf8 hash keys are handled here */
@@ -3996,16 +3999,21 @@
                       // extend the value to arrayref or push
                       old_value = HeVAL(hv_fetch_ent (hv, keysv, 0, 0));
                       SvREFCNT_inc (old_value);
-                      if (dupkeys_first) {
-                        AV *av = newAV ();
-                        av_extend (av, 2);
-                        if (av_store(av, 0, old_value))
-                          old_value = newRV ((SV*)av);
-                      } else if (SvTYPE (old_value) != SVt_RV &&
-                                 SvTYPE (SvRV (old_value)) != SVt_PVAV) {
-                        // not an AvREF
-                        ERR ("Invalid dupkeys_as_arrayref hash key");
-                      }
+                      if (!dupkeys_seen
+                          || !hv_exists_ent (dupkeys_seen, keysv, 0))
+                        {
+                          AV *av = newAV ();
+                          av_extend (av, 2);
+                          if (av_store (av, 0, old_value))
+                            old_value = newRV ((SV*)av);
+                          if (!dupkeys_seen)
+                            {
+                              dupkeys_seen = newHV ();
+                              sv_2mortal ((SV *)dupkeys_seen);
+                            }
+                          (void)hv_store_ent (dupkeys_seen, keysv,
+                                              newSV (0), 0);
+                        }
                     } // else overwrite it below
                   }
                   decode_ws (dec);
@@ -4029,11 +4037,6 @@
                     {
                       av_push ((AV*)SvRV (old_value), value);
                       (void)hv_store_ent (hv, keysv, old_value, 0);
-                      if (dupkeys_first)
-                        {
-                          dupkeys_first = 0;
-                          dec->json.flags &= ~F_DUPKEYS_FIRST;
-                        }
                     }
                   else
                     {
@@ -4067,16 +4070,21 @@
                       SV** rv = hv_fetch (hv, key, len, 0);
                       old_value = *rv;
                       SvREFCNT_inc (old_value);
-                      if (dupkeys_first) {
-                        AV *av = newAV ();
-                        av_extend (av, 2);
-                        if (av_store(av, 0, old_value))
-                          old_value = newRV ((SV*)av);
-                      } else if (SvTYPE (old_value) != SVt_RV &&
-                                 SvTYPE (SvRV (old_value)) != SVt_PVAV) {
-                        // not an AvREF
-                        ERR ("Invalid dupkeys_as_arrayref hash key");
-                      }
+                      if (!dupkeys_seen
+                          || !hv_exists (dupkeys_seen, key, len))
+                        {
+                          AV *av = newAV ();
+                          av_extend (av, 2);
+                          if (av_store (av, 0, old_value))
+                            old_value = newRV ((SV*)av);
+                          if (!dupkeys_seen)
+                            {
+                              dupkeys_seen = newHV ();
+                              sv_2mortal ((SV *)dupkeys_seen);
+                            }
+                          (void)hv_store (dupkeys_seen, key, len,
+                                          newSV (0), 0);
+                        }
                     } // else overwrite it below
                   }
 
@@ -4101,11 +4109,6 @@
                     {
                       av_push ((AV*)SvRV (old_value), value);
                       hv_store_str (aTHX_ hv, key, len, old_value);
-                      if (dupkeys_first)
-                        {
-                          dupkeys_first = 0;
-                          dec->json.flags &= ~F_DUPKEYS_FIRST;
-                        }
                     }
                   else
                     {
@@ -4503,8 +4506,6 @@
         converted = 1 + (json->flags & F_UTF8);
         json->flags |= F_UTF8;
         offset = 3;
-        SvPV_set(string, SvPVX_mutable (string) + 3);
-        SvCUR_set(string, len - 3);
         SvUTF8_on(string);
         /* omitting the endian name will skip the BOM in the result */
       } else if (len >= 4 && memEQc(s, UTF32BOM)) {
@@ -4539,7 +4540,7 @@
     SvGROW (string, SvCUR (string) + 1);
 
   dec.json  = *json;
-  dec.cur   = SvPVX (string);
+  dec.cur   = SvPVX (string) + offset;
   dec.end   = SvEND (string);
   dec.err   = 0;
   dec.depth = 0;
@@ -4553,10 +4554,11 @@
   sv = decode_sv (aTHX_ &dec, typesv);
 
   if (offset_return) {
-    if (dec.cur < SvPVX (string) || dec.cur > SvEND (string))
+    char *base = SvPVX (string) + offset;
+    if (dec.cur < base || dec.cur > SvEND (string))
       *offset_return = 0;
     else
-      *offset_return = dec.cur - SvPVX (string);
+      *offset_return = dec.cur - base;
   }
 
   if (!(offset_return || !sv))
@@ -4571,12 +4573,6 @@
           sv = NULL;
         }
     }
-  /* restore old utf8 string with BOM */
-  if (UNLIKELY(offset)) {
-    SvPV_set(string, SvPVX_mutable (string) - offset);
-    SvCUR_set(string, len);
-  }
-
   if (!sv)
     {
       SV *uni = sv_newmortal ();
@@ -4667,6 +4663,15 @@
             self->incr_mode = INCR_M_STR;
             goto incr_m_str;
 
+          /* skip a single char inside a single-quoted string (for 
\\-processing) */
+          case INCR_M_SQBS:
+            if (!*p)
+              goto interrupt;
+
+            ++p;
+            self->incr_mode = INCR_M_SQSTR;
+            goto incr_m_sqstr;
+
           /* inside #-style comments */
           case INCR_M_C0:
           case INCR_M_C1:
@@ -4717,6 +4722,37 @@
                 ++p;
               }
 
+          /* inside a single-quoted string (allow_singlequote) */
+          case INCR_M_SQSTR:
+          incr_m_sqstr:
+            for (;;)
+              {
+                if (*p == '\'')
+                  {
+                    ++p;
+                    self->incr_mode = INCR_M_JSON;
+
+                    if (!self->incr_nest)
+                      goto interrupt;
+
+                    goto incr_m_json;
+                  }
+                else if (*p == '\\')
+                  {
+                    ++p;
+
+                    if (!*p)
+                      {
+                        self->incr_mode = INCR_M_SQBS;
+                        goto interrupt;
+                      }
+                  }
+                else if (!*p)
+                  goto interrupt;
+
+                ++p;
+              }
+
           /* after initial ws, outside string */
           case INCR_M_JSON:
           incr_m_json:
@@ -4743,6 +4779,14 @@
                       self->incr_mode = INCR_M_STR;
                       goto incr_m_str;
 
+                    case '\'':
+                      if (self->flags & F_ALLOW_SQUOTE)
+                        {
+                          self->incr_mode = INCR_M_SQSTR;
+                          goto incr_m_sqstr;
+                        }
+                      break;
+
                     case '[':
                     case '{':
                     case '(':
@@ -4886,7 +4930,7 @@
         # Turning on DUPKEYS_AS_AREF also turns on ALLOW_DUPKEYS
         # But turning off DUPKEYS_AS_AREF does not
         if (ix == F_DUPKEYS_AS_AREF && enable != 0)
-          self->flags |= F_ALLOW_DUPKEYS | F_DUPKEYS_FIRST;
+          self->flags |= F_ALLOW_DUPKEYS;
         XPUSHs (ST (0));
 
 void get_ascii (JSON *self)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Cpanel-JSON-XS-4.40/t/106_allow_barekey.t 
new/Cpanel-JSON-XS-4.41/t/106_allow_barekey.t
--- old/Cpanel-JSON-XS-4.40/t/106_allow_barekey.t       2023-02-22 
12:08:39.000000000 +0100
+++ new/Cpanel-JSON-XS-4.41/t/106_allow_barekey.t       2026-05-27 
20:00:10.000000000 +0200
@@ -1,5 +1,5 @@
 
-use Test::More tests => 6;
+use Test::More tests => 9;
 use strict;
 use utf8;
 use Cpanel::JSON::XS;
@@ -25,3 +25,21 @@
   is(utf8::is_utf8($k[0]), 1, 'keep utf8 as bare key');
 }
 
+# GH #244: truncated bare-key input must not cause a one-byte OOB heap read.
+# The fast-scan loop previously ran p past dec->end before checking for ':'.
+{
+  my $coder = Cpanel::JSON::XS->new->allow_barekey(1);
+  my $err;
+
+  # bare key with no ':' or value — truncated at key end
+  eval { $coder->decode('{a') };
+  $err = $@;
+  ok($err, 'truncated bare key {a errors');
+  like($err, qr/expected|truncated|':'/i, 'truncated bare key: sensible 
error');
+
+  # bare key followed by truncated value
+  eval { $coder->decode('{ab:') };
+  $err = $@;
+  ok($err, 'truncated bare key {ab: errors');
+}
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Cpanel-JSON-XS-4.40/t/19_incr.t 
new/Cpanel-JSON-XS-4.41/t/19_incr.t
--- old/Cpanel-JSON-XS-4.40/t/19_incr.t 2022-05-03 13:12:46.000000000 +0200
+++ new/Cpanel-JSON-XS-4.41/t/19_incr.t 2026-05-27 20:00:10.000000000 +0200
@@ -2,7 +2,7 @@
 
 use strict;
 no warnings;
-use Test::More $] < 5.008 ? (tests => 39) : (tests => 702);
+use Test::More $] < 5.008 ? (tests => 39) : (tests => 713);
 
 use Cpanel::JSON::XS;
 
@@ -110,3 +110,51 @@
    is (encode_json($coder->incr_parse($text)), '[1]', "incr_parse1");
    is (encode_json($coder->incr_parse), '[5]', "incr_parse2");
 }
+
+# allow_singlequote: } inside single-quoted string must not close the object
+{
+   my $coder = Cpanel::JSON::XS->new->allow_singlequote(1);
+
+   # feed partial input: the } is inside the single-quoted value, must not 
trigger done
+   ok (!defined $coder->incr_parse("{'a':'}'"), "sqstr-incr: } inside 
single-quote does not close object");
+
+   # complete the object
+   my $r = $coder->incr_parse("}");
+   ok (defined $r, "sqstr-incr: object completes after closing brace");
+   is_deeply ($r, {a => "}"}, "sqstr-incr: decoded value correct");
+}
+
+# allow_singlequote: chunked streaming with structural chars inside 
single-quoted string
+{
+   my $coder = Cpanel::JSON::XS->new->allow_singlequote(1);
+
+   # feed one byte at a time to exercise every state transition
+   my $json = "{'x':'}[{'}";
+   my $r;
+   for my $ch (split //, $json) {
+      $r = $coder->incr_parse($ch);
+   }
+   ok (defined $r, "sqstr-incr chunked: defined result");
+   is_deeply ($r, {x => "}[{"}, "sqstr-incr chunked: decoded value correct");
+}
+
+# allow_singlequote: backslash inside single-quoted string does not cause 
premature end
+{
+   my $coder = Cpanel::JSON::XS->new->allow_singlequote(1);
+
+   ok (!defined $coder->incr_parse("{'k':'a\\'"), "sqstr-incr BS: partial with 
escaped quote waits");
+   my $r = $coder->incr_parse("b'}");
+   ok (defined $r, "sqstr-incr BS: completes after full input");
+   is_deeply ($r, {k => "a'b"}, "sqstr-incr BS: decoded value correct");
+}
+
+# allow_singlequote: single-quoted array element containing structural chars
+{
+   my $coder = Cpanel::JSON::XS->new->allow_singlequote(1);
+   my $r;
+   $r = $coder->incr_parse("['he");
+   ok (!defined $r, "sqstr-incr array: partial waits");
+   $r = $coder->incr_parse("llo}']");
+   ok (defined $r, "sqstr-incr array: completes");
+   is_deeply ($r, ['hello}'], "sqstr-incr array: value correct");
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Cpanel-JSON-XS-4.40/t/26_duplicate.t 
new/Cpanel-JSON-XS-4.41/t/26_duplicate.t
--- old/Cpanel-JSON-XS-4.40/t/26_duplicate.t    2022-05-06 07:40:07.000000000 
+0200
+++ new/Cpanel-JSON-XS-4.41/t/26_duplicate.t    2026-05-27 20:00:10.000000000 
+0200
@@ -1,5 +1,5 @@
 use strict;
-use Test::More tests => 12;
+use Test::More tests => 17;
 use Cpanel::JSON::XS;
 
 my $json = Cpanel::JSON::XS->new;
@@ -47,3 +47,33 @@
     'dupkeys_as_arrayref to []');
 is (encode_json ($json->decode ('{"a":["b","c"],"a":["c"]}')), 
'{"a":[["b","c"],["c"]]}',
     'dupkeys_as_arrayref to [[]]');
+
+# fast path: short ASCII keys
+is_deeply ($json->decode ('{"a":1,"a":2,"b":3,"b":4}'),
+           { a => [1, 2], b => [3, 4] },
+           'dupkeys_as_arrayref: two distinct duplicated keys, fast path');
+
+# slow path: keys longer than 24 bytes force _decode_str
+{
+  my $k1 = 'a' x 30;
+  my $k2 = 'b' x 30;
+  my $in = qq({"$k1":1,"$k1":2,"$k2":3,"$k2":4});
+  is_deeply ($json->decode ($in),
+             { $k1 => [1, 2], $k2 => [3, 4] },
+             'dupkeys_as_arrayref: two distinct duplicated keys, slow path');
+}
+
+# three distinct duplicated keys - confirms fix past the first transition
+is_deeply ($json->decode ('{"a":1,"a":2,"b":3,"b":4,"c":5,"c":6}'),
+           { a => [1, 2], b => [3, 4], c => [5, 6] },
+           'dupkeys_as_arrayref: three distinct duplicated keys');
+
+# triple duplicate of a second key - further dups should append, not re-wrap
+is_deeply ($json->decode ('{"a":1,"a":2,"b":3,"b":4,"b":5}'),
+           { a => [1, 2], b => [3, 4, 5] },
+           'dupkeys_as_arrayref: triple dup of second key');
+
+# pre-existing arrayref values combine correctly across multiple keys
+is_deeply ($json->decode ('{"a":["x"],"a":"y","b":["p"],"b":"q"}'),
+           { a => [['x'], 'y'], b => [['p'], 'q'] },
+           'dupkeys_as_arrayref: existing array values, two distinct keys');
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Cpanel-JSON-XS-4.40/t/31_bom.t 
new/Cpanel-JSON-XS-4.41/t/31_bom.t
--- old/Cpanel-JSON-XS-4.40/t/31_bom.t  2022-05-03 13:12:46.000000000 +0200
+++ new/Cpanel-JSON-XS-4.41/t/31_bom.t  2026-05-27 20:00:10.000000000 +0200
@@ -2,7 +2,7 @@
 #
 # https://tools.ietf.org/html/rfc7159#section-8.1
 # JSON text SHALL be encoded in UTF-8, UTF-16, or UTF-32.
-use Test::More ($] >= 5.008) ? (tests => 9) : (skip_all => "needs 5.8");;
+use Test::More ($] >= 5.008) ? (tests => 9 + 6) : (skip_all => "needs 5.8");;
 use Cpanel::JSON::XS;
 use Encode; # Currently required for <5.20
 use charnames qw(:short);
@@ -41,3 +41,50 @@
 ok(eval { $j->decode($as_json) }, 'can decode again');
 ok(eval { $j->decode("\x{feff}" . $as_json) }, 'can decode with BOM');
 ok(eval { $j->decode($as_json) }, 'can decode original');
+
+# Tests by Paul Johnson:
+
+# Assert the caller's input SV is preserved bit-for-bit across a decode
+# call whose filter callback throws, for each of the three croak-reachable
+# callback sites (filter_json_object, filter_json_single_key_object, and
+# allow_tags + THAW), with a leading UTF-8 BOM on the input.
+
+my $bom = "\xef\xbb\xbf";
+
+sub assert_preserved {
+  my ($name, $payload, $setup) = @_;
+  my $original = $bom . $payload;
+  my $s        = $bom . $payload;
+  my $j        = Cpanel::JSON::XS->new;
+  $setup->($j);
+  eval { $j->decode ($s) };
+  ok ($@, "$name: callback threw");
+  # The BOM-decode path legitimately sets SvUTF8 on the caller's SV.
+  # We care about the underlying byte sequence (no SvPVX shift), not
+  # the flag, so clear SvUTF8 on a copy and compare raw bytes.
+  my $copy = $s;
+  Encode::_utf8_off ($copy);
+  is ($copy, $original, "$name: SV bytes preserved across throw");
+}
+
+assert_preserved (
+  "filter_json_object",
+  '{}',
+  sub { $_[0]->filter_json_object (sub { die "boom\n" }) },
+);
+
+assert_preserved (
+  "filter_json_single_key_object",
+  '{"k":1}',
+  sub { $_[0]->filter_json_single_key_object (k => sub { die "boom\n" }) },
+);
+
+{
+  package BomFilterCorruption::Thaw;
+  sub THAW { die "boom\n" }
+}
+assert_preserved (
+  "allow_tags THAW",
+  '("BomFilterCorruption::Thaw")[]',
+  sub { $_[0]->allow_tags (1) },
+);

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.Axe8ku/_old  2026-06-03 20:30:27.237191143 +0200
+++ /var/tmp/diff_new_pack.Axe8ku/_new  2026-06-03 20:30:27.245191475 +0200
@@ -1,6 +1,6 @@
-mtime: 1757351017
-commit: 8bf9656a284497e3756b5e3b0057cec36ffe9828d8cace5e2beea00487cc3673
-url: https://src.opensuse.org/perl/perl-Cpanel-JSON-XS.git
-revision: 8bf9656a284497e3756b5e3b0057cec36ffe9828d8cace5e2beea00487cc3673
+mtime: 1779958415
+commit: 06d5182b5f0a2350b0d78e9abcc91c7a661926ee49d2df426628bbf170461d5d
+url: https://src.opensuse.org/perl/perl-Cpanel-JSON-XS
+revision: 06d5182b5f0a2350b0d78e9abcc91c7a661926ee49d2df426628bbf170461d5d
 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-05-28 10:53:35.000000000 +0200
@@ -0,0 +1 @@
+.osc

Reply via email to