Skip to content

feat(security): default 30 s render timeout for UntrustedHtml - #380

Merged
raroche merged 2 commits into
mainfrom
feat/untrusted-default-render-timeout
Sep 30, 2026
Merged

raroche merged 2 commits into
mainfrom
feat/untrusted-default-render-timeout

Conversation

@raroche

@raroche raroche commented Sep 30, 2026

Copy link
Copy Markdown
Owner

What

  • New SecurityPolicy.RenderTimeout (TimeSpan?, default null = no cap).
  • SecurityPolicy.UntrustedHtml now sets it to 30 seconds.
  • HtmlPdfOptions.Timeout stays the optional per-render knob and always wins:
    • null (default) → use the policy's RenderTimeout.
    • positive value → use it instead.
    • Timeout.InfiniteTimeSpan → no cap, even under UntrustedHtml.
    • TimeSpan.Zero / other negative → fail immediately (unchanged).
  • TimeoutException message names the source (HtmlPdfOptions.Timeout or SecurityPolicy.RenderTimeout).

Why

Follow-up from the security review: UntrustedHtml locked down every fetch surface and budget, but a render had no time bound unless the caller remembered to set Timeout. A pathological document could hold a worker indefinitely.

Compatibility

  • Additive public API (one new property). SafeDefault / TrustedTemplate behavior is unchanged.
  • Behavior change: UntrustedHtml renders longer than 30 s now throw TimeoutException unless Timeout is set.
  • Timeout = Timeout.InfiniteTimeSpan previously cancelled immediately (treated as negative); it now means no cap, matching .NET convention.

Tests

9 new facade tests (policy defaults, policy cap fires, explicit override, infinite opt-out, error source, UntrustedHtml ordinary render). Public API baseline updated. Unit suite: 8658 passed / 3 skipped.

Docs: README security checklist, docs/security/deployment.md, CHANGELOG [Unreleased].

🤖 Generated with Claude Code

Add SecurityPolicy.RenderTimeout, a default hard cap on conversion time for
renders that use the policy. UntrustedHtml now defaults to 30 seconds, so a
hostile document cannot hold a worker indefinitely when the caller sets no
timeout. SafeDefault and TrustedTemplate stay uncapped.

HtmlPdfOptions.Timeout stays the per-render knob and always wins over the
policy default. Timeout.InfiniteTimeSpan now means "no cap" (it previously
cancelled immediately, like any negative value). The TimeoutException
message names the setting that fired.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings September 30, 2026 15:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unrelated pipeline cancellations can be incorrectly reported as policy timeout failures.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Adds a default 30-second render timeout for untrusted HTML while preserving explicit timeout overrides.

Changes:

  • Adds SecurityPolicy.RenderTimeout and configures UntrustedHtml.
  • Resolves policy and per-render timeout precedence.
  • Updates tests, API baseline, and security documentation.
File Description
src/​NetPdf/​HtmlPdf.cs Applies effective render timeout and reports its source.
src/​NetPdf/​HtmlPdfOptions.cs Documents timeout precedence and infinite opt-out.
src/​NetPdf/​Resources/​SecurityPolicy.cs Adds the policy timeout and 30-second untrusted default.
tests/​NetPdf.UnitTests/​HtmlPdfFacadeTests.cs Tests defaults, overrides, messages, and opt-out behavior.
tests/​NetPdf.UnitTests/​Api/​PublicApi/​NetPdf.txt Updates the public API baseline.
README.md Documents the untrusted-render timeout.
docs/​security/​deployment.md Updates deployment guidance and checklist.
CHANGELOG.md Records the new security feature.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/NetPdf/HtmlPdf.cs

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b4144a1ef7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/NetPdf/HtmlPdf.cs
Comment thread src/NetPdf/HtmlPdf.cs
…Save

PR #380 review (Copilot + Codex):
- Translate an OperationCanceledException into TimeoutException only when
  the render timer itself fired. A stage that cancels for its own reason
  (e.g. a custom IResourceLoader) keeps its original exception.
- PdfDocument.Save takes no token, so the pipeline re-checks cancellation
  after serialization. A deadline that passes during Save now fails the
  render instead of returning a PDF past the cap.

Tests: unrelated loader cancellation propagates unchanged (fails without
the fix); a token-ignoring stage that crosses the deadline still times out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@raroche
raroche merged commit 6bedc4e into main Sep 30, 2026
13 of 14 checks passed
@raroche
raroche deleted the feat/untrusted-default-render-timeout branch September 30, 2026 15:33
@raroche raroche mentioned this pull request Sep 30, 2026
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