@nitra/cursor 1.13.82 → 1.13.84
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/.claude-template/commands/n-check.md +2 -2
- package/CHANGELOG.md +59 -26
- package/README.md +11 -11
- package/bin/n-cursor.js +38 -67
- package/package.json +1 -1
- package/rules/abie/abie.mdc +9 -9
- package/rules/abie/fix.mjs +19 -0
- package/rules/abie/policy/base_deployment_preem/base_deployment_preem.rego +2 -2
- package/rules/abie/policy/health_check_policy/health_check_policy.rego +2 -2
- package/rules/abie/policy/http_route_base/http_route_base.rego +1 -1
- package/rules/abie/utils/k8s-tree.mjs +1 -1
- package/rules/adr/adr.mdc +1 -1
- package/rules/adr/fix.mjs +19 -0
- package/rules/adr/{fix → js}/hooks/check.mjs +1 -1
- package/rules/bun/bun.mdc +1 -1
- package/rules/bun/fix.mjs +19 -0
- package/rules/bun/{fix → js}/layout/check.mjs +1 -1
- package/rules/capacitor/fix.mjs +19 -0
- package/rules/capacitor/policy/package_json/package_json.rego +3 -3
- package/rules/changelog/changelog.mdc +2 -2
- package/rules/changelog/fix.mjs +19 -0
- package/rules/ci4/ci4.mdc +1 -1
- package/rules/ci4/fix.mjs +19 -0
- package/rules/docker/docker.mdc +7 -7
- package/rules/docker/fix.mjs +19 -0
- package/rules/docker/lint/lint.mjs +2 -2
- package/rules/docker/policy/package_json/package_json.rego +1 -1
- package/rules/efes/efes.mdc +1 -1
- package/rules/efes/fix.mjs +19 -0
- package/rules/feedback/feedback.mdc +2 -2
- package/rules/feedback/fix.mjs +19 -0
- package/rules/ga/fix.mjs +19 -0
- package/rules/ga/lint/lint.mjs +6 -6
- package/rules/ga/policy/workflow_common/workflow_common.rego +1 -1
- package/rules/graphql/fix.mjs +19 -0
- package/rules/graphql/{fix → js}/tooling/graphql-gql-scan.mjs +1 -1
- package/rules/graphql/policy/vscode_extensions/vscode_extensions.rego +2 -2
- package/rules/hasura/fix.mjs +19 -0
- package/rules/hasura/policy/svc_hl/svc_hl.rego +1 -1
- package/rules/image-avif/fix.mjs +19 -0
- package/rules/image-avif/image-avif.mdc +1 -1
- package/rules/image-avif/{fix → js}/avif_generation/check.mjs +1 -1
- package/rules/image-compress/fix.mjs +19 -0
- package/rules/image-compress/{fix → js}/package_setup/check.mjs +1 -1
- package/rules/js-bun-db/fix.mjs +19 -0
- package/rules/js-bun-redis/fix.mjs +19 -0
- package/rules/js-bun-redis/policy/package_json/package_json.rego +1 -1
- package/rules/js-lint/fix.mjs +19 -0
- package/rules/js-lint/{fix → js}/tooling/check.mjs +5 -5
- package/rules/js-lint/{fix → js}/tooling/rebuild-oxlint-canonical.mjs +1 -1
- package/rules/js-lint/js-lint.mdc +3 -3
- package/rules/js-mssql/fix.mjs +19 -0
- package/rules/js-mssql/policy/package_json/package_json.rego +2 -2
- package/rules/js-run/fix.mjs +19 -0
- package/rules/js-run/{fix → js}/runtime/check.mjs +3 -3
- package/rules/k8s/fix.mjs +19 -0
- package/rules/k8s/{fix → js}/manifests/check.mjs +1 -1
- package/rules/k8s/k8s.mdc +13 -13
- package/rules/k8s/lint/lint.mjs +2 -2
- package/rules/k8s/policy/base_kustomization/base_kustomization.rego +3 -3
- package/rules/k8s/policy/base_manifest/base_manifest.rego +2 -2
- package/rules/k8s/policy/gateway/gateway.rego +2 -2
- package/rules/k8s/policy/hasura_configmap/hasura_configmap.rego +3 -3
- package/rules/k8s/policy/hasura_httproute/hasura_httproute.rego +1 -1
- package/rules/k8s/policy/hpa_pdb/hpa_pdb.rego +1 -1
- package/rules/k8s/policy/kustomization/kustomization.rego +2 -2
- package/rules/k8s/policy/manifest/manifest.rego +4 -4
- package/rules/k8s/policy/svc_hl_yaml/svc_hl_yaml.rego +2 -2
- package/rules/k8s/policy/svc_yaml/svc_yaml.rego +2 -2
- package/rules/nginx-default-tpl/fix.mjs +19 -0
- package/rules/nginx-default-tpl/{fix → js}/template/check.mjs +1 -1
- package/rules/nginx-default-tpl/policy/vscode_extensions/vscode_extensions.rego +2 -2
- package/rules/nginx-default-tpl/policy/vscode_settings/vscode_settings.rego +1 -1
- package/rules/npm-module/fix.mjs +19 -0
- package/rules/npm-module/{fix → js}/package_structure/check.mjs +3 -3
- package/rules/php/fix.mjs +19 -0
- package/rules/php/{fix → js}/tooling/check.mjs +2 -2
- package/rules/rego/fix.mjs +19 -0
- package/rules/security/fix.mjs +19 -0
- package/rules/security/security.mdc +3 -3
- package/rules/style-lint/fix.mjs +19 -0
- package/rules/style-lint/{fix → js}/tooling/check.mjs +2 -2
- package/rules/tauri/fix.mjs +19 -0
- package/rules/tauri/{fix → js}/tooling/check.mjs +1 -1
- package/rules/tauri/policy/vscode_extensions/vscode_extensions.rego +2 -2
- package/rules/test/fix.mjs +19 -0
- package/rules/test/test.mdc +2 -2
- package/rules/text/fix.mjs +19 -0
- package/rules/text/{fix → js}/formatting/check.mjs +2 -2
- package/rules/text/text.mdc +1 -1
- package/rules/vue/fix.mjs +19 -0
- package/rules/vue/{fix → js}/packages/check.mjs +1 -1
- package/rules/vue/policy/package_json/package_json.rego +1 -1
- package/rules/vue/vue.mdc +1 -1
- package/schemas/n-cursor.json +1 -1
- package/scripts/auto-rules.mjs +3 -3
- package/scripts/build-agents-commands.mjs +1 -1
- package/scripts/claude-stop-hook.mjs +1 -1
- package/scripts/sync-claude-config.mjs +2 -2
- package/scripts/utils/ast-scan-utils.mjs +3 -3
- package/scripts/utils/discover-check-rules-from-cursor.mjs +1 -1
- package/scripts/utils/discover-checkable-rules.mjs +30 -18
- package/scripts/utils/list-rule-ids.mjs +23 -0
- package/scripts/utils/read-n-cursor-config-lite.mjs +59 -0
- package/scripts/utils/run-rule-cli.mjs +37 -0
- package/scripts/utils/run-rule.mjs +7 -7
- package/scripts/utils/run-standard-rule.mjs +34 -0
- package/scripts/utils/walk-cache.mjs +24 -0
- package/scripts/utils/workspaces.mjs +1 -1
- package/skills/fix/SKILL.md +5 -5
- package/skills/lint/SKILL.md +1 -1
- /package/rules/abie/{fix → js}/applies/check.mjs +0 -0
- /package/rules/abie/{fix → js}/env_dns/check.mjs +0 -0
- /package/rules/abie/{fix → js}/firebase_hosting/check.mjs +0 -0
- /package/rules/abie/{fix → js}/hc_pairing/check.mjs +0 -0
- /package/rules/abie/{fix → js}/ua_http_route/check.mjs +0 -0
- /package/rules/abie/{fix → js}/ua_node_selector/check.mjs +0 -0
- /package/rules/adr/{fix → js}/hooks/template/.gitignore.snippet +0 -0
- /package/rules/capacitor/{fix → js}/platforms/check.mjs +0 -0
- /package/rules/changelog/{fix → js}/consistency/check.mjs +0 -0
- /package/rules/changelog/{fix → js}/consistency/package-manifest.mjs +0 -0
- /package/rules/docker/{fix → js}/lint/check.mjs +0 -0
- /package/rules/docker/{fix → js}/lint/docker-hadolint.mjs +0 -0
- /package/rules/docker/{fix → js}/lint/docker-mirror.mjs +0 -0
- /package/rules/ga/{fix → js}/workflows/check.mjs +0 -0
- /package/rules/graphql/{fix → js}/tooling/check.mjs +0 -0
- /package/rules/hasura/{fix → js}/internal_urls/check.mjs +0 -0
- /package/rules/js-bun-db/{fix → js}/safety/bun-sql-scan.mjs +0 -0
- /package/rules/js-bun-db/{fix → js}/safety/check.mjs +0 -0
- /package/rules/js-bun-redis/{fix → js}/imports/check.mjs +0 -0
- /package/rules/js-lint/{fix → js}/tooling/knip-canonical.json +0 -0
- /package/rules/js-lint/{fix → js}/tooling/oxlint-canonical-skeleton.json +0 -0
- /package/rules/js-lint/{fix → js}/tooling/oxlint-canonical.json +0 -0
- /package/rules/js-lint/{fix → js}/tooling/oxlint-rules.tsv +0 -0
- /package/rules/js-mssql/{fix → js}/deps/check.mjs +0 -0
- /package/rules/js-mssql/{fix → js}/deps/mssql-pool-scan.mjs +0 -0
- /package/rules/js-run/{fix → js}/runtime/bunyan-imports.mjs +0 -0
- /package/rules/js-run/{fix → js}/runtime/check-env-scan.mjs +0 -0
- /package/rules/js-run/{fix → js}/runtime/conn-file-rules.mjs +0 -0
- /package/rules/js-run/{fix → js}/runtime/conn-imports-scan.mjs +0 -0
- /package/rules/js-run/{fix → js}/runtime/promise-settimeout-scan.mjs +0 -0
- /package/rules/k8s/{fix → js}/kubescape_exceptions/template/.kubescape-exceptions.json.snippet.json +0 -0
- /package/rules/rego/{fix → js}/applies/check.mjs +0 -0
- /package/rules/security/{fix → js}/sample_secret/check.mjs +0 -0
- /package/rules/security/{fix → js}/trufflehog/check.mjs +0 -0
- /package/rules/security/{fix → js}/trufflehog/template/.trufflehog-exclude.snippet.txt +0 -0
- /package/rules/test/{fix → js}/location/check.mjs +0 -0
- /package/rules/vue/{fix → js}/packages/vue-forbidden-imports.mjs +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Перевірка `.vscode/extensions.json` для graphql (graphql.mdc).
|
|
2
2
|
#
|
|
3
|
-
# Викликається з `
|
|
3
|
+
# Викликається з `rules/graphql/fix.mjs` через `runConftestBatch` лише ПІСЛЯ того,
|
|
4
4
|
# як JS виявив `gql\`…\`` tagged template literal у джерелах (умовне правило).
|
|
5
|
-
# Тому без `target.json` поруч (не auto-discoverable через `n-cursor
|
|
5
|
+
# Тому без `target.json` поруч (не auto-discoverable через `n-cursor fix`) —
|
|
6
6
|
# інакше були б false-positive порушення на проєктах без gql.
|
|
7
7
|
#
|
|
8
8
|
# Canonical (graphql.mdc):
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
6
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
7
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
8
|
+
*/
|
|
9
|
+
export function run(ctx) {
|
|
10
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
17
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
19
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# conftest test hasura/k8s/base/svc-hl.yaml -p npm/rules/hasura/policy/svc_hl \
|
|
9
9
|
# --namespace hasura.svc_hl
|
|
10
10
|
#
|
|
11
|
-
# Cross-file (`HASURA_GRAPHQL_ENDPOINT` ↔ YAML) — `
|
|
11
|
+
# Cross-file (`HASURA_GRAPHQL_ENDPOINT` ↔ YAML) — `js/internal_urls/check.mjs`.
|
|
12
12
|
package hasura.svc_hl
|
|
13
13
|
|
|
14
14
|
import rego.v1
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
6
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
7
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
8
|
+
*/
|
|
9
|
+
export function run(ctx) {
|
|
10
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
17
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
19
|
+
}
|
|
@@ -5,7 +5,7 @@ globs: "**/*.{png,jpg,jpeg,gif,avif,vue,html}"
|
|
|
5
5
|
alwaysApply: false
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
AVIF-двійники (`<name>.<ext>.avif`) генерує **виключно** `npx @nitra/cursor
|
|
8
|
+
AVIF-двійники (`<name>.<ext>.avif`) генерує **виключно** `npx @nitra/cursor fix image-avif` — у `lint-image` прапорець `--avif` заборонений (це валідує правило `image-compress`). Перевірка робить чотири кроки в порядку:
|
|
9
9
|
|
|
10
10
|
1. **Pre-scan**: шукає у `.vue`/`.html` хоча б одне raster-посилання, яке потенційно треба переписати на AVIF-двійник (`import x from '...png'` або `<img src="...png" />`). Пакети з opt-out `disable-avif: true` пропускаються. **Якщо жодного raster-посилання не знайдено — `check image-avif` завершується успіхом одразу: ні `npx --avif`, ні rewrite, ні cleanup-сиріт не виконуються** (нічого було б змінювати). Так уникаємо дорогого `npx @nitra/minify-image` у проєктах, де AVIF не вживається.
|
|
11
11
|
2. Запускає `npx @nitra/minify-image --src=. --write --avif` (≥ **3.3.1**) — генерує `<name>.<ext>.avif` поряд з кожним PNG/JPEG/GIF. CLI порівнює sha1 кожного raster-сорсу зі збереженим у `.n-minify-image.tsv` і перезаписує `<source>.avif` при зміні оригіналу.
|
|
@@ -218,7 +218,7 @@ async function checkVueAvifImportsInPackage(packageRoot, otherRootsAbs, ignorePa
|
|
|
218
218
|
VUE_RASTER_IMPORT_RE,
|
|
219
219
|
importPath =>
|
|
220
220
|
`[${label}] ${rel}: import з '${importPath}' має посилатись на AVIF-двійник '${importPath}.avif' ` +
|
|
221
|
-
`(\`npx @nitra/cursor
|
|
221
|
+
`(\`npx @nitra/cursor fix image-avif\` створює його поряд, якщо оригінал є на диску). Вимкнути локально: "@nitra/minify-image": { "disable-avif": true } у package.json пакета`
|
|
222
222
|
)
|
|
223
223
|
processMatches(
|
|
224
224
|
VUE_RASTER_STATIC_SRC_RE,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
6
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
7
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
8
|
+
*/
|
|
9
|
+
export function run(ctx) {
|
|
10
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
17
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
19
|
+
}
|
|
@@ -100,7 +100,7 @@ export async function check() {
|
|
|
100
100
|
fail('package.json не знайдено в корені — додай (image-compress.mdc)')
|
|
101
101
|
return reporter.getExitCode()
|
|
102
102
|
}
|
|
103
|
-
pass('package.json є (структуру перевіряє npx @nitra/cursor
|
|
103
|
+
pass('package.json є (структуру перевіряє npx @nitra/cursor fix → image_compress.package_json)')
|
|
104
104
|
|
|
105
105
|
await checkHashCacheNotIgnored(pass, fail)
|
|
106
106
|
await checkLegacyCacheRemoved(pass, fail)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
6
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
7
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
8
|
+
*/
|
|
9
|
+
export function run(ctx) {
|
|
10
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
17
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
6
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
7
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
8
|
+
*/
|
|
9
|
+
export function run(ctx) {
|
|
10
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
17
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
19
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Канон надходить через --data: { "template": { "deny": ... } }
|
|
4
4
|
# Структура --data сформована з template/package.json.deny.json.
|
|
5
5
|
# AST-скан коду (`import`/`require`/dynamic `import()` тих самих пакетів)
|
|
6
|
-
# лишається у `js-bun-redis/
|
|
6
|
+
# лишається у `js-bun-redis/js/imports/check.mjs`.
|
|
7
7
|
package js_bun_redis.package_json
|
|
8
8
|
|
|
9
9
|
import rego.v1
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
6
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
7
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
8
|
+
*/
|
|
9
|
+
export function run(ctx) {
|
|
10
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
17
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
19
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Перевіряє лінт JavaScript за правилом js-lint.mdc.
|
|
3
3
|
*
|
|
4
4
|
* Flat ESLint з getConfig і ignore для auto-imports,
|
|
5
|
-
* `.oxlintrc.json` має збігатися з каноном oxlint у пакеті (`npm/rules/js-lint/
|
|
5
|
+
* `.oxlintrc.json` має збігатися з каноном oxlint у пакеті (`npm/rules/js-lint/js/tooling/oxlint-canonical.json`):
|
|
6
6
|
* plugins, jsPlugins, categories, усі правила з канону (додаткові записи в `rules` дозволені), settings, env,
|
|
7
7
|
* globals, ignorePatterns. Також перевіряє workspace `package.json` на `type: "module"`
|
|
8
8
|
* і `engines`, workflow-дубль у `lint.yml`, `knip.json` autofill і застарілі `.eslintrc*`.
|
|
@@ -154,7 +154,7 @@ export function verifyOxlintRcAgainstCanonical(cfg, canonical) {
|
|
|
154
154
|
|
|
155
155
|
if (!deepEqualOxlintCanonical(actual, expected)) {
|
|
156
156
|
failures.push(
|
|
157
|
-
`.oxlintrc.json: поле "${key}" має збігатися з каноном пакета @nitra/cursor (npm/rules/js-lint/
|
|
157
|
+
`.oxlintrc.json: поле "${key}" має збігатися з каноном пакета @nitra/cursor (npm/rules/js-lint/js/tooling/oxlint-canonical.json)`
|
|
158
158
|
)
|
|
159
159
|
}
|
|
160
160
|
}
|
|
@@ -347,9 +347,9 @@ async function checkOxlintRc(passFn, failFn) {
|
|
|
347
347
|
*/
|
|
348
348
|
async function checkLintJsWorkflows(passFn, failFn) {
|
|
349
349
|
if (existsSync('.github/workflows/lint-js.yml')) {
|
|
350
|
-
passFn('.github/workflows/lint-js.yml є (структуру перевіряє npx @nitra/cursor
|
|
350
|
+
passFn('.github/workflows/lint-js.yml є (структуру перевіряє npx @nitra/cursor fix → js_lint.lint_js_yml)')
|
|
351
351
|
} else {
|
|
352
|
-
failFn('.github/workflows/lint-js.yml не існує — створи його (див.
|
|
352
|
+
failFn('.github/workflows/lint-js.yml не існує — створи його (див. rules/js-lint/fix.mjs / js-lint.mdc)')
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
if (existsSync('.github/workflows/lint.yml')) {
|
|
@@ -384,7 +384,7 @@ async function checkKnipConfig(passFn, failFn) {
|
|
|
384
384
|
return
|
|
385
385
|
}
|
|
386
386
|
await copyFile(KNIP_CANONICAL_JSON_PATH, 'knip.json')
|
|
387
|
-
passFn('knip.json створено з канонічного npm/rules/js-lint/
|
|
387
|
+
passFn('knip.json створено з канонічного npm/rules/js-lint/js/tooling/knip-canonical.json (js-lint.mdc)')
|
|
388
388
|
}
|
|
389
389
|
|
|
390
390
|
/**
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Збирає `oxlint-canonical.json` з `oxlint-canonical-skeleton.json` (без поля rules) та списку
|
|
3
3
|
* правил у `oxlint-rules.tsv` (колонки: ім’я правила, TAB, severity: deny | off | error).
|
|
4
4
|
*
|
|
5
|
-
* Після змін у TSV або скелеті запускай з каталогу пакета: `bun ./rules/js-lint/
|
|
5
|
+
* Після змін у TSV або скелеті запускай з каталогу пакета: `bun ./rules/js-lint/js/tooling/rebuild-oxlint-canonical.mjs`,
|
|
6
6
|
* потім скопіюй оновлений канон у корінь споживача як `.oxlintrc.json` за потреби.
|
|
7
7
|
*/
|
|
8
8
|
import { readFileSync, writeFileSync } from 'node:fs'
|
|
@@ -25,7 +25,7 @@ version: '1.23'
|
|
|
25
25
|
|
|
26
26
|
У `.vscode/extensions.json` `recommendations` мають містити `dbaeumer.vscode-eslint`, `github.vscode-github-actions`, `oxc.oxc-vscode`: [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
|
|
27
27
|
|
|
28
|
-
У корені має бути **`.oxlintrc.json`**, який **збігається з каноном** oxlint з пакета **`@nitra/cursor`**: файл **`npm/rules/js-lint/
|
|
28
|
+
У корені має бути **`.oxlintrc.json`**, який **збігається з каноном** oxlint з пакета **`@nitra/cursor`**: файл **`npm/rules/js-lint/js/tooling/oxlint-canonical.json`** (plugins, jsPlugins з **`@e18e/eslint-plugin`**, categories, повний набір **rules** із канону — додаткові записи в **`rules`** дозволені; також **`settings`**, **`env`**, **`globals`**). Поле **`ignorePatterns`** працює як **`rules`**: канонічні патерни з **`oxlint-canonical.json`** (наразі **`**/schema.graphql`**, **`**/auto-imports.d.ts`**) мають бути присутні, додаткові локальні glob-и дозволені. Оновити канон можна з репозиторію пакета або скопіювавши файл після **`bun ./rules/js-lint/js/tooling/rebuild-oxlint-canonical.mjs`** (джерело правил — **`oxlint-rules.tsv`** + скелет **`oxlint-canonical-skeleton.json`**). Модуль **`@e18e/eslint-plugin`** не оголошуй окремо в **`package.json`** — він уже в залежностях **`@nitra/eslint-config`** (з **3.8.0**), oxlint підвантажує його з **`node_modules`**.
|
|
29
29
|
|
|
30
30
|
Мінімум для розуміння структури (реальний корінь конфігу має збігатися з каноном повністю):
|
|
31
31
|
|
|
@@ -62,9 +62,9 @@ version: '1.23'
|
|
|
62
62
|
|
|
63
63
|
Перевірку невикористаних залежностей і експортів виконує **knip** (заміна `depcheck`). Викликається у скрипті `lint-js` і в CI разом з oxlint/eslint/jscpd — окремий крок у CI не потрібен.
|
|
64
64
|
|
|
65
|
-
У корені проєкту має бути **`knip.json`**, який стартує з канонічного baseline з пакета `@nitra/cursor` — файл [`npm/rules/js-lint/
|
|
65
|
+
У корені проєкту має бути **`knip.json`**, який стартує з канонічного baseline з пакета `@nitra/cursor` — файл [`npm/rules/js-lint/js/tooling/knip-canonical.json`](./js/tooling/knip-canonical.json). Він покриває типові false-positives для наших правил: `entry` зі CLI-конфігами (eslint, stylelint, oxlint, jscpd, markdownlint-cli2, `commitlint`), `project` для `**/*.{js,mjs,cjs,jsx,ts,tsx,mts,cts}`, `ignore` для `**/__fixtures__/**`, `ignoreDependencies` для пакетів, посилання на які є лише в не-JS-конфігах (`@nitra/cspell-dict`, `/@cspell\/dict-.+/`), і `ignoreBinaries` для CLI, які канон вимагає викликати через `npx`/`bunx` і яких заборонено додавати в `devDependencies` (`actionlint`, `cspell`, `depcheck`, `eslint`, `git-ai`, `jscpd`, `markdownlint-cli2`, `oxfmt`, `oxlint`, `shellcheck`, `uvx`, `v8r`, `zizmor`).
|
|
66
66
|
|
|
67
|
-
Якщо `knip.json` відсутній — `npx @nitra/cursor
|
|
67
|
+
Якщо `knip.json` відсутній — `npx @nitra/cursor fix js-lint` копіює канон у корінь проєкту (side effect). Після створення модифікуй файл під свій проєкт як завгодно: перевіряємо лише наявність, зміст подальших змін не валідується.
|
|
68
68
|
|
|
69
69
|
Пакет `knip` окремо в `devDependencies` не додавай — `bunx knip` тягне його ad-hoc, як oxlint/eslint/jscpd.
|
|
70
70
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
6
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
7
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
8
|
+
*/
|
|
9
|
+
export function run(ctx) {
|
|
10
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
17
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
19
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Порт перевірки версії `mssql` з `npm/scripts/
|
|
1
|
+
# Порт перевірки версії `mssql` з `npm/scripts/rules/js-mssql/fix.mjs` (js-mssql.mdc).
|
|
2
2
|
#
|
|
3
3
|
# Запуск (локально, для будь-якого `package.json`):
|
|
4
4
|
# conftest test path/to/package.json -p npm/policy/js_mssql \
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
#
|
|
10
10
|
# AST-скан коду на per-request `new sql.ConnectionPool(...)` всередині функцій
|
|
11
11
|
# (потребує парсингу `.js` / `.ts` через oxc-parser), а також full-semver
|
|
12
|
-
# (`major.minor.patch` triple-compare у `
|
|
12
|
+
# (`major.minor.patch` triple-compare у `rules/js-mssql/fix.mjs`) лишаються у JS:
|
|
13
13
|
# JS-перевірка authoritative, ця Rego — швидкий gate для одиничного `package.json`.
|
|
14
14
|
#
|
|
15
15
|
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
6
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
7
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
8
|
+
*/
|
|
9
|
+
export function run(ctx) {
|
|
10
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
17
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
19
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* (див. `utils/bunyan-imports.mjs`);
|
|
8
8
|
* - наявність `OTEL_RESOURCE_ATTRIBUTES` зі значеннями `service.name=` та `service.namespace=`
|
|
9
9
|
* у `k8s/base/configmap.yaml`, якщо такий файл існує (відповідність імені ConfigMap імені
|
|
10
|
-
* Deployment перевіряється в `
|
|
10
|
+
* Deployment перевіряється в `rules/k8s/fix.mjs`);
|
|
11
11
|
* - «Внутрішні аліаси» (`#conn/*`): імпорти `bun#SQL`, будь-який `mssql`, `@nitra/graphql-request#GraphQLClient`
|
|
12
12
|
* дозволені лише у каталозі conn (за замовчуванням `src/conn/`; за наявності
|
|
13
13
|
* `package.json#imports['#conn/*']` — у його цільовому каталозі); поза ним — порушення
|
|
@@ -334,7 +334,7 @@ async function checkWorkspacePackage(rootDir, ignorePaths, fail, passFn) {
|
|
|
334
334
|
// Frontend-пакети (vite у devDependencies) виходять за межі js-run:
|
|
335
335
|
// браузерний бандл не має `node:process`, а `process.env.*` бандлер
|
|
336
336
|
// обробляє самостійно. Перевірку process.env / conn-аліасів пропускаємо;
|
|
337
|
-
// bunyan-залежність валідується в Rego (`npx @nitra/cursor
|
|
337
|
+
// bunyan-залежність валідується в Rego (`npx @nitra/cursor fix`).
|
|
338
338
|
if (packageJsonHasViteDevDependency(pkgJson)) {
|
|
339
339
|
passFn(`${label}vite-пакет (frontend) — js-run пропущено (process.env / conn-aliases / OTEL configmap)`)
|
|
340
340
|
return
|
|
@@ -418,7 +418,7 @@ async function loadPackageJson(rootDir) {
|
|
|
418
418
|
function checkOtelConfigmap(rootDir, passFn) {
|
|
419
419
|
const configmapPath = join(rootDir, 'k8s', 'base', 'configmap.yaml')
|
|
420
420
|
if (!existsSync(configmapPath)) return
|
|
421
|
-
passFn(`${rootDir}/k8s/base/configmap.yaml є (OTEL — npx @nitra/cursor
|
|
421
|
+
passFn(`${rootDir}/k8s/base/configmap.yaml є (OTEL — npx @nitra/cursor fix → js_run.configmap)`)
|
|
422
422
|
}
|
|
423
423
|
|
|
424
424
|
/**
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { runStandardRule } from '../../scripts/utils/run-standard-rule.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Запускає правило: applies → JS-concerns → policy → mdc-refs (через runStandardRule).
|
|
5
|
+
* Library mode: викликається CLI orchestration через `import + run(ctx)`.
|
|
6
|
+
* @param {import('../../scripts/utils/run-standard-rule.mjs').RuleContext} [ctx] контекст прогону (walkCache тощо)
|
|
7
|
+
* @returns {Promise<number>} 0 — OK, 1 — порушення
|
|
8
|
+
*/
|
|
9
|
+
export function run(ctx) {
|
|
10
|
+
return runStandardRule(import.meta.dirname, ctx)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (import.meta.main) {
|
|
14
|
+
// Standalone: bun rules/<id>/fix.mjs — повний еквівалент `npx @nitra/cursor fix <id>`
|
|
15
|
+
// (config-loading + whitelist + summary). Дві ролі fix.mjs: library (run) + standalone (main).
|
|
16
|
+
const { runRuleCli } = await import('../../scripts/utils/run-rule-cli.mjs')
|
|
17
|
+
// eslint-disable-next-line unicorn/no-process-exit -- standalone entry-point має повертати exit-code для CI/IDE
|
|
18
|
+
process.exit(await runRuleCli(import.meta.dirname))
|
|
19
|
+
}
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
* Явні винятки до загальної логіки yannh/datree — таблиця **`EXPLICIT_K8S_SCHEMAS`** (`Map`): ключ
|
|
80
80
|
* **`apiVersion`, `kind`, `type`** (для CRD без поля `type` у маніфесті — зірочка **`*`** як третій
|
|
81
81
|
* компонент). Спочатку шукається збіг за фактичним `type`, потім за **`*`**.
|
|
82
|
-
* Dockerfile — правило docker.mdc, скрипт
|
|
82
|
+
* Dockerfile — правило docker.mdc, скрипт rules/docker/fix.mjs.
|
|
83
83
|
*
|
|
84
84
|
* **Структура `HTTPRoute` для Hasura-Deployment:** звіряється канон 4 правил у **`spec.rules`** (редиректи **`<prefix>/ql`** і **`<prefix>/ql/`** на **`<prefix>/ql/console`** 302, **`PathPrefix <prefix>/ql`** + **URLRewrite** на **`/`**, окреме WebSocket-правило з **`RequestHeaderModifier`** remove **`Authorization`**). **Префікс параметризовано** (рядок перед **`/ql`** у першому Hasura-правилі). **Прив'язка** — за **`metadata.name`** у тому ж каталозі, що й **Deployment** з образом **`hasura/graphql-engine`** (див. k8s.mdc). **Додаткові правила** поверх канону дозволені.
|
|
85
85
|
*
|
package/rules/k8s/k8s.mdc
CHANGED
|
@@ -17,13 +17,13 @@ alwaysApply: false
|
|
|
17
17
|
|
|
18
18
|
**Modeline — опційний:** якщо для конкретного поєднання `apiVersion`/`kind` **немає** надійної публічної схеми (yannh/datree/schemastore не покривають), залиш файл **без** рядка `# yaml-language-server: $schema=…`. **Заборонено** ставити `$schema=file:…` як заглушку — це створює видимість валідації без неї. Без modeline `check-k8s` не валідує URL, але **`lint-k8s`** (kubeconform/kubescape) продовжить роботу. Якщо modeline присутній — він обов'язково перший рядок і **тільки** `https://` URL, який відповідає очікуваному за `apiVersion`/`kind`.
|
|
19
19
|
|
|
20
|
-
**Виняток — modeline заборонено:** `apiVersion: alb.yc.io/v1alpha1`, `kind: HttpBackendGroup` (Yandex ALB) — рядка **`# yaml-language-server: $schema=…`** у файлі **не** має бути (ні в першому рядку, ні далі). Перший рядок — одразу YAML (`apiVersion:` тощо). Перевірка — **`
|
|
20
|
+
**Виняток — modeline заборонено:** `apiVersion: alb.yc.io/v1alpha1`, `kind: HttpBackendGroup` (Yandex ALB) — рядка **`# yaml-language-server: $schema=…`** у файлі **не** має бути (ні в першому рядку, ні далі). Перший рядок — одразу YAML (`apiVersion:` тощо). Перевірка — **`rules/k8s/fix.mjs`**.
|
|
21
21
|
|
|
22
22
|
**Розширення:** усі маніфести під **`k8s`**, включно з **`kustomization.yaml`**, — лише **`.yaml`** (розширення **`.yml`** не використовуй).
|
|
23
23
|
|
|
24
24
|
**Скоп — поза `.github/`:** правило стосується YAML-маніфестів у каталогах **`k8s`** (визначаються відносно кореня репо, не за абсолютним шляхом). Файли під **`.github/workflows/`** та **`.github/actions/`** ця перевірка **не** зачіпає — їхній скоп визначає **`ga.mdc`** (там канон — **`.yml`**). Це робить два правила несуперечливими навіть у проєктах, де сам корінь репо випадково має ім'я `k8s/` (тоді сегмент `k8s` присутній у абсолютному шляху всіх файлів, але **відносно кореня** його там немає).
|
|
25
25
|
|
|
26
|
-
**Dockerfile / hadolint** — окреме правило **`docker.mdc`** і **`npx @nitra/cursor
|
|
26
|
+
**Dockerfile / hadolint** — окреме правило **`docker.mdc`** і **`npx @nitra/cursor fix docker`**.
|
|
27
27
|
|
|
28
28
|
## lint-k8s: kubeconform і kubescape
|
|
29
29
|
|
|
@@ -31,7 +31,7 @@ alwaysApply: false
|
|
|
31
31
|
|
|
32
32
|
**Залежності:** виконувані файли kubeconform, kubescape і kubectl у **PATH** (kustomize використовуємо як вшиту підкоманду **`kubectl kustomize`** — окремий бінарник `kustomize` не потрібен); не додавай їх у **devDependencies**.
|
|
33
33
|
|
|
34
|
-
**Версія Kubernetes для kubeconform** має відповідати PIN yannh у цьому правилі та в **`
|
|
34
|
+
**Версія Kubernetes для kubeconform** має відповідати PIN yannh у цьому правилі та в **`rules/k8s/fix.mjs`** (зараз **`-kubernetes-version 1.33.9`** — semver без префікса `v`, еквівалент релізу **v1.33.9**; набір схем **`v1.33.9-standalone-strict`**). Для CRD додатково підключай реєстр [datreeio/CRDs-catalog](https://github.com/datreeio/CRDs-catalog) другим **`-schema-location`**, як у [прикладах kubeconform](https://github.com/yannh/kubeconform#readme). За потреби **`-ignore-missing-schemas`**, якщо частина CRD ще без публічної схеми.
|
|
35
35
|
|
|
36
36
|
**kubescape — вхід через зібраний kustomize-маніфест:** для кожного dir-у з `kustomization.yaml` (`kind: Kustomization`; **`kind: Component`** пропускається — він не білдиться окремо) `lint-k8s` виконує **`kubectl kustomize <dir>`** і передає stdout у **`kubescape scan <tmp-file>`** з порогом **`--severity-threshold high`** (вбудована в kubectl підкоманда `kustomize` — окремий бінарник `kustomize` не потрібен; рендеринг локальний і не потребує доступу до кластера). Маніфест проходить через тимчасовий файл, бо **`kubescape scan` у v4.x не читає stdin** (`-` як шлях → `no resources found to scan`; прапорця `--input`/`--stdin` немає); тимчасова директорія створюється під `os.tmpdir()` і прибирається після скану. Збірка через kustomize нормалізує namespace на workload-маніфестах і **NetworkPolicy у `base/networkpolicy.yaml`** (через `base/kustomization.yaml` `namespace:`), що дає коректний матчинг `podSelector` у `C-0260` (`Missing network policy`) і дозволяє kubescape бачити дерево overlays / components зі справжніми ресурсами. Якщо в дереві **`…/k8s`** немає жодного `kustomization.yaml` (проєкт без Kustomize) — fallback на старий dir-скан **`kubescape scan <каталог-k8s>`**. Перший запуск kubescape може завантажувати артефакти — у CI потрібна мережа або [offline](https://github.com/kubescape/kubescape#readme). На відміну від kubeconform, у **kubescape scan** немає прапорця **`-kubernetes-version`**: перевірка йде за **framework/control** (NSA, MITRE, CIS тощо), а не проти OpenAPI-схеми конкретного релізу Kubernetes. **Орієнтир** для репозиторію той самий, що й для kubeconform — кластер **v1.33.9** (див. **`-kubernetes-version 1.33.9`** вище); для CIS і подібних наближень обирай актуальний framework під політику команди (**`kubescape list frameworks`**, див. [CLI reference](https://github.com/kubescape/kubescape/blob/master/docs/cli-reference.md)).
|
|
37
37
|
|
|
@@ -39,7 +39,7 @@ alwaysApply: false
|
|
|
39
39
|
|
|
40
40
|
Якщо в **корені проєкту** є файл **`.kubescape-exceptions.json`** — `lint-k8s` автоматично передає його в `kubescape scan` через **`--exceptions`** ([postureExceptionPolicy](https://github.com/kubescape/kubescape/blob/master/docs/exceptions.md)). Файл — JSON-масив об'єктів з полями `name`, `policyType: "postureExceptionPolicy"`, `actions` (`["alertOnly"]` — знижує fail до alert, не блокує lint), `resources` (resource designator) і `posturePolicies` (масив `controlID`).
|
|
41
41
|
|
|
42
|
-
Канонічний кейс — **C-0012** (`Applications credentials in configuration files`, High): control тригериться на **імʼя** env, що містить підрядок `secret`/`password`/`key`/`token`, а **не** на значення. Для `HASURA_GRAPHQL_JWT_SECRET` у ConfigMap значення — публічний JWT-конфіг (`jwk_url`, `issuer`), не credentials, але kubescape падає лише через імʼя. Точкове виключення для ConfigMap із цим env — канон: [.kubescape-exceptions.json.snippet.json](./
|
|
42
|
+
Канонічний кейс — **C-0012** (`Applications credentials in configuration files`, High): control тригериться на **імʼя** env, що містить підрядок `secret`/`password`/`key`/`token`, а **не** на значення. Для `HASURA_GRAPHQL_JWT_SECRET` у ConfigMap значення — публічний JWT-конфіг (`jwk_url`, `issuer`), не credentials, але kubescape падає лише через імʼя. Точкове виключення для ConfigMap із цим env — канон: [.kubescape-exceptions.json.snippet.json](./js/kubescape_exceptions/template/.kubescape-exceptions.json.snippet.json)
|
|
43
43
|
|
|
44
44
|
Підстав свою `attributes.name` (рядок або regex), якщо ConfigMap зветься інакше; виключай контрольно, а не глобально (не додавай винятки без `attributes.name`/`labels`, бо тоді C-0012 знімається для усіх ConfigMap-ів проєкту і реальні витоки credentials теж пройдуть).
|
|
45
45
|
|
|
@@ -53,7 +53,7 @@ alwaysApply: false
|
|
|
53
53
|
}
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
Якщо правило **`k8s`** підключено в **`.n-cursor.json`** (масив **`rules`**), у **кореневому** `package.json` **мають** бути скрипт **`lint-k8s`** і виклик **`bun run lint-k8s`** у агрегованому **`lint`** (див. **`bun.mdc`**). Це перевіряє **`npx @nitra/cursor
|
|
56
|
+
Якщо правило **`k8s`** підключено в **`.n-cursor.json`** (масив **`rules`**), у **кореневому** `package.json` **мають** бути скрипт **`lint-k8s`** і виклик **`bun run lint-k8s`** у агрегованому **`lint`** (див. **`bun.mdc`**). Це перевіряє **`npx @nitra/cursor fix bun`**.
|
|
57
57
|
|
|
58
58
|
Додай workflow **`.github/workflows/lint-k8s.yml`** (гілки **`dev`** і **`main`**, лише **`.yml`**, узгоджено з **`ga.mdc`**). **Не** дублюй **`setup-node`**, **`oven-sh/setup-bun`**, **`actions/cache`** і **`bun install`** у job — після **`checkout`** використовуй локальний composite **`setup-bun-deps`** (шлях **`./.github/actions/setup-bun-deps`** або **`./npm/github-actions/setup-bun-deps`**, як у репозиторії). Встановлення **kubeconform** / **kubescape** лишаються окремими кроками.
|
|
59
59
|
|
|
@@ -152,7 +152,7 @@ patches:
|
|
|
152
152
|
|
|
153
153
|
Поле **`imagePullPolicy`** скрипт **не** перевіряє (залишається політиці Kubernetes за тегом образу).
|
|
154
154
|
|
|
155
|
-
Образ **`hasura/graphql-engine`**: дозволений лише канонічний тег із константи **`HASURA_GRAPHQL_ENGINE_IMAGE`** у **`
|
|
155
|
+
Образ **`hasura/graphql-engine`**: дозволений лише канонічний тег із константи **`HASURA_GRAPHQL_ENGINE_IMAGE`** у **`rules/k8s/fix.mjs`** (допускається префікс **`docker.io/`**); решта — помилка **check k8s**.
|
|
156
156
|
|
|
157
157
|
### HTTPRoute для Deployment з `hasura/graphql-engine`
|
|
158
158
|
|
|
@@ -254,7 +254,7 @@ spec:
|
|
|
254
254
|
|
|
255
255
|
Пара файлів для кластерного й headless **Service**: **`svc.yaml`** + **`svc-hl.yaml`** в одному каталозі, **`spec.type: ClusterIP`** / **`clusterIP: None`**, імена **`-hl`**, узгодженість пар, маршрути **`gateway.networking.k8s.io`** (**HTTPRoute**, **GRPCRoute**, **TCPRoute**, **TLSRoute**, **UDPRoute**) — **backendRef** лише на сервіси з суфіксом **`-hl`**; якщо **kustomization** посилається на **`svc.yaml`**, у **тому ж** **`kustomization.yaml`** має бути посилання на sibling **`svc-hl.yaml`**. Скрипт **не** створює файли — додай **`svc-hl.yaml`** вручну (копія з правками **name** / **clusterIP**).
|
|
256
256
|
|
|
257
|
-
**Точні умови та повідомлення `fail`** — верхній JSDoc **`npm/scripts/
|
|
257
|
+
**Точні умови та повідомлення `fail`** — верхній JSDoc **`npm/scripts/rules/k8s/fix.mjs`**.
|
|
258
258
|
|
|
259
259
|
### Gateway API: не дублюй namespace у `backendRef`
|
|
260
260
|
|
|
@@ -293,11 +293,11 @@ spec:
|
|
|
293
293
|
|
|
294
294
|
## ConfigMap: ім'я збігається з Deployment
|
|
295
295
|
|
|
296
|
-
Якщо в `k8s/base/` є **`configmap.yaml`** і **Deployment**, і цей Deployment посилається рівно на **один** ConfigMap — `metadata.name` ConfigMap має збігатися з `metadata.name` Deployment. Точні умови перевірки — **`
|
|
296
|
+
Якщо в `k8s/base/` є **`configmap.yaml`** і **Deployment**, і цей Deployment посилається рівно на **один** ConfigMap — `metadata.name` ConfigMap має збігатися з `metadata.name` Deployment. Точні умови перевірки — **`rules/k8s/fix.mjs`**.
|
|
297
297
|
|
|
298
298
|
## ConfigMap для Hasura-Deployment
|
|
299
299
|
|
|
300
|
-
Якщо в `k8s/base/` поруч із **`configmap.yaml`** є **Deployment** з образом **`hasura/graphql-engine`**, у `data` ConfigMap **обов'язково** має бути ключ **`HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS`** зі значенням **`"true"`**. Точні умови перевірки — **`
|
|
300
|
+
Якщо в `k8s/base/` поруч із **`configmap.yaml`** є **Deployment** з образом **`hasura/graphql-engine`**, у `data` ConfigMap **обов'язково** має бути ключ **`HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS`** зі значенням **`"true"`**. Точні умови перевірки — **`rules/k8s/fix.mjs`**.
|
|
301
301
|
|
|
302
302
|
```yaml
|
|
303
303
|
data:
|
|
@@ -323,7 +323,7 @@ data:
|
|
|
323
323
|
|
|
324
324
|
- **`base/kustomization.yaml`:** поле **`namespace:`** має бути **непорожнім** (перевіряє **check k8s**, якщо файл є).
|
|
325
325
|
|
|
326
|
-
- **Коли `metadata.namespace` обов’язковий у файлі:** YAML під **`k8s`** — непорожній **`metadata.namespace`** для namespaced **kind** (винятки — кластерні **kind**, перелік **`CLUSTER_SCOPED_KINDS`** у **`
|
|
326
|
+
- **Коли `metadata.namespace` обов’язковий у файлі:** YAML під **`k8s`** — непорожній **`metadata.namespace`** для namespaced **kind** (винятки — кластерні **kind**, перелік **`CLUSTER_SCOPED_KINDS`** у **`rules/k8s/fix.mjs`**). У overlays Kustomize значення в маніфесті буде перезаписано полем **`namespace:`** з відповідного **`kustomization.yaml`**, тому в `base` пиши канонічний dev-namespace.
|
|
327
327
|
|
|
328
328
|
- **Не додавай** окремі **patches** Kustomize, які лише змінюють **namespace**: **namespace** визначає Kustomize; у overlays додаткові зміни — без дублювання логіки **namespace**.
|
|
329
329
|
|
|
@@ -408,11 +408,11 @@ images:
|
|
|
408
408
|
- **`pdb.yaml`** — `policy/v1`, `PodDisruptionBudget`, `spec.selector.matchLabels.app` **= `spec.selector.matchLabels.app`** Deployment.
|
|
409
409
|
- **`topologySpreadConstraints`** — запис з `maxSkew: 1`, `topologyKey: kubernetes.io/hostname`, `whenUnsatisfiable: ScheduleAnyway`, `labelSelector.matchLabels.app` рівне тій самій мітці `app`.
|
|
410
410
|
|
|
411
|
-
**Перевірка структури `components/`** (для кожного Deployment у `base/`): наявність каталогу, валідний `kustomization.yaml` як Component, `hpa.yaml` і `pdb.yaml` з відповідністю до Deployment-name / app-label. Алгоритм — функція `validateComponentsForBaseDeployment` у **`
|
|
411
|
+
**Перевірка структури `components/`** (для кожного Deployment у `base/`): наявність каталогу, валідний `kustomization.yaml` як Component, `hpa.yaml` і `pdb.yaml` з відповідністю до Deployment-name / app-label. Алгоритм — функція `validateComponentsForBaseDeployment` у **`rules/k8s/fix.mjs`**.
|
|
412
412
|
|
|
413
413
|
**Локальні шляхи в `kustomization.yaml`:** кожен запис без `://` (remote) з `resources` / `bases` / `components` / `crds`, `patchesStrategicMerge`, `patches[].path`, `patchesJson6902[].path`, `configurations[]`, `replacements[].path` має вказувати на **існуючий** у репозиторії файл (`.yaml` / `.yml`) або **каталог**; биті посилання — помилка **`check k8s`**.
|
|
414
414
|
|
|
415
|
-
**Порядок `resources`:** у маніфесті з **`apiVersion: kustomize.config.k8s.io/…`**, **`kind: Kustomization`**, елементи **`resources:`** (лише непорожні рядки) мають бути **відсортовані за алфавітом (англ. локаль, як `localeCompare('en')` у `
|
|
415
|
+
**Порядок `resources`:** у маніфесті з **`apiVersion: kustomize.config.k8s.io/…`**, **`kind: Kustomization`**, елементи **`resources:`** (лише непорожні рядки) мають бути **відсортовані за алфавітом (англ. локаль, як `localeCompare('en')` у `rules/k8s/fix.mjs`)**. Поля **`bases`**, **`components`**, **`crds`** цією перевіркою **не** впорядковуються.
|
|
416
416
|
|
|
417
417
|
### Env-залежні межі (за сегментом після `/k8s/`)
|
|
418
418
|
|
|
@@ -617,7 +617,7 @@ spec:
|
|
|
617
617
|
app: backend-api
|
|
618
618
|
```
|
|
619
619
|
|
|
620
|
-
Точні умови та повідомлення `fail` — JSDoc на початку `npm/scripts/
|
|
620
|
+
Точні умови та повідомлення `fail` — JSDoc на початку `npm/scripts/rules/k8s/fix.mjs` і функції `validateComponentsForBaseDeployment` / `prodOverlayHpaPdbOverrideNeeds`.
|
|
621
621
|
|
|
622
622
|
## HorizontalPodAutoscaler: `autoscaling/v2`
|
|
623
623
|
|
package/rules/k8s/lint/lint.mjs
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Запуск kubeconform та kubescape для каталогів `…/k8s`, де є YAML-маніфести (див. k8s.mdc).
|
|
3
3
|
*
|
|
4
4
|
* Знаходить унікальні корені каталогів із іменем `k8s` за шляхами файлів **`*.yaml`**
|
|
5
|
-
* (той самий принцип сегмента `k8s`, що й у
|
|
5
|
+
* (той самий принцип сегмента `k8s`, що й у rules/k8s/fix.mjs; розширення **`.yml`** під `k8s` не використовується). Якщо таких файлів немає — вихід 0
|
|
6
6
|
* без виклику зовнішніх CLI.
|
|
7
7
|
*
|
|
8
8
|
* kubeconform перевіряє маніфести проти OpenAPI-схем Kubernetes; kubescape — сканування на
|
|
9
9
|
* misconfiguration / compliance (NSA, MITRE, CIS тощо). Обидві утиліти очікуються в PATH
|
|
10
10
|
* (локально: Homebrew, релізи GitHub; у CI — крок установки з k8s.mdc).
|
|
11
11
|
*
|
|
12
|
-
* Версія `-kubernetes-version` для kubeconform узгоджена з PIN yannh у
|
|
12
|
+
* Версія `-kubernetes-version` для kubeconform узгоджена з PIN yannh у rules/k8s/fix.mjs / k8s.mdc.
|
|
13
13
|
* Kubescape не має аналога цього прапорця; орієнтир цільового кластера — та сама лінія релізу (див. k8s.mdc).
|
|
14
14
|
*/
|
|
15
15
|
import { spawnSync } from 'node:child_process'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Порт перевірки `k8s/base/kustomization.yaml` з `npm/scripts/
|
|
1
|
+
# Порт перевірки `k8s/base/kustomization.yaml` з `npm/scripts/rules/k8s/fix.mjs`
|
|
2
2
|
# (k8s.mdc): у base-kustomization обов'язково має бути непорожнє поле
|
|
3
3
|
# `namespace:`.
|
|
4
4
|
#
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# -p npm/policy/k8s/base_kustomization \
|
|
8
8
|
# --namespace k8s.base_kustomization
|
|
9
9
|
#
|
|
10
|
-
# JS authoritative (`
|
|
10
|
+
# JS authoritative (`rules/k8s/fix.mjs`: `baseKustomizationNamespaceViolation`,
|
|
11
11
|
# `isBaseKustomizationPath` для відбору файла, `ensureBaseKustomizationHasNamespace`
|
|
12
12
|
# як оркестратор).
|
|
13
13
|
#
|
|
@@ -39,7 +39,7 @@ deny contains base_namespace_required_msg if {
|
|
|
39
39
|
# на *локальний* `resources:` base/kustomization.yaml (точне ім'я `hpa.yaml`/`pdb.yaml`,
|
|
40
40
|
# у будь-якому підкаталозі). Рекурсивний обхід `resources:`/`components:`/`bases:`
|
|
41
41
|
# (із зануренням у вкладені kustomization.yaml) — JS-оркестратор
|
|
42
|
-
# `verifyK8sBaseKustomizeHasNoHpaPdb` у `
|
|
42
|
+
# `verifyK8sBaseKustomizeHasNoHpaPdb` у `rules/k8s/fix.mjs` (потребує fs-доступу). Цей
|
|
43
43
|
# rego-deny — defense-in-depth: спрацює навіть якщо JS-крок упаде з винятку раніше.
|
|
44
44
|
#
|
|
45
45
|
# NetworkPolicy у base — навпаки, обов'язковий (k8s.mdc): обмеження мережі мають
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
#
|
|
8
8
|
# JS відбирає файли під `…/k8s/.../base/…` (окрім `kustomization.yaml`) і
|
|
9
9
|
# викликає conftest з цією намеспейс. JS authoritative
|
|
10
|
-
# (`
|
|
10
|
+
# (`rules/k8s/fix.mjs`: `metadataNamespaceRequiredViolation` з `inBaseDir=true`,
|
|
11
11
|
# `deploymentResourcesViolation` з `inK8sBaseLayer=true`,
|
|
12
12
|
# `isK8sBaseManifestYamlPath`).
|
|
13
13
|
#
|
|
@@ -25,7 +25,7 @@ package k8s.base_manifest
|
|
|
25
25
|
import rego.v1
|
|
26
26
|
|
|
27
27
|
# Cluster-scoped kind (не вимагають metadata.namespace) — узгоджено з
|
|
28
|
-
# `CLUSTER_SCOPED_KINDS` у `
|
|
28
|
+
# `CLUSTER_SCOPED_KINDS` у `rules/k8s/fix.mjs`.
|
|
29
29
|
cluster_scoped_kinds := {
|
|
30
30
|
"APIService",
|
|
31
31
|
"CertificateSigningRequest",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Порт пер-документних перевірок для Gateway API і HealthCheckPolicy з
|
|
2
|
-
# `npm/scripts/
|
|
2
|
+
# `npm/scripts/rules/k8s/fix.mjs` (k8s.mdc).
|
|
3
3
|
#
|
|
4
4
|
# Запуск (локально, по одному файлу або по дереву):
|
|
5
5
|
# conftest test path/to/manifest.yaml -p npm/policy/k8s/gateway \
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# `metadata.namespace` маршруту, — заборонено (надлишкове поле, ламається при
|
|
15
15
|
# overlay-перенесеннях).
|
|
16
16
|
#
|
|
17
|
-
# JS authoritative (`
|
|
17
|
+
# JS authoritative (`rules/k8s/fix.mjs` — функції `failIfGatewayRouteUsesNonHeadlessService`,
|
|
18
18
|
# `healthCheckPolicyTargetRefHeadlessServiceViolation`,
|
|
19
19
|
# `collectGatewayApiRouteBackendServiceNames`,
|
|
20
20
|
# `collectGatewayApiRouteBackendRefsWithRedundantNamespace`); ця Rego — швидкий
|