@intentius/chant-lexicon-k8s 0.124.31 → 0.124.32

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 (270) hide show
  1. package/dist/actions/apps.js +18 -0
  2. package/dist/actions/batch.js +6 -0
  3. package/dist/actions/core.js +48 -0
  4. package/dist/actions/index.js +13 -0
  5. package/dist/actions/networking.js +11 -0
  6. package/dist/actions/rbac.js +8 -0
  7. package/dist/api/classify.js +123 -0
  8. package/dist/api/connect.js +62 -0
  9. package/dist/api/fake-cluster.js +175 -0
  10. package/dist/api/operation-surface.d.ts.map +1 -1
  11. package/dist/api/operation-surface.js +109 -0
  12. package/dist/api/owner-chain.js +100 -0
  13. package/dist/api/sweep-types.js +35 -0
  14. package/dist/behaviour-kinds.js +82 -0
  15. package/dist/codegen/docs-cli.js +2 -0
  16. package/dist/codegen/docs.js +146 -0
  17. package/dist/codegen/generate-cli.js +30 -0
  18. package/dist/codegen/generate-lexicon.js +74 -0
  19. package/dist/codegen/generate-list-map-keys.js +88 -0
  20. package/dist/codegen/generate-operations.js +53 -0
  21. package/dist/codegen/generate-typescript.js +124 -0
  22. package/dist/codegen/generate.js +173 -0
  23. package/dist/codegen/naming.js +174 -0
  24. package/dist/codegen/package.js +36 -0
  25. package/dist/codegen/patches.js +88 -0
  26. package/dist/codegen/typecheck.js +4 -0
  27. package/dist/codegen/versions.js +41 -0
  28. package/dist/components/argo-app.js +51 -0
  29. package/dist/components/builders.js +22 -0
  30. package/dist/components/capability-plugin.js +31 -0
  31. package/dist/components/flux-reconcile.js +61 -0
  32. package/dist/components/index.d.ts +1 -1
  33. package/dist/components/index.d.ts.map +1 -1
  34. package/dist/components/index.js +18 -0
  35. package/dist/components/kubectl-apply.d.ts +83 -1
  36. package/dist/components/kubectl-apply.d.ts.map +1 -1
  37. package/dist/components/kubectl-apply.js +274 -0
  38. package/dist/components/kustomize-apply.js +25 -0
  39. package/dist/composites/adot-collector.js +184 -0
  40. package/dist/composites/agic-ingress.js +85 -0
  41. package/dist/composites/aks-external-dns-agent.js +146 -0
  42. package/dist/composites/alb-ingress.js +86 -0
  43. package/dist/composites/argo-app.js +199 -0
  44. package/dist/composites/autoscaled-service.js +174 -0
  45. package/dist/composites/azure-disk-storage-class.js +47 -0
  46. package/dist/composites/azure-file-storage-class.js +48 -0
  47. package/dist/composites/azure-monitor-collector.js +176 -0
  48. package/dist/composites/batch-job.js +139 -0
  49. package/dist/composites/catalog.js +4110 -0
  50. package/dist/composites/cockroachdb-cluster.js +371 -0
  51. package/dist/composites/cockroachdb-region-stack.js +369 -0
  52. package/dist/composites/config-connector-context.js +40 -0
  53. package/dist/composites/configured-app.js +135 -0
  54. package/dist/composites/cron-schedule.js +38 -0
  55. package/dist/composites/cron-workload.js +109 -0
  56. package/dist/composites/ebs-storage-class.js +54 -0
  57. package/dist/composites/efs-storage-class.js +46 -0
  58. package/dist/composites/external-dns-agent.js +122 -0
  59. package/dist/composites/filestore-storage-class.js +49 -0
  60. package/dist/composites/fluent-bit-agent.js +157 -0
  61. package/dist/composites/flux-app.js +156 -0
  62. package/dist/composites/gce-ingress.js +83 -0
  63. package/dist/composites/gce-pd-storage-class.js +49 -0
  64. package/dist/composites/gke-external-dns-agent.js +129 -0
  65. package/dist/composites/gke-fluent-bit-agent.js +165 -0
  66. package/dist/composites/gke-gateway.js +91 -0
  67. package/dist/composites/gke-otel-collector.js +94 -0
  68. package/dist/composites/index.js +51 -0
  69. package/dist/composites/inference-service.js +76 -0
  70. package/dist/composites/irsa-service-account.js +77 -0
  71. package/dist/composites/member-defaults.js +14 -0
  72. package/dist/composites/metrics-server.js +181 -0
  73. package/dist/composites/model.js +92 -0
  74. package/dist/composites/monitored-service.js +152 -0
  75. package/dist/composites/namespace-env.js +114 -0
  76. package/dist/composites/network-isolated-app.js +124 -0
  77. package/dist/composites/node-agent.js +149 -0
  78. package/dist/composites/operator-stack.js +268 -0
  79. package/dist/composites/otel-collector-agent.js +142 -0
  80. package/dist/composites/otel-collector-gateway.js +216 -0
  81. package/dist/composites/otel-collector-node.js +176 -0
  82. package/dist/composites/otel-collector-rbac.js +115 -0
  83. package/dist/composites/otel-collector-shape.js +128 -0
  84. package/dist/composites/otel-collector.js +115 -0
  85. package/dist/composites/otel-operator-collector.js +123 -0
  86. package/dist/composites/ray-cluster.js +378 -0
  87. package/dist/composites/ray-job.js +136 -0
  88. package/dist/composites/ray-service.js +178 -0
  89. package/dist/composites/secure-ingress.js +100 -0
  90. package/dist/composites/security-context.js +1 -0
  91. package/dist/composites/sidecar-app.js +105 -0
  92. package/dist/composites/stateful-app.js +111 -0
  93. package/dist/composites/vllm-serving-runtime.js +114 -0
  94. package/dist/composites/web-app.js +157 -0
  95. package/dist/composites/worker-pool.js +167 -0
  96. package/dist/composites/workload-identity-sa.js +81 -0
  97. package/dist/composites/workload-identity-service-account.js +77 -0
  98. package/dist/config-schema.js +72 -0
  99. package/dist/config.js +59 -0
  100. package/dist/coverage.js +27 -0
  101. package/dist/crd/crd-sources.js +615 -0
  102. package/dist/crd/loader.js +181 -0
  103. package/dist/crd/oci-chart.js +144 -0
  104. package/dist/crd/parser.js +382 -0
  105. package/dist/crd/project-codegen.js +273 -0
  106. package/dist/crd/types.js +7 -0
  107. package/dist/deep-observe-hooks.js +306 -0
  108. package/dist/deep-observe.js +234 -0
  109. package/dist/default-labels.js +87 -0
  110. package/dist/describe-resources.js +699 -0
  111. package/dist/describe-stack-status.js +99 -0
  112. package/dist/detect.js +11 -0
  113. package/dist/effect-receipt-row.js +205 -0
  114. package/dist/export-resources.js +39 -0
  115. package/dist/generated/index.js +1092 -0
  116. package/dist/generated/runtime.js +4 -0
  117. package/dist/group-namespace.js +96 -0
  118. package/dist/import/embedded.js +197 -0
  119. package/dist/import/generator.js +288 -0
  120. package/dist/import/live-export.js +85 -0
  121. package/dist/import/parser.js +204 -0
  122. package/dist/import/testdata/embedded/fixtures.js +60 -0
  123. package/dist/include/entity.js +78 -0
  124. package/dist/include/resolve.js +170 -0
  125. package/dist/index.js +59 -0
  126. package/dist/integrity.json +3 -3
  127. package/dist/kube/apply.js +90 -0
  128. package/dist/kube/connect.js +46 -0
  129. package/dist/kube/delete.js +138 -0
  130. package/dist/kube/describe.js +169 -0
  131. package/dist/kube/events.js +132 -0
  132. package/dist/kube/flags.js +91 -0
  133. package/dist/kube/get.js +159 -0
  134. package/dist/kube/group.js +123 -0
  135. package/dist/kube/logs.js +82 -0
  136. package/dist/kube/project.js +94 -0
  137. package/dist/kube/render.js +227 -0
  138. package/dist/kube/source.js +122 -0
  139. package/dist/kube/target.js +63 -0
  140. package/dist/kube/testing.js +28 -0
  141. package/dist/kube/top.js +99 -0
  142. package/dist/kube/verdict.js +72 -0
  143. package/dist/kube/wait.js +129 -0
  144. package/dist/kustomize/render.js +73 -0
  145. package/dist/kustomize/rendered-entity.js +67 -0
  146. package/dist/kustomize/root.js +68 -0
  147. package/dist/lint/audit-catalog.js +61 -0
  148. package/dist/lint/audit-lineage.js +118 -0
  149. package/dist/lint/post-synth/argo-helpers.js +36 -0
  150. package/dist/lint/post-synth/argo002.js +31 -0
  151. package/dist/lint/post-synth/argo003.js +53 -0
  152. package/dist/lint/post-synth/argo005.js +54 -0
  153. package/dist/lint/post-synth/crd-schema-helpers.d.ts.map +1 -1
  154. package/dist/lint/post-synth/crd-schema-helpers.js +214 -0
  155. package/dist/lint/post-synth/flux002.js +56 -0
  156. package/dist/lint/post-synth/flux003.js +50 -0
  157. package/dist/lint/post-synth/index.js +104 -0
  158. package/dist/lint/post-synth/k8s-helpers.js +186 -0
  159. package/dist/lint/post-synth/monitoring-selection-helpers.js +163 -0
  160. package/dist/lint/post-synth/otel-placement-helpers.js +293 -0
  161. package/dist/lint/post-synth/sops-helpers.js +43 -0
  162. package/dist/lint/post-synth/wk8005.js +44 -0
  163. package/dist/lint/post-synth/wk8006.js +56 -0
  164. package/dist/lint/post-synth/wk8041.js +60 -0
  165. package/dist/lint/post-synth/wk8042.js +36 -0
  166. package/dist/lint/post-synth/wk8101.js +52 -0
  167. package/dist/lint/post-synth/wk8102.js +30 -0
  168. package/dist/lint/post-synth/wk8103.js +33 -0
  169. package/dist/lint/post-synth/wk8104.js +58 -0
  170. package/dist/lint/post-synth/wk8105.js +33 -0
  171. package/dist/lint/post-synth/wk8201.js +44 -0
  172. package/dist/lint/post-synth/wk8202.js +34 -0
  173. package/dist/lint/post-synth/wk8203.js +34 -0
  174. package/dist/lint/post-synth/wk8204.js +71 -0
  175. package/dist/lint/post-synth/wk8205.js +38 -0
  176. package/dist/lint/post-synth/wk8207.js +33 -0
  177. package/dist/lint/post-synth/wk8208.js +33 -0
  178. package/dist/lint/post-synth/wk8209.js +33 -0
  179. package/dist/lint/post-synth/wk8301.js +49 -0
  180. package/dist/lint/post-synth/wk8302.js +34 -0
  181. package/dist/lint/post-synth/wk8303.js +68 -0
  182. package/dist/lint/post-synth/wk8304.js +58 -0
  183. package/dist/lint/post-synth/wk8305.js +96 -0
  184. package/dist/lint/post-synth/wk8306.js +38 -0
  185. package/dist/lint/post-synth/wk8401.js +86 -0
  186. package/dist/lint/post-synth/wk8402.js +31 -0
  187. package/dist/lint/post-synth/wk8403.js +49 -0
  188. package/dist/lint/post-synth/wk8404.js +36 -0
  189. package/dist/lint/post-synth/wk8405.js +65 -0
  190. package/dist/lint/post-synth/wk8406.js +50 -0
  191. package/dist/lint/post-synth/wk8407.js +58 -0
  192. package/dist/lint/post-synth/wk8501.js +36 -0
  193. package/dist/lint/post-synth/wk8502.js +35 -0
  194. package/dist/lint/post-synth/wk8503.js +164 -0
  195. package/dist/lint/post-synth/wk8504.js +20 -0
  196. package/dist/lint/post-synth/wk8505.js +31 -0
  197. package/dist/lint/post-synth/wk8601.js +40 -0
  198. package/dist/lint/post-synth/wk8602.js +120 -0
  199. package/dist/lint/post-synth/wk8603.js +63 -0
  200. package/dist/lint/post-synth/wk8604.js +31 -0
  201. package/dist/lint/post-synth/wk8605.js +124 -0
  202. package/dist/lint/post-synth/wk8606.js +22 -0
  203. package/dist/lint/post-synth/wk8701.js +31 -0
  204. package/dist/lint/post-synth/wk8702.js +37 -0
  205. package/dist/lint/post-synth/wk8703.js +31 -0
  206. package/dist/lint/post-synth/wk8704.js +135 -0
  207. package/dist/lint/post-synth/wk8705.js +38 -0
  208. package/dist/lint/rules/argo-appset-single-project.js +56 -0
  209. package/dist/lint/rules/argo-ast.js +94 -0
  210. package/dist/lint/rules/argo-automated-prune.js +59 -0
  211. package/dist/lint/rules/flux-source-ref-pin.js +44 -0
  212. package/dist/lint/rules/hardcoded-namespace.js +57 -0
  213. package/dist/lint/rules/latest-image-tag.js +103 -0
  214. package/dist/lint/rules/missing-resource-limits.js +85 -0
  215. package/dist/lsp/completions.d.ts.map +1 -1
  216. package/dist/lsp/completions.js +18 -0
  217. package/dist/lsp/hover.d.ts.map +1 -1
  218. package/dist/lsp/hover.js +57 -0
  219. package/dist/manifest-entity.js +71 -0
  220. package/dist/manifest.json +1 -1
  221. package/dist/micro-time.js +49 -0
  222. package/dist/op/activities/apply-diff.d.ts +31 -0
  223. package/dist/op/activities/apply-diff.d.ts.map +1 -0
  224. package/dist/op/activities/apply-diff.js +79 -0
  225. package/dist/op/activities/argo.js +98 -0
  226. package/dist/op/activities/ensure-secret.js +38 -0
  227. package/dist/op/activities/index.js +51 -0
  228. package/dist/op/activities/kubectl.d.ts +64 -0
  229. package/dist/op/activities/kubectl.d.ts.map +1 -1
  230. package/dist/op/activities/kubectl.js +517 -0
  231. package/dist/op/activities/wait-for-ready.js +229 -0
  232. package/dist/op/builders.js +68 -0
  233. package/dist/package-cli.js +21 -0
  234. package/dist/patch/json-patch.js +187 -0
  235. package/dist/patch/rendered-patch.js +94 -0
  236. package/dist/plugin.js +741 -0
  237. package/dist/project-kinds.js +56 -0
  238. package/dist/receipt-store.js +216 -0
  239. package/dist/rules/crd-schema-helpers.ts +2 -1
  240. package/dist/secret-labels.js +33 -0
  241. package/dist/secret-store.js +116 -0
  242. package/dist/serializer.d.ts.map +1 -1
  243. package/dist/serializer.js +578 -0
  244. package/dist/sops/encrypted-secret-file.js +194 -0
  245. package/dist/sops/entity.js +32 -0
  246. package/dist/spec/fetch.js +58 -0
  247. package/dist/spec/parse.js +622 -0
  248. package/dist/subscribe-changes.js +168 -0
  249. package/dist/teardown.js +197 -0
  250. package/dist/telemetry-release.js +86 -0
  251. package/dist/validate-cli.js +17 -0
  252. package/dist/validate.js +47 -0
  253. package/dist/variables.js +34 -0
  254. package/dist/whoami.js +109 -0
  255. package/package.json +12 -12
  256. package/src/api/operation-surface.ts +2 -1
  257. package/src/codegen/package.ts +2 -2
  258. package/src/components/index.ts +3 -0
  259. package/src/components/kubectl-apply-plan.k3d.e2e.test.ts +127 -0
  260. package/src/components/kubectl-apply.test.ts +262 -0
  261. package/src/components/kubectl-apply.ts +341 -12
  262. package/src/deep-observe-hooks.ts +2 -2
  263. package/src/import/roundtrip-build.test.ts +46 -0
  264. package/src/lint/post-synth/crd-schema-helpers.ts +2 -1
  265. package/src/lsp/completions.ts +2 -1
  266. package/src/lsp/hover.ts +2 -1
  267. package/src/op/activities/apply-diff.ts +87 -0
  268. package/src/op/activities/kubectl-plan.test.ts +224 -0
  269. package/src/op/activities/kubectl.ts +204 -16
  270. package/src/serializer.ts +2 -1
@@ -0,0 +1,36 @@
1
+ /**
2
+ * WK8042: Private Keys in ConfigMap
3
+ *
4
+ * Detects PEM-encoded private keys stored in ConfigMap data values.
5
+ * Private keys should be stored in Secrets, not ConfigMaps, since
6
+ * ConfigMaps are not encrypted at rest by default.
7
+ */
8
+ import { docsToManifests } from "./k8s-helpers.js";
9
+ const PRIVATE_KEY_PATTERN = /-----BEGIN\s+[\w\s]*PRIVATE KEY-----/;
10
+ export const wk8042 = {
11
+ id: "WK8042",
12
+ description: "Private keys in ConfigMap — private keys should be stored in Secrets, not ConfigMaps",
13
+ check(ctx) {
14
+ const diagnostics = [];
15
+ for (const manifest of docsToManifests(ctx)) {
16
+ if (manifest.kind !== "ConfigMap")
17
+ continue;
18
+ const resourceName = manifest.metadata?.name ?? "ConfigMap";
19
+ const data = manifest.data;
20
+ if (typeof data !== "object" || data === null)
21
+ continue;
22
+ for (const [key, value] of Object.entries(data)) {
23
+ if (typeof value === "string" && PRIVATE_KEY_PATTERN.test(value)) {
24
+ diagnostics.push({
25
+ checkId: "WK8042",
26
+ severity: "error",
27
+ message: `ConfigMap "${resourceName}" contains a private key in data field "${key}" — use a Secret resource instead`,
28
+ entity: resourceName,
29
+ lexicon: "k8s",
30
+ });
31
+ }
32
+ }
33
+ }
34
+ return diagnostics;
35
+ },
36
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * WK8101: Deployment Selector Must Match Template Labels
3
+ *
4
+ * A Deployment's spec.selector.matchLabels must be a subset of
5
+ * spec.template.metadata.labels. If they don't match, the Deployment
6
+ * controller cannot find the Pods it creates, causing a runtime failure.
7
+ */
8
+ import { docsToManifests } from "./k8s-helpers.js";
9
+ export const wk8101 = {
10
+ id: "WK8101",
11
+ description: "Deployment selector must match template labels — mismatched selectors cause runtime failures",
12
+ check(ctx) {
13
+ const diagnostics = [];
14
+ for (const manifest of docsToManifests(ctx)) {
15
+ if (manifest.kind !== "Deployment")
16
+ continue;
17
+ const resourceName = manifest.metadata?.name ?? "Deployment";
18
+ const spec = manifest.spec;
19
+ if (!spec)
20
+ continue;
21
+ const selector = spec.selector;
22
+ const matchLabels = selector?.matchLabels;
23
+ if (!matchLabels || typeof matchLabels !== "object")
24
+ continue;
25
+ const template = spec.template;
26
+ const templateMetadata = template?.metadata;
27
+ const templateLabels = templateMetadata?.labels;
28
+ if (!templateLabels || typeof templateLabels !== "object") {
29
+ diagnostics.push({
30
+ checkId: "WK8101",
31
+ severity: "error",
32
+ message: `Deployment "${resourceName}" has spec.selector.matchLabels but no spec.template.metadata.labels`,
33
+ entity: resourceName,
34
+ lexicon: "k8s",
35
+ });
36
+ continue;
37
+ }
38
+ for (const [key, value] of Object.entries(matchLabels)) {
39
+ if (templateLabels[key] !== value) {
40
+ diagnostics.push({
41
+ checkId: "WK8101",
42
+ severity: "error",
43
+ message: `Deployment "${resourceName}" selector label "${key}=${value}" does not match template label "${key}=${templateLabels[key] ?? '(missing)'}"`,
44
+ entity: resourceName,
45
+ lexicon: "k8s",
46
+ });
47
+ }
48
+ }
49
+ }
50
+ return diagnostics;
51
+ },
52
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * WK8102: Resources Should Have Metadata Labels
3
+ *
4
+ * All Kubernetes resources should have metadata.labels for organizational
5
+ * purposes. Labels enable filtering, selection, and operational tooling.
6
+ */
7
+ import { docsToManifests } from "./k8s-helpers.js";
8
+ export const wk8102 = {
9
+ id: "WK8102",
10
+ description: "Resources should have metadata labels — labels enable filtering and operational tooling",
11
+ check(ctx) {
12
+ const diagnostics = [];
13
+ for (const manifest of docsToManifests(ctx)) {
14
+ if (!manifest.kind || !manifest.metadata)
15
+ continue;
16
+ const resourceName = manifest.metadata.name ?? manifest.kind;
17
+ const labels = manifest.metadata.labels;
18
+ if (!labels || typeof labels !== "object" || Object.keys(labels).length === 0) {
19
+ diagnostics.push({
20
+ checkId: "WK8102",
21
+ severity: "warning",
22
+ message: `${manifest.kind} "${resourceName}" has no metadata.labels — add labels for organizational purposes`,
23
+ entity: resourceName,
24
+ lexicon: "k8s",
25
+ });
26
+ }
27
+ }
28
+ return diagnostics;
29
+ },
30
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * WK8103: Containers Must Have Name
3
+ *
4
+ * Every container in a pod spec must have a `name` field. This is required
5
+ * by the Kubernetes API and will be rejected at apply time if missing.
6
+ */
7
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers.js";
8
+ export const wk8103 = {
9
+ id: "WK8103",
10
+ description: "Containers must have name — the name field is required by the Kubernetes API",
11
+ check(ctx) {
12
+ const diagnostics = [];
13
+ for (const manifest of docsToManifests(ctx)) {
14
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind))
15
+ continue;
16
+ const containers = extractContainers(manifest);
17
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
18
+ for (let i = 0; i < containers.length; i++) {
19
+ const container = containers[i];
20
+ if (!container.name || typeof container.name !== "string" || container.name.trim() === "") {
21
+ diagnostics.push({
22
+ checkId: "WK8103",
23
+ severity: "error",
24
+ message: `${manifest.kind} "${resourceName}" has a container at index ${i} without a name — the name field is required`,
25
+ entity: resourceName,
26
+ lexicon: "k8s",
27
+ });
28
+ }
29
+ }
30
+ }
31
+ return diagnostics;
32
+ },
33
+ };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * WK8104: Ports Should Be Named
3
+ *
4
+ * Container ports and Service ports should have names. Named ports enable
5
+ * referencing by name in Service targetPort and NetworkPolicy rules,
6
+ * improving maintainability.
7
+ */
8
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers.js";
9
+ export const wk8104 = {
10
+ id: "WK8104",
11
+ description: "Ports should be named — named ports improve Service and NetworkPolicy configuration",
12
+ check(ctx) {
13
+ const diagnostics = [];
14
+ for (const manifest of docsToManifests(ctx)) {
15
+ const resourceName = manifest.metadata?.name ?? manifest.kind ?? "(unknown)";
16
+ // Check container ports in workloads
17
+ if (manifest.kind && WORKLOAD_KINDS.has(manifest.kind)) {
18
+ const containers = extractContainers(manifest);
19
+ for (const container of containers) {
20
+ if (!Array.isArray(container.ports))
21
+ continue;
22
+ for (const port of container.ports) {
23
+ if (typeof port === "object" && port !== null && !port.name) {
24
+ diagnostics.push({
25
+ checkId: "WK8104",
26
+ severity: "warning",
27
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has an unnamed port (containerPort: ${port.containerPort ?? "?"}) — add a name for clarity`,
28
+ entity: resourceName,
29
+ lexicon: "k8s",
30
+ });
31
+ }
32
+ }
33
+ }
34
+ }
35
+ // Check Service ports
36
+ if (manifest.kind === "Service") {
37
+ const spec = manifest.spec;
38
+ if (!spec)
39
+ continue;
40
+ const ports = spec.ports;
41
+ if (!Array.isArray(ports))
42
+ continue;
43
+ for (const port of ports) {
44
+ if (typeof port === "object" && port !== null && !port.name) {
45
+ diagnostics.push({
46
+ checkId: "WK8104",
47
+ severity: "warning",
48
+ message: `Service "${resourceName}" has an unnamed port (port: ${port.port ?? "?"}) — add a name for clarity`,
49
+ entity: resourceName,
50
+ lexicon: "k8s",
51
+ });
52
+ }
53
+ }
54
+ }
55
+ }
56
+ return diagnostics;
57
+ },
58
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * WK8105: ImagePullPolicy Should Be Explicit
3
+ *
4
+ * Containers should have an explicit imagePullPolicy. When omitted,
5
+ * Kubernetes defaults to "Always" for :latest and "IfNotPresent" for
6
+ * other tags, which can lead to unexpected behavior.
7
+ */
8
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers.js";
9
+ export const wk8105 = {
10
+ id: "WK8105",
11
+ description: "ImagePullPolicy should be explicit — avoids surprising default behavior",
12
+ check(ctx) {
13
+ const diagnostics = [];
14
+ for (const manifest of docsToManifests(ctx)) {
15
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind))
16
+ continue;
17
+ const containers = extractContainers(manifest);
18
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
19
+ for (const container of containers) {
20
+ if (!container.imagePullPolicy) {
21
+ diagnostics.push({
22
+ checkId: "WK8105",
23
+ severity: "warning",
24
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has no explicit imagePullPolicy — set it to "Always", "IfNotPresent", or "Never"`,
25
+ entity: resourceName,
26
+ lexicon: "k8s",
27
+ });
28
+ }
29
+ }
30
+ }
31
+ return diagnostics;
32
+ },
33
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * WK8201: Resource Limits Required
3
+ *
4
+ * Containers should have CPU and memory limits defined in resources.limits.
5
+ * Without limits, a container can consume unbounded resources, potentially
6
+ * affecting other workloads on the node.
7
+ */
8
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers.js";
9
+ export const wk8201 = {
10
+ id: "WK8201",
11
+ description: "Resource limits required — containers should have CPU and memory limits",
12
+ check(ctx) {
13
+ const diagnostics = [];
14
+ for (const manifest of docsToManifests(ctx)) {
15
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind))
16
+ continue;
17
+ const containers = extractContainers(manifest);
18
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
19
+ for (const container of containers) {
20
+ const limits = container.resources?.limits;
21
+ const missing = [];
22
+ if (!limits || typeof limits !== "object") {
23
+ missing.push("cpu", "memory");
24
+ }
25
+ else {
26
+ if (!limits.cpu)
27
+ missing.push("cpu");
28
+ if (!limits.memory)
29
+ missing.push("memory");
30
+ }
31
+ if (missing.length > 0) {
32
+ diagnostics.push({
33
+ checkId: "WK8201",
34
+ severity: "warning",
35
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" is missing resource limits for ${missing.join(", ")}`,
36
+ entity: resourceName,
37
+ lexicon: "k8s",
38
+ });
39
+ }
40
+ }
41
+ }
42
+ return diagnostics;
43
+ },
44
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * WK8202: No Privileged Containers
3
+ *
4
+ * Containers should not run in privileged mode. Privileged containers
5
+ * have full access to the host's devices and kernel capabilities,
6
+ * which is a significant security risk.
7
+ */
8
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers.js";
9
+ export const wk8202 = {
10
+ id: "WK8202",
11
+ description: "No privileged containers — privileged mode grants full host access",
12
+ check(ctx) {
13
+ const diagnostics = [];
14
+ for (const manifest of docsToManifests(ctx)) {
15
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind))
16
+ continue;
17
+ const containers = extractContainers(manifest);
18
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
19
+ for (const container of containers) {
20
+ const secCtx = container.securityContext;
21
+ if (secCtx && secCtx.privileged === true) {
22
+ diagnostics.push({
23
+ checkId: "WK8202",
24
+ severity: "error",
25
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" runs in privileged mode — this grants full host access and should be avoided`,
26
+ entity: resourceName,
27
+ lexicon: "k8s",
28
+ });
29
+ }
30
+ }
31
+ }
32
+ return diagnostics;
33
+ },
34
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * WK8203: ReadOnlyRootFilesystem Recommended
3
+ *
4
+ * Containers should set securityContext.readOnlyRootFilesystem to true.
5
+ * A read-only root filesystem prevents runtime modification of the container
6
+ * image, reducing the attack surface.
7
+ */
8
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers.js";
9
+ export const wk8203 = {
10
+ id: "WK8203",
11
+ description: "ReadOnlyRootFilesystem recommended — prevents runtime modification of the container image",
12
+ check(ctx) {
13
+ const diagnostics = [];
14
+ for (const manifest of docsToManifests(ctx)) {
15
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind))
16
+ continue;
17
+ const containers = extractContainers(manifest);
18
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
19
+ for (const container of containers) {
20
+ const secCtx = container.securityContext;
21
+ if (!secCtx || secCtx.readOnlyRootFilesystem !== true) {
22
+ diagnostics.push({
23
+ checkId: "WK8203",
24
+ severity: "warning",
25
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" does not set readOnlyRootFilesystem: true — set it to reduce the attack surface`,
26
+ entity: resourceName,
27
+ lexicon: "k8s",
28
+ });
29
+ }
30
+ }
31
+ }
32
+ return diagnostics;
33
+ },
34
+ };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * WK8204: RunAsNonRoot Recommended
3
+ *
4
+ * Containers should set securityContext.runAsNonRoot to true at either
5
+ * the container level or pod level. Running as root inside a container
6
+ * increases the blast radius of a container breakout.
7
+ *
8
+ * Additionally warns when runAsNonRoot: true is set but no explicit
9
+ * runAsUser is provided — without a numeric UID, K8s relies on the
10
+ * image's USER directive, which may be root (UID 0).
11
+ */
12
+ import { docsToManifests, extractContainers, extractPodSpec, WORKLOAD_KINDS } from "./k8s-helpers.js";
13
+ export const wk8204 = {
14
+ id: "WK8204",
15
+ description: "RunAsNonRoot recommended — running as root increases container breakout risk",
16
+ check(ctx) {
17
+ const diagnostics = [];
18
+ for (const manifest of docsToManifests(ctx)) {
19
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind))
20
+ continue;
21
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
22
+ const podSpec = extractPodSpec(manifest);
23
+ if (!podSpec)
24
+ continue;
25
+ // Check pod-level securityContext
26
+ const podSecCtx = podSpec.securityContext;
27
+ const podRunAsNonRoot = podSecCtx?.runAsNonRoot === true;
28
+ const podRunAsUser = podSecCtx?.runAsUser;
29
+ const containers = extractContainers(manifest);
30
+ for (const container of containers) {
31
+ const secCtx = container.securityContext;
32
+ const containerRunAsNonRoot = secCtx?.runAsNonRoot === true;
33
+ const containerRunAsUser = secCtx?.runAsUser;
34
+ const hasRunAsNonRoot = podRunAsNonRoot || containerRunAsNonRoot;
35
+ if (!hasRunAsNonRoot) {
36
+ diagnostics.push({
37
+ checkId: "WK8204",
38
+ severity: "warning",
39
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" does not set runAsNonRoot: true — set it at container or pod level`,
40
+ entity: resourceName,
41
+ lexicon: "k8s",
42
+ });
43
+ continue;
44
+ }
45
+ // runAsNonRoot is true — check for explicit runAsUser
46
+ const effectiveRunAsUser = containerRunAsUser ?? podRunAsUser;
47
+ if (effectiveRunAsUser === 0) {
48
+ // Contradictory: runAsNonRoot: true + runAsUser: 0
49
+ diagnostics.push({
50
+ checkId: "WK8204",
51
+ severity: "warning",
52
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has runAsNonRoot: true but runAsUser: 0 — these settings are contradictory and the container will fail to start`,
53
+ entity: resourceName,
54
+ lexicon: "k8s",
55
+ });
56
+ }
57
+ else if (effectiveRunAsUser === undefined || effectiveRunAsUser === null) {
58
+ // runAsNonRoot: true but no explicit UID
59
+ diagnostics.push({
60
+ checkId: "WK8204",
61
+ severity: "warning",
62
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has runAsNonRoot: true but no explicit runAsUser — set a numeric UID to ensure the container doesn't run as root`,
63
+ entity: resourceName,
64
+ lexicon: "k8s",
65
+ });
66
+ }
67
+ }
68
+ }
69
+ return diagnostics;
70
+ },
71
+ };
@@ -0,0 +1,38 @@
1
+ /**
2
+ * WK8205: Drop ALL Capabilities
3
+ *
4
+ * Containers should drop all Linux capabilities and only add back those
5
+ * explicitly needed. This follows the principle of least privilege and
6
+ * reduces the attack surface.
7
+ */
8
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers.js";
9
+ export const wk8205 = {
10
+ id: "WK8205",
11
+ description: "Drop ALL capabilities — containers should drop all capabilities and add only what is needed",
12
+ check(ctx) {
13
+ const diagnostics = [];
14
+ for (const manifest of docsToManifests(ctx)) {
15
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind))
16
+ continue;
17
+ const containers = extractContainers(manifest);
18
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
19
+ for (const container of containers) {
20
+ const secCtx = container.securityContext;
21
+ const capabilities = secCtx?.capabilities;
22
+ const drop = capabilities?.drop;
23
+ const dropsAll = Array.isArray(drop) &&
24
+ drop.some((cap) => (typeof cap === "string" && cap.toUpperCase() === "ALL"));
25
+ if (!dropsAll) {
26
+ diagnostics.push({
27
+ checkId: "WK8205",
28
+ severity: "warning",
29
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" does not drop ALL capabilities — add securityContext.capabilities.drop: ["ALL"]`,
30
+ entity: resourceName,
31
+ lexicon: "k8s",
32
+ });
33
+ }
34
+ }
35
+ }
36
+ return diagnostics;
37
+ },
38
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * WK8207: No hostNetwork
3
+ *
4
+ * Pods should not use hostNetwork: true. Using the host network namespace
5
+ * bypasses network isolation and allows the pod to access all network
6
+ * interfaces on the host.
7
+ */
8
+ import { docsToManifests, extractPodSpec, WORKLOAD_KINDS } from "./k8s-helpers.js";
9
+ export const wk8207 = {
10
+ id: "WK8207",
11
+ description: "No hostNetwork — using host network bypasses network isolation",
12
+ check(ctx) {
13
+ const diagnostics = [];
14
+ for (const manifest of docsToManifests(ctx)) {
15
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind))
16
+ continue;
17
+ const podSpec = extractPodSpec(manifest);
18
+ if (!podSpec)
19
+ continue;
20
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
21
+ if (podSpec.hostNetwork === true) {
22
+ diagnostics.push({
23
+ checkId: "WK8207",
24
+ severity: "warning",
25
+ message: `${manifest.kind} "${resourceName}" uses hostNetwork: true — this bypasses network isolation and should be avoided`,
26
+ entity: resourceName,
27
+ lexicon: "k8s",
28
+ });
29
+ }
30
+ }
31
+ return diagnostics;
32
+ },
33
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * WK8208: No hostPID
3
+ *
4
+ * Pods should not use hostPID: true. Sharing the host PID namespace
5
+ * allows the pod to see and interact with all processes on the host,
6
+ * which is a significant security risk.
7
+ */
8
+ import { docsToManifests, extractPodSpec, WORKLOAD_KINDS } from "./k8s-helpers.js";
9
+ export const wk8208 = {
10
+ id: "WK8208",
11
+ description: "No hostPID — sharing host PID namespace allows visibility into all host processes",
12
+ check(ctx) {
13
+ const diagnostics = [];
14
+ for (const manifest of docsToManifests(ctx)) {
15
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind))
16
+ continue;
17
+ const podSpec = extractPodSpec(manifest);
18
+ if (!podSpec)
19
+ continue;
20
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
21
+ if (podSpec.hostPID === true) {
22
+ diagnostics.push({
23
+ checkId: "WK8208",
24
+ severity: "warning",
25
+ message: `${manifest.kind} "${resourceName}" uses hostPID: true — this allows visibility into all host processes and should be avoided`,
26
+ entity: resourceName,
27
+ lexicon: "k8s",
28
+ });
29
+ }
30
+ }
31
+ return diagnostics;
32
+ },
33
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * WK8209: No hostIPC
3
+ *
4
+ * Pods should not use hostIPC: true. Sharing the host IPC namespace
5
+ * allows the pod to access shared memory segments on the host, which
6
+ * can be used to escalate privileges or exfiltrate data.
7
+ */
8
+ import { docsToManifests, extractPodSpec, WORKLOAD_KINDS } from "./k8s-helpers.js";
9
+ export const wk8209 = {
10
+ id: "WK8209",
11
+ description: "No hostIPC — sharing host IPC namespace can expose shared memory segments",
12
+ check(ctx) {
13
+ const diagnostics = [];
14
+ for (const manifest of docsToManifests(ctx)) {
15
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind))
16
+ continue;
17
+ const podSpec = extractPodSpec(manifest);
18
+ if (!podSpec)
19
+ continue;
20
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
21
+ if (podSpec.hostIPC === true) {
22
+ diagnostics.push({
23
+ checkId: "WK8209",
24
+ severity: "warning",
25
+ message: `${manifest.kind} "${resourceName}" uses hostIPC: true — this exposes host shared memory segments and should be avoided`,
26
+ entity: resourceName,
27
+ lexicon: "k8s",
28
+ });
29
+ }
30
+ }
31
+ return diagnostics;
32
+ },
33
+ };
@@ -0,0 +1,49 @@
1
+ /**
2
+ * WK8301: Probes Required
3
+ *
4
+ * Containers that expose a port should have both livenessProbe and
5
+ * readinessProbe configured. Without probes, Kubernetes cannot detect
6
+ * unhealthy containers or know when a container is ready to receive traffic.
7
+ *
8
+ * Containers that declare no port (queue/Temporal workers, batch consumers)
9
+ * are exempt: they take no inbound traffic, so there is no endpoint to
10
+ * HTTP-probe and no readiness signal for a Service to gate on. Flagging them
11
+ * would force a placeholder exec probe on a correct, port-less pattern.
12
+ */
13
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers.js";
14
+ export const wk8301 = {
15
+ id: "WK8301",
16
+ description: "Probes required — containers should have livenessProbe and readinessProbe",
17
+ check(ctx) {
18
+ const diagnostics = [];
19
+ for (const manifest of docsToManifests(ctx)) {
20
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind))
21
+ continue;
22
+ // CronJobs and Jobs are short-lived — probes are less relevant
23
+ if (manifest.kind === "Job" || manifest.kind === "CronJob")
24
+ continue;
25
+ const containers = extractContainers(manifest);
26
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
27
+ for (const container of containers) {
28
+ // No declared port → no inbound traffic → nothing to HTTP-probe.
29
+ if (!container.ports || container.ports.length === 0)
30
+ continue;
31
+ const missing = [];
32
+ if (!container.livenessProbe)
33
+ missing.push("livenessProbe");
34
+ if (!container.readinessProbe)
35
+ missing.push("readinessProbe");
36
+ if (missing.length > 0) {
37
+ diagnostics.push({
38
+ checkId: "WK8301",
39
+ severity: "warning",
40
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" is missing ${missing.join(" and ")}`,
41
+ entity: resourceName,
42
+ lexicon: "k8s",
43
+ });
44
+ }
45
+ }
46
+ }
47
+ return diagnostics;
48
+ },
49
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * WK8302: Replicas >= 2 for High Availability
3
+ *
4
+ * Deployments should have at least 2 replicas for high availability.
5
+ * A single replica means any pod disruption causes downtime.
6
+ */
7
+ import { docsToManifests } from "./k8s-helpers.js";
8
+ export const wk8302 = {
9
+ id: "WK8302",
10
+ description: "Replicas >= 2 recommended — single-replica Deployments have no high availability",
11
+ check(ctx) {
12
+ const diagnostics = [];
13
+ for (const manifest of docsToManifests(ctx)) {
14
+ if (manifest.kind !== "Deployment")
15
+ continue;
16
+ const resourceName = manifest.metadata?.name ?? "Deployment";
17
+ const spec = manifest.spec;
18
+ if (!spec)
19
+ continue;
20
+ const replicas = spec.replicas;
21
+ // If replicas is omitted, Kubernetes defaults to 1
22
+ if (replicas === undefined || replicas === null || replicas === 1) {
23
+ diagnostics.push({
24
+ checkId: "WK8302",
25
+ severity: "info",
26
+ message: `Deployment "${resourceName}" has ${replicas ?? 1} replica(s) — consider at least 2 for high availability`,
27
+ entity: resourceName,
28
+ lexicon: "k8s",
29
+ });
30
+ }
31
+ }
32
+ return diagnostics;
33
+ },
34
+ };