Skip to content

fix: default trust_remote_code to False in model_fine_tuner - #114

Merged
SHA888 merged 4 commits into
mainfrom
fix/remove-trust-remote-code
Jul 31, 2026
Merged

fix: default trust_remote_code to False in model_fine_tuner#114
SHA888 merged 4 commits into
mainfrom
fix/remove-trust-remote-code

Conversation

@SHA888

@SHA888 SHA888 commented Jul 31, 2026

Copy link
Copy Markdown
Owner

What

Changed both AutoTokenizer.from_pretrained and AutoModelForCausalLM.from_pretrained calls from trust_remote_code=True to trust_remote_code=False (the safe default). The # nosec B615 suppression comments remain because B615 flags missing revision=, not trust_remote_code — revision pinning is left to the caller.

Why

trust_remote_code=True allows a HuggingFace repo to execute arbitrary Python code on model load. Combined with the _resolve_hf_base_model() fallback that uses model_name verbatim as an HF repo id for unknown families, a bad config value or env var (EVOSEAL_CODER_MODEL) could execute arbitrary remote code locally.

The known HF base models (DeepSeek, Qwen, CodeLlama, Mistral) are standard architectures that do not require remote code execution — trust_remote_code=False is the correct default.

Changes

  • model_fine_tuner.py: trust_remote_code=TrueFalse in both loading calls
  • test_model_fine_tuner.py: Added 2 regression tests — mock-based verification that both from_pretrained calls receive trust_remote_code=False, plus behavior check when TRANSFORMERS_AVAILABLE is False

Verification

  • ruff format --check . ✅ (400 files formatted)
  • ruff check evoseal/ tests/ ✅ All checks passed
  • pytest tests/unit/fine_tuning/test_model_fine_tuner.py -q → 5 passed ✅

Closes TODO.md item: model_fine_tuner.py:160-178

Summary by CodeRabbit

  • Security

    • Improved model and tokenizer loading by disabling execution of untrusted remote code by default.
  • Bug Fixes

    • Reduced security risk during fine-tuning setup.
    • Preserved expected behavior when the Transformers package is unavailable.
  • Tests

    • Added coverage confirming remote code execution remains disabled for both loading operations.
    • Updated tokenization and security-related test documentation.

Changed both AutoTokenizer.from_pretrained and
AutoModelForCausalLM.from_pretrained calls from trust_remote_code=True
to trust_remote_code=False. The known HF base models (DeepSeek, Qwen,
CodeLlama, Mistral) are standard architectures that do not require
remote code execution.

Removed the # nosec B615 suppression comments since the security flag
is now safe by default.

The _resolve_hf_base_model() fallback still uses model_name verbatim
for unknown families, but without trust_remote_code=True it can no
longer execute arbitrary code from an untrusted repo.

Added 3 regression tests: source inspection for the safe default, and
behavior check when TRANSFORMERS_AVAILABLE is False.

Closes TODO.md item: model_fine_tuner.py:160-178
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@SHA888, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 34 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1dc0786a-73c5-4a19-be29-2259f96e89d3

📥 Commits

Reviewing files that changed from the base of the PR and between c219721 and 0ff2ad4.

📒 Files selected for processing (1)
  • tests/unit/fine_tuning/test_model_fine_tuner.py
📝 Walkthrough

Walkthrough

The fine-tuning loader now disables remote code execution for tokenizer and model loading. Tests verify the setting and unavailable-Transformers behavior. TODO tracking marks the security item complete and updates completion counts.

Changes

Remote-code execution control

Layer / File(s) Summary
Secure tokenizer and model loading
evoseal/fine_tuning/model_fine_tuner.py
Tokenizer and model loading now use trust_remote_code=False. Existing dtype and device-map behavior remains unchanged.
Security verification and tracking
tests/unit/fine_tuning/test_model_fine_tuner.py, TODO.md
Tests verify the security setting and behavior when Transformers is unavailable. TODO tracking marks the item complete and updates counts.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary security change: setting trust_remote_code to False in model_fine_tuner.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/remove-trust-remote-code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/unit/fine_tuning/test_model_fine_tuner.py`:
- Around line 89-107: Update test_initialize_returns_false_without_transformers
to accept the monkeypatch fixture and set the module-level
TRANSFORMERS_AVAILABLE flag to False before calling tuner.initialize_model().
Patch the module that defines ModelFineTuner.initialize_model, preserving the
existing assertions and avoiding real model loading.
- Around line 73-87: Strengthen test_source_uses_trust_remote_code_false to mock
both AutoTokenizer.from_pretrained and AutoModelForCausalLM.from_pretrained,
invoke ModelFineTuner.initialize_model, and assert each call receives
trust_remote_code explicitly as False in its keyword arguments. Replace the
source-text checks with call-level assertions so both loading call sites are
covered.
- Around line 1-6: Update the module docstring for ModelFineTuner tests to state
only that trust_remote_code=False prevents loading custom code from untrusted
model repositories; do not imply broader artifact or serialization safety unless
the covered loader configuration also enforces it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 194b1898-af39-4e87-9a5e-ae0e6aa8d065

📥 Commits

Reviewing files that changed from the base of the PR and between b60803f and 80485bc.

📒 Files selected for processing (3)
  • TODO.md
  • evoseal/fine_tuning/model_fine_tuner.py
  • tests/unit/fine_tuning/test_model_fine_tuner.py

Comment thread tests/unit/fine_tuning/test_model_fine_tuner.py
Comment thread tests/unit/fine_tuning/test_model_fine_tuner.py Outdated
Comment thread tests/unit/fine_tuning/test_model_fine_tuner.py
SHA888 added 2 commits July 31, 2026 04:47
B615 flags missing revision= parameter in from_pretrained(), not
trust_remote_code. The PR removed these suppressions along with the
trust_remote_code=True fix, but revision pinning is intentionally left
to the caller. Restore # nosec B615 on both loading calls and update
the regression test accordingly.
- Fix TODO.md changelog: nosec B615 comments were relocated, not removed;
  B615 flags missing revision=, not trust_remote_code
- Move tokenizer nosec B615 back to opening from_pretrained( line so bandit
  matches it to the correct line number
- Replace fragile inspect.getsource test with mock-based test that asserts
  trust_remote_code=False on both from_pretrained call kwargs
- Fix test_initialize_returns_false_without_transformers to actually patch
  TRANSFORMERS_AVAILABLE=False via monkeypatch
- Update PR body to correct inaccurate claim about removing nosec comments
@SHA888

SHA888 commented Jul 31, 2026

Copy link
Copy Markdown
Owner Author

Addressed review feedback:

1. TODO.md changelog mismatch — Fixed. The entry incorrectly said "removed the # nosec B615 suppression comments." They were relocated, not removed. B615 flags missing revision=, not trust_remote_code — the suppressions are still needed. Corrected the TODO.md entry and the PR body.

2. # nosec B615 moved to closing ) line — Fixed. Moved the comment back to the opening AutoTokenizer.from_pretrained( line where bandit reports the issue. The model call was already on one line so its placement was fine.

3. Hardcoding trust_remote_code=False removes override path — Acknowledged but not changed in this PR. The original TODO item was specifically to close the RCE vector. The known model families (DeepSeek, Qwen, CodeLlama, Mistral) are standard architectures. Adding a config knob for trust_remote_code would be scope creep — if someone needs it for a specific model, that should be a separate feature with proper allowlist semantics.

4. Fragile source-inspection test — Fixed. Replaced inspect.getsource + substring matching with a mock-based test that patches from_pretrained on both classes and asserts trust_remote_code=False in each call's kwargs — consistent with the existing test_passes_max_length_through_to_tokenizer style. Also fixed test_initialize_returns_false_without_transformers to actually patch TRANSFORMERS_AVAILABLE=False via monkeypatch instead of relying on the environment accidentally lacking peft.

Verification: ruff formatruff checkpytest 5/5 ✅

Addresses review feedback on PR #114: the existing tests only asserted the
kwarg value on mocked from_pretrained calls but never exercised the failure
path where a model that needs custom code raises on load.

The new test mocks from_pretrained to raise OSError (what transformers
actually raises when the auto-map can't resolve the architecture without
custom code) and verifies that:
- initialize_model() returns False
- is_initialized stays False
- Model load is not attempted (tokenizer fails first)
- The error is logged at ERROR level for operator diagnosis
@SHA888

SHA888 commented Jul 31, 2026

Copy link
Copy Markdown
Owner Author

Review feedback response

Point 1: Hardcoded trust_remote_code=False — consider allowlist/opt-in

Pushing back on this one. All5 currently-mapped base models use standard transformers architectures and don't need trust_remote_code=True:

Model Architecture Needs custom code?
deepseek-ai/deepseek-coder-6.7b-instruct LlamaForCausalLM No
Qwen/Qwen2.5-Coder-7B-Instruct Qwen2ForCausalLM No
codellama/CodeLlama-7b-Instruct-hf LlamaForCausalLM No
mistralai/Mistral-7B-Instruct-v0.2 MistralForCausalLM No

(Confirmed via HF API — all tagged library_name: transformers with standard model_type.)

Adding an opt-in trust_remote_code parameter to __init__ would reintroduce the exact RCE vector this PR closes — any caller (or misconfigured env var) could flip it back to True. The _resolve_hf_base_model() unknown-family fallback (L116-120) that uses model_name verbatim as an HF repo id is the attack surface; trust_remote_code=False is the fix.

If a future model genuinely needs custom code, the right path is: add it to HF_BASE_MODEL_BY_FAMILY with a pinned revision, or the operator sets base_model_path to a local path they've already vetted. That's a separate feature, not a regression from this change.

Point 2: Tests don't exercise the failure path

Valid — addressed. Added test_initialize_returns_false_when_model_needs_trust_remote_code which:

  • Mocks AutoTokenizer.from_pretrained to raise OSError (what transformers actually raises when the auto-map can't resolve architecture without custom code)
  • Asserts initialize_model() returns False and is_initialized stays False
  • Asserts model load was never attempted (tokenizer fails first)
  • Asserts the error is logged at ERROR level with "Error initializing model" for operator diagnosis

Point 3: How from_pretrained exceptions are surfaced

Already handled. initialize_model() wraps the entire body in try/except Exception (L186-188) which catches the error, logs it via logger.error(f"Error initializing model: {e}"), and returns False. The new failure-path test explicitly asserts this log message is emitted. The full method body is visible in the diff — there's no hidden code path that silently swallows the exception.

@SHA888
SHA888 merged commit 3ba3118 into main Jul 31, 2026
10 checks passed
@SHA888
SHA888 deleted the fix/remove-trust-remote-code branch July 31, 2026 06:21
SHA888 added a commit that referenced this pull request Jul 31, 2026
* docs: update workspace prompt files to reflect current state

- EVOLUTION.md: mark architecture doc and sequence diagram items done,
  update maturity caveat to reflect closed co-evolution loop, mark config
  validation and checkpoint save/restore tests done
- TODO.md: mark 'Adopt workspace prompt file conventions' done, update
  P3 (15→16) and total (61→62) tracking counts

* docs: update maturity caveat date stamp and add PR references

Address review feedback on PR #113:
- Update caveat date from 2026-07-24 to 2026-07-30 to match PR merge date
- Add references to PRs #73, #89 and TODO.md section so the co-evolution
  loop-closure claim is auditable from the doc itself

* fix(docs): correct config-validation date and reconcile P1/P2 summary counts

- EVOLUTION.md: fix config-validation test date from 2026-07-24 to 2026-07-30
  (actual commit 85e92c2 was 2026-07-30, not 2026-07-24)
- TODO.md: fix P1 done count from 14 to 13 (13 actual checked boxes)
- TODO.md: fix P2 done count from 21 to 24 (24 actual checked boxes)
- TODO.md: update total from 62 to 64 to match corrected per-priority counts

* fix: default trust_remote_code to False in model_fine_tuner (#114)

* fix: default trust_remote_code to False in model_fine_tuner

Changed both AutoTokenizer.from_pretrained and
AutoModelForCausalLM.from_pretrained calls from trust_remote_code=True
to trust_remote_code=False. The known HF base models (DeepSeek, Qwen,
CodeLlama, Mistral) are standard architectures that do not require
remote code execution.

Removed the # nosec B615 suppression comments since the security flag
is now safe by default.

The _resolve_hf_base_model() fallback still uses model_name verbatim
for unknown families, but without trust_remote_code=True it can no
longer execute arbitrary code from an untrusted repo.

Added 3 regression tests: source inspection for the safe default, and
behavior check when TRANSFORMERS_AVAILABLE is False.

Closes TODO.md item: model_fine_tuner.py:160-178

* fix: restore nosec B615 for revision pinning

B615 flags missing revision= parameter in from_pretrained(), not
trust_remote_code. The PR removed these suppressions along with the
trust_remote_code=True fix, but revision pinning is intentionally left
to the caller. Restore # nosec B615 on both loading calls and update
the regression test accordingly.

* fix: address review feedback on trust_remote_code PR

- Fix TODO.md changelog: nosec B615 comments were relocated, not removed;
  B615 flags missing revision=, not trust_remote_code
- Move tokenizer nosec B615 back to opening from_pretrained( line so bandit
  matches it to the correct line number
- Replace fragile inspect.getsource test with mock-based test that asserts
  trust_remote_code=False on both from_pretrained call kwargs
- Fix test_initialize_returns_false_without_transformers to actually patch
  TRANSFORMERS_AVAILABLE=False via monkeypatch
- Update PR body to correct inaccurate claim about removing nosec comments

* test: add failure-path test for trust_remote_code=False incompatibility

Addresses review feedback on PR #114: the existing tests only asserted the
kwarg value on mocked from_pretrained calls but never exercised the failure
path where a model that needs custom code raises on load.

The new test mocks from_pretrained to raise OSError (what transformers
actually raises when the auto-map can't resolve the architecture without
custom code) and verifies that:
- initialize_model() returns False
- is_initialized stays False
- Model load is not attempted (tokenizer fails first)
- The error is logged at ERROR level for operator diagnosis
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.

1 participant