Skip to content

fix: restore cross-platform nightly CI safety#1704

Merged
mldangelo-oai merged 11 commits into
mainfrom
fix/nightly-ci-stale-integration-tests
Jun 26, 2026
Merged

fix: restore cross-platform nightly CI safety#1704
mldangelo-oai merged 11 commits into
mainfrom
fix/nightly-ci-stale-integration-tests

Conversation

@mldangelo

@mldangelo mldangelo commented Jun 21, 2026

Copy link
Copy Markdown
Member

Summary

  • align nightly integration assertions with hardened scanner outcomes while preserving malicious-positive and benign-negative coverage
  • reject premature pickle STOP opcodes inside Joblib NumPy wrappers so embedded pickles cannot terminate trusted-tail analysis early
  • fail closed when static Joblib NumPy-wrapper validation cannot complete, even if an embedded pickle backend reports clean
  • integrate the audited fix from fix(cache): normalize bypassed scan failures #1702 so unsuccessful direct cache bypasses retain fail-closed outcome metadata
  • validate Windows MLflow hardlinks with native file identities and classify protocol-relative sources before any UNC filesystem probe
  • make nightly Windows regressions platform-correct for optional dependencies, native paths, and descriptor-only race coverage without disabling the Windows lane
  • stabilize the final nightly-only assertions against documented fail-closed outcomes and skip live Hugging Face checks only when the service explicitly returns HTTP 429

Root causes

Nightly CI runs slow and integration tests skipped by the normal PR lane. Several expectations predated hardened detections and still required malicious fixtures to scan as clean. The first repaired nightly run then exposed three additional issues hidden by those early assertions:

  1. direct cache-bypass returns could skip unsuccessful-result normalization (the issue fixed independently in fix(cache): normalize bypassed scan failures #1702);
  2. the organized-asset timing ceiling was below the 73-88 second history already recorded by prior CI runs; and
  3. Windows integration tests mixed genuine platform defects with POSIX-only test mechanics.

The Windows audit found two implementation defects rather than test-only noise: MLflow's outside-hardlink check could miss NTFS aliases when relying on stat().st_nlink, and protocol-relative report sources were probed as UNC paths before redaction.

The second repaired nightly run passed all Python 3.10/3.12/3.13 jobs, Rust, performance, and Windows shard 1. Its remaining failures were two platform-dependent fail-closed picklescan outcomes plus three live Hugging Face requests that returned explicit HTTP 429 responses. The assertions now validate the actual fail-closed report semantics, and live tests skip only confirmed 429 rate limits; 404s and all other remote errors still fail.

The third nightly run passed all 11 other jobs and isolated its sole failure to a Windows report that correctly stayed suspicious and fail-closed as INCONCLUSIVE, but did not emit the narrower source-unavailable notice the test used to predict that status. The final assertion accepts only COMPLETE or INCONCLUSIVE, requires analysis_incomplete metadata for the latter, and still requires the exact NON_ALLOWLISTED_GLOBAL finding.

The fourth nightly run passed all 13 jobs. Standard Windows then failed twice on different outcomes in the same synthetic Joblib wrapper family: one malformed tail was accepted, while one valid multi-array payload was rejected. Both failures occurred on gw0; isolated execution and both nightly Windows shards passed. The scanner now normalizes any failed static wrapper validation to an explicit inconclusive result, and the synthetic fixtures patch wrapper-origin trust directly instead of depending on platform/source-cache state.

Existing PR audit

  • fix(cache): normalize bypassed scan failures #1702 directly fixed the cache normalization failure and is integrated here unchanged after review; it was mergeable and had no unresolved review threads.
  • fix(hashing): preserve deadline responsiveness #1710 addresses hashing deadline responsiveness and is orthogonal; its current branch is conflicting and its Windows lane fails the stale Joblib raw-tail assertion repaired here, so no code from it was imported.
  • dependency and release PRs do not touch the failing paths.

Validation

  • focused cache, Joblib, JFrog, MLflow, SBOM, SARIF, picklescan, and asset regressions pass
  • the original 42-test residual-nightly set and the final focused cross-platform/rate-limit regressions pass
  • organized-asset performance scan passes locally in 32 seconds under the calibrated 60-second local / 120-second CI ceiling
  • Ruff format and lint are clean across the repository
  • Linux-target mypy is clean across 479 source files
  • native macOS mypy reproduces only the five existing os.*xattr stub errors in unchanged CLI files
  • the initial full fast suite reached 4,566 passes; the final rerun reached 3,255 passes before the same xdist cache races, and serial reruns left only the pre-existing compressed-cache failure reproduced on untouched main
  • exact final head 4c8e384e: standard Python CI run 28233836558 passed all 16 applicable jobs (6 path-filtered jobs skipped), and nightly run 28233851478 passed all 13 jobs, including both Windows shards and performance

No detection was weakened. Descriptor-only race tests skip only on platforms without descriptor-walk APIs; Windows fallback-path coverage remains active.

Nightly CI runs integration and performance tests (-m "not performance" /
-m performance) that the PR lane (-m "not slow and not integration") skips.
A series of intentional detection-hardening changes merged between the last
green nightly (2b46042) and the first red one (77b2295) made several
nightly-only tests fail: they over-asserted result.success on inputs the
scanner now correctly flags, or checked for now-redacted literals.

These are test-only corrections. No scanner/product code changed and no
detection is weakened; each rewritten test still asserts the real security
signal (finding fires at the right severity, scan still discovers malicious
content, scan completes without operational errors).

- test_security_asset_integration: exclude malicious-by-design keras fixtures
  (loss/metric injection, custom-layer attack) from the safe-sample set;
  assert scan-ran / findings-present instead of success on exploit-laden
  inputs (agpl __main__ pickle, whole assets tree).
- test_integration, file-type validation, inventory, lazy-loading: replace
  the success-as-"scan completed" proxy with has_errors / files_scanned plus
  explicit detection assertions (e.g. the fail-closed CRITICAL TF SavedModel
  finding, the import-only allowlisted-global WARNING).
- test_network_comm_integration: assert the CRITICAL network finding still
  fires instead of the now-redacted "evil.com" literal; tolerate the correct
  absence of an INFO finding for inert benign metadata URLs while keeping the
  no-warning/no-critical invariant.
- performance: assert operational health (no errors, files scanned) on the
  adversarial corpus; replace a brittle absolute validation-time threshold
  with a relative one measured against the underlying scan baseline.
- picklescan future-pending-callback test: load concurrent.futures before the
  call-graph trust snapshot so the test deterministically exercises the lazy
  callback property. Late-loaded stdlib references correctly fail closed by
  design (covered by test_scan_bytes_fails_closed_for_late_loaded_stdlib_*),
  which is what intermittently flipped this test to SUSPICIOUS in CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Workflow run and artifacts

Performance Benchmarks

Compared 13 shared benchmarks with a regression threshold of 15%.
Status: 0 regressions, 0 improved, 13 stable, 0 new, 0 missing.
Aggregate shared-benchmark median: 4.296s -> 4.261s (-0.8%).

Workload Benchmark Target Size Files Baseline Current Change Status
nested-payload-review tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_nested_payload_review[nested_raw] nested_raw 78 B 1 237.6us 253.1us +6.5% stable
direct-malicious-upload tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_direct_malicious_upload malicious_reduce 52 B 1 190.1us 197.1us +3.7% stable
mixed-model-repository tests/benchmarks/test_scan_benchmarks.py::test_scan_release_candidate_repository release-candidate 547.3 KiB 32 635.32ms 654.96ms +3.1% stable
nested-payload-review tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_nested_payload_review[nested_base64] nested_base64 98 B 1 257.0us 262.3us +2.1% stable
rejected-basic-auth-candidates tests/benchmarks/test_scan_benchmarks.py::test_rejected_basic_auth_candidates_scan_linearly - 371.1 KiB 1 2.440s 2.392s -2.0% stable
warm-cache-rescan tests/benchmarks/test_scan_benchmarks.py::test_scan_warm_cached_repository_rescan release-candidate 547.3 KiB 32 142.09ms 144.25ms +1.5% stable
suspicious-pickle-intake tests/benchmarks/test_scan_benchmarks.py::test_scan_suspicious_pickle_intake suspicious-intake 183.8 KiB 4 149.45ms 151.43ms +1.3% stable
duplicate-heavy-registry tests/benchmarks/test_scan_benchmarks.py::test_scan_duplicate_registry_snapshot registry-snapshot 915.2 KiB 13 589.30ms 581.63ms -1.3% stable
nested-payload-review tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_nested_payload_review[nested_hex] nested_hex 130 B 1 268.2us 271.3us +1.1% stable
single-checkpoint-preflight tests/benchmarks/test_scan_benchmarks.py::test_scan_single_checkpoint_before_load single_checkpoint.pkl 183.0 KiB 1 106.57ms 105.75ms -0.8% stable
clean-training-checkpoint tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_clean_training_checkpoint safe_large 278.2 KiB 1 114.08ms 113.22ms -0.8% stable
padded-multi-stream-upload tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_padded_multi_stream_upload multi_stream_padded 4.1 KiB 1 307.3us 309.3us +0.7% stable
chunked-upload-stream tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_chunked_upload_stream chunked_stream 278.2 KiB 1 117.30ms 116.82ms -0.4% stable

Require operational-error-free outcomes in the corrected nightly tests, narrow malicious Keras fixture exclusions to explicit files, and add the required pytest annotations.
Mock every trust decision path exercised by call graph enrichment so the benign raw-array fixture does not depend on ambient module origin state or test ordering.
Require the safe Joblib parser to close every MARK scope before STOP so embedded pickle streams cannot terminate trusted wrapper analysis early. Make Joblib trust fixtures deterministic and cover the fail-closed result.
@mldangelo-oai mldangelo-oai changed the title test: align nightly integration tests with hardened detections fix: restore nightly CI and harden Joblib tail parsing Jun 25, 2026
@mldangelo-oai mldangelo-oai changed the title fix: restore nightly CI and harden Joblib tail parsing fix: restore cross-platform nightly CI safety Jun 26, 2026
@mldangelo-oai mldangelo-oai merged commit 9df81da into main Jun 26, 2026
49 checks passed
@mldangelo-oai mldangelo-oai deleted the fix/nightly-ci-stale-integration-tests branch June 26, 2026 15:27
@github-actions github-actions Bot mentioned this pull request Jun 26, 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