oxlint-plugin-react-doctor 0.5.8-dev.bd0f465 → 0.5.8-dev.c16e8ea

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +54 -16
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -35941,22 +35941,6 @@ const isSupabaseMigrationPath = (relativePath) => /(?:^|\/)supabase\/(?:migratio
35941
35941
  //#endregion
35942
35942
  //#region src/plugin/rules/security-scan/utils/is-sql-path.ts
35943
35943
  const isSqlPath = (relativePath) => relativePath.endsWith(".sql") || isSupabaseMigrationPath(relativePath);
35944
- const supabaseRlsPolicyRisk = defineRule({
35945
- id: "supabase-rls-policy-risk",
35946
- title: "Permissive Supabase RLS policy",
35947
- severity: "error",
35948
- recommendation: "Keep public-read policies explicit, but gate inserts, updates, deletes, and service-role bypasses behind `auth.uid()` plus trusted tenant membership.",
35949
- scan: scanByPattern({
35950
- shouldScan: (file) => isSqlPath(file.relativePath),
35951
- pattern: [
35952
- /disable\s+row\s+level\s+security/i,
35953
- /create\s+policy[\s\S]{0,700}auth\.role\(\)\s*=\s*["']service_role["']/i,
35954
- /create\s+policy[\s\S]{0,700}\bfor\s+(?:all|insert|update|delete)\b[\s\S]{0,500}\b(?:using|with\s+check)\s*\(\s*true\s*\)/i,
35955
- /create\s+policy(?:(?!\bfor\s+select\b)[\s\S]){0,700}\b(?:using|with\s+check)\s*\(\s*true\s*\)/i
35956
- ],
35957
- message: "Supabase policy SQL disables RLS, permits writes broadly, or references a service-role bypass."
35958
- })
35959
- });
35960
35944
  //#endregion
35961
35945
  //#region src/plugin/rules/security-scan/utils/sanitize-sql-for-scan.ts
35962
35946
  const DOLLAR_QUOTE_TAG_PATTERN = /^\$[A-Za-z_]?\w*\$/;
@@ -36133,6 +36117,60 @@ const sanitizeSqlForScan = (content) => {
36133
36117
  return characters.join("");
36134
36118
  };
36135
36119
  //#endregion
36120
+ //#region src/plugin/rules/security-scan/supabase-rls-policy-risk.ts
36121
+ const DISABLED_RLS_PATTERN = /disable\s+row\s+level\s+security/i;
36122
+ const SERVICE_ROLE_BODY_BYPASS_PATTERN = /auth\.role\(\)\s*=\s*["']service_role["']/i;
36123
+ const CREATE_POLICY_PATTERN = /create\s+policy/gi;
36124
+ const STATEMENT_END_PATTERN = /;|create\s+policy/i;
36125
+ const PERMISSIVE_TRUE_PATTERN = /\b(?:using|with\s+check)\s*\(\s*true\s*\)/i;
36126
+ const FOR_SELECT_PATTERN = /\bfor\s+select\b/i;
36127
+ const TO_CLAUSE_PATTERN = /\bto\s+([\s\S]+?)(?=\s+(?:using|with\s+check|as|for)\b|;|$)/i;
36128
+ const SERVER_ONLY_ROLES = new Set([
36129
+ "service_role",
36130
+ "postgres",
36131
+ "supabase_admin"
36132
+ ]);
36133
+ const isServerOnlyScoped = (statement) => {
36134
+ const toClause = TO_CLAUSE_PATTERN.exec(statement);
36135
+ if (toClause === null) return false;
36136
+ const roles = toClause[1].split(",").map((role) => role.trim().replace(/["'`]/g, "").toLowerCase()).filter(Boolean);
36137
+ return roles.length > 0 && roles.every((role) => SERVER_ONLY_ROLES.has(role));
36138
+ };
36139
+ const isRiskyPolicyStatement = (statement, rawStatement) => {
36140
+ if (SERVICE_ROLE_BODY_BYPASS_PATTERN.test(rawStatement)) return true;
36141
+ if (!PERMISSIVE_TRUE_PATTERN.test(statement)) return false;
36142
+ if (FOR_SELECT_PATTERN.test(statement)) return false;
36143
+ return !isServerOnlyScoped(statement);
36144
+ };
36145
+ const POLICY_RISK_MESSAGE = "Supabase policy SQL disables RLS, permits writes broadly, or references a service-role bypass.";
36146
+ const supabaseRlsPolicyRisk = defineRule({
36147
+ id: "supabase-rls-policy-risk",
36148
+ title: "Permissive Supabase RLS policy",
36149
+ severity: "error",
36150
+ recommendation: "Keep public-read policies explicit, but gate inserts, updates, deletes, and service-role bypasses behind `auth.uid()` plus trusted tenant membership.",
36151
+ scan: (file) => {
36152
+ if (!isSqlPath(file.relativePath)) return [];
36153
+ const content = sanitizeSqlForScan(file.content);
36154
+ let earliestRiskIndex = content.search(DISABLED_RLS_PATTERN);
36155
+ CREATE_POLICY_PATTERN.lastIndex = 0;
36156
+ for (let policyMatch = CREATE_POLICY_PATTERN.exec(content); policyMatch !== null; policyMatch = CREATE_POLICY_PATTERN.exec(content)) {
36157
+ const afterKeyword = policyMatch.index + policyMatch[0].length;
36158
+ const terminatorOffset = content.slice(afterKeyword).search(STATEMENT_END_PATTERN);
36159
+ const statementEnd = terminatorOffset < 0 ? content.length : afterKeyword + terminatorOffset;
36160
+ if (!isRiskyPolicyStatement(content.slice(policyMatch.index, statementEnd), file.content.slice(policyMatch.index, statementEnd))) continue;
36161
+ if (earliestRiskIndex < 0 || policyMatch.index < earliestRiskIndex) earliestRiskIndex = policyMatch.index;
36162
+ break;
36163
+ }
36164
+ if (earliestRiskIndex < 0) return [];
36165
+ const { line, column } = getLocationAtIndex(content, earliestRiskIndex);
36166
+ return [{
36167
+ message: POLICY_RISK_MESSAGE,
36168
+ line,
36169
+ column
36170
+ }];
36171
+ }
36172
+ });
36173
+ //#endregion
36136
36174
  //#region src/plugin/rules/security-scan/supabase-table-missing-rls.ts
36137
36175
  const CREATE_PUBLIC_TABLE_PATTERN = /create\s+(?:unlogged\s+)?table\s+(?:if\s+not\s+exists\s+)?(?!(?:auth|storage|realtime|vault|extensions|graphql|graphql_public|pgbouncer|net|supabase_functions|supabase_migrations|cron|pgsodium|pgmq|information_schema|pg_catalog|pg_temp|private|internal)\s*\.)(?:public\s*\.\s*)?["`]?([A-Za-z_][\w$]*)["`]?(?:\s*\(|\s+as\b)/gi;
36138
36176
  const enableRlsForTablePattern = (tableName) => new RegExp(`alter\\s+table\\s+(?:if\\s+exists\\s+)?(?:only\\s+)?(?:public\\s*\\.\\s*)?["\`]?${escapeRegExp(tableName)}["\`]?\\s+(?:force\\s+)?enable\\s+row\\s+level\\s+security`, "i");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.5.8-dev.bd0f465",
3
+ "version": "0.5.8-dev.c16e8ea",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",