Skip to content

sentinel: harden FFI strict parsing for trust boundary 🛡️#2672

Draft
EffortlessSteven wants to merge 1 commit into
mainfrom
sentinel-boundaries-3245254242784963356
Draft

sentinel: harden FFI strict parsing for trust boundary 🛡️#2672
EffortlessSteven wants to merge 1 commit into
mainfrom
sentinel-boundaries-3245254242784963356

Conversation

@EffortlessSteven

@EffortlessSteven EffortlessSteven commented Jun 21, 2026

Copy link
Copy Markdown
Member

The previous parsing logic for sub-objects like scan, lang, module, and export used args.get("scan").unwrap_or(args). If scan was passed as a non-object (e.g., a string or array), Value::get on a non-object returns None, silently acting like an empty object instead of returning an invalid_settings error. This broke the contract of strict configuration parsing across the FFI trust boundary and allowed bindings to bypass validation checks silently.

This PR introduces a strict get_settings_object validator and applies it across the configuration endpoints to ensure any non-object config value returns a proper invalid_settings error.


PR created automatically by Jules for task 3245254242784963356 started by @EffortlessSteven


Note

Medium Risk
Touches the untrusted FFI input boundary and changes behavior for malformed nested keys (previously silent success); well-formed callers are unaffected but bindings sending wrong types will now error.

Overview
Hardens the JSON FFI run_json path so nested mode blocks (scan, lang, module, export, analyze, cockpit, diff) cannot be passed as strings or arrays and still succeed with defaults.

Replaces scan_arg_object and args.get(field).unwrap_or(args) with get_settings_object, which treats a missing or null key as “use the top-level args object,” requires a JSON object when the key is present, and otherwise returns TokmdError::invalid_field (invalid_settings). All mode-specific parsers in settings_parse.rs now call this helper before reading fields.

Adds unit coverage for the helper in parse.rs and E2E contract tests in ffi_contract.rs (e.g. {"scan": "not an object", "paths": ["src"]} must fail instead of ignoring paths). Jules run metadata under .jules/runs/run-1/ documents the decision and verification receipts; no runtime behavior change for well-formed JSON.

Reviewed by Cursor Bugbot for commit 525a48b. Bugbot is set up for automated code reviews on this repo. Configure here.

Hardened the JSON FFI boundary against setting block evasion by rejecting invalid top-level component types. Replaced silent default fallbacks with strict error responses for invalid nested configuration objects (e.g. `{"scan": "not an object"}`).
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@EffortlessSteven, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 6 minutes and 51 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

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 credits.

🚦 How do rate limits work?

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

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, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4f45007a-7b95-48d2-95ca-3d6f6d5598fb

📥 Commits

Reviewing files that changed from the base of the PR and between a6b9118 and 525a48b.

📒 Files selected for processing (8)
  • .jules/runs/run-1/decision.md
  • .jules/runs/run-1/envelope.json
  • .jules/runs/run-1/pr_body.md
  • .jules/runs/run-1/receipts.jsonl
  • .jules/runs/run-1/result.json
  • crates/tokmd-core/src/ffi/parse.rs
  • crates/tokmd-core/src/ffi/settings_parse.rs
  • crates/tokmd-core/tests/ffi_contract.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sentinel-boundaries-3245254242784963356

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 and usage tips.

@factory-droid

factory-droid Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Droid finished @EffortlessSteven's task —— View job


Droid review complete for PR #2672.

LGTM. The PR correctly hardens the FFI trust boundary by introducing a strict get_settings_object validator that rejects non-object config values with invalid_settings, and applies it across all mode-specific parsers (scan/lang/module/export/analyze/cockpit/diff). All old args.get("X").unwrap_or(args) patterns in settings_parse.rs are replaced. The 16 ffi_contract tests pass, including the two new validation tests. No security or correctness findings.

No review comments were generated for this PR.

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request hardens the JSON FFI boundary in crates/tokmd-core by replacing silent default fallbacks with strict error responses when invalid nested configuration objects (such as scan, lang, module, etc.) are provided. It introduces a helper function get_settings_object to perform strict object validation and updates the parsing logic and tests accordingly. The review feedback suggests explicitly verifying that the top-level args is a JSON object in get_settings_object to prevent unexpected behavior, and moving the inline import in ffi_contract.rs to the top of the file to adhere to standard Rust style guidelines.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +11 to 20
pub(super) fn get_settings_object<'a>(
args: &'a Value,
field: &str,
) -> Result<&'a Value, TokmdError> {
match args.get(field) {
None | Some(Value::Null) => Ok(args),
Some(v) if v.is_object() => Ok(v),
Some(_) => Err(TokmdError::invalid_field(field, "an object")),
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

If args is not a JSON object (e.g., if it is a string, array, or null), args.get(field) will return None, which currently causes get_settings_object to return Ok(args) (a non-object). To prevent silent fallback to defaults and ensure robust defensive programming, we should explicitly verify that args is a JSON object before attempting to retrieve the field.

pub(super) fn get_settings_object<'a>(
    args: &'a Value,
    field: &str,
) -> Result<&'a Value, TokmdError> {
    if !args.is_object() {
        return Err(TokmdError::invalid_json("Expected a JSON object"));
    }
    match args.get(field) {
        None | Some(Value::Null) => Ok(args),
        Some(v) if v.is_object() => Ok(v),
        Some(_) => Err(TokmdError::invalid_field(field, "an object")),
    }
}

"error envelope must not have 'data'"
);
}
use serde_json::Value;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Imports should be placed at the top of the file rather than inline or in the middle of the file, to adhere to standard Rust style guidelines (idiomatic Rust / Rust Style Guide).

@github-actions

Copy link
Copy Markdown
Contributor

Glass Cockpit

Base: origin/main
Head: HEAD

Change Surface:

  • Files: 8
  • Insertions: 249
  • Deletions: 18

Composition:

  • Code: 28.6%
  • Test: 14.3%
  • Docs: 28.6%
  • Config: 28.6%

Contracts:

  • API: No
  • CLI: No
  • Schema: No

Health: 100/100 (A)
Risk: low (0/100)

Review Plan

  • .jules/runs/run-1/decision.md (priority: 2)
  • .jules/runs/run-1/pr_body.md (priority: 2)
  • .jules/runs/run-1/envelope.json (priority: 3)
  • .jules/runs/run-1/receipts.jsonl (priority: 3)
  • .jules/runs/run-1/result.json (priority: 3)
  • crates/tokmd-core/src/ffi/parse.rs (priority: 3)
  • crates/tokmd-core/src/ffi/settings_parse.rs (priority: 3)
  • crates/tokmd-core/tests/ffi_contract.rs (priority: 3)

Receipts

Full receipt data available in JSON format.

@cursor

cursor Bot commented Jun 21, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_ad60a59e-a929-4e4a-a8ef-ef646a721fd3)

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