fix(929): pin Pkcs 9.0.21; hold ImageSharp 3.1.12 with TIFF decoding disabled #934

Open
timothy wants to merge 7 commits from 929-vuln-pkcs-imagesharp-v4 into main
7 Commits
Author SHA1 Message Date
timothyandClaude Opus 5.5 71d660f213 test(929): the ICC carrier list includes BMP and is checked against a round trip of every configured format
Build ErsatzTV Image / CI toolchain image resolves (pull_request) Successful in 9s
Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 20s
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 9s
PR Gates / Docs update reminder (pull_request) Successful in 11s
PR Gates / decisions lifecycle (pull_request) Successful in 14s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 10s
Review verdict / Set review-verdict status (pull_request_target) Successful in 16s
PR Gates / Script lint and tests (ruff + pytest) (pull_request) Successful in 12m42s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m37s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 6m0s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Successful in 5m56s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Successful in 8s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Successful in 7s
review-verdict/h10 Review-verdict: MERGEABLE @ 71d660f (base: main)
Round 4 review found two gaps in the gwg2 runtime pin. The carrier list
(png, jpeg, webp) was hand-written and left out BMP, whose 3.1.12
decoder also constructs an IccProfile (BmpDecoderCore::ReadInfoHeader)
and which remote-URL and on-disk graphics can reach. And the record
said every hostile-ICC case asserts the profile is still attached,
while the WatermarkElement case asserts only allocation (its images are
private).

- IccCarriers is now bmp, jpeg, png, webp, with a BmpEncoder arm.
- Icc_Carriers_Are_Every_Configured_Format_That_Round_Trips_A_Profile
  round-trips the hostile profile through each format of
  ImageSharpDecoding.Configuration via its default encoder and requires
  the formats that come back carrying a profile to equal IccCarriers.
  Witnessed red by dropping "bmp" from the list.
- A Cecil scan of the 3.1.12 package (2026-10-28) lists the decoders that
  construct an IccProfile as BMP, JPEG, PNG, WebP and TIFF, so the
  round trip's blind spot (a decoder reading a profile its encoder never
  writes) is empty today; the record says so, dated.
- The record and the element test's comment now say only the validator
  cases assert attachment; the element case shares their fixture.

Measured: product path 38-94 KiB, element 368-834 KiB, Entries control
about 129,545 KiB for all four carriers.

Decisions-Edit: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: f9ce6bc6-5b36-491a-8d72-89c1f70dd3e1
2026-10-30 00:03:25 +01:00
timothyandClaude Opus 5.5 bed50e2b77 test(929): pin by allocation that decode and clone do not parse a hostile ICC profile
Round-3 review: the record said every bound is enforced over compiled code,
but half of the gwg2 (ICC CLUT) bound is ImageSharp 3.1.12's own runtime
behaviour, which the IL scan cannot see.

A 208-byte ICC profile declaring an mft2 CLUT (3 in, 15 out, 255 grid
points), carried in PNG, JPEG and WebP, now goes through
RemoteImageValidator.DecodeAndValidate + CloneFrame/Resize/CloneAs and
through a real remote WatermarkElement. Each case asserts the profile is
still attached and fails above 16 MiB allocated. The positive control reads
Entries on the same profile and must allocate over 64 MiB.

Measured 2026-10-28 on 3.1.12: the product paths allocate 38-94 KiB and
the element 322-390 KiB; Entries allocates about 129,550 KiB. Mutation
witnesses: a try/catch read of IccProfile.Entries injected after the
validator's post-decode check reddens all 6 product-path cases on the
allocation assertion. The same read after ImageElementBase's per-frame
Resize reddens the 3 element cases. Both were restored from git.

The record's gwg2 row and 'What holds the bounds' now name the two halves
and which test holds each. docs/testing.md names the ICC cases.

Decisions-Edit: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: f9ce6bc6-5b36-491a-8d72-89c1f70dd3e1
2026-10-30 00:03:25 +01:00
timothyandClaude Opus 5.5 59749babd9 test(929): only ImageSharpDecoding may choose the decode configuration, and the test map names the IL scan
Review found that the surface guard checked that every decode takes
DecoderOptions and that only ImageSharpDecoding constructs them, but not
that the TIFF-blind configuration is the one that picks the decoder. A
new rule rejects, outside ImageSharpDecoding, a DecoderOptions
Configuration assignment, a read of Configuration.Default, and any
ImageFormatManager reference (a decoder fetched from a registry decodes
its own format whatever the options carry).

Mutation sweep against ImageElementBase.LoadImage, restored from git
after each:
- Configuration.Default.ImageFormatsManager.GetDecoder(...).DecodeAsync(
  ImageSharpDecoding.Options(), ...): only the new rule reds (get_Default,
  get_ImageFormatsManager, GetDecoder sites).
- ImageSharpDecoding.Configuration.ImageFormatsManager.GetDecoder(...):
  only the new rule reds (ImageFormatManager clause alone).
- new DecoderOptions { Configuration = ImageSharpDecoding.Configuration }:
  the new rule reds on set_Configuration and the construction rule on .ctor.
- options.Configuration = ... on a returned instance does not compile
  (CS8852: init-only), so the reviewer's reassignment shape is not
  expressible in C#; the IL rule still covers the setter.

docs/testing.md: the Architecture.Tests row now names all three rule
families (layering, provider-statics wiring, the Mono.Cecil ImageSharp
scan) and the host reference, and drops the stale exact count; the
missing ErsatzTV.Infrastructure.Tests row is added, since this branch
adds ImageSharpDecodingTests there. The record and guard inventory name
the fourth shape and what an IL scan cannot see.

Decisions-Edit: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: f9ce6bc6-5b36-491a-8d72-89c1f70dd3e1
2026-10-30 00:03:25 +01:00
timothyandClaude Opus 5.5 f30a54172b docs(929): the record names what holds each bound, and Renovate is held
security.imagesharp-3x-tiff-decoder-disabled now says the bounds are
enforced by ImageSharpSurfaceTests rather than by its own prose. The
"Load pass is masked" claim is dropped from the record and from the two
code comments that repeated it: with the architecture pin, reverting the
Load pass's options alone now reddens
Only_ImageSharpDecoding_constructs_DecoderOptions (witnessed
2026-10-27), so the claim had become false. The residual it cannot see,
an extension-dispatched Save(path), is named.

Renovate: a packageRule holds SixLabors.ImageSharp on 3.1.x. The
existing "range pins are respected" sentence in ci-cd.md and the EF
group's description were wrong: Dependency Dashboard #22 offers EF Core
v10 and MediatR 14.2.0 over their range pins, so a range does not hold
Renovate. Whether the rule holds the OSV security PR (#923) cannot be
measured before merge, and the record says so.

guard-inventory.md's hand-written C# guard list gains the new fixture
and its two Architecture.Tests siblings, and stops quoting a count.

This branch also edits the rationale prose of
ffmpeg.remote-image-fetcher-bounded (commit 73045865f: the MaxFrames
measurement is dated to 3.1.12 and TIFF is no longer in play), which
the decisions lifecycle check requires this trailer for.

Decisions-Edit: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: f9ce6bc6-5b36-491a-8d72-89c1f70dd3e1
2026-10-30 00:03:25 +01:00
timothyandClaude Opus 5.5 52c383e832 test(929): pin the ImageSharp call surface the 3.1.12 hold depends on
The bounds in security.imagesharp-3x-tiff-decoder-disabled were prose:
they held only while every decode passed ImageSharpDecoding's options
and nothing touched ICC entries, the TIFF codec or histogram
equalization. A new decode site or accessor would have reopened an
advisory with no test going red.

ImageSharpSurfaceTests reads the IL of every production assembly that
can reach ImageSharp (derived from ErsatzTV.sln and the csproj graph,
so a project gaining the reference joins the population) and fails on a
decode entry point without DecoderOptions, a DecoderOptions built
outside ImageSharpDecoding, or any reference into the TIFF, ICC or
Normalization namespaces or a Tiff/HistogramEqualization member. The
host joins Architecture.Tests' references so it is scanned too.

The ImageSharp pin now carries a comment naming the hold and its record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: f9ce6bc6-5b36-491a-8d72-89c1f70dd3e1
2026-10-30 00:03:25 +01:00
timothyandClaude Opus 5.5 e96e47a9d8 docs(929): record why ImageSharp stays on 3.1.12 and what bounds each advisory
New record security.imagesharp-3x-tiff-decoder-disabled: the license-key
build gate in every patched release (measured on 3.2.0 and 4.1.3 Release
builds), the per-advisory reachability table, the masked Load-pass clause
(measured: reverting it alone leaves the suite green), that dotnet list
--vulnerable ignores NuGetAuditSuppress (measured), and the revisit trigger.
ci-cd.md Dependency management records the Pkcs direct pin, the rejected
central transitive pin (NU1109), and that the weekly scan is red by design
on ImageSharp until then. The #511 record's MaxFrames measurement is dated
to 3.1.12 and no longer lists TIFF as in play.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: f9ce6bc6-5b36-491a-8d72-89c1f70dd3e1
2026-10-30 00:03:25 +01:00
timothyandClaude Opus 5.5 9eed552876 fix(929): pin System.Security.Cryptography.Pkcs 9.0.21 and stop ImageSharp 3.1.12 decoding TIFF
The Pkcs 9.0.4 that EFCore.BulkExtensions' Oracle/SqlServer providers pull
transitively carries GHSA-846f-f764-76mx (High) and GHSA-2fmv-rv2c-59x7.
It is overridden by a direct reference in ErsatzTV.Infrastructure, the
existing pattern (Microsoft.OpenApi, SQLitePCLRaw). The central transitive
pin the issue suggested was measured and rejected: enabling
CentralPackageTransitivePinningEnabled fails restore with NU1109 in every
project, because Microsoft.Extensions.DependencyModel is exact-pinned [8.0.2]
below the >= 9.0.x EF Core requires.

ImageSharp stays on 3.1.12. Every patched release (3.2.0, 4.1.2, 4.1.3)
ships a build-time Six Labors license check that fails a Release build
without a key (measured: dotnet build -c Release errors 'No Six Labors
license found'), and CI and the image build Release. Of the six 3.1.12
advisories now listed, the two DECODER ones are TIFF; every ImageSharp
decode now goes through a configuration that cannot detect TIFF. The rest
are an encoder, a processor and an accessor ErsatzTV never calls.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: f9ce6bc6-5b36-491a-8d72-89c1f70dd3e1
2026-10-30 00:03:25 +01:00