Bump VellumPdf.Layout from 1.5.4 to 2.3.0 - #55
Closed
dependabot[bot] wants to merge 1 commit into
Closed
Conversation
--- updated-dependencies: - dependency-name: VellumPdf.Layout dependency-version: 2.3.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Author
|
Superseded by #56. |
dependabot
Bot
deleted the
dependabot/nuget/dependencies/VellumPdf.Layout-2.3.0
branch
September 2, 2026 08:15
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Updated VellumPdf.Layout from 1.5.4 to 2.3.0.
Release notes
Sourced from VellumPdf.Layout's releases.
2.3.0
Breaking changes
A same-revision
/XRefStmno longer overrides a classic cross-reference table's free entryfor the same object. Given one revision whose classic table marks object N free and whose
/XRefStmdefines it,PdfReadernow resolves N tonull, matching qpdf; it previouslyresolved N from the stream. The construct is describable beyond a hand-freed
/Contents: anincremental update from a writer that copies
/XRefStmforward without understanding it, andalso carries the previous revision's free entries along unchanged, produces exactly this shape —
the case MatthiasValvekens describes in
pdf-association/pdf-issues#237, by
his own account without having checked it against a real processor ("I'm not aware of any
processors that do either of this, so maybe my intuition is completely wrong"). The
cross-section arrangement ISO 32000-2 §7.5.8.4 actually describes, where the free entry sits in
an earlier
/Prevrevision, is unaffected only in the two-revision case; a hybrid revisionsitting between two others in a
/Prevchain is not exempt, and loses its own copy the same waya same-revision one does while also suppressing whatever a still-older revision defined. What
survives a chain like that is the definition in whichever revision is newest among the ones that
mention the object at all, not simply whichever one sits outside a same-revision pairing. This
aligns with the reading in issue #237, open at the time of writing; if it or errata
#523 resolves the other way, this is
revisited.
VellumPdf.Readeris still Preview, where a behaviour change would ordinarily stayunder Changed, but silently dropping rendered content is closer to what this section otherwise
covers than a changed exception type is, so it's recorded here instead. (#206)
Consequences, measured rather than assumed except where a bullet says otherwise — the
/Encryptshape has no test, and the
/DecodeParmshalf rests on a code reading rather than a fixture:/Contents, the page now has nocontent stream where it had one.
count drops from 1 to 0; if N is an intermediate
/Pagesnode, freeing it drops only its ownsubtree — a three-page document with a two-page branch under that node loses those two pages
and keeps the third, 3 becoming 1. The surviving root still declares its old
/Count, so acaller trusting
/Countand one walking/Kidsnow disagree.PreflightContext.WalkPageswalks
/Kidsthe same way, so page-scoped PDF/A rules silently stop covering the lost subtreetoo.
/Encryptreference points at, the documentnow fails to open with
InvalidDataExceptioninstead of decrypting, because/Encryptcan nolonger resolve to a dictionary at all. This shape has no test yet.
PdfReader.Openthrows
InvalidDataException: Malformed PDF: /Root does not resolve to a dictionary.where itpreviously opened.
/Filteror/DecodeParmsobject resolving tonulldoes not degrade tonulloutput —it produces wrong bytes.
PdfFilters.GetFilterListtreats an unresolvable/Filteras nofilter at all, so
GetDecodedStreamDatareturns the raw, still-encoded body. Measured: a24-byte plaintext body, FlateDecode-compressed to 32 bytes (zlib header
78 9C), comes back asthose 32 raw bytes instead of the 24-byte plaintext once its
/Filterreference is freed thisway; a byte-identical control with the filter object live decodes correctly.
/DecodeParmsdegrades the same way — a PNG predictor's rows are never undone. qpdf degrades identically
here, so this is not a divergence from the oracle, only from what this entry previously implied
the general case is.
... (truncated)
2.2.0
Breaking changes
PdfDictionary.Set,TryGetandGetnow throwArgumentNullExceptionfor anullkey.Previously
TryGet(null, out _)returnedfalse,Get(null)returnednull, andSet(null, value)appended an entry that only failed later — with aNullReferenceExceptionoutof
WriteTo, once the dictionary was serialised. All three are Stable API, which is why this isrecorded here rather than under Security, where the rest of this fix lives: without the guard, a
nullkey would behave differently depending on which side of the internal indexing threshold adictionary sits — returning
falsebelow it, throwing above it — exactly the property thatthreshold is supposed to be free to move without changing what callers observe. (#208)
Security
OwnerPassword = ""beside a realUserPasswordproduced a file with no real passwordprotection at all, in every release from v1.0.0 through v2.1.0.
??treats an empty string asa value, not as "unset", so that combination sealed
/Oand/OE(ISO 32000-2 §7.6.4.4.6,Algorithm 9) under the empty password instead of falling back to the user password. At
/R6 anempty password fails the
/Ucheck and satisfies the/Ocheck, so any conforming reader landson owner access when no password is supplied. That is a property of the file itself, not of the
order a particular reader tries
/Uand/Oin. Verified concretely: a file written by v2.1.0with
UserPassword = "hunter2", OwnerPassword = ""opens with no password supplied at all,and the catalog, hence the whole object graph, decrypts. The document's confidentiality is gone,
not merely its permission flags: nothing enforced
Permissions, but nothing enforcedUserPasswordeither.OwnerPassword = ""beside a non-emptyUserPasswordnow throws insteadof producing a file. Both passwords empty is unchanged, since an unprotected document is
legitimate and ISO 32000-2 permits an empty owner password.
OwnerPassword = nullis unchangedtoo: that is the documented fallback to the user password as owner, and it does not reproduce
this defect (
/Ois sealed under the real user password, not the empty string) — though anyonewho can open the document still holds owner access under it, so
Permissionsstill binds nobody.The guard sits in
StandardSecurityHandler's constructor, which is Stable API and callabledirectly, and in
PdfDocument.Encrypt, so the failure surfaces at the call site rather than atSave();VellumPdf.Layout.Document.Encryptinherits it by delegation. (EncryptionSetup.TryAuthenticate,this library's own authentication order, is why our reader reports such a file's access level as
owner rather than user — that explains what we report, not why the exposure exists.)
A document already written with this shape cannot be fixed in place: correcting it means
re-deriving
/Oand/OE, which needs a real owner password behind them from the start. Affecteddocuments must be re-encrypted from the original plaintext, with a distinct
OwnerPasswordthistime. (#211)
PdfDictionarylookup is no longer quadratic in the key count.SetandTryGetnow build ahash index once a dictionary passes 16 entries, rather than scanning the whole entry list on every
call. The
/Encryptdictionary is parsed, and copied again byEncryptionSetup.DereferenceValues,before any password is checked, on a file anyone can send, so a hostile document naming tens of
thousands of keys there previously cost time quadratic in that count with nothing to show for it:
opening a fixture with an 80,000-key
/Encryptdictionary took about 27 seconds before this fix andwell under a second after.
EncryptionSetup's/CFcap (MaxCryptFilters, still 64) keeps itscomment but loses the reason it used to give: every term that touches
/Encryptis linear now, sothe cap no longer bears any of the weight of bounding this cost. It stays because a real document
names one or two crypt filters, not sixty-four. (#208)
... (truncated)
2.1.0
Breaking changes
A password-protected document now reaches
PdfPreflightasPdfPasswordException, which noexisting
catchcovers. Every prior version threwUnsupportedPdfFeatureException, and soNotSupportedException, for any/Encryptat all, so that is what a caller ofPdfPreflight.ValidateorPdfPreflight.DetectClaimedProfileswrote to detect an encrypted file.Both are Stable API in a Stable package, and both now open an encrypted document whose empty user
password suffices, and throw
PdfPasswordExceptionfor one that needs a non-empty password. Thatexception derives from
Exceptiondirectly, and deliberately so: a document the readerunderstands but was not given the credentials for is not an unsupported feature. An existing
catch (NotSupportedException)around either method therefore lets it through. CatchPdfPasswordExceptionbeside it. (#97)PdfDocument.DocumentIdnow throwsArgumentExceptionfor a value that is not 16 bytes.Previously any other length was accepted and then written as no
/IDat all — silently. ISO32000-2 Table 15 requires
/IDonce/Encryptis present, so on an encrypted document thatproduced a file qpdf rejects outright ("invalid /ID in trailer dictionary"), with nothing to tell
the caller which value caused it. On an unencrypted document the old behaviour merely omitted an
optional entry, so code that set a wrong-length id and relied on that omission now sees an
exception.
DocumentIdis Stable API, which is why this is recorded here rather than underFixed. (#97)
Added
A committed corpus of encrypted PDFs, one per standard-security-handler
/V+/Rcombination.Generated once with qpdf and committed rather than shelled out for at test time, so the corpus is
byte-identical on CI and locally and leaves no silently-skipped gate. A guard test pins each fixture
by SHA-256 as well as
/V,/Rand/CFM: qpdf refuses to write RC4 without--allow-weak-cryptoand still leaves a zero-byte file behind, and two fixtures are both
/V 4 /R 4, differing only inthe cipher — neither an existence check nor a
/V+/Rcheck would notice either. Groundwork forthe decrypt side. (#99)
Hand-written RC4 and MD5 primitives for the legacy (
/V1–2) decryption path. Internal only —no public surface yet, no reader wiring, no security handler; that is a separate change. The BCL
has never shipped RC4, and its
MD5type defers to the OS crypto library everywhere except BrowserWASM, where MD5 is unsupported outright, so decrypting an old PDF under Blazor WASM would otherwise
be a dead end.
PdfDocument's/IDgeneration (ISO 32000-2 §14.4) now goes through this MD5 too,in place of the BCL call it used before, so the codebase is actually clear of CA5351 (flags MD5 as
weak) rather than clear of it only on the new, not-yet-wired decryption path — and Browser WASM
document writing, not just decryption, no longer depends on a platform MD5 that isn't there. RC4 is
verified against all three vectors in draft-kaukonen-cipher-arcfour-03 Appendix A, including the
309-byte vector that runs the keystream past its first 256-byte cycle; MD5 against the full RFC
1321 §A.5 suite plus a length sweep across the padding and block boundaries, and a differential
sweep against the BCL.
/IDitself is pinned by a known-answer test, because nothing elsepinned it: every golden document sets its own id, and the computed one folds in a millisecond
timestamp, so no snapshot could cover it. Groundwork for the decrypt side. (#97)
The decrypt side of the Standard security handler, covering every
/V+/Rcombination thecommitted corpus (#99) exercises:
/V1//R2 (RC4-40) through/V5//R6 (AES-256).Internal only: no public surface, no
PdfReaderwiring, no/Encryptgate; a decrypting reader... (truncated)
2.0.0
The first major version since 1.0. Every package moves to 2.0.0 together, as usual.
Two things made a major version necessary: assemblies are strong-named, which changes their
identity, and the analyzer that was supposed to be locking the public API is now actually
locking it, which meant fixing the defects in that surface while doing so was still free.
Most of the rest is work that had to land before the surface froze.
Read Upgrading to 2.0 first if you bind to an assembly
identity by hand — a
PackageReferenceneeds no change, but a binding redirect, anInternalsVisibleTo, or anAssembly.Loadstring does.Breaking changes
Assembly identity
eng/VellumPdf.snk), with public key tokenb2757187a6d18ae5.AssemblyVersionis pinned to2.0.0.0for the whole 2.x line, soservicing releases will not force another rebind. (#53)
Public API
PdfSignature.ByteRangeint[]ReadOnlyMemory<long>(#178)PdfLinkAnnotation.FlagsintPdfAnnotationFlags(#176)TextEncodingWarningcharactercharSystem.Text.Rune(#177)CcittImageLoader.LoadCcittOptions(#177)PdfPreflight.Validate(PdfDocumentReader, PdfConformance)HttpRevocationClient(HttpClient, TimeSpan)HttpTimestampClient(#177)PdfSignatureSettings.SubFilterETSI.CAdES.detachedandadbe.pkcs7.detached(#176)SignaturePlaceholderOptions.SubFilterEach is explained under Added, Changed, or Fixed below.
Behaviour
signing-timesigned attribute. ETSIEN 319 142-1 admits only the signed attributes its table 1 lists, and
signing-timeis notamong them — PAdES conveys the claimed time in the signature dictionary's
/M, which thislibrary already wrote from the same value. Emitting it anyway held every signature at
PAdES-BES instead of PAdES-BASELINE-B. Code reading
signing-timeout ofSignerInfoon asignature written with the default
/SubFilter ETSI.CAdES.detachedwill no longer find it;/Mstill carries the value, andadbe.pkcs7.detachedkeeps the attribute, since it makesno ETSI claim. (#170)
/StructTreeRoot. SettingTagged = trueand drawing nothing previously produced no structure tree at all, whichfailed PDF/A-2a and PDF/UA-1 validation.
Taggednow means tagged. (#120)in-process signing paths, with a message naming the offending bytes and the way forward,
instead of an opaque
ArgumentExceptionraised from inside the BCL's CMS encoder. The... (truncated)
1.11.0
What's Changed
Full Changelog: Tim81/VellumPDF@v1.10.0...v1.11.0
1.10.0
What's Changed
Full Changelog: Tim81/VellumPDF@v1.9.0...v1.10.0
1.9.0
What's Changed
Full Changelog: Tim81/VellumPDF@v1.8.2...v1.9.0
1.8.2
What's Changed
Full Changelog: Tim81/VellumPDF@v1.8.1...v1.8.2
1.8.1
What's Changed
Full Changelog: Tim81/VellumPDF@v1.8.0...v1.8.1
1.8.0
What's Changed
Full Changelog: Tim81/VellumPDF@v1.7.8...v1.8.0
1.7.8
What's Changed
Full Changelog: Tim81/VellumPDF@v1.7.7...v1.7.8
1.7.7
What's Changed
Full Changelog: Tim81/VellumPDF@v1.7.6...v1.7.7
1.7.6
What's Changed
Full Changelog: Tim81/VellumPDF@v1.7.5...v1.7.6
1.7.5
What's Changed
Full Changelog: Tim81/VellumPDF@v1.7.4...v1.7.5
1.7.4
What's Changed
Full Changelog: Tim81/VellumPDF@v1.7.3...v1.7.4
1.7.3
What's Changed
Full Changelog: Tim81/VellumPDF@v1.7.2...v1.7.3
1.7.2
What's Changed
Full Changelog: Tim81/VellumPDF@v1.7.1...v1.7.2
1.7.1
What's Changed
Full Changelog: Tim81/VellumPDF@v1.7.0...v1.7.1
1.7.0
What's Changed
Full Changelog: Tim81/VellumPDF@v1.6.0...v1.7.0
1.6.0
What's Changed
Full Changelog: Tim81/VellumPDF@v1.5.6...v1.6.0
1.5.6
What's Changed
Full Changelog: Tim81/VellumPDF@v1.5.5...v1.5.6
1.5.5
What's Changed
Full Changelog: Tim81/VellumPDF@v1.5.4...v1.5.5
Commits viewable in compare view.
Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting
@dependabot rebase.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore this major versionwill close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor versionwill close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)