{"id":"PYSEC-2026-1925","summary":"SKOPS Card.get_model happily allows arbitrary code execution","details":"## Summary\n\nThe `Card` class of `skops`, used for model documentation and sharing, allows arbitrary code execution. When a file other than `.zip` is provided to the `Card` class during instantiation, the internally invoked `Card.get_model` method silently falls back to `joblib` without warning. Unlike the `.skops` zip-based format, `joblib` permits unrestricted code execution, hence bypassing the security measures of `skops` and enabling the execution of malicious code.\n\n\n## Details\n\nThe `Card` class supports loading the model linked to the card using the `get_model` method. When a `.skops` model is provided, it uses the `load` function from `skops`, which includes security mechanisms. The `Card` class also supports consistent management of the `trusted` list, which can be passed during instance creation. As expected, if a `.skops` model is provided without a `trusted` list and an untrusted type is encountered during loading, an error is raised. This behavior is consistent with the security principles of `skops`.\n\nThe problem arises when a file format other than `.zip` is provided. As shown in the code snippet below, in this case, the `joblib` library is used to load the model. This happens **silently**, without any warning or indication that `joblib` is being used. This is a significant security risk because `joblib` does not enforce the same security measures as `skops`, allowing arbitrary code execution.\n\n```python\n# from `card/_model_card.py:354-358`\ntry:\n    if zipfile.is_zipfile(model_path):\n        model = load(model_path, trusted=trusted)\n    else:\n        model = joblib.load(model_path)\n```\n\nTo increase the concern, `get_model` is actually called internally by `skops` during card creation, so the user does not need to call it explicitly—only to create the `Card` object passing a `joblib` file.\n\n## PoC\n\nConsider the following example:\n\n```python\nfrom skops.card import Card\n\ncard = Card(\"model.skops\")\n```\n\nAn attacker could share a `model.skops` file that, despite its name, is **not** a `.zip` file. In this case, the `joblib.load` function is called, allowing arbitrary code execution if the file is actually a pickle-like object. This is difficult for the user to detect, as the check is based on the file’s format, not its extension or name.\n\nThis vulnerability exists regardless of the `trusted` list provided (or omitted) during `Card` instance creation, and is unaffected by any other parameters. Moreover, it occurs at the time of `Card` instantiation.\n\n## Attack Scenario\n\nAn attacker can craft a malicious model file that, when used to instantiate a `Card` object, enables **arbitrary code** on the victim’s machine. This requires no user interaction beyond instantiating the `Card` object (not even explicit loading). Given that `skops` is often used in collaborative environments and is designed with security in mind, this vulnerability poses a significant threat.\n\n## Attachments\nThe complete PoC is available on GitHub at [io-no/CVE-2025-54886](https://github.com/io-no/CVE-Reports/tree/main/CVE-2025-54886).","aliases":["CVE-2025-54886","GHSA-378x-6p4f-8jgm"],"modified":"2026-07-07T17:47:52.914365818Z","published":"2026-07-07T16:03:00.401309Z","references":[{"type":"WEB","url":"https://github.com/skops-dev/skops/security/advisories/GHSA-378x-6p4f-8jgm"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2025-54886"},{"type":"WEB","url":"https://github.com/skops-dev/skops/commit/29d61ea8a92f2bde6830e8f32cc72a1a87211cda"},{"type":"WEB","url":"https://github.com/io-no/CVE-Reports/tree/main/CVE-2025-54886"},{"type":"PACKAGE","url":"https://github.com/skops-dev/skops"},{"type":"PACKAGE","url":"https://pypi.org/project/skops"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-378x-6p4f-8jgm"}],"affected":[{"package":{"name":"skops","ecosystem":"PyPI","purl":"pkg:pypi/skops"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"0.13.0"}]}],"versions":["0.1","0.1.dev0","0.10.0","0.11.0","0.12.0","0.2","0.3.0","0.4.0","0.5.0","0.6.0","0.7.0","0.7.post0","0.8.0","0.9.0"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/skops/PYSEC-2026-1925.yaml"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"}]}