Skip to content

fix(ggml): detect embedded ZIP polyglot payloads#1780

Open
mldangelo-oai wants to merge 1 commit into
mainfrom
fix/ggml-zip-polyglot-coverage
Open

fix(ggml): detect embedded ZIP polyglot payloads#1780
mldangelo-oai wants to merge 1 commit into
mainfrom
fix/ggml-zip-polyglot-coverage

Conversation

@mldangelo-oai

Copy link
Copy Markdown
Contributor

Summary

  • close a clean-scan security bypass affecting all five supported legacy GGML model signatures
  • reuse the existing bounded ZIP preflight, nested pickle analysis, scanner-selection handling, and S908 polyglot reporting
  • add failing-first malicious-pickle/path-traversal regressions plus a benign invalid-ZIP near match

Validation

  • 876 passed: GGUF, Llamafile, and Joblib scanner suites
  • 131 passed: release workflow regression suite
  • 8 passed: targeted new GGML and existing ZIP policy/preflight regressions
  • repository-wide Ruff lint and formatting checks pass
  • changed-file mypy passes

The broader scanner run also reaches one unrelated compressed-wrapper cache baseline failure, and full local macOS mypy reports existing out-of-scope platform/type errors; current-main GitHub Python CI and Type Check are green.

Copilot AI review requested due to automatic review settings July 22, 2026 22:11
@mldangelo-oai mldangelo-oai added codex security Security-related issues and vulnerabilities codex-automation labels Jul 22, 2026
@github-actions

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.269s -> 4.286s (+0.4%).

Workload Benchmark Target Size Files Baseline Current Change Status
clean-training-checkpoint tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_clean_training_checkpoint safe_large 278.2 KiB 1 112.75ms 110.20ms -2.3% stable
padded-multi-stream-upload tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_padded_multi_stream_upload multi_stream_padded 4.1 KiB 1 339.1us 335.6us -1.0% stable
direct-malicious-upload tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_direct_malicious_upload malicious_reduce 52 B 1 223.0us 224.9us +0.9% stable
chunked-upload-stream tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_chunked_upload_stream chunked_stream 278.2 KiB 1 114.02ms 113.15ms -0.8% stable
single-checkpoint-preflight tests/benchmarks/test_scan_benchmarks.py::test_scan_single_checkpoint_before_load single_checkpoint.pkl 183.0 KiB 1 104.48ms 103.71ms -0.7% stable
rejected-basic-auth-candidates tests/benchmarks/test_scan_benchmarks.py::test_rejected_basic_auth_candidates_scan_linearly - 371.1 KiB 1 2.441s 2.457s +0.7% stable
mixed-model-repository tests/benchmarks/test_scan_benchmarks.py::test_scan_release_candidate_repository release-candidate 547.3 KiB 32 626.12ms 629.64ms +0.6% stable
nested-payload-review tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_nested_payload_review[nested_hex] nested_hex 130 B 1 304.5us 302.8us -0.6% stable
nested-payload-review tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_nested_payload_review[nested_base64] nested_base64 98 B 1 294.2us 293.1us -0.4% stable
nested-payload-review tests/benchmarks/test_picklescan_benchmarks.py::test_picklescan_nested_payload_review[nested_raw] nested_raw 78 B 1 276.4us 277.3us +0.3% stable
duplicate-heavy-registry tests/benchmarks/test_scan_benchmarks.py::test_scan_duplicate_registry_snapshot registry-snapshot 915.2 KiB 13 568.88ms 570.65ms +0.3% stable
warm-cache-rescan tests/benchmarks/test_scan_benchmarks.py::test_scan_warm_cached_repository_rescan release-candidate 547.3 KiB 32 152.94ms 153.08ms +0.1% stable
suspicious-pickle-intake tests/benchmarks/test_scan_benchmarks.py::test_scan_suspicious_pickle_intake suspicious-intake 183.8 KiB 4 147.35ms 147.37ms +0.0% stable

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Closes a security bypass in the GGUF/GGML scanner by ensuring legacy GGML variants also trigger the existing ZIP-polyglot preflight and embedded-archive inspection path, with regressions to prevent recurrence.

Changes:

  • Extend GGUF ZIP polyglot inspection helper to support GGML context labeling and invoke it from GGML scans.
  • Add regression tests covering embedded ZIP polyglots across all legacy GGML magic variants plus an invalid-ZIP near match.
  • Document the security fix in the Unreleased changelog.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
modelaudit/scanners/gguf_scanner.py Parameterizes ZIP polyglot detection labeling and enables it for GGML scans.
tests/scanners/test_gguf_scanner.py Adds malicious polyglot regressions for GGML variants and a benign invalid-ZIP near-match test.
CHANGELOG.md Notes the GGML ZIP/pickle inspection security hardening under Unreleased.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@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: a8ee962c4c

ℹ️ 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".

"""Basic GGML file validation with security checks."""
result.metadata["format"] = "ggml"
result.metadata["magic"] = magic.decode("ascii", "ignore")
self._scan_zip_polyglot(result, format_name="GGML")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve nested ZIP byte accounting

When a compressed ZIP member expands beyond the outer file size, this call merges the member scan's bytes_scanned, but _scan_ggml later replaces it with file_size. In a directory containing several GGML polyglots whose individual uncompressed totals are below max_total_size, aggregate accounting can therefore remain below --max-size and continue scanning past the safety budget; retain at least the merged count, as _scan_gguf does, and cover this budget outcome.

AGENTS.md reference: AGENTS.md:L137-L137

Useful? React with 👍 / 👎.

"""Basic GGML file validation with security checks."""
result.metadata["format"] = "ggml"
result.metadata["magic"] = magic.decode("ascii", "ignore")
self._scan_zip_polyglot(result, format_name="GGML")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore outer GGML identity after ZIP merging

When a ZIP member is itself a GGUF/GGML model, merge_executable_zip_container_findings() merges that child's metadata into result; because the outer format and magic are assigned before this call, the child can replace them. For example, a GGMF polyglot containing nested.gguf is reported with format == "gguf", while a nested GGML variant can replace the outer magic. Move the outer identity assignments after the merge or restore them so reports describe the scanned outer artifact.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex codex-automation security Security-related issues and vulnerabilities

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants