@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.
- package/CHANGELOG.md +102 -0
- package/bin/n-cursor.js +4 -2
- package/package.json +4 -2
- package/rules/ga/fix/workflows/check.mjs +6 -109
- package/rules/ga/ga.mdc +10 -15
- package/rules/ga/policy/package_json/package_json.rego +20 -0
- package/rules/ga/policy/package_json/target.json +8 -0
- package/rules/ga/policy/package_json/template/package.json.contains.json +1 -0
- package/rules/ga/policy/vscode_extensions/target.json +8 -0
- package/rules/ga/policy/vscode_extensions/template/extensions.json.snippet.json +1 -0
- package/rules/ga/policy/vscode_extensions/vscode_extensions.rego +18 -0
- package/rules/ga/policy/vscode_settings/target.json +8 -0
- package/rules/ga/policy/vscode_settings/template/settings.json.snippet.json +1 -0
- package/rules/ga/policy/vscode_settings/vscode_settings.rego +22 -0
- package/rules/ga/policy/zizmor_yml/target.json +8 -0
- package/rules/ga/policy/zizmor_yml/template/zizmor.yml.snippet.yml +5 -0
- package/rules/ga/policy/zizmor_yml/zizmor_yml.rego +27 -0
- package/rules/js-lint/fix/tooling/check.mjs +6 -83
- package/rules/js-lint/policy/jscpd/jscpd.rego +38 -0
- package/rules/js-lint/policy/jscpd/target.json +8 -0
- package/rules/js-lint/policy/vscode_extensions/target.json +8 -0
- package/rules/js-lint/policy/vscode_extensions/vscode_extensions.rego +25 -0
- package/rules/rego/lint/lint.mjs +5 -4
- package/rules/rego/policy/package_json/package_json.rego +8 -29
- package/rules/rego/policy/package_json/template/package.json.snippet.json +1 -0
- package/rules/rego/policy/vscode_extensions/template/extensions.json.snippet.json +1 -0
- package/rules/rego/policy/vscode_extensions/vscode_extensions.rego +7 -11
- package/rules/rego/policy/vscode_settings/template/settings.json.snippet.json +6 -0
- package/rules/rego/policy/vscode_settings/vscode_settings.rego +19 -27
- package/rules/rego/rego.mdc +10 -8
- package/rules/security/fix/gitleaks/check.mjs +8 -45
- package/rules/security/fix/gitleaks/template/.gitleaks.toml.snippet.toml +12 -0
- package/rules/security/policy/gitleaks/gitleaks.rego +17 -0
- package/rules/security/policy/gitleaks/target.json +8 -0
- package/rules/security/policy/package_json/package_json.rego +22 -59
- package/rules/security/policy/package_json/template/package.json.contains.json +1 -0
- package/rules/security/policy/package_json/template/package.json.deny.json +4 -0
- package/rules/security/policy/package_json/template/package.json.snippet.json +1 -0
- package/rules/security/security.mdc +7 -26
- package/rules/security/todo.MD +27 -0
- package/rules/vue/fix/packages/check.mjs +7 -64
- package/rules/vue/policy/package_json/package_json.rego +45 -2
- package/rules/vue/vue.mdc +15 -2
- package/scripts/ensure-nitra-cursor-dev-dependencies.mjs +41 -21
- package/scripts/utils/check-mdc-template-refs.mjs +47 -0
- package/scripts/utils/inline-template-links.mjs +60 -0
- package/scripts/utils/run-conftest-batch.mjs +60 -33
- package/scripts/utils/run-rule.mjs +16 -1
- 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,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
|
+
}
|
package/rules/rego/lint/lint.mjs
CHANGED
|
@@ -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/
|
|
22
|
-
*
|
|
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/
|
|
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
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
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
|
-
|
|
17
|
-
not
|
|
18
|
-
msg := ".vscode/extensions.json: recommendations має містити
|
|
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
|
}
|
|
@@ -1,38 +1,30 @@
|
|
|
1
1
|
# Перевірка `.vscode/settings.json` для rego (rego.mdc).
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
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
|
-
#
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
}
|
package/rules/rego/rego.mdc
CHANGED
|
@@ -15,19 +15,21 @@ alwaysApply: false
|
|
|
15
15
|
bun run lint-rego
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Цілі — `npm/policy
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
|
2
|
+
* FS-частина правила `security`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* - наявність `package.json`
|
|
6
|
-
* -
|
|
7
|
-
* - `.gitleaks.toml` має `useDefault = true` у блоці `[extend]` (інакше дефолтні правила
|
|
8
|
-
* gitleaks перетираються і скан стає сліпим до 95% типових витоків).
|
|
4
|
+
* Перевіряє:
|
|
5
|
+
* - наявність `package.json` (структуру валідує Rego);
|
|
6
|
+
* - контекстне pass-повідомлення для JS-концерну.
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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 є (структуру перевіряє
|
|
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,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
|
+
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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:
|
|
50
|
-
|
|
29
|
+
# ── deny: рядкові поля з contains мають містити кожен substring ──────────────
|
|
30
|
+
# Перевіряємо лише наявні поля (якщо `scripts.<name>` відсутній — поле опціональне).
|
|
51
31
|
deny contains msg if {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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 @@
|
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|