Skip to content

CI: Run test suite on Java 8 matrix; make tests Java 8 compatible. - #4

Merged
oleksandrlazarenko-pi merged 1 commit into
mainfrom
ci/java8-matrix
Jul 7, 2026
Merged

CI: Run test suite on Java 8 matrix; make tests Java 8 compatible.#4
oleksandrlazarenko-pi merged 1 commit into
mainfrom
ci/java8-matrix

Conversation

@oleksandrlazarenko-pi

Copy link
Copy Markdown

No description provided.

@oleksandrlazarenko-pi
oleksandrlazarenko-pi merged commit 7a7f303 into main Jul 7, 2026
8 checks passed
jwrosewell added a commit that referenced this pull request Aug 31, 2026
* Check the declared payload length against the bytes present before allocating

Io.Reader.readByteArray took the sender's declared payload count and
passed it to readBytes, which checked the count against the end of the
buffer before sizing the copy, so this port never allocated from the
declared number alone. What it did not check was that the payload is
followed by exactly the 64 byte signature and nothing else. A declared
count short of the bytes present parsed, with the signature read from
the middle of the envelope and the remainder ignored, and an envelope
with bytes after the signature parsed with those bytes ignored. The
reference fix in owid-dotnet sets the rule every port now follows, so
the same malformed envelopes are refused the same way everywhere.

The count is now checked against the bytes present before anything is
sized by it. A valid OWID is the declared payload followed by the 64
byte signature and nothing else, so the count must equal the bytes
remaining less the signature length, and any other count, short or
long, is refused with the existing OwidException, which names the
declared length and the bytes present. Envelopes with a byte after the
signature, previously ignored, are now refused as malformed. The
separate Integer.MAX_VALUE check is gone because the new check covers
it. The other length driven reads were checked and none needed
changing, being the domain terminator scan, which stops at the end of
the buffer, and the date and signature reads, which go through the
bounded readBytes.

PayloadLengthTest covers a matching envelope, the library's own signed
output, off-by-one counts, a trailing byte, a short signature, declared
lengths of 64 MiB, 2 GiB and 0xFFFFFFFF each refused with under 64 KiB
allocated on the thread, and an empty payload. The Javadoc on
Owid.fromByteArray and on the serialized form now say the signature
ends the envelope.

Files changed:
src/main/java/com/swancommunity/owid/Io.java
src/main/java/com/swancommunity/owid/Owid.java
src/test/java/com/swancommunity/owid/PayloadLengthTest.java

* Clarify and optimize large payload handling

* Bound the domain read at the published maximum

The creator domain is stored as text followed by a zero terminator, and
the reader found the end of it by walking forward to that terminator. A
missing or corrupted terminator sent the walk to the end of the buffer,
so the cost of parsing was set by the length of the input rather than
by the size of the field, which is the same class of attacker
controlled work as the declared payload length this branch already
checks.

RFC 1035 section 2.3.4, "Size limits", restricts the total length of a
domain name to 255 octets or less. That number counts the wire format,
which spends one length octet on every label and one zero octet on the
root. An OWID stores the presentation form instead, being the text
"example.com", where the dots stand in for the label length octets and
the root has no text at all, so the same published limit is two
characters shorter here. The new constant MAXIMUM_DOMAIN_LENGTH in Io
carries that reasoning next to the number.

The reader now stops at the maximum rather than at the end of the
buffer, so an unterminated field costs no more than the maximum however
long the buffer is, and a domain over the maximum is refused without
reading past it. The refusal uses the existing OwidException with a new
message, because the module has one exception type and no error
variants to add to.

Nothing about a valid envelope changes. New tests cover a domain at the
maximum parsing and round tripping, one character over being refused, a
buffer with no terminator at all being refused, a sixteen mebibyte
domain field being refused while allocating under 64 KiB, and the
library's own signed output still parsing and verifying. The suite goes
from 43 to 48 tests, all passing.

* Refuse to write a domain longer than the maximum

Reading an OWID has been bounded at the published maximum for a domain
name since the previous commit, but writing one was not, so a creator
configured with a longer domain still produced an OWID that this same
library would refuse to parse. A library that can emit what it cannot
read leaves the fault to surface at the consumer rather than at the
creator.

The one constant, Io.MAXIMUM_DOMAIN_LENGTH, now bounds both halves.
Creator.create refuses a domain over the maximum when the caller
supplies it, before the crypto instance is looked at and before any
OWID exists, and Io.writeString refuses one that reaches serialisation
by another route such as Owid.setDomain. Both raise the OwidException
the read raises, through one shared factory, so the two halves report
the one condition in the same words. The length counted is the UTF-8
bytes, being what the read counts as it walks to the terminator.

Nothing at or under the maximum behaves differently. Four tests cover
a creator holding a domain of exactly the maximum signing an OWID that
round trips and verifies, a creator refused one character over with
the maximum named in the message, the refusal arriving before the
private key is used, and serialisation refusing a domain that arrived
by another route. Removing only the two new checks fails three of the
four.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants