Skip to content

enforce column-level read permissions across queries and responses - #1866

Merged
Artuomka merged 2 commits into
mainfrom
backend_agent_table_public_permissions
Aug 5, 2026
Merged

enforce column-level read permissions across queries and responses#1866
Artuomka merged 2 commits into
mainfrom
backend_agent_table_public_permissions

Conversation

@Artuomka

@Artuomka Artuomka commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened column-level access controls for table reads, filtering, sorting, searching, and CSV exports.
    • Prevented restricted columns from influencing query results, including binary and hex searches.
    • Requests with no readable columns now fail safely.
    • Ensured responses expose only permitted columns.
  • Tests

    • Added coverage for restricted-column behavior, anonymous access, filtering, searching, ordering, and exports.

Copilot AI review requested due to automatic review settings August 5, 2026 14:10
@Artuomka
Artuomka enabled auto-merge August 5, 2026 14:10
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Column read permissions now constrain filtering, ordering, searching, DAO settings, row reads, CSV exports, and response projections. New utilities provide shared validation and restriction logic, with unit and end-to-end coverage.

Changes

Readable-column enforcement

Layer / File(s) Summary
Readable-column restriction utilities
backend/src/entities/table/utils/restrict-query-to-readable-columns.util.ts
Adds fail-closed validation, readable table-structure filtering, and DAO settings restrictions for excluded and search fields.
Query and export integration
backend/src/entities/table/table-pure-crud-operations/use-cases/pure-get-rows-from-table.use.case.ts, backend/src/entities/table/table-pure-crud-operations/use-cases/pure-read-row-from-table.use.case.ts, backend/src/entities/table/use-cases/export-csv-from-table.use.case.ts
Builds filters, ordering, searches, and table settings from readable columns. Retains response projection as defense in depth.
Restriction validation and regression coverage
backend/test/ava-tests/non-saas-tests/non-saas-restrict-query-to-readable-columns.test.ts, backend/test/ava-tests/non-saas-tests/non-saas-agents-microservice-public-permissions-e2e.test.ts
Tests empty permissions, structure and settings restrictions, anonymous CRUD filtering, searching, pagination, and readable-column results.

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

Sequence Diagram(s)

sequenceDiagram
  participant TableUseCase
  participant CedarPermissionsService
  participant DAO
  TableUseCase->>CedarPermissionsService: Resolve readableColumns
  TableUseCase->>TableUseCase: Build queryableStructure and restricted settings
  TableUseCase->>DAO: Execute bounded query or export
  DAO-->>TableUseCase: Return rows or export data
Loading

Possibly related PRs

Suggested reviewers: copilot, lyubov-voloshko

Poem

A rabbit checked each column’s gate,
And bound the query before its state.
Hidden fields stayed out of sight,
Readable rows returned just right.
Tests hopped through every case.

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main change: enforcing column-level read permissions at the query and response stages, which matches the core objective of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Security Check ✅ Passed PR enforces column-level read permissions at query time (filters, search, ordering) via three complementary utility functions (assertSomeColumnReadable, readableTableStructure, restrictTableSetting...
✨ 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 backend_agent_table_public_permissions

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.

Copilot AI 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

This PR strengthens column-level read permissions in the backend by ensuring “readable columns” constrain not only the response projection but also the underlying query (filters, search, ordering, and select lists), preventing information leaks via pagination.total and row selection behavior.

Changes:

  • Introduces shared helpers to (1) fail closed on empty readable-column sets, (2) reduce table structure to readable columns for parsing, and (3) restrict DAO table settings to readable columns.
  • Updates table row retrieval and CSV export use-cases to apply readable-column restrictions before parsing filters/ordering and before executing queries.
  • Adds unit and e2e tests to pin the security behavior for public/anonymous access paths and settings restriction logic.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
backend/test/ava-tests/non-saas-tests/non-saas-restrict-query-to-readable-columns.test.ts New unit tests for the readable-column restriction helpers.
backend/test/ava-tests/non-saas-tests/non-saas-agents-microservice-public-permissions-e2e.test.ts New e2e coverage ensuring public readable columns bound query behavior (filters/search) and response projection.
backend/src/entities/table/utils/restrict-query-to-readable-columns.util.ts Adds helper utilities to enforce readable columns at query-construction time and fail closed on empty readable sets.
backend/src/entities/table/use-cases/export-csv-from-table.use.case.ts Applies readable-column constraints to CSV export query parsing/settings and keeps response projection as defense-in-depth.
backend/src/entities/table/table-pure-crud-operations/use-cases/pure-read-row-from-table.use.case.ts Adds fail-closed behavior when no readable columns exist for a row read.
backend/src/entities/table/table-pure-crud-operations/use-cases/pure-get-rows-from-table.use.case.ts Applies readable-column constraints before filter/order parsing and enforces readable-only DAO settings.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 111 to 117
if (isHexString(searchingFieldValue)) {
searchingFieldValue = hexToBinary(searchingFieldValue) as any;
tableSettings.search_fields = tableStructure
// Readable columns only — a binary search must not reach a withheld column either.
tableSettings.search_fields = queryableStructure
.filter((field) => isBinary(field.data_type))
.map((field) => field.column_name);
}
Comment on lines 90 to +94
? await this.cedarPermissions.getReadableColumns(userId, connectionId, tableName, allColumnNames)
: await this.cedarPermissions.getReadableColumnsForPublic(connectionId, tableName, allColumnNames);
// Fail closed (plan 13 P0-3): no readable column ⇒ 403, not a 200 carrying an empty object —
// the 200-vs-400 outcome of the primary-key lookup is itself a row-existence signal.
assertSomeColumnReadable(readableColumns);

@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: 1

🧹 Nitpick comments (3)
backend/test/ava-tests/non-saas-tests/non-saas-restrict-query-to-readable-columns.test.ts (1)

14-16: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use an arrow helper.

Replace the structure function declaration with a const arrow function.

As per coding guidelines: “Prefer arrow functions over function declarations.”

🤖 Prompt for 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.

In
`@backend/test/ava-tests/non-saas-tests/non-saas-restrict-query-to-readable-columns.test.ts`
around lines 14 - 16, Replace the structure function declaration with a
const-bound arrow function while preserving its variadic string parameters,
TableStructureDS return type, and existing columnNames.map behavior.

Source: Coding guidelines

backend/src/entities/table/utils/restrict-query-to-readable-columns.util.ts (1)

35-66: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use arrow functions for the new helper exports.

Replace the three export function declarations with export const arrow functions.

As per coding guidelines: “Prefer arrow functions over function declarations.”

🤖 Prompt for 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.

In `@backend/src/entities/table/utils/restrict-query-to-readable-columns.util.ts`
around lines 35 - 66, Convert the exported helpers assertSomeColumnReadable,
readableTableStructure, and restrictTableSettingsToReadableColumns from function
declarations to exported const arrow functions, preserving their parameters,
return types, bodies, and behavior.

Source: Coding guidelines

backend/test/ava-tests/non-saas-tests/non-saas-agents-microservice-public-permissions-e2e.test.ts (1)

320-325: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use an arrow helper and annotate extraQuery.

Replace anonymousCrudRowsRequest with a const arrow function. Declare extraQuery as string.

As per coding guidelines: “Prefer arrow functions over function declarations” and “Always add type annotations to function parameters and return types in TypeScript.”

🤖 Prompt for 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.

In
`@backend/test/ava-tests/non-saas-tests/non-saas-agents-microservice-public-permissions-e2e.test.ts`
around lines 320 - 325, Update anonymousCrudRowsRequest to use a const arrow
function instead of a function declaration, and add an explicit string type
annotation to extraQuery while keeping the existing request.Test return type and
other parameter types unchanged. Anchor the change around
anonymousCrudRowsRequest so the helper matches the TypeScript style guidelines
without altering its behavior.

Source: Coding guidelines

🤖 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 `@backend/src/entities/table/use-cases/export-csv-from-table.use.case.ts`:
- Around line 111-116: Update the binary-search branch in the table export flow
to assign the filtered binary column names to
builtDAOsTableSettings.search_fields, which is the settings object passed to
dao.getTableRowsStream, instead of tableSettings.search_fields. Add a CSV
regression test covering configured text search_fields combined with a
hexadecimal search on a binary column.

---

Nitpick comments:
In `@backend/src/entities/table/utils/restrict-query-to-readable-columns.util.ts`:
- Around line 35-66: Convert the exported helpers assertSomeColumnReadable,
readableTableStructure, and restrictTableSettingsToReadableColumns from function
declarations to exported const arrow functions, preserving their parameters,
return types, bodies, and behavior.

In
`@backend/test/ava-tests/non-saas-tests/non-saas-agents-microservice-public-permissions-e2e.test.ts`:
- Around line 320-325: Update anonymousCrudRowsRequest to use a const arrow
function instead of a function declaration, and add an explicit string type
annotation to extraQuery while keeping the existing request.Test return type and
other parameter types unchanged. Anchor the change around
anonymousCrudRowsRequest so the helper matches the TypeScript style guidelines
without altering its behavior.

In
`@backend/test/ava-tests/non-saas-tests/non-saas-restrict-query-to-readable-columns.test.ts`:
- Around line 14-16: Replace the structure function declaration with a
const-bound arrow function while preserving its variadic string parameters,
TableStructureDS return type, and existing columnNames.map behavior.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1eb552ce-2838-41a4-a7a9-eb086875fa5e

📥 Commits

Reviewing files that changed from the base of the PR and between 8ec0bc2 and b289ecb.

📒 Files selected for processing (6)
  • backend/src/entities/table/table-pure-crud-operations/use-cases/pure-get-rows-from-table.use.case.ts
  • backend/src/entities/table/table-pure-crud-operations/use-cases/pure-read-row-from-table.use.case.ts
  • backend/src/entities/table/use-cases/export-csv-from-table.use.case.ts
  • backend/src/entities/table/utils/restrict-query-to-readable-columns.util.ts
  • backend/test/ava-tests/non-saas-tests/non-saas-agents-microservice-public-permissions-e2e.test.ts
  • backend/test/ava-tests/non-saas-tests/non-saas-restrict-query-to-readable-columns.test.ts

Comment on lines 111 to 116
if (isHexString(searchingFieldValue)) {
searchingFieldValue = hexToBinary(searchingFieldValue) as any;
tableSettings.search_fields = tableStructure
// Readable columns only — a binary search must not reach a withheld column either.
tableSettings.search_fields = queryableStructure
.filter((field) => isBinary(field.data_type))
.map((field) => field.column_name);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Update the DAO settings object for binary searches.

dao.getTableRowsStream receives builtDAOsTableSettings at Line 121. Line 114 updates tableSettings.search_fields after builtDAOsTableSettings was built. The binary-only search-field restriction therefore has no effect.

Set builtDAOsTableSettings.search_fields instead. Add a CSV regression test with configured text search_fields and a binary-column hex search.

🤖 Prompt for 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.

In `@backend/src/entities/table/use-cases/export-csv-from-table.use.case.ts`
around lines 111 - 116, Update the binary-search branch in the table export flow
to assign the filtered binary column names to
builtDAOsTableSettings.search_fields, which is the settings object passed to
dao.getTableRowsStream, instead of tableSettings.search_fields. Add a CSV
regression test covering configured text search_fields combined with a
hexadecimal search on a binary column.

@Artuomka
Artuomka merged commit 845cd17 into main Aug 5, 2026
16 of 17 checks passed
@Artuomka
Artuomka deleted the backend_agent_table_public_permissions branch August 5, 2026 14:15
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