Skip to content

python: add hf-trust-remote-code rule (HuggingFace trust_remote_code=True RCE)#83

Open
DevamShah wants to merge 2 commits into
trailofbits:mainfrom
DevamShah:add-hf-trust-remote-code
Open

python: add hf-trust-remote-code rule (HuggingFace trust_remote_code=True RCE)#83
DevamShah wants to merge 2 commits into
trailofbits:mainfrom
DevamShah:add-hf-trust-remote-code

Conversation

@DevamShah

Copy link
Copy Markdown

Add hf-trust-remote-code rule: arbitrary code execution via Hugging Face trust_remote_code=True

Summary

Adds a Python rule, hf-trust-remote-code, that flags loading Hugging Face transformers / datasets assets with trust_remote_code=True, the highest-impact untracked remote-code-execution vector in the ML-supply-chain category this repo already covers (pickle, joblib, keras, ONNX, torch).

Problem / motivation

trust_remote_code=True instructs transformers/datasets to download and execute arbitrary Python shipped inside a model or dataset repository (modeling_*.py, configuration_*.py, dataset loading scripts) at load time. A malicious, typosquatted, or compromised Hub repository therefore achieves remote code execution on the loading host, the same trust boundary as the pickle deserialization rules already in this directory, but reached through an explicit, greppable keyword argument rather than an opaque serialized blob.

The repository covers pickle-based RCE across PyTorch, NumPy, Pandas, TensorFlow, Keras, ONNX, and scikit/joblib, yet has no rule for trust_remote_code, which is the most common code-execution switch in modern LLM and dataset loading code. Hugging Face's own documentation warns to "take extra precaution when loading a custom model" and to pin a reviewed revision, but provides no static enforcement. This rule closes that gap.

Change

  • python/hf-trust-remote-code.yaml — new rule following the existing ML-supply-chain rule conventions (category: security, subcategory: [vuln], severity: ERROR, impact: HIGH, confidence: MEDIUM, >- multiline message, backticked identifiers, languages: [python]).
  • python/hf-trust-remote-code.py — test fixture with 9 true positives (ruleid:) and 9 true negatives (ok:), ordered simple-to-complex per CONTRIBUTING.
  • README.md — regenerated python rules table (python rules_table_generator.py); single-row insertion.

Detection covers pattern-either:

  • $OBJ.from_pretrained(..., trust_remote_code=True, ...) — covers AutoModel*, AutoTokenizer, AutoConfig, AutoFeatureExtractor, AutoProcessor, and model-specific classes uniformly.
  • pipeline(...) and transformers.pipeline(...).
  • load_dataset(...) and datasets.load_dataset(...).

False-positive controls (2+ matchers): every call shape has a paired pattern-not: ... trust_remote_code=False, and the rule matches the literal True (not a bare metavariable). Combined with Semgrep constant propagation this flags provably-true values, ignores explicit False, and does not fire on omitted arguments, the safe default.

Security rationale

  • CWE-94: Improper Control of Generation of Code (Code Injection) — untrusted code from a remote repository is fetched and executed in-process.
  • OWASP LLM Top 10 (2025): LLM03 Supply Chain — model/dataset provenance and untrusted custom code are explicitly called out; trust_remote_code=True is the canonical in-code manifestation.
  • MITRE ATT&CK T1195.002 (Supply Chain Compromise: Software Supply Chain) — malicious Hub repos and dependency confusion / typosquatting map directly to this sink.
  • This is a deliberate-flag, not a heuristic: the keyword argument is HF-specific, so positives are high-signal. Pinning a reviewed revision (HF's recommended mitigation) does not remove the risk and is intentionally still flagged for review.

Testing / validation

Run from a clean clone with the rule and fixture in place (semgrep 1.159.0):

$ semgrep --validate --metrics=off --config ./python/hf-trust-remote-code.yaml
Configuration is valid - found 0 configuration error(s), and 1 rule(s).

$ semgrep --test --test-ignore-todo --metrics=off \
    --config ./python/hf-trust-remote-code.yaml ./python/hf-trust-remote-code.py
1/1: ✓ All tests passed

$ semgrep --metrics=off --json --config ./python/hf-trust-remote-code.yaml \
    ./python/hf-trust-remote-code.py | jq '.results | length'
9   # exactly the 9 ruleid lines; 0 matches on the 9 ok lines

README table regenerated with the repo's own python rules_table_generator.py; diff is a single inserted row, alphabetically placed between automatic-memory-pinning and lxml-in-pandas.

References used: Hugging Face custom-models documentation and the Trail of Bits ML supply-chain writeup, per CONTRIBUTING's reference guidance.

🤖 Generated with Claude Code

…True RCE)

Signed-off-by: Devam Shah <devamshah91@gmail.com>
…True RCE)

Signed-off-by: Devam Shah <devamshah91@gmail.com>
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants