Skip to content

fix: avoid substring matches in policy table dependency detection#474

Open
meesvandongen wants to merge 2 commits into
pgplex:mainfrom
meesvandongen:fix/policy-identifier-substring-false-positive
Open

fix: avoid substring matches in policy table dependency detection#474
meesvandongen wants to merge 2 commits into
pgplex:mainfrom
meesvandongen:fix/policy-identifier-substring-false-positive

Conversation

@meesvandongen

Copy link
Copy Markdown

Fix false positives when detecting whether an RLS policy depends on another newly added table.

Previously, table names were matched by substring, so identifiers like CURRENT_USER or user_name could be mistaken for references to a new table named user.

This change switches policy dependency detection to identifier-aware matching and adds a focused regression test plus a file-based fixture.

@meesvandongen meesvandongen marked this pull request as ready for review June 12, 2026 11:40
Copilot AI review requested due to automatic review settings June 12, 2026 11:40

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

Note

Copilot was unable to run its full agentic suite in this review.

Adds identifier-aware detection of whether an RLS policy references other newly-created tables, preventing false positives from substring matches (e.g., orders matching orders_archive).

Changes:

  • Updates policyReferencesOtherNewTable to use containsIdentifier instead of raw substring matching.
  • Adds a focused unit test to validate identifier-aware matching behavior.
  • Adds testdata fixtures reproducing the substring false-positive scenario across multiple plan formats.

Reviewed changes

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

Show a summary per file
File Description
testdata/diff/dependency/policy_identifier_substring_false_positive/plan.txt Adds plan output fixture for the false-positive scenario.
testdata/diff/dependency/policy_identifier_substring_false_positive/plan.sql Adds SQL plan fixture for the false-positive scenario.
testdata/diff/dependency/policy_identifier_substring_false_positive/plan.json Adds JSON plan fixture for the false-positive scenario.
testdata/diff/dependency/policy_identifier_substring_false_positive/old.sql Adds “empty schema” baseline fixture.
testdata/diff/dependency/policy_identifier_substring_false_positive/new.sql Adds “new schema” fixture including RLS policy and "user" table.
testdata/diff/dependency/policy_identifier_substring_false_positive/diff.sql Adds expected diff fixture for the scenario.
internal/diff/policy_dependency_test.go Adds a unit test ensuring substring-only matches inside longer identifiers don’t trigger deferral.
internal/diff/diff.go Switches policy dependency detection to identifier-aware matching to avoid substring false positives.

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

Comment thread internal/diff/diff.go
Comment on lines +2393 to 2395
if containsIdentifier(expr, tableName) || containsIdentifier(expr, qualifiedName) {
return true
}
Comment thread internal/diff/diff.go
Comment on lines 2386 to +2393
// Skip the policy's own table
if qualifiedName == ownQualified {
continue
}
// Extract the unqualified table name for substring matching.
// Policy expressions may use unqualified or qualified references.
parts := strings.SplitN(qualifiedName, ".", 2)
tableName := parts[len(parts)-1]
if strings.Contains(exprLower, tableName) {
if containsIdentifier(expr, tableName) || containsIdentifier(expr, qualifiedName) {
@greptile-apps

greptile-apps Bot commented Jun 12, 2026

Copy link
Copy Markdown

Greptile Summary

This PR makes RLS policy table dependency checks use identifier-aware matching. The main changes are:

  • Replaces substring matching with containsIdentifier checks.
  • Adds a focused unit test for policy table references.
  • Adds a file-based regression fixture for CURRENT_USER and user_name false positives.

Confidence Score: 4/5

This looks safe to merge, but a few false-positive cases remain.

  • The main migration flow should still produce valid SQL.
  • Policy dependency detection can still defer policies when table names appear in literals.
  • Valid identifiers containing $ can still trigger substring-like matches.

internal/diff/diff.go

Important Files Changed

Filename Overview
internal/diff/diff.go Changes policy dependency detection to use regex-based identifier matching.
internal/diff/policy_dependency_test.go Adds unit coverage for normal references and longer-identifier false positives.

Reviews (1): Last reviewed commit: "add test" | Re-trigger Greptile

Comment thread internal/diff/diff.go
parts := strings.SplitN(qualifiedName, ".", 2)
tableName := parts[len(parts)-1]
if strings.Contains(exprLower, tableName) {
if containsIdentifier(expr, tableName) || containsIdentifier(expr, qualifiedName) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Literals Still Match

This still treats any whole-word occurrence as a table dependency, even when the word is inside a SQL string literal or comment. For example, if a new table users is added and a policy only has USING (group_name = 'users'), this returns true and unnecessarily defers the policy even though it does not reference that table. That keeps the false-positive class alive for policy dependency detection.

Comment thread internal/diff/diff.go
parts := strings.SplitN(qualifiedName, ".", 2)
tableName := parts[len(parts)-1]
if strings.Contains(exprLower, tableName) {
if containsIdentifier(expr, tableName) || containsIdentifier(expr, qualifiedName) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Dollar Identifiers Split

PostgreSQL identifiers can contain $, but containsIdentifier only treats letters, digits, and _ as identifier characters. A new table named foo will therefore match an unrelated identifier like foo$bar in a policy expression, because $ is treated as a boundary. That can still produce substring-style false positives for valid PostgreSQL identifiers.

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