Instructions to use testamentaria/nemo with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- NeMo
How to use testamentaria/nemo with NeMo:
# tag did not correspond to a valid NeMo domain.
- Notebooks
- Google Colab
- Kaggle
| # PoC β NeMo `setup_model()` target-allowlist bypass | |
| `malicious_target_bypass.nemo` is an uncompressed tar archive (`tarfile.open(mode="w:")`, the | |
| same mode NeMo itself uses to write `.nemo` files) containing: | |
| - `model_config.yaml` β a minimal config whose top-level `target` field is | |
| `subprocess.Popen`, a class completely unrelated to any NeMo model and **not** present in | |
| `nemo.core.classes.common.ALLOWED_TARGET_PREFIXES`. | |
| - `model_weights.ckpt` β a placeholder (not a real PyTorch checkpoint); it is never reached in | |
| this specific demonstration because `subprocess.Popen` has no `.restore_from()`. | |
| ## How this is meant to be used | |
| ``` | |
| python examples/asr/transcribe_speech.py model_path=malicious_target_bypass.nemo audio_dir=<any> | |
| ``` | |
| Expected effect, per the vulnerability described in the accompanying `SUBMISSION.md`: | |
| 1. `setup_model()` (`nemo/collections/asr/parts/utils/transcribe_utils.py:315-344`) calls | |
| `ASRModel.restore_from(restore_path=model_path, return_config=True)`, which extracts and | |
| parses `model_config.yaml` **without** ever calling `_is_target_allowed()` / | |
| `safe_instantiate()`. | |
| 2. `classpath = model_cfg.target` β `"subprocess.Popen"`. | |
| 3. `model_utils.import_class_by_path("subprocess.Popen")` succeeds β this is the raw, | |
| unprotected function; it imports `subprocess` and resolves `Popen` with no allowlist check | |
| at all. | |
| 4. `subprocess.Popen.restore_from(restore_path=..., map_location=...)` is then attempted and | |
| raises `AttributeError` (`Popen` has no `restore_from`), so the process crashes at this | |
| point. | |
| The crash on step 4 is the **intentionally non-destructive, minimum-guaranteed** demonstration: | |
| it proves the import of an arbitrary, attacker-chosen, out-of-allowlist class already happened | |
| (step 3) before anything fails. Swapping `subprocess.Popen` for a module with dangerous | |
| import-time side effects, or for a compatible internal NeMo model class, changes the outcome | |
| without changing the underlying bug. | |
| ## Caveat β not run end-to-end against a live NeMo installation | |
| The exact bypass mechanism (`import_class_by_path()` accepting `"subprocess.Popen"` while | |
| `_is_target_allowed()` rejects it) **was** empirically verified by importing and calling the | |
| real, unmodified functions from this commit's `nemo/utils/model_utils.py` and | |
| `nemo/core/classes/common.py` directly (see `attachments/poc_allowlist_bypass.py` + | |
| `attachments/run_output.txt` in the main submission folder). | |
| What was **not** independently re-verified is running `transcribe_speech.py` end-to-end against | |
| this exact `.nemo` file inside a full NeMo installation (setting up the complete pinned | |
| dependency stack β `lightning`, `nv_one_logger*`, `megatron-core`, etc. β proved impractical in | |
| the available verification environment due to unrelated third-party version incompatibilities | |
| in the `nv_one_logger` / `lightning` integration layer, not to anything about this bug). The | |
| static trace of `setup_model()` β `restore_from(..., return_config=True)` β | |
| `load_config_and_state_dict()` (`nemo/core/connectors/save_restore_connector.py:98-202`) was | |
| read in full and is quoted in the submission; only the live end-to-end run was not reproduced. | |
| If it does not reproduce cleanly against a specific NeMo install, the standalone function-level | |
| PoC remains valid, verified, independent evidence of the root-cause bug regardless. | |