@nitra/cursor 1.11.17 → 1.13.1
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/hooks/capture-decisions.sh +7 -2
- package/.claude-template/hooks/normalize-decisions.sh +7 -1
- package/CHANGELOG.md +46 -0
- package/bin/n-cursor.js +3 -1
- package/package.json +1 -1
- package/rules/abie/abie.mdc +1 -9
- package/rules/adr/adr.mdc +25 -16
- package/rules/adr/fix/hooks/check.mjs +70 -0
- package/rules/bun/bun.mdc +3 -20
- package/rules/capacitor/capacitor.mdc +4 -8
- package/rules/changelog/changelog.mdc +1 -5
- package/rules/ci4/ci4.mdc +0 -4
- package/rules/docker/docker.mdc +1 -19
- package/rules/ga/ga.mdc +1 -26
- package/rules/graphql/graphql.mdc +0 -8
- package/rules/hasura/hasura.mdc +0 -6
- package/rules/image-avif/image-avif.mdc +1 -13
- package/rules/image-compress/image-compress.mdc +7 -33
- package/rules/js-bun-db/js-bun-db.mdc +3 -6
- package/rules/js-bun-redis/js-bun-redis.mdc +3 -6
- package/rules/js-lint/js-lint.mdc +3 -16
- package/rules/js-mssql/js-mssql.mdc +3 -4
- package/rules/js-run/js-run.mdc +3 -10
- package/rules/k8s/k8s.mdc +2 -21
- package/rules/nginx-default-tpl/nginx-default-tpl.mdc +0 -25
- package/rules/npm-module/npm-module.mdc +4 -9
- package/rules/rego/rego.mdc +2 -38
- package/rules/security/auto.md +1 -0
- package/rules/security/fix/gitleaks/check.mjs +62 -0
- package/rules/security/policy/package_json/package_json.rego +75 -0
- package/rules/security/policy/package_json/target.json +4 -0
- package/rules/security/security.mdc +77 -0
- package/rules/style-lint/style-lint.mdc +0 -23
- package/rules/tauri/tauri.mdc +3 -6
- package/scripts/auto-rules.mjs +2 -0
- package/scripts/sync-claude-config.mjs +133 -4
|
@@ -5,11 +5,9 @@ globs: "**/*.{png,jpg,jpeg,gif,svg}"
|
|
|
5
5
|
alwaysApply: false
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
CLI [`@nitra/minify-image`](https://www.npmjs.com/package/@nitra/minify-image) (≥ **3.3.1**) запускається через `npx`
|
|
8
|
+
CLI [`@nitra/minify-image`](https://www.npmjs.com/package/@nitra/minify-image) (≥ **3.3.1**) запускається через `npx` і **не** додається в `dependencies` / `devDependencies`. Канонічний `lint-image` — авто-оптимізація з прапорцем `--write`: стискає raster/SVG на місці. **AVIF-генерація (`--avif`) у `lint-image` заборонена** — її виконує окреме правило `image-avif`.
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Перевірка лише локальна — у CI `lint-image` не запускаємо (sharp/svgo тягнуть бінарні залежності, цінність на ubuntu-runner-ах нижча за час прогону). Окремий workflow `lint-image.yml` створювати не треба.
|
|
10
|
+
Перевірка лише локальна — у CI `lint-image` не запускаємо. Окремий workflow `lint-image.yml` створювати не треба.
|
|
13
11
|
|
|
14
12
|
## `package.json`
|
|
15
13
|
|
|
@@ -24,36 +22,12 @@ CLI [`@nitra/minify-image`](https://www.npmjs.com/package/@nitra/minify-image) (
|
|
|
24
22
|
|
|
25
23
|
Якщо в `package.json` уже є агрегований `lint`, додай у його ланцюжок `bun run lint-image` (як `bun run lint-text`, `bun run lint-js`, `bun run lint-ga`). Так розробник, що локально гонить `bun run lint`, перед фіксацією одразу бачить, чи зросли зображення.
|
|
26
24
|
|
|
27
|
-
##
|
|
28
|
-
|
|
29
|
-
Починаючи з `@nitra/minify-image` **3.2.0** кеш розбитий на два файли з різною семантикою:
|
|
30
|
-
|
|
31
|
-
### `.n-minify-image.tsv` — source of truth у git
|
|
32
|
-
|
|
33
|
-
У корені сканованого каталогу. Формат: `<rel-path>\t<sha1-hex>\t<originalSize>\t<size>`.
|
|
34
|
-
|
|
35
|
-
Slow-path і джерело даних для `Project lifetime savings`. **Має бути в git** — після `git clone` чи `git checkout` (mtime скидається на час checkout-у) CLI читає файл, рахує SHA-1 і порівнює зі збереженим у TSV хешем; на match локальний mtime-кеш зігрівається без reprocess. Рядки відсортовані алфавітно, hash і size змінюються лише при реальній зміні контенту — diff чистий.
|
|
36
|
-
|
|
37
|
-
### `node_modules/.cache/@nitra/minify-image/mtime.tsv` — локальний fast-path
|
|
38
|
-
|
|
39
|
-
Формат: `<rel-path>\t<mtime>\t<size>`. При збігу `(size, mtime)` CLI пропускає файл без читання — константа per-file.
|
|
25
|
+
## Кеш
|
|
40
26
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Старий єдиний `.minify-image-cache.tsv` (4 колонки `path\tmtime\toriginalSize\tsize`, зазвичай у `.gitignore`) автоматично читається при першому запуску для seed-у `originalSize` у `.n-minify-image.tsv` (lifetime savings не скидається). Після цього старий файл видаляють вручну:
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
git rm --cached .minify-image-cache.tsv 2>/dev/null || true
|
|
49
|
-
rm -f .minify-image-cache.tsv
|
|
50
|
-
# прибери відповідний рядок з .gitignore, якщо був
|
|
51
|
-
```
|
|
27
|
+
- **`.n-minify-image.tsv`** у корені сканованого каталогу — **має бути в git** (source of truth для sha1-перевірок і lifetime savings). У `.gitignore` його не додавай.
|
|
28
|
+
- **`node_modules/.cache/@nitra/minify-image/mtime.tsv`** — локальний fast-path; авто-gitignored через `node_modules/`.
|
|
29
|
+
- Застарілий `.minify-image-cache.tsv` у корені — видали (`git rm --cached`, прибери рядок з `.gitignore`).
|
|
52
30
|
|
|
53
31
|
## Заборонені залежності
|
|
54
32
|
|
|
55
|
-
`@nitra/minify-image` не повинен зʼявлятися ні в `dependencies`, ні в `devDependencies`
|
|
56
|
-
|
|
57
|
-
## Перевірка
|
|
58
|
-
|
|
59
|
-
`npx @nitra/cursor check image-compress` (охоплює `lint-image` з обовʼязковими `--src=.`, `--write` і **забороненим** `--avif`; агрегований `lint`; заборону `@nitra/minify-image` у залежностях; `.n-minify-image.tsv` НЕ в `.gitignore` — має бути в git; відсутність застарілого `.minify-image-cache.tsv` у корені).
|
|
33
|
+
`@nitra/minify-image` не повинен зʼявлятися ні в `dependencies`, ні в `devDependencies` — CLI запускається лише через `npx`. Якщо потрібен явний пін — у самому виклику (`npx @nitra/minify-image@^3 --src=. --write`).
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Використання pg / mysql2 / Bun SQL у Node.js та Bun
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
globs: "**/package.json,**/src/conn/**"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
version: '1.7'
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
## Підтримувані версії баз даних
|
|
@@ -250,7 +251,3 @@ function getUser(id) {
|
|
|
250
251
|
Якщо в коді з'явився `import { sql } from 'bun'`, то `pg`, `pg-format` та `mysql2` мають бути прибрані і з `dependencies`, і з імпортів — щоб не лишалось двох паралельних шляхів до БД та ручного форматування поряд із параметризованими template literal.
|
|
251
252
|
|
|
252
253
|
Те саме стосується **локальних шимів**: будь-який модуль, що експортує `format`, `pgRead`, `pgWrite`, `query(text, params)`, `quoteLiteral`, `quoteIdent` як обгортку над `sql.unsafe(...)`, потрібно переписати — всі call-site на tagged template, сам шим видалити (див. `## pg-format: повне видалення, без шимів`).
|
|
253
|
-
|
|
254
|
-
## Перевірка
|
|
255
|
-
|
|
256
|
-
`npx @nitra/cursor check js-bun-db`.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Використання Redis/Valkey з Bun
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
globs: "**/package.json,**/src/conn/**"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
version: '1.1'
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
## Підтримувані версії redis
|
|
@@ -15,7 +16,3 @@ Redis 7.2+
|
|
|
15
16
|
- Видалити з `dependencies`: `ioredis`, `node-redis`.
|
|
16
17
|
- Видалити з коду: усі `import` / `require` цих пакетів та власні обгортки над ними.
|
|
17
18
|
- Замінити на `import { redis } from 'bun'`
|
|
18
|
-
|
|
19
|
-
## Перевірка
|
|
20
|
-
|
|
21
|
-
`npx @nitra/cursor check js-bun-redis`.
|
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Перевірка JavaScript коду
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
globs: "**/{.oxlintrc.json,eslint.config.js,.jscpd.json,knip.json,package.json},**/*.{js,mjs,cjs,jsx,ts,tsx}"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
version: '1.22'
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
**oxlint**, **ESLint**, **jscpd**, **knip**. У скрипті **`lint-js`** і в CI — **`bunx oxlint`**, **`bunx eslint`**, **`bunx jscpd`**, **`bunx knip`** (у CI без **`--fix`** для oxlint/eslint — див. приклад workflow нижче). Без **prettier** і **@nitra/prettier-config**. У **`devDependencies`** має бути **`@nitra/eslint-config` мінімум `^3.9.2`** (з **3.8.0** правило `no-restricted-syntax` забороняє `for...in`; з **3.9.2** у `getConfig` вбудовано ignore для **`**/adr/**`** — ADR-документи не валідуються ESLint, локально цей glob додавати не потрібно; також транзитивно йде **`@e18e/eslint-plugin`** для oxlint); пакет **`@e18e/eslint-plugin`** окремо не додавай. Пакети oxlint/eslint/jscpd/knip не додавай без потреби монорепо.
|
|
8
9
|
|
|
9
|
-
```json title=".vscode/extensions.json"
|
|
10
|
-
{
|
|
11
|
-
"recommendations": [
|
|
12
|
-
"dbaeumer.vscode-eslint",
|
|
13
|
-
"github.vscode-github-actions",
|
|
14
|
-
"oxc.oxc-vscode"
|
|
15
|
-
]
|
|
16
|
-
}
|
|
17
|
-
```
|
|
18
|
-
|
|
19
10
|
У кожному **`package.json`** проєкту (корінь і всі workspace-пакети) має бути **`"type": "module"`** — весь код у ESM.
|
|
20
11
|
|
|
21
12
|
```json title="package.json"
|
|
@@ -189,7 +180,3 @@ for (const item of arr) {
|
|
|
189
180
|
## Тести
|
|
190
181
|
|
|
191
182
|
Проєкт має бути покритий unit-тестами (**Bun test**). Код: синтаксис Node **24+**, **top level await** (узгоджено з `engines.node` у `package.json`).
|
|
192
|
-
|
|
193
|
-
## Перевірка
|
|
194
|
-
|
|
195
|
-
`npx @nitra/cursor check js-lint`
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Використання mssql в nodejs
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
globs: "**/package.json,**/src/conn/mssql-*"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
version: '1.4'
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
## Підтримувана версія SQL Server
|
|
@@ -211,5 +212,3 @@ await pool.request().query`
|
|
|
211
212
|
Допустимі парсери: `parseInt(...)`, `parseFloat(...)`, `Number(...)`, `BigInt(...)` або унарний `+x`. Літеральні масиви чисел (`[1, 2, 3]`) теж безпечні — без парсера, але без жодних рядків.
|
|
212
213
|
|
|
213
214
|
Це правило діє і для безпечного `pool.request().query\`...\`` (де mssql сам параметризує масив), і поготів для `pool.query(String.raw\`...\`)` чи `pool.query(\`...\`)`, де такий парсинг — єдиний бар'єр.
|
|
214
|
-
|
|
215
|
-
Перевірка: `npx @nitra/cursor check js-mssql`.
|
package/rules/js-run/js-run.mdc
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Це правила для backend проектів на JavaScript/Node.js, сюди входять і job і WEB сервери.
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
globs: "**/package.json,**/jsconfig.json,**/src/**/*.{js,mjs,cjs,ts,tsx}"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
version: '1.8'
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
## Область застосування
|
|
@@ -209,12 +210,4 @@ await setTimeout(500)
|
|
|
209
210
|
|
|
210
211
|
Імпорт `setTimeout` з `node:timers/promises` затіняє глобальний таймер у файлі — якщо в тому ж файлі потрібен callback-варіант, імпортуй його під іншим іменем (наприклад, `import { setTimeout as setTimeoutCb } from 'node:timers'`).
|
|
211
212
|
|
|
212
|
-
## Перевірка
|
|
213
|
-
|
|
214
|
-
`npx @nitra/cursor check js-run` — зокрема для кожного backend workspace-пакета з каталогом **`src/`** перевіряє наявність **`jsconfig.json`** і збіг вмісту з каноном вище. Додатково для файлів у каталозі `#conn/` (за замовчуванням `src/conn/`) перевіряється:
|
|
215
|
-
|
|
216
|
-
- **basename файла** відповідає канону: `ql-<id>` (GraphQL) / `(pg|mysql|mssql)-(read|write)[-<id>]` (БД), kebab-case `[a-z0-9-]`;
|
|
217
|
-
- **відсутній `export default`** — лише іменований експорт;
|
|
218
|
-
- **імʼя експорту** дорівнює camelCase від basename (`pg-write-contract.js` → `export const pgWriteContract`).
|
|
219
|
-
|
|
220
213
|
Файли `index.*` у conn-каталозі пропускаються як можливий reexport-барель.
|
package/rules/k8s/k8s.mdc
CHANGED
|
@@ -27,12 +27,9 @@ alwaysApply: false
|
|
|
27
27
|
|
|
28
28
|
## lint-k8s: kubeconform і kubescape
|
|
29
29
|
|
|
30
|
-
Окремо від modeline `$schema` у редакторі варто ганяти CLI-лінтери по тих самих дерев’ях **`…/k8s`**.
|
|
30
|
+
Окремо від modeline `$schema` у редакторі варто ганяти CLI-лінтери (**kubeconform** і **kubescape**) по тих самих дерев’ях **`…/k8s`**.
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
- **[kubescape](https://github.com/kubescape/kubescape#readme)** — сканування misconfiguration / compliance (NSA-CISA, MITRE ATT&CK, CIS тощо) по файлах, Helm, Kustomize або кластеру.
|
|
34
|
-
|
|
35
|
-
**Залежності:** виконувані файли kubeconform і kubescape у **PATH**; не додавай їх у **devDependencies** npm (аналогія до `v8r` у `n-text.mdc`). Локально: наприклад `brew install kubeconform kubescape` або релізи з GitHub.
|
|
32
|
+
**Залежності:** виконувані файли kubeconform і kubescape у **PATH**; не додавай їх у **devDependencies**.
|
|
36
33
|
|
|
37
34
|
**Версія Kubernetes для kubeconform** має відповідати PIN yannh у цьому правилі та в **`check-k8s.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 ще без публічної схеми.
|
|
38
35
|
|
|
@@ -645,18 +642,6 @@ patch: |-
|
|
|
645
642
|
value: 2
|
|
646
643
|
```
|
|
647
644
|
|
|
648
|
-
## Перевірка
|
|
649
|
-
|
|
650
|
-
**`npx @nitra/cursor check k8s`** — програмні критерії в **JSDoc на початку** **`npm/scripts/check-k8s.mjs`**. Якщо під **`k8s`** немає **`*.yaml`** — крок пропущено. Канон **`$schema`** для редактора — розділ **«Визначення схеми YAML`** нижче.
|
|
651
|
-
|
|
652
|
-
**Не входить у check k8s:** **kubeconform** / **kubescape** — це **`bun run lint-k8s`**.
|
|
653
|
-
|
|
654
|
-
## Коли застосовувати (агентам)
|
|
655
|
-
|
|
656
|
-
- Після змін у k8s YAML: **`npx @nitra/cursor check k8s`** і за наявності правила — **`bun run lint-k8s`**.
|
|
657
|
-
- Оновив **`apiVersion` / `kind`** — підправ **перший** рядок **`$schema`** (див. **Визначення схеми YAML**).
|
|
658
|
-
- Дотримуйся **Kustomize** з цього правила; деталі **namespace** / графа ресурсів — **check k8s** + підказки в JSDoc скрипта.
|
|
659
|
-
|
|
660
645
|
## Визначення схеми YAML (канон)
|
|
661
646
|
|
|
662
647
|
Орієнтир — **перший документ** (до наступного `---`).
|
|
@@ -702,7 +687,3 @@ patch: |-
|
|
|
702
687
|
## Багатодокументні YAML
|
|
703
688
|
|
|
704
689
|
Одна схема на файл; скрипт звіряє **перший** документ. Інші `kind` у тому ж файлі — розділи файли або узгодь у рев’ю.
|
|
705
|
-
|
|
706
|
-
## Редактор
|
|
707
|
-
|
|
708
|
-
Для `$schema` у VS Code / Cursor: **Red Hat YAML** (`redhat.vscode-yaml`) — за потреби в **`.vscode/extensions.json`**.
|
|
@@ -5,8 +5,6 @@ globs: "**/default.{conf.template,tpl.conf}"
|
|
|
5
5
|
alwaysApply: false
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
> **Автоматична міграція:** `npx @nitra/cursor check nginx-default-tpl` автоматично перейменовує `default.tpl.conf` → `default.conf.template` (або перезаписує вміст, якщо обидва файли існують). Якщо шаблон відсутній — перевірка пропускається.
|
|
9
|
-
|
|
10
8
|
default.conf.template повинен виглядати так:
|
|
11
9
|
|
|
12
10
|
```nginx
|
|
@@ -122,26 +120,3 @@ RUN NAMES=$(sed -nE '/^\s*[#;]/d; /^\s*$/d; s/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=.*
|
|
|
122
120
|
```
|
|
123
121
|
|
|
124
122
|
Якщо у конфігураційних файлах *.ini є змінні які відсутні в default.conf.template, то їх потрібно вилучити.
|
|
125
|
-
|
|
126
|
-
в файлі .vscode/extensions.json є налаштування для NGINX Configuration Language Support:
|
|
127
|
-
|
|
128
|
-
```json title=".vscode/extensions.json"
|
|
129
|
-
{
|
|
130
|
-
"recommendations": ["ahmadalli.vscode-nginx-conf"]
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
в файлі .vscode/settings.json є налаштування для NGINX Configuration Language Support:
|
|
135
|
-
|
|
136
|
-
```json title=".vscode/settings.json"
|
|
137
|
-
{
|
|
138
|
-
"editor.formatOnSave": true,
|
|
139
|
-
"[nginx]": {
|
|
140
|
-
"editor.defaultFormatter": "ahmadalli.vscode-nginx-conf"
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
## Перевірка
|
|
146
|
-
|
|
147
|
-
`npx @nitra/cursor check nginx-default-tpl`
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Оформлення репозиторію для npm модуля
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
globs: "npm/**,**/package.json,**/hk.pkl,.github/workflows/npm-publish.yml,**/tsconfig*.json"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
version: '1.13'
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
Bun monorepo: workspace **`npm/`**, кореневий **`package.json`**, **`.github/workflows/`**; опційно **`demo/`**.
|
|
@@ -68,9 +69,7 @@ bunx -p typescript tsc src/**/*.js --declaration --allowJs --emitDeclarationOnly
|
|
|
68
69
|
|
|
69
70
|
## CHANGELOG
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Найновіша версія — **перша** секція **`## [version]`** у файлі (зверху після заголовка). Вона **має збігатися** з полем **`version`** у **`npm/package.json`** — це перевіряє **`npx @nitra/cursor check npm-module`**.
|
|
72
|
+
Найновіша версія — **перша** секція **`## [version]`** у файлі (зверху після заголовка). Вона **має збігатися** з полем **`version`** у **`npm/package.json`**.
|
|
74
73
|
|
|
75
74
|
## npm publish
|
|
76
75
|
|
|
@@ -112,7 +111,3 @@ jobs:
|
|
|
112
111
|
with:
|
|
113
112
|
package: npm/package.json
|
|
114
113
|
```
|
|
115
|
-
|
|
116
|
-
## Перевірка
|
|
117
|
-
|
|
118
|
-
`npx @nitra/cursor check npm-module` — зокрема узгодженість першої секції **`npm/CHANGELOG.md`** з **`version`** у **`npm/package.json`** і нагадування про bump при незакомічених змінах під **`npm/`** (через `git`).
|
package/rules/rego/rego.mdc
CHANGED
|
@@ -9,49 +9,15 @@ alwaysApply: false
|
|
|
9
9
|
|
|
10
10
|
Синтаксичні правила (`rego.v1`, `import rego.v1`, заборона legacy v0) — у `conftest.mdc` (alwaysApply). Цей файл — про **інструментарій**: VS Code, лінтери, форматування.
|
|
11
11
|
|
|
12
|
-
## VS Code
|
|
13
|
-
|
|
14
|
-
Розширення `tsandall.opa` (від автора OPA): підсвічування, hover, go-to-definition, оцінка виразів і `format-on-save` через `opa fmt`. Працює лише за наявності `opa` у `PATH` — встановити нижче.
|
|
15
|
-
|
|
16
|
-
```json title=".vscode/extensions.json"
|
|
17
|
-
{
|
|
18
|
-
"recommendations": ["tsandall.opa"]
|
|
19
|
-
}
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
```json title=".vscode/settings.json"
|
|
23
|
-
{
|
|
24
|
-
"[rego]": {
|
|
25
|
-
"editor.defaultFormatter": "tsandall.opa",
|
|
26
|
-
"editor.formatOnSave": true
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
`opa.checkOnSave` за замовчуванням увімкнено в розширенні — діагностика від `opa check` показується в редакторі, тож синтаксичні/типові помилки видно одразу, без запуску `lint-rego`.
|
|
32
|
-
|
|
33
12
|
## Перевірка
|
|
34
13
|
|
|
35
14
|
```bash
|
|
36
15
|
bun run lint-rego
|
|
37
16
|
```
|
|
38
17
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
1. **preflight** — наявність `opa` і `regal` у `PATH`; якщо хоча б одного нема — exit 1 з підказкою встановлення;
|
|
42
|
-
2. `opa check --strict <targets>` — компіляція з типами та `--strict` (мертвий код, неоднозначні правила, незадекларовані змінні);
|
|
43
|
-
3. `regal lint <targets>` — статичний лінтер Rego ([Styra Regal](https://docs.styra.com/regal)): ловить v0-синтаксис, неявні set-rules і відхилення від `rego.v1`, плюс bugs/idiomatic/style-правила.
|
|
18
|
+
Цілі — `npm/policy/`. Інші *.rego поза деревом додай у `LINT_TARGETS` у `npm/rules/rego/js/lint.mjs`.
|
|
44
19
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
### Встановлення інструментів
|
|
48
|
-
|
|
49
|
-
- macOS: `brew install opa regal`
|
|
50
|
-
- Linux/Windows:
|
|
51
|
-
- opa — <https://www.openpolicyagent.org/docs/latest/#1-download-opa>
|
|
52
|
-
- regal — <https://docs.styra.com/regal#installation>
|
|
53
|
-
|
|
54
|
-
Обидва — лише в `PATH`, **не** додавай у `dependencies` / `devDependencies` (як `shellcheck` у `text.mdc`).
|
|
20
|
+
`opa` і `regal` — лише у `PATH`, **не** додавай у `dependencies` / `devDependencies`.
|
|
55
21
|
|
|
56
22
|
### `package.json`
|
|
57
23
|
|
|
@@ -63,8 +29,6 @@ bun run lint-rego
|
|
|
63
29
|
}
|
|
64
30
|
```
|
|
65
31
|
|
|
66
|
-
У кореневому `lint` (з `text.mdc` і дотичних) включай `bun run lint-rego` — щоб локальний прогін співпадав з CI.
|
|
67
|
-
|
|
68
32
|
## Конфіг regal
|
|
69
33
|
|
|
70
34
|
У корені — `.regal/config.yaml`. Дозволено вимикати окремі правила під специфіку репо (наприклад, conftest-полісі — `deny`-правила як де-факто entrypoint-и):
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
завжди
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FS-частина правила `security` (security.mdc).
|
|
3
|
+
*
|
|
4
|
+
* **Що тут лишилося** (FS / cross-file):
|
|
5
|
+
* - наявність `package.json` у корені (структуру валідує Rego);
|
|
6
|
+
* - наявність `.gitleaks.toml` у корені — нагадування створити з канону `security.mdc`;
|
|
7
|
+
* - `.gitleaks.toml` має `useDefault = true` у блоці `[extend]` (інакше дефолтні правила
|
|
8
|
+
* gitleaks перетираються і скан стає сліпим до 95% типових витоків).
|
|
9
|
+
*
|
|
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).
|
|
14
|
+
*/
|
|
15
|
+
import { existsSync } from 'node:fs'
|
|
16
|
+
import { readFile } from 'node:fs/promises'
|
|
17
|
+
|
|
18
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
19
|
+
|
|
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
|
+
export async function check() {
|
|
50
|
+
const reporter = createCheckReporter()
|
|
51
|
+
const { pass, fail } = reporter
|
|
52
|
+
|
|
53
|
+
if (!existsSync('package.json')) {
|
|
54
|
+
fail('package.json не знайдено в корені — додай (security.mdc)')
|
|
55
|
+
return reporter.getExitCode()
|
|
56
|
+
}
|
|
57
|
+
pass('package.json є (структуру перевіряє npx @nitra/cursor check → security.package_json)')
|
|
58
|
+
|
|
59
|
+
await checkGitleaksConfig(pass, fail)
|
|
60
|
+
|
|
61
|
+
return reporter.getExitCode()
|
|
62
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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).
|
|
16
|
+
package security.package_json
|
|
17
|
+
|
|
18
|
+
import rego.v1
|
|
19
|
+
|
|
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
|
+
|
|
29
|
+
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)"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
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)"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
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)"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# ── deny: агрегований `lint` має кликати `bun run lint-security` ─────────
|
|
50
|
+
|
|
51
|
+
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)"
|
|
57
|
+
}
|
|
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,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Локальний та CI-секюріті-лінт через gitleaks — скрипт `lint-security`, `.gitleaks.toml`, інтеграція в агрегований `lint`
|
|
3
|
+
globs: "**/.gitleaks.toml,**/package.json,**/.github/workflows/**/*.yml"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
version: '1.1'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
[gitleaks](https://github.com/gitleaks/gitleaks) — глобальний CLI (як `shellcheck`, `conftest`); **не** додавай до `dependencies`/`devDependencies`.
|
|
9
|
+
|
|
10
|
+
## Канон `package.json#scripts`
|
|
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
|
+
```
|
|
20
|
+
|
|
21
|
+
- `gitleaks detect` — сканує робоче дерево (uncommitted + tracked); швидше і безпечніше для частого `bun run lint`, ніж `gitleaks git`.
|
|
22
|
+
- `--no-banner` — прибирає ASCII-арт (CI-friendly).
|
|
23
|
+
- Позиція в `lint`: за конвенцією після інших `lint-*` і перед `oxfmt`.
|
|
24
|
+
|
|
25
|
+
## `.gitleaks.toml` (рекомендована основа)
|
|
26
|
+
|
|
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
|
+
```
|
|
44
|
+
|
|
45
|
+
`useDefault = true` — НЕ перетирай дефолтний `rules`-масив; реальні винятки лише через `[allowlist]`.
|
|
46
|
+
|
|
47
|
+
## GitHub Actions (опційно)
|
|
48
|
+
|
|
49
|
+
Окремий workflow не обовʼязковий — `lint-security` уже виконується через агрегований `lint`. Для fast-feedback окремо:
|
|
50
|
+
|
|
51
|
+
```yaml title=".github/workflows/lint-security.yml"
|
|
52
|
+
name: Lint Security
|
|
53
|
+
|
|
54
|
+
on:
|
|
55
|
+
push:
|
|
56
|
+
branches: [dev, main]
|
|
57
|
+
pull_request:
|
|
58
|
+
branches: [dev, main]
|
|
59
|
+
|
|
60
|
+
concurrency:
|
|
61
|
+
group: ${{ github.ref }}-${{ github.workflow }}
|
|
62
|
+
cancel-in-progress: true
|
|
63
|
+
|
|
64
|
+
jobs:
|
|
65
|
+
security:
|
|
66
|
+
runs-on: ubuntu-latest
|
|
67
|
+
permissions:
|
|
68
|
+
contents: read
|
|
69
|
+
steps:
|
|
70
|
+
- uses: actions/checkout@v6
|
|
71
|
+
with:
|
|
72
|
+
persist-credentials: false
|
|
73
|
+
fetch-depth: 0
|
|
74
|
+
- uses: gitleaks/gitleaks-action@v2
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Для повного скану git-історії потрібен `fetch-depth: 0`.
|
|
@@ -12,25 +12,6 @@ alwaysApply: false
|
|
|
12
12
|
- **Запуск stylelint:** лише **`npx stylelint`**. Локально — через скрипт **`lint-style`** (`bun run lint-style`); у **GitHub Actions** у кроці **`run`** викликай `npx stylelint '**/*.{css,scss,vue}' --fix` напряму (не через **`bun run lint-style`**). Не використовуй **`bunx stylelint`**. Після змін запускай **`bun run lint-style`** і виправляй усе, що лишилось після auto-fix; за потреби — повний `bun run lint` (навичка **`/n-lint`**).
|
|
13
13
|
- **Не розширюй винятки:** не додавай зайві **`stylelint-disable`** без потреби; краще підлаштувати стилі під правила проєкту.
|
|
14
14
|
|
|
15
|
-
**VSCode:** у **`.vscode/extensions.json`** рекомендуй **`stylelint.vscode-stylelint`**. У **`.vscode/settings.json`** вимкни вбудовану валідацію CSS/SCSS/Less і увімкни явні code actions:
|
|
16
|
-
|
|
17
|
-
```json title=".vscode/extensions.json"
|
|
18
|
-
{
|
|
19
|
-
"recommendations": ["stylelint.vscode-stylelint"]
|
|
20
|
-
}
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
```json title=".vscode/settings.json"
|
|
24
|
-
{
|
|
25
|
-
"css.validate": false,
|
|
26
|
-
"less.validate": false,
|
|
27
|
-
"scss.validate": false,
|
|
28
|
-
"editor.codeActionsOnSave": {
|
|
29
|
-
"source.fixAll": "explicit"
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
15
|
**`package.json`:**
|
|
35
16
|
|
|
36
17
|
```json title="package.json"
|
|
@@ -94,7 +75,3 @@ jobs:
|
|
|
94
75
|
```text title=".stylelintignore"
|
|
95
76
|
dist/
|
|
96
77
|
```
|
|
97
|
-
|
|
98
|
-
## Перевірка
|
|
99
|
-
|
|
100
|
-
`npx @nitra/cursor check style-lint`
|
package/rules/tauri/tauri.mdc
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: Tauri
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
globs: "**/src-tauri/**,**/tauri.conf.json"
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
version: '1.1'
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
|
|
@@ -13,7 +14,3 @@ version: '1.0'
|
|
|
13
14
|
"rust-lang.rust-analyzer"]
|
|
14
15
|
}
|
|
15
16
|
```
|
|
16
|
-
|
|
17
|
-
## Перевірка
|
|
18
|
-
|
|
19
|
-
`npx @nitra/cursor check tauri`
|
package/scripts/auto-rules.mjs
CHANGED
|
@@ -47,6 +47,7 @@ export const AUTO_RULE_ORDER = Object.freeze([
|
|
|
47
47
|
'npm-module',
|
|
48
48
|
'php',
|
|
49
49
|
'rego',
|
|
50
|
+
'security',
|
|
50
51
|
'style-lint',
|
|
51
52
|
'text',
|
|
52
53
|
'vue'
|
|
@@ -659,6 +660,7 @@ export async function detectAutoRules({
|
|
|
659
660
|
}
|
|
660
661
|
}
|
|
661
662
|
addRule('adr')
|
|
663
|
+
addRule('security')
|
|
662
664
|
addRule('text')
|
|
663
665
|
if (facts.hasVueSource) {
|
|
664
666
|
addRule('vue')
|