@sarj/eslint-plugin 15.26.1 → 15.26.2
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 +49 -16
- package/dist/index.d.cts +3 -8
- package/dist/index.d.ts +3 -8
- package/dist/index.js +49 -16
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4149,12 +4149,12 @@ function createSqlListener(handler) {
|
|
|
4149
4149
|
|
|
4150
4150
|
// src/rules/no-dynamic-sql.ts
|
|
4151
4151
|
var NO_DYNAMIC_SQL_DOCUMENTATION = {
|
|
4152
|
-
summary: "Disallow runtime values
|
|
4153
|
-
rationale: "Embedding runtime values in SQL bypasses driver parameterization and can introduce injection defects or unstable query plans.",
|
|
4154
|
-
remediation: "
|
|
4152
|
+
summary: "Disallow runtime values and fragments interpolated into SQL passed to statement-execution methods.",
|
|
4153
|
+
rationale: "Embedding runtime values or fragments in SQL bypasses driver parameterization or an explicit allowlist and can introduce injection defects or unstable query plans.",
|
|
4154
|
+
remediation: "Bind data values through SQL placeholders. Select non-bindable identifiers or clauses from fixed, reviewed fragments instead of interpolating runtime text.",
|
|
4155
4155
|
category: "security",
|
|
4156
4156
|
limitations: [
|
|
4157
|
-
"
|
|
4157
|
+
"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.",
|
|
4158
4158
|
"Literal fragments, legacy uppercase fragment names, and parameterizing tagged templates are exempt; uppercase spelling does not prove a value is static.",
|
|
4159
4159
|
"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."
|
|
4160
4160
|
],
|
|
@@ -4176,6 +4176,26 @@ var NO_DYNAMIC_SQL_DOCUMENTATION = {
|
|
|
4176
4176
|
focusPath: "src/users.ts",
|
|
4177
4177
|
expectedCount: 1,
|
|
4178
4178
|
public: true
|
|
4179
|
+
},
|
|
4180
|
+
{
|
|
4181
|
+
id: "bound-unquoted-value",
|
|
4182
|
+
scenarioId: "unquoted",
|
|
4183
|
+
title: "An unquoted runtime value is bound separately",
|
|
4184
|
+
outcome: "no-match",
|
|
4185
|
+
files: [{ path: "src/users.ts", source: "db.prepare('select id from users where id = ?').bind(userId);" }],
|
|
4186
|
+
focusPath: "src/users.ts",
|
|
4187
|
+
expectedCount: 0,
|
|
4188
|
+
public: true
|
|
4189
|
+
},
|
|
4190
|
+
{
|
|
4191
|
+
id: "unquoted-runtime-fragment",
|
|
4192
|
+
scenarioId: "unquoted",
|
|
4193
|
+
title: "A runtime fragment is interpolated into SQL",
|
|
4194
|
+
outcome: "match",
|
|
4195
|
+
files: [{ path: "src/users.ts", source: "db.prepare(`select id from users where id = ${userId}`);" }],
|
|
4196
|
+
focusPath: "src/users.ts",
|
|
4197
|
+
expectedCount: 1,
|
|
4198
|
+
public: true
|
|
4179
4199
|
}
|
|
4180
4200
|
]
|
|
4181
4201
|
};
|
|
@@ -4198,14 +4218,21 @@ function isStaticFragment(expression) {
|
|
|
4198
4218
|
}
|
|
4199
4219
|
function runtimeInterpolations(template) {
|
|
4200
4220
|
const parts = template.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw);
|
|
4201
|
-
const
|
|
4221
|
+
const statement = parts.join(RUNTIME_MARKER);
|
|
4222
|
+
const ranges = sqlSingleQuotedRanges(statement);
|
|
4223
|
+
const visible = stripSqlNoise(statement);
|
|
4202
4224
|
let offset = 0;
|
|
4203
|
-
return template.expressions.
|
|
4225
|
+
return template.expressions.flatMap(
|
|
4204
4226
|
(expression, index) => {
|
|
4205
4227
|
offset += parts[index]?.length ?? 0;
|
|
4206
4228
|
const inValue = ranges.some(([start, end]) => start < offset && offset < end);
|
|
4229
|
+
const unquoted = visible.slice(offset, offset + RUNTIME_MARKER.length) === RUNTIME_MARKER;
|
|
4207
4230
|
offset += RUNTIME_MARKER.length;
|
|
4208
|
-
|
|
4231
|
+
if (isStaticFragment(expression)) return [];
|
|
4232
|
+
if (inValue && endsWithSqlQuote(parts[index] ?? "") && startsWithSqlQuote(parts[index + 1] ?? "")) {
|
|
4233
|
+
return [{ expression, messageId: "dynamicSql" }];
|
|
4234
|
+
}
|
|
4235
|
+
return unquoted ? [{ expression, messageId: "dynamicFragment" }] : [];
|
|
4209
4236
|
}
|
|
4210
4237
|
);
|
|
4211
4238
|
}
|
|
@@ -4236,16 +4263,21 @@ function runtimeConcatOperands(node) {
|
|
|
4236
4263
|
return [];
|
|
4237
4264
|
}
|
|
4238
4265
|
const parts = operands.map((operand) => staticLiteralText(operand) ?? RUNTIME_MARKER);
|
|
4239
|
-
const
|
|
4266
|
+
const statement = parts.join("");
|
|
4267
|
+
const ranges = sqlSingleQuotedRanges(statement);
|
|
4268
|
+
const visible = stripSqlNoise(statement);
|
|
4240
4269
|
let offset = 0;
|
|
4241
|
-
return operands.
|
|
4270
|
+
return operands.flatMap((operand, index) => {
|
|
4242
4271
|
const inValue = ranges.some(([start, end]) => start < offset && offset < end);
|
|
4272
|
+
const unquoted = visible.slice(offset, offset + RUNTIME_MARKER.length) === RUNTIME_MARKER;
|
|
4243
4273
|
offset += parts[index]?.length ?? 0;
|
|
4244
|
-
if (
|
|
4245
|
-
if (isStaticFragment(operand)) return false;
|
|
4274
|
+
if (isStaticFragment(operand)) return [];
|
|
4246
4275
|
const before = operands[index - 1];
|
|
4247
4276
|
const after = operands[index + 1];
|
|
4248
|
-
|
|
4277
|
+
if (inValue && before !== void 0 && after !== void 0 && endsWithSqlQuote(staticLiteralText(before) ?? "") && startsWithSqlQuote(staticLiteralText(after) ?? "")) {
|
|
4278
|
+
return [{ expression: operand, messageId: "dynamicSql" }];
|
|
4279
|
+
}
|
|
4280
|
+
return unquoted ? [{ expression: operand, messageId: "dynamicFragment" }] : [];
|
|
4249
4281
|
});
|
|
4250
4282
|
}
|
|
4251
4283
|
function concatOperands(node) {
|
|
@@ -4301,7 +4333,8 @@ var no_dynamic_sql_default = createRule({
|
|
|
4301
4333
|
}
|
|
4302
4334
|
],
|
|
4303
4335
|
messages: {
|
|
4304
|
-
dynamicSql: "Runtime value embedded inside a quoted SQL value passed to `{{method}}()`. Replace the quoted interpolation with a placeholder and bind the value separately."
|
|
4336
|
+
dynamicSql: "Runtime value embedded inside a quoted SQL value passed to `{{method}}()`. Replace the quoted interpolation with a placeholder and bind the value separately.",
|
|
4337
|
+
dynamicFragment: "Runtime fragment interpolated into SQL passed to `{{method}}()`. Bind data values; select non-bindable SQL fragments from fixed, reviewed alternatives."
|
|
4305
4338
|
}
|
|
4306
4339
|
},
|
|
4307
4340
|
defaultOptions: [{}],
|
|
@@ -4320,8 +4353,8 @@ var no_dynamic_sql_default = createRule({
|
|
|
4320
4353
|
const offenders = statement.type === import_utils27.AST_NODE_TYPES.TemplateLiteral ? runtimeInterpolations(statement) : runtimeConcatOperands(statement);
|
|
4321
4354
|
for (const offender of offenders) {
|
|
4322
4355
|
context.report({
|
|
4323
|
-
node: offender,
|
|
4324
|
-
messageId:
|
|
4356
|
+
node: offender.expression,
|
|
4357
|
+
messageId: offender.messageId,
|
|
4325
4358
|
data: { method }
|
|
4326
4359
|
});
|
|
4327
4360
|
}
|
|
@@ -23252,7 +23285,7 @@ var RULES = {
|
|
|
23252
23285
|
};
|
|
23253
23286
|
var meta = {
|
|
23254
23287
|
name: "@sarj/eslint-plugin",
|
|
23255
|
-
version: "15.26.
|
|
23288
|
+
version: "15.26.2"
|
|
23256
23289
|
};
|
|
23257
23290
|
var APPLICATION_ONLY_RULES = [];
|
|
23258
23291
|
var LIBRARY_IMPORT_POLICY = ["error", {
|
package/dist/index.d.cts
CHANGED
|
@@ -175,11 +175,6 @@ interface RuleOptions$1 {
|
|
|
175
175
|
readonly max?: number;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
/**
|
|
179
|
-
* @fileoverview no-dynamic-sql — bind runtime values separately from SQL string literals.
|
|
180
|
-
*
|
|
181
|
-
* Examples: https://github.com/sarj-ai/code-standards/blob/main/packages/typescript/tests/rules/no-dynamic-sql.test.ts
|
|
182
|
-
*/
|
|
183
178
|
interface RuleOptions {
|
|
184
179
|
readonly methods?: readonly string[];
|
|
185
180
|
}
|
|
@@ -247,7 +242,7 @@ declare const RULES: {
|
|
|
247
242
|
readonly "no-cors-wildcard-with-credentials": DocumentedRule<readonly [], "corsWildcardWithCredentials">;
|
|
248
243
|
readonly "no-duplicate-lifecycle-refresh-listeners": DocumentedRule<readonly [], "duplicateLifecycleRefresh">;
|
|
249
244
|
readonly "no-dangerously-allow-svg": DocumentedRule<readonly [], "noDangerouslyAllowSvg">;
|
|
250
|
-
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql">;
|
|
245
|
+
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql" | "dynamicFragment">;
|
|
251
246
|
readonly "no-enum": DocumentedRule<readonly [{
|
|
252
247
|
ignoreFiles?: readonly string[];
|
|
253
248
|
}?], "noEnum">;
|
|
@@ -1001,7 +996,7 @@ type FlatPreset = {
|
|
|
1001
996
|
declare const PLUGIN: {
|
|
1002
997
|
readonly meta: {
|
|
1003
998
|
readonly name: "@sarj/eslint-plugin";
|
|
1004
|
-
readonly version: "15.26.
|
|
999
|
+
readonly version: "15.26.2";
|
|
1005
1000
|
};
|
|
1006
1001
|
readonly rules: {
|
|
1007
1002
|
readonly "no-conditional-empty-object-spread": DocumentedRule<[], "avoid">;
|
|
@@ -1037,7 +1032,7 @@ declare const PLUGIN: {
|
|
|
1037
1032
|
readonly "no-cors-wildcard-with-credentials": DocumentedRule<readonly [], "corsWildcardWithCredentials">;
|
|
1038
1033
|
readonly "no-duplicate-lifecycle-refresh-listeners": DocumentedRule<readonly [], "duplicateLifecycleRefresh">;
|
|
1039
1034
|
readonly "no-dangerously-allow-svg": DocumentedRule<readonly [], "noDangerouslyAllowSvg">;
|
|
1040
|
-
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql">;
|
|
1035
|
+
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql" | "dynamicFragment">;
|
|
1041
1036
|
readonly "no-enum": DocumentedRule<readonly [{
|
|
1042
1037
|
ignoreFiles?: readonly string[];
|
|
1043
1038
|
}?], "noEnum">;
|
package/dist/index.d.ts
CHANGED
|
@@ -175,11 +175,6 @@ interface RuleOptions$1 {
|
|
|
175
175
|
readonly max?: number;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
/**
|
|
179
|
-
* @fileoverview no-dynamic-sql — bind runtime values separately from SQL string literals.
|
|
180
|
-
*
|
|
181
|
-
* Examples: https://github.com/sarj-ai/code-standards/blob/main/packages/typescript/tests/rules/no-dynamic-sql.test.ts
|
|
182
|
-
*/
|
|
183
178
|
interface RuleOptions {
|
|
184
179
|
readonly methods?: readonly string[];
|
|
185
180
|
}
|
|
@@ -247,7 +242,7 @@ declare const RULES: {
|
|
|
247
242
|
readonly "no-cors-wildcard-with-credentials": DocumentedRule<readonly [], "corsWildcardWithCredentials">;
|
|
248
243
|
readonly "no-duplicate-lifecycle-refresh-listeners": DocumentedRule<readonly [], "duplicateLifecycleRefresh">;
|
|
249
244
|
readonly "no-dangerously-allow-svg": DocumentedRule<readonly [], "noDangerouslyAllowSvg">;
|
|
250
|
-
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql">;
|
|
245
|
+
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql" | "dynamicFragment">;
|
|
251
246
|
readonly "no-enum": DocumentedRule<readonly [{
|
|
252
247
|
ignoreFiles?: readonly string[];
|
|
253
248
|
}?], "noEnum">;
|
|
@@ -1001,7 +996,7 @@ type FlatPreset = {
|
|
|
1001
996
|
declare const PLUGIN: {
|
|
1002
997
|
readonly meta: {
|
|
1003
998
|
readonly name: "@sarj/eslint-plugin";
|
|
1004
|
-
readonly version: "15.26.
|
|
999
|
+
readonly version: "15.26.2";
|
|
1005
1000
|
};
|
|
1006
1001
|
readonly rules: {
|
|
1007
1002
|
readonly "no-conditional-empty-object-spread": DocumentedRule<[], "avoid">;
|
|
@@ -1037,7 +1032,7 @@ declare const PLUGIN: {
|
|
|
1037
1032
|
readonly "no-cors-wildcard-with-credentials": DocumentedRule<readonly [], "corsWildcardWithCredentials">;
|
|
1038
1033
|
readonly "no-duplicate-lifecycle-refresh-listeners": DocumentedRule<readonly [], "duplicateLifecycleRefresh">;
|
|
1039
1034
|
readonly "no-dangerously-allow-svg": DocumentedRule<readonly [], "noDangerouslyAllowSvg">;
|
|
1040
|
-
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql">;
|
|
1035
|
+
readonly "no-dynamic-sql": DocumentedRule<readonly [RuleOptions?], "dynamicSql" | "dynamicFragment">;
|
|
1041
1036
|
readonly "no-enum": DocumentedRule<readonly [{
|
|
1042
1037
|
ignoreFiles?: readonly string[];
|
|
1043
1038
|
}?], "noEnum">;
|
package/dist/index.js
CHANGED
|
@@ -4113,12 +4113,12 @@ function createSqlListener(handler) {
|
|
|
4113
4113
|
|
|
4114
4114
|
// src/rules/no-dynamic-sql.ts
|
|
4115
4115
|
var NO_DYNAMIC_SQL_DOCUMENTATION = {
|
|
4116
|
-
summary: "Disallow runtime values
|
|
4117
|
-
rationale: "Embedding runtime values in SQL bypasses driver parameterization and can introduce injection defects or unstable query plans.",
|
|
4118
|
-
remediation: "
|
|
4116
|
+
summary: "Disallow runtime values and fragments interpolated into SQL passed to statement-execution methods.",
|
|
4117
|
+
rationale: "Embedding runtime values or fragments in SQL bypasses driver parameterization or an explicit allowlist and can introduce injection defects or unstable query plans.",
|
|
4118
|
+
remediation: "Bind data values through SQL placeholders. Select non-bindable identifiers or clauses from fixed, reviewed fragments instead of interpolating runtime text.",
|
|
4119
4119
|
category: "security",
|
|
4120
4120
|
limitations: [
|
|
4121
|
-
"
|
|
4121
|
+
"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.",
|
|
4122
4122
|
"Literal fragments, legacy uppercase fragment names, and parameterizing tagged templates are exempt; uppercase spelling does not prove a value is static.",
|
|
4123
4123
|
"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."
|
|
4124
4124
|
],
|
|
@@ -4140,6 +4140,26 @@ var NO_DYNAMIC_SQL_DOCUMENTATION = {
|
|
|
4140
4140
|
focusPath: "src/users.ts",
|
|
4141
4141
|
expectedCount: 1,
|
|
4142
4142
|
public: true
|
|
4143
|
+
},
|
|
4144
|
+
{
|
|
4145
|
+
id: "bound-unquoted-value",
|
|
4146
|
+
scenarioId: "unquoted",
|
|
4147
|
+
title: "An unquoted runtime value is bound separately",
|
|
4148
|
+
outcome: "no-match",
|
|
4149
|
+
files: [{ path: "src/users.ts", source: "db.prepare('select id from users where id = ?').bind(userId);" }],
|
|
4150
|
+
focusPath: "src/users.ts",
|
|
4151
|
+
expectedCount: 0,
|
|
4152
|
+
public: true
|
|
4153
|
+
},
|
|
4154
|
+
{
|
|
4155
|
+
id: "unquoted-runtime-fragment",
|
|
4156
|
+
scenarioId: "unquoted",
|
|
4157
|
+
title: "A runtime fragment is interpolated into SQL",
|
|
4158
|
+
outcome: "match",
|
|
4159
|
+
files: [{ path: "src/users.ts", source: "db.prepare(`select id from users where id = ${userId}`);" }],
|
|
4160
|
+
focusPath: "src/users.ts",
|
|
4161
|
+
expectedCount: 1,
|
|
4162
|
+
public: true
|
|
4143
4163
|
}
|
|
4144
4164
|
]
|
|
4145
4165
|
};
|
|
@@ -4162,14 +4182,21 @@ function isStaticFragment(expression) {
|
|
|
4162
4182
|
}
|
|
4163
4183
|
function runtimeInterpolations(template) {
|
|
4164
4184
|
const parts = template.quasis.map((quasi) => quasi.value.cooked ?? quasi.value.raw);
|
|
4165
|
-
const
|
|
4185
|
+
const statement = parts.join(RUNTIME_MARKER);
|
|
4186
|
+
const ranges = sqlSingleQuotedRanges(statement);
|
|
4187
|
+
const visible = stripSqlNoise(statement);
|
|
4166
4188
|
let offset = 0;
|
|
4167
|
-
return template.expressions.
|
|
4189
|
+
return template.expressions.flatMap(
|
|
4168
4190
|
(expression, index) => {
|
|
4169
4191
|
offset += parts[index]?.length ?? 0;
|
|
4170
4192
|
const inValue = ranges.some(([start, end]) => start < offset && offset < end);
|
|
4193
|
+
const unquoted = visible.slice(offset, offset + RUNTIME_MARKER.length) === RUNTIME_MARKER;
|
|
4171
4194
|
offset += RUNTIME_MARKER.length;
|
|
4172
|
-
|
|
4195
|
+
if (isStaticFragment(expression)) return [];
|
|
4196
|
+
if (inValue && endsWithSqlQuote(parts[index] ?? "") && startsWithSqlQuote(parts[index + 1] ?? "")) {
|
|
4197
|
+
return [{ expression, messageId: "dynamicSql" }];
|
|
4198
|
+
}
|
|
4199
|
+
return unquoted ? [{ expression, messageId: "dynamicFragment" }] : [];
|
|
4173
4200
|
}
|
|
4174
4201
|
);
|
|
4175
4202
|
}
|
|
@@ -4200,16 +4227,21 @@ function runtimeConcatOperands(node) {
|
|
|
4200
4227
|
return [];
|
|
4201
4228
|
}
|
|
4202
4229
|
const parts = operands.map((operand) => staticLiteralText(operand) ?? RUNTIME_MARKER);
|
|
4203
|
-
const
|
|
4230
|
+
const statement = parts.join("");
|
|
4231
|
+
const ranges = sqlSingleQuotedRanges(statement);
|
|
4232
|
+
const visible = stripSqlNoise(statement);
|
|
4204
4233
|
let offset = 0;
|
|
4205
|
-
return operands.
|
|
4234
|
+
return operands.flatMap((operand, index) => {
|
|
4206
4235
|
const inValue = ranges.some(([start, end]) => start < offset && offset < end);
|
|
4236
|
+
const unquoted = visible.slice(offset, offset + RUNTIME_MARKER.length) === RUNTIME_MARKER;
|
|
4207
4237
|
offset += parts[index]?.length ?? 0;
|
|
4208
|
-
if (
|
|
4209
|
-
if (isStaticFragment(operand)) return false;
|
|
4238
|
+
if (isStaticFragment(operand)) return [];
|
|
4210
4239
|
const before = operands[index - 1];
|
|
4211
4240
|
const after = operands[index + 1];
|
|
4212
|
-
|
|
4241
|
+
if (inValue && before !== void 0 && after !== void 0 && endsWithSqlQuote(staticLiteralText(before) ?? "") && startsWithSqlQuote(staticLiteralText(after) ?? "")) {
|
|
4242
|
+
return [{ expression: operand, messageId: "dynamicSql" }];
|
|
4243
|
+
}
|
|
4244
|
+
return unquoted ? [{ expression: operand, messageId: "dynamicFragment" }] : [];
|
|
4213
4245
|
});
|
|
4214
4246
|
}
|
|
4215
4247
|
function concatOperands(node) {
|
|
@@ -4265,7 +4297,8 @@ var no_dynamic_sql_default = createRule({
|
|
|
4265
4297
|
}
|
|
4266
4298
|
],
|
|
4267
4299
|
messages: {
|
|
4268
|
-
dynamicSql: "Runtime value embedded inside a quoted SQL value passed to `{{method}}()`. Replace the quoted interpolation with a placeholder and bind the value separately."
|
|
4300
|
+
dynamicSql: "Runtime value embedded inside a quoted SQL value passed to `{{method}}()`. Replace the quoted interpolation with a placeholder and bind the value separately.",
|
|
4301
|
+
dynamicFragment: "Runtime fragment interpolated into SQL passed to `{{method}}()`. Bind data values; select non-bindable SQL fragments from fixed, reviewed alternatives."
|
|
4269
4302
|
}
|
|
4270
4303
|
},
|
|
4271
4304
|
defaultOptions: [{}],
|
|
@@ -4284,8 +4317,8 @@ var no_dynamic_sql_default = createRule({
|
|
|
4284
4317
|
const offenders = statement.type === AST_NODE_TYPES25.TemplateLiteral ? runtimeInterpolations(statement) : runtimeConcatOperands(statement);
|
|
4285
4318
|
for (const offender of offenders) {
|
|
4286
4319
|
context.report({
|
|
4287
|
-
node: offender,
|
|
4288
|
-
messageId:
|
|
4320
|
+
node: offender.expression,
|
|
4321
|
+
messageId: offender.messageId,
|
|
4289
4322
|
data: { method }
|
|
4290
4323
|
});
|
|
4291
4324
|
}
|
|
@@ -23273,7 +23306,7 @@ var RULES = {
|
|
|
23273
23306
|
};
|
|
23274
23307
|
var meta = {
|
|
23275
23308
|
name: "@sarj/eslint-plugin",
|
|
23276
|
-
version: "15.26.
|
|
23309
|
+
version: "15.26.2"
|
|
23277
23310
|
};
|
|
23278
23311
|
var APPLICATION_ONLY_RULES = [];
|
|
23279
23312
|
var LIBRARY_IMPORT_POLICY = ["error", {
|