@noctcore/lint-meta-rules 0.4.2 → 0.6.0

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 CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  recursiveGlobs,
11
11
  stripYamlComment,
12
12
  unquote
13
- } from "./chunk-Z7TXSZR4.js";
13
+ } from "./chunk-OYFQKSJN.js";
14
14
 
15
15
  // src/rules/agents-doc-presence.ts
16
16
  function createAgentsDocPresenceRule(options = {}) {
@@ -244,8 +244,293 @@ function createFileSizeRatchetRule(options = {}) {
244
244
  };
245
245
  }
246
246
 
247
+ // src/rules/github-actions-least-privilege-permissions.ts
248
+ var RULE_ID2 = "github-actions-least-privilege-permissions";
249
+ var TOP_KEY = /^([\w-]+):\s*(.*)$/u;
250
+ var SCOPE_ENTRY = /^\s*['"]?([\w-]+)['"]?:\s*['"]?([\w-]+)['"]?\s*$/u;
251
+ function indentOf(line) {
252
+ return /^\s*/u.exec(line)?.[0].length ?? 0;
253
+ }
254
+ function isContent(line) {
255
+ const trimmed = line.trim();
256
+ return trimmed !== "" && !trimmed.startsWith("#");
257
+ }
258
+ function topLevelKeys(lines) {
259
+ const keys = [];
260
+ lines.forEach((line, index) => {
261
+ const match = TOP_KEY.exec(line);
262
+ if (match === null) return;
263
+ keys.push({ key: unquote(match[1] ?? ""), value: stripYamlComment(match[2] ?? "").trim(), index });
264
+ });
265
+ return keys;
266
+ }
267
+ function blockOf(lines, index) {
268
+ const block = [];
269
+ for (let j = index + 1; j < lines.length; j += 1) {
270
+ const line = lines[j] ?? "";
271
+ if (!isContent(line)) continue;
272
+ if (indentOf(line) === 0) break;
273
+ block.push({ text: stripYamlComment(line), index: j });
274
+ }
275
+ return block;
276
+ }
277
+ function scopeEntries(lines, permissions) {
278
+ if (permissions.value.startsWith("{")) {
279
+ return permissions.value.replace(/^\{|\}$/gu, "").split(",").map((pair) => SCOPE_ENTRY.exec(pair)).filter((match) => match !== null).map((match) => ({ scope: match[1] ?? "", level: match[2] ?? "", line: permissions.index + 1 }));
280
+ }
281
+ return blockOf(lines, permissions.index).flatMap(({ text, index }) => {
282
+ const match = SCOPE_ENTRY.exec(text);
283
+ return match === null ? [] : [{ scope: match[1] ?? "", level: match[2] ?? "", line: index + 1 }];
284
+ });
285
+ }
286
+ function jobsWithoutPermissions(lines, jobs) {
287
+ const block = blockOf(lines, jobs.index);
288
+ const jobColumn = indentOf(block[0]?.text ?? "");
289
+ const missing = [];
290
+ let current;
291
+ let keyColumn = -1;
292
+ const settle = () => {
293
+ if (current !== void 0 && !current.hasPermissions) missing.push(current.name);
294
+ };
295
+ for (const { text } of block) {
296
+ const column = indentOf(text);
297
+ if (column === jobColumn) {
298
+ settle();
299
+ current = { name: unquote(text.trim().replace(/:.*$/u, "")), hasPermissions: false };
300
+ keyColumn = -1;
301
+ continue;
302
+ }
303
+ if (current === void 0 || column < jobColumn) continue;
304
+ if (keyColumn === -1) keyColumn = column;
305
+ if (column === keyColumn && /^\s*permissions:/u.test(text)) current.hasPermissions = true;
306
+ }
307
+ settle();
308
+ return missing;
309
+ }
310
+ function checkWorkflowPermissions(file, text, allowTopLevelWrite = []) {
311
+ const lines = text.split("\n");
312
+ const keys = topLevelKeys(lines);
313
+ const permissions = keys.find((entry) => entry.key === "permissions");
314
+ const report = (line, message) => ({
315
+ file,
316
+ rule: RULE_ID2,
317
+ message: `line ${line}: ${message}`,
318
+ line
319
+ });
320
+ if (permissions === void 0) {
321
+ const jobs = keys.find((entry) => entry.key === "jobs");
322
+ const missing = jobs === void 0 ? [] : jobsWithoutPermissions(lines, jobs);
323
+ if (missing.length === 0) return [];
324
+ return [
325
+ report(
326
+ 1,
327
+ `no top-level \`permissions:\`, so ${missing.map((job) => `\`${job}\``).join(", ")} ${missing.length === 1 ? "runs" : "run"} with the repository's default token, which can be read-write. Add \`permissions: { contents: read }\` at the top and grant writes on the job that needs them.`
328
+ )
329
+ ];
330
+ }
331
+ const shorthand = unquote(permissions.value);
332
+ if (shorthand === "write-all" || shorthand === "read-all") {
333
+ return [
334
+ report(
335
+ permissions.index + 1,
336
+ `top-level \`permissions: ${shorthand}\` grants every scope to every job. List only the scopes the workflow reads (for example \`contents: read\`) and grant writes on the job that needs them.`
337
+ )
338
+ ];
339
+ }
340
+ const allowed = new Set(allowTopLevelWrite);
341
+ return scopeEntries(lines, permissions).filter(({ scope, level }) => level === "write" && !allowed.has(scope)).map(
342
+ ({ scope, line }) => report(
343
+ line,
344
+ `top-level \`permissions\` grants \`${scope}: write\` to every job. Move it to the job that needs it and keep the top level read-only.`
345
+ )
346
+ );
347
+ }
348
+ function createGithubActionsLeastPrivilegePermissionsRule(options = {}) {
349
+ const workflowGlobs = options.workflowGlobs ?? DEFAULT_WORKFLOW_GLOBS;
350
+ const allowTopLevelWrite = options.allowTopLevelWrite ?? [];
351
+ const ciCritical = options.ciCritical ?? true;
352
+ return {
353
+ id: RULE_ID2,
354
+ category: "ci",
355
+ ciCritical,
356
+ description: "GitHub Actions workflows declare a read-only top-level `permissions:` (no `write-all`/`read-all`, no `<scope>: write`); writes go on the job that needs them.",
357
+ run(ctx) {
358
+ return globFiles((p) => ctx.glob(p), workflowGlobs).flatMap(
359
+ (file) => checkWorkflowPermissions(file, ctx.read(file) ?? "", allowTopLevelWrite)
360
+ );
361
+ }
362
+ };
363
+ }
364
+
365
+ // src/rules/github-actions-no-template-injection.ts
366
+ var RULE_ID3 = "github-actions-no-template-injection";
367
+ var DEFAULT_ACTION_GLOBS = anywhereGlobs(["action.yml", "action.yaml"]);
368
+ var EVENT = String.raw`(?<![\w.])github\.event\.`;
369
+ var ANY_ITEM = String.raw`(?:\.\*|\[\d+\])`;
370
+ var TAINTED_CONTEXTS = [
371
+ new RegExp(String.raw`${EVENT}(?:issue|pull_request|discussion)\.(?:title|body)\b`, "u"),
372
+ new RegExp(String.raw`${EVENT}pull_request\.head\.(?:ref|label)\b`, "u"),
373
+ new RegExp(String.raw`${EVENT}(?:comment|review|review_comment)\.body\b`, "u"),
374
+ new RegExp(String.raw`${EVENT}pages${ANY_ITEM}\.page_name\b`, "u"),
375
+ new RegExp(String.raw`${EVENT}commits${ANY_ITEM}\.(?:message|author|committer)\b`, "u"),
376
+ new RegExp(String.raw`${EVENT}head_commit\.(?:message|author|committer)\b`, "u"),
377
+ new RegExp(String.raw`${EVENT}workflow_run\.head_branch\b`, "u"),
378
+ new RegExp(String.raw`${EVENT}workflow_run\.head_commit\.(?:message|author|committer)\b`, "u"),
379
+ /(?<![\w.])github\.head_ref\b/u
380
+ ];
381
+ var INPUT_CONTEXT = /(?<![\w.])(?:github\.event\.)?inputs\.([\w-]+)/gu;
382
+ var STEP_OUTPUT_CONTEXT = /(?<![\w.])steps\.[\w-]+\.outputs\.[\w-]+/u;
383
+ var SAFE_INPUT_TYPES = /* @__PURE__ */ new Set(["boolean", "number", "choice"]);
384
+ var EXPRESSION = /\$\{\{(.*?)\}\}/gu;
385
+ var SCRIPT_KEY = /^(\s*)(-\s+)?(run|script):(?:\s+(.*))?$/u;
386
+ var BLOCK_INDICATOR = /^[|>][-+0-9]*\s*(?:#.*)?$/u;
387
+ var MAPPING_LINE = /^\s*[\w-]+:(?:\s|$)/u;
388
+ var GITHUB_SCRIPT_USES = /^\s*(?:-\s+)?uses:\s*['"]?actions\/github-script@/u;
389
+ function indentOf2(line) {
390
+ return /^\s*/u.exec(line)?.[0].length ?? 0;
391
+ }
392
+ function isContent2(line) {
393
+ const trimmed = line.trim();
394
+ return trimmed !== "" && !trimmed.startsWith("#");
395
+ }
396
+ function parentOf(lines, index, column) {
397
+ for (let j = index - 1; j >= 0; j -= 1) {
398
+ const line = lines[j] ?? "";
399
+ if (isContent2(line) && indentOf2(line) < column) return j;
400
+ }
401
+ return -1;
402
+ }
403
+ function isGithubScriptStep(lines, withIndex) {
404
+ const stepStart = parentOf(lines, withIndex, indentOf2(lines[withIndex] ?? ""));
405
+ const stepLine = lines[stepStart] ?? "";
406
+ if (!stepLine.trimStart().startsWith("-")) return false;
407
+ const dashColumn = indentOf2(stepLine);
408
+ for (let j = stepStart; j < lines.length; j += 1) {
409
+ const line = lines[j] ?? "";
410
+ if (j > stepStart && isContent2(line) && indentOf2(line) <= dashColumn) break;
411
+ if (GITHUB_SCRIPT_USES.test(line)) return true;
412
+ }
413
+ return false;
414
+ }
415
+ function safeInputNames(lines) {
416
+ const safe = /* @__PURE__ */ new Set();
417
+ lines.forEach((line, index) => {
418
+ if (!/^\s*inputs:\s*(?:#.*)?$/u.test(line)) return;
419
+ const column = indentOf2(line);
420
+ let nameColumn = -1;
421
+ let name = "";
422
+ for (const child of lines.slice(index + 1)) {
423
+ if (!isContent2(child)) continue;
424
+ const childColumn = indentOf2(child);
425
+ if (childColumn <= column) break;
426
+ if (nameColumn === -1) nameColumn = childColumn;
427
+ if (childColumn === nameColumn) {
428
+ name = /^\s*['"]?([\w-]+)['"]?:/u.exec(child)?.[1] ?? "";
429
+ continue;
430
+ }
431
+ const type = /^\s*type:\s*['"]?(\w+)/u.exec(stripYamlComment(child))?.[1];
432
+ if (type !== void 0 && SAFE_INPUT_TYPES.has(type) && name !== "") safe.add(name);
433
+ }
434
+ });
435
+ return safe;
436
+ }
437
+ function scriptLines(lines, index, keyColumn) {
438
+ const inline = SCRIPT_KEY.exec(lines[index] ?? "")?.[4] ?? "";
439
+ const block = BLOCK_INDICATOR.test(inline.trim());
440
+ const quoted = /^['"]/u.test(inline.trim());
441
+ const keep = (text) => block || quoted ? text : stripYamlComment(text);
442
+ const collected = block ? [] : [{ line: index + 1, text: keep(inline) }];
443
+ for (let j = index + 1; j < lines.length; j += 1) {
444
+ const line = lines[j] ?? "";
445
+ if (line.trim() !== "" && indentOf2(line) <= keyColumn) break;
446
+ collected.push({ line: j + 1, text: keep(line) });
447
+ }
448
+ const first = collected.find((entry) => entry.text.trim() !== "");
449
+ if (inline.trim() === "" && first !== void 0 && MAPPING_LINE.test(first.text)) return [];
450
+ return collected;
451
+ }
452
+ function taintedContexts(expression, checkInputs, checkStepOutputs, safeInputs) {
453
+ const found = [];
454
+ for (const pattern of TAINTED_CONTEXTS) {
455
+ const match = pattern.exec(expression);
456
+ if (match !== null) found.push(match[0]);
457
+ }
458
+ if (checkInputs) {
459
+ for (const match of expression.matchAll(INPUT_CONTEXT)) {
460
+ if (!safeInputs.has(match[1] ?? "")) found.push(match[0]);
461
+ }
462
+ }
463
+ if (checkStepOutputs) {
464
+ const match = STEP_OUTPUT_CONTEXT.exec(expression);
465
+ if (match !== null) found.push(match[0]);
466
+ }
467
+ return found;
468
+ }
469
+ function checkWorkflowTemplateInjection(file, text, options = {}) {
470
+ const checkInputs = options.checkInputs ?? true;
471
+ const checkStepOutputs = options.checkStepOutputs ?? false;
472
+ const lines = text.split("\n");
473
+ const safeInputs = checkInputs ? safeInputNames(lines) : /* @__PURE__ */ new Set();
474
+ const violations = [];
475
+ let skipThrough = -1;
476
+ lines.forEach((line, index) => {
477
+ const match = index > skipThrough ? SCRIPT_KEY.exec(line) : null;
478
+ if (match === null) return;
479
+ const keyColumn = (match[1]?.length ?? 0) + (match[2]?.length ?? 0);
480
+ const key = match[3] ?? "run";
481
+ if (key === "script") {
482
+ const parent = parentOf(lines, index, keyColumn);
483
+ if (!/^\s*with:\s*(?:#.*)?$/u.test(lines[parent] ?? "")) return;
484
+ if (!isGithubScriptStep(lines, parent)) return;
485
+ }
486
+ const where = key === "run" ? "`run:` script" : "`actions/github-script` `script:`";
487
+ const script = scriptLines(lines, index, keyColumn);
488
+ skipThrough = (script[script.length - 1]?.line ?? index + 1) - 1;
489
+ for (const entry of script) {
490
+ for (const expression of entry.text.matchAll(EXPRESSION)) {
491
+ const contexts = taintedContexts(
492
+ expression[1] ?? "",
493
+ checkInputs,
494
+ checkStepOutputs,
495
+ safeInputs
496
+ );
497
+ if (contexts.length === 0) continue;
498
+ violations.push({
499
+ file,
500
+ rule: RULE_ID3,
501
+ message: `line ${entry.line}: \`${expression[0]}\` expands attacker-controllable \`${contexts.join("`, `")}\` into a ${where}, where it runs as code. Pass it through \`env:\` (for example \`VALUE: ${expression[0]}\`) and read \`"$VALUE"\` instead.`,
502
+ line: entry.line
503
+ });
504
+ }
505
+ }
506
+ });
507
+ return violations;
508
+ }
509
+ function createGithubActionsNoTemplateInjectionRule(options = {}) {
510
+ const workflowGlobs = options.workflowGlobs ?? DEFAULT_WORKFLOW_GLOBS;
511
+ const actionGlobs = options.actionGlobs ?? DEFAULT_ACTION_GLOBS;
512
+ const skipDirs = options.skipDirs ?? DEFAULT_SKIP_DIRS;
513
+ const ciCritical = options.ciCritical ?? true;
514
+ return {
515
+ id: RULE_ID3,
516
+ category: "ci",
517
+ ciCritical,
518
+ description: "GitHub Actions `run:` and github-script bodies never expand attacker-controllable `${{ }}` context (issue/PR titles, comments, branch names); pass it through `env:` instead.",
519
+ run(ctx) {
520
+ const files = globFiles(
521
+ (p) => ctx.glob(p),
522
+ [...workflowGlobs, ...actionGlobs],
523
+ skipDirs
524
+ );
525
+ return files.flatMap(
526
+ (file) => checkWorkflowTemplateInjection(file, ctx.read(file) ?? "", options)
527
+ );
528
+ }
529
+ };
530
+ }
531
+
247
532
  // src/rules/github-actions-runner-pinned.ts
248
- var RULE_ID2 = "github-actions-runner-pinned";
533
+ var RULE_ID4 = "github-actions-runner-pinned";
249
534
  var RUNS_ON = /^(\s*)runs-on:\s*(.*)$/u;
250
535
  var DEFAULT_FLOATING_LABEL = /^[\w.-]+-latest$/u;
251
536
  function runnerLabels(lines, start, indent) {
@@ -277,7 +562,7 @@ function checkWorkflowRunnersPinned(file, text, floatingLabel = DEFAULT_FLOATING
277
562
  if (floatingLabel.test(label)) {
278
563
  violations.push({
279
564
  file,
280
- rule: RULE_ID2,
565
+ rule: RULE_ID4,
281
566
  message: `line ${index + 1}: \`runs-on\` uses the floating runner label "${label}". Pin a named image (for example \`ubuntu-24.04\`) so a runner image change is a reviewed diff.`,
282
567
  line: index + 1
283
568
  });
@@ -291,7 +576,7 @@ function createGithubActionsRunnerPinnedRule(options = {}) {
291
576
  const floatingLabel = options.floatingLabel ?? DEFAULT_FLOATING_LABEL;
292
577
  const ciCritical = options.ciCritical ?? true;
293
578
  return {
294
- id: RULE_ID2,
579
+ id: RULE_ID4,
295
580
  category: "ci",
296
581
  ciCritical,
297
582
  description: "GitHub Actions jobs must run on a pinned runner image (for example ubuntu-24.04), never a *-latest label.",
@@ -304,7 +589,7 @@ function createGithubActionsRunnerPinnedRule(options = {}) {
304
589
  }
305
590
 
306
591
  // src/rules/github-actions-sha-pinned.ts
307
- var RULE_ID3 = "github-actions-sha-pinned";
592
+ var RULE_ID5 = "github-actions-sha-pinned";
308
593
  var USES_LINE = /^\s*(?:-\s+)?uses:\s*['"]?([^\s'"#]+)['"]?\s*(#.*)?$/u;
309
594
  var FULL_SHA = /^[0-9a-f]{40}$/u;
310
595
  var DOCKER_DIGEST = /@sha256:[0-9a-f]{64}$/u;
@@ -319,7 +604,7 @@ function checkWorkflowActionsPinned(file, text) {
319
604
  }
320
605
  const where = `line ${index + 1}`;
321
606
  const report = (message) => {
322
- violations.push({ file, rule: RULE_ID3, message, line: index + 1 });
607
+ violations.push({ file, rule: RULE_ID5, message, line: index + 1 });
323
608
  };
324
609
  if (ref.startsWith("docker://")) {
325
610
  if (!DOCKER_DIGEST.test(ref)) {
@@ -347,7 +632,7 @@ function createGithubActionsShaPinnedRule(options = {}) {
347
632
  const workflowGlobs = options.workflowGlobs ?? DEFAULT_WORKFLOW_GLOBS;
348
633
  const ciCritical = options.ciCritical ?? true;
349
634
  return {
350
- id: RULE_ID3,
635
+ id: RULE_ID5,
351
636
  category: "ci",
352
637
  ciCritical,
353
638
  description: "GitHub Actions `uses:` refs must be pinned to a 40-character commit SHA with a `# vN` comment (local ./ actions exempt).",
@@ -572,7 +857,7 @@ function createPackageShapeRule(options = {}) {
572
857
  }
573
858
 
574
859
  // src/rules/security-scanner-version-parity.ts
575
- var RULE_ID4 = "security-scanner-version-parity";
860
+ var RULE_ID6 = "security-scanner-version-parity";
576
861
  function createSecurityScannerVersionParityRule(options = {}) {
577
862
  const scanner = options.scanner ?? "gitleaks";
578
863
  const versionVariable = options.versionVariable ?? "GITLEAKS_VERSION";
@@ -597,7 +882,7 @@ function createSecurityScannerVersionParityRule(options = {}) {
597
882
  const runtimeCheck = new RegExp(`\\b${name}\\s+version\\b`, "u");
598
883
  const mentionsScanner = new RegExp(name, "iu");
599
884
  return {
600
- id: RULE_ID4,
885
+ id: RULE_ID6,
601
886
  category: "ci",
602
887
  ciCritical,
603
888
  description: `The ${scanner} version pinned in the workflows must equal the one in ${hookFile}, and the hook must compare a native ${scanner} against it at run time.`,
@@ -616,7 +901,7 @@ function createSecurityScannerVersionParityRule(options = {}) {
616
901
  }
617
902
  const violations = [];
618
903
  const report = (file, message) => {
619
- violations.push({ file, rule: RULE_ID4, message });
904
+ violations.push({ file, rule: RULE_ID6, message });
620
905
  };
621
906
  if (ciVersions.size > 1) {
622
907
  report(
@@ -669,7 +954,7 @@ function createSecurityScannerVersionParityRule(options = {}) {
669
954
  }
670
955
 
671
956
  // src/rules/service-image-digest-pin.ts
672
- var RULE_ID5 = "service-image-digest-pin";
957
+ var RULE_ID7 = "service-image-digest-pin";
673
958
  var DIGEST2 = /@sha256:[0-9a-f]{64}$/u;
674
959
  var KEY_LINE = /^(\s*)(?:-\s+)?([\w-]+):\s*(.*)$/u;
675
960
  var DEFAULT_COMPOSE_GLOBS = anywhereGlobs(
@@ -677,7 +962,7 @@ var DEFAULT_COMPOSE_GLOBS = anywhereGlobs(
677
962
  (stem) => ["", ".*", "-*"].flatMap((infix) => ["yml", "yaml"].map((ext) => `${stem}${infix}.${ext}`))
678
963
  )
679
964
  );
680
- function indentOf(line) {
965
+ function indentOf3(line) {
681
966
  return /^\s*/u.exec(line)?.[0].length ?? 0;
682
967
  }
683
968
  function workflowImages(text) {
@@ -729,7 +1014,7 @@ function composeImages(text) {
729
1014
  if (line.trim() === "" || line.trimStart().startsWith("#")) {
730
1015
  continue;
731
1016
  }
732
- const indent = indentOf(line);
1017
+ const indent = indentOf3(line);
733
1018
  if (indent === 0) {
734
1019
  break;
735
1020
  }
@@ -763,7 +1048,7 @@ function violationsFor(file, images, allowUnpinned) {
763
1048
  const reference = image.split("@")[0];
764
1049
  return {
765
1050
  file,
766
- rule: RULE_ID5,
1051
+ rule: RULE_ID7,
767
1052
  message: `line ${line}: image "${image}" is not pinned by digest. Write \`${reference}@sha256:<digest>\` (resolve it with \`docker buildx imagetools inspect ${reference}\`) so CI and local runs use the same bytes.`,
768
1053
  line
769
1054
  };
@@ -776,7 +1061,7 @@ function createServiceImageDigestPinRule(options = {}) {
776
1061
  const allowUnpinned = new Set(options.allowUnpinned ?? []);
777
1062
  const ciCritical = options.ciCritical ?? true;
778
1063
  return {
779
- id: RULE_ID5,
1064
+ id: RULE_ID7,
780
1065
  category: "ci",
781
1066
  ciCritical,
782
1067
  description: "Workflow service and container images, and docker-compose images, must be pinned by `@sha256:` digest (a service that builds locally is exempt).",
@@ -1061,6 +1346,8 @@ var RULE_FACTORIES = {
1061
1346
  "canonical-helpers-single-home": createCanonicalHelpersSingleHomeRule,
1062
1347
  "dockerfile-base-image-digest-pin": createDockerfileBaseImageDigestPinRule,
1063
1348
  "file-size-ratchet": createFileSizeRatchetRule,
1349
+ "github-actions-least-privilege-permissions": createGithubActionsLeastPrivilegePermissionsRule,
1350
+ "github-actions-no-template-injection": createGithubActionsNoTemplateInjectionRule,
1064
1351
  "github-actions-runner-pinned": createGithubActionsRunnerPinnedRule,
1065
1352
  "github-actions-sha-pinned": createGithubActionsShaPinnedRule,
1066
1353
  "layer-rank": createLayerRankRule,
@@ -1088,6 +1375,8 @@ export {
1088
1375
  createCanonicalHelpersSingleHomeRule,
1089
1376
  createDockerfileBaseImageDigestPinRule,
1090
1377
  createFileSizeRatchetRule,
1378
+ createGithubActionsLeastPrivilegePermissionsRule,
1379
+ createGithubActionsNoTemplateInjectionRule,
1091
1380
  createGithubActionsRunnerPinnedRule,
1092
1381
  createGithubActionsShaPinnedRule,
1093
1382
  createLayerRankRule,
package/dist/prisma.js CHANGED
@@ -1,11 +1,11 @@
1
- import {
2
- globFiles
3
- } from "./chunk-Z7TXSZR4.js";
4
1
  import {
5
2
  firstOptionOf,
6
3
  resolveRules,
7
4
  severityOf
8
5
  } from "./chunk-VFCX3QKZ.js";
6
+ import {
7
+ globFiles
8
+ } from "./chunk-OYFQKSJN.js";
9
9
 
10
10
  // src/prisma/prisma-method-surface.ts
11
11
  import { PRISMA_READ_METHODS, PRISMA_WRITE_METHODS } from "@noctcore/eslint-plugin-prisma";