Skip to content

fix!: security updates and drop Python 3.10 - #665

Merged
viseshrp merged 16 commits into
mainfrom
fix/security-updates-drop-python-310-main
Nov 5, 2026
Merged

viseshrp merged 16 commits into
mainfrom
fix/security-updates-drop-python-310-main

Conversation

@viseshrp

@viseshrp viseshrp commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Ports #645 (merged into stable/27.1) to main.

Addresses https://ado.internal.synopsys.com/tfs/ANSYS_development/Portfolio/_workitems/edit/1534172
Also https://ado.internal.synopsys.com/tfs/ANSYS_Development/Portfolio/_workitems/edit/1536094, https://ado.internal.synopsys.com/tfs/ANSYS_Development/Portfolio/_workitems/edit/1536102 and https://ado.internal.synopsys.com/tfs/ANSYS_Development/Portfolio/_workitems/edit/1536105

Changes

  • Breaking: drop Python 3.10. requires-python is now >=3.11, <3.15. 3.10 is end-of-life, has no eMudhra fix, and current Pillow/Django don't support it.
  • Security:
    • djangorestframework 3.18 (cap <3.19.0): CVE-2026-114136, CVE-2026-114137.
    • Playwright 1.64.1: its bundled Node.js distrusts the eMudhra intermediates.
    • Pillow 13.0.0 (lockfile). Pillow 13 can't save I-mode images as PNG, so png_compatible_image converts them to I;16 first (same output as before).
    • CI: pypa/gh-action-pypi-publish v1.14.3; Security Scan runs on stable/**.
    • certifi is not pinned. ADR guarantees certifi ≥ 2026.10.19 in its own environment; SECURITY.md tells users who manage their own environment to update it.
  • Browser PDF: if the ADR installation ships a different browser build than Playwright expects, launch the installation's browser by path and log a warning. This compares build names; no launch has to fail first, and a launch failure is never retried or interpreted. A build without its executable raises ADRException before any launch. It never uses another browser.
  • Browser PDF, extra builds: the export uses the build named in the installation's browser metadata and ignores other builds in the folder (for example after copying a newer ADR package over an older one), with a warning. The metadata entry must be a plain folder name.
  • Docker image: the default image moves from ghcr.io/ansys-internal/adr_dev to ghcr.io/ansys/adr_dev:latest (constants.py, scripts/pull_adr_image.sh, tests/test_compatibility.py), because the built-in token can no longer pull from ansys-internal. This changes the default image for Docker-mode users.
  • Minimum versions: runtime floors match ADR 26.1's bundled versions. statsmodels needs 0.14.5+. constraints/v261.txt is unchanged, so 26.1-profile installs keep Django 4.2 and DRF 3.15.

Differences from #645

  • The browser-PDF logic has moved on from fix!: security updates and drop Python 3.10 on stable/27.1 #645 (build-name comparison instead of retrying a failed launch; metadata-named build).
  • The README link to the TLS guidance points at main instead of stable/27.1.
  • uv.lock is regenerated on main and refreshed with uv lock --upgrade, so it no longer matches stable/27.1 exactly. Notably python-dateutil is locked at 4.0.0.
  • Django 5.2.18 and ansys/actions v11.1.3 are already on main.

Validation

uv lock --locked, pre-commit, smoke test and wheel build pass. The browser-PDF renderer and browser-binary tests pass (169) against a local ADR 27.1 install. Full ADR/Docker tests run in CI. The manual ADR 26.1 workflow runs were not repeated for this port.

Browser PDF was also checked end to end against a local ADR 27.2 install, which ships Chromium 1223 while this release expects 1248: the local_tests browser-PDF scripts produced 8 correct PDFs (serverless and service mode, portrait and landscape, rendered by Chromium 148), and real-browser launch checks covered a leftover 1248 folder next to 1223, a missing executable (no launch, no leaked temp folders) and a non-browser executable. The PDFs were produced on b3cc226bc and the launch checks on c51cc809c; later commits do not change the path an older installation takes.

Related

Please squash-merge.

viseshrp and others added 6 commits November 2, 2026 21:30
Pillow 13 no longer writes 32-bit integer (I mode) images as PNG; earlier releases clipped them to 16 bits on save. Add report_utils.png_compatible_image, which converts I mode images to I;16 and returns every other image unchanged, and use it in PIL_image_to_data and the serverless Image PNG conversion. The stored output is the same as with Pillow 12, and the change is harmless on older Pillow releases.

Tests cover the I to I;16 clipping, the pass-through for other modes, and a 32-bit TIFF round trip through PIL_image_to_data. Verified with uv run pytest tests/test_report_utils.py -k 'png_compatible or saves_32_bit'.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Playwright looks for the Chromium build it was released with inside the product browser folder. If the ADR installation ships a different build, or the expected build cannot be determined, the normal launch fails and every browser PDF export breaks.

Add _launch_product_browser. It runs the normal launch first, so a matching product behaves exactly as before. Only when that launch fails with a Playwright error does it retry once with the packaged headless-shell executable by path, logging a warning that names the build. Timeouts are never retried because they already used the shared browser time budget. If the packaged executable is missing or the platform has no known layout, raise an ADRException that names only the incomplete build and chains the original error. It never uses another browser. _playwright_browser_binary_env now yields the validated browser metadata so render_pdf can pass it through.

The browser PDF docs now say the product browser is used without a separate install, that exports change process-wide settings and must run one at a time per process, and that a different build logs a warning. Unit tests cover the successful normal launch, the fallback on win64 and linux_2.6_64, the missing executable, the timeout, and the full render_pdf path. Tests that launched Chromium directly now use the same launch helper. Verified with uv run pytest tests/serverless/test_pdf_renderer.py --use-local-launcher --install-path <ADR 27.1 install> (125 passed).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Security updates ahead of the next release, and Python 3.10 support is dropped. Python 3.10 is end-of-life, has no fix for the eMudhra certificate incident, and is not supported by Pillow 13.

Dependencies: allow djangorestframework 3.18 (cap < 3.19.0) for CVE-2026-114136 and CVE-2026-114137; pin Playwright 1.64.1, whose bundled Node.js 24.22.1 distrusts the compromised eMudhra intermediates; raise the runtime floors to the versions bundled with ADR 26.1 (requests 2.32.4, Pillow 11.1.0, python-dateutil 2.9.0.post0, pytz 2024.2, psutil 7.2.1, psycopg 3.2.12, bleach 6.3.0); require statsmodels 0.14.5 or later because 0.14.2 to 0.14.4 cannot be imported with SciPy 1.16 or later. constraints/v261.txt is unchanged, so 26.1 profile installs keep Django 4.2 and DRF 3.15, which the Serverless caveats now state.

Python 3.10: requires-python is now >=3.11,<3.15, the 3.10 classifier is removed, Ruff targets py311, and the CI and test matrices drop 3.10. README, AGENTS.md and the getting-started page say 3.11 through 3.14.

Other: uv.lock is regenerated with Pillow 13.0.0, DRF 3.18.3, Playwright 1.64.1 and certifi 2026.10.23, and no longer carries Python 3.10 resolution forks. SECURITY.md gains TLS guidance after the eMudhra incident, linked from the README. pypa/gh-action-pypi-publish moves to v1.14.3, and the Security Scan triggers include stable/** branches. CHANGELOG is updated.

Verified with uv lock --locked, pre-commit, tests/smoketest.py, and the focused image and browser PDF tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bump the locked grpcio (1.85.0), pip (26.2.2), uv (0.13.5) and virtualenv (21.14.13) to the versions in the stable/27.1 lockfile so the dependency set matches the maintenance line. Only these four dev-tool entries change; no runtime dependency is affected.

Verified with uv lock --locked, uv sync --frozen --all-extras and pre-commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Run uv sync --upgrade --all-extras and uv lock --upgrade as AGENTS.md requires after a commit, so the Security Scan checks resolve against current releases. 59 locked packages change and the lock goes from 164 to 162 packages. Runtime-relevant updates: python-dateutil 2.9.0.post0 to 4.0.0 (which drops its six dependency), pytz 2026.6, tzlocal 5.4.5, tzdata 2026.6, django-guardian 3.5.2, asgiref 3.13.0, sqlparse 0.6.1, numpy 2.5.4, markupsafe 3.0.4 and pypdf 6.21.4. The rest are dev and docs tooling (ruff, pytest, pre-commit, sphinx and others). No pyproject.toml change; the declared version ranges are unchanged.

Verified with uv lock --locked, tests/smoketest.py, pre-commit, and uv run pytest on tests/test_report_objects.py, tests/test_report_utils.py and tests/serverless/test_pdf_renderer.py against a local ADR 27.1 install (246 passed). The only dateutil use in src is dateutil.parser.parse in report_objects.py, which is covered by tests/test_report_objects.py.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The built-in GITHUB_TOKEN can no longer pull ghcr.io/ansys-internal/adr_dev, so the Testing jobs fail at 'Pull Docker container' with 'denied' before any test runs. Use ghcr.io/ansys/adr_dev:latest instead, exactly as #645 did on stable/27.1.

The image is set in three places that must agree: scripts/pull_adr_image.sh (what CI pulls), DOCKER_REPO_URL and DOCKER_DEV_REPO_URL in constants.py (the default image Docker mode runs, so the pulled image is the one the tests start), and the docker_image labels in tests/test_compatibility.py. The deliberately non-existent image in tests/test_service.py is unchanged.

Verified with uv run pytest tests/test_compatibility.py (37 passed), tests/smoketest.py, pre-commit and uv lock --locked. The pull itself can only be confirmed in CI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@codecov-commenter

codecov-commenter commented Nov 3, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.42857% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.08%. Comparing base (002e1cf) to head (d7ca49d).

Files with missing lines Patch % Lines
.../ansys/dynamicreporting/core/utils/pdf_renderer.py 95.83% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #665      +/-   ##
==========================================
+ Coverage   82.00%   82.08%   +0.07%     
==========================================
  Files          32       32              
  Lines        9399     9443      +44     
  Branches     1691     1698       +7     
==========================================
+ Hits         7708     7751      +43     
- Misses       1194     1195       +1     
  Partials      497      497              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

viseshrp and others added 8 commits November 2, 2026 22:58
rc4 shipped from stable/27.1 on 2026-11-02, so give it its own dated section on main, using the same text as the stable/27.1 CHANGELOG, instead of listing its changes under [Unreleased]. [Unreleased] keeps only the changes that are specific to main: the ADR release cadence documentation and the development marker tag exclusion. The separate main-only Django 5.2.18 lockfile bullet is dropped because the rc4 Security entry already covers it.

Verified that the rc4 section is identical to stable/27.1's and that everything from rc3 down is unchanged, and ran pre-commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ld is missing

The browser fallback added for rc4 retried with the packaged headless shell on
any non-timeout Playwright launch error. That was broader than its purpose,
which is an ADR installation that ships a different browser build than the one
this Playwright version expects.

For a launch failure with another cause, such as a host that is missing system
libraries or a browser that crashes on start-up, the retry launched the same
build by path, skipped Playwright's own dependency check, failed again, and
logged a warning that wrongly named the expected build as a stand-in. The
actionable error was left as the context of the second failure instead of the
cause of the ADRException.

- Only a launch error saying the expected executable does not exist now
  triggers the fallback. Any other Playwright launch error is re-raised
  unchanged, so the caller gets the same ADRException chained to the original
  error as before the fallback existed. Timeouts are still never retried.
- Log the original launch error at debug level before the packaged executable
  is looked up, so the missing-executable path keeps the debug log the code had
  before the fallback.
- Add tests for an unrelated launch failure (the helper and the render), the
  debug log, a fallback launch that fails or times out, the shared browser time
  budget across the two launches (including a spent budget), and the warning
  plus the PLAYWRIGHT_BROWSERS_PATH restore.
- Record the change under Unreleased in the CHANGELOG.

Not changed: a first launch that is expected to fail still leaves two empty
Playwright temp folders behind when the fallback runs. That only happens on a
build mismatch, and avoiding it needs the expected build to be detected before
launching, which this change does not attempt.

Verified without the pytest runner: local_tests/test_browser_selection_scenarios.py
against the installed ADR 27.1 build that ships 1223 (the fallback still
triggers with Playwright 1.64.1 and the PDF is produced with Chromium 148), and
a local script that calls the new tests directly, which passes 8 of 8 on this
change and fails the three tests for the two fixes on the previous code.
pre-commit passes on the changed files.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Run the post-commit dependency refresh from AGENTS.md (uv sync --upgrade
--all-extras, then uv lock --upgrade) so the CI Security Scan sees current
locked versions after the browser fallback fix.

Three locked packages moved, all of them development tooling:

- coverage 7.17.0 -> 7.17.1
- python-discovery 1.9.1 -> 1.9.2
- soupsieve 3.0.4 -> 4.0

None of the runtime dependencies changed, and the exact Playwright pin
(1.64.1) is untouched.

Verified with uv lock --check, which reports the lock as consistent, and with
pre-commit on uv.lock.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the Ansys installation ships a different Chromium headless shell
build than the one the installed Playwright expects, the default browser
launch can never succeed. Each failed attempt also leaves two empty
folders in the temp directory (playwright-artifacts-* and
playwright_chromiumdev_profile-*), so every PDF export leaked them.

Read the expected build from the browsers.json registry that Playwright
ships with its driver. When the packaged build folder is a plain
chromium_headless_shell-<revision> that differs, launch its executable
by path straight away and log the usual warning. Anything unreadable,
platform specific or overridden in the registry keeps Playwright's own
lookup, so the default launch and the missing-build fallback stay
exactly as before.

Real runs against Playwright 1.64.1 on Windows:
- an installed 1223 build: 10 exports leak no folders; the previous
  launch order leaked 20
- a real 1248 build: launches normally, no warning
- a 1248 folder that is not a working browser: the launch error is
  re-raised, no second launch
- a 1223 folder without its executable: the incomplete-build error with
  no launch

The real-browser tests now check which launch path ran, using the
installed Playwright's own Chromium revision as an independent oracle.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Browser-PDF export rejected the ADR installation's playwright-browsers
folder whenever it held more than one browser build. Copying a newer ADR
package over an older installation leaves the previous build behind, so
Fluent's PDF export failed with "Browser PDF export requires a valid
product-shipped browser binary" even though the shipped build was intact.

The resolver now uses the build named by packaged_binary_dir in
playwright_browser_metadata.json, still requires its INSTALLATION_COMPLETE
marker, and logs a warning naming any other builds it ignores. The metadata
entry must be a plain folder name, so it cannot point outside
playwright-browsers now that it is no longer matched against the single
folder on disk.

Verified with the browser-binary and renderer unit tests (170 passed) and
an end-to-end render against ADR 27.1's real chromium_headless_shell-1248
with a leftover chromium_headless_shell-1223 folder beside it, which
produced a valid PDF and logged the warning.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Run uv lock --upgrade after the browser-folder fix so the Security Scan
check sees current versions. Only python-discovery, soupsieve and
virtualenv change; playwright stays pinned at 1.64.1.

Verified with uv lock --locked and an import smoke test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The readiness step's except block bound the exception to exc but only
re-raised it with a bare raise, so ruff's F841 check flags the name as
unused. Removing it does not change behavior: the bare raise still
re-raises the original exception with its traceback.

Verified with ruff (F401, F841), pre-commit, the renderer and
browser-binary unit tests (170 passed), and an import smoke test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the browser build in the ADR installation matched the build
Playwright expects but its executable was missing, the renderer first let
Playwright try its normal launch. Playwright creates its temporary
playwright-artifacts and profile folders before it finds the executable
missing, and leaves them behind when the launch fails, so every failed
export leaked two folders.

The renderer now looks up the packaged executable before any launch and
raises the "browser build ... is incomplete" ADRException straight away
when it is missing. Playwright looks for its own build at the same path,
so that launch could not have succeeded. The fallback after a failed
normal launch is unchanged for the case where the packaged executable
exists.

Tests: the mocked render helpers now provide a packaged executable, the
missing-executable tests assert that no launch happens, and a regression
test covers the matching-build case. 170 renderer and browser-binary unit
tests pass. The real-browser launch checks against ADR 27.2 now report
0 launches and 0 leaked folders for that case, down from 1 launch and 2
leaked folders.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
viseshrp and others added 2 commits November 5, 2026 13:13
…launch error

The browser-PDF launch decided whether to use the packaged ADR browser by looking for Playwright's wording ('Executable doesn't exist') in a failed launch. A Playwright upgrade that changes that text would silently disable the fallback, and no test could notice because the tests raised the same text.

Compare build names instead. After the existing up-front check that the packaged executable exists, launch it by path with a warning when the expected headless-shell build is known and differs from the packaged one. In every other case, including an unknown expected build, launch normally and let any failure propagate unchanged, so there is no retry, no timeout special case, and no error-text matching. _launch_packaged_browser no longer needs a launch_error, and the unused PlaywrightError import and _MISSING_BROWSER_EXECUTABLE_ERROR constant are removed.

Also: correct the executable-table comment, which said the packaged browser was only used after a failed launch, and import the playwright package as playwright_package so it no longer shares a name with the local 'playwright' variable in render_pdf.

Tests: replace the failure-then-retry tests with ones for the new contract. A failed launch is never retried whatever its message, for unknown and matching expected builds and for timeouts; a failure of the direct launch propagates; and the render-level tests share an _arrange_mismatched_build_renderer helper. The two budget-sharing tests only applied to a second launch and are removed. CHANGELOG entry updated. Verified with the renderer and browser-binary tests (169 passed against a local ADR 27.1 install) and pre-commit.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Make the descriptions of browser-PDF browser selection match the code after
the switch from a retry to a build-name comparison.

- CHANGELOG: rc4 already used the packaged ADR browser through a retry, so
  the Unreleased entry now says what changed: the build names are compared
  instead of failing a launch first. It also states that other launch
  failures surface as the ADR error with the original Playwright error as
  its cause, rather than as the original error itself.
- pyproject.toml: the Playwright pin comment said browser-PDF "falls back"
  to the packaged build. It now says the packaged build is launched by path
  with a warning.
- Serverless browser-PDF page: add the paragraph on leftover browser builds
  that the user guide already has, so both pages describe the same
  behaviour.

The rc4 CHANGELOG entry still describes rc4's retry and is left unchanged.
Verified with pre-commit and uv lock --locked.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@viseshrp
viseshrp requested a review from phchen95 November 5, 2026 18:52
@viseshrp
viseshrp merged commit c4ed862 into main Nov 5, 2026
23 checks passed
@viseshrp
viseshrp deleted the fix/security-updates-drop-python-310-main branch November 5, 2026 18:54
@viseshrp
viseshrp restored the fix/security-updates-drop-python-310-main branch November 6, 2026 19:22
viseshrp added a commit that referenced this pull request Nov 6, 2026
Resolve R-01 by annotating the image conversion helpers, serverless Image.save, the browser metadata context manager, and a named Chromium launch callback. Preserve call-time timeout calculation, headless launch, and executable-path forwarding.

Resolve R-03 with DEBUG diagnostics for incomplete browser executables and unknown registry revisions, preserving all caller-facing exception messages and launch selection. Resolve R-04 with concrete success and None examples. Document DEBUG diagnostics in both browser-PDF guides.

Verified on Windows with ADR 27.1 builds 1248 and 1223: 194 focused existing tests passed; matching, mismatched, leftover, incomplete, and malformed-registry runtime scenarios passed. Pre-commit (documented Windows license-hook skip), make check, make smoketest, and make docs passed; docs emitted 504 warnings. Changed functions have complete annotations; unrelated annotation findings and two pre-existing unused imports remain out of scope. R-02, R-05, R-06, test authoring, and final coverage remain deferred to phase 09.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@viseshrp

viseshrp commented Nov 6, 2026

Copy link
Copy Markdown
Collaborator Author

viseshrp added a commit that referenced this pull request Nov 9, 2026
…tence

- Remove test_resolve_playwright_browser_binary_info_rejects_missing_named_dir_beside_leftover.
  It builds the same scenario as the existing
  ..._rejects_packaged_dir_name_mismatch: one folder on disk, metadata naming a
  different one, expect None. The leftover listing it guarded is gone.
- Remove test_playwright_pdf_ignores_another_complete_build_beside_the_metadata_selected_build.
  It stubs the resolver, so the extra build folder it creates is never read by
  the code under test; it asserted the same launch path as
  test_playwright_pdf_direct_launch_logs_build_and_version. Leftover handling is
  covered by the resolver tests.
- Remove _arrange_revision_build_renderer. It only gave the build a
  revision-style name, which mattered for the removed build comparison. Its
  remaining user now uses _arrange_product_browser_renderer.
- Collapse the build-name parametrization of the launch test to one name: with
  no build comparison, "different", "matching", and "non-numeric" builds take
  the same path. Both platforms stay.
- Use DOCKER_DEV_REPO_URL in tests/test_compatibility.py instead of repeating
  the image URL, like every other test; #665 had to edit both copies by hand.
- Trim the CHANGELOG environment-variable entry to its first sentence; the
  Fixed entry already says the browser launches by path.

No production code changes.

Verified: 147 focused renderer tests and 37 compatibility tests pass against
ADR 27.1; pdf_renderer.py coverage is unchanged at 96.18% with no newly
uncovered lines; pre-commit, make check, and make smoketest pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

3 participants