oxlint-plugin-react-doctor 0.5.6-dev.8908f98 → 0.5.6-dev.b08ca1c
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 +125 -4
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -3104,6 +3104,76 @@ const AUTH_FUNCTION_NAMES = new Set([
|
|
|
3104
3104
|
"getAuth",
|
|
3105
3105
|
"validateSession"
|
|
3106
3106
|
]);
|
|
3107
|
+
const AUTH_STRONG_TOKEN_PATTERN = /^auth(?:n|z|ed|enticate[ds]?|enticating|entication|orize[ds]?|orizing|orization|orizer)?$/;
|
|
3108
|
+
const AUTH_STANDALONE_NOUN_TOKENS = new Set([
|
|
3109
|
+
"signedin",
|
|
3110
|
+
"loggedin",
|
|
3111
|
+
"signin"
|
|
3112
|
+
]);
|
|
3113
|
+
const AUTH_ASSERTIVE_VERB_TOKENS = new Set([
|
|
3114
|
+
"require",
|
|
3115
|
+
"ensure",
|
|
3116
|
+
"assert",
|
|
3117
|
+
"verify",
|
|
3118
|
+
"validate",
|
|
3119
|
+
"check",
|
|
3120
|
+
"protect",
|
|
3121
|
+
"enforce",
|
|
3122
|
+
"guard",
|
|
3123
|
+
"gate",
|
|
3124
|
+
"restrict",
|
|
3125
|
+
"is",
|
|
3126
|
+
"has",
|
|
3127
|
+
"can",
|
|
3128
|
+
"must"
|
|
3129
|
+
]);
|
|
3130
|
+
const AUTH_GETTER_VERB_TOKENS = new Set([
|
|
3131
|
+
"get",
|
|
3132
|
+
"fetch",
|
|
3133
|
+
"load",
|
|
3134
|
+
"read",
|
|
3135
|
+
"resolve",
|
|
3136
|
+
"retrieve",
|
|
3137
|
+
"use"
|
|
3138
|
+
]);
|
|
3139
|
+
const AUTH_QUALIFIER_TOKENS = new Set([
|
|
3140
|
+
"current",
|
|
3141
|
+
"my",
|
|
3142
|
+
"own"
|
|
3143
|
+
]);
|
|
3144
|
+
const AUTH_STRONG_NOUN_TOKENS = new Set([
|
|
3145
|
+
"session",
|
|
3146
|
+
"sessions",
|
|
3147
|
+
"login",
|
|
3148
|
+
"admin",
|
|
3149
|
+
"admins",
|
|
3150
|
+
"superadmin",
|
|
3151
|
+
"superuser",
|
|
3152
|
+
"role",
|
|
3153
|
+
"roles",
|
|
3154
|
+
"permission",
|
|
3155
|
+
"permissions",
|
|
3156
|
+
"jwt",
|
|
3157
|
+
"identity",
|
|
3158
|
+
"principal",
|
|
3159
|
+
"credential",
|
|
3160
|
+
"credentials"
|
|
3161
|
+
]);
|
|
3162
|
+
const AUTH_WEAK_NOUN_TOKENS = new Set([
|
|
3163
|
+
"user",
|
|
3164
|
+
"users",
|
|
3165
|
+
"account",
|
|
3166
|
+
"accounts",
|
|
3167
|
+
"token",
|
|
3168
|
+
"tokens",
|
|
3169
|
+
"access",
|
|
3170
|
+
"me",
|
|
3171
|
+
"viewer",
|
|
3172
|
+
"caller",
|
|
3173
|
+
"subject",
|
|
3174
|
+
"scope",
|
|
3175
|
+
"scopes"
|
|
3176
|
+
]);
|
|
3107
3177
|
const GENERIC_AUTH_METHOD_NAMES = new Set(["getUser"]);
|
|
3108
3178
|
const AUTH_OBJECT_PATTERN = /(?:^|[._])(?:auth|authn|authz|clerk|session|jwt|firebase|supabase|nextauth|kinde|workos|stytch|descope|cognito|propelauth|lucia)/i;
|
|
3109
3179
|
const SECRET_PATTERNS = [
|
|
@@ -22944,11 +23014,17 @@ const classifySecretFileExposure = (filename, options = {}) => {
|
|
|
22944
23014
|
return "unknown";
|
|
22945
23015
|
};
|
|
22946
23016
|
//#endregion
|
|
22947
|
-
//#region src/plugin/utils/
|
|
22948
|
-
const
|
|
22949
|
-
|
|
23017
|
+
//#region src/plugin/utils/tokenize-identifier-words.ts
|
|
23018
|
+
const IDENTIFIER_WORD_PATTERN = /[A-Z]+(?=[A-Z][a-z]|\b)|[A-Z]?[a-z]+|\d+/g;
|
|
23019
|
+
const tokenizeIdentifierWords = (identifierName) => {
|
|
23020
|
+
const words = identifierName.match(IDENTIFIER_WORD_PATTERN);
|
|
23021
|
+
if (!words) return [];
|
|
23022
|
+
return words.map((word) => word.toLowerCase());
|
|
22950
23023
|
};
|
|
22951
23024
|
//#endregion
|
|
23025
|
+
//#region src/plugin/utils/get-identifier-trailing-word.ts
|
|
23026
|
+
const getIdentifierTrailingWord = (identifierName) => tokenizeIdentifierWords(identifierName).at(-1) ?? identifierName.toLowerCase();
|
|
23027
|
+
//#endregion
|
|
22952
23028
|
//#region src/plugin/constants/tanstack.ts
|
|
22953
23029
|
const TANSTACK_ROUTE_FILE_PATTERN = /\/routes\//;
|
|
22954
23030
|
const TANSTACK_ROOT_ROUTE_FILE_PATTERN = /__root\.(tsx?|jsx?)$/;
|
|
@@ -34895,6 +34971,47 @@ const serverAfterNonblocking = defineRule({
|
|
|
34895
34971
|
}
|
|
34896
34972
|
});
|
|
34897
34973
|
//#endregion
|
|
34974
|
+
//#region src/plugin/utils/is-auth-guard-name.ts
|
|
34975
|
+
const SIGNED_IN_HEAD_TOKENS = new Set([
|
|
34976
|
+
"signed",
|
|
34977
|
+
"logged",
|
|
34978
|
+
"sign"
|
|
34979
|
+
]);
|
|
34980
|
+
const mergeSignedInTokens = (tokens) => {
|
|
34981
|
+
const mergedTokens = [];
|
|
34982
|
+
for (let tokenIndex = 0; tokenIndex < tokens.length; tokenIndex += 1) {
|
|
34983
|
+
const currentToken = tokens[tokenIndex];
|
|
34984
|
+
if (SIGNED_IN_HEAD_TOKENS.has(currentToken) && tokens[tokenIndex + 1] === "in") {
|
|
34985
|
+
mergedTokens.push(`${currentToken}in`);
|
|
34986
|
+
tokenIndex += 1;
|
|
34987
|
+
continue;
|
|
34988
|
+
}
|
|
34989
|
+
mergedTokens.push(currentToken);
|
|
34990
|
+
}
|
|
34991
|
+
return mergedTokens;
|
|
34992
|
+
};
|
|
34993
|
+
const isAuthGuardName = (calleeName) => {
|
|
34994
|
+
const tokens = mergeSignedInTokens(tokenizeIdentifierWords(calleeName));
|
|
34995
|
+
if (tokens.length === 0) return false;
|
|
34996
|
+
let hasAssertiveVerb = false;
|
|
34997
|
+
let hasGetterVerb = false;
|
|
34998
|
+
let hasQualifier = false;
|
|
34999
|
+
let hasStrongNoun = false;
|
|
35000
|
+
let hasWeakNoun = false;
|
|
35001
|
+
for (const token of tokens) {
|
|
35002
|
+
if (AUTH_STRONG_TOKEN_PATTERN.test(token) || AUTH_STANDALONE_NOUN_TOKENS.has(token)) return true;
|
|
35003
|
+
if (AUTH_ASSERTIVE_VERB_TOKENS.has(token)) hasAssertiveVerb = true;
|
|
35004
|
+
if (AUTH_GETTER_VERB_TOKENS.has(token)) hasGetterVerb = true;
|
|
35005
|
+
if (AUTH_QUALIFIER_TOKENS.has(token)) hasQualifier = true;
|
|
35006
|
+
if (AUTH_STRONG_NOUN_TOKENS.has(token)) hasStrongNoun = true;
|
|
35007
|
+
if (AUTH_WEAK_NOUN_TOKENS.has(token)) hasWeakNoun = true;
|
|
35008
|
+
}
|
|
35009
|
+
if (hasAssertiveVerb && (hasStrongNoun || hasWeakNoun)) return true;
|
|
35010
|
+
if (hasGetterVerb && hasStrongNoun) return true;
|
|
35011
|
+
if (hasQualifier && hasWeakNoun) return true;
|
|
35012
|
+
return false;
|
|
35013
|
+
};
|
|
35014
|
+
//#endregion
|
|
34898
35015
|
//#region src/plugin/rules/server/server-auth-actions.ts
|
|
34899
35016
|
const isAsyncFunctionLikeNode = (node) => {
|
|
34900
35017
|
if (!node) return false;
|
|
@@ -34937,9 +35054,13 @@ const isMemberCallAuthRelated = (receiverNode, methodName, genericMethodNames) =
|
|
|
34937
35054
|
const getAuthCallName = (callExpression, allowedFunctionNames, genericMethodNames) => {
|
|
34938
35055
|
const calleeNode = unwrapTypeWrappedCallee(callExpression.callee);
|
|
34939
35056
|
if (!calleeNode) return null;
|
|
34940
|
-
if (isNodeOfType(calleeNode, "Identifier"))
|
|
35057
|
+
if (isNodeOfType(calleeNode, "Identifier")) {
|
|
35058
|
+
const calleeName = calleeNode.name;
|
|
35059
|
+
return allowedFunctionNames.has(calleeName) || isAuthGuardName(calleeName) ? calleeName : null;
|
|
35060
|
+
}
|
|
34941
35061
|
if (isNodeOfType(calleeNode, "MemberExpression") && isNodeOfType(calleeNode.property, "Identifier")) {
|
|
34942
35062
|
const methodName = calleeNode.property.name;
|
|
35063
|
+
if (isAuthGuardName(methodName)) return methodName;
|
|
34943
35064
|
if (!allowedFunctionNames.has(methodName)) return null;
|
|
34944
35065
|
if (!isMemberCallAuthRelated(calleeNode.object, methodName, genericMethodNames)) return null;
|
|
34945
35066
|
return methodName;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oxlint-plugin-react-doctor",
|
|
3
|
-
"version": "0.5.6-dev.
|
|
3
|
+
"version": "0.5.6-dev.b08ca1c",
|
|
4
4
|
"description": "oxlint plugin for React Doctor: diagnose React codebases for security, performance, correctness, accessibility, bundle-size, and architecture issues",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@typescript-eslint/types": "^8.59.3",
|
|
45
45
|
"eslint-scope": "^9.1.2",
|
|
46
46
|
"eslint-visitor-keys": "^5.0.1",
|
|
47
|
-
"oxc-parser": "^0.
|
|
47
|
+
"oxc-parser": "^0.135.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/node": "^25.6.0"
|