Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,35 @@ reviews:
profile: chill
collapse_walkthrough: true
poem: false
path_instructions:
- path: "modelopt/**/*.py"
instructions: &security_instructions |
Review all modelopt package and examples Python changes against the security coding practices in
SECURITY.md. Flag any of the following as CRITICAL security issues,
request changes, and fail the check if ANY are present:
1. torch.load(..., weights_only=False) with no inline comment justifying why it is safe
(e.g. confirming the file is internally-generated and not user-supplied).
2. numpy.load(..., allow_pickle=True) with no inline comment justifying why it is safe.
Should expose allow_pickle as a caller-configurable parameter defaulting to False, not hardcode True.
3. trust_remote_code=True hardcoded for transformers model or tokenizer loading.
Code should expose it as a caller-configurable parameter defaulting to False, not hardcode True.
4. eval() or exec() on any input that could originate from outside the process.
5. Any use of "# nosec" comments to bypass Bandit security checks is not allowed.
If a security-sensitive pattern is genuinely necessary, the PR must be reviewed and approved
by @NVIDIA/modelopt-setup-codeowners with an explicit justification in the PR description.
- path: "examples/**/*.py"
instructions: *security_instructions
auto_review:
auto_incremental_review: false
auto_incremental_review: true
drafts: false
base_branches: ["main", "release/.*", "feature/.*"]
pre_merge_checks:
custom_checks:
- name: "Security anti-patterns"
mode: "error"
instructions: *security_instructions
knowledge_base:
code_guidelines:
filePatterns:
- "CONTRIBUTING.md"
- "SECURITY.md"
29 changes: 15 additions & 14 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
## What does this PR do?
### What does this PR do?

**Type of change:** ? <!-- Use one of the following: Bug fix, new feature, new example, new tests, documentation. -->
Type of change: ? <!-- Use one of the following: Bug fix, new feature, new example, new tests, documentation. -->

**Overview:** ?
<!-- Details about the change. -->

## Usage
<!-- You can potentially add a usage example below. -->
### Usage

```python
# Add a code snippet demonstrating how to use this
```

## Testing
### Testing
<!-- Mention how have you tested your change if applicable. -->

## Before your PR is "*Ready for review*"
<!-- If you haven't finished some of the above items you can still open `Draft` PR. -->
### Before your PR is "*Ready for review*"

- **Make sure you read and follow [Contributor guidelines](https://github.com/NVIDIA/Model-Optimizer/blob/main/CONTRIBUTING.md)** and your commits are signed.
- **Is this change backward compatible?**: Yes/No <!--- If No, explain why. -->
- **Did you write any new necessary tests?**: Yes/No
- **Did you add or update any necessary documentation?**: Yes/No
- **Did you update [Changelog](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?**: Yes/No <!--- Only for new features, API changes, critical bug fixes or bw breaking changes. -->
Make sure you read and follow [Contributor guidelines](https://github.com/NVIDIA/Model-Optimizer/blob/main/CONTRIBUTING.md) and your commits are signed (`git commit -s -S`).

## Additional Information
Make sure you read and follow the [Security Best Practices](https://github.com/NVIDIA/Model-Optimizer/blob/main/SECURITY.md#security-coding-practices-for-contributors) (e.g. avoiding hardcoded `trust_remote_code=True`, using `torch.load(..., weights_only=True)`, avoiding `pickle`, etc.).

- Is this change backward compatible?: ✅ / ❌ / N/A <!--- If ❌, explain why. -->
- If you copied code from any other source, did you follow IP policy in [CONTRIBUTING.md](https://github.com/NVIDIA/Model-Optimizer/blob/main/CONTRIBUTING.md#-copying-code-from-other-sources)?: ✅ / ❌ / N/A <!--- Mandatory -->
- Did you write any new necessary tests?: ✅ / ❌ / N/A <!--- Mandatory for new features or examples. -->
- Did you update [Changelog](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?: ✅ / ❌ / N/A <!--- Only for new features, API changes, critical bug fixes or backward incompatible changes. -->

### Additional Information
<!-- E.g. related issue. -->
53 changes: 32 additions & 21 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,36 +39,41 @@ To run the pre-commit hooks without committing, use:
pre-commit run --all-files
```

## 📝 Writing tests
## 🔒 Security coding practices

We use [pytest](https://docs.pytest.org/) for all tests. The tests are organized into the following directories:
All contributors must follow the security coding practices documented in *Security Coding Practices for
Contributors* section of [SECURITY.md](./SECURITY.md#security-coding-practices-for-contributors) page.

- `tests/unit`: Fast cpu-based unit tests for the core ModelOpt library. They should not take more than a few seconds to run.
- `tests/gpu`: Fast GPU-based unit tests for the core ModelOpt library. In most cases, they should not take more than a few seconds to run.
- `tests/examples`: Integration tests for ModelOpt examples. They should not take more than a few minutes to run. Please refer to [example test README](./tests/examples/README.md) for more details.
Any security-sensitive exception requires review and approval from `@NVIDIA/modelopt-setup-codeowners`.

Please refer to [tox.ini](./tox.ini) for more details on how to run the tests and their dependencies.
## 📋 Copying code from other sources

### Code Coverage
The utilization of third-party code requires authorization via the Open Source Review Board (OSRB) team and needs to follow proper guidance on contributing code.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

For any new features / examples, make sure to they are covered by the tests and that the Codecov coverage check in your PR passes.
If you are an external contributor, seek guidance from `@NVIDIA/modelopt-setup-codeowners` for next steps. For internal contributors, follow the steps below:

## Submitting your code
- **File NVBug for use of open-source code:**
Clone NVBug 2885977 and add your use case. Copying code from permissive licensed repositories (e.g. MIT, Apache 2) is generally self-checkout but for other licenses, it is necessary to get expert guidance before merging your PR.
- **License header format:** The file which has code copied from another third-party GitHub repository should have the following in order:
1. A reference link (with commit hash) to the source from which the code was copied.
1. The original repository's Copyright / License.
1. The NVIDIA Apache 2.0 Copyright / License header.

- If you are an external contributor, create a fork of the repository.
- Rebase (not merge) your code to the most recent commit of the `main` branch. We want to ensure a linear history;
see [Merge vs Rebase](https://www.atlassian.com/git/tutorials/merging-vs-rebasing). Remember to test again locally after rebasing to catch any new issues before pushing to your PR.
See [`modelopt/torch/speculative/eagle/utils.py`](./modelopt/torch/speculative/eagle/utils.py)
for an example of the correct license header format.
- **Exclude from license pre-commit hook:** Exclude copied files from the license pre-commit hook so it doesn't auto-add the NVIDIA Apache 2.0 license on top of the file. Add the file path to the `exclude` list in the `insert-license` hook in [`.pre-commit-config.yaml`](./.pre-commit-config.yaml).

```bash
git pull
git rebase origin/main
git push origin <branch> --force-with-lease
```
## 📝 Writing tests

- When pushing the rebased (or any) branch, use `git push --force-with-lease` instead of `git push --force`.
- Submit a pull request and let auto-assigned reviewers (based on [CODEOWNERS](./.github/CODEOWNERS)) review your PR.
- If any CI/CD checks fail, fix the issues and push again.
- Once your PR is approved and all checks pass, one of the reviewers will merge the PR.
We use [pytest](https://docs.pytest.org/) for all tests. For any new features / examples, make sure to add tests and that the coverage check in your PR passes. The tests are organized into the following directories:

- `tests/unit`: Fast cpu-based unit tests for the core ModelOpt library. They should not take more than a few seconds to run.
- `tests/gpu`: Fast GPU-based unit tests for the core ModelOpt library. In most cases, they should not take more than a few seconds to run.
- `tests/gpu_megatron`: Fast GPU-based unit tests for the core ModelOpt library for Megatron-Core features. In most cases, they should not take more than a few seconds to run.
- `tests/gpu_trtllm`: Fast GPU-based unit tests for the core ModelOpt library for TensorRT-LLM features. In most cases, they should not take more than a few seconds to run.
- `tests/examples`: Integration tests for ModelOpt examples. They should not take more than a few minutes to run. Please refer to [example test README](./tests/examples/README.md) for more details.

Please refer to [tox.ini](./tox.ini) for more details on how to run the tests and their dependencies.

## ✍️ Signing your work

Expand Down Expand Up @@ -135,3 +140,9 @@ git push origin <branch> --force-with-lease

(d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved.
```

## Submitting your code

- Submit a pull request and let auto-assigned reviewers (based on [CODEOWNERS](./.github/CODEOWNERS)) review your PR.
- If any CI/CD checks fail, fix the issues and push again.
- Once your PR is approved and all checks pass, one of the reviewers will merge the PR.
148 changes: 147 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,150 @@ While NVIDIA currently does not have a bug bounty program, we do offer acknowled

## NVIDIA Product Security

For all security-related concerns, please visit NVIDIA's [Product Security portal](https://www.nvidia.com/en-us/security)
For all security-related concerns, please visit NVIDIA's [Product Security portal](https://www.nvidia.com/en-us/security).

---

## Security Considerations

### Overview

NVIDIA Model Optimizer (ModelOpt) is a library used to optimize ML models and may load and process user-provided artifacts (models, weights, configs, calibration data) and their dependencies. Secure deployment depends on how you source artifacts, validate inputs, and harden the environment where ModelOpt runs.

### What to Be Aware Of

#### Untrusted model and data inputs

- Models, weights, configs and data may be malicious or corrupted.

#### Deserialization and code-execution risks

- Unsafe deserialization can lead to arbitrary code execution if fed untrusted inputs.
- Avoid using serialization formats/settings that can deserialize arbitrary objects.

#### Input validation and resource exhaustion

- Large or malformed inputs can trigger crashes or excessive CPU/GPU/memory use.
- Missing size/type checks can increase DoS risk.

#### Data in transit and at rest

- If fetching models or dependencies over the network, insecure transport can enable tampering.
- Stored artifacts, logs, and caches may contain sensitive data.

#### Logging and observability

- Logs may inadvertently contain sensitive inputs, paths, tokens, or proprietary model details.
- Overly verbose logs can leak operational and security-relevant information.

#### Supply chain and third-party components

- Dependencies may include known vulnerabilities or be compromised.
- Third-party plugins/components loaded at runtime may not have the same security assurances.

### Example Security Approaches

#### Artifact integrity

- Only load artifacts from trusted sources.
- Prefer signed artifacts; verify signatures before loading.

#### Safe parsing and deserialization

- Prefer safer storage formats (avoid object deserialization for untrusted inputs).
- Avoid `pickle`, `torch.load()` with untrusted weights, or YAML `unsafe_load`.
- Treat any unverified artifact as untrusted and block/guard its loading.

#### Hardening and least privilege

- Run with least privilege and isolate workloads.

#### Data protection

- Encrypt sensitive data at rest; use TLS 1.3 for data in transit.
- Never hardcode or log credentials.

#### Resilience

- Validate inputs and enforce limits (file size, timeouts, quotas, etc.).
- Keep OS, containers, and dependencies patched; scan for known vulnerabilities.

---

## Security Coding Practices for Contributors

ModelOpt processes model checkpoints and weights from various sources. Contributors must avoid patterns that can introduce security vulnerabilities. These rules apply to all code except tests. These rules cover a few key security considerations as follows:

### Deserializing untrusted data

**Do not use `torch.load(..., weights_only=False)`** unless a documented exception is provided. It uses pickle under the hood and can execute arbitrary code from a malicious checkpoint.

```python
# Bad — allows arbitrary code execution from the checkpoint file
state = torch.load(path, weights_only=False)

# Good
state = torch.load(path, weights_only=True, map_location="cpu")

# Acceptable only with an inline comment explaining why weights_only=False
# is required and confirming the file is internally-generated / trusted.
state = torch.load(
path,
weights_only=False, # loaded file is generated internally by ModelOpt and not supplied by the user
map_location="cpu",
)
```

**Do not use `numpy.load(..., allow_pickle=True)`** unless a documented exception is provided. It uses pickle under the hood and can execute arbitrary code from a malicious checkpoint.

```python
# Bad — allows arbitrary code execution from the checkpoint file
state = numpy.load(path, allow_pickle=True)

# Good - let the caller decide; default to False
def load_data(path: str, trust_data: bool = False):
return numpy.load(path, allow_pickle=trust_data)
```

**Do not use `yaml.load()`** — always use `yaml.safe_load()`. The default loader can execute arbitrary Python objects embedded in YAML.

### Loading transformers models with `trust_remote_code`

**Do not hardcode `trust_remote_code=True`.** This flag tells Transformers to execute arbitrary Python shipped with a checkpoint, which is an RCE vector if the model source is untrusted.

```python
# Bad — silently opts every user into remote code execution
model = AutoModel.from_pretrained(name, trust_remote_code=True)

# Good — let the caller decide; default to False
def load_model(name: str, trust_remote_code: bool = False):
return AutoModel.from_pretrained(name, trust_remote_code=trust_remote_code)
```

### Subprocess and shell commands

**Never use `shell=True` with string interpolation or user-supplied input.** This is a command-injection vector.

```python
# Bad — command injection if model_name contains shell metacharacters
subprocess.run(f"python convert.py --model {model_name}", shell=True)

# Good — pass arguments as a list
subprocess.run(["python", "convert.py", "--model", model_name])
```

### Other patterns to avoid

- **`eval()` / `exec()`** on strings derived from external input. If you must generate and execute code dynamically, validate the input against an allowlist of safe patterns.
- **Hardcoded secrets or credentials** — never commit tokens, passwords, or API keys. Use environment variables or config files listed in `.gitignore`.

### Bandit security checks

Bandit is used as a pre-commit hook to check for security-sensitive patterns in the code. **`# nosec` comments are not allowed** as a bypass for security checks.

### Creating a security exception

If a security-sensitive pattern (e.g. `pickle`, `subprocess`) is genuinely required, the contributor must:

1. **Add an inline comment** explaining *why* the pattern is necessary and *why* it is safe in this specific context (e.g. "loaded file is generated internally by ModelOpt").
1. **Request review from [@NVIDIA/modelopt-setup-codeowners](https://github.com/orgs/NVIDIA/teams/modelopt-setup-codeowners)** and include a clear justification in the PR description.
4 changes: 1 addition & 3 deletions docs/source/guides/2_save_load.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,7 @@ Here is the example workflow of restoring the ModelOpt-modified model architectu
model = ...

# Restore the model architecture using the saved `modelopt_state`
# Security NOTE: weights_only=False is used here on ModelOpt-generated state_dict, not on untrusted user input
modelopt_state = torch.load("modelopt_state.pth", weights_only=False)
model = mto.restore_from_modelopt_state(model, modelopt_state)
model = mto.restore_from_modelopt_state(model, modelopt_state_path="modelopt_state.pth")

# Load the model weights separately after restoring the model architecture
custom_method_to_load_model_weights(model)
Expand Down
78 changes: 0 additions & 78 deletions docs/source/reference/2_security.rst

This file was deleted.

Loading