commit:     813018bb1e76ab055fdfb492f847446bf09600d5
Author:     Andrei Horodniceanu <a.horodniceanu <AT> proton <DOT> me>
AuthorDate: Fri Mar 28 07:21:03 2025 +0000
Commit:     Arthur Zamarin <arthurzam <AT> gentoo <DOT> org>
CommitDate: Sun Mar 30 16:21:56 2025 +0000
URL:        https://gitweb.gentoo.org/proj/javatoolkit.git/commit/?id=813018bb

cvv.py: Don't fail when MANIFEST.MF isn't utf-8 encoded

Use the UTF-8 replacement character rather then raising and exception
when encountering invalid unicode in MANIFEST.MF, which is against the
standard yet some programs like the maven-jar-plugin embed the
author's name literally without properly encoding it first.

Closes: https://bugs.gentoo.org/952052
Signed-off-by: Andrei Horodniceanu <a.horodniceanu <AT> proton.me>
Closes: https://github.com/gentoo/javatoolkit/pull/4
Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org>

 NEWS                                     |   3 +
 src/javatoolkit/cvv.py                   |   7 +-
 src/test/res/maven-generated-manifest.mf | 169 +++++++++++++++++++++++++++++++
 src/test/test_cvv.py                     |  24 +++++
 4 files changed, 202 insertions(+), 1 deletion(-)

diff --git a/NEWS b/NEWS
index 983670a..bef013a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,6 @@
+0.6.9 (??)
+- Prevent failure when MANIFEST.MF isn't UTF-8 encoded (bug #952052)
+
 0.6.8 (08 Mar 2025)
 - Change output format
 - Add tests

diff --git a/src/javatoolkit/cvv.py b/src/javatoolkit/cvv.py
index 0ca8023..cba3d0f 100644
--- a/src/javatoolkit/cvv.py
+++ b/src/javatoolkit/cvv.py
@@ -100,7 +100,12 @@ class CVVMagic:
             pass
         else:
             with manifest:
-                lines = [line.decode('utf-8').rstrip() for line in 
manifest.readlines()]
+                def decode_line(line: bytes) -> str:
+                    # The Manifest spec requires that the file is utf-8 
encoded.
+                    # Unfortunately, stuff like the maven-jar-plugin can 
generate
+                    # an invalid manifest when it blindly copies the author 
name
+                    return line.decode('utf-8', 'replace').rstrip('\r\n')
+                lines = [decode_line(line) for line in manifest.readlines()]
                 is_multirelease = 'Multi-Release: true' in lines
 
         invalid_version_dirs: set[str] = set()

diff --git a/src/test/res/maven-generated-manifest.mf 
b/src/test/res/maven-generated-manifest.mf
new file mode 100644
index 0000000..09bd061
--- /dev/null
+++ b/src/test/res/maven-generated-manifest.mf
@@ -0,0 +1,169 @@
+Manifest-Version: 1.0
+Created-By: Maven JAR Plugin 3.4.0
+Multi-Release: true
+Build-Jdk-Spec: 21
+Specification-Title: Maven Artifact Resolver API
+Specification-Version: 1.9
+Specification-Vendor: The Apache Software Foundation
+Implementation-Title: Maven Artifact Resolver API
+Implementation-Version: 1.9.22
+Implementation-Vendor: The Apache Software Foundation
+Automatic-Module-Name: org.apache.maven.resolver
+Bundle-Description: The application programming interface for the reposi
+ tory system.
+Bundle-Developers: khmarbaise;email="[email protected]";name="Karl H
+ einz Marbaise";roles="PMC Chair";timezone="+1",aheritier;email="aheriti
+ [email protected]";name="Arnaud Héritier";roles="PMC Member";timezone="+1"
+ ,andham;email="[email protected]";name="Anders Hammar";roles="PMC Membe
+ r";timezone="+1",baerrach;email="[email protected]";name="Barrie Trel
+ oar";roles="PMC Member";timezone="Australia/Adelaide",bimargulies;email
+ ="[email protected]";name="Benson Margulies";roles="PMC Member";ti
+ mezone="America/New_York",bmarwell;email="[email protected]";name="Be
+ njamin Marwell";organization=ASF;roles="PMC Member";timezone="Europe/Be
+ rlin",brianf;email="[email protected]";name="Brian Fox";organization=So
+ natype;roles="PMC Member";timezone=-5,cstamas;email="[email protected]
+ ";name="Tamas Cservenak";roles="PMC Member";timezone="+1",dennisl;email
+ ="[email protected]";name="Dennis Lundberg";organization=ASF;roles="PM
+ C Member";timezone="+1",dkulp;email="[email protected]";name="Daniel Kul
+ p";organization=ASF;roles="PMC Member";timezone=-5,evenisse;email="even
+ [email protected]";name="Emmanuel Venisse";organization=ASF;roles="PMC Me
+ mber";timezone="+1",gboue;email="[email protected]";name="Guillaume Bou�
+ �";roles="PMC Member";timezone="Europe/Paris",gnodet;email="gnodet@apac
+ he.org";name="Guillaume Nodet";organization="Red Hat";roles="PMC Member
+ ";timezone="Europe/Paris",henning;email="[email protected]";name="Henn
+ ing Schmiedehausen";organization=ASF;roles="PMC Member";timezone="Ameri
+ ca/Los_Angeles",hboutemy;email="[email protected]";name="Hervé Boute
+ my";organization=ASF;roles="PMC Member";timezone="Europe/Paris",ifedore
+ nko;email="[email protected]";name="Igor Fedorenko";organization=Sona
+ type;roles="PMC Member";timezone=-5,jvanzyl;email="[email protected]";nam
+ e="Jason van Zyl";roles="PMC Member";timezone=-5,krosenvold;email="kros
+ [email protected]";name="Kristian Rosenvold";roles="PMC Member";timezon
+ e="+1",kwin;email="[email protected]";name="Konrad Windszus";organization
+ ="Cognizant Netcentric";roles="PMC Member";timezone="Europe/Berlin",mkl
+ eint;name="Milos Kleint";roles="PMC Member",mthmulders;email="mthmulder
+ [email protected]";name="Maarten Mulders";organization="Info Support";roles=
+ "PMC Member";timezone="Europe/Amsterdam",olamy;email="[email protected]"
+ ;name="Olivier Lamy";roles="PMC Member";timezone="Australia/Brisbane",m
+ ichaelo;email="[email protected]";name="Michael Osipov";roles="PMC Me
+ mber";timezone="Europe/Berlin",rfscholte;email="[email protected]";n
+ ame="Robert Scholte";roles="PMC Member";timezone="Europe/Amsterdam",rgo
+ ers;email="[email protected]";name="Ralph Goers";organization=Intuit;ro
+ les="PMC Member";timezone=-8,sjaranowski;email="[email protected]"
+ ;name="Slawomir Jaranowski";roles="PMC Member";timezone="Europe/Warsaw"
+ ,stephenc;email="[email protected]";name="Stephen Connolly";roles="PM
+ C Member";timezone=0,slachiewicz;email="[email protected]";name="S
+ ylwester Lachiewicz";roles="PMC Member";timezone="Europe/Warsaw",strube
+ rg;email="[email protected]";name="Mark Struberg";roles="PMC Member",
+ tibordigana;email="[email protected]";name="Tibor Digaňa";roles="
+ PMC Member";timezone="Europe/Bratislava",vsiveton;email="vsiveton@apach
+ e.org";name="Vincent Siveton";organization=ASF;roles="PMC Member";timez
+ one=-5,wfay;email="[email protected]";name="Wayne Fay";organization=ASF;r
+ oles="PMC Member";timezone=-6,adangel;email="[email protected]";name="
+ Andreas Dangel";roles=Committer;timezone="Europe/Berlin",bdemers;email=
+ "[email protected]";name="Brian Demers";organization=Sonatype;roles=Co
+ mmitter;timezone=-5,bellingard;name="Fabrice Bellingard";roles=Committe
+ r,bentmann;email="[email protected]";name="Benjamin Bentmann";organiz
+ ation=Sonatype;roles=Committer;timezone="+1",chrisgwarp;email="chrisgwa
+ [email protected]";name="Chris Graham";roles=Committer;timezone="Australia/
+ Melbourne",dantran;email="[email protected]";name="Dan Tran";roles=Com
+ mitter;timezone=-8,dbradicich;email="[email protected]";name="Damia
+ n Bradicich";organization=Sonatype;roles=Committer;timezone=-5,brett;em
+ ail="[email protected]";name="Brett Porter";organization=ASF;roles=Commi
+ tter;timezone="+10",dfabulich;email="[email protected]";name="Daniel
+  Fabulich";roles=Committer;timezone=-8,eolivelli;email="eolivelli@apach
+ e.org";name="Enrico Olivelli";organization=Diennea;roles=Committer;time
+ zone="Europe/Rome",fgiust;email="[email protected]";name="Fabrizio Gius
+ tina";organization=openmind;roles=Committer;timezone="+1",godin;email="
+ [email protected]";name="Evgeny Mandrikov";organization=SonarSource;role
+ s=Committer;timezone="+3",handyande;email="[email protected]";name="
+ Andrew Williams";roles=Committer;timezone=0,imod;email="[email protected]
+ ";name="Dominik Bartholdi";roles=Committer;timezone="Europe/Zurich",jje
+ nsen;name="Jeff Jensen";roles=Committer,ltheussl;email="ltheussl@apache
+ .org";name="Lukas Theussl";roles=Committer;timezone="+1",markh;email="m
+ [email protected]";name="Mark Hobson";roles=Committer;timezone=0,martinka
+ nters;email="[email protected]";name="Martin Kanters";organizati
+ on=JPoint;roles=Committer;timezone="Europe/Amsterdam",mauro;name="Mauro
+  Talevi";roles=Committer,mfriedenhagen;email="[email protected]"
+ ;name="Mirko Friedenhagen";roles=Committer;timezone="+1",mmoser;email="
+ [email protected]";name="Manfred Moser";roles=Committer;timezone=-8,nic
+ olas;name="Nicolas de Loof";roles=Committer,oching;name="Maria Odea B. 
+ Ching";roles=Committer,pgier;email="[email protected]";name="Paul Gier";
+ organization="Red Hat";roles=Committer;timezone=-6,ptahchiev;email="pta
+ [email protected]";name="Petar Tahchiev";roles=Committer;timezone="+2",
+ rafale;email="[email protected]";name="Raphaël Piéroni";organization=
+ Dexem;roles=Committer;timezone="+1",schulte;email="[email protected]";
+ name="Christian Schulte";roles=Committer;timezone="Europe/Berlin",snico
+ ll;email="[email protected]";name="Stephane Nicoll";roles=Committer;ti
+ mezone="+1",simonetripodi;email="[email protected]";name="Simone
+  Tripodi";roles=Committer;timezone="+1",sor;email="[email protected]";name
+ ="Christian Stein";roles=Committer;timezone="Europe/Berlin",tchemit;ema
+ il="[email protected]";name="Tony Chemit";organization=CodeLutin;roles
+ =Committer;timezone="Europe/Paris",vmassol;email="[email protected]";n
+ ame="Vincent Massol";organization=ASF;roles=Committer;timezone="+1",elh
+ aro;email="[email protected]";name="Elliotte Rusty Harold";roles=Commit
+ ter;timezone="America/New_York",agudian;email="[email protected]";name
+ ="Andreas Gudian";roles=Emeritus;timezone="Europe/Berlin",aramirez;name
+ ="Allan Q. Ramirez";roles=Emeritus,bayard;name="Henri Yandell";roles=Em
+ eritus,carlos;email="[email protected]";name="Carlos Sanchez";organizat
+ ion=ASF;roles=Emeritus;timezone="+1",chrisjs;name="Chris Stevenson";rol
+ es=Emeritus,dblevins;name="David Blevins";roles=Emeritus,dlr;name="Dani
+ el Rall";roles=Emeritus,epunzalan;email="[email protected]";name="Ed
+ win Punzalan";roles=Emeritus;timezone=-8,felipeal;name="Felipe Leme";ro
+ les=Emeritus,jdcasey;email="[email protected]";name="John Casey";organ
+ ization=ASF;roles=Emeritus;timezone=-6,jmcconnell;email="jmcconnell@apa
+ che.org";name="Jesse McConnell";organization=ASF;roles=Emeritus;timezon
+ e=-6,joakime;email="[email protected]";name="Joakim Erdfelt";organizat
+ ion=ASF;roles=Emeritus;timezone=-5,jruiz;email="[email protected]";name=
+ "Johnny Ruiz III";roles=Emeritus,jstrachan;name="James Strachan";roles=
+ Emeritus,jtolentino;email="[email protected]";name="Ernesto Tolenti
+ no Jr.";organization=ASF;roles=Emeritus;timezone="+8",kenney;email="ken
+ [email protected]";name="Kenney Westerhof";organization=Neonics;roles=Emer
+ itus;timezone="+1",mperham;email="[email protected]";name="Mike Perham"
+ ;organization=IBM;roles=Emeritus;timezone=-6,ogusakov;name="Oleg Gusako
+ v";roles=Emeritus,pschneider;email="[email protected]";name="Patrick
+  Schneider";roles=Emeritus;timezone=-6,rinku;name="Rahul Thakur";roles=
+ Emeritus,shinobu;name="Shinobu Kuwai";roles=Emeritus,smorgrav;name="Tor
+ bjorn Eikli Smorgrav";roles=Emeritus,trygvis;email="[email protected]"
+ ;name="Trygve Laugstol";organization=ASF;roles=Emeritus;timezone="+1",w
+ smoak;email="[email protected]";name="Wendy Smoak";roles=Emeritus;timez
+ one=-7
+Bundle-DocURL: https://maven.apache.org/resolver/maven-resolver-api/
+Bundle-License: "Apache-2.0";link="https://www.apache.org/licenses/LICEN
+ SE-2.0.txt"
+Bundle-ManifestVersion: 2
+Bundle-Name: Maven Artifact Resolver API
+Bundle-SCM: url="https://github.com/apache/maven-resolver/tree/maven-res
+ olver-1.9.22/maven-resolver-api",connection="scm:git:https://gitbox.apa
+ che.org/repos/asf/maven-resolver.git/maven-resolver-api",developer-conn
+ ection="scm:git:https://gitbox.apache.org/repos/asf/maven-resolver.git/
+ maven-resolver-api",tag="maven-resolver-1.9.22"
+Bundle-SymbolicName: org.apache.maven.resolver.api
+Bundle-Vendor: The Apache Software Foundation
+Bundle-Version: 1.9.22
+Export-Package: org.eclipse.aether;uses:="org.eclipse.aether.artifact,or
+ g.eclipse.aether.collection,org.eclipse.aether.deployment,org.eclipse.a
+ ether.installation,org.eclipse.aether.metadata,org.eclipse.aether.repos
+ itory,org.eclipse.aether.resolution,org.eclipse.aether.transfer,org.ecl
+ ipse.aether.transform";version="1.9.22",org.eclipse.aether.artifact;ver
+ sion="1.9.22",org.eclipse.aether.collection;uses:="org.eclipse.aether,o
+ rg.eclipse.aether.artifact,org.eclipse.aether.graph,org.eclipse.aether.
+ repository,org.eclipse.aether.version";version="1.9.22",org.eclipse.aet
+ her.deployment;uses:="org.eclipse.aether,org.eclipse.aether.artifact,or
+ g.eclipse.aether.metadata,org.eclipse.aether.repository";version="1.9.2
+ 2",org.eclipse.aether.graph;uses:="org.eclipse.aether.artifact,org.ecli
+ pse.aether.repository,org.eclipse.aether.version";version="1.9.22",org.
+ eclipse.aether.installation;uses:="org.eclipse.aether,org.eclipse.aethe
+ r.artifact,org.eclipse.aether.metadata";version="1.9.22",org.eclipse.ae
+ ther.metadata;uses:="org.eclipse.aether";version="1.9.22",org.eclipse.a
+ ether.repository;uses:="org.eclipse.aether,org.eclipse.aether.artifact,
+ org.eclipse.aether.metadata";version="1.9.22",org.eclipse.aether.resolu
+ tion;uses:="org.eclipse.aether,org.eclipse.aether.artifact,org.eclipse.
+ aether.collection,org.eclipse.aether.graph,org.eclipse.aether.metadata,
+ org.eclipse.aether.repository,org.eclipse.aether.version";version="1.9.
+ 22",org.eclipse.aether.transfer;uses:="org.eclipse.aether,org.eclipse.a
+ ether.artifact,org.eclipse.aether.metadata,org.eclipse.aether.repositor
+ y";version="1.9.22",org.eclipse.aether.transform;uses:="org.eclipse.aet
+ her.artifact";version="1.9.22",org.eclipse.aether.version;uses:="org.ec
+ lipse.aether";version="1.9.22"
+Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
+

diff --git a/src/test/test_cvv.py b/src/test/test_cvv.py
index e872161..8c0e4e9 100644
--- a/src/test/test_cvv.py
+++ b/src/test/test_cvv.py
@@ -4,6 +4,7 @@ import javatoolkit.cvv as cvv
 from zipfile import ZipFile
 import struct
 import typing as T
+import os
 
 
 def create_class_header(version: int) -> bytes:
@@ -211,3 +212,26 @@ class SimpleTest(TestCase):
         ])
         self.assertListEqual(m.bad, [])
         self.assertListEqual(m.skipped, [])
+
+    def test_maven_generated_manifest(self):
+        m = cvv.CVVMagic('10')
+        jar_path = cvv.FileLoc('a.jar')
+
+        jar = ZipFile(io.BytesIO(), 'w')
+        jar.writestr('A.class', create_class_header(10))
+        jar.writestr('META-INF/versions/12/A.class', create_class_header(12))
+        this_dir = os.path.dirname(os.path.realpath(__file__))
+        with open(f'{this_dir}/res/maven-generated-manifest.mf', 'rb') as f:
+            jar.writestr('META-INF/MANIFEST.MF', f.read())
+
+        m.do_jar(jar, jar_path)
+
+        def jar_member(member: str) -> cvv.JarLoc:
+            return cvv.JarLoc(jar_path, member)
+
+        self.assertListEqual(m.good, [
+            cvv.GoodFile(jar_member('A.class'), '10', '10'),
+            cvv.GoodFile(jar_member('META-INF/versions/12/A.class'), '12', 
'12'),
+        ])
+        self.assertListEqual(m.bad, [])
+        self.assertListEqual(m.skipped, [])

Reply via email to