Fix invoice-image code-review findings: aspect ratio, whitelist config, stale refs
- MigraDocExtensions: fix a bug where capping an oversized explicit width+height image re-derived the other dimension from the image's native pixel ratio instead of scaling both sides uniformly, silently distorting a deliberately non-native-ratio (e.g. stretched) image. - InvoiceService: derive the mfr-portal image whitelist prefix from the existing Fds:MFR_host config setting (via MFRClientConfig.RestRoot) instead of a second hardcoded literal, so it can't drift from the actual configured host; parse <img> sources with HtmlAgilityPack (already used by the PDF's own HTML converter) instead of a regex, so a decoy attribute like data-src can't be picked up in place of the real src; dedup fetch URLs with a HashSet instead of O(n^2) List scanning; reuse FuchsPdf.MigraDocFilenameFromByteArray instead of re-spelling the "base64:" prefix. - Fix a stale log message and comment left over from the GenerateInvoicePdf -> GenerateInvoicePdfAsync rename, and update INVOICE_LIFECYCLE.md's references to the old sync method name. - Add tests for the config-derived whitelist, a decoy-attribute src, and the 20-image-per-invoice fetch cap boundary. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,17 +28,24 @@ user-supplied URL server-side is also an SSRF surface — the invoice text is us
|
||||
so a naive "download whatever `src` says" would let it reach internal/private network addresses.
|
||||
|
||||
## Decision
|
||||
- Invoice images are inlined into the PDF only when their `src` starts with the literal prefix
|
||||
`https://portal.mobilefieldreport.com/mfr/` (`InvoiceService.AllowedInlineImagePrefix`) — the
|
||||
only origin the invoice editor's picture links come from. Anything else (any other host, `http://`,
|
||||
a different path) is left unrendered, exactly as before this feature — no generic remote fetch.
|
||||
- Invoice images are inlined into the PDF only when their `src` starts with the mfr REST root
|
||||
(`InvoiceService.AllowedInlineImagePrefix`, e.g. `https://portal.mobilefieldreport.com/mfr/`) —
|
||||
the only origin the invoice editor's picture links come from. This is derived from the same
|
||||
`Fds:MFR_host` configuration entry the MFR REST client itself uses (via
|
||||
`MFR_RESTClient.MFRClientConfig.RestRoot`), not a second hardcoded literal, so it can't drift if
|
||||
the configured mfr host ever changes per environment/tenant. Anything else (any other host,
|
||||
`http://`, a different path) is left unrendered, exactly as before this feature — no generic
|
||||
remote fetch.
|
||||
- The fetch happens once per invoice render, in `InvoiceService.ResolveInlineImagesAsync`
|
||||
(async, off a named `IHttpClientFactory` client `"InvoiceInlineImages"`, 10s timeout), *before*
|
||||
the synchronous MigraDoc render starts. `InvoiceService.GenerateInvoicePdf` therefore became
|
||||
`GenerateInvoicePdfAsync` — every caller already ran in an async context. Each fetched image is
|
||||
capped at 5MB, must report an `image/*` content-type, and the invoice fetches at most 20 distinct
|
||||
images; failures (unreachable, wrong type, oversized) are logged and just leave that one image out
|
||||
— they never fail the whole render.
|
||||
`GenerateInvoicePdfAsync` — every caller already ran in an async context. `<img>` sources are
|
||||
found by parsing the description HTML with HtmlAgilityPack (`ResolveInlineImagesAsync`) — the
|
||||
same library the `HtmlConverter` itself parses the identical text with — rather than a regex, so
|
||||
a decoy attribute next to the real `src` (e.g. `data-src="..."`) can't be picked up in its place.
|
||||
Each fetched image is capped at 5MB, must report an `image/*` content-type, and the invoice
|
||||
fetches at most 20 distinct images; failures (unreachable, wrong type, oversized) are logged and
|
||||
just leave that one image out — they never fail the whole render.
|
||||
- The resolved `{src → "base64:..."}` map flows down as a plain `IReadOnlyDictionary<string,string>?`
|
||||
parameter through `IPdfService.ApplyInvoice` → `FuchsPdf.ApplyInvoice` → `Cell.AddHtml` into the
|
||||
`HtmlConverter`, whose new `img` handler only emits an image for a `src` present in that map —
|
||||
|
||||
Reference in New Issue
Block a user