You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2026/09/09 02:52:13 UTC

(directory-ldap-api) branch master updated: Fix the GeneralizedTime parsing to avoid accepting garbage at the end of a time

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

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/directory-ldap-api.git


The following commit(s) were added to refs/heads/master by this push:
     new 85f9835b0 Fix the GeneralizedTime parsing to avoid accepting garbage at the end of a time
85f9835b0 is described below

commit 85f9835b0d18e2c9f00b9c60353dd0b5db0ac734
Author: emmanuel lecharny <el...@apache.org>
AuthorDate: Wed Sep 9 04:52:09 2026 +0200

    Fix the GeneralizedTime parsing to avoid accepting garbage at the end of
    a time
---
 .../apache/directory/api/util/GeneralizedTime.java |  8 +-
 .../util/GeneralizedTimeStrictTimezoneTest.java    | 88 ++++++++++++++++++++++
 2 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/util/src/main/java/org/apache/directory/api/util/GeneralizedTime.java b/util/src/main/java/org/apache/directory/api/util/GeneralizedTime.java
index c81dbdee6..8f50ca6bc 100644
--- a/util/src/main/java/org/apache/directory/api/util/GeneralizedTime.java
+++ b/util/src/main/java/org/apache/directory/api/util/GeneralizedTime.java
@@ -448,7 +448,13 @@ public class GeneralizedTime implements Comparable<GeneralizedTime>
                 throw new ParseException( I18n.err( I18n.ERR_17053_MISSING_TZ ), pos + 1 + digits.length() );
             }
         }
-    }
+        else
+        {
+            // The time zone must be 'Z', '+HH[MM]' or '-HH[MM]' and must terminate the
+            // string: anything else (e.g. trailing garbage after a fraction) is invalid
+            throw new ParseException( I18n.err( I18n.ERR_17053_MISSING_TZ ), pos );
+        }
+   }
 
 
     /**
diff --git a/util/src/test/java/org/apache/directory/api/util/GeneralizedTimeStrictTimezoneTest.java b/util/src/test/java/org/apache/directory/api/util/GeneralizedTimeStrictTimezoneTest.java
new file mode 100644
index 000000000..b1eb28aa4
--- /dev/null
+++ b/util/src/test/java/org/apache/directory/api/util/GeneralizedTimeStrictTimezoneTest.java
@@ -0,0 +1,88 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    https://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.directory.api.util;
+
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.text.ParseException;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
+
+
+/**
+ * Tests that GeneralizedTime rejects any time zone token other than 'Z',
+ * '+HH[MM]' or '-HH[MM]'. Before this was enforced, a value containing a
+ * fraction could carry unlimited trailing garbage ("20250101120000.5whatever")
+ * and still parse successfully as GMT, creating a parsing differential with
+ * strict RFC 4517 implementations.
+ *
+ * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
+ */
+@Execution( ExecutionMode.CONCURRENT )
+public class GeneralizedTimeStrictTimezoneTest
+{
+    /**
+     * Trailing garbage after a fraction of a second must be rejected.
+     */
+    @Test
+    public void testFractionOfSecondTrailingGarbageRejected()
+    {
+        assertThrows( ParseException.class, () -> new GeneralizedTime( "20250101120000.5X" ) );
+        assertThrows( ParseException.class, () -> new GeneralizedTime( "20991231235959.9UNTIL-FOREVER" ) );
+    }
+
+
+    /**
+     * Trailing garbage after a fraction of a minute must be rejected.
+     */
+    @Test
+    public void testFractionOfMinuteTrailingGarbageRejected()
+    {
+        assertThrows( ParseException.class, () -> new GeneralizedTime( "202501011200.5garbage" ) );
+    }
+
+
+    /**
+     * Trailing garbage after a fraction of an hour must be rejected.
+     */
+    @Test
+    public void testFractionOfHourTrailingGarbageRejected()
+    {
+        assertThrows( ParseException.class, () -> new GeneralizedTime( "2025010112.5garbage" ) );
+    }
+
+
+    /**
+     * Valid time zone tokens after a fraction must still be accepted.
+     */
+    @Test
+    public void testValidTimezoneAfterFractionStillAccepted() throws ParseException
+    {
+        assertNotNull( new GeneralizedTime( "20250101120000.5Z" ) );
+        assertNotNull( new GeneralizedTime( "20250101120000.5+0100" ) );
+        assertNotNull( new GeneralizedTime( "20250101120000.5-05" ) );
+        assertNotNull( new GeneralizedTime( "202501011200.5Z" ) );
+        assertNotNull( new GeneralizedTime( "2025010112.5Z" ) );
+    }
+}