RLSTesterResults.utils.test.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. import type { SafeSqlFragment } from '@supabase/pg-meta'
  2. import { describe, expect, it } from 'vitest'
  3. import type { Policy } from '@/components/interfaces/Auth/Policies/PolicyTableRow/PolicyTableRow.utils'
  4. import type { ParseQueryResults } from '@/components/interfaces/Auth/RLSTester/RLSTester.types'
  5. import { deriveRLSTestState } from '@/components/interfaces/Auth/RLSTester/RLSTesterResults.utils'
  6. const sql = (s: string) => s as unknown as SafeSqlFragment
  7. const makePolicy = (definition: string | null = null): Policy =>
  8. ({ definition: definition !== null ? sql(definition) : null }) as Policy
  9. const makeTable = (
  10. overrides?: Partial<ParseQueryResults['tables'][number]>
  11. ): ParseQueryResults['tables'][number] => ({
  12. schema: 'public',
  13. table: 'items',
  14. isRLSEnabled: true,
  15. tablePolicies: [],
  16. ...overrides,
  17. })
  18. const makeResults = (overrides?: Partial<ParseQueryResults>): ParseQueryResults => ({
  19. tables: [],
  20. operation: 'SELECT',
  21. role: 'anon',
  22. ...overrides,
  23. })
  24. describe('deriveRLSTestState', () => {
  25. describe('isServiceRole', () => {
  26. it('is true when parseQueryResults is undefined', () => {
  27. const { isServiceRole } = deriveRLSTestState(undefined)
  28. expect(isServiceRole).toBe(true)
  29. })
  30. it('is true when role is undefined (postgres / service role)', () => {
  31. const { isServiceRole } = deriveRLSTestState(makeResults({ role: undefined }))
  32. expect(isServiceRole).toBe(true)
  33. })
  34. it('is false when role is anon', () => {
  35. const { isServiceRole } = deriveRLSTestState(makeResults({ role: 'anon' }))
  36. expect(isServiceRole).toBe(false)
  37. })
  38. it('is false when role is authenticated', () => {
  39. const { isServiceRole } = deriveRLSTestState(makeResults({ role: 'authenticated' }))
  40. expect(isServiceRole).toBe(false)
  41. })
  42. })
  43. describe('noAccessToData', () => {
  44. it('is false when parseQueryResults is undefined', () => {
  45. const { noAccessToData } = deriveRLSTestState(undefined)
  46. expect(noAccessToData).toBe(false)
  47. })
  48. it('is false for service role even when tables have no policies', () => {
  49. const { noAccessToData } = deriveRLSTestState(
  50. makeResults({ role: undefined, tables: [makeTable()] })
  51. )
  52. expect(noAccessToData).toBe(false)
  53. })
  54. it('is false when RLS is disabled on table', () => {
  55. const { noAccessToData } = deriveRLSTestState(
  56. makeResults({ tables: [makeTable({ isRLSEnabled: false, tablePolicies: [] })] })
  57. )
  58. expect(noAccessToData).toBe(false)
  59. })
  60. it('is true when RLS is enabled and table has no policies', () => {
  61. const { noAccessToData } = deriveRLSTestState(
  62. makeResults({ tables: [makeTable({ isRLSEnabled: true, tablePolicies: [] })] })
  63. )
  64. expect(noAccessToData).toBe(true)
  65. })
  66. it('is true when RLS is enabled and a policy definition is false', () => {
  67. const { noAccessToData } = deriveRLSTestState(
  68. makeResults({
  69. tables: [makeTable({ tablePolicies: [makePolicy('false')] })],
  70. })
  71. )
  72. expect(noAccessToData).toBe(true)
  73. })
  74. it('is false when RLS is enabled and policies are valid (not false)', () => {
  75. const { noAccessToData } = deriveRLSTestState(
  76. makeResults({
  77. tables: [makeTable({ tablePolicies: [makePolicy('auth.uid() = user_id')] })],
  78. })
  79. )
  80. expect(noAccessToData).toBe(false)
  81. })
  82. it('is false when all tables have RLS disabled regardless of policy state', () => {
  83. const { noAccessToData } = deriveRLSTestState(
  84. makeResults({
  85. tables: [
  86. makeTable({ isRLSEnabled: false, tablePolicies: [] }),
  87. makeTable({
  88. table: 'other',
  89. isRLSEnabled: false,
  90. tablePolicies: [makePolicy('false')],
  91. }),
  92. ],
  93. })
  94. )
  95. expect(noAccessToData).toBe(false)
  96. })
  97. })
  98. describe('tableWithRLSEnabledButNoPolicies', () => {
  99. it('is undefined when no tables', () => {
  100. const { tableWithRLSEnabledButNoPolicies } = deriveRLSTestState(makeResults({ tables: [] }))
  101. expect(tableWithRLSEnabledButNoPolicies).toBeUndefined()
  102. })
  103. it('is undefined when RLS disabled', () => {
  104. const { tableWithRLSEnabledButNoPolicies } = deriveRLSTestState(
  105. makeResults({ tables: [makeTable({ isRLSEnabled: false })] })
  106. )
  107. expect(tableWithRLSEnabledButNoPolicies).toBeUndefined()
  108. })
  109. it('is undefined when table has policies', () => {
  110. const { tableWithRLSEnabledButNoPolicies } = deriveRLSTestState(
  111. makeResults({ tables: [makeTable({ tablePolicies: [makePolicy('true')] })] })
  112. )
  113. expect(tableWithRLSEnabledButNoPolicies).toBeUndefined()
  114. })
  115. it('returns the matching table when RLS enabled with no policies', () => {
  116. const table = makeTable({ table: 'profiles', tablePolicies: [] })
  117. const { tableWithRLSEnabledButNoPolicies } = deriveRLSTestState(
  118. makeResults({ tables: [table] })
  119. )
  120. expect(tableWithRLSEnabledButNoPolicies).toEqual(table)
  121. })
  122. it('returns the first matching table among multiple', () => {
  123. const first = makeTable({ table: 'profiles', tablePolicies: [] })
  124. const second = makeTable({ table: 'posts', tablePolicies: [] })
  125. const { tableWithRLSEnabledButNoPolicies } = deriveRLSTestState(
  126. makeResults({ tables: [first, second] })
  127. )
  128. expect(tableWithRLSEnabledButNoPolicies).toEqual(first)
  129. })
  130. })
  131. describe('tableWithRLSEnabledWithPolicyFalse', () => {
  132. it('is undefined when no tables', () => {
  133. const { tableWithRLSEnabledWithPolicyFalse } = deriveRLSTestState(makeResults({ tables: [] }))
  134. expect(tableWithRLSEnabledWithPolicyFalse).toBeUndefined()
  135. })
  136. it('is undefined when RLS disabled even with false policy', () => {
  137. const { tableWithRLSEnabledWithPolicyFalse } = deriveRLSTestState(
  138. makeResults({
  139. tables: [makeTable({ isRLSEnabled: false, tablePolicies: [makePolicy('false')] })],
  140. })
  141. )
  142. expect(tableWithRLSEnabledWithPolicyFalse).toBeUndefined()
  143. })
  144. it('is undefined when no policy has definition of false', () => {
  145. const { tableWithRLSEnabledWithPolicyFalse } = deriveRLSTestState(
  146. makeResults({
  147. tables: [makeTable({ tablePolicies: [makePolicy('auth.uid() = user_id')] })],
  148. })
  149. )
  150. expect(tableWithRLSEnabledWithPolicyFalse).toBeUndefined()
  151. })
  152. it('returns the table when a policy definition is exactly "false"', () => {
  153. const table = makeTable({ table: 'secrets', tablePolicies: [makePolicy('false')] })
  154. const { tableWithRLSEnabledWithPolicyFalse } = deriveRLSTestState(
  155. makeResults({ tables: [table] })
  156. )
  157. expect(tableWithRLSEnabledWithPolicyFalse).toEqual(table)
  158. })
  159. it('is undefined when policy definition is null (no definition)', () => {
  160. const { tableWithRLSEnabledWithPolicyFalse } = deriveRLSTestState(
  161. makeResults({ tables: [makeTable({ tablePolicies: [makePolicy(null)] })] })
  162. )
  163. expect(tableWithRLSEnabledWithPolicyFalse).toBeUndefined()
  164. })
  165. })
  166. })