@nitra/cursor 1.11.3 → 1.11.6

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 (86) hide show
  1. package/CHANGELOG.md +32 -3
  2. package/bin/n-cursor.js +40 -6
  3. package/package.json +2 -1
  4. package/rules/abie/js/applies/check.mjs +4 -4
  5. package/rules/abie/js/env_dns/check.mjs +1 -1
  6. package/rules/abie/js/firebase_hosting/check.mjs +1 -1
  7. package/rules/abie/js/hc_pairing/check.mjs +3 -3
  8. package/rules/abie/js/ua_http_route/check.mjs +3 -3
  9. package/rules/abie/js/ua_node_selector/check.mjs +4 -2
  10. package/rules/abie/policy/base_deployment_preem/target.json +1 -5
  11. package/rules/abie/utils/enabled.mjs +1 -1
  12. package/rules/abie/utils/env-dns.mjs +4 -4
  13. package/rules/abie/utils/http-route.mjs +5 -5
  14. package/rules/abie/utils/k8s-tree.mjs +23 -15
  15. package/rules/abie/utils/kustomization-patches.mjs +20 -20
  16. package/rules/abie/utils/overlay-paths.mjs +8 -8
  17. package/rules/abie/utils/yaml.mjs +4 -4
  18. package/rules/adr/adr.mdc +2 -2
  19. package/rules/adr/js/hooks/check.mjs +5 -5
  20. package/rules/docker/docker.mdc +2 -2
  21. package/rules/docker/js/run.mjs +3 -2
  22. package/rules/docker/policy/package_json/package_json.rego +1 -1
  23. package/rules/ga/js/lint.mjs +3 -26
  24. package/rules/hasura/js/internal_urls/check.mjs +1 -1
  25. package/rules/js-bun-redis/js/imports/check.mjs +5 -1
  26. package/rules/js-run/js/runtime/check.mjs +4 -1
  27. package/rules/k8s/js/run.mjs +3 -2
  28. package/rules/k8s/k8s.mdc +2 -4
  29. package/rules/k8s/policy/base_manifest/target.json +1 -5
  30. package/rules/nginx-default-tpl/js/template/check.mjs +4 -2
  31. package/rules/npm-module/js/package_structure/check.mjs +8 -3
  32. package/rules/npm-module/npm-module.mdc +3 -3
  33. package/rules/rego/js/applies/check.mjs +2 -2
  34. package/rules/rego/js/lint.mjs +4 -1
  35. package/rules/rego/policy/package_json/package_json.rego +5 -3
  36. package/rules/rego/rego.mdc +3 -3
  37. package/rules/style-lint/js/tooling/check.mjs +1 -1
  38. package/rules/style-lint/style-lint.mdc +1 -1
  39. package/rules/tauri/js/tooling/check.mjs +3 -1
  40. package/rules/text/js/formatting/check.mjs +8 -24
  41. package/rules/text/js/lint.mjs +34 -0
  42. package/rules/text/js/run-shellcheck.mjs +2 -2
  43. package/rules/text/js/run-v8r.mjs +2 -2
  44. package/rules/text/text.mdc +5 -5
  45. package/schemas/v8r-catalog.json +6 -0
  46. package/scripts/auto-skills.mjs +3 -7
  47. package/scripts/utils/discover-checkable-rules.mjs +4 -3
  48. package/scripts/utils/resolve-target-files.mjs +1 -1
  49. package/scripts/utils/run-lint-step.mjs +33 -0
  50. package/scripts/utils/run-rule.mjs +5 -3
  51. package/skills/abie-clean/SKILL.md +13 -11
  52. package/skills/adr-normalize/SKILL.md +0 -1
  53. package/skills/fix/SKILL.md +3 -7
  54. package/rules/abie/policy/base_deployment_preem/base_deployment_preem_test.rego +0 -60
  55. package/rules/abie/policy/clean_merged_ignore_branches/clean_merged_ignore_branches_test.rego +0 -48
  56. package/rules/abie/policy/health_check_policy/health_check_policy_test.rego +0 -99
  57. package/rules/abie/policy/http_route_base/http_route_base_test.rego +0 -64
  58. package/rules/bun/policy/package_json/package_json_test.rego +0 -109
  59. package/rules/docker/policy/lint_docker_yml/lint_docker_yml_test.rego +0 -104
  60. package/rules/docker/policy/package_json/package_json_test.rego +0 -42
  61. package/rules/graphql/policy/vscode_extensions/vscode_extensions_test.rego +0 -34
  62. package/rules/image-avif/policy/package_json/package_json_test.rego +0 -69
  63. package/rules/js-lint/policy/package_json/package_json_test.rego +0 -130
  64. package/rules/js-run/policy/jsconfig/jsconfig_test.rego +0 -88
  65. package/rules/k8s/policy/base_kustomization/base_kustomization_test.rego +0 -73
  66. package/rules/k8s/policy/base_manifest/base_manifest_test.rego +0 -94
  67. package/rules/k8s/policy/gateway/gateway_test.rego +0 -122
  68. package/rules/k8s/policy/hasura_configmap/hasura_configmap_test.rego +0 -49
  69. package/rules/k8s/policy/hasura_httproute/hasura_httproute_test.rego +0 -148
  70. package/rules/k8s/policy/hpa_pdb/hpa_pdb_test.rego +0 -101
  71. package/rules/k8s/policy/kustomization/kustomization_test.rego +0 -128
  72. package/rules/k8s/policy/manifest/manifest_test.rego +0 -309
  73. package/rules/k8s/policy/svc_hl_yaml/svc_hl_yaml_test.rego +0 -42
  74. package/rules/k8s/policy/svc_yaml/svc_yaml_test.rego +0 -41
  75. package/rules/nginx-default-tpl/policy/vscode_extensions/vscode_extensions_test.rego +0 -30
  76. package/rules/nginx-default-tpl/policy/vscode_settings/vscode_settings_test.rego +0 -53
  77. package/rules/npm-module/policy/npm_package_json/npm_package_json_test.rego +0 -81
  78. package/rules/rego/policy/package_json/package_json_test.rego +0 -42
  79. package/rules/rego/policy/vscode_extensions/vscode_extensions_test.rego +0 -34
  80. package/rules/rego/policy/vscode_settings/vscode_settings_test.rego +0 -55
  81. package/rules/style-lint/policy/vscode_extensions/vscode_extensions_test.rego +0 -39
  82. package/rules/style-lint/policy/vscode_settings/vscode_settings_test.rego +0 -49
  83. package/rules/tauri/policy/vscode_extensions/vscode_extensions_test.rego +0 -44
  84. package/rules/text/policy/markdownlint/markdownlint_test.rego +0 -98
  85. package/rules/text/policy/vscode_extensions/vscode_extensions_test.rego +0 -51
  86. package/rules/text/policy/vscode_settings/vscode_settings_test.rego +0 -85
@@ -1,94 +0,0 @@
1
- # Тести для `k8s.base_manifest`. Запуск:
2
- # conftest verify -p npm/policy/k8s/base_manifest --namespace k8s.base_manifest
3
- package k8s.base_manifest_test
4
-
5
- import rego.v1
6
-
7
- import data.k8s.base_manifest
8
-
9
- # ── metadata.namespace required ─────────────────────────────────────────
10
-
11
- test_deny_namespaced_kind_without_metadata if {
12
- count(base_manifest.deny) > 0 with input as {
13
- "apiVersion": "v1",
14
- "kind": "ConfigMap",
15
- }
16
- }
17
-
18
- test_deny_namespaced_kind_empty_namespace if {
19
- count(base_manifest.deny) > 0 with input as {
20
- "apiVersion": "v1",
21
- "kind": "ConfigMap",
22
- "metadata": {"name": "cm", "namespace": ""},
23
- }
24
- }
25
-
26
- test_allow_cluster_scoped_kind_without_namespace if {
27
- count(base_manifest.deny) == 0 with input as {
28
- "apiVersion": "v1",
29
- "kind": "Namespace",
30
- "metadata": {"name": "dev"},
31
- }
32
- }
33
-
34
- test_allow_namespaced_kind_with_namespace if {
35
- count(base_manifest.deny) == 0 with input as {
36
- "apiVersion": "v1",
37
- "kind": "ConfigMap",
38
- "metadata": {"name": "cm", "namespace": "dev"},
39
- }
40
- }
41
-
42
- # ── base canon resources ─────────────────────────────────────────────────
43
-
44
- test_deny_deployment_cpu_not_base_canon if {
45
- count(base_manifest.deny) > 0 with input as {
46
- "apiVersion": "apps/v1",
47
- "kind": "Deployment",
48
- "metadata": {"name": "api", "namespace": "dev"},
49
- "spec": {"template": {"spec": {"containers": [{
50
- "name": "main",
51
- "image": "x",
52
- "resources": {"requests": {"cpu": "100m", "memory": "128Mi"}},
53
- }]}}},
54
- }
55
- }
56
-
57
- test_deny_deployment_memory_not_base_canon if {
58
- count(base_manifest.deny) > 0 with input as {
59
- "apiVersion": "apps/v1",
60
- "kind": "Deployment",
61
- "metadata": {"name": "api", "namespace": "dev"},
62
- "spec": {"template": {"spec": {"containers": [{
63
- "name": "main",
64
- "image": "x",
65
- "resources": {"requests": {"cpu": "0.02", "memory": "256Mi"}},
66
- }]}}},
67
- }
68
- }
69
-
70
- test_allow_deployment_with_base_canon_string if {
71
- count(base_manifest.deny) == 0 with input as {
72
- "apiVersion": "apps/v1",
73
- "kind": "Deployment",
74
- "metadata": {"name": "api", "namespace": "dev"},
75
- "spec": {"template": {"spec": {"containers": [{
76
- "name": "main",
77
- "image": "x",
78
- "resources": {"requests": {"cpu": "0.02", "memory": "128Mi"}},
79
- }]}}},
80
- }
81
- }
82
-
83
- test_allow_deployment_with_base_canon_number_cpu if {
84
- count(base_manifest.deny) == 0 with input as {
85
- "apiVersion": "apps/v1",
86
- "kind": "Deployment",
87
- "metadata": {"name": "api", "namespace": "dev"},
88
- "spec": {"template": {"spec": {"containers": [{
89
- "name": "main",
90
- "image": "x",
91
- "resources": {"requests": {"cpu": 0.02, "memory": "128mi"}},
92
- }]}}},
93
- }
94
- }
@@ -1,122 +0,0 @@
1
- # Тести для `k8s.gateway`. Запуск:
2
- # conftest verify -p npm/policy/k8s/gateway --namespace k8s.gateway
3
- package k8s.gateway_test
4
-
5
- import rego.v1
6
-
7
- import data.k8s.gateway
8
-
9
- # ── HealthCheckPolicy ─────────────────────────────────────────────────────
10
-
11
- test_deny_hcp_targetref_without_hl_suffix if {
12
- count(gateway.deny) > 0 with input as {
13
- "apiVersion": "networking.gke.io/v1",
14
- "kind": "HealthCheckPolicy",
15
- "metadata": {"name": "hc"},
16
- "spec": {"targetRef": {
17
- "group": "",
18
- "kind": "Service",
19
- "name": "auth",
20
- }},
21
- }
22
- }
23
-
24
- test_allow_hcp_targetref_with_hl_suffix if {
25
- count(gateway.deny) == 0 with input as {
26
- "apiVersion": "networking.gke.io/v1",
27
- "kind": "HealthCheckPolicy",
28
- "metadata": {"name": "hc"},
29
- "spec": {"targetRef": {
30
- "group": "",
31
- "kind": "Service",
32
- "name": "auth-hl",
33
- }},
34
- }
35
- }
36
-
37
- test_deny_hcp_missing_targetref if {
38
- count(gateway.deny) > 0 with input as {
39
- "apiVersion": "networking.gke.io/v1",
40
- "kind": "HealthCheckPolicy",
41
- "metadata": {"name": "hc"},
42
- "spec": {},
43
- }
44
- }
45
-
46
- # Без kind=Service у targetRef правило не діє (інші kind не оцінюємо).
47
- test_allow_hcp_targetref_other_kind if {
48
- count(gateway.deny) == 0 with input as {
49
- "apiVersion": "networking.gke.io/v1",
50
- "kind": "HealthCheckPolicy",
51
- "metadata": {"name": "hc"},
52
- "spec": {"targetRef": {
53
- "kind": "Gateway",
54
- "name": "gw",
55
- }},
56
- }
57
- }
58
-
59
- # ── HTTPRoute backendRef → Service з суфіксом `-hl` ──────────────────────
60
-
61
- test_deny_httproute_backend_without_hl if {
62
- count(gateway.deny) > 0 with input as {
63
- "apiVersion": "gateway.networking.k8s.io/v1",
64
- "kind": "HTTPRoute",
65
- "metadata": {"name": "r", "namespace": "dev"},
66
- "spec": {"rules": [{"backendRefs": [{"name": "auth", "port": 8080}]}]},
67
- }
68
- }
69
-
70
- test_allow_httproute_backend_with_hl if {
71
- count(gateway.deny) == 0 with input as {
72
- "apiVersion": "gateway.networking.k8s.io/v1",
73
- "kind": "HTTPRoute",
74
- "metadata": {"name": "r", "namespace": "dev"},
75
- "spec": {"rules": [{"backendRefs": [{"name": "auth-hl", "port": 8080}]}]},
76
- }
77
- }
78
-
79
- # ── HTTPRoute backendRef з redundant namespace ───────────────────────────
80
-
81
- test_deny_httproute_backend_redundant_namespace if {
82
- count(gateway.deny) > 0 with input as {
83
- "apiVersion": "gateway.networking.k8s.io/v1",
84
- "kind": "HTTPRoute",
85
- "metadata": {"name": "r", "namespace": "dev"},
86
- "spec": {"rules": [{"backendRefs": [{
87
- "name": "auth-hl",
88
- "namespace": "dev",
89
- "port": 8080,
90
- }]}]},
91
- }
92
- }
93
-
94
- test_allow_httproute_backend_different_namespace if {
95
- count(gateway.deny) == 0 with input as {
96
- "apiVersion": "gateway.networking.k8s.io/v1",
97
- "kind": "HTTPRoute",
98
- "metadata": {"name": "r", "namespace": "dev"},
99
- "spec": {"rules": [{"backendRefs": [{
100
- "name": "auth-hl",
101
- "namespace": "other",
102
- "port": 8080,
103
- }]}]},
104
- }
105
- }
106
-
107
- # Перевірка не діє на HTTPHeaderMatch (немає `port`).
108
- test_allow_httproute_header_match_without_port if {
109
- count(gateway.deny) == 0 with input as {
110
- "apiVersion": "gateway.networking.k8s.io/v1",
111
- "kind": "HTTPRoute",
112
- "metadata": {"name": "r", "namespace": "dev"},
113
- "spec": {"rules": [{
114
- "matches": [{"headers": [{
115
- "type": "Exact",
116
- "name": "X-Tenant",
117
- "value": "acme",
118
- }]}],
119
- "backendRefs": [{"name": "auth-hl", "port": 8080}],
120
- }]},
121
- }
122
- }
@@ -1,49 +0,0 @@
1
- # Тести для `k8s.hasura_configmap`. Запуск:
2
- # conftest verify -p npm/policy/k8s/hasura_configmap --namespace k8s.hasura_configmap
3
- package k8s.hasura_configmap_test
4
-
5
- import rego.v1
6
-
7
- import data.k8s.hasura_configmap
8
-
9
- required_key := "HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS"
10
-
11
- base_cm := {
12
- "apiVersion": "v1",
13
- "kind": "ConfigMap",
14
- "metadata": {"name": "db-h", "namespace": "dev"},
15
- }
16
-
17
- with_data(value) := object.union(base_cm, {"data": {required_key: value}})
18
-
19
- test_deny_missing_data if {
20
- count(hasura_configmap.deny) > 0 with input as base_cm
21
- }
22
-
23
- test_deny_missing_required_key if {
24
- count(hasura_configmap.deny) > 0 with input as object.union(base_cm, {"data": {"OTHER": "foo"}})
25
- }
26
-
27
- test_deny_required_key_value_false if {
28
- count(hasura_configmap.deny) > 0 with input as with_data("false")
29
- }
30
-
31
- test_allow_required_key_string_true if {
32
- count(hasura_configmap.deny) == 0 with input as with_data("true")
33
- }
34
-
35
- test_allow_required_key_boolean_true if {
36
- count(hasura_configmap.deny) == 0 with input as with_data(true)
37
- }
38
-
39
- test_allow_required_key_uppercase_true if {
40
- count(hasura_configmap.deny) == 0 with input as with_data("TRUE")
41
- }
42
-
43
- test_allow_non_configmap if {
44
- count(hasura_configmap.deny) == 0 with input as {
45
- "apiVersion": "apps/v1",
46
- "kind": "Deployment",
47
- "metadata": {"name": "x"},
48
- }
49
- }
@@ -1,148 +0,0 @@
1
- # Тести для `k8s.hasura_httproute`. Запуск:
2
- # conftest verify -p npm/policy/k8s/hasura_httproute --namespace k8s.hasura_httproute
3
- package k8s.hasura_httproute_test
4
-
5
- import rego.v1
6
-
7
- import data.k8s.hasura_httproute
8
-
9
- base_route := {
10
- "apiVersion": "gateway.networking.k8s.io/v1",
11
- "kind": "HTTPRoute",
12
- "metadata": {"name": "db-h", "namespace": "dev"},
13
- }
14
-
15
- rule1(prefix) := {
16
- "matches": [{"path": {"type": "Exact", "value": sprintf("%s/ql", [prefix])}}],
17
- "filters": [{
18
- "type": "RequestRedirect",
19
- "requestRedirect": {
20
- "path": {"type": "ReplaceFullPath", "replaceFullPath": sprintf("%s/ql/console", [prefix])},
21
- "statusCode": 302,
22
- },
23
- }],
24
- }
25
-
26
- rule2(prefix) := {
27
- "matches": [{"path": {"type": "Exact", "value": sprintf("%s/ql/", [prefix])}}],
28
- "filters": [{
29
- "type": "RequestRedirect",
30
- "requestRedirect": {
31
- "path": {"type": "ReplaceFullPath", "replaceFullPath": sprintf("%s/ql/console", [prefix])},
32
- "statusCode": 302,
33
- },
34
- }],
35
- }
36
-
37
- rule3(prefix, backend) := {
38
- "matches": [{"path": {"type": "PathPrefix", "value": sprintf("%s/ql", [prefix])}}],
39
- "filters": [{
40
- "type": "URLRewrite",
41
- "urlRewrite": {"path": {"type": "ReplacePrefixMatch", "replacePrefixMatch": "/"}},
42
- }],
43
- "backendRefs": [{"name": backend, "port": 8080}],
44
- }
45
-
46
- rule4(prefix, backend) := {
47
- "matches": [{
48
- "path": {"type": "PathPrefix", "value": sprintf("%s/ql", [prefix])},
49
- "headers": [{"type": "Exact", "name": "Upgrade", "value": "websocket"}],
50
- }],
51
- "filters": [
52
- {
53
- "type": "URLRewrite",
54
- "urlRewrite": {"path": {"type": "ReplacePrefixMatch", "replacePrefixMatch": "/"}},
55
- },
56
- {
57
- "type": "RequestHeaderModifier",
58
- "requestHeaderModifier": {"remove": ["Authorization"]},
59
- },
60
- ],
61
- "backendRefs": [{"name": backend, "port": 8080}],
62
- }
63
-
64
- # ── canonical positive case ─────────────────────────────────────────────
65
-
66
- test_allow_canonical_route_empty_prefix if {
67
- count(hasura_httproute.deny) == 0 with input as object.union(base_route, {"spec": {"rules": [
68
- rule1(""),
69
- rule2(""),
70
- rule3("", "db-h-hl"),
71
- rule4("", "db-h-hl"),
72
- ]}})
73
- }
74
-
75
- test_allow_canonical_route_with_prefix if {
76
- count(hasura_httproute.deny) == 0 with input as object.union(base_route, {"spec": {"rules": [
77
- rule1("/notify"),
78
- rule2("/notify"),
79
- rule3("/notify", "db-h-hl"),
80
- rule4("/notify", "db-h-hl"),
81
- ]}})
82
- }
83
-
84
- # ── deny-кейси ───────────────────────────────────────────────────────────
85
-
86
- test_deny_missing_spec if {
87
- count(hasura_httproute.deny) > 0 with input as base_route
88
- }
89
-
90
- test_deny_empty_rules if {
91
- count(hasura_httproute.deny) > 0 with input as object.union(base_route, {"spec": {"rules": []}})
92
- }
93
-
94
- test_deny_missing_rule1 if {
95
- count(hasura_httproute.deny) > 0 with input as object.union(base_route, {"spec": {"rules": [{
96
- "matches": [{"path": {"type": "PathPrefix", "value": "/api"}}],
97
- "backendRefs": [{"name": "api-hl", "port": 8080}],
98
- }]}})
99
- }
100
-
101
- test_deny_rule1_wrong_redirect if {
102
- bad_rule1 := object.union(rule1(""), {"filters": [{
103
- "type": "RequestRedirect",
104
- "requestRedirect": {
105
- "path": {"type": "ReplaceFullPath", "replaceFullPath": "/wrong"},
106
- "statusCode": 302,
107
- },
108
- }]})
109
- count(hasura_httproute.deny) > 0 with input as object.union(base_route, {"spec": {"rules": [
110
- bad_rule1,
111
- rule2(""),
112
- rule3("", "db-h-hl"),
113
- rule4("", "db-h-hl"),
114
- ]}})
115
- }
116
-
117
- test_deny_rule2_missing if {
118
- count(hasura_httproute.deny) > 0 with input as object.union(base_route, {"spec": {"rules": [
119
- rule1(""),
120
- rule3("", "db-h-hl"),
121
- rule4("", "db-h-hl"),
122
- ]}})
123
- }
124
-
125
- test_deny_rule3_missing if {
126
- count(hasura_httproute.deny) > 0 with input as object.union(base_route, {"spec": {"rules": [
127
- rule1(""),
128
- rule2(""),
129
- rule4("", "db-h-hl"),
130
- ]}})
131
- }
132
-
133
- test_deny_rule4_missing if {
134
- count(hasura_httproute.deny) > 0 with input as object.union(base_route, {"spec": {"rules": [
135
- rule1(""),
136
- rule2(""),
137
- rule3("", "db-h-hl"),
138
- ]}})
139
- }
140
-
141
- test_deny_rule4_wrong_backend if {
142
- count(hasura_httproute.deny) > 0 with input as object.union(base_route, {"spec": {"rules": [
143
- rule1(""),
144
- rule2(""),
145
- rule3("", "db-h-hl"),
146
- rule4("", "other-hl"),
147
- ]}})
148
- }
@@ -1,101 +0,0 @@
1
- # Тести для `k8s.hpa_pdb`. Запуск:
2
- # conftest verify -p npm/policy/k8s/hpa_pdb --namespace k8s.hpa_pdb
3
- package k8s.hpa_pdb_test
4
-
5
- import rego.v1
6
-
7
- import data.k8s.hpa_pdb
8
-
9
- valid_hpa := {
10
- "apiVersion": "autoscaling/v2",
11
- "kind": "HorizontalPodAutoscaler",
12
- "metadata": {"name": "api"},
13
- "spec": {
14
- "scaleTargetRef": {"apiVersion": "apps/v1", "kind": "Deployment", "name": "api"},
15
- "minReplicas": 1,
16
- "maxReplicas": 1,
17
- "metrics": [{
18
- "type": "Resource",
19
- "resource": {"name": "cpu", "target": {"type": "Utilization", "averageUtilization": 75}},
20
- }],
21
- "behavior": {
22
- "scaleUp": {"policies": [{"type": "Pods", "value": 1, "periodSeconds": 60}]},
23
- "scaleDown": {"policies": [{"type": "Pods", "value": 1, "periodSeconds": 60}]},
24
- },
25
- },
26
- }
27
-
28
- valid_pdb := {
29
- "apiVersion": "policy/v1",
30
- "kind": "PodDisruptionBudget",
31
- "metadata": {"name": "api"},
32
- "spec": {
33
- "minAvailable": 1,
34
- "selector": {"matchLabels": {"app": "api"}},
35
- },
36
- }
37
-
38
- # ── HPA позитив / негатив ────────────────────────────────────────────────
39
-
40
- test_allow_valid_hpa if {
41
- count(hpa_pdb.deny) == 0 with input as valid_hpa
42
- }
43
-
44
- test_deny_hpa_v1 if {
45
- count(hpa_pdb.deny) > 0 with input as object.union(valid_hpa, {"apiVersion": "autoscaling/v1"})
46
- }
47
-
48
- test_deny_hpa_missing_metrics if {
49
- bad := json.patch(valid_hpa, [{"op": "remove", "path": "/spec/metrics"}])
50
- count(hpa_pdb.deny) > 0 with input as bad
51
- }
52
-
53
- test_deny_hpa_empty_metrics if {
54
- bad := json.patch(valid_hpa, [{"op": "replace", "path": "/spec/metrics", "value": []}])
55
- count(hpa_pdb.deny) > 0 with input as bad
56
- }
57
-
58
- test_deny_hpa_missing_behavior if {
59
- bad := json.patch(valid_hpa, [{"op": "remove", "path": "/spec/behavior"}])
60
- count(hpa_pdb.deny) > 0 with input as bad
61
- }
62
-
63
- test_deny_hpa_empty_scale_up_policies if {
64
- bad := json.patch(valid_hpa, [{"op": "replace", "path": "/spec/behavior/scaleUp/policies", "value": []}])
65
- count(hpa_pdb.deny) > 0 with input as bad
66
- }
67
-
68
- test_deny_hpa_missing_scale_down if {
69
- bad := json.patch(valid_hpa, [{"op": "remove", "path": "/spec/behavior/scaleDown"}])
70
- count(hpa_pdb.deny) > 0 with input as bad
71
- }
72
-
73
- # ── PDB позитив / негатив ────────────────────────────────────────────────
74
-
75
- test_allow_valid_pdb if {
76
- count(hpa_pdb.deny) == 0 with input as valid_pdb
77
- }
78
-
79
- test_deny_pdb_wrong_api_version if {
80
- bad := object.union(valid_pdb, {"apiVersion": "policy/v1beta1"})
81
- count(hpa_pdb.deny) > 0 with input as bad
82
- }
83
-
84
- test_deny_pdb_missing_selector if {
85
- bad := json.patch(valid_pdb, [{"op": "remove", "path": "/spec/selector"}])
86
- count(hpa_pdb.deny) > 0 with input as bad
87
- }
88
-
89
- test_deny_pdb_missing_match_labels if {
90
- bad := json.patch(valid_pdb, [{"op": "replace", "path": "/spec/selector", "value": {}}])
91
- count(hpa_pdb.deny) > 0 with input as bad
92
- }
93
-
94
- # Не той kind/apiVersion — пакет не діє.
95
- test_allow_unrelated_manifest if {
96
- count(hpa_pdb.deny) == 0 with input as {
97
- "apiVersion": "v1",
98
- "kind": "ConfigMap",
99
- "metadata": {"name": "x"},
100
- }
101
- }
@@ -1,128 +0,0 @@
1
- # Тести для `k8s.kustomization`. Запуск:
2
- # conftest verify -p npm/policy/k8s/kustomization --namespace k8s.kustomization
3
- package k8s.kustomization_test
4
-
5
- import rego.v1
6
-
7
- import data.k8s.kustomization
8
-
9
- base_kust := {
10
- "apiVersion": "kustomize.config.k8s.io/v1beta1",
11
- "kind": "Kustomization",
12
- }
13
-
14
- # ── resources sort ───────────────────────────────────────────────────────
15
-
16
- test_deny_resources_unsorted if {
17
- count(kustomization.deny) > 0 with input as object.union(base_kust, {"resources": [
18
- "deployment.yaml",
19
- "configmap.yaml",
20
- ]})
21
- }
22
-
23
- test_allow_resources_sorted if {
24
- count(kustomization.deny) == 0 with input as object.union(base_kust, {"resources": [
25
- "configmap.yaml",
26
- "deployment.yaml",
27
- ]})
28
- }
29
-
30
- test_allow_resources_case_insensitive_sorted if {
31
- count(kustomization.deny) == 0 with input as object.union(base_kust, {"resources": [
32
- "AAA.yaml",
33
- "bbb.yaml",
34
- "CCC.yaml",
35
- ]})
36
- }
37
-
38
- test_deny_resources_not_array if {
39
- count(kustomization.deny) > 0 with input as object.union(base_kust, {"resources": "string-not-array"})
40
- }
41
-
42
- test_deny_resources_item_not_string if {
43
- count(kustomization.deny) > 0 with input as object.union(base_kust, {"resources": [
44
- "a.yaml",
45
- {"obj": "not allowed"},
46
- ]})
47
- }
48
-
49
- # ── patches sort ─────────────────────────────────────────────────────────
50
-
51
- test_deny_patches_unsorted_by_kind_name if {
52
- count(kustomization.deny) > 0 with input as object.union(base_kust, {"patches": [
53
- {"target": {"kind": "Deployment", "name": "z"}},
54
- {"target": {"kind": "Deployment", "name": "a"}},
55
- ]})
56
- }
57
-
58
- test_allow_patches_sorted_by_kind_name if {
59
- count(kustomization.deny) == 0 with input as object.union(base_kust, {"patches": [
60
- {"target": {"kind": "Deployment", "name": "a"}},
61
- {"target": {"kind": "Deployment", "name": "z"}},
62
- ]})
63
- }
64
-
65
- test_deny_patches_not_array if {
66
- count(kustomization.deny) > 0 with input as object.union(base_kust, {"patches": "string-not-array"})
67
- }
68
-
69
- # ── JSON6902 remove+add conflict ─────────────────────────────────────────
70
-
71
- test_deny_json6902_remove_and_add_same_path if {
72
- patch_text := concat("\n", [
73
- "- op: remove",
74
- " path: /spec/replicas",
75
- "- op: add",
76
- " path: /spec/replicas",
77
- " value: 3",
78
- ])
79
- count(kustomization.deny) > 0 with input as object.union(base_kust, {"patches": [{
80
- "target": {"kind": "Deployment", "name": "api"},
81
- "patch": patch_text,
82
- }]})
83
- }
84
-
85
- test_allow_json6902_replace_same_path if {
86
- patch_text := concat("\n", [
87
- "- op: replace",
88
- " path: /spec/replicas",
89
- " value: 3",
90
- ])
91
- count(kustomization.deny) == 0 with input as object.union(base_kust, {"patches": [{
92
- "target": {"kind": "Deployment", "name": "api"},
93
- "patch": patch_text,
94
- }]})
95
- }
96
-
97
- test_allow_json6902_remove_and_add_different_paths if {
98
- patch_text := concat("\n", [
99
- "- op: remove",
100
- " path: /spec/replicas",
101
- "- op: add",
102
- " path: /spec/strategy",
103
- " value: {}",
104
- ])
105
- count(kustomization.deny) == 0 with input as object.union(base_kust, {"patches": [{
106
- "target": {"kind": "Deployment", "name": "api"},
107
- "patch": patch_text,
108
- }]})
109
- }
110
-
111
- # Маніфест не Kustomization — правила не діють.
112
- test_allow_non_kustomization if {
113
- count(kustomization.deny) == 0 with input as {
114
- "apiVersion": "v1",
115
- "kind": "ConfigMap",
116
- "metadata": {"name": "x"},
117
- "data": {"key": "value"},
118
- }
119
- }
120
-
121
- # Не той apiVersion — правила не діють.
122
- test_allow_kustomization_other_api_version if {
123
- count(kustomization.deny) == 0 with input as {
124
- "apiVersion": "other.example.com/v1",
125
- "kind": "Kustomization",
126
- "resources": ["z.yaml", "a.yaml"],
127
- }
128
- }