@nitra/cursor 1.13.2 → 1.13.11

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/bin/n-cursor.js +4 -2
  3. package/package.json +4 -2
  4. package/rules/ga/fix/workflows/check.mjs +6 -109
  5. package/rules/ga/ga.mdc +10 -15
  6. package/rules/ga/policy/package_json/package_json.rego +20 -0
  7. package/rules/ga/policy/package_json/target.json +8 -0
  8. package/rules/ga/policy/package_json/template/package.json.contains.json +1 -0
  9. package/rules/ga/policy/vscode_extensions/target.json +8 -0
  10. package/rules/ga/policy/vscode_extensions/template/extensions.json.snippet.json +1 -0
  11. package/rules/ga/policy/vscode_extensions/vscode_extensions.rego +18 -0
  12. package/rules/ga/policy/vscode_settings/target.json +8 -0
  13. package/rules/ga/policy/vscode_settings/template/settings.json.snippet.json +1 -0
  14. package/rules/ga/policy/vscode_settings/vscode_settings.rego +22 -0
  15. package/rules/ga/policy/zizmor_yml/target.json +8 -0
  16. package/rules/ga/policy/zizmor_yml/template/zizmor.yml.snippet.yml +5 -0
  17. package/rules/ga/policy/zizmor_yml/zizmor_yml.rego +27 -0
  18. package/rules/js-lint/fix/tooling/check.mjs +6 -83
  19. package/rules/js-lint/policy/jscpd/jscpd.rego +38 -0
  20. package/rules/js-lint/policy/jscpd/target.json +8 -0
  21. package/rules/js-lint/policy/vscode_extensions/target.json +8 -0
  22. package/rules/js-lint/policy/vscode_extensions/vscode_extensions.rego +25 -0
  23. package/rules/rego/lint/lint.mjs +5 -4
  24. package/rules/rego/policy/package_json/package_json.rego +8 -29
  25. package/rules/rego/policy/package_json/template/package.json.snippet.json +1 -0
  26. package/rules/rego/policy/vscode_extensions/template/extensions.json.snippet.json +1 -0
  27. package/rules/rego/policy/vscode_extensions/vscode_extensions.rego +7 -11
  28. package/rules/rego/policy/vscode_settings/template/settings.json.snippet.json +6 -0
  29. package/rules/rego/policy/vscode_settings/vscode_settings.rego +19 -27
  30. package/rules/rego/rego.mdc +10 -8
  31. package/rules/security/fix/gitleaks/check.mjs +8 -45
  32. package/rules/security/fix/gitleaks/template/.gitleaks.toml.snippet.toml +12 -0
  33. package/rules/security/policy/gitleaks/gitleaks.rego +17 -0
  34. package/rules/security/policy/gitleaks/target.json +8 -0
  35. package/rules/security/policy/package_json/package_json.rego +22 -59
  36. package/rules/security/policy/package_json/template/package.json.contains.json +1 -0
  37. package/rules/security/policy/package_json/template/package.json.deny.json +4 -0
  38. package/rules/security/policy/package_json/template/package.json.snippet.json +1 -0
  39. package/rules/security/security.mdc +7 -26
  40. package/rules/security/todo.MD +27 -0
  41. package/rules/vue/fix/packages/check.mjs +7 -64
  42. package/rules/vue/policy/package_json/package_json.rego +45 -2
  43. package/rules/vue/vue.mdc +15 -2
  44. package/scripts/ensure-nitra-cursor-dev-dependencies.mjs +41 -21
  45. package/scripts/utils/check-mdc-template-refs.mjs +47 -0
  46. package/scripts/utils/inline-template-links.mjs +60 -0
  47. package/scripts/utils/run-conftest-batch.mjs +60 -33
  48. package/scripts/utils/run-rule.mjs +16 -1
  49. package/scripts/utils/template.mjs +215 -0
@@ -0,0 +1,38 @@
1
+ # Перевірка `.jscpd.json` для js-lint (js-lint.mdc).
2
+ #
3
+ # JS-частина лишається для FS/cross-file: наявність workflow, flat ESLint config,
4
+ # `.oxlintrc.json` проти embedded canonical snapshot, `knip.json` autofill.
5
+ # Цей пакет покриває лише структуру одного JSON-документа.
6
+ #
7
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
8
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
9
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
10
+ package js_lint.jscpd
11
+
12
+ import rego.v1
13
+
14
+ deny contains msg if {
15
+ input.gitignore != true
16
+ msg := ".jscpd.json має містити \"gitignore\": true (js-lint.mdc)"
17
+ }
18
+
19
+ deny contains msg if {
20
+ input.exitCode != 1
21
+ msg := ".jscpd.json має містити \"exitCode\": 1 (інакше CI не впаде на клонах) (js-lint.mdc)"
22
+ }
23
+
24
+ deny contains msg if {
25
+ not "console" in {r | some r in object.get(input, "reporters", [])}
26
+ msg := ".jscpd.json має містити \"reporters\": [\"console\"] або масив із \"console\" (js-lint.mdc)"
27
+ }
28
+
29
+ deny contains msg if {
30
+ not is_number(object.get(input, "minLines", null))
31
+ msg := ".jscpd.json має містити \"minLines\" як число >= 25 (js-lint.mdc)"
32
+ }
33
+
34
+ deny contains msg if {
35
+ is_number(input.minLines)
36
+ input.minLines < 25
37
+ msg := ".jscpd.json має містити \"minLines\" як число >= 25 (js-lint.mdc)"
38
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@nitra/cursor/schemas/target.json",
3
+ "files": {
4
+ "single": ".jscpd.json",
5
+ "required": true
6
+ },
7
+ "missingMessage": ".jscpd.json не існує — створи з полями згідно js-lint.mdc"
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@nitra/cursor/schemas/target.json",
3
+ "files": {
4
+ "single": ".vscode/extensions.json",
5
+ "required": true
6
+ },
7
+ "missingMessage": ".vscode/extensions.json не існує — додай recommendations з js-lint.mdc"
8
+ }
@@ -0,0 +1,25 @@
1
+ # Перевірка `.vscode/extensions.json` для js-lint (js-lint.mdc).
2
+ #
3
+ # Canonical: у `recommendations` мають бути ESLint, GitHub Actions і Oxlint.
4
+ # Канон задає мінімум — додаткові рекомендації від інших правил дозволені.
5
+ #
6
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
7
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
8
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
9
+ package js_lint.vscode_extensions
10
+
11
+ import rego.v1
12
+
13
+ required_extensions := {
14
+ "dbaeumer.vscode-eslint",
15
+ "github.vscode-github-actions",
16
+ "oxc.oxc-vscode",
17
+ }
18
+
19
+ recommendations_set := {r | some r in object.get(input, "recommendations", [])}
20
+
21
+ deny contains msg if {
22
+ some required in required_extensions
23
+ not required in recommendations_set
24
+ msg := sprintf(".vscode/extensions.json: recommendations має містити %q (js-lint.mdc)", [required])
25
+ }
@@ -18,9 +18,10 @@
18
18
  * потрібен VS Code-розширенню `tsandall.opa` (LSP, format-on-save через `opa fmt`) — деталі в
19
19
  * `mdc/rego.mdc`.
20
20
  *
21
- * Цілі лінту: `npm/policy/` (місце, де поки що живуть Rego-полісі пакета `@nitra/cursor`).
22
- * Якщо в репозиторії з’являться інші *.rego поза цим деревом, додай шлях у `LINT_TARGETS` —
23
- * усі три інструменти приймають кілька шляхів і самі рекурсивно обходять директорії.
21
+ * Цілі лінту: `npm/rules/` (де живуть Rego-полісі пакета `@nitra/cursor` — у
22
+ * `npm/rules/<id>/policy/<concern>/`). Усі три інструменти приймають один шлях
23
+ * і самі рекурсивно знаходять `.rego` (ігноруючи інші розширення на кшталт
24
+ * `target.json` чи template-фіх).
24
25
  */
25
26
  import { spawnSync } from 'node:child_process'
26
27
  import { existsSync } from 'node:fs'
@@ -30,7 +31,7 @@ import { isRunAsCli } from '../../../scripts/cli-entry.mjs'
30
31
  import { resolveCmd } from '../../../scripts/utils/resolve-cmd.mjs'
31
32
 
32
33
  /** Шляхи з Rego-полісі (відносно cwd). Існують не всі на ранніх стадіях — фільтруємо нижче. */
33
- const LINT_TARGETS = ['npm/policy']
34
+ const LINT_TARGETS = ['npm/rules']
34
35
 
35
36
  /**
36
37
  * Друкує підказку зі встановлення `opa` (потрібен для `opa check --strict` і VS Code LSP).
@@ -1,37 +1,16 @@
1
1
  # Перевірка `package.json` для rego (rego.mdc).
2
2
  #
3
- # Викликається з `check-rego.mjs` через `runConftestBatch` лише ПІСЛЯ виявлення
4
- # `.rego` файлів у дереві. Глобально без `target.json` поруч (не auto-discoverable через `n-cursor check`).
5
- #
6
- # Canonical (rego.mdc): scripts.lint-rego має бути "n-cursor lint-rego" — CLI пакета `@nitra/cursor`
7
- # (бінарка з `node_modules/.bin/`) робить preflight `opa`/`regal` і прогонить
8
- # `opa check --strict` → `regal lint` → опц. `conftest verify` (`@nitra/cursor lint-rego`).
9
- #
10
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/package.json.snippet.json.
5
+ # Дозволяємо whitespace навколо значення (trim_space) — допуск, який мав
6
+ # попередній inline-варіант.
11
7
  package rego.package_json
12
8
 
13
9
  import rego.v1
14
10
 
15
- canonical_lint_rego := "n-cursor lint-rego"
16
-
17
- lint_rego_template := concat(" ", [
18
- "package.json: scripts.lint-rego має бути %q",
19
- "(зараз: %q) (rego.mdc)",
20
- ])
21
-
22
- deny contains msg if {
23
- scripts := object.get(input, "scripts", {})
24
- not "lint-rego" in object.keys(scripts)
25
- msg := concat(" ", [
26
- "package.json: відсутній scripts.lint-rego — додай",
27
- "\"lint-rego\": \"n-cursor lint-rego\" (rego.mdc)",
28
- ])
29
- }
30
-
31
11
  deny contains msg if {
32
- scripts := object.get(input, "scripts", {})
33
- lint_rego := object.get(scripts, "lint-rego", "")
34
- lint_rego != ""
35
- trim_space(lint_rego) != canonical_lint_rego
36
- msg := sprintf(lint_rego_template, [canonical_lint_rego, lint_rego])
12
+ some script_name, expected in data.template.snippet.scripts
13
+ actual := object.get(object.get(input, "scripts", {}), script_name, "")
14
+ trim_space(actual) != expected
15
+ msg := sprintf("package.json: scripts.%s має бути %q (rego.mdc)", [script_name, expected])
37
16
  }
@@ -0,0 +1 @@
1
+ { "scripts": { "lint-rego": "n-cursor lint-rego" } }
@@ -0,0 +1 @@
1
+ { "recommendations": ["tsandall.opa"] }
@@ -1,19 +1,15 @@
1
1
  # Перевірка `.vscode/extensions.json` для rego (rego.mdc).
2
2
  #
3
- # Викликається з `check-rego.mjs` через `runConftestBatch` лише ПІСЛЯ того, як
4
- # JS виявив `.rego` файли у дереві (умовне правило — проєкти без rego не
5
- # зобовʼязані ставити tsandall.opa). Без `target.json` поруч НЕ
6
- # реєструється.
7
- #
8
- # Canonical (rego.mdc): `recommendations` має містити `tsandall.opa`.
9
- #
10
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/extensions.json.snippet.json.
5
+ # `recommendations` subset-of: кожна рекомендація з template має бути у input.
6
+ # Додаткові рекомендації від інших правил дозволені.
11
7
  package rego.vscode_extensions
12
8
 
13
9
  import rego.v1
14
10
 
15
11
  deny contains msg if {
16
- recs := object.get(input, "recommendations", [])
17
- not "tsandall.opa" in {r | some r in recs}
18
- msg := ".vscode/extensions.json: recommendations має містити \"tsandall.opa\" (rego.mdc)"
12
+ some rec in data.template.snippet.recommendations
13
+ not rec in {r | some r in object.get(input, "recommendations", [])}
14
+ msg := sprintf(".vscode/extensions.json: recommendations має містити %q (rego.mdc)", [rec])
19
15
  }
@@ -0,0 +1,6 @@
1
+ {
2
+ "[rego]": {
3
+ "editor.defaultFormatter": "tsandall.opa",
4
+ "editor.formatOnSave": true
5
+ }
6
+ }
@@ -1,38 +1,30 @@
1
1
  # Перевірка `.vscode/settings.json` для rego (rego.mdc).
2
2
  #
3
- # Викликається з `check-rego.mjs` через `runConftestBatch` лише ПІСЛЯ виявлення
4
- # `.rego` файлів у дереві. Глобально без `target.json` поруч (не auto-discoverable через `n-cursor check`).
5
- #
6
- # Canonical (rego.mdc):
7
- # { "[rego]": { "editor.defaultFormatter": "tsandall.opa",
8
- # "editor.formatOnSave": true } }
9
- #
10
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
3
+ # Канон надходить через --data: { "template": { "snippet": ... } }
4
+ # Структура --data сформована з template/settings.json.snippet.json.
5
+ # Snippet — 2-рівнева мапа: <language-block-key>.<setting-key> = <expected>
6
+ # (VS Code-конвенція: ключі `[rego]` і `editor.defaultFormatter`/`editor.formatOnSave`
7
+ # це літеральні string-keys із дужками/крапкою, не вкладені обʼєкти).
11
8
  package rego.vscode_settings
12
9
 
13
10
  import rego.v1
14
11
 
15
- # ── deny: [rego] block ──────────────────────────────────────────────────
16
-
17
- deny contains msg if {
18
- not is_object(object.get(input, "[rego]", null))
19
- msg := concat(" ", [
20
- ".vscode/settings.json: \"[rego]\" має бути обʼєктом з",
21
- "\"editor.defaultFormatter\": \"tsandall.opa\" і",
22
- "\"editor.formatOnSave\": true (rego.mdc)",
23
- ])
24
- }
25
-
12
+ # Leaf-by-leaf: працює коли block присутній і є обʼєктом.
26
13
  deny contains msg if {
27
- rego_block := object.get(input, "[rego]", {})
28
- is_object(rego_block)
29
- object.get(rego_block, "editor.defaultFormatter", null) != "tsandall.opa"
30
- msg := ".vscode/settings.json: \"[rego].editor.defaultFormatter\" має бути \"tsandall.opa\" (rego.mdc)"
14
+ some block_key, expected_inner in data.template.snippet
15
+ inner := object.get(input, block_key, {})
16
+ is_object(inner)
17
+ some leaf_key, expected_value in expected_inner
18
+ actual := object.get(inner, leaf_key, null)
19
+ actual != expected_value
20
+ msg := sprintf(".vscode/settings.json: %s.%s має бути %v (rego.mdc)", [block_key, leaf_key, expected_value])
31
21
  }
32
22
 
23
+ # Block існує, але не обʼєкт (напр. рядок) — окрема помилка типу.
33
24
  deny contains msg if {
34
- rego_block := object.get(input, "[rego]", {})
35
- is_object(rego_block)
36
- object.get(rego_block, "editor.formatOnSave", null) != true
37
- msg := ".vscode/settings.json: \"[rego].editor.formatOnSave\" має бути true (rego.mdc)"
25
+ some block_key in object.keys(data.template.snippet)
26
+ raw := object.get(input, block_key, null)
27
+ raw != null
28
+ not is_object(raw)
29
+ msg := sprintf(".vscode/settings.json: %s має бути обʼєктом (rego.mdc)", [block_key])
38
30
  }
@@ -15,19 +15,21 @@ alwaysApply: false
15
15
  bun run lint-rego
16
16
  ```
17
17
 
18
- Цілі — `npm/policy/`. Інші *.rego поза деревом додай у `LINT_TARGETS` у `npm/rules/rego/js/lint.mjs`.
18
+ Цілі — `npm/rules/` (рекурсивно знаходить `.rego` у `<rule>/policy/<concern>/`). Інші *.rego поза деревом додай у `LINT_TARGETS` у `npm/rules/rego/lint/lint.mjs`.
19
19
 
20
20
  `opa` і `regal` — лише у `PATH`, **не** додавай у `dependencies` / `devDependencies`.
21
21
 
22
22
  ### `package.json`
23
23
 
24
- ```json title="package.json"
25
- {
26
- "scripts": {
27
- "lint-rego": "n-cursor lint-rego"
28
- }
29
- }
30
- ```
24
+ - Канон `scripts.lint-rego`: [package.json.snippet.json](./policy/package_json/template/package.json.snippet.json)
25
+
26
+ ### `.vscode/extensions.json`
27
+
28
+ - Канон `recommendations` має містити `tsandall.opa` (LSP, format-on-save через `opa fmt`): [extensions.json.snippet.json](./policy/vscode_extensions/template/extensions.json.snippet.json)
29
+
30
+ ### `.vscode/settings.json`
31
+
32
+ - Канон `[rego]`-block (`editor.defaultFormatter` + `editor.formatOnSave`): [settings.json.snippet.json](./policy/vscode_settings/template/settings.json.snippet.json)
31
33
 
32
34
  ## Конфіг regal
33
35
 
@@ -1,62 +1,25 @@
1
1
  /**
2
- * FS-частина правила `security` (security.mdc).
2
+ * FS-частина правила `security`.
3
3
  *
4
- * **Що тут лишилося** (FS / cross-file):
5
- * - наявність `package.json` у корені (структуру валідує Rego);
6
- * - наявність `.gitleaks.toml` у корені — нагадування створити з канону `security.mdc`;
7
- * - `.gitleaks.toml` має `useDefault = true` у блоці `[extend]` (інакше дефолтні правила
8
- * gitleaks перетираються і скан стає сліпим до 95% типових витоків).
4
+ * Перевіряє:
5
+ * - наявність `package.json` (структуру валідує Rego);
6
+ * - контекстне pass-повідомлення для JS-концерну.
9
7
  *
10
- * **Що покрила Rego** (`npx \@nitra/cursor check`, `npm/policy/security/package_json/`):
11
- * - `scripts.lint-security` існує і викликає `gitleaks` з `detect`/`git` subcommand;
12
- * - агрегований `scripts.lint` (якщо є) містить `bun run lint-security`;
13
- * - `gitleaks` НЕ у `dependencies` / `devDependencies` (бо це глобальний CLI).
8
+ * Наявність і вміст `.gitleaks.toml` (`[extend].useDefault = true`) тепер
9
+ * перевіряє policy `security.gitleaks`.
14
10
  */
15
11
  import { existsSync } from 'node:fs'
16
- import { readFile } from 'node:fs/promises'
17
12
 
18
13
  import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
19
14
 
20
- const GITLEAKS_CONFIG = '.gitleaks.toml'
21
-
22
- /**
23
- * Перевіряє наявність `.gitleaks.toml` у корені та канонічну вимогу `useDefault = true`
24
- * у блоці `[extend]`. Користувач сам наповнює `[allowlist]` локальними патернами.
25
- * @param {(msg: string) => void} pass callback при успішній перевірці
26
- * @param {(msg: string) => void} fail callback при помилці
27
- * @returns {Promise<void>}
28
- */
29
- async function checkGitleaksConfig(pass, fail) {
30
- if (!existsSync(GITLEAKS_CONFIG)) {
31
- fail(`${GITLEAKS_CONFIG} не знайдено в корені — створи за каноном security.mdc (useDefault = true + [allowlist])`)
32
- return
33
- }
34
- const raw = await readFile(GITLEAKS_CONFIG, 'utf8')
35
- if (!/useDefault\s*=\s*true/u.test(raw)) {
36
- fail(
37
- `${GITLEAKS_CONFIG}: відсутнє \`useDefault = true\` у блоці [extend] — без нього вбудовані ` +
38
- 'gitleaks-правила перетираються і скан стає сліпим (security.mdc)'
39
- )
40
- return
41
- }
42
- pass(`${GITLEAKS_CONFIG} існує і успадковує дефолтні gitleaks-правила (useDefault = true)`)
43
- }
44
-
45
- /**
46
- * Запускає всі FS-перевірки правила security.
47
- * @returns {Promise<number>} 0 — все OK, 1 — є зауваження
48
- */
49
15
  export async function check() {
50
16
  const reporter = createCheckReporter()
51
17
  const { pass, fail } = reporter
52
-
53
18
  if (!existsSync('package.json')) {
54
19
  fail('package.json не знайдено в корені — додай (security.mdc)')
55
20
  return reporter.getExitCode()
56
21
  }
57
- pass('package.json є (структуру перевіряє npx @nitra/cursor check → security.package_json)')
58
-
59
- await checkGitleaksConfig(pass, fail)
60
-
22
+ pass('package.json є (структуру перевіряє Rego)')
23
+ pass('.gitleaks.toml перевіряє npx @nitra/cursor check → security.gitleaks')
61
24
  return reporter.getExitCode()
62
25
  }
@@ -0,0 +1,12 @@
1
+ [extend]
2
+ useDefault = true
3
+
4
+ [allowlist]
5
+ paths = [
6
+ '''(^|/)node_modules(/|$)''',
7
+ '''(^|/)\.git(/|$)''',
8
+ '''(^|/)dist(/|$)''',
9
+ '''(^|/)build(/|$)''',
10
+ '''.*\.lock$''',
11
+ '''.*fixtures?/.*'''
12
+ ]
@@ -0,0 +1,17 @@
1
+ # Перевірка `.gitleaks.toml` для security (security.mdc).
2
+ #
3
+ # Канонічна мінімальна вимога: `[extend].useDefault = true`, щоб локальний
4
+ # конфіг не вимикав стандартні правила gitleaks. Додаткові локальні правила
5
+ # дозволені.
6
+ #
7
+ # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
8
+ # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
9
+ # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
10
+ package security.gitleaks
11
+
12
+ import rego.v1
13
+
14
+ deny contains msg if {
15
+ object.get(object.get(input, "extend", {}), "useDefault", null) != true
16
+ msg := ".gitleaks.toml: [extend].useDefault має бути true (security.mdc)"
17
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@nitra/cursor/schemas/target.json",
3
+ "files": {
4
+ "single": ".gitleaks.toml",
5
+ "required": true
6
+ },
7
+ "missingMessage": ".gitleaks.toml не знайдено — створи за каноном security.mdc"
8
+ }
@@ -1,75 +1,38 @@
1
1
  # Перевірка `package.json` для правила security (security.mdc).
2
- #
3
- # Запуск (локально):
4
- # conftest test package.json -p npm/policy/security \
5
- # --namespace security.package_json
6
- #
7
- # Перевіряє: наявність `scripts.lint-security`, виклик `gitleaks detect`,
8
- # входження `bun run lint-security` у агрегований `scripts.lint` (якщо `lint` є),
9
- # та заборону `gitleaks` у dependencies/devDependencies (інструмент глобальний).
10
- #
11
- # FS-перевірки (наявність `.gitleaks.toml`, `useDefault = true`) — у JS.
12
- #
13
- # Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
14
- # Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
15
- # (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
2
+ # Канон надходить через --data: { "template": { "snippet": ..., "deny": ..., "contains": ... } }
3
+ # Структура --data сформована з template/<target>.{snippet,deny,contains}.json концерну.
16
4
  package security.package_json
17
5
 
18
6
  import rego.v1
19
7
 
20
- gitleaks_pkg := "gitleaks"
21
-
22
- dep_template := concat(" ", [
23
- "package.json: %q не повинен бути в %s —",
24
- "gitleaks встановлюється глобально (security.mdc)",
25
- ])
26
-
27
- # ── deny: scripts.lint-security ──────────────────────────────────────────
28
-
8
+ # ── deny: кожен snippet leaf має співпадати з input ──────────────────────────
29
9
  deny contains msg if {
30
- scripts := object.get(input, "scripts", {})
31
- not "lint-security" in object.keys(scripts)
32
- msg := "package.json: відсутній scripts.lint-security — додай `gitleaks detect --no-banner` (security.mdc)"
10
+ some script_name, expected in data.template.snippet.scripts
11
+ actual := object.get(object.get(input, "scripts", {}), script_name, "")
12
+ actual != expected
13
+ msg := sprintf("package.json: scripts.%s має бути %q (security.mdc)", [script_name, expected])
33
14
  }
34
15
 
16
+ # ── deny: жодного ключа з deny у dependencies/devDependencies ────────────────
35
17
  deny contains msg if {
36
- lint_security := object.get(object.get(input, "scripts", {}), "lint-security", "")
37
- lint_security != ""
38
- not contains(lint_security, "gitleaks")
39
- msg := "package.json: lint-security має викликати `gitleaks` (security.mdc)"
18
+ some pkg, reason in data.template.deny.dependencies
19
+ pkg in object.keys(object.get(input, "dependencies", {}))
20
+ msg := sprintf("package.json: dependencies.%s — %s (security.mdc)", [pkg, reason])
40
21
  }
41
22
 
42
23
  deny contains msg if {
43
- lint_security := object.get(object.get(input, "scripts", {}), "lint-security", "")
44
- contains(lint_security, "gitleaks")
45
- not has_detect_or_git_subcommand(lint_security)
46
- msg := "package.json: lint-security має містити `detect` або `git` як gitleaks-subcommand (security.mdc)"
24
+ some pkg, reason in data.template.deny.devDependencies
25
+ pkg in object.keys(object.get(input, "devDependencies", {}))
26
+ msg := sprintf("package.json: devDependencies.%s — %s (security.mdc)", [pkg, reason])
47
27
  }
48
28
 
49
- # ── deny: агрегований `lint` має кликати `bun run lint-security` ─────────
50
-
29
+ # ── deny: рядкові поля з contains мають містити кожен substring ──────────────
30
+ # Перевіряємо лише наявні поля (якщо `scripts.<name>` відсутній — поле опціональне).
51
31
  deny contains msg if {
52
- "lint-security" in object.keys(object.get(input, "scripts", {}))
53
- lint := object.get(object.get(input, "scripts", {}), "lint", "")
54
- lint != ""
55
- not contains(lint, "bun run lint-security")
56
- msg := "package.json: агрегований `lint` має містити `bun run lint-security` (security.mdc)"
32
+ some script_name, needles in data.template.contains.scripts
33
+ actual := object.get(object.get(input, "scripts", {}), script_name, "")
34
+ actual != ""
35
+ some needle in needles
36
+ not contains(actual, needle)
37
+ msg := sprintf("package.json: scripts.%s має містити %q (security.mdc)", [script_name, needle])
57
38
  }
58
-
59
- # ── deny: `gitleaks` НЕ в dependencies/devDependencies ───────────────────
60
-
61
- deny contains msg if {
62
- gitleaks_pkg in object.keys(object.get(input, "dependencies", {}))
63
- msg := sprintf(dep_template, [gitleaks_pkg, "dependencies"])
64
- }
65
-
66
- deny contains msg if {
67
- gitleaks_pkg in object.keys(object.get(input, "devDependencies", {}))
68
- msg := sprintf(dep_template, [gitleaks_pkg, "devDependencies"])
69
- }
70
-
71
- # ── helpers ──────────────────────────────────────────────────────────────
72
-
73
- # Чи містить рядок subcommand `detect` або `git` (як слово, не як підрядок випадкового шляху).
74
- # `gitleaks detect ...`, `gitleaks git --no-banner`, `gitleaks detect --source=.` — усі OK.
75
- has_detect_or_git_subcommand(s) if regex.match(`\bgitleaks\s+(detect|git)\b`, s)
@@ -0,0 +1 @@
1
+ { "scripts": { "lint": ["bun run lint-security"] } }
@@ -0,0 +1,4 @@
1
+ {
2
+ "dependencies": { "gitleaks": "глобальний CLI — не додавай у dependencies" },
3
+ "devDependencies": { "gitleaks": "глобальний CLI — не додавай у devDependencies" }
4
+ }
@@ -0,0 +1 @@
1
+ { "scripts": { "lint-security": "gitleaks detect --no-banner" } }
@@ -9,14 +9,11 @@ version: '1.1'
9
9
 
10
10
  ## Канон `package.json#scripts`
11
11
 
12
- ```json title="package.json"
13
- {
14
- "scripts": {
15
- "lint-security": "gitleaks detect --no-banner",
16
- "lint": "bun run lint-rego && bun run lint-js && bun run lint-text && bun run lint-security && oxfmt ."
17
- }
18
- }
19
- ```
12
+ - `lint-security` скрипт: [package.json.snippet.json](./policy/package_json/template/package.json.snippet.json)
13
+ - `lint` агрегатор повинен містити: [package.json.contains.json](./policy/package_json/template/package.json.contains.json)
14
+ - Заборонено `gitleaks` у `dependencies`/`devDependencies`: [package.json.deny.json](./policy/package_json/template/package.json.deny.json)
15
+
16
+ **Зауваження:**
20
17
 
21
18
  - `gitleaks detect` — сканує робоче дерево (uncommitted + tracked); швидше і безпечніше для частого `bun run lint`, ніж `gitleaks git`.
22
19
  - `--no-banner` — прибирає ASCII-арт (CI-friendly).
@@ -24,25 +21,9 @@ version: '1.1'
24
21
 
25
22
  ## `.gitleaks.toml` (рекомендована основа)
26
23
 
27
- ```toml title=".gitleaks.toml"
28
- title = "Project gitleaks config"
29
-
30
- [extend]
31
- useDefault = true
32
-
33
- [allowlist]
34
- description = "Файли й шляхи, які навмисно містять test-фікстури з паттернами секретів."
35
- paths = [
36
- '''(^|/)node_modules(/|$)''',
37
- '''(^|/)\.git(/|$)''',
38
- '''(^|/)dist(/|$)''',
39
- '''(^|/)build(/|$)''',
40
- '''.*\.lock$''',
41
- '''.*fixtures?/.*'''
42
- ]
43
- ```
24
+ Канон (допускає розширення в `[allowlist].paths`): [.gitleaks.toml.snippet.toml](./fix/gitleaks/template/.gitleaks.toml.snippet.toml)
44
25
 
45
- `useDefault = true` — НЕ перетирай дефолтний `rules`-масив; реальні винятки лише через `[allowlist]`.
26
+ **Важливо:** `useDefault = true` — НЕ перетирай дефолтний `rules`-масив; реальні винятки лише через `[allowlist]`.
46
27
 
47
28
  ## GitHub Actions (опційно)
48
29
 
@@ -0,0 +1,27 @@
1
+ # TODO:
2
+
3
+ ## semgrep --config p/kubernetes
4
+
5
+ p/security-audit
6
+
7
+ p/secrets
8
+
9
+ p/nodejs
10
+
11
+ p/javascript
12
+
13
+ p/typescript
14
+
15
+ p/docker
16
+
17
+ p/kubernetes
18
+
19
+ ## Trivy
20
+ Найцінніша частина Trivy
21
+
22
+ НЕ image scanning. А:
23
+
24
+
25
+ config scanning
26
+ +
27
+ Kubernetes scanning