Skip to content

ogar-doc-ir: resolve documents under a reader's access decision - #359

Merged
AdaWorldAPI merged 4 commits into
mainfrom
ccr-0455e606-wmtsor
Oct 14, 2026
Merged

AdaWorldAPI merged 4 commits into
mainfrom
ccr-0455e606-wmtsor

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

What

Documents can now be resolved for a specific reader. Two new entry points, resolve_slot_authorized and resolve_doc_authorized, take an Access { fields, admits }:

  • fields(class) -> WideFieldMask is the set of field positions the reader may see, per class. Callers should pass lance-graph-rbac's visible_fields(), which is EMPTY on a refused decision.
  • admits(key) -> bool is the decision's row scope. The field mask is per class, so this is what tells two objects of the same class apart.

Both entry points walk through lance-graph's walk_rails_authorized, merged in lance-graph#1558, so:

  • A hidden field is not emitted at any depth. A reader who may see a work package but not a user's e-mail gets the assignee's name without the e-mail. This holds both in the nested hop and in the user's own slot.
  • A hidden rail is not followed. Nothing behind a link the reader may not see is resolved.
  • Row scope applies at every hop. An object outside the scope contributes no fields, and nothing is reached through it.
  • A root the reader may not read resolves like a missing object. That covers a class with no visible field and an object outside the scope. It returns its snapshot fallback, or Unresolvable when there is none. It does not return Resolved with no fields, which would say the object exists and is empty.
  • Grid views are refused under an access decision. Grid cells have no field positions, so no field grant can say which cells a reader may see. Grids resolve only through resolve_slot.

resolve_slot and resolve_doc are unchanged. Internally both entry points delegate to one resolve_slot_with, with no decision.

Tests

Seven new tests in resolve::tests:

  • an_authorized_resolve_hides_a_nested_field_the_reader_may_not_see
  • an_authorized_resolve_does_not_follow_a_hidden_rail
  • an_unreadable_root_resolves_like_a_missing_object
  • a_grid_view_is_refused_under_an_access_decision
  • an_object_outside_the_row_scope_resolves_like_a_missing_one
  • a_rail_to_an_object_outside_the_row_scope_is_not_followed
  • resolve_doc_authorized_applies_the_mask_to_every_slot

Disable runs show each check is needed:

Check removed Tests that fail
Readable-root check 2 (1 for the scope part alone)
Authorized walk replaced by the plain walk 3
Grid refusal 1
Scoped field mask 1

A rail-target filter in the scoped graph turned out to be redundant: an out-of-scope object already has no fields, so the walk emits nothing and goes no further through it. That filter was removed.

Local checks:

  • cargo test passes for ogar-doc-ir, ogar-render-typst, ogar-from-docv1 and ogar-a2ui-frame.
  • cargo check --workspace --all-targets is clean.
  • clippy -D warnings and fmt --check are clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg


Generated by Claude Code

Documents now resolve under a reader's access decision. The new entry
points take allowed(class) -> WideFieldMask (lance-graph-rbac's
visible_fields(), EMPTY on a refused decision) and walk through
lance-graph's walk_rails_authorized: a hidden field is not emitted at any
depth and a hidden rail is not followed. A root the reader may not read
resolves like a missing object (fallback, else Unresolvable) instead of an
empty Resolved, and a grid view needs a readable root. resolve_slot and
resolve_doc are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
@AdaWorldAPI
AdaWorldAPI marked this pull request as ready for review October 14, 2026 22:58
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 762cd3a8-6076-414e-894a-757f64134eae
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.

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.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-14T23:01:10.872126Z e6a0e1d Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Some((root, root_view, root_class)) if let Some(grid) = source.grid_of(root, root_view) => {
if grid.is_well_formed() {
SlotOutcome::Grid {
class: root_class,
grid,
}

P1 Badge Do not return unmasked grids to field-limited readers

For an authorized grid request, any nonempty class mask passes the root check, after which this branch returns every axis label, measure, and cell from grid_of without intersecting them with allowed. Thus a reader permitted to see only one harmless field can receive an entire grid containing values outside that field grant—the added test even demonstrates that bit 0 alone unlocks the complete grid. Until grid dimensions and measures have mask positions, authorized resolution should fail closed or require an explicit full-grid permission rather than treating any readable field as permission for all cells.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/ogar-doc-ir/src/resolve.rs Outdated
Two gaps codex found in the authorized entry points:

- The field mask is per class, so it cannot tell two objects of one class
  apart. The entry points now take Access { fields, admits }: admits(key)
  is the decision's row scope. A root outside it resolves like a missing
  object, and through a scoped view of the graph an object outside it has
  no fields and no rail leads to it.
- A grid has no field positions, so any readable field opened every cell.
  Under an access decision a grid view is now refused (fallback, else
  Unresolvable); grids resolve only through resolve_slot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg
A disable run showed the rail_target filter is not load-bearing: an object
outside the scope already has no present fields, so the walk emits nothing
for it and follows none of its rails. The doc comments now say that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G22yT6htkcdyXsihxxXdrg

Copy link
Copy Markdown
Owner Author

On the Codex review's grid finding (any readable field returned the whole grid): agreed. Fixed in 4804b72.

Grid cells have no field positions, so no field grant can select among them. Under an access decision, a grid view is now refused: it returns the fallback if there is one, otherwise Unresolvable. Grids resolve only through resolve_slot.

a_grid_view_is_refused_under_an_access_decision checks that granting every field of the class still does not open the grid. It fails with the refusal removed.


Generated by Claude Code

@AdaWorldAPI
AdaWorldAPI merged commit 7c85d7e into main Oct 14, 2026
4 checks passed
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