Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 70 additions & 6 deletions crates/pgls_configuration/src/splinter/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,13 +540,19 @@ pub struct Security {
#[doc = "RLS Enabled No Policy: Detects cases where row level security (RLS) has been enabled on a table but no RLS policies have been created."]
#[serde(skip_serializing_if = "Option::is_none")]
pub rls_enabled_no_policy: Option<RuleConfiguration<crate::splinter::SplinterRuleOptions>>,
#[doc = "RLS Policy Always True: Detects RLS policies that use overly permissive expressions like USING (true) or WITH CHECK (true) for UPDATE, DELETE, or INSERT operations. SELECT policies with USING (true) are intentionally excluded as this pattern is often used deliberately for public read access."]
#[serde(skip_serializing_if = "Option::is_none")]
pub rls_policy_always_true: Option<RuleConfiguration<crate::splinter::SplinterRuleOptions>>,
#[doc = "RLS references user metadata: Detects when Supabase Auth user_metadata is referenced insecurely in a row level security (RLS) policy."]
#[serde(skip_serializing_if = "Option::is_none")]
pub rls_references_user_metadata:
Option<RuleConfiguration<crate::splinter::SplinterRuleOptions>>,
#[doc = "Security Definer View: Detects views defined with the SECURITY DEFINER property. These views enforce Postgres permissions and row level security policies (RLS) of the view creator, rather than that of the querying user"]
#[serde(skip_serializing_if = "Option::is_none")]
pub security_definer_view: Option<RuleConfiguration<crate::splinter::SplinterRuleOptions>>,
#[doc = "Sensitive Columns Exposed: Detects tables exposed via API that contain columns with potentially sensitive data (PII, credentials, financial info) without RLS protection."]
#[serde(skip_serializing_if = "Option::is_none")]
pub sensitive_columns_exposed: Option<RuleConfiguration<crate::splinter::SplinterRuleOptions>>,
#[doc = "Unsupported reg types: Identifies columns using unsupported reg* types outside pg_catalog schema, which prevents database upgrades using pg_upgrade."]
#[serde(skip_serializing_if = "Option::is_none")]
pub unsupported_reg_types: Option<RuleConfiguration<crate::splinter::SplinterRuleOptions>>,
Expand All @@ -565,8 +571,10 @@ impl Security {
"policyExistsRlsDisabled",
"rlsDisabledInPublic",
"rlsEnabledNoPolicy",
"rlsPolicyAlwaysTrue",
"rlsReferencesUserMetadata",
"securityDefinerView",
"sensitiveColumnsExposed",
"unsupportedRegTypes",
];
const RECOMMENDED_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[
Expand All @@ -584,6 +592,8 @@ impl Security {
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]),
];
const ALL_RULES_AS_FILTERS: &'static [RuleFilter<'static>] = &[
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]),
Expand All @@ -600,6 +610,8 @@ impl Security {
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]),
RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]),
];
#[doc = r" Retrieves the recommended rules"]
pub(crate) fn is_recommended_true(&self) -> bool {
Expand Down Expand Up @@ -671,21 +683,31 @@ impl Security {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10]));
}
}
if let Some(rule) = self.rls_references_user_metadata.as_ref() {
if let Some(rule) = self.rls_policy_always_true.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]));
}
}
if let Some(rule) = self.security_definer_view.as_ref() {
if let Some(rule) = self.rls_references_user_metadata.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]));
}
}
if let Some(rule) = self.unsupported_reg_types.as_ref() {
if let Some(rule) = self.security_definer_view.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]));
}
}
if let Some(rule) = self.sensitive_columns_exposed.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]));
}
}
if let Some(rule) = self.unsupported_reg_types.as_ref() {
if rule.is_enabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]));
}
}
index_set
}
pub(crate) fn get_disabled_rules(&self) -> FxHashSet<RuleFilter<'static>> {
Expand Down Expand Up @@ -745,21 +767,31 @@ impl Security {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10]));
}
}
if let Some(rule) = self.rls_references_user_metadata.as_ref() {
if let Some(rule) = self.rls_policy_always_true.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]));
}
}
if let Some(rule) = self.security_definer_view.as_ref() {
if let Some(rule) = self.rls_references_user_metadata.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]));
}
}
if let Some(rule) = self.unsupported_reg_types.as_ref() {
if let Some(rule) = self.security_definer_view.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]));
}
}
if let Some(rule) = self.sensitive_columns_exposed.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]));
}
}
if let Some(rule) = self.unsupported_reg_types.as_ref() {
if rule.is_disabled() {
index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]));
}
}
index_set
}
#[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"]
Expand Down Expand Up @@ -800,8 +832,10 @@ impl Security {
"policyExistsRlsDisabled" => Severity::Error,
"rlsDisabledInPublic" => Severity::Error,
"rlsEnabledNoPolicy" => Severity::Information,
"rlsPolicyAlwaysTrue" => Severity::Warning,
"rlsReferencesUserMetadata" => Severity::Error,
"securityDefinerView" => Severity::Error,
"sensitiveColumnsExposed" => Severity::Error,
"unsupportedRegTypes" => Severity::Warning,
_ => unreachable!(),
}
Expand Down Expand Up @@ -855,6 +889,10 @@ impl Security {
.rls_enabled_no_policy
.as_ref()
.map(|conf| (conf.level(), conf.get_options())),
"rlsPolicyAlwaysTrue" => self
.rls_policy_always_true
.as_ref()
.map(|conf| (conf.level(), conf.get_options())),
"rlsReferencesUserMetadata" => self
.rls_references_user_metadata
.as_ref()
Expand All @@ -863,6 +901,10 @@ impl Security {
.security_definer_view
.as_ref()
.map(|conf| (conf.level(), conf.get_options())),
"sensitiveColumnsExposed" => self
.sensitive_columns_exposed
.as_ref()
.map(|conf| (conf.level(), conf.get_options())),
"unsupportedRegTypes" => self
.unsupported_reg_types
.as_ref()
Expand Down Expand Up @@ -996,6 +1038,17 @@ impl Security {
}
}
}
if let Some(conf) = &self.rls_policy_always_true {
if let Some(options) = conf.get_options_ref() {
if !options.ignore.is_empty() {
let mut m = pgls_matcher::Matcher::new(pgls_matcher::MatchOptions::default());
for p in &options.ignore {
let _ = m.add_pattern(p);
}
matchers.insert("rlsPolicyAlwaysTrue", m);
}
}
}
if let Some(conf) = &self.rls_references_user_metadata {
if let Some(options) = conf.get_options_ref() {
if !options.ignore.is_empty() {
Expand All @@ -1018,6 +1071,17 @@ impl Security {
}
}
}
if let Some(conf) = &self.sensitive_columns_exposed {
if let Some(options) = conf.get_options_ref() {
if !options.ignore.is_empty() {
let mut m = pgls_matcher::Matcher::new(pgls_matcher::MatchOptions::default());
for p in &options.ignore {
let _ = m.add_pattern(p);
}
matchers.insert("sensitiveColumnsExposed", m);
}
}
}
if let Some(conf) = &self.unsupported_reg_types {
if let Some(options) = conf.get_options_ref() {
if !options.ignore.is_empty() {
Expand Down
2 changes: 2 additions & 0 deletions crates/pgls_diagnostics_categories/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,10 @@ define_categories! {
"splinter/security/policyExistsRlsDisabled": "https://supabase.com/docs/guides/database/database-linter?lint=0007_policy_exists_rls_disabled",
"splinter/security/rlsDisabledInPublic": "https://supabase.com/docs/guides/database/database-linter?lint=0013_rls_disabled_in_public",
"splinter/security/rlsEnabledNoPolicy": "https://supabase.com/docs/guides/database/database-linter?lint=0008_rls_enabled_no_policy",
"splinter/security/rlsPolicyAlwaysTrue": "https://supabase.com/docs/guides/database/database-linter?lint=0024_permissive_rls_policy",
"splinter/security/rlsReferencesUserMetadata": "https://supabase.com/docs/guides/database/database-linter?lint=0015_rls_references_user_metadata",
"splinter/security/securityDefinerView": "https://supabase.com/docs/guides/database/database-linter?lint=0010_security_definer_view",
"splinter/security/sensitiveColumnsExposed": "https://supabase.com/docs/guides/database/database-linter?lint=0023_sensitive_columns_exposed",
"splinter/security/unsupportedRegTypes": "https://supabase.com/docs/guides/database/database-linter?lint=unsupported_reg_types",
// splinter rules end
;
Expand Down
2 changes: 1 addition & 1 deletion crates/pgls_splinter/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::fs;
use std::io::Write;
use std::path::Path;

const EXPECTED_COMMIT: &str = "27ea2ece65464213e466cd969cc61b6940d16219";
const EXPECTED_COMMIT: &str = "b9de3a3001cbdf01dc1da327acae0700c07f0110";
const REPO: &str = "supabase/splinter";

fn main() {
Expand Down
Loading
Loading