This is an automated email from the ASF dual-hosted git repository. chibenwa pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-jdkim.git
commit b2c96a4aee3c92bb2782ae9799dd307233416e0a Author: Benoit TELLIER <[email protected]> AuthorDate: Fri Oct 30 08:31:15 2026 +0100 [FIX] SignatureRecord: reject an x= tag that is not 1*12DIGIT validate() parsed x= with Long.parseLong, so an empty or non-numeric expiration leaked a NumberFormatException out of the constructor instead of the IllegalStateException the verifier reports as an invalid record. Both record classes now check the RFC 6376 grammar first. Co-Authored-By: Claude Opus 5.5 <[email protected]> --- .../org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java | 4 ++++ .../apache/james/jdkim/tagvalue/SignatureRecordTemplate.java | 4 ++++ .../java/org/apache/james/jdkim/SignatureRecordImplTest.java | 12 ++++++++++++ .../org/apache/james/jdkim/SignatureRecordTemplateTest.java | 6 ++++++ 4 files changed, 26 insertions(+) diff --git a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java index 2e2a9f1..bd10540 100644 --- a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java +++ b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordImpl.java @@ -33,6 +33,7 @@ public class SignatureRecordImpl extends TagValue implements SignatureRecord { // TODO ftext is defined as a sequence of at least one in %d33-57 or // %d59-126 private static final Pattern hdrNamePattern = Pattern.compile("^[^: \r\n\t]+$"); + private static final Pattern timestampPattern = Pattern.compile("^[0-9]{1,12}$"); public SignatureRecordImpl(String data) { super(data); @@ -83,6 +84,9 @@ public class SignatureRecordImpl extends TagValue implements SignatureRecord { // when "x=" exists and signature expired then return PERMFAIL // (signature expired) if (getValue("x") != null) { + // RFC 6376 section 3.5: sig-x-tag = %x78 [FWS] "=" [FWS] 1*12DIGIT + if (!timestampPattern.matcher(getValue("x")).matches()) + throw new IllegalStateException("Invalid expiration (x=): " + getValue("x")); long expiration = Long.parseLong(getValue("x").toString()); long lifetime = (expiration - System.currentTimeMillis() / 1000); if (lifetime < 0) { diff --git a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordTemplate.java b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordTemplate.java index d33bab4..b18fd29 100644 --- a/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordTemplate.java +++ b/main/src/main/java/org/apache/james/jdkim/tagvalue/SignatureRecordTemplate.java @@ -35,6 +35,7 @@ public class SignatureRecordTemplate extends TagValue implements SignatureRecord // TODO ftext is defined as a sequence of at least one in %d33-57 or // %d59-126 private static final Pattern hdrNamePattern = Pattern.compile("^[^: \r\n\t]+$"); + private static final Pattern timestampPattern = Pattern.compile("^[0-9]{1,12}$"); public SignatureRecordTemplate(String data) { super(data); @@ -91,6 +92,9 @@ public class SignatureRecordTemplate extends TagValue implements SignatureRecord // when "x=" exists and signature expired then return PERMFAIL // (signature expired) if (getValue("x") != null) { + // RFC 6376 section 3.5: sig-x-tag = %x78 [FWS] "=" [FWS] 1*12DIGIT + if (!timestampPattern.matcher(getValue("x")).matches()) + throw new IllegalStateException("Invalid expiration (x=): " + getValue("x")); long expiration = Long.parseLong(getValue("x").toString()); long lifetime = (expiration - System.currentTimeMillis() / 1000); if (lifetime < 0) { diff --git a/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java b/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java index 2f44f6e..c6f0e9f 100644 --- a/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java +++ b/main/src/test/java/org/apache/james/jdkim/SignatureRecordImplTest.java @@ -166,4 +166,16 @@ public class SignatureRecordImplTest { assertThat(record.getSignature()).isEqualTo(signature); assertThat(new SignatureRecordImpl(record.toString()).getSignature()).isEqualTo(signature); } + + @Test + public void nonNumericExpirationShouldBeASyntaxError() { + String base = "v=1; a=rsa-sha256; d=example.com; s=s; h=from; bh=AA==; b=AA==; "; + + assertThatThrownBy(() -> new SignatureRecordImpl(base + "x=")) + .isInstanceOf(IllegalStateException.class); + assertThatThrownBy(() -> new SignatureRecordImpl(base + "x=abc")) + .isInstanceOf(IllegalStateException.class); + assertThatThrownBy(() -> new SignatureRecordImpl(base + "x=9999999999999")) + .isInstanceOf(IllegalStateException.class); + } } diff --git a/main/src/test/java/org/apache/james/jdkim/SignatureRecordTemplateTest.java b/main/src/test/java/org/apache/james/jdkim/SignatureRecordTemplateTest.java index 18ba729..266154e 100644 --- a/main/src/test/java/org/apache/james/jdkim/SignatureRecordTemplateTest.java +++ b/main/src/test/java/org/apache/james/jdkim/SignatureRecordTemplateTest.java @@ -159,4 +159,10 @@ public class SignatureRecordTemplateTest { assertThat(signatureRecord.getBodyHash()).isEqualTo(bodyHash); assertThat(signatureRecord.getSignature()).isEqualTo(signature); } + + @Test + public void nonNumericExpirationShouldBeASyntaxError() { + assertThatThrownBy(() -> new SignatureRecordTemplate("v=1; a=rsa-sha256; d=example.com; s=s; h=from; x=abc")) + .isInstanceOf(IllegalStateException.class); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
