Skip to content

fix(config): harden integer callback boundaries - #873

Closed
seonghobae wants to merge 6 commits into
mainfrom
fix/config-integer-callback-safety-872
Closed

fix(config): harden integer callback boundaries#873
seonghobae wants to merge 6 commits into
mainfrom
fix/config-integer-callback-safety-872

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Scope

Advances #872 by making public simulation and fit integer validation establish a package-trusted scalar identity before any caller-dispatchable coercion.

Test-first evidence

  • Protected base: fb67ced09d8ee00542c05d56374537a9a7239751.
  • RED head: 4c81e4dc465312d13f044b9b47e14d839af6cc1a.
  • Hosted RED CI run 31859346010 reproduced the trust-boundary defect: Python 3.12/3.14 failed with the new callback regressions while Rust, package, fuzz and GPU jobs were independently green. The Python 3.12 suite reported 14 failures, 3670 passes and 2 skips; the failures covered caller __index__ execution, accepted caller-defined integer subclasses, and fit controls that did not reject the adversarial values.
  • GREEN implementation head lineage culminates at exact current head e2375ce88ee0b456f483a0374db57aea894df4fa.

Contract

Validation now accepts exact built-in integers plus genuine supported NumPy integer scalar identities, normalizing admitted NumPy values to built-in integers only after trust is established. It rejects booleans, caller-defined int subclasses, NumPy integer subclasses, and arbitrary __index__ providers before their coercion hooks can execute.

The hardened simulation controls are n_persons, n_dims, items_per_dim, and latent_dim. The hardened fit controls are latent_dim, lbfgs_history, max_iter, n_restarts, q_theta, q_xi, q_u, m_steps, xi_points, and xi_seed; existing bounds and aggregate optimizer-work checks remain intact.

This is validation/marshalling only. No likelihood, estimator, quadrature, optimizer, recovery, uncertainty, or other psychometric/statistical arithmetic moves into Python.

Exact current evidence

  • Exact source head: e2375ce88ee0b456f483a0374db57aea894df4fa.
  • Exact-head CI is terminal-success across Python 3.12/3.14, Rust, package/release acceptance, fuzz including config validators, GPU parity, and the aggregate Python gate.
  • Exact-head security/static-analysis and required coverage/review checks are terminal with no failing, queued, or in-progress check in the current check-run snapshot.
  • No submitted formal review or unresolved review thread is present at this head.

Protected integration still requires the live independent non-author approval, last-push approval, conversation-resolution, and all exact-head required checks at decision time. Automated checks do not replace that approval, and predecessor-head evidence does not transfer if the source head moves.

Summary by CodeRabbit

  • Bug Fixes

    • Hardened integer configuration validation for simulation and fitting settings.
    • Rejects booleans, custom integer subclasses, and arbitrary index providers without invoking conversion callbacks.
    • Continues to support built-in integers and NumPy integer values.
  • Documentation

    • Added documentation and changelog details covering the updated validation behavior and supported configuration values.

@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dba1ab24-3c4d-49a7-a6bb-0db930948339

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8c51eddb-ac79-4d79-bc58-dcdfaec945c4

📥 Commits

Reviewing files that changed from the base of the PR and between fb67ced and e2375ce.

📒 Files selected for processing (4)
  • docs/changelog.d/872-config-integer-callback-safety.md
  • docs/doctoring/config_integer_callback_safety.md
  • python/fast_mlsirm/config.py
  • tests/test_config_integer_callback_safety.py

📝 Walkthrough

Walkthrough

Configuration validation now rejects caller-controlled integer subclasses and arbitrary __index__ providers. It accepts built-in integers and supported NumPy integer scalars across simulation and fit settings, with regression tests and documentation.

Changes

Configuration integer safety

Layer / File(s) Summary
Trusted integer boundary
python/fast_mlsirm/config.py
Adds _trusted_integer, which accepts exact built-in integers and supported NumPy integer scalars, then converts accepted NumPy values to built-in integers.
Configuration validation and coverage
python/fast_mlsirm/config.py, tests/test_config_integer_callback_safety.py, docs/doctoring/config_integer_callback_safety.md, docs/changelog.d/872-config-integer-callback-safety.md
Applies trusted validation to simulation size, optimizer settings, quadrature, M-step, and latent-integration controls. Tests cover hostile providers, custom subclasses, built-in integers, and NumPy integers. Documentation records the validation rules.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to e2375

The change hardens integer validation at public simulation and fit boundaries, and the current head is merge-ready after normal checks and review with no actionable merge-blocking risk remaining.

Possibly related issues

  • ContextualWisdomLab/fast-mlsirm issue 872 — Directly covers hardening MLS2PLMConfig.validate and FitConfig.validate against callback-triggering integer-like inputs.
  • ContextualWisdomLab/fast-mlsirm issue 870 — Addresses similar integer callback hardening in different configuration and assembly controls.

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: hardening integer validation against unsafe callback boundaries.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/config-integer-callback-safety-872

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.

@seonghobae
seonghobae marked this pull request as ready for review August 15, 2026 02:48
@seonghobae
seonghobae enabled auto-merge (squash) August 15, 2026 02:48

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

OpenCode cannot approve yet because required coverage evidence did not pass.

Review outcome

1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence

  • Problem: The required coverage-evidence job result was failure, so OpenCode cannot establish approval sufficiency for this head.

  • Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.

  • Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present.

  • Result: REQUEST_CHANGES

  • Reason: coverage-evidence result was failure, so required test/docstring evidence was not proven for current head e2375ce88ee0b456f483a0374db57aea894df4fa.

  • Head SHA: e2375ce88ee0b456f483a0374db57aea894df4fa

  • Workflow run: 31939283649

  • Workflow attempt: 1

Coverage evidence

Coverage evidence job did not run or did not publish coverage evidence.

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Docs (2 files)"]
  S1 --> I1["operator or user guidance"]
  I1 --> R1["Review risk: Docs (2 files)"]
  R1 --> V1["docs review"]
  Evidence --> S2["Changed file: config.py"]
  S2 --> I2["repository behavior"]
  I2 --> R2["Review risk: Changed file: config.py"]
  R2 --> V2["required checks"]
  Evidence --> S3["Test: test_config_integer_callback_safety.py"]
  S3 --> I3["regression suite"]
  I3 --> R3["Review risk: Test: test_config_integer_callback_safety.py"]
  R3 --> V3["targeted test run"]
Loading

@opencode-agent

Copy link
Copy Markdown
Contributor

OpenCode Review Overview

  • Head SHA: e2375ce88ee0b456f483a0374db57aea894df4fa
  • Workflow run: 31939283649
  • Workflow attempt: 1
  • Gate result: REQUEST_CHANGES (approval step)

Pull request overview

OpenCode cannot approve yet because required coverage evidence did not pass.

Review outcome

1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence

  • Problem: The required coverage-evidence job result was failure, so OpenCode cannot establish approval sufficiency for this head.

  • Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.

  • Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports success with required evidence or explicit no-source not-applicable evidence.

  • Regression test: Keep the approval branch checking needs.coverage-evidence.result == success before posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present.

  • Result: REQUEST_CHANGES

  • Reason: coverage-evidence result was failure, so required test/docstring evidence was not proven for current head e2375ce88ee0b456f483a0374db57aea894df4fa.

  • Head SHA: e2375ce88ee0b456f483a0374db57aea894df4fa

  • Workflow run: 31939283649

  • Workflow attempt: 1

Coverage evidence

Coverage evidence job did not run or did not publish coverage evidence.

Changed-File Evidence Map

flowchart LR
  PR["PR changed files"] --> Evidence["OpenCode bounded evidence"]
  Evidence --> S1["Docs (2 files)"]
  S1 --> I1["operator or user guidance"]
  I1 --> R1["Review risk: Docs (2 files)"]
  R1 --> V1["docs review"]
  Evidence --> S2["Changed file: config.py"]
  S2 --> I2["repository behavior"]
  I2 --> R2["Review risk: Changed file: config.py"]
  R2 --> V2["required checks"]
  Evidence --> S3["Test: test_config_integer_callback_safety.py"]
  S3 --> I3["regression suite"]
  I3 --> R3["Review risk: Test: test_config_integer_callback_safety.py"]
  R3 --> V3["targeted test run"]
Loading

@opencode-agent
opencode-agent Bot disabled auto-merge August 16, 2026 10:49
@seonghobae
seonghobae marked this pull request as draft August 16, 2026 11:03

Copy link
Copy Markdown
Contributor Author

@opencode-agent Please re-review exact unchanged head e2375ce88ee0b456f483a0374db57aea894df4fa against the current central review implementation. The effective CHANGES_REQUESTED came from central run 31939283649 at .github workflow SHA 6eb06cdd08c79a06f7b390069d4ffa49e2eb7dba; its first causal boundary was materialize_base_python_requirements.py failing before PR tests with trusted uv archive download failed: HTTPError. Central protected main is now c47afc2dc68488292c1db7c9d6f82dcd5360f181. Review only; do not update this branch or merge.

@seonghobae
seonghobae marked this pull request as ready for review August 16, 2026 14:22
@cursor

cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

seonghobae and others added 6 commits August 16, 2026 15:32
Strix VULN-0001 failed the current head because MLS2PLMConfig and
FitConfig could be constructed with values that bypass memory-safety
bounds until a later explicit validate() call. Run the existing
validators from __post_init__ so invalid objects cannot exist, and keep
validate() public and idempotent.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
@cursor
cursor Bot force-pushed the fix/config-integer-callback-safety-872 branch from e2375ce to 805df46 Compare August 16, 2026 15:34

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Head 805df46 is not merge-ready. Construction-time validate() is the right fail-closed hook, but two buyer-facing holes remain on this exact surface.

  1. seed and verbose never pass _trusted_integer. MLS2PLMConfig(seed=HostileIndex()) and FitConfig(seed=HostileIndex()) construct. Save-time int(config.seed) in io.py dispatches __index__. config.seed + restart in fit.py TypeErrors on a hostile object and wraps np.uint8(250) + 10 to 4.
  2. Admitted narrow NumPy integers stay on the frozen fields. MLS2PLMConfig(n_dims=np.uint8(16), items_per_dim=np.uint8(16)) validates, then n_items becomes 0 and simulate() cannot match the published size product.

Doctoring that says untrusted controls cannot exist as constructed objects is false on this head.

Repair is already on successor #939 (0814458f): seed/verbose go through the same allowlist, trusted ints are written back, hostile __index__ coverage includes seed/verbose/q_*, and a 2-by-256 simulation shape check fails if n_items wraps. Prefer #939. Close this PR as superseded after #939 is green with a non-author approval.

Do not merge this head. Do not self-approve. Exact-head CI on 805df46 does not transfer to #939. OpenCode CHANGES_REQUESTED on e2375ce was coverage-evidence tooling, not this defect.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues


def __post_init__(self) -> None:
"""Reject invalid simulation controls at construction."""
self.validate()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

validate() here still skips seed and never writes trusted integers back. A hostile or np.uint8 seed survives construction; int(config.seed) and config.seed + restart remain live callback/wrap sites. Successor #939 stores built-in ints after the allowlist.

if value_type is int:
return value
if any(value_type is trusted_type for trusted_type in _NUMPY_INTEGER_SCALAR_TYPES):
return int(value)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

int(value) is only used for local bounds. n_items still multiplies the stored fields, so np.uint8(16) * np.uint8(16) wraps to 0 after a successful validate. Write the trusted built-in ints back before simulate() reads n_items.

@seonghobae seonghobae closed this Aug 16, 2026
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