This is an automated email from the ASF dual-hosted git repository.

slachiewicz pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/maven-remote-resources-plugin.git

commit 205a0cd5f0eb2eeb577d5bfb429e47200f3a7862
Author: Sylwester Lachiewicz <[email protected]>
AuthorDate: Wed Oct 28 09:32:22 2026 +0000

    Reject a licenseMapping whose spdxId is not an SPDX ID or expression
---
 .../remote/AbstractProcessRemoteResourcesMojo.java | 10 +++--
 .../plugin/resources/remote/SpdxLicenses.java      | 46 ++++++++++++++++++----
 .../plugin/resources/remote/SpdxLicensesTest.java  | 10 +++++
 3 files changed, 55 insertions(+), 11 deletions(-)

diff --git 
a/src/main/java/org/apache/maven/plugin/resources/remote/AbstractProcessRemoteResourcesMojo.java
 
b/src/main/java/org/apache/maven/plugin/resources/remote/AbstractProcessRemoteResourcesMojo.java
index c6cecfb..8367976 100644
--- 
a/src/main/java/org/apache/maven/plugin/resources/remote/AbstractProcessRemoteResourcesMojo.java
+++ 
b/src/main/java/org/apache/maven/plugin/resources/remote/AbstractProcessRemoteResourcesMojo.java
@@ -746,11 +746,15 @@ public abstract class AbstractProcessRemoteResourcesMojo 
extends AbstractMojo {
 
     protected void validate() throws MojoExecutionException {
         if (licenseMappings != null) {
+            SpdxLicenses spdx = new SpdxLicenses();
             for (LicenseMapping mapping : licenseMappings) {
-                if (SpdxLicenses.isBlank(mapping.getSpdxId())
-                        || (SpdxLicenses.isBlank(mapping.getName()) && 
SpdxLicenses.isBlank(mapping.getUrl()))) {
+                if (SpdxLicenses.isBlank(mapping.getName()) && 
SpdxLicenses.isBlank(mapping.getUrl())) {
+                    throw new MojoExecutionException("Each licenseMapping 
needs a name, a url or both: " + mapping);
+                }
+                if (spdx.canonicalMappingId(mapping.getSpdxId()) == null) {
                     throw new MojoExecutionException(
-                            "Each licenseMapping needs an spdxId and a name, a 
url or both: " + mapping);
+                            "The spdxId of a licenseMapping must be an SPDX 
license ID, such as MIT, or an SPDX "
+                                    + "license expression, such as Apache-2.0 
OR MIT: " + mapping);
                 }
             }
         }
diff --git 
a/src/main/java/org/apache/maven/plugin/resources/remote/SpdxLicenses.java 
b/src/main/java/org/apache/maven/plugin/resources/remote/SpdxLicenses.java
index 6e34ce3..a6e980a 100644
--- a/src/main/java/org/apache/maven/plugin/resources/remote/SpdxLicenses.java
+++ b/src/main/java/org/apache/maven/plugin/resources/remote/SpdxLicenses.java
@@ -61,6 +61,8 @@ public class SpdxLicenses {
 
     private static final Pattern LICENSE_REF = 
Pattern.compile("LicenseRef-[A-Za-z0-9.-]+");
 
+    private static final Pattern ID_SHAPE = 
Pattern.compile("[A-Za-z0-9][A-Za-z0-9.+-]*");
+
     private static final Pattern ID_URL =
             
Pattern.compile("(?:opensource\\.org/licenses?|spdx\\.org/licenses)/([^/?]+)");
 
@@ -173,7 +175,7 @@ public class SpdxLicenses {
     public String id(String name, String url) {
         for (LicenseMapping mapping : mappings) {
             if (matches(mapping.getName(), name, true) && 
matches(mapping.getUrl(), url, false)) {
-                return mapping.getSpdxId().trim();
+                return canonicalMappingId(mapping.getSpdxId());
             }
         }
         String byName = isBlank(name) ? null : idOfName(name.trim());
@@ -187,7 +189,7 @@ public class SpdxLicenses {
     private String idOfName(String name) {
         String id = knownId(name);
         if (id == null) {
-            id = parseExpression(name);
+            id = parseExpression(name, false, true);
         }
         if (id == null) {
             id = names.get(normaliseName(name));
@@ -206,29 +208,54 @@ public class SpdxLicenses {
         return id;
     }
 
+    /**
+     * Returns the SPDX ID or expression a {@link LicenseMapping} declares, 
with known IDs in their SPDX spelling,
+     * or {@code null} unless it is a well-formed expression. IDs the built-in 
table does not know are accepted
+     * as declared, since a mapping exists to name them.
+     */
+    String canonicalMappingId(String spdxId) {
+        if (isBlank(spdxId)) {
+            return null;
+        }
+        String id = knownId(spdxId.trim(), true);
+        return id != null ? id : parseExpression(spdxId, true, false);
+    }
+
     /** Returns the ID in its SPDX spelling, a <code>LicenseRef-</code> ID as 
declared, or {@code null}. */
     private String knownId(String token) {
+        return knownId(token, false);
+    }
+
+    private String knownId(String token, boolean anyIdShape) {
         String id = ids.get(token.toLowerCase(Locale.ROOT));
         if (id == null && LICENSE_REF.matcher(token).matches()) {
             id = token;
         }
+        if (id == null && anyIdShape && ID_SHAPE.matcher(token).matches() && 
!isOperator(token)) {
+            id = token;
+        }
         return id;
     }
 
+    private static boolean isOperator(String token) {
+        return token.equalsIgnoreCase("AND") || token.equalsIgnoreCase("OR") 
|| token.equalsIgnoreCase("WITH");
+    }
+
     /**
      * Returns the expression with each ID in its SPDX spelling, or {@code 
null} unless it is a well-formed
      * SPDX license expression that combines known IDs with <code>AND</code>, 
<code>OR</code> or
      * <code>WITH</code>.
      */
-    private String parseExpression(String declared) {
+    private String parseExpression(String declared, boolean anyIdShape, 
boolean needsOperator) {
         List<String> tokens = new ArrayList<>();
         Matcher m = EXPRESSION_TOKEN.matcher(declared);
         while (m.find()) {
             tokens.add(m.group());
         }
-        ExpressionParser parser = new ExpressionParser(tokens);
+        ExpressionParser parser = new ExpressionParser(tokens, anyIdShape);
         String result = parser.expression();
-        return result != null && parser.next == tokens.size() && 
parser.operators > 0 ? result : null;
+        boolean complete = result != null && parser.next == tokens.size();
+        return complete && (parser.operators > 0 || !needsOperator) ? result : 
null;
     }
 
     /** Recursive descent over <code>term ((AND | OR) term)*</code>, where a 
term is an ID, optionally
@@ -240,8 +267,11 @@ public class SpdxLicenses {
 
         private int operators;
 
-        ExpressionParser(List<String> tokens) {
+        private final boolean anyIdShape;
+
+        ExpressionParser(List<String> tokens, boolean anyIdShape) {
             this.tokens = tokens;
+            this.anyIdShape = anyIdShape;
         }
 
         String expression() {
@@ -280,14 +310,14 @@ public class SpdxLicenses {
                 }
                 return "(" + inner + ")";
             }
-            String id = knownId(token);
+            String id = knownId(token, anyIdShape);
             if (id == null) {
                 return null;
             }
             if (next < tokens.size() && 
tokens.get(next).equalsIgnoreCase("WITH")) {
                 next++;
                 operators++;
-                String exception = next < tokens.size() ? 
knownId(tokens.get(next++)) : null;
+                String exception = next < tokens.size() ? 
knownId(tokens.get(next++), anyIdShape) : null;
                 return exception == null ? null : id + " WITH " + exception;
             }
             return id;
diff --git 
a/src/test/java/org/apache/maven/plugin/resources/remote/SpdxLicensesTest.java 
b/src/test/java/org/apache/maven/plugin/resources/remote/SpdxLicensesTest.java
index 97c2453..f8296d6 100644
--- 
a/src/test/java/org/apache/maven/plugin/resources/remote/SpdxLicensesTest.java
+++ 
b/src/test/java/org/apache/maven/plugin/resources/remote/SpdxLicensesTest.java
@@ -155,6 +155,16 @@ class SpdxLicensesTest {
         assertEquals("MIT", mapped.id("MIT", null));
     }
 
+    @Test
+    void mappingIds() {
+        assertEquals("Zlib", spdx.canonicalMappingId("Zlib"));
+        assertEquals("Apache-2.0 OR MIT", spdx.canonicalMappingId(" apache-2.0 
or mit "));
+        assertEquals("LicenseRef-Widget", 
spdx.canonicalMappingId("LicenseRef-Widget"));
+        assertNull(spdx.canonicalMappingId("Apache License 2.0"));
+        assertNull(spdx.canonicalMappingId("OR"));
+        assertNull(spdx.canonicalMappingId(""));
+    }
+
     private static LicenseMapping mapping(String spdxId, String name, String 
url) {
         LicenseMapping mapping = new LicenseMapping();
         mapping.setSpdxId(spdxId);

Reply via email to