This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/timestamp in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
commit 51d824f201c8e988e39a0128829344f9926b5152 Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Mon Sep 21 12:30:35 2026 +0100 Fix timestamp parsing --- .../apache/wss4j/dom/message/token/Timestamp.java | 22 +++++++- .../apache/wss4j/dom/message/TimestampTest.java | 65 ++++++++++++++++++++++ 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/token/Timestamp.java b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/token/Timestamp.java index 8fd219129..153ba37d9 100644 --- a/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/token/Timestamp.java +++ b/ws-security-dom/src/main/java/org/apache/wss4j/dom/message/token/Timestamp.java @@ -38,7 +38,6 @@ import org.apache.wss4j.dom.WSConstants; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; -import org.w3c.dom.Text; /** * Timestamp according to SOAP Message Security 1.0, @@ -79,7 +78,7 @@ public class Timestamp { // We can't have a ValueType attribute as per the BSP spec bspEnforcer.handleBSPRule(BSPRule.R3225); } - createdString = ((Text)currentChildElement.getFirstChild()).getData(); + createdString = getRequiredText(currentChildElement); } else { // Test for multiple Created elements bspEnforcer.handleBSPRule(BSPRule.R3203); @@ -99,7 +98,7 @@ public class Timestamp { // We can't have a ValueType attribute as per the BSP spec bspEnforcer.handleBSPRule(BSPRule.R3226); } - strExpires = ((Text)currentChildElement.getFirstChild()).getData(); + strExpires = getRequiredText(currentChildElement); } } else { bspEnforcer.handleBSPRule(BSPRule.R3222); @@ -155,6 +154,23 @@ public class Timestamp { } + /** + * Return the text content of a Created / Expires element. The element's first child is not + * necessarily a Text node - it may be absent, or be a comment - and reading it as one turned + * wire-supplied content into an unchecked exception rather than an INVALID_SECURITY fault. + */ + private static String getRequiredText(Element element) throws WSSecurityException { + String text = XMLUtils.getElementText(element); + if (text == null) { + throw new WSSecurityException( + WSSecurityException.ErrorCode.INVALID_SECURITY, + "invalidTimestamp", + new Object[] {"The " + element.getLocalName() + " element has no text content"} + ); + } + return text; + } + /** * Constructs a <code>Timestamp</code> object according * to the defined parameters. diff --git a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/TimestampTest.java b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/TimestampTest.java index 1994b2778..09a5adda2 100644 --- a/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/TimestampTest.java +++ b/ws-security-dom/src/test/java/org/apache/wss4j/dom/message/TimestampTest.java @@ -459,6 +459,71 @@ public class TimestampTest { verify(doc, Collections.singletonList(BSPRule.R3203)); } + /** + * A "Created" element with no text content of its own must be rejected as an invalid + * Timestamp, not read as though its first child were a Text node. + */ + @Test + public void testEmptyCreated() throws Exception { + Document doc = createTimestampWithCreatedContent(null); + + try { + verify(doc); + fail("The timestamp validation should have failed on an empty Created element"); + } catch (WSSecurityException ex) { + assertTrue(ex.getErrorCode() == WSSecurityException.ErrorCode.INVALID_SECURITY); + } + } + + /** + * The first child of a "Created" element need not be a Text node - here it is a comment, + * which c14n excludes from the signed bytes in any case. + */ + @Test + public void testCreatedWithLeadingComment() throws Exception { + ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC); + Document doc = + createTimestampWithCreatedContent(DateUtil.getDateTimeFormatter(true).format(now), true); + + verify(doc); + } + + private Document createTimestampWithCreatedContent(String createdText) throws Exception { + return createTimestampWithCreatedContent(createdText, false); + } + + private Document createTimestampWithCreatedContent( + String createdText, boolean leadingComment + ) throws Exception { + Document doc = SOAPUtil.toSOAPPart(SOAPUtil.SAMPLE_SOAP_MSG); + WSSecHeader secHeader = new WSSecHeader(doc); + secHeader.insertSecurityHeader(); + + Element timestampElement = + doc.createElementNS( + WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.TIMESTAMP_TOKEN_LN + ); + + Element elementCreated = + doc.createElementNS( + WSConstants.WSU_NS, WSConstants.WSU_PREFIX + ":" + WSConstants.CREATED_LN + ); + if (leadingComment) { + elementCreated.appendChild(doc.createComment("a comment")); + } + if (createdText != null) { + elementCreated.appendChild(doc.createTextNode(createdText)); + } + timestampElement.appendChild(elementCreated); + + secHeader.getSecurityHeaderElement().appendChild(timestampElement); + + if (LOG.isDebugEnabled()) { + LOG.debug(XMLUtils.prettyDocumentToString(doc)); + } + return doc; + } + /** * This is a test for processing an Timestamp where it contains no "Created" element. * This Timestamp should be rejected.
