Files
XianBWandCopilot 7c95268c0c fix(security)!: harden artifact serialization and loading (#2339)
* security: harden artifact serialization and loading

* ci: restore dependency and title lint compatibility

* fix: address artifact test and lint failures

* ci: handle current MLflow and Windows OSQP changes

* ci: use lxml release with cross-platform wheels

* fix: defer optional CVXPY solver imports

* docs: mark notebook model artifacts as trusted

* docs: mark position artifacts as trusted

* style: document intentional lazy import

* ci: skip broken released OSQP on Windows Python 3.8

* fix: mark online model artifacts as trusted

* test: make MLflow client timing check robust

* ci: retry nondeterministic benchmark smoke test

* fix: cap filelock before fork safety regression

* fix: use filelock release compatible with multiprocessing

* fix: preserve artifact loading compatibility after hardening

* fix(security): validate derived artifact paths and extend regressions

* fix(security): preserve extended data artifact round trips

* fix(security): retain legacy NumPy and pandas artifact classes

Allow the exact MaskedArray and SparseDtype module paths emitted by the older dependency versions in CI. Preserve restricted loading and cover both old and new paths.

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

* fix(data): avoid unused trading calendar lookup in PIT normalization

PIT normalization uses report dates and calendar-day offsets, so do not request an unused Baostock calendar. Cover annual and quarterly normalization without online calendar access.

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

* fix(security): require explicit trust in artifact workflows

Expose default-off artifact trust at online, delayed-training and meta-workflow entry points. Keep data loading restricted, preserve legacy default recorder calls and restored workflow defaults, and document migration and trust boundaries.

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

* test(workflow): cover complete DDG-DA and HIST artifact flows

Exercise real training, cache and recorder restoration, saved-task replay, signals and portfolio backtests. Fix the cache trust, grouping, relative-path, LightGBM early-stopping and HIST CPU blockers exposed by full execution.

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

* fix(security)!: unify artifact trust and publish upgrade guidance

Use trusted consistently across public APIs and CLI options. Migrate pre-release saved consent only at serialization boundaries, reject invalid or conflicting settings, and preserve restricted data reads. Add a canonical upgrade guide with README, installation, changelog and runtime-error entry points.

BREAKING CHANGE: executable artifact restoration requires explicit trusted=True. HIST mappings use JSON and high-frequency cache paths must remain within artifact_root. See docs/start/artifact_migration.rst for upgrading existing workflows.

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

* fix(security): remove trial-only trust compatibility

Target upgrades from pre-PR Qlib rather than artifacts generated by intermediate PR revisions. Retain restricted defaults for saved objects without consent, unify current APIs, and remove state/keyword migration branches and documentation.

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

* ci: avoid failed dataset probes and space download retries

Download the same existing v2 archives used by successful jobs without probing nonexistent package-version assets. Allow five download attempts 60 seconds apart; preserve the full matrix, datasets, noninteractive setup and failure propagation. Cover the exact archives and retry policy in offline CI tests.

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

* docs: add practical artifact migration examples

Show explicit consent for restored online components while preserving the original manager, schema-aware Arrow conversions, timezone-preserving ZoneInfo migration, and restricted round trips into a separate recorder. Explain shared-component scope, staging artifacts and trusted-source prerequisites.

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

* fix(workflow): preserve fresh artifact workflows with scoped trust

Keep immutable cache references intact at open rolling tails. Add explicit per-load record-template consent and per-artifact collector loading options without widening other reads. Restore the original template class after parent-load failures, and support audited business-frequency and NumPy record representations through exact pickle globals.

Document the scoped APIs and cover fresh trained-model reads, cache generation, numerical round trips and refusal paths.

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-08 17:49:08 +08:00
..

CI compatibility baseline

The three Python workflows share constraints.txt via a job-level, absolute PIP_CONSTRAINT path. It applies to every pip invocation, including installs inside make ci-install, later notebook/tool installs, and the released pyqlib package. A bound in this checkout's pyproject.toml alone does not constrain a package installed from PyPI. Keep the compatibility bounds in both files in sync; tests/test_ci_configuration.py checks this.

This is a set of known compatibility bounds, not a fully reproducible Python lockfile. The workflows run pip check and always print installed versions for future diagnosis. They retain all 25 OS/Python combinations per workflow and disable matrix fail-fast so that one failure does not hide the others.

Why these bounds exist

Dependency Constraint Failure addressed
MLflow <3.13 Qlib uses the filesystem tracking backend, disabled by default in newer versions.
filelock >=3.16.0,<3.30 Fork-safety checks conflict with DataQueue's producer thread and multiprocessing workers.
fastjsonschema <2.22 on Python 3.8/3.9 Python 3.10 union annotations are evaluated on older interpreters.
Plotly <7 Qlib still imports figure_factory.create_distplot.
lxml <6.1.3 The Windows job falls back to a source distribution without libxml2 headers.
OSQP ==1.0.5 on Windows/Python 3.8 Native imports crash with 1.1.3 and also with 0.6.7.post3.
Black <26.1 Use one compatible formatter for both Python and notebooks.

OSQP 1.0.5 is the wheel used by the successful full Windows/Python 3.8 job in PR #2308. It is selected during dependency resolution, not installed as a downgrade after an incompatible environment has already been built. All three workflows smoke test native solver imports rather than excluding Windows/Python 3.8.

MLFLOW_ALLOW_FILE_STORE=true retains the original PR's explicit file-backend opt-in. The upper bound also protects source installs outside CI. Migrating Qlib's tracking backend is a separate change.

Install only each test job's dependencies

CI calls make ci-install with an explicit CI_EXTRAS profile. The existing make dev target still installs the complete developer environment.

Source job Extras
Regular, Windows/macOS dev,test,analysis,lint
Regular, Ubuntu 24.04 dev,test,analysis,lint,rl
Regular, Ubuntu 22.04 dev,test,analysis,lint,rl,docs
Slow, Windows/macOS dev,test,analysis
Slow, Linux dev,test,analysis,rl

All source jobs additionally install test-requirements.txt: PyTorch is required for the neural-network tests even without the RL extra. NumPy/SciPy retain the compatibility bounds previously inherited from the RL/docs extras. Ubuntu jobs select CPU PyTorch wheels. torchvision/torchaudio are not used by the test suite and are no longer installed explicitly. No test job needs the package-building extra; docs dependencies are installed only where docs-gen runs. Lint checks stay in the existing regular source matrix.

tests/conftest.py already excludes RL tests on non-Linux platforms. Installing RL extras there used to pull in tianshou -> tensorboard -> grpcio, requiring expensive source builds to work around incorrect macOS wheel metadata and an Xcode 16.4 bundled-zlib build failure. Those jobs no longer install this unused dependency chain, so the grpcio source-build workaround has been removed. Linux retains the RL extra and the same RL tests. The PyPI workflow is unchanged.

Explicit imports of PyTorch, Qlib's PyTorch model registry, GeneralPTNN, and both dataset classes prevent missing dependencies from silently bypassing neural-network tests. On macOS, prepare LightGBM and its libomp runtime before these checks: importing the model package also eagerly imports LightGBM. The preparation step verifies the LightGBM native import before checking the model registry. A non-Linux footprint check rejects accidental reintroduction of tianshou/TensorBoard/grpcio, followed by the unchanged pip check. If a future test genuinely needs these dependencies on macOS, update this policy and validate native wheel metadata/imports rather than disabling the checks. This change does not reduce the matrix, alter test selection, add caches, or reduce test data sizes.

Network and workflow retries

Dataset commands name the existing v2 release archives explicitly: source jobs use qlib_data_simple_cn_1d_latest.zip, and PyPI jobs use qlib_data_cn_1d_latest.zip. These are the same datasets selected by successful version-probing downloads, not smaller substitutes. Naming the archive avoids an unnecessary request for a nonexistent package-version asset: the downloader can otherwise mistake an HTTP 504 response for confirmation that the file exists.

Download steps have at most five attempts, with a 15-minute timeout per attempt and a 60-second pause between failures, allowing transient GitHub download outages more time to recover than three closely spaced attempts. --delete_old False makes retries noninteractive in these fresh CI directories, including when an earlier attempt already extracted one dataset. Bash command blocks use set -euo pipefail, so an earlier failure cannot be hidden by a later successful command. Exhausted retries still fail the job.

The configuration-driven workflow has at most three 30-minute attempts. This is a bounded mitigation for the observed MLflow file-store error Metric 'IC' is malformed. No data found., not a fix for a suspected read/write race. A deterministic error must still fail all attempts; do not introduce continue-on-error or silently skip assertions. Fixing file-store consistency should be handled separately, with a reproducer.

The MLflow client performance test initializes the tracking store before timing repeated client construction, uses a monotonic performance clock, and retains the original 10ms (Linux) / 20ms (other platforms) thresholds. Cold plugin/store initialization is not a client-construction regression.

The PyPI workflow runs outside the checkout (RUNNER_TEMP) and passes the example YAML by absolute path, so imports test the installed distribution.

Title lint and maintenance

Checkout, Python setup, Node setup, and retry actions use Node 24-based releases (checkout@v6, setup-python@v6, setup-node@v6, retry@v4). The action runtime is separate from the Python and Node versions installed for tests.

Remote action references are pinned to full commit SHAs with version comments, including the download and workflow retry steps. When updating an action, verify its SHA against the upstream repository and update the version comment. The offline policy checks reject mutable action references. Dependabot checks GitHub Actions weekly and groups their version updates; its seven-day cooldown filters newly published versions, rather than setting the interval between PRs. Its explicit ci commit prefix and dependency scope keep generated PR titles aligned with the repository's conventional-commit title checks.

Node 22 satisfies commitlint's Node >=22.12 requirement. The root package.json and package-lock.json pin the title tooling and its transitive dependencies; CI uses npm ci --ignore-scripts and npx --no-install.

When updating a bound, update the package metadata and constraints together, run the offline policy checks, and verify the full matrix, including PyPI:

python -m unittest discover -s tests -p test_ci_configuration.py
npm ci --ignore-scripts --no-audit --no-fund
printf '%s\n' 'ci: update compatibility baseline' | npx --no-install commitlint --config .commitlintrc.js

For local pip installs, set PIP_CONSTRAINT to the absolute path of this directory's constraints.txt. Do not infer Windows/macOS runtime correctness from a Linux resolver check. Remove individual bounds only after reproducing and verifying the upstream fix on the affected environment.