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.
- package/dist/index.js +54 -16
- 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");
|