@nitra/cursor 1.13.15 → 1.13.26
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/normalize-decisions.sh +1 -1
- package/CHANGELOG.md +85 -0
- package/package.json +1 -1
- package/rules/abie/abie.mdc +1 -4
- package/rules/abie/policy/clean_merged_ignore_branches/clean_merged_ignore_branches.rego +21 -40
- package/rules/abie/policy/clean_merged_ignore_branches/template/clean-merged-branch.yml.snippet.yml +9 -0
- package/rules/adr/adr.mdc +2 -2
- package/rules/docker/docker.mdc +2 -50
- package/rules/docker/policy/lint_docker_yml/lint_docker_yml.rego +33 -47
- package/rules/docker/policy/lint_docker_yml/template/lint-docker.yml.snippet.yml +41 -0
- package/rules/docker/policy/package_json/package_json.rego +11 -28
- package/rules/docker/policy/package_json/template/package.json.snippet.json +1 -0
- package/rules/image-avif/policy/package_json/package_json.rego +21 -35
- package/rules/image-avif/policy/package_json/template/package.json.deny.json +5 -0
- package/rules/image-compress/image-compress.mdc +2 -8
- package/rules/image-compress/policy/package_json/package_json.rego +24 -65
- package/rules/image-compress/policy/package_json/template/package.json.contains.json +5 -0
- package/rules/image-compress/policy/package_json/template/package.json.deny.json +8 -0
- package/rules/js-bun-db/policy/package_json/package_json.rego +8 -21
- package/rules/js-bun-db/policy/package_json/template/package.json.deny.json +7 -0
- package/rules/js-bun-redis/policy/package_json/package_json.rego +8 -30
- package/rules/js-bun-redis/policy/package_json/template/package.json.deny.json +12 -0
- package/rules/js-lint/policy/jscpd/jscpd.rego +29 -23
- package/rules/js-lint/policy/jscpd/template/.jscpd.json.snippet.json +6 -0
- package/rules/js-lint/policy/lint_js_yml/lint_js_yml.rego +39 -47
- package/rules/js-lint/policy/lint_js_yml/template/lint-js.yml.snippet.yml +44 -0
- package/rules/js-lint/policy/package_json/package_json.rego +22 -42
- package/rules/js-lint/policy/package_json/template/package.json.snippet.json +6 -0
- package/rules/js-lint/policy/vscode_extensions/template/extensions.json.snippet.json +7 -0
- package/rules/js-lint/policy/vscode_extensions/vscode_extensions.rego +4 -17
- package/rules/js-run/policy/configmap/configmap.rego +11 -35
- package/rules/js-run/policy/configmap/template/configmap.yaml.contains.yml +4 -0
- package/rules/js-run/policy/jsconfig/jsconfig.rego +39 -46
- package/rules/js-run/policy/jsconfig/template/jsconfig.json.snippet.json +10 -0
- package/rules/js-run/policy/package_json/package_json.rego +10 -21
- package/rules/js-run/policy/package_json/template/package.json.deny.json +10 -0
- package/rules/php/php.mdc +2 -56
- package/rules/php/policy/lint_php_yml/lint_php_yml.rego +15 -13
- package/rules/php/policy/lint_php_yml/template/lint-php.yml.snippet.yml +47 -0
- package/rules/php/policy/package_json/package_json.rego +9 -12
- package/rules/php/policy/package_json/template/package.json.contains.json +5 -0
- package/rules/security/fix/trufflehog/check.mjs +45 -0
- package/rules/security/fix/trufflehog/template/.trufflehog-exclude.snippet.txt +6 -0
- package/rules/security/policy/lint_security_yml/lint_security_yml.rego +33 -0
- package/rules/security/policy/lint_security_yml/target.json +8 -0
- package/rules/security/policy/lint_security_yml/template/lint-security.yml.snippet.yml +31 -0
- package/rules/security/policy/package_json/template/package.json.deny.json +2 -2
- package/rules/security/policy/package_json/template/package.json.snippet.json +1 -1
- package/rules/security/security.mdc +17 -38
- package/rules/style-lint/policy/lint_style_yml/lint_style_yml.rego +14 -16
- package/rules/style-lint/policy/lint_style_yml/template/lint-style.yml.snippet.yml +39 -0
- package/rules/style-lint/policy/package_json/package_json.rego +22 -30
- package/rules/style-lint/policy/package_json/template/package.json.contains.json +5 -0
- package/rules/style-lint/policy/package_json/template/package.json.snippet.json +5 -0
- package/rules/style-lint/policy/vscode_extensions/template/extensions.json.snippet.json +1 -0
- package/rules/style-lint/policy/vscode_extensions/vscode_extensions.rego +5 -15
- package/rules/style-lint/policy/vscode_settings/template/settings.json.snippet.json +5 -0
- package/rules/style-lint/policy/vscode_settings/vscode_settings.rego +7 -16
- package/rules/text/policy/cspell/cspell.rego +39 -59
- package/rules/text/policy/cspell/template/.cspell.json.contains.json +3 -0
- package/rules/text/policy/cspell/template/.cspell.json.deny.json +5 -0
- package/rules/text/policy/cspell/template/.cspell.json.snippet.json +12 -0
- package/rules/text/policy/markdownlint/markdownlint.rego +37 -50
- package/rules/text/policy/markdownlint/template/.markdownlint-cli2.jsonc.snippet.jsonc +11 -0
- package/rules/text/policy/oxfmtrc/oxfmtrc.rego +23 -58
- package/rules/text/policy/oxfmtrc/template/.oxfmtrc.json.snippet.json +12 -0
- package/rules/text/policy/package_json/package_json.rego +14 -52
- package/rules/text/policy/package_json/template/package.json.deny.json +15 -0
- package/rules/text/policy/vscode_extensions/template/extensions.json.snippet.json +7 -0
- package/rules/text/policy/vscode_extensions/vscode_extensions.rego +4 -28
- package/rules/text/policy/vscode_settings/template/settings.json.snippet.json +9 -0
- package/rules/text/policy/vscode_settings/vscode_settings.rego +20 -42
- package/skills/adr-normalize/SKILL.md +2 -2
- package/rules/security/fix/gitleaks/check.mjs +0 -25
- package/rules/security/fix/gitleaks/template/.gitleaks.toml.snippet.toml +0 -12
- package/rules/security/policy/gitleaks/gitleaks.rego +0 -17
- package/rules/security/policy/gitleaks/target.json +0 -8
|
@@ -1,25 +1,12 @@
|
|
|
1
1
|
# Перевірка `.vscode/extensions.json` для js-lint (js-lint.mdc).
|
|
2
2
|
#
|
|
3
|
-
#
|
|
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).
|
|
3
|
+
# Канон надходить через --data: { "template": { "snippet": ... } }
|
|
9
4
|
package js_lint.vscode_extensions
|
|
10
5
|
|
|
11
6
|
import rego.v1
|
|
12
7
|
|
|
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
8
|
deny contains msg if {
|
|
22
|
-
some
|
|
23
|
-
not
|
|
24
|
-
msg := sprintf(".vscode/extensions.json: recommendations має містити %q (js-lint.mdc)", [
|
|
9
|
+
some rec in data.template.snippet.recommendations
|
|
10
|
+
not rec in {r | some r in object.get(input, "recommendations", [])}
|
|
11
|
+
msg := sprintf(".vscode/extensions.json: recommendations має містити %q (js-lint.mdc)", [rec])
|
|
25
12
|
}
|
|
@@ -1,45 +1,21 @@
|
|
|
1
|
-
#
|
|
2
|
-
# (js-run.mdc) — `OTEL_RESOURCE_ATTRIBUTES` має містити `service.name=` і
|
|
3
|
-
# `service.namespace=`.
|
|
1
|
+
# Перевірка ConfigMap (js-run.mdc).
|
|
4
2
|
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# Відповідність імені ConfigMap імені Deployment (cross-file) — у JS і `check-k8s.mjs`.
|
|
10
|
-
#
|
|
11
|
-
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
12
|
-
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
13
|
-
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
3
|
+
# Канон надходить через --data: { "template": { "contains": ... } }
|
|
4
|
+
# Структура --data сформована з template/configmap.yaml.contains.yml.
|
|
5
|
+
# Контекст: kind=ConfigMap; OTEL_RESOURCE_ATTRIBUTES має містити кожен substring
|
|
6
|
+
# зі snippet (`service.name=`, `service.namespace=`).
|
|
14
7
|
package js_run.configmap
|
|
15
8
|
|
|
16
9
|
import rego.v1
|
|
17
10
|
|
|
18
|
-
# Шаблони повідомлень — через `concat` для regal style/line-length.
|
|
19
|
-
otel_service_name_template := concat(" ", [
|
|
20
|
-
"ConfigMap %q: OTEL_RESOURCE_ATTRIBUTES має містити",
|
|
21
|
-
"`service.name=` (js-run.mdc)",
|
|
22
|
-
])
|
|
23
|
-
|
|
24
|
-
otel_service_namespace_template := concat(" ", [
|
|
25
|
-
"ConfigMap %q: OTEL_RESOURCE_ATTRIBUTES має містити",
|
|
26
|
-
"`service.namespace=` (js-run.mdc)",
|
|
27
|
-
])
|
|
28
|
-
|
|
29
|
-
deny contains msg if {
|
|
30
|
-
input.kind == "ConfigMap"
|
|
31
|
-
otel := object.get(object.get(input, "data", {}), "OTEL_RESOURCE_ATTRIBUTES", "")
|
|
32
|
-
otel != ""
|
|
33
|
-
not contains(otel, "service.name=")
|
|
34
|
-
msg := sprintf(otel_service_name_template, [cm_name])
|
|
35
|
-
}
|
|
36
|
-
|
|
37
11
|
deny contains msg if {
|
|
38
12
|
input.kind == "ConfigMap"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
13
|
+
some field, needles in data.template.contains.data
|
|
14
|
+
actual := object.get(object.get(input, "data", {}), field, "")
|
|
15
|
+
actual != ""
|
|
16
|
+
some needle in needles
|
|
17
|
+
not contains(actual, needle)
|
|
18
|
+
msg := sprintf("ConfigMap %q: %s має містити %q (js-run.mdc)", [cm_name, field, needle])
|
|
43
19
|
}
|
|
44
20
|
|
|
45
21
|
cm_name := object.get(object.get(input, "metadata", {}), "name", "?")
|
|
@@ -1,66 +1,59 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Перевірка `jsconfig.json` (js-run.mdc).
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# Перевіряє: `compilerOptions.{lib, module, moduleResolution, target, checkJs}` і
|
|
8
|
-
# `include` мають канонічні значення (js-run.mdc).
|
|
9
|
-
#
|
|
10
|
-
# FS-перевірка (наявність каталогу `src/` у пакеті, наявність самого `jsconfig.json`)
|
|
11
|
-
# і вибір файлу-кандидата — у 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).
|
|
3
|
+
# Канон надходить через --data: { "template": { "snippet": ... } }
|
|
4
|
+
# Структура --data сформована з template/jsconfig.json.snippet.yml.
|
|
5
|
+
# Walker: для кожного leaf у template's snippet порівнюємо input[path].
|
|
6
|
+
# Для масивів — equality (не subset-of, бо в jsconfig потрібен точний набір).
|
|
16
7
|
package js_run.jsconfig
|
|
17
8
|
|
|
18
9
|
import rego.v1
|
|
19
10
|
|
|
20
|
-
#
|
|
21
|
-
|
|
11
|
+
# Generic 2-level walker: section → key → expected (для compilerOptions).
|
|
22
12
|
deny contains msg if {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
13
|
+
some section, expected_inner in data.template.snippet
|
|
14
|
+
is_object(expected_inner)
|
|
15
|
+
inner := object.get(input, section, {})
|
|
16
|
+
is_object(inner)
|
|
17
|
+
some leaf_key, expected_value in expected_inner
|
|
18
|
+
actual := object.get(inner, leaf_key, null)
|
|
19
|
+
not values_match(actual, expected_value)
|
|
20
|
+
msg := sprintf("jsconfig.json: %s.%s має бути %v (js-run.mdc)", [section, leaf_key, expected_value])
|
|
26
21
|
}
|
|
27
22
|
|
|
23
|
+
# Section відсутня або не обʼєкт.
|
|
28
24
|
deny contains msg if {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
some section, expected_inner in data.template.snippet
|
|
26
|
+
is_object(expected_inner)
|
|
27
|
+
raw := object.get(input, section, null)
|
|
28
|
+
not is_object(raw)
|
|
29
|
+
msg := sprintf("jsconfig.json: відсутній обʼєкт %s (js-run.mdc)", [section])
|
|
33
30
|
}
|
|
34
31
|
|
|
32
|
+
# Top-level масив (наприклад include) — порівнюємо як множину.
|
|
35
33
|
deny contains msg if {
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
some field, expected_array in data.template.snippet
|
|
35
|
+
is_array(expected_array)
|
|
36
|
+
actual := object.get(input, field, null)
|
|
37
|
+
not is_array(actual)
|
|
38
|
+
msg := sprintf("jsconfig.json: %s має бути масив %v (js-run.mdc)", [field, expected_array])
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
deny contains msg if {
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
some field, expected_array in data.template.snippet
|
|
43
|
+
is_array(expected_array)
|
|
44
|
+
is_array(object.get(input, field, null))
|
|
45
|
+
{x | some x in input[field]} != {x | some x in expected_array}
|
|
46
|
+
msg := sprintf("jsconfig.json: %s має бути %v (js-run.mdc)", [field, expected_array])
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
# Helper: leaf-level value match, з підтримкою масивів як множин.
|
|
50
|
+
values_match(actual, expected) if {
|
|
51
|
+
not is_array(expected)
|
|
52
|
+
actual == expected
|
|
48
53
|
}
|
|
49
54
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
# ── deny: include ──────────────────────────────────────────────────────────
|
|
56
|
-
|
|
57
|
-
deny contains msg if {
|
|
58
|
-
not is_array(object.get(input, "include", null))
|
|
59
|
-
msg := "jsconfig.json: include має бути [\"src/**/*\"] (js-run.mdc)"
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
deny contains msg if {
|
|
63
|
-
is_array(input.include)
|
|
64
|
-
{p | some p in input.include} != {"src/**/*"}
|
|
65
|
-
msg := "jsconfig.json: include має бути [\"src/**/*\"] (js-run.mdc)"
|
|
55
|
+
values_match(actual, expected) if {
|
|
56
|
+
is_array(expected)
|
|
57
|
+
is_array(actual)
|
|
58
|
+
{x | some x in actual} == {x | some x in expected}
|
|
66
59
|
}
|
|
@@ -1,31 +1,20 @@
|
|
|
1
|
-
#
|
|
2
|
-
# (js-run.mdc) — заборона `bunyan` / `@nitra/bunyan`.
|
|
1
|
+
# Перевірка `package.json` (js-run.mdc).
|
|
3
2
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# AST-скан коду на імпорти `bunyan` / `process.env` без `checkEnv`,
|
|
9
|
-
# `new Promise(resolve => setTimeout(resolve, ...))`, обмеження `#conn/*`-аліасів —
|
|
10
|
-
# у JS (потребує парсингу `.js` / `.ts` через oxc-parser).
|
|
11
|
-
#
|
|
12
|
-
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
13
|
-
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
14
|
-
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
3
|
+
# Канон надходить через --data: { "template": { "deny": ... } }
|
|
4
|
+
# Структура --data сформована з template/package.json.deny.json.
|
|
5
|
+
# AST-скан коду (`bunyan`/`process.env`/`#conn/*`) — у JS.
|
|
15
6
|
package js_run.package_json
|
|
16
7
|
|
|
17
8
|
import rego.v1
|
|
18
9
|
|
|
19
|
-
forbidden_packages := {"bunyan", "@nitra/bunyan"}
|
|
20
|
-
|
|
21
10
|
deny contains msg if {
|
|
22
|
-
some
|
|
23
|
-
|
|
24
|
-
msg := sprintf("dependencies
|
|
11
|
+
some pkg, reason in data.template.deny.dependencies
|
|
12
|
+
pkg in object.keys(object.get(input, "dependencies", {}))
|
|
13
|
+
msg := sprintf("dependencies.%s — %s", [pkg, reason])
|
|
25
14
|
}
|
|
26
15
|
|
|
27
16
|
deny contains msg if {
|
|
28
|
-
some
|
|
29
|
-
|
|
30
|
-
msg := sprintf("devDependencies
|
|
17
|
+
some pkg, reason in data.template.deny.devDependencies
|
|
18
|
+
pkg in object.keys(object.get(input, "devDependencies", {}))
|
|
19
|
+
msg := sprintf("devDependencies.%s — %s", [pkg, reason])
|
|
31
20
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"dependencies": {
|
|
3
|
+
"bunyan": "використовуй стандартні логери (js-run.mdc)",
|
|
4
|
+
"@nitra/bunyan": "використовуй стандартні логери (js-run.mdc)"
|
|
5
|
+
},
|
|
6
|
+
"devDependencies": {
|
|
7
|
+
"bunyan": "використовуй стандартні логери (js-run.mdc)",
|
|
8
|
+
"@nitra/bunyan": "використовуй стандартні логери (js-run.mdc)"
|
|
9
|
+
}
|
|
10
|
+
}
|
package/rules/php/php.mdc
CHANGED
|
@@ -65,13 +65,7 @@ composer audit
|
|
|
65
65
|
|
|
66
66
|
`composer`-інструмененти не мають єдиного CLI, який сам обходить репозиторій, тому `lint-php` зручно делегувати у JS-скрипт-обгортку (як `lint-docker`, `lint-k8s`).
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
{
|
|
70
|
-
"scripts": {
|
|
71
|
-
"lint-php": "bun ./npm/scripts/run-php.mjs"
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
```
|
|
68
|
+
- Канон `package.json#scripts.lint-php` (substring requirement): [package.json.contains.json](./policy/package_json/template/package.json.contains.json)
|
|
75
69
|
|
|
76
70
|
Скрипт `run-php.mjs`:
|
|
77
71
|
|
|
@@ -82,52 +76,4 @@ composer audit
|
|
|
82
76
|
|
|
83
77
|
## CI: `.github/workflows/lint-php.yml`
|
|
84
78
|
|
|
85
|
-
|
|
86
|
-
name: Lint PHP
|
|
87
|
-
|
|
88
|
-
on:
|
|
89
|
-
push:
|
|
90
|
-
branches:
|
|
91
|
-
- dev
|
|
92
|
-
- main
|
|
93
|
-
paths:
|
|
94
|
-
- '**/*.php'
|
|
95
|
-
- 'composer.json'
|
|
96
|
-
- 'composer.lock'
|
|
97
|
-
- 'phpstan.neon'
|
|
98
|
-
- 'phpstan.neon.dist'
|
|
99
|
-
- 'psalm.xml'
|
|
100
|
-
- '.github/workflows/lint-php.yml'
|
|
101
|
-
|
|
102
|
-
pull_request:
|
|
103
|
-
branches:
|
|
104
|
-
- dev
|
|
105
|
-
- main
|
|
106
|
-
|
|
107
|
-
concurrency:
|
|
108
|
-
group: ${{ github.ref }}-${{ github.workflow }}
|
|
109
|
-
cancel-in-progress: true
|
|
110
|
-
|
|
111
|
-
jobs:
|
|
112
|
-
php:
|
|
113
|
-
runs-on: ubuntu-latest
|
|
114
|
-
permissions:
|
|
115
|
-
contents: read
|
|
116
|
-
steps:
|
|
117
|
-
- uses: actions/checkout@v6
|
|
118
|
-
with:
|
|
119
|
-
persist-credentials: false
|
|
120
|
-
|
|
121
|
-
- uses: ./.github/actions/setup-bun-deps
|
|
122
|
-
|
|
123
|
-
- name: Install PHP
|
|
124
|
-
uses: shivammathur/setup-php@v2
|
|
125
|
-
with:
|
|
126
|
-
php-version: '8.5'
|
|
127
|
-
|
|
128
|
-
- name: Install Composer dependencies
|
|
129
|
-
run: composer install --no-interaction --no-progress --prefer-dist
|
|
130
|
-
|
|
131
|
-
- name: Lint PHP
|
|
132
|
-
run: bun run lint-php
|
|
133
|
-
```
|
|
79
|
+
- Канон: [lint-php.yml.snippet.yml](./policy/lint_php_yml/template/lint-php.yml.snippet.yml)
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Перевірка `lint-php.yml` (php.mdc).
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# Перевіряє: хоча б один крок `run` містить `bun run lint-php`. Універсальні
|
|
8
|
-
# workflow-перевірки — у `ga.workflow_common`.
|
|
9
|
-
#
|
|
10
|
-
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
11
|
-
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
12
|
-
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
3
|
+
# Канон надходить через --data: { "template": { "snippet": ... } }
|
|
4
|
+
# Структура --data сформована з template/lint-php.yml.snippet.yml.
|
|
5
|
+
# Маркер `run:` (bun run lint-php) збирається з template's php-job steps.
|
|
6
|
+
# Універсальні workflow-перевірки — у `ga.workflow_common`.
|
|
13
7
|
package php.lint_php_yml
|
|
14
8
|
|
|
15
9
|
import rego.v1
|
|
16
10
|
|
|
11
|
+
# Очікуваний `run:` маркер — конкатенація всіх run-блоків з template.
|
|
12
|
+
expected_run_blob := concat("\n", [r |
|
|
13
|
+
some step in data.template.snippet.jobs.php.steps
|
|
14
|
+
r := object.get(step, "run", "")
|
|
15
|
+
r != ""
|
|
16
|
+
])
|
|
17
|
+
|
|
17
18
|
all_run_text := concat("\n", [run_text |
|
|
18
19
|
some job in object.get(input, "jobs", {})
|
|
19
20
|
some step in object.get(job, "steps", [])
|
|
@@ -21,8 +22,9 @@ all_run_text := concat("\n", [run_text |
|
|
|
21
22
|
])
|
|
22
23
|
|
|
23
24
|
deny contains msg if {
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
expected_run_blob != ""
|
|
26
|
+
not contains(all_run_text, expected_run_blob)
|
|
27
|
+
msg := sprintf("lint-php.yml: жоден крок run не містить %q (php.mdc)", [expected_run_blob])
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
step_run_to_text(step) := step.run if is_string(step.run)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: Lint PHP
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- dev
|
|
7
|
+
- main
|
|
8
|
+
paths:
|
|
9
|
+
- '**/*.php'
|
|
10
|
+
- 'composer.json'
|
|
11
|
+
- 'composer.lock'
|
|
12
|
+
- 'phpstan.neon'
|
|
13
|
+
- 'phpstan.neon.dist'
|
|
14
|
+
- 'psalm.xml'
|
|
15
|
+
- '.github/workflows/lint-php.yml'
|
|
16
|
+
|
|
17
|
+
pull_request:
|
|
18
|
+
branches:
|
|
19
|
+
- dev
|
|
20
|
+
- main
|
|
21
|
+
|
|
22
|
+
concurrency:
|
|
23
|
+
group: ${{ github.ref }}-${{ github.workflow }}
|
|
24
|
+
cancel-in-progress: true
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
php:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
permissions:
|
|
30
|
+
contents: read
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v6
|
|
33
|
+
with:
|
|
34
|
+
persist-credentials: false
|
|
35
|
+
|
|
36
|
+
- uses: ./.github/actions/setup-bun-deps
|
|
37
|
+
|
|
38
|
+
- name: Install PHP
|
|
39
|
+
uses: shivammathur/setup-php@v2
|
|
40
|
+
with:
|
|
41
|
+
php-version: '8.5'
|
|
42
|
+
|
|
43
|
+
- name: Install Composer dependencies
|
|
44
|
+
run: composer install --no-interaction --no-progress --prefer-dist
|
|
45
|
+
|
|
46
|
+
- name: Lint PHP
|
|
47
|
+
run: bun run lint-php
|
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Перевірка `package.json` (php.mdc).
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# Перевіряє: наявність скрипта `lint-php`. FS-перевірки (`composer.json`, наявність
|
|
7
|
-
# `package.json` як такого) — у JS.
|
|
8
|
-
#
|
|
9
|
-
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
10
|
-
# Конвенція проєкту — `import rego.v1` + multi-value `deny contains msg if { … }`
|
|
11
|
-
# (.cursor/rules/conftest.mdc). Лінт — `bun run lint-rego` (regal).
|
|
3
|
+
# Канон надходить через --data: { "template": { "contains": ... } }
|
|
4
|
+
# Структура --data сформована з template/package.json.contains.json.
|
|
5
|
+
# FS-перевірки (`composer.json`, наявність `package.json` як такого) — у JS.
|
|
12
6
|
package php.package_json
|
|
13
7
|
|
|
14
8
|
import rego.v1
|
|
15
9
|
|
|
16
10
|
deny contains msg if {
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
some script_name, needles in data.template.contains.scripts
|
|
12
|
+
actual := object.get(object.get(input, "scripts", {}), script_name, "")
|
|
13
|
+
some needle in needles
|
|
14
|
+
not contains(actual, needle)
|
|
15
|
+
msg := sprintf("package.json: scripts.%s має містити %q (php.mdc)", [script_name, needle])
|
|
19
16
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FS-частина правила `security`.
|
|
3
|
+
*
|
|
4
|
+
* Перевіряє:
|
|
5
|
+
* - наявність `package.json` (структуру валідує policy security.package_json);
|
|
6
|
+
* - наявність `.trufflehog-exclude` у корені та subset канонічних patterns
|
|
7
|
+
* (text-subset, бо `.trufflehog-exclude` — plain text, не структурований).
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync } from 'node:fs'
|
|
10
|
+
import { readFile } from 'node:fs/promises'
|
|
11
|
+
import { dirname, join } from 'node:path'
|
|
12
|
+
import { fileURLToPath } from 'node:url'
|
|
13
|
+
|
|
14
|
+
import { createCheckReporter } from '../../../../scripts/utils/check-reporter.mjs'
|
|
15
|
+
import { checkTextSubset } from '../../../../scripts/utils/template.mjs'
|
|
16
|
+
|
|
17
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
18
|
+
const SNIPPET_PATH = join(HERE, 'template', '.trufflehog-exclude.snippet.txt')
|
|
19
|
+
|
|
20
|
+
export async function check() {
|
|
21
|
+
const reporter = createCheckReporter()
|
|
22
|
+
const { pass, fail } = reporter
|
|
23
|
+
|
|
24
|
+
if (!existsSync('package.json')) {
|
|
25
|
+
fail('package.json не знайдено в корені — додай (security.mdc)')
|
|
26
|
+
return reporter.getExitCode()
|
|
27
|
+
}
|
|
28
|
+
pass('package.json є (структуру перевіряє Rego)')
|
|
29
|
+
|
|
30
|
+
if (!existsSync('.trufflehog-exclude')) {
|
|
31
|
+
fail('.trufflehog-exclude не знайдено в корені — додай за каноном (security.mdc)')
|
|
32
|
+
return reporter.getExitCode()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const actual = await readFile('.trufflehog-exclude', 'utf8')
|
|
36
|
+
const template = await readFile(SNIPPET_PATH, 'utf8')
|
|
37
|
+
const errors = checkTextSubset(actual, template, {
|
|
38
|
+
targetPath: '.trufflehog-exclude',
|
|
39
|
+
source: 'security.mdc'
|
|
40
|
+
})
|
|
41
|
+
for (const msg of errors) fail(msg)
|
|
42
|
+
if (errors.length === 0) pass('.trufflehog-exclude містить канонічні patterns')
|
|
43
|
+
|
|
44
|
+
return reporter.getExitCode()
|
|
45
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Перевірка `.github/workflows/lint-security.yml` (security.mdc).
|
|
2
|
+
#
|
|
3
|
+
# Канон надходить через --data: { "template": { "snippet": ... } }, побудоване
|
|
4
|
+
# з template/lint-security.yml.snippet.yml. З нього збирається перелік
|
|
5
|
+
# `uses:` action-refs security-job (виключаючи універсальні `actions/*`, які
|
|
6
|
+
# валідує `ga.workflow_common`).
|
|
7
|
+
#
|
|
8
|
+
# Структура каталогу збігається зі шляхом пакету (regal: directory-package-mismatch).
|
|
9
|
+
package security.lint_security_yml
|
|
10
|
+
|
|
11
|
+
import rego.v1
|
|
12
|
+
|
|
13
|
+
# Очікувані action-uses із template (не-`actions/*`).
|
|
14
|
+
expected_uses_blob := concat("\n", [u |
|
|
15
|
+
some step in data.template.snippet.jobs.security.steps
|
|
16
|
+
u := object.get(step, "uses", "")
|
|
17
|
+
u != ""
|
|
18
|
+
not startswith(u, "actions/")
|
|
19
|
+
])
|
|
20
|
+
|
|
21
|
+
# Усі `uses:` із input workflow.
|
|
22
|
+
all_uses_text := concat("\n", [u |
|
|
23
|
+
some job in object.get(input, "jobs", {})
|
|
24
|
+
some step in object.get(job, "steps", [])
|
|
25
|
+
u := object.get(step, "uses", "")
|
|
26
|
+
u != ""
|
|
27
|
+
])
|
|
28
|
+
|
|
29
|
+
deny contains msg if {
|
|
30
|
+
expected_uses_blob != ""
|
|
31
|
+
not contains(all_uses_text, expected_uses_blob)
|
|
32
|
+
msg := sprintf("lint-security.yml: відсутній крок з uses %q (security.mdc)", [expected_uses_blob])
|
|
33
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Lint Security
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- dev
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
pull_request:
|
|
10
|
+
branches:
|
|
11
|
+
- dev
|
|
12
|
+
- main
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: ${{ github.ref }}-${{ github.workflow }}
|
|
16
|
+
cancel-in-progress: true
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
security:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v6
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
fetch-depth: 0
|
|
28
|
+
|
|
29
|
+
- uses: trufflesecurity/trufflehog@main
|
|
30
|
+
with:
|
|
31
|
+
extra_args: --results=verified,unknown
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"dependencies": { "
|
|
3
|
-
"devDependencies": { "
|
|
2
|
+
"dependencies": { "trufflehog": "глобальний CLI — не додавай у dependencies" },
|
|
3
|
+
"devDependencies": { "trufflehog": "глобальний CLI — не додавай у devDependencies" }
|
|
4
4
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{ "scripts": { "lint-security": "
|
|
1
|
+
{ "scripts": { "lint-security": "trufflehog filesystem . --no-update --exclude-paths .trufflehog-exclude --results=verified,unknown --fail" } }
|