{"id":"CVE-2026-122014","kind":"vulnerability","aliases":["CVE-2026-122014"],"related":[],"title":"npcpy: model fine-tuning loader lets remote input run injected code","description":"A vulnerability has been found in NPC-Worldwide npcpy up to 2.1.27. This affects the function load_sft_model of the file npcpy/ft/sft.py. The manipulation of the argument base_model_name_or_path leads to code injection. The attack can be initiated remotely. The exploit has been disclosed to the public and may be used.","summary":"load_sft_model() in npcpy/ft/sft.py reads base_model_name_or_path from the adapter's adapter_config.json and passes it to from_pretrained with trust_remote_code=True hardcoded. If the named base-model repo ships a modeling_*.py file, it runs in the loading process before the adapter weights load. The reporter's PoC shows this. As of 2026-11-04 the issue is open and the latest release (2.1.27) is still listed as affected.","severity":"high","cvss":7.5,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"npcpy","fixed":[],"affected":"<= 2.1.27","ecosystem":"pypi"}],"ecosystems":[],"tags":["ai-tooling","fine-tuning","llm","npcpy","pypi","python","trust-remote-code"],"not_affected_if":["hard-coded-model-path","no-sft-usage","trusted-adapters-only"],"how_to_check":"- Run `pip show npcpy`: 2.1.27 and earlier are affected, and 2.1.27 is the latest release, so no fixed version exists yet.\n- Search your code for `load_sft_model` (also used by npcpy/ft/model_ensembler.py). Check whether the adapter directory or Hub repo id you pass in can come from someone you do not trust.\n- Open the adapter's adapter_config.json and check that base_model_name_or_path points to a base model you chose. Look for auto_map entries and .py files in that base repo.","fix":null,"status":"open","published_at":"2026-11-04T21:18:07+00:00","first_seen_at":"2026-11-04T21:49:18+00:00","triaged_at":"2026-11-04T21:50:55+00:00","updated_at":"2026-11-04T22:13:30+00:00","category":"ai-tooling","confidence":0.8,"triage":{"at":"2026-11-04T21:50:55Z","model":"claude-haiku-5-5","output":{"fix":"unknown","reach":"unknown","title":"npcpy: model fine-tuning loader lets remote input run injected code","impact":"high","summary":"npcpy is a Python framework for building LLM agents and NPCs. Its load_sft_model function in npcpy/ft/sft.py lets an attacker inject code through the base_model_name_or_path argument, so a crafted value can run code on the host.","audience":"developers","category":"ai-tooling","nicknames":[],"who_cares":["python","npcpy","llm","fine-tuning"],"confidence":0.6,"fix_status":"unknown","needs_auth":"unknown","worst_case":"code runs on the host running npcpy","analyst_note":"Confirm the vulnerable call path and whether any fixed npcpy release exists; check the NVD entry and the npcpy repository for a patch or commit.","how_to_check":"- Check the installed npcpy version with `pip show npcpy`; versions up to and including `2.1.27` are listed as affected.\n- Search your code for calls to `load_sft_model` and check whether `base_model_name_or_path` can come from user input, an API request or a config file.","plain_action":"","needs_analyst":true,"not_affected_if":["hard-coded-model-path","no-sft-usage"],"who_is_affected":"Teams that use npcpy for fine-tuning and call load_sft_model with a model path that an outside party can influence are exposed. Projects that only use npcpy for inference, or that hard-code the model path, have less exposure, but the sources do not say which code paths are safe. The sources do not state a fixed version.","user_interaction":"unknown","needs_special_setup":"unknown","severity_if_unknown":"unknown"},"cost_usd":0.00094,"input_tokens":4132,"output_tokens":1054,"severity_before":"high"},"analyst_status":"done","analyst_note":"Confirm the vulnerable call path and whether any fixed npcpy release exists; check the NVD entry and the npcpy repository for a patch or commit.","search":"'-04':95B '-11':94B '-122014':15A,18A '-2026':14A,17A '2.1.27':104B,122C '2026':93B 'adapt':47B,82B 'adapter_config.json':49B 'affect':109B,124C 'ai':21B 'ai-tool':20B 'argument':138C 'attack':149C 'base':40B,66B,139C 'base-model':65B 'code':12A,33B,59B,146C 'cve':13A,16A 'disclos':158C 'exploit':155C 'file':73B,132C 'fine':4A,24B 'fine-tun':3A,23B 'found':114C 'function':126C 'hardcod':61B 'initi':152C 'inject':11A,147C 'input':9A 'issu':97B 'latest':102B 'lead':144C 'let':7A 'list':107B 'llm':26B 'load':34B,78B,84B,127C 'loader':6A 'manipul':135C 'may':163C 'model':2A,36B,41B,67B,71B,129C,140C 'name':42B,64B,141C 'npc':117C 'npc-worldwid':116C 'npcpi':1A,27B,119C 'npcpy':19A 'npcpy/ft/sft.py':38B,133C 'open':99B 'pass':51B 'path':44B,143C 'poc':88B 'pretrain':55B 'process':79B 'public':161C 'py':72B 'pypi':28B 'python':29B 'read':39B 'releas':103B 'remot':8A,32B,58B,153C 'repo':68B 'report':86B 'run':10A,75B 'sft':35B,128C 'ship':69B 'show':89B 'still':106B 'tool':22B 'true':60B 'trust':31B,57B 'trust-remote-cod':30B 'tune':5A,25B 'use':165C 'vulner':111C 'weight':83B 'worldwid':118C","nicknames":[],"mitigations":[],"who_affected":"Teams that call npcpy's load_sft_model on an adapter they did not train themselves (a Hugging Face Hub adapter, a colleague's or an agent's output, or a user-supplied path) are exposed. Loading such an adapter runs Python from the base-model repo it names, with the loader's privileges and access to its environment variables and cloud credentials. You can skip this if you only load adapters you trained yourself on a base model you chose, or if you do not use npcpy's fine-tuning module.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":"none","audience":"developers","plain_action":null,"pkeys":["npcpy"],"ptokens":["npcpy"],"labels":["aitooling","finetuning","high","llm","npcpy","pypi","python","trustremotecode"],"campaign_type":null,"first_public_at":"2026-11-04T21:18:07+00:00","kev":null,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","cvss_by":"cna@vuldb.com","poc_count":0,"sources":[{"source":"nvd","external_id":"CVE-2026-122014","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-122014","severity":null,"published_at":"2026-11-04T21:18:07+00:00","modified_at":"2026-11-04T21:18:07+00:00","first_seen_at":"2026-11-04T21:49:18+00:00","fetched_at":"2026-11-04T21:49:18+00:00","note":null},{"source":"analyst","external_id":"CVE-2026-122014:1711","url":"https://github.com/NPC-Worldwide/npcpy/issues/308","severity":null,"published_at":"2026-11-04T21:52:45+00:00","modified_at":"2026-11-04T21:52:45+00:00","first_seen_at":"2026-11-04T21:52:45+00:00","fetched_at":"2026-11-04T21:52:45+00:00","note":"Primary: maintainer-repo issue #308 (researcher 3em0, PoC; maintainer cagostino acknowledged 2026-10-11, no fix yet). PyPI/GitHub latest is 2.1.27, so no fixed release. NVD says remote attack; actual trigger is loading a poisoned adapter. Nothing marked verified (researcher report, not a vendor advisory). Posts not searched.","cites":["https://github.com/NPC-Worldwide/npcpy/issues/308","https://pypi.org/project/npcpy/"]}],"documents":[{"id":70877,"url":"https://github.com/NPC-Worldwide/npcpy/issues/308","title":"load_sft_model() hardcodes trust_remote_code=True (npcpy #308)","kind":"issue","source":"analyst","publisher":null,"text":null,"published_at":null,"relation":"advisory","confidence":null,"note":null},{"id":70878,"url":"https://pypi.org/project/npcpy/","title":"npcpy on PyPI (latest 2.1.27)","kind":"release","source":"analyst","publisher":null,"text":null,"published_at":null,"relation":"mentions","confidence":null,"note":null}],"exploit_sources":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}