oxlint-plugin-react-doctor 0.5.8-dev.a7ad969 → 0.5.8-dev.b69f4a7
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 +50 -166
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1176,20 +1176,6 @@ const getImportSourceForName = (contextNode, localIdentifierName) => {
|
|
|
1176
1176
|
if (!lookup) return null;
|
|
1177
1177
|
return lookup.get(localIdentifierName)?.source ?? null;
|
|
1178
1178
|
};
|
|
1179
|
-
const getImportBindingForName = (contextNode, localIdentifierName) => {
|
|
1180
|
-
const info = getImportLookup(contextNode)?.get(localIdentifierName);
|
|
1181
|
-
if (!info) return null;
|
|
1182
|
-
if (info.isNamespace) return {
|
|
1183
|
-
source: info.source,
|
|
1184
|
-
exportedName: null,
|
|
1185
|
-
isNamespace: true
|
|
1186
|
-
};
|
|
1187
|
-
return {
|
|
1188
|
-
source: info.source,
|
|
1189
|
-
exportedName: info.isDefault ? "default" : info.imported,
|
|
1190
|
-
isNamespace: false
|
|
1191
|
-
};
|
|
1192
|
-
};
|
|
1193
1179
|
//#endregion
|
|
1194
1180
|
//#region src/plugin/utils/find-variable-initializer.ts
|
|
1195
1181
|
const FUNCTION_LIKE_TYPES$1 = new Set([
|
|
@@ -29487,19 +29473,6 @@ const REACT_NATIVE_TEXT_COMPONENTS = new Set([
|
|
|
29487
29473
|
"H5",
|
|
29488
29474
|
"H6"
|
|
29489
29475
|
]);
|
|
29490
|
-
const REACT_NATIVE_RAW_TEXT_HOST_COMPONENTS = new Set([
|
|
29491
|
-
"View",
|
|
29492
|
-
"ScrollView",
|
|
29493
|
-
"SafeAreaView",
|
|
29494
|
-
"KeyboardAvoidingView",
|
|
29495
|
-
"ImageBackground",
|
|
29496
|
-
"Modal",
|
|
29497
|
-
"Pressable",
|
|
29498
|
-
"TouchableOpacity",
|
|
29499
|
-
"TouchableHighlight",
|
|
29500
|
-
"TouchableWithoutFeedback",
|
|
29501
|
-
"TouchableNativeFeedback"
|
|
29502
|
-
]);
|
|
29503
29476
|
const REACT_NATIVE_TEXT_COMPONENT_KEYWORDS = new Set([
|
|
29504
29477
|
"Text",
|
|
29505
29478
|
"Title",
|
|
@@ -29512,11 +29485,7 @@ const REACT_NATIVE_TEXT_COMPONENT_KEYWORDS = new Set([
|
|
|
29512
29485
|
"Description",
|
|
29513
29486
|
"Body"
|
|
29514
29487
|
]);
|
|
29515
|
-
const REACT_NATIVE_TEXT_TRANSPARENT_COMPONENTS = new Set([
|
|
29516
|
-
"Fragment",
|
|
29517
|
-
"fbt",
|
|
29518
|
-
"fbs"
|
|
29519
|
-
]);
|
|
29488
|
+
const REACT_NATIVE_TEXT_TRANSPARENT_COMPONENTS = new Set(["fbt", "fbs"]);
|
|
29520
29489
|
const DEPRECATED_RN_MODULE_REPLACEMENTS = new Map([
|
|
29521
29490
|
["AsyncStorage", "@react-native-async-storage/async-storage"],
|
|
29522
29491
|
["Picker", "@react-native-picker/picker"],
|
|
@@ -30474,29 +30443,23 @@ const jsxRootForwardsChildrenIntoText = (jsxRoot, bindings, isTextHandlingElemen
|
|
|
30474
30443
|
return didForwardIntoText;
|
|
30475
30444
|
};
|
|
30476
30445
|
const isMeaningfulJsxChild = (child) => !isNodeOfType(child, "JSXText") || Boolean(child.value?.trim());
|
|
30477
|
-
const
|
|
30478
|
-
let
|
|
30446
|
+
const jsxRootRendersChildrenOutsideText = (jsxRoot, bindings, isTextHandlingElement) => {
|
|
30447
|
+
let didRenderOutsideText = false;
|
|
30479
30448
|
walkAst(jsxRoot, (node) => {
|
|
30480
|
-
if (
|
|
30449
|
+
if (didRenderOutsideText || isFunctionNode(node)) return false;
|
|
30481
30450
|
if (!isNodeOfType(node, "JSXElement") && !isNodeOfType(node, "JSXFragment")) return;
|
|
30482
30451
|
if (isNodeOfType(node, "JSXElement")) {
|
|
30483
30452
|
const elementName = resolveJsxElementName(node.openingElement);
|
|
30484
30453
|
if (elementName && isTextHandlingElement(elementName)) return false;
|
|
30485
|
-
if (!(node.children ?? []).some(isMeaningfulJsxChild) &&
|
|
30486
|
-
|
|
30454
|
+
if (!(node.children ?? []).some(isMeaningfulJsxChild) && (node.openingElement.attributes ?? []).some((attribute) => isChildrenForwardingAttribute(attribute, bindings))) {
|
|
30455
|
+
didRenderOutsideText = true;
|
|
30487
30456
|
return;
|
|
30488
30457
|
}
|
|
30489
30458
|
}
|
|
30490
|
-
|
|
30459
|
+
didRenderOutsideText = (node.children ?? []).some((child) => isChildrenForwardingJsxChild(child, bindings));
|
|
30491
30460
|
});
|
|
30492
|
-
return
|
|
30461
|
+
return didRenderOutsideText;
|
|
30493
30462
|
};
|
|
30494
|
-
const jsxRootRendersChildrenOutsideText = (jsxRoot, bindings, isTextHandlingElement) => jsxRootForwardsChildren(jsxRoot, bindings, isTextHandlingElement, () => true);
|
|
30495
|
-
const jsxRootRendersChildrenIntoNonTextHost = (jsxRoot, bindings, isTextHandlingElement, isNonTextHostElement) => jsxRootForwardsChildren(jsxRoot, bindings, isTextHandlingElement, (node) => {
|
|
30496
|
-
if (!isNodeOfType(node, "JSXElement")) return false;
|
|
30497
|
-
const elementName = resolveJsxElementName(node.openingElement);
|
|
30498
|
-
return elementName !== null && isNonTextHostElement(elementName);
|
|
30499
|
-
});
|
|
30500
30463
|
const resolveStyledFactoryBaseName = (definitionNode) => {
|
|
30501
30464
|
let current = stripParenExpression(definitionNode);
|
|
30502
30465
|
while (current) {
|
|
@@ -30533,71 +30496,49 @@ const resolveClassRenderFunction = (classNode) => {
|
|
|
30533
30496
|
}
|
|
30534
30497
|
return null;
|
|
30535
30498
|
};
|
|
30536
|
-
const
|
|
30499
|
+
const recordWrapperFromDeclaration = (componentName, definitionNode, isTextHandlingElement, wrappers) => {
|
|
30500
|
+
if (!componentName || !isReactComponentName(componentName)) return;
|
|
30501
|
+
if (wrappers.has(componentName)) return;
|
|
30502
|
+
if (!definitionNode) return;
|
|
30537
30503
|
const unwrapped = unwrapComponentDefinition(definitionNode);
|
|
30538
30504
|
const styledBaseName = resolveStyledFactoryBaseName(unwrapped);
|
|
30539
|
-
if (styledBaseName) {
|
|
30540
|
-
|
|
30541
|
-
|
|
30542
|
-
return "unknown";
|
|
30505
|
+
if (styledBaseName && isTextHandlingElement(styledBaseName)) {
|
|
30506
|
+
wrappers.add(componentName);
|
|
30507
|
+
return;
|
|
30543
30508
|
}
|
|
30544
30509
|
const functionNode = resolveClassRenderFunction(unwrapped) ?? (isFunctionNode(unwrapped) ? unwrapped : null);
|
|
30545
|
-
if (!functionNode) return
|
|
30510
|
+
if (!functionNode) return;
|
|
30546
30511
|
const bindings = resolveParamChildrenBindings(functionNode);
|
|
30547
30512
|
collectChildrenAliases(functionNode, bindings);
|
|
30548
30513
|
const jsxRoots = collectReturnedJsxRoots(functionNode);
|
|
30549
|
-
if (jsxRoots.some((jsxRoot) =>
|
|
30550
|
-
if (jsxRoots.some((jsxRoot) => jsxRootRendersChildrenOutsideText(jsxRoot, bindings, isTextHandlingElement))) return "unknown";
|
|
30514
|
+
if (jsxRoots.some((jsxRoot) => jsxRootRendersChildrenOutsideText(jsxRoot, bindings, isTextHandlingElement))) return;
|
|
30551
30515
|
for (const jsxRoot of jsxRoots) {
|
|
30552
30516
|
if (isNodeOfType(jsxRoot, "JSXElement")) {
|
|
30553
30517
|
const rootName = resolveJsxElementName(jsxRoot.openingElement);
|
|
30554
|
-
if (rootName && isTextHandlingElement(rootName))
|
|
30518
|
+
if (rootName && isTextHandlingElement(rootName)) {
|
|
30519
|
+
wrappers.add(componentName);
|
|
30520
|
+
return;
|
|
30521
|
+
}
|
|
30522
|
+
}
|
|
30523
|
+
if (jsxRootForwardsChildrenIntoText(jsxRoot, bindings, isTextHandlingElement)) {
|
|
30524
|
+
wrappers.add(componentName);
|
|
30525
|
+
return;
|
|
30555
30526
|
}
|
|
30556
|
-
if (jsxRootForwardsChildrenIntoText(jsxRoot, bindings, isTextHandlingElement)) return "text";
|
|
30557
30527
|
}
|
|
30558
|
-
return "unknown";
|
|
30559
|
-
};
|
|
30560
|
-
const recordWrapperFromDeclaration = (componentName, definitionNode, isTextHandlingElement, isNonTextHostElement, wrappers, nonTextWrappers) => {
|
|
30561
|
-
if (!componentName || !isReactComponentName(componentName)) return;
|
|
30562
|
-
if (wrappers.has(componentName)) return;
|
|
30563
|
-
if (!definitionNode) return;
|
|
30564
|
-
const kind = classifyChildrenForwarding(definitionNode, isTextHandlingElement, isNonTextHostElement);
|
|
30565
|
-
if (kind === "text") wrappers.add(componentName);
|
|
30566
|
-
else if (kind === "nonText") nonTextWrappers.add(componentName);
|
|
30567
30528
|
};
|
|
30568
30529
|
const MAX_TRANSITIVE_WRAPPER_PASSES = 3;
|
|
30569
|
-
const collectTextWrapperComponents = (programNode, isTextHandlingRoot
|
|
30530
|
+
const collectTextWrapperComponents = (programNode, isTextHandlingRoot) => {
|
|
30570
30531
|
const wrappers = /* @__PURE__ */ new Set();
|
|
30571
|
-
const nonTextWrappers = /* @__PURE__ */ new Set();
|
|
30572
30532
|
const isTextHandlingElement = (elementName) => isTextHandlingRoot(elementName) || wrappers.has(elementName);
|
|
30573
|
-
const isNonTextHostElement = (elementName) => isNonTextHostRoot(elementName) || nonTextWrappers.has(elementName);
|
|
30574
|
-
const recordDeclaration = (componentName, definitionNode) => recordWrapperFromDeclaration(componentName, definitionNode, isTextHandlingElement, isNonTextHostElement, wrappers, nonTextWrappers);
|
|
30575
30533
|
for (let pass = 0; pass < MAX_TRANSITIVE_WRAPPER_PASSES; pass += 1) {
|
|
30576
|
-
const
|
|
30577
|
-
const nonTextSizeBeforePass = nonTextWrappers.size;
|
|
30534
|
+
const sizeBeforePass = wrappers.size;
|
|
30578
30535
|
walkAst(programNode, (node) => {
|
|
30579
|
-
if (isNodeOfType(node, "VariableDeclarator"))
|
|
30580
|
-
else if (isNodeOfType(node, "FunctionDeclaration") || isNodeOfType(node, "ClassDeclaration"))
|
|
30536
|
+
if (isNodeOfType(node, "VariableDeclarator")) recordWrapperFromDeclaration(node.id && isNodeOfType(node.id, "Identifier") ? node.id.name : null, node.init, isTextHandlingElement, wrappers);
|
|
30537
|
+
else if (isNodeOfType(node, "FunctionDeclaration") || isNodeOfType(node, "ClassDeclaration")) recordWrapperFromDeclaration(node.id && isNodeOfType(node.id, "Identifier") ? node.id.name : null, node, isTextHandlingElement, wrappers);
|
|
30581
30538
|
});
|
|
30582
|
-
if (wrappers.size ===
|
|
30539
|
+
if (wrappers.size === sizeBeforePass) break;
|
|
30583
30540
|
}
|
|
30584
|
-
|
|
30585
|
-
return {
|
|
30586
|
-
textWrappers: wrappers,
|
|
30587
|
-
nonTextWrappers
|
|
30588
|
-
};
|
|
30589
|
-
};
|
|
30590
|
-
//#endregion
|
|
30591
|
-
//#region src/plugin/rules/react-native/utils/resolve-imported-component-forwarding.ts
|
|
30592
|
-
const resolveImportedComponentForwarding = (contextNode, fromFilename, localName, isTextHandlingRoot, isNonTextHostRoot) => {
|
|
30593
|
-
const binding = getImportBindingForName(contextNode, localName);
|
|
30594
|
-
if (!binding || binding.isNamespace || binding.exportedName === null) return null;
|
|
30595
|
-
const resolvedNode = resolveCrossFileFunctionExport(fromFilename, binding.source, binding.exportedName);
|
|
30596
|
-
if (!resolvedNode) return null;
|
|
30597
|
-
const moduleProgram = findProgramRoot(resolvedNode);
|
|
30598
|
-
if (moduleProgram === null) return classifyChildrenForwarding(resolvedNode, isTextHandlingRoot, isNonTextHostRoot);
|
|
30599
|
-
const { textWrappers, nonTextWrappers } = collectTextWrapperComponents(moduleProgram, isTextHandlingRoot, isNonTextHostRoot);
|
|
30600
|
-
return classifyChildrenForwarding(resolvedNode, (elementName) => isTextHandlingRoot(elementName) || textWrappers.has(elementName), (elementName) => isNonTextHostRoot(elementName) || nonTextWrappers.has(elementName));
|
|
30541
|
+
return wrappers;
|
|
30601
30542
|
};
|
|
30602
30543
|
//#endregion
|
|
30603
30544
|
//#region src/plugin/rules/react-native/utils/is-expo-ui-component-element.ts
|
|
@@ -30668,37 +30609,19 @@ const rnNoRawText = defineRule({
|
|
|
30668
30609
|
recommendation: "Text outside a `<Text>` component crashes on React Native. Wrap it like `<Text>{value}</Text>`.",
|
|
30669
30610
|
create: (context) => {
|
|
30670
30611
|
let isDomComponentFile = false;
|
|
30671
|
-
let
|
|
30672
|
-
let autoDetectedNonTextWrappers = /* @__PURE__ */ new Set();
|
|
30673
|
-
const isNonTextHostName = (elementName) => !isReactComponentName(elementName) || REACT_NATIVE_RAW_TEXT_HOST_COMPONENTS.has(elementName);
|
|
30674
|
-
const isRawTextReportTarget = (elementName) => elementName !== null && (isNonTextHostName(elementName) || autoDetectedNonTextWrappers.has(elementName));
|
|
30675
|
-
const importedNonTextWrapperCache = /* @__PURE__ */ new Map();
|
|
30676
|
-
const isImportedNonTextWrapper = (elementName, contextNode) => {
|
|
30677
|
-
if (elementName === null || !isReactComponentName(elementName)) return false;
|
|
30678
|
-
const { filename } = context;
|
|
30679
|
-
if (filename === void 0) return false;
|
|
30680
|
-
const cached = importedNonTextWrapperCache.get(elementName);
|
|
30681
|
-
if (cached !== void 0) return cached;
|
|
30682
|
-
const isNonTextWrapper = resolveImportedComponentForwarding(contextNode, filename, elementName, isTextHandlingComponent, isNonTextHostName) === "nonText";
|
|
30683
|
-
importedNonTextWrapperCache.set(elementName, isNonTextWrapper);
|
|
30684
|
-
return isNonTextWrapper;
|
|
30685
|
-
};
|
|
30612
|
+
let autoDetectedWrappers = /* @__PURE__ */ new Set();
|
|
30686
30613
|
return {
|
|
30687
30614
|
Program(programNode) {
|
|
30688
30615
|
isDomComponentFile = hasDirective(programNode, "use dom");
|
|
30689
|
-
|
|
30690
|
-
autoDetectedTextWrappers = childrenForwarding.textWrappers;
|
|
30691
|
-
autoDetectedNonTextWrappers = childrenForwarding.nonTextWrappers;
|
|
30616
|
+
autoDetectedWrappers = collectTextWrapperComponents(programNode, isTextHandlingComponent);
|
|
30692
30617
|
},
|
|
30693
30618
|
JSXElement(node) {
|
|
30694
30619
|
if (isDomComponentFile) return;
|
|
30695
30620
|
const elementName = resolveTextBoundaryName(node.openingElement);
|
|
30696
|
-
if (elementName && (isTextHandlingComponent(elementName) ||
|
|
30621
|
+
if (elementName && (isTextHandlingComponent(elementName) || autoDetectedWrappers.has(elementName))) return;
|
|
30697
30622
|
if (isExpoUiComponentElement(node.openingElement, node, "ListItem")) return;
|
|
30698
30623
|
if (isInsidePlatformOsWebBranch(node)) return;
|
|
30699
30624
|
if (isTransparentTextWrapper(elementName) && isInsideTextHandlingComponent(node)) return;
|
|
30700
|
-
if (!(node.children ?? []).some(isRawTextContent)) return;
|
|
30701
|
-
if (!isRawTextReportTarget(elementName) && !isImportedNonTextWrapper(elementName, node)) return;
|
|
30702
30625
|
for (const child of node.children ?? []) {
|
|
30703
30626
|
if (!isRawTextContent(child)) continue;
|
|
30704
30627
|
context.report({
|
|
@@ -35941,6 +35864,22 @@ const isSupabaseMigrationPath = (relativePath) => /(?:^|\/)supabase\/(?:migratio
|
|
|
35941
35864
|
//#endregion
|
|
35942
35865
|
//#region src/plugin/rules/security-scan/utils/is-sql-path.ts
|
|
35943
35866
|
const isSqlPath = (relativePath) => relativePath.endsWith(".sql") || isSupabaseMigrationPath(relativePath);
|
|
35867
|
+
const supabaseRlsPolicyRisk = defineRule({
|
|
35868
|
+
id: "supabase-rls-policy-risk",
|
|
35869
|
+
title: "Permissive Supabase RLS policy",
|
|
35870
|
+
severity: "error",
|
|
35871
|
+
recommendation: "Keep public-read policies explicit, but gate inserts, updates, deletes, and service-role bypasses behind `auth.uid()` plus trusted tenant membership.",
|
|
35872
|
+
scan: scanByPattern({
|
|
35873
|
+
shouldScan: (file) => isSqlPath(file.relativePath),
|
|
35874
|
+
pattern: [
|
|
35875
|
+
/disable\s+row\s+level\s+security/i,
|
|
35876
|
+
/create\s+policy[\s\S]{0,700}auth\.role\(\)\s*=\s*["']service_role["']/i,
|
|
35877
|
+
/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,
|
|
35878
|
+
/create\s+policy(?:(?!\bfor\s+select\b)[\s\S]){0,700}\b(?:using|with\s+check)\s*\(\s*true\s*\)/i
|
|
35879
|
+
],
|
|
35880
|
+
message: "Supabase policy SQL disables RLS, permits writes broadly, or references a service-role bypass."
|
|
35881
|
+
})
|
|
35882
|
+
});
|
|
35944
35883
|
//#endregion
|
|
35945
35884
|
//#region src/plugin/rules/security-scan/utils/sanitize-sql-for-scan.ts
|
|
35946
35885
|
const DOLLAR_QUOTE_TAG_PATTERN = /^\$[A-Za-z_]?\w*\$/;
|
|
@@ -36117,60 +36056,6 @@ const sanitizeSqlForScan = (content) => {
|
|
|
36117
36056
|
return characters.join("");
|
|
36118
36057
|
};
|
|
36119
36058
|
//#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
|
|
36174
36059
|
//#region src/plugin/rules/security-scan/supabase-table-missing-rls.ts
|
|
36175
36060
|
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;
|
|
36176
36061
|
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");
|
|
@@ -42661,7 +42546,6 @@ const CROSS_FILE_RULE_IDS = new Set([
|
|
|
42661
42546
|
"nextjs-missing-metadata",
|
|
42662
42547
|
"nextjs-no-use-search-params-without-suspense",
|
|
42663
42548
|
"no-mutating-reducer-state",
|
|
42664
|
-
"rn-no-raw-text",
|
|
42665
42549
|
"rn-prefer-expo-image"
|
|
42666
42550
|
]);
|
|
42667
42551
|
//#endregion
|