@sarj/eslint-plugin 16.12.0 → 16.13.1
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.cjs +294 -252
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2032 -1993
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -52,7 +52,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
52
52
|
// package.json
|
|
53
53
|
var package_default = {
|
|
54
54
|
name: "@sarj/eslint-plugin",
|
|
55
|
-
version: "16.
|
|
55
|
+
version: "16.13.1",
|
|
56
56
|
packageManager: "npm@12.2.0",
|
|
57
57
|
description: "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
|
|
58
58
|
type: "module",
|
|
@@ -4794,254 +4794,291 @@ var no_comment_cruft_default = createRule({
|
|
|
4794
4794
|
|
|
4795
4795
|
// src/rules/no-cors-wildcard-with-credentials.ts
|
|
4796
4796
|
var import_utils28 = require("@typescript-eslint/utils");
|
|
4797
|
+
var ACAO_HEADER = "access-control-allow-origin";
|
|
4798
|
+
var ACAC_HEADER = "access-control-allow-credentials";
|
|
4799
|
+
var HEADER_METHODS = /* @__PURE__ */ new Set(["set", "setHeader", "append", "delete", "removeHeader"]);
|
|
4800
|
+
var CONTROL_STATEMENTS = /* @__PURE__ */ new Set([
|
|
4801
|
+
"BlockStatement",
|
|
4802
|
+
"IfStatement",
|
|
4803
|
+
"SwitchStatement",
|
|
4804
|
+
"TryStatement",
|
|
4805
|
+
"ForStatement",
|
|
4806
|
+
"ForInStatement",
|
|
4807
|
+
"ForOfStatement",
|
|
4808
|
+
"WhileStatement",
|
|
4809
|
+
"DoWhileStatement",
|
|
4810
|
+
"WithStatement",
|
|
4811
|
+
"LabeledStatement"
|
|
4812
|
+
]);
|
|
4797
4813
|
var NO_CORS_WILDCARD_WITH_CREDENTIALS_DOCUMENTATION = {
|
|
4798
|
-
summary: "
|
|
4799
|
-
rationale: "
|
|
4800
|
-
remediation: "
|
|
4801
|
-
category: "
|
|
4814
|
+
summary: "Review wildcard CORS origins combined with an explicit credentials opt-in.",
|
|
4815
|
+
rationale: "Browsers do not expose credentialed cross-origin responses when Access-Control-Allow-Origin is '*'. Credentialless requests can still use a wildcard even when Access-Control-Allow-Credentials is emitted, so review the intended caller contract.",
|
|
4816
|
+
remediation: "Use a trusted explicit origin for credentialed callers, or omit the credentials opt-in for a credentialless wildcard endpoint.",
|
|
4817
|
+
category: "correctness",
|
|
4818
|
+
defaultLevel: "warning",
|
|
4819
|
+
autofix: "none",
|
|
4820
|
+
references: ["https://fetch.spec.whatwg.org/#cors-protocol-and-credentials"],
|
|
4802
4821
|
limitations: [
|
|
4803
|
-
"
|
|
4822
|
+
"Options require a stable official cors default import or hono/cors named or namespace import, one literal object argument, literal origin '*', and credentials true. Custom bindings, callbacks, arrays, conditional origins and dynamic options are excluded.",
|
|
4823
|
+
"Literal header maps use effective case-sensitive JavaScript property writes followed by native Headers combination of differently-cased header names. Unknown values/spreads/computed keys, accessors and non-null prototypes lose proof; literals used only inside object spreads are not reported independently.",
|
|
4824
|
+
"Manual set/setHeader/delete/removeHeader calls use direct identifier receivers and literal arguments in straight-line program or function bodies. Control-flow bodies, member receivers, aliases and unknown operations are excluded. Append requires a proven empty native Headers constructor or a preceding known write/removal.",
|
|
4825
|
+
"The rule reviews a local configuration contract, not actual request credentials or a data disclosure. Runtime patches, custom header method implementations, interprocedural mutation and later consumers are not resolved. No autofix."
|
|
4804
4826
|
],
|
|
4805
4827
|
examples: [
|
|
4806
4828
|
{
|
|
4807
4829
|
id: "trusted-origin-with-credentials",
|
|
4808
|
-
title: "
|
|
4830
|
+
title: "Use a trusted origin for credentialed callers",
|
|
4809
4831
|
outcome: "no-match",
|
|
4810
|
-
files: [{ path: "src/server.ts", source: "
|
|
4832
|
+
files: [{ path: "src/server.ts", source: "import cors from 'cors'; cors({ origin: 'https://app.example.com', credentials: true });" }],
|
|
4811
4833
|
focusPath: "src/server.ts",
|
|
4812
4834
|
expectedCount: 0,
|
|
4813
4835
|
public: true
|
|
4814
4836
|
},
|
|
4815
4837
|
{
|
|
4816
4838
|
id: "wildcard-origin-with-credentials",
|
|
4817
|
-
title: "
|
|
4839
|
+
title: "Review a wildcard that blocks access to credentialed responses",
|
|
4818
4840
|
outcome: "match",
|
|
4819
|
-
files: [{ path: "src/server.ts", source: "
|
|
4841
|
+
files: [{ path: "src/server.ts", source: "import cors from 'cors'; cors({ origin: '*', credentials: true });" }],
|
|
4820
4842
|
focusPath: "src/server.ts",
|
|
4821
4843
|
expectedCount: 1,
|
|
4822
4844
|
public: true
|
|
4823
4845
|
}
|
|
4824
4846
|
]
|
|
4825
4847
|
};
|
|
4826
|
-
var ACAO_HEADER = "access-control-allow-origin";
|
|
4827
|
-
var ACAC_HEADER = "access-control-allow-credentials";
|
|
4828
|
-
var HEADER_SET_METHODS = /* @__PURE__ */ new Set(["setheader", "set", "append"]);
|
|
4829
|
-
function isCredentialsTrueValue(node) {
|
|
4830
|
-
node = unwrapExpression(node);
|
|
4831
|
-
if (node.type === "Literal") {
|
|
4832
|
-
if (node.value === true) {
|
|
4833
|
-
return true;
|
|
4834
|
-
}
|
|
4835
|
-
if (typeof node.value === "string") {
|
|
4836
|
-
return node.value.trim().toLowerCase() === "true";
|
|
4837
|
-
}
|
|
4838
|
-
}
|
|
4839
|
-
return false;
|
|
4840
|
-
}
|
|
4841
|
-
function isStarLiteral(node) {
|
|
4842
|
-
return node.type === "Literal" && node.value === "*";
|
|
4843
|
-
}
|
|
4844
|
-
function propertyKeyName(prop) {
|
|
4845
|
-
return import_utils28.ASTUtils.getPropertyName(prop) ?? void 0;
|
|
4846
|
-
}
|
|
4847
|
-
function isCorsWildcardCredentialsCall(node) {
|
|
4848
|
-
const name = calleeName(node);
|
|
4849
|
-
if (name === void 0 || name.toLowerCase() !== "cors") {
|
|
4850
|
-
return false;
|
|
4851
|
-
}
|
|
4852
|
-
const options = node.arguments.map(unwrapExpression).find(
|
|
4853
|
-
(arg) => arg.type === "ObjectExpression"
|
|
4854
|
-
);
|
|
4855
|
-
if (options === void 0) {
|
|
4856
|
-
return false;
|
|
4857
|
-
}
|
|
4858
|
-
let hasCredentials = false;
|
|
4859
|
-
let hasWildcardOrigin = false;
|
|
4860
|
-
for (const prop of options.properties) {
|
|
4861
|
-
if (prop.type !== "Property") {
|
|
4862
|
-
continue;
|
|
4863
|
-
}
|
|
4864
|
-
const key = propertyKeyName(prop);
|
|
4865
|
-
if (key === "credentials" && isTrueLiteral(prop.value)) {
|
|
4866
|
-
hasCredentials = true;
|
|
4867
|
-
} else if (key === "origin" && subtreeContainsStarLiteral(prop.value)) {
|
|
4868
|
-
hasWildcardOrigin = true;
|
|
4869
|
-
}
|
|
4870
|
-
}
|
|
4871
|
-
return hasCredentials && hasWildcardOrigin;
|
|
4872
|
-
}
|
|
4873
|
-
function isTrueLiteral(node) {
|
|
4874
|
-
node = unwrapExpression(node);
|
|
4875
|
-
return node.type === "Literal" && node.value === true;
|
|
4876
|
-
}
|
|
4877
|
-
function subtreeContainsStarLiteral(node) {
|
|
4878
|
-
if (isStarLiteral(node)) {
|
|
4879
|
-
return true;
|
|
4880
|
-
}
|
|
4881
|
-
return forEachOwnAstChild(
|
|
4882
|
-
node,
|
|
4883
|
-
subtreeContainsStarLiteral,
|
|
4884
|
-
(key) => key !== "loc" && key !== "range"
|
|
4885
|
-
);
|
|
4886
|
-
}
|
|
4887
|
-
function calleeName(node) {
|
|
4888
|
-
const callee = unwrapExpression(node.callee);
|
|
4889
|
-
if (callee.type === "Identifier") {
|
|
4890
|
-
return callee.name;
|
|
4891
|
-
}
|
|
4892
|
-
if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier") {
|
|
4893
|
-
return callee.property.name;
|
|
4894
|
-
}
|
|
4895
|
-
return void 0;
|
|
4896
|
-
}
|
|
4897
|
-
function isWildcardCredentialsHeaderObject(node) {
|
|
4898
|
-
let wildcardOrigin = false;
|
|
4899
|
-
let credentialsTrue = false;
|
|
4900
|
-
for (const prop of node.properties) {
|
|
4901
|
-
if (prop.type !== "Property") {
|
|
4902
|
-
continue;
|
|
4903
|
-
}
|
|
4904
|
-
const key = propertyKeyName(prop);
|
|
4905
|
-
if (key === void 0) {
|
|
4906
|
-
continue;
|
|
4907
|
-
}
|
|
4908
|
-
const header = key.toLowerCase();
|
|
4909
|
-
if (header === ACAO_HEADER && isStarLiteral(prop.value)) {
|
|
4910
|
-
wildcardOrigin = true;
|
|
4911
|
-
} else if (header === ACAC_HEADER && isCredentialsTrueValue(prop.value)) {
|
|
4912
|
-
credentialsTrue = true;
|
|
4913
|
-
}
|
|
4914
|
-
}
|
|
4915
|
-
return wildcardOrigin && credentialsTrue;
|
|
4916
|
-
}
|
|
4917
|
-
function classifyHeaderSetCall(node) {
|
|
4918
|
-
const callee = unwrapExpression(node.callee);
|
|
4919
|
-
if (callee.type !== "MemberExpression" || callee.computed || callee.property.type !== "Identifier" || !HEADER_SET_METHODS.has(callee.property.name.toLowerCase())) {
|
|
4920
|
-
return void 0;
|
|
4921
|
-
}
|
|
4922
|
-
const [nameArg, valueArg] = node.arguments;
|
|
4923
|
-
if (nameArg === void 0 || valueArg === void 0 || nameArg.type !== "Literal" || typeof nameArg.value !== "string") {
|
|
4924
|
-
return void 0;
|
|
4925
|
-
}
|
|
4926
|
-
const header = nameArg.value.toLowerCase();
|
|
4927
|
-
if (header === ACAO_HEADER && isStarLiteral(valueArg)) {
|
|
4928
|
-
return "origin";
|
|
4929
|
-
}
|
|
4930
|
-
if (header === ACAC_HEADER && isCredentialsTrueValue(valueArg)) {
|
|
4931
|
-
return "credentials";
|
|
4932
|
-
}
|
|
4933
|
-
return void 0;
|
|
4934
|
-
}
|
|
4935
|
-
function enclosingScope(node) {
|
|
4936
|
-
let current = node.parent;
|
|
4937
|
-
while (current) {
|
|
4938
|
-
if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") {
|
|
4939
|
-
return current;
|
|
4940
|
-
}
|
|
4941
|
-
current = current.parent;
|
|
4942
|
-
}
|
|
4943
|
-
return void 0;
|
|
4944
|
-
}
|
|
4945
4848
|
var no_cors_wildcard_with_credentials_default = createRule({
|
|
4946
4849
|
name: "no-cors-wildcard-with-credentials",
|
|
4947
4850
|
documentation: NO_CORS_WILDCARD_WITH_CREDENTIALS_DOCUMENTATION,
|
|
4948
4851
|
meta: {
|
|
4949
4852
|
type: "problem",
|
|
4950
|
-
docs: {
|
|
4951
|
-
description: "Disallow wildcard CORS origins when credentials are enabled."
|
|
4952
|
-
},
|
|
4853
|
+
docs: { description: NO_CORS_WILDCARD_WITH_CREDENTIALS_DOCUMENTATION.summary },
|
|
4953
4854
|
schema: [],
|
|
4954
4855
|
messages: {
|
|
4955
|
-
corsWildcardWithCredentials:
|
|
4856
|
+
corsWildcardWithCredentials: "A wildcard CORS origin blocks browser access to credentialed responses. Review the intended contract: use a trusted explicit origin for credentialed callers, or omit the credentials opt-in for a credentialless endpoint."
|
|
4956
4857
|
}
|
|
4957
4858
|
},
|
|
4958
4859
|
defaultOptions: [],
|
|
4959
4860
|
create(context) {
|
|
4960
|
-
const
|
|
4961
|
-
const
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
const
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
scopeHeaderSets.set(key, receivers);
|
|
4973
|
-
}
|
|
4974
|
-
let entry = receivers.get(receiver);
|
|
4975
|
-
if (entry === void 0) {
|
|
4976
|
-
entry = { originNodes: [], credentialsNodes: [] };
|
|
4977
|
-
receivers.set(receiver, entry);
|
|
4978
|
-
}
|
|
4979
|
-
if (kind === "origin") {
|
|
4980
|
-
entry.originNodes.push(node);
|
|
4981
|
-
} else {
|
|
4982
|
-
entry.credentialsNodes.push(node);
|
|
4861
|
+
const sourceCode = context.sourceCode;
|
|
4862
|
+
const importedCors = /* @__PURE__ */ new Set();
|
|
4863
|
+
const importedNamespaces = /* @__PURE__ */ new Set();
|
|
4864
|
+
const receiverProof = /* @__PURE__ */ new Map();
|
|
4865
|
+
function binding(node) {
|
|
4866
|
+
return import_utils28.ASTUtils.findVariable(sourceCode.getScope(node), node.name);
|
|
4867
|
+
}
|
|
4868
|
+
function officialCors(node) {
|
|
4869
|
+
const value = unwrapExpression(node);
|
|
4870
|
+
if (value.type === "Identifier") {
|
|
4871
|
+
const variable2 = binding(value);
|
|
4872
|
+
return variable2 !== null && importedCors.has(variable2);
|
|
4983
4873
|
}
|
|
4874
|
+
if (value.type !== "MemberExpression" || value.optional || value.computed || value.property.type !== "Identifier" || value.property.name !== "cors" || value.object.type !== "Identifier") return false;
|
|
4875
|
+
const variable = binding(value.object);
|
|
4876
|
+
return variable !== null && importedNamespaces.has(variable);
|
|
4984
4877
|
}
|
|
4985
|
-
function
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4878
|
+
function recordImports(node) {
|
|
4879
|
+
for (const statement of node.body) {
|
|
4880
|
+
if (statement.type === "ImportDeclaration" && statement.importKind !== "type") recordImport(statement);
|
|
4881
|
+
}
|
|
4882
|
+
}
|
|
4883
|
+
function recordImport(node) {
|
|
4884
|
+
for (const specifier of node.specifiers) {
|
|
4885
|
+
const role = importRole(specifier, node.source.value);
|
|
4886
|
+
if (role === null) continue;
|
|
4887
|
+
const variable = binding(specifier.local);
|
|
4888
|
+
if (variable === null || variable.references.some((reference) => reference.isWrite())) continue;
|
|
4889
|
+
(role === "namespace" ? importedNamespaces : importedCors).add(variable);
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4892
|
+
function inspectSequence(node) {
|
|
4893
|
+
if (node.body.some((statement) => CONTROL_STATEMENTS.has(statement.type))) return;
|
|
4894
|
+
const states = /* @__PURE__ */ new Map();
|
|
4895
|
+
for (const statement of node.body) {
|
|
4896
|
+
if (statement.type === "ImportDeclaration" || statement.type === "FunctionDeclaration") continue;
|
|
4897
|
+
if (statement.type === "VariableDeclaration") {
|
|
4898
|
+
recordHeaderDeclaration(statement, states);
|
|
4899
|
+
continue;
|
|
4900
|
+
}
|
|
4901
|
+
const expression = statement.type === "ExpressionStatement" ? unwrapExpression(statement.expression) : null;
|
|
4902
|
+
if (expression?.type !== "CallExpression" || !applyHeaderWrite(expression, states)) states.clear();
|
|
4992
4903
|
}
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4904
|
+
for (const state of states.values()) {
|
|
4905
|
+
if (state.values[ACAO_HEADER] === "*" && state.values[ACAC_HEADER] === "true" && state.origin !== null) {
|
|
4906
|
+
context.report({ node: state.origin, messageId: "corsWildcardWithCredentials" });
|
|
4907
|
+
}
|
|
4997
4908
|
}
|
|
4998
|
-
return null;
|
|
4999
4909
|
}
|
|
5000
|
-
function
|
|
5001
|
-
const
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
4910
|
+
function recordHeaderDeclaration(node, states) {
|
|
4911
|
+
for (const declaration of node.declarations) {
|
|
4912
|
+
if (declaration.id.type === "Identifier" && declaration.init !== null && freshHeaders(declaration.init)) {
|
|
4913
|
+
const key = receiverKey(declaration.id);
|
|
4914
|
+
if (key !== null) states.set(key, { values: { [ACAO_HEADER]: null, [ACAC_HEADER]: null }, origin: null });
|
|
4915
|
+
} else if (declaration.id.type !== "Identifier" || declaration.init !== null && unwrapExpression(declaration.init).type !== "Literal") states.clear();
|
|
4916
|
+
}
|
|
4917
|
+
}
|
|
4918
|
+
function freshHeaders(node) {
|
|
4919
|
+
const value = unwrapExpression(node);
|
|
4920
|
+
if (value.type !== "NewExpression" || value.arguments.length !== 0) return false;
|
|
4921
|
+
const callee = unwrapExpression(value.callee);
|
|
4922
|
+
return callee.type === "Identifier" && callee.name === "Headers" && !binding(callee)?.defs.length;
|
|
4923
|
+
}
|
|
4924
|
+
function applyHeaderWrite(node, states) {
|
|
4925
|
+
const callee = unwrapExpression(node.callee);
|
|
4926
|
+
if (node.optional || callee.type !== "MemberExpression" || callee.optional || callee.computed || callee.property.type !== "Identifier" || !HEADER_METHODS.has(callee.property.name) || callee.object.type !== "Identifier") return false;
|
|
4927
|
+
const method = callee.property.name;
|
|
4928
|
+
const removal = method === "delete" || method === "removeHeader";
|
|
4929
|
+
if (node.arguments.length !== (removal ? 1 : 2)) return false;
|
|
4930
|
+
const name = literalString(node.arguments[0]);
|
|
4931
|
+
const value = removal ? null : headerValue(node.arguments[1]);
|
|
4932
|
+
if (name === null || !removal && value === null) return false;
|
|
4933
|
+
const key = receiverKey(callee.object);
|
|
4934
|
+
if (key === null) return false;
|
|
4935
|
+
let state = states.get(key);
|
|
4936
|
+
if (state === void 0) {
|
|
4937
|
+
state = { values: { [ACAO_HEADER]: void 0, [ACAC_HEADER]: void 0 }, origin: null };
|
|
4938
|
+
states.set(key, state);
|
|
4939
|
+
}
|
|
4940
|
+
const header = name.toLowerCase();
|
|
4941
|
+
if (header !== ACAO_HEADER && header !== ACAC_HEADER) return true;
|
|
4942
|
+
state.values[header] = method === "append" ? appendValue(state.values[header], value) : value;
|
|
4943
|
+
if (header === ACAO_HEADER) state.origin = node;
|
|
4944
|
+
return true;
|
|
4945
|
+
}
|
|
4946
|
+
function receiverKey(node) {
|
|
4947
|
+
const variable = binding(node);
|
|
4948
|
+
const key = variable ?? node.name;
|
|
4949
|
+
const cached = receiverProof.get(key);
|
|
4950
|
+
if (cached !== void 0) return cached ? key : null;
|
|
4951
|
+
const references = variable?.references ?? sourceCode.scopeManager?.globalScope?.through.filter((reference) => reference.identifier.name === node.name) ?? [];
|
|
4952
|
+
const unaliased = !variable?.defs.some(aliasDefinition) && references.every(headerReceiverReference);
|
|
4953
|
+
receiverProof.set(key, unaliased);
|
|
4954
|
+
return unaliased ? key : null;
|
|
5006
4955
|
}
|
|
5007
4956
|
return {
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
4957
|
+
Program(node) {
|
|
4958
|
+
recordImports(node);
|
|
4959
|
+
inspectSequence(node);
|
|
4960
|
+
},
|
|
4961
|
+
BlockStatement(node) {
|
|
4962
|
+
if (["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].includes(node.parent.type)) inspectSequence(node);
|
|
5012
4963
|
},
|
|
5013
4964
|
CallExpression(node) {
|
|
5014
|
-
if (
|
|
4965
|
+
if (node.optional || node.arguments.length !== 1 || !officialCors(node.callee)) return;
|
|
4966
|
+
const argument = node.arguments[0];
|
|
4967
|
+
const options = argument === void 0 ? void 0 : unwrapExpression(argument);
|
|
4968
|
+
if (options?.type !== "ObjectExpression") return;
|
|
4969
|
+
const properties = effectiveProperties(options);
|
|
4970
|
+
const origin = properties?.get("origin");
|
|
4971
|
+
const credentials = properties?.get("credentials");
|
|
4972
|
+
const value = credentials === void 0 ? void 0 : unwrapExpression(credentials.value);
|
|
4973
|
+
if (origin !== void 0 && literalString(origin.value) === "*" && value?.type === "Literal" && value.value === true) {
|
|
5015
4974
|
context.report({ node, messageId: "corsWildcardWithCredentials" });
|
|
5016
|
-
return;
|
|
5017
|
-
}
|
|
5018
|
-
const kind = classifyHeaderSetCall(node);
|
|
5019
|
-
if (kind !== void 0) {
|
|
5020
|
-
recordHeaderSet(node, kind);
|
|
5021
4975
|
}
|
|
5022
4976
|
},
|
|
5023
4977
|
ObjectExpression(node) {
|
|
5024
|
-
if (
|
|
4978
|
+
if (spreadOnly(node)) return;
|
|
4979
|
+
const properties = effectiveProperties(node);
|
|
4980
|
+
if (properties === null) return;
|
|
4981
|
+
const headers = /* @__PURE__ */ new Map();
|
|
4982
|
+
for (const [key, property] of properties) {
|
|
4983
|
+
const value = headerValue(property.value);
|
|
4984
|
+
if (value === null) return;
|
|
4985
|
+
const name = key.toLowerCase();
|
|
4986
|
+
if (name !== ACAO_HEADER && name !== ACAC_HEADER) continue;
|
|
4987
|
+
const previous = headers.get(name);
|
|
4988
|
+
headers.set(name, previous === void 0 ? value : previous + ", " + value);
|
|
4989
|
+
}
|
|
4990
|
+
if (headers.get(ACAO_HEADER) === "*" && headers.get(ACAC_HEADER) === "true") {
|
|
5025
4991
|
context.report({ node, messageId: "corsWildcardWithCredentials" });
|
|
5026
4992
|
}
|
|
5027
|
-
},
|
|
5028
|
-
"Program:exit"() {
|
|
5029
|
-
for (const receivers of scopeHeaderSets.values()) {
|
|
5030
|
-
for (const { originNodes, credentialsNodes } of receivers.values()) {
|
|
5031
|
-
if (originNodes.length > 0 && credentialsNodes.length > 0) {
|
|
5032
|
-
for (const node of originNodes) {
|
|
5033
|
-
context.report({
|
|
5034
|
-
node,
|
|
5035
|
-
messageId: "corsWildcardWithCredentials"
|
|
5036
|
-
});
|
|
5037
|
-
}
|
|
5038
|
-
}
|
|
5039
|
-
}
|
|
5040
|
-
}
|
|
5041
4993
|
}
|
|
5042
4994
|
};
|
|
5043
4995
|
}
|
|
5044
4996
|
});
|
|
4997
|
+
function importRole(specifier, module2) {
|
|
4998
|
+
if (module2 === "hono/cors" && specifier.type === "ImportNamespaceSpecifier") return "namespace";
|
|
4999
|
+
if (module2 === "cors" && specifier.type === "ImportDefaultSpecifier") return "function";
|
|
5000
|
+
if (specifier.type !== "ImportSpecifier" || specifier.importKind === "type") return null;
|
|
5001
|
+
const name = specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value;
|
|
5002
|
+
return module2 === "cors" && name === "default" || module2 === "hono/cors" && name === "cors" ? "function" : null;
|
|
5003
|
+
}
|
|
5004
|
+
function aliasDefinition(definition) {
|
|
5005
|
+
if (definition.type !== "Variable") return false;
|
|
5006
|
+
if (definition.node.id.type !== "Identifier") return true;
|
|
5007
|
+
const initializer = definition.node.init;
|
|
5008
|
+
return initializer !== null && ["Identifier", "MemberExpression", "AssignmentExpression", "ConditionalExpression", "LogicalExpression", "SequenceExpression"].includes(unwrapExpression(initializer).type);
|
|
5009
|
+
}
|
|
5010
|
+
function headerReceiverReference(reference) {
|
|
5011
|
+
let typeName = reference.identifier;
|
|
5012
|
+
while (typeName.parent.type === "TSQualifiedName") typeName = typeName.parent;
|
|
5013
|
+
if (reference.isValueReference === false || typeName.parent.type === "TSTypeQuery") return true;
|
|
5014
|
+
if (reference.isWrite()) return reference.init === true;
|
|
5015
|
+
const member = reference.identifier.parent;
|
|
5016
|
+
if (member.type !== "MemberExpression" || member.object !== reference.identifier || member.computed || member.optional || member.property.type !== "Identifier" || !HEADER_METHODS.has(member.property.name)) return false;
|
|
5017
|
+
return member.parent.type === "CallExpression" && member.parent.callee === member && !member.parent.optional;
|
|
5018
|
+
}
|
|
5019
|
+
function appendValue(previous, value) {
|
|
5020
|
+
if (previous === void 0) return void 0;
|
|
5021
|
+
return previous === null ? value : previous + ", " + value;
|
|
5022
|
+
}
|
|
5023
|
+
function literalString(node) {
|
|
5024
|
+
const value = node === void 0 ? void 0 : unwrapExpression(node);
|
|
5025
|
+
return value?.type === "Literal" && typeof value.value === "string" ? value.value : null;
|
|
5026
|
+
}
|
|
5027
|
+
function headerValue(node) {
|
|
5028
|
+
const value = node === void 0 ? void 0 : unwrapExpression(node);
|
|
5029
|
+
if (value?.type !== "Literal" || !["string", "boolean", "number"].includes(typeof value.value)) return null;
|
|
5030
|
+
return String(value.value).replace(/^[\t\n\r ]+|[\t\n\r ]+$/g, "");
|
|
5031
|
+
}
|
|
5032
|
+
function effectiveProperties(node) {
|
|
5033
|
+
const properties = /* @__PURE__ */ new Map();
|
|
5034
|
+
for (const entry of node.properties) {
|
|
5035
|
+
const entries = propertyEntries(entry);
|
|
5036
|
+
if (entries === null) return null;
|
|
5037
|
+
for (const [key, property] of entries) properties.set(key, property);
|
|
5038
|
+
}
|
|
5039
|
+
return properties;
|
|
5040
|
+
}
|
|
5041
|
+
function propertyEntries(entry) {
|
|
5042
|
+
if (entry.type === "SpreadElement") {
|
|
5043
|
+
const value = unwrapExpression(entry.argument);
|
|
5044
|
+
return value.type === "ObjectExpression" ? effectiveProperties(value) : null;
|
|
5045
|
+
}
|
|
5046
|
+
if (entry.kind !== "init" || entry.method) return null;
|
|
5047
|
+
const key = entry.computed ? literalPropertyKey(entry.key) : import_utils28.ASTUtils.getPropertyName(entry);
|
|
5048
|
+
if (key === null) return null;
|
|
5049
|
+
if (key === "__proto__" && !entry.computed) {
|
|
5050
|
+
const value = unwrapExpression(entry.value);
|
|
5051
|
+
return value.type === "Literal" && value.value === null ? [] : null;
|
|
5052
|
+
}
|
|
5053
|
+
return [[key, entry]];
|
|
5054
|
+
}
|
|
5055
|
+
function literalPropertyKey(node) {
|
|
5056
|
+
const value = unwrapExpression(node);
|
|
5057
|
+
const literal = literalString(value);
|
|
5058
|
+
if (literal !== null) return literal;
|
|
5059
|
+
if (value.type !== "TemplateLiteral" || value.expressions.length !== 0) return null;
|
|
5060
|
+
return value.quasis[0]?.value.cooked ?? null;
|
|
5061
|
+
}
|
|
5062
|
+
function spreadOnly(node) {
|
|
5063
|
+
let current = outerExpression(node);
|
|
5064
|
+
while (current.parent !== void 0) {
|
|
5065
|
+
const parent = current.parent;
|
|
5066
|
+
if (parent.type === "SpreadElement") return parent.parent.type === "ObjectExpression";
|
|
5067
|
+
if (![
|
|
5068
|
+
"Property",
|
|
5069
|
+
"ObjectExpression",
|
|
5070
|
+
"ConditionalExpression",
|
|
5071
|
+
"LogicalExpression",
|
|
5072
|
+
"SequenceExpression",
|
|
5073
|
+
"TSAsExpression",
|
|
5074
|
+
"TSSatisfiesExpression",
|
|
5075
|
+
"TSNonNullExpression",
|
|
5076
|
+
"TSTypeAssertion"
|
|
5077
|
+
].includes(parent.type)) return false;
|
|
5078
|
+
current = parent;
|
|
5079
|
+
}
|
|
5080
|
+
return false;
|
|
5081
|
+
}
|
|
5045
5082
|
|
|
5046
5083
|
// src/rules/no-duplicate-lifecycle-refresh-listeners.ts
|
|
5047
5084
|
var import_utils30 = require("@typescript-eslint/utils");
|
|
@@ -5300,7 +5337,7 @@ var NO_DANGEROUSLY_ALLOW_SVG_DOCUMENTATION = {
|
|
|
5300
5337
|
}
|
|
5301
5338
|
]
|
|
5302
5339
|
};
|
|
5303
|
-
function
|
|
5340
|
+
function literalString2(node) {
|
|
5304
5341
|
if (node?.value.type === "Literal" && typeof node.value.value === "string") return node.value.value;
|
|
5305
5342
|
if (node?.value.type === "TemplateLiteral" && node.value.expressions.length === 0) return node.value.quasis[0]?.value.cooked ?? null;
|
|
5306
5343
|
return null;
|
|
@@ -5332,10 +5369,10 @@ var no_dangerously_allow_svg_default = createRule({
|
|
|
5332
5369
|
const node = exportedNextConfigProperty(context.sourceCode, ["images", "dangerouslyAllowSVG"]);
|
|
5333
5370
|
if (node !== null && node.value.type === "Literal" && node.value.value === true) {
|
|
5334
5371
|
const disposition = exportedNextConfigProperty(context.sourceCode, ["images", "contentDispositionType"]);
|
|
5335
|
-
const policy =
|
|
5372
|
+
const policy = literalString2(
|
|
5336
5373
|
exportedNextConfigProperty(context.sourceCode, ["images", "contentSecurityPolicy"])
|
|
5337
5374
|
);
|
|
5338
|
-
const attachmentDisposition = disposition === null ||
|
|
5375
|
+
const attachmentDisposition = disposition === null || literalString2(disposition) === "attachment";
|
|
5339
5376
|
if (attachmentDisposition && policy !== null && hasHardenedSvgPolicy(policy)) return;
|
|
5340
5377
|
context.report({ node, messageId: "noDangerouslyAllowSvg" });
|
|
5341
5378
|
}
|
|
@@ -5456,31 +5493,48 @@ function sqlTextOf(node) {
|
|
|
5456
5493
|
const right = sqlTextOf(node.right);
|
|
5457
5494
|
return left !== null && right !== null ? left + right : null;
|
|
5458
5495
|
}
|
|
5459
|
-
case import_utils32.AST_NODE_TYPES.ArrayExpression:
|
|
5460
|
-
|
|
5461
|
-
for (const element of node.elements) {
|
|
5462
|
-
if (element === null) {
|
|
5463
|
-
return null;
|
|
5464
|
-
}
|
|
5465
|
-
const part = sqlTextOf(element);
|
|
5466
|
-
if (part === null) {
|
|
5467
|
-
return null;
|
|
5468
|
-
}
|
|
5469
|
-
parts.push(part);
|
|
5470
|
-
}
|
|
5471
|
-
return parts.length > 0 ? parts.join(" ") : null;
|
|
5472
|
-
}
|
|
5496
|
+
case import_utils32.AST_NODE_TYPES.ArrayExpression:
|
|
5497
|
+
return fragmentArrayText(node);
|
|
5473
5498
|
default:
|
|
5474
5499
|
return null;
|
|
5475
5500
|
}
|
|
5476
5501
|
}
|
|
5477
|
-
function
|
|
5502
|
+
function fragmentArrayText(node) {
|
|
5503
|
+
const join4 = joinedFragmentCall(node);
|
|
5504
|
+
const separator = join4 === null ? " " : join4.arguments.length === 0 ? "," : join4.arguments.length === 1 ? staticString(join4.arguments[0]) : null;
|
|
5505
|
+
if (separator === null) return null;
|
|
5506
|
+
const parts = [];
|
|
5507
|
+
for (const element of node.elements) {
|
|
5508
|
+
if (element === null || join4 !== null && element.type === import_utils32.AST_NODE_TYPES.ArrayExpression) {
|
|
5509
|
+
return null;
|
|
5510
|
+
}
|
|
5511
|
+
const part = sqlTextOf(element);
|
|
5512
|
+
if (part === null) {
|
|
5513
|
+
return null;
|
|
5514
|
+
}
|
|
5515
|
+
parts.push(part);
|
|
5516
|
+
}
|
|
5517
|
+
return parts.length > 0 ? parts.join(separator) : null;
|
|
5518
|
+
}
|
|
5519
|
+
function joinedFragmentCall(node) {
|
|
5478
5520
|
const parent = node.parent;
|
|
5479
|
-
return parent?.type === import_utils32.AST_NODE_TYPES.MemberExpression && parent.object === node && !parent.computed && parent.property.type === import_utils32.AST_NODE_TYPES.Identifier && parent.property.name === "join" && parent.parent?.type === import_utils32.AST_NODE_TYPES.CallExpression;
|
|
5521
|
+
return parent?.type === import_utils32.AST_NODE_TYPES.MemberExpression && parent.object === node && !parent.computed && parent.property.type === import_utils32.AST_NODE_TYPES.Identifier && parent.property.name === "join" && parent.parent?.type === import_utils32.AST_NODE_TYPES.CallExpression && parent.parent.callee === parent ? parent.parent : null;
|
|
5480
5522
|
}
|
|
5481
5523
|
function markConsumed(node, consumed) {
|
|
5482
5524
|
consumed.add(node);
|
|
5483
|
-
|
|
5525
|
+
const value = unwrapExpression(node);
|
|
5526
|
+
if (value !== node) {
|
|
5527
|
+
markConsumed(value, consumed);
|
|
5528
|
+
return;
|
|
5529
|
+
}
|
|
5530
|
+
if (node.type === import_utils32.AST_NODE_TYPES.BinaryExpression && node.operator === "+") {
|
|
5531
|
+
markConsumed(node.left, consumed);
|
|
5532
|
+
markConsumed(node.right, consumed);
|
|
5533
|
+
} else if (node.type === import_utils32.AST_NODE_TYPES.ArrayExpression) {
|
|
5534
|
+
for (const element of node.elements) if (element !== null) markConsumed(element, consumed);
|
|
5535
|
+
} else if (node.type === import_utils32.AST_NODE_TYPES.TaggedTemplateExpression) {
|
|
5536
|
+
markConsumed(node.quasi, consumed);
|
|
5537
|
+
}
|
|
5484
5538
|
}
|
|
5485
5539
|
function createSqlListener(handler) {
|
|
5486
5540
|
const consumed = /* @__PURE__ */ new WeakSet();
|
|
@@ -5500,9 +5554,12 @@ function createSqlListener(handler) {
|
|
|
5500
5554
|
visit(node);
|
|
5501
5555
|
},
|
|
5502
5556
|
ArrayExpression: (node) => {
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5557
|
+
const join4 = joinedFragmentCall(node);
|
|
5558
|
+
if (join4 === null) return;
|
|
5559
|
+
visit(node);
|
|
5560
|
+
markConsumed(node, consumed);
|
|
5561
|
+
const separator = join4.arguments[0];
|
|
5562
|
+
if (separator !== void 0 && staticString(separator) !== null) markConsumed(separator, consumed);
|
|
5506
5563
|
},
|
|
5507
5564
|
TemplateLiteral: (node) => {
|
|
5508
5565
|
visit(node);
|
|
@@ -5533,7 +5590,7 @@ var NO_DYNAMIC_SQL_DOCUMENTATION = {
|
|
|
5533
5590
|
remediation: "Bind data values through SQL placeholders. Select non-bindable identifiers or clauses from fixed, reviewed fragments instead of interpolating runtime text.",
|
|
5534
5591
|
category: "security",
|
|
5535
5592
|
limitations: [
|
|
5536
|
-
"Template and concatenation fragments are inspected at recognized execution calls. Double-quoted identifiers, comments, and dollar strings are excluded; this is not a general SQL injection detector.",
|
|
5593
|
+
"Template and concatenation fragments are inspected at recognized execution calls, including runtime pieces surrounded by literal text inside a quoted value. Double-quoted identifiers, comments, and dollar strings are excluded; this is not a general SQL injection detector.",
|
|
5537
5594
|
"Literal fragments, legacy uppercase fragment names, and parameterizing tagged templates are exempt; uppercase spelling does not prove a value is static.",
|
|
5538
5595
|
"The bounded lexer recognizes doubled quotes, comments, and PostgreSQL dollar strings; dialect-specific escape modes and SQL generated through other APIs require separate security review."
|
|
5539
5596
|
],
|
|
@@ -5581,6 +5638,7 @@ var NO_DYNAMIC_SQL_DOCUMENTATION = {
|
|
|
5581
5638
|
var DEFAULT_METHODS = ["prepare", "exec", "query"];
|
|
5582
5639
|
var CONSTANT_CASE_RE = /^[A-Z][A-Z0-9_]*$/;
|
|
5583
5640
|
function isStaticFragment(expression) {
|
|
5641
|
+
expression = unwrapExpression(expression);
|
|
5584
5642
|
if (expression.type === import_utils33.AST_NODE_TYPES.Identifier) {
|
|
5585
5643
|
return CONSTANT_CASE_RE.test(expression.name);
|
|
5586
5644
|
}
|
|
@@ -5608,28 +5666,13 @@ function runtimeInterpolations(template) {
|
|
|
5608
5666
|
const unquoted = visible.slice(offset, offset + RUNTIME_MARKER.length) === RUNTIME_MARKER;
|
|
5609
5667
|
offset += RUNTIME_MARKER.length;
|
|
5610
5668
|
if (isStaticFragment(expression)) return [];
|
|
5611
|
-
if (inValue
|
|
5669
|
+
if (inValue) {
|
|
5612
5670
|
return [{ expression, messageId: "dynamicSql" }];
|
|
5613
5671
|
}
|
|
5614
5672
|
return unquoted ? [{ expression, messageId: "dynamicFragment" }] : [];
|
|
5615
5673
|
}
|
|
5616
5674
|
);
|
|
5617
5675
|
}
|
|
5618
|
-
function endsWithSqlQuote(text) {
|
|
5619
|
-
return /'\s*$/u.test(text);
|
|
5620
|
-
}
|
|
5621
|
-
function startsWithSqlQuote(text) {
|
|
5622
|
-
return /^\s*'/u.test(text);
|
|
5623
|
-
}
|
|
5624
|
-
function staticLiteralText(node) {
|
|
5625
|
-
if (node.type === import_utils33.AST_NODE_TYPES.Literal && typeof node.value === "string") {
|
|
5626
|
-
return node.value;
|
|
5627
|
-
}
|
|
5628
|
-
if (node.type === import_utils33.AST_NODE_TYPES.TemplateLiteral && node.expressions.length === 0) {
|
|
5629
|
-
return node.quasis[0]?.value.raw;
|
|
5630
|
-
}
|
|
5631
|
-
return void 0;
|
|
5632
|
-
}
|
|
5633
5676
|
function runtimeConcatOperands(node) {
|
|
5634
5677
|
if (node.type !== import_utils33.AST_NODE_TYPES.BinaryExpression || node.operator !== "+") {
|
|
5635
5678
|
return [];
|
|
@@ -5641,7 +5684,7 @@ function runtimeConcatOperands(node) {
|
|
|
5641
5684
|
if (!hasStringLiteral) {
|
|
5642
5685
|
return [];
|
|
5643
5686
|
}
|
|
5644
|
-
const parts = operands.map((operand) =>
|
|
5687
|
+
const parts = operands.map((operand) => staticString(operand) ?? RUNTIME_MARKER);
|
|
5645
5688
|
const statement = parts.join("");
|
|
5646
5689
|
const ranges = sqlSingleQuotedRanges(statement);
|
|
5647
5690
|
const visible = stripSqlNoise(statement);
|
|
@@ -5651,9 +5694,7 @@ function runtimeConcatOperands(node) {
|
|
|
5651
5694
|
const unquoted = visible.slice(offset, offset + RUNTIME_MARKER.length) === RUNTIME_MARKER;
|
|
5652
5695
|
offset += parts[index]?.length ?? 0;
|
|
5653
5696
|
if (isStaticFragment(operand)) return [];
|
|
5654
|
-
|
|
5655
|
-
const after = operands[index + 1];
|
|
5656
|
-
if (inValue && before !== void 0 && after !== void 0 && endsWithSqlQuote(staticLiteralText(before) ?? "") && startsWithSqlQuote(staticLiteralText(after) ?? "")) {
|
|
5697
|
+
if (inValue) {
|
|
5657
5698
|
return [{ expression: operand, messageId: "dynamicSql" }];
|
|
5658
5699
|
}
|
|
5659
5700
|
return unquoted ? [{ expression: operand, messageId: "dynamicFragment" }] : [];
|
|
@@ -8721,7 +8762,7 @@ var LOGGING_OPTION_PROPERTIES = {
|
|
|
8721
8762
|
loggerNames: { type: "array", items: { type: "string" } },
|
|
8722
8763
|
logFunctions: { type: "array", items: { type: "string" } }
|
|
8723
8764
|
};
|
|
8724
|
-
function
|
|
8765
|
+
function calleeName(callee) {
|
|
8725
8766
|
callee = unwrapExpression(callee);
|
|
8726
8767
|
if (callee.type === "Identifier") {
|
|
8727
8768
|
return callee.name;
|
|
@@ -8769,7 +8810,7 @@ function createLogMatcher(options = {}) {
|
|
|
8769
8810
|
if (expr.type !== "CallExpression" || logFunctions.size === 0) {
|
|
8770
8811
|
return false;
|
|
8771
8812
|
}
|
|
8772
|
-
const name =
|
|
8813
|
+
const name = calleeName(expr.callee);
|
|
8773
8814
|
return name !== null && logFunctions.has(name);
|
|
8774
8815
|
}
|
|
8775
8816
|
function isLoggingCall(expr) {
|
|
@@ -11555,7 +11596,7 @@ function isRawBlobName(name) {
|
|
|
11555
11596
|
const last = tokens.at(-1);
|
|
11556
11597
|
return last !== void 0 && RAW_BLOB_WORDS.has(last);
|
|
11557
11598
|
}
|
|
11558
|
-
function
|
|
11599
|
+
function propertyKeyName(prop) {
|
|
11559
11600
|
if (prop.computed) {
|
|
11560
11601
|
return null;
|
|
11561
11602
|
}
|
|
@@ -11626,12 +11667,12 @@ var no_secret_in_log_default = createRule({
|
|
|
11626
11667
|
continue;
|
|
11627
11668
|
}
|
|
11628
11669
|
for (const property of literalProperties(entry.argument)) {
|
|
11629
|
-
const key2 =
|
|
11670
|
+
const key2 = propertyKeyName(property);
|
|
11630
11671
|
if (key2 !== null) effective.set(key2, property);
|
|
11631
11672
|
}
|
|
11632
11673
|
continue;
|
|
11633
11674
|
}
|
|
11634
|
-
const key =
|
|
11675
|
+
const key = propertyKeyName(entry);
|
|
11635
11676
|
if (key === null) {
|
|
11636
11677
|
effective.clear();
|
|
11637
11678
|
continue;
|
|
@@ -11652,7 +11693,7 @@ var no_secret_in_log_default = createRule({
|
|
|
11652
11693
|
return false;
|
|
11653
11694
|
}
|
|
11654
11695
|
function reportSecretProperty(prop) {
|
|
11655
|
-
const keyName =
|
|
11696
|
+
const keyName = propertyKeyName(prop);
|
|
11656
11697
|
const value = valueName(prop.value);
|
|
11657
11698
|
if (value !== null && hasRedactionMarker(value)) return false;
|
|
11658
11699
|
const name = value !== null && isSecretKeyword(value) ? value : keyName;
|
|
@@ -12220,7 +12261,7 @@ var no_sentinel_return_on_catch_default = createRule({
|
|
|
12220
12261
|
if (matcher.isLoggingCall(current)) {
|
|
12221
12262
|
return caughtName === null || argsIncludeBinding(current.arguments, caughtName);
|
|
12222
12263
|
}
|
|
12223
|
-
const name =
|
|
12264
|
+
const name = calleeName(current.callee);
|
|
12224
12265
|
return name !== null && REPORT_NAME_RE.test(name) && (caughtName === null || argsIncludeBinding(current.arguments, caughtName));
|
|
12225
12266
|
});
|
|
12226
12267
|
}
|
|
@@ -25312,6 +25353,7 @@ var LIBRARY_IMPORT_POLICY = ["error", {
|
|
|
25312
25353
|
var ADVISORY_RULES = [
|
|
25313
25354
|
"@sarj/excessive-commentary",
|
|
25314
25355
|
"@sarj/no-bespoke-api-case-conversion",
|
|
25356
|
+
"@sarj/no-cors-wildcard-with-credentials",
|
|
25315
25357
|
"@sarj/no-exception-text-in-5xx-response",
|
|
25316
25358
|
"@sarj/no-restated-comment",
|
|
25317
25359
|
"@sarj/no-restated-jsdoc",
|
|
@@ -25349,7 +25391,7 @@ var RECOMMENDED_RULES = {
|
|
|
25349
25391
|
"@sarj/enforce-file-structure": "error",
|
|
25350
25392
|
"@sarj/no-client-side-data-fetching": "error",
|
|
25351
25393
|
"@sarj/no-comment-cruft": "error",
|
|
25352
|
-
"@sarj/no-cors-wildcard-with-credentials": "
|
|
25394
|
+
"@sarj/no-cors-wildcard-with-credentials": "warn",
|
|
25353
25395
|
"@sarj/no-dynamic-sql": "error",
|
|
25354
25396
|
"@sarj/no-fat-try-blocks": ["error", { max: 5 }],
|
|
25355
25397
|
"@sarj/no-hand-rolled-sleep": "error",
|
|
@@ -25468,7 +25510,7 @@ var STRICT_RULES = {
|
|
|
25468
25510
|
"@sarj/enforce-file-structure": "error",
|
|
25469
25511
|
"@sarj/no-client-side-data-fetching": "error",
|
|
25470
25512
|
"@sarj/no-comment-cruft": "error",
|
|
25471
|
-
"@sarj/no-cors-wildcard-with-credentials": "
|
|
25513
|
+
"@sarj/no-cors-wildcard-with-credentials": "warn",
|
|
25472
25514
|
"@sarj/no-dynamic-sql": "error",
|
|
25473
25515
|
"@sarj/no-enum": "error",
|
|
25474
25516
|
"@sarj/no-fat-try-blocks": ["error", { max: 5 }],
|