@intentius/chant-lexicon-k8s 0.49.0 → 0.50.0

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 (120) hide show
  1. package/dist/composites/index.d.ts +2 -0
  2. package/dist/composites/index.d.ts.map +1 -1
  3. package/dist/composites/operator-stack.d.ts +183 -0
  4. package/dist/composites/operator-stack.d.ts.map +1 -0
  5. package/dist/index.d.ts +2 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/integrity.json +35 -35
  8. package/dist/lint/post-synth/argo-helpers.d.ts.map +1 -1
  9. package/dist/lint/post-synth/crd-schema-helpers.d.ts.map +1 -1
  10. package/dist/lint/post-synth/k8s-helpers.d.ts +28 -5
  11. package/dist/lint/post-synth/k8s-helpers.d.ts.map +1 -1
  12. package/dist/lint/post-synth/wk8005.d.ts.map +1 -1
  13. package/dist/lint/post-synth/wk8006.d.ts.map +1 -1
  14. package/dist/lint/post-synth/wk8041.d.ts.map +1 -1
  15. package/dist/lint/post-synth/wk8042.d.ts.map +1 -1
  16. package/dist/lint/post-synth/wk8101.d.ts.map +1 -1
  17. package/dist/lint/post-synth/wk8102.d.ts.map +1 -1
  18. package/dist/lint/post-synth/wk8103.d.ts.map +1 -1
  19. package/dist/lint/post-synth/wk8104.d.ts.map +1 -1
  20. package/dist/lint/post-synth/wk8105.d.ts.map +1 -1
  21. package/dist/lint/post-synth/wk8201.d.ts.map +1 -1
  22. package/dist/lint/post-synth/wk8202.d.ts.map +1 -1
  23. package/dist/lint/post-synth/wk8203.d.ts.map +1 -1
  24. package/dist/lint/post-synth/wk8204.d.ts.map +1 -1
  25. package/dist/lint/post-synth/wk8205.d.ts.map +1 -1
  26. package/dist/lint/post-synth/wk8207.d.ts.map +1 -1
  27. package/dist/lint/post-synth/wk8208.d.ts.map +1 -1
  28. package/dist/lint/post-synth/wk8209.d.ts.map +1 -1
  29. package/dist/lint/post-synth/wk8301.d.ts.map +1 -1
  30. package/dist/lint/post-synth/wk8302.d.ts.map +1 -1
  31. package/dist/lint/post-synth/wk8303.d.ts.map +1 -1
  32. package/dist/lint/post-synth/wk8304.d.ts.map +1 -1
  33. package/dist/lint/post-synth/wk8305.d.ts.map +1 -1
  34. package/dist/lint/post-synth/wk8306.d.ts.map +1 -1
  35. package/dist/lint/post-synth/wk8401.d.ts.map +1 -1
  36. package/dist/lint/post-synth/wk8402.d.ts.map +1 -1
  37. package/dist/lint/post-synth/wk8403.d.ts.map +1 -1
  38. package/dist/lint/post-synth/wk8404.d.ts.map +1 -1
  39. package/dist/lint/post-synth/wk8405.d.ts.map +1 -1
  40. package/dist/lint/post-synth/wk8406.d.ts.map +1 -1
  41. package/dist/lint/post-synth/wk8407.d.ts.map +1 -1
  42. package/dist/manifest.json +1 -1
  43. package/dist/op/builders.d.ts +69 -0
  44. package/dist/op/builders.d.ts.map +1 -0
  45. package/dist/rules/argo-helpers.ts +2 -6
  46. package/dist/rules/crd-schema-helpers.ts +4 -6
  47. package/dist/rules/k8s-helpers.ts +32 -23
  48. package/dist/rules/wk8005.ts +27 -32
  49. package/dist/rules/wk8006.ts +33 -38
  50. package/dist/rules/wk8041.ts +19 -24
  51. package/dist/rules/wk8042.ts +17 -22
  52. package/dist/rules/wk8101.ts +29 -34
  53. package/dist/rules/wk8102.ts +13 -18
  54. package/dist/rules/wk8103.ts +17 -22
  55. package/dist/rules/wk8104.ts +30 -35
  56. package/dist/rules/wk8105.ts +16 -21
  57. package/dist/rules/wk8201.ts +26 -31
  58. package/dist/rules/wk8202.ts +17 -22
  59. package/dist/rules/wk8203.ts +17 -22
  60. package/dist/rules/wk8204.ts +46 -51
  61. package/dist/rules/wk8205.ts +27 -32
  62. package/dist/rules/wk8207.ts +14 -19
  63. package/dist/rules/wk8208.ts +14 -19
  64. package/dist/rules/wk8209.ts +14 -19
  65. package/dist/rules/wk8301.ts +25 -30
  66. package/dist/rules/wk8302.ts +19 -24
  67. package/dist/rules/wk8303.ts +23 -26
  68. package/dist/rules/wk8304.ts +35 -40
  69. package/dist/rules/wk8305.ts +39 -42
  70. package/dist/rules/wk8306.ts +20 -25
  71. package/dist/rules/wk8401.ts +59 -64
  72. package/dist/rules/wk8402.ts +13 -18
  73. package/dist/rules/wk8403.ts +32 -37
  74. package/dist/rules/wk8404.ts +15 -21
  75. package/dist/rules/wk8405.ts +15 -18
  76. package/dist/rules/wk8406.ts +29 -34
  77. package/dist/rules/wk8407.ts +17 -22
  78. package/dist/serializer.d.ts.map +1 -1
  79. package/package.json +3 -3
  80. package/src/composites/composites.test.ts +283 -0
  81. package/src/composites/index.ts +4 -0
  82. package/src/composites/operator-stack.ts +385 -0
  83. package/src/index.ts +11 -0
  84. package/src/lint/post-synth/argo-helpers.ts +2 -6
  85. package/src/lint/post-synth/crd-schema-helpers.ts +4 -6
  86. package/src/lint/post-synth/k8s-helpers.test.ts +26 -9
  87. package/src/lint/post-synth/k8s-helpers.ts +32 -23
  88. package/src/lint/post-synth/wk8005.ts +27 -32
  89. package/src/lint/post-synth/wk8006.ts +33 -38
  90. package/src/lint/post-synth/wk8041.ts +19 -24
  91. package/src/lint/post-synth/wk8042.ts +17 -22
  92. package/src/lint/post-synth/wk8101.ts +29 -34
  93. package/src/lint/post-synth/wk8102.ts +13 -18
  94. package/src/lint/post-synth/wk8103.ts +17 -22
  95. package/src/lint/post-synth/wk8104.ts +30 -35
  96. package/src/lint/post-synth/wk8105.ts +16 -21
  97. package/src/lint/post-synth/wk8201.ts +26 -31
  98. package/src/lint/post-synth/wk8202.ts +17 -22
  99. package/src/lint/post-synth/wk8203.ts +17 -22
  100. package/src/lint/post-synth/wk8204.ts +46 -51
  101. package/src/lint/post-synth/wk8205.ts +27 -32
  102. package/src/lint/post-synth/wk8207.ts +14 -19
  103. package/src/lint/post-synth/wk8208.ts +14 -19
  104. package/src/lint/post-synth/wk8209.ts +14 -19
  105. package/src/lint/post-synth/wk8301.ts +25 -30
  106. package/src/lint/post-synth/wk8302.ts +19 -24
  107. package/src/lint/post-synth/wk8303.ts +23 -26
  108. package/src/lint/post-synth/wk8304.ts +35 -40
  109. package/src/lint/post-synth/wk8305.ts +39 -42
  110. package/src/lint/post-synth/wk8306.ts +20 -25
  111. package/src/lint/post-synth/wk8401.ts +59 -64
  112. package/src/lint/post-synth/wk8402.ts +13 -18
  113. package/src/lint/post-synth/wk8403.ts +32 -37
  114. package/src/lint/post-synth/wk8404.ts +15 -21
  115. package/src/lint/post-synth/wk8405.ts +15 -18
  116. package/src/lint/post-synth/wk8406.ts +29 -34
  117. package/src/lint/post-synth/wk8407.ts +17 -22
  118. package/src/op/builders.test.ts +82 -0
  119. package/src/op/builders.ts +95 -0
  120. package/src/serializer.ts +9 -1
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
9
- import { getPrimaryOutput, parseK8sManifests } from "./k8s-helpers";
9
+ import { docsToManifests } from "./k8s-helpers";
10
10
 
11
11
  export const wk8102: PostSynthCheck = {
12
12
  id: "WK8102",
@@ -15,25 +15,20 @@ export const wk8102: PostSynthCheck = {
15
15
  check(ctx: PostSynthContext): PostSynthDiagnostic[] {
16
16
  const diagnostics: PostSynthDiagnostic[] = [];
17
17
 
18
- for (const [, output] of ctx.outputs) {
19
- const yaml = getPrimaryOutput(output);
20
- const manifests = parseK8sManifests(yaml);
18
+ for (const manifest of docsToManifests(ctx)) {
19
+ if (!manifest.kind || !manifest.metadata) continue;
21
20
 
22
- for (const manifest of manifests) {
23
- if (!manifest.kind || !manifest.metadata) continue;
21
+ const resourceName = manifest.metadata.name ?? manifest.kind;
22
+ const labels = manifest.metadata.labels;
24
23
 
25
- const resourceName = manifest.metadata.name ?? manifest.kind;
26
- const labels = manifest.metadata.labels;
27
-
28
- if (!labels || typeof labels !== "object" || Object.keys(labels).length === 0) {
29
- diagnostics.push({
30
- checkId: "WK8102",
31
- severity: "warning",
32
- message: `${manifest.kind} "${resourceName}" has no metadata.labels — add labels for organizational purposes`,
33
- entity: resourceName,
34
- lexicon: "k8s",
35
- });
36
- }
24
+ if (!labels || typeof labels !== "object" || Object.keys(labels).length === 0) {
25
+ diagnostics.push({
26
+ checkId: "WK8102",
27
+ severity: "warning",
28
+ message: `${manifest.kind} "${resourceName}" has no metadata.labels — add labels for organizational purposes`,
29
+ entity: resourceName,
30
+ lexicon: "k8s",
31
+ });
37
32
  }
38
33
  }
39
34
 
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
9
- import { getPrimaryOutput, parseK8sManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
9
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
10
10
 
11
11
  export const wk8103: PostSynthCheck = {
12
12
  id: "WK8103",
@@ -15,27 +15,22 @@ export const wk8103: PostSynthCheck = {
15
15
  check(ctx: PostSynthContext): PostSynthDiagnostic[] {
16
16
  const diagnostics: PostSynthDiagnostic[] = [];
17
17
 
18
- for (const [, output] of ctx.outputs) {
19
- const yaml = getPrimaryOutput(output);
20
- const manifests = parseK8sManifests(yaml);
21
-
22
- for (const manifest of manifests) {
23
- if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
24
-
25
- const containers = extractContainers(manifest);
26
- const resourceName = manifest.metadata?.name ?? manifest.kind;
27
-
28
- for (let i = 0; i < containers.length; i++) {
29
- const container = containers[i];
30
- if (!container.name || typeof container.name !== "string" || container.name.trim() === "") {
31
- diagnostics.push({
32
- checkId: "WK8103",
33
- severity: "error",
34
- message: `${manifest.kind} "${resourceName}" has a container at index ${i} without a name — the name field is required`,
35
- entity: resourceName,
36
- lexicon: "k8s",
37
- });
38
- }
18
+ for (const manifest of docsToManifests(ctx)) {
19
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
20
+
21
+ const containers = extractContainers(manifest);
22
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
23
+
24
+ for (let i = 0; i < containers.length; i++) {
25
+ const container = containers[i];
26
+ if (!container.name || typeof container.name !== "string" || container.name.trim() === "") {
27
+ diagnostics.push({
28
+ checkId: "WK8103",
29
+ severity: "error",
30
+ message: `${manifest.kind} "${resourceName}" has a container at index ${i} without a name the name field is required`,
31
+ entity: resourceName,
32
+ lexicon: "k8s",
33
+ });
39
34
  }
40
35
  }
41
36
  }
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
10
- import { getPrimaryOutput, parseK8sManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
10
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
11
11
 
12
12
  export const wk8104: PostSynthCheck = {
13
13
  id: "WK8104",
@@ -16,45 +16,20 @@ export const wk8104: PostSynthCheck = {
16
16
  check(ctx: PostSynthContext): PostSynthDiagnostic[] {
17
17
  const diagnostics: PostSynthDiagnostic[] = [];
18
18
 
19
- for (const [, output] of ctx.outputs) {
20
- const yaml = getPrimaryOutput(output);
21
- const manifests = parseK8sManifests(yaml);
19
+ for (const manifest of docsToManifests(ctx)) {
20
+ const resourceName = manifest.metadata?.name ?? manifest.kind ?? "(unknown)";
22
21
 
23
- for (const manifest of manifests) {
24
- const resourceName = manifest.metadata?.name ?? manifest.kind ?? "(unknown)";
25
-
26
- // Check container ports in workloads
27
- if (manifest.kind && WORKLOAD_KINDS.has(manifest.kind)) {
28
- const containers = extractContainers(manifest);
29
- for (const container of containers) {
30
- if (!Array.isArray(container.ports)) continue;
31
- for (const port of container.ports) {
32
- if (typeof port === "object" && port !== null && !port.name) {
33
- diagnostics.push({
34
- checkId: "WK8104",
35
- severity: "warning",
36
- message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has an unnamed port (containerPort: ${port.containerPort ?? "?"}) — add a name for clarity`,
37
- entity: resourceName,
38
- lexicon: "k8s",
39
- });
40
- }
41
- }
42
- }
43
- }
44
-
45
- // Check Service ports
46
- if (manifest.kind === "Service") {
47
- const spec = manifest.spec;
48
- if (!spec) continue;
49
- const ports = spec.ports as Array<Record<string, unknown>> | undefined;
50
- if (!Array.isArray(ports)) continue;
51
-
52
- for (const port of ports) {
22
+ // Check container ports in workloads
23
+ if (manifest.kind && WORKLOAD_KINDS.has(manifest.kind)) {
24
+ const containers = extractContainers(manifest);
25
+ for (const container of containers) {
26
+ if (!Array.isArray(container.ports)) continue;
27
+ for (const port of container.ports) {
53
28
  if (typeof port === "object" && port !== null && !port.name) {
54
29
  diagnostics.push({
55
30
  checkId: "WK8104",
56
31
  severity: "warning",
57
- message: `Service "${resourceName}" has an unnamed port (port: ${port.port ?? "?"}) — add a name for clarity`,
32
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has an unnamed port (containerPort: ${port.containerPort ?? "?"}) — add a name for clarity`,
58
33
  entity: resourceName,
59
34
  lexicon: "k8s",
60
35
  });
@@ -62,6 +37,26 @@ export const wk8104: PostSynthCheck = {
62
37
  }
63
38
  }
64
39
  }
40
+
41
+ // Check Service ports
42
+ if (manifest.kind === "Service") {
43
+ const spec = manifest.spec;
44
+ if (!spec) continue;
45
+ const ports = spec.ports as Array<Record<string, unknown>> | undefined;
46
+ if (!Array.isArray(ports)) continue;
47
+
48
+ for (const port of ports) {
49
+ if (typeof port === "object" && port !== null && !port.name) {
50
+ diagnostics.push({
51
+ checkId: "WK8104",
52
+ severity: "warning",
53
+ message: `Service "${resourceName}" has an unnamed port (port: ${port.port ?? "?"}) — add a name for clarity`,
54
+ entity: resourceName,
55
+ lexicon: "k8s",
56
+ });
57
+ }
58
+ }
59
+ }
65
60
  }
66
61
 
67
62
  return diagnostics;
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
10
- import { getPrimaryOutput, parseK8sManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
10
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
11
11
 
12
12
  export const wk8105: PostSynthCheck = {
13
13
  id: "WK8105",
@@ -16,26 +16,21 @@ export const wk8105: PostSynthCheck = {
16
16
  check(ctx: PostSynthContext): PostSynthDiagnostic[] {
17
17
  const diagnostics: PostSynthDiagnostic[] = [];
18
18
 
19
- for (const [, output] of ctx.outputs) {
20
- const yaml = getPrimaryOutput(output);
21
- const manifests = parseK8sManifests(yaml);
22
-
23
- for (const manifest of manifests) {
24
- if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
25
-
26
- const containers = extractContainers(manifest);
27
- const resourceName = manifest.metadata?.name ?? manifest.kind;
28
-
29
- for (const container of containers) {
30
- if (!container.imagePullPolicy) {
31
- diagnostics.push({
32
- checkId: "WK8105",
33
- severity: "warning",
34
- message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has no explicit imagePullPolicy — set it to "Always", "IfNotPresent", or "Never"`,
35
- entity: resourceName,
36
- lexicon: "k8s",
37
- });
38
- }
19
+ for (const manifest of docsToManifests(ctx)) {
20
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
21
+
22
+ const containers = extractContainers(manifest);
23
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
24
+
25
+ for (const container of containers) {
26
+ if (!container.imagePullPolicy) {
27
+ diagnostics.push({
28
+ checkId: "WK8105",
29
+ severity: "warning",
30
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" has no explicit imagePullPolicy — set it to "Always", "IfNotPresent", or "Never"`,
31
+ entity: resourceName,
32
+ lexicon: "k8s",
33
+ });
39
34
  }
40
35
  }
41
36
  }
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
10
- import { getPrimaryOutput, parseK8sManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
10
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
11
11
 
12
12
  export const wk8201: PostSynthCheck = {
13
13
  id: "WK8201",
@@ -16,36 +16,31 @@ export const wk8201: PostSynthCheck = {
16
16
  check(ctx: PostSynthContext): PostSynthDiagnostic[] {
17
17
  const diagnostics: PostSynthDiagnostic[] = [];
18
18
 
19
- for (const [, output] of ctx.outputs) {
20
- const yaml = getPrimaryOutput(output);
21
- const manifests = parseK8sManifests(yaml);
22
-
23
- for (const manifest of manifests) {
24
- if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
25
-
26
- const containers = extractContainers(manifest);
27
- const resourceName = manifest.metadata?.name ?? manifest.kind;
28
-
29
- for (const container of containers) {
30
- const limits = container.resources?.limits;
31
- const missing: string[] = [];
32
-
33
- if (!limits || typeof limits !== "object") {
34
- missing.push("cpu", "memory");
35
- } else {
36
- if (!limits.cpu) missing.push("cpu");
37
- if (!limits.memory) missing.push("memory");
38
- }
39
-
40
- if (missing.length > 0) {
41
- diagnostics.push({
42
- checkId: "WK8201",
43
- severity: "warning",
44
- message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" is missing resource limits for ${missing.join(", ")}`,
45
- entity: resourceName,
46
- lexicon: "k8s",
47
- });
48
- }
19
+ for (const manifest of docsToManifests(ctx)) {
20
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
21
+
22
+ const containers = extractContainers(manifest);
23
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
24
+
25
+ for (const container of containers) {
26
+ const limits = container.resources?.limits;
27
+ const missing: string[] = [];
28
+
29
+ if (!limits || typeof limits !== "object") {
30
+ missing.push("cpu", "memory");
31
+ } else {
32
+ if (!limits.cpu) missing.push("cpu");
33
+ if (!limits.memory) missing.push("memory");
34
+ }
35
+
36
+ if (missing.length > 0) {
37
+ diagnostics.push({
38
+ checkId: "WK8201",
39
+ severity: "warning",
40
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" is missing resource limits for ${missing.join(", ")}`,
41
+ entity: resourceName,
42
+ lexicon: "k8s",
43
+ });
49
44
  }
50
45
  }
51
46
  }
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
10
- import { getPrimaryOutput, parseK8sManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
10
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
11
11
 
12
12
  export const wk8202: PostSynthCheck = {
13
13
  id: "WK8202",
@@ -16,27 +16,22 @@ export const wk8202: PostSynthCheck = {
16
16
  check(ctx: PostSynthContext): PostSynthDiagnostic[] {
17
17
  const diagnostics: PostSynthDiagnostic[] = [];
18
18
 
19
- for (const [, output] of ctx.outputs) {
20
- const yaml = getPrimaryOutput(output);
21
- const manifests = parseK8sManifests(yaml);
22
-
23
- for (const manifest of manifests) {
24
- if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
25
-
26
- const containers = extractContainers(manifest);
27
- const resourceName = manifest.metadata?.name ?? manifest.kind;
28
-
29
- for (const container of containers) {
30
- const secCtx = container.securityContext;
31
- if (secCtx && secCtx.privileged === true) {
32
- diagnostics.push({
33
- checkId: "WK8202",
34
- severity: "error",
35
- message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" runs in privileged mode — this grants full host access and should be avoided`,
36
- entity: resourceName,
37
- lexicon: "k8s",
38
- });
39
- }
19
+ for (const manifest of docsToManifests(ctx)) {
20
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
21
+
22
+ const containers = extractContainers(manifest);
23
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
24
+
25
+ for (const container of containers) {
26
+ const secCtx = container.securityContext;
27
+ if (secCtx && secCtx.privileged === true) {
28
+ diagnostics.push({
29
+ checkId: "WK8202",
30
+ severity: "error",
31
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" runs in privileged mode this grants full host access and should be avoided`,
32
+ entity: resourceName,
33
+ lexicon: "k8s",
34
+ });
40
35
  }
41
36
  }
42
37
  }
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
10
- import { getPrimaryOutput, parseK8sManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
10
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
11
11
 
12
12
  export const wk8203: PostSynthCheck = {
13
13
  id: "WK8203",
@@ -16,27 +16,22 @@ export const wk8203: PostSynthCheck = {
16
16
  check(ctx: PostSynthContext): PostSynthDiagnostic[] {
17
17
  const diagnostics: PostSynthDiagnostic[] = [];
18
18
 
19
- for (const [, output] of ctx.outputs) {
20
- const yaml = getPrimaryOutput(output);
21
- const manifests = parseK8sManifests(yaml);
22
-
23
- for (const manifest of manifests) {
24
- if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
25
-
26
- const containers = extractContainers(manifest);
27
- const resourceName = manifest.metadata?.name ?? manifest.kind;
28
-
29
- for (const container of containers) {
30
- const secCtx = container.securityContext;
31
- if (!secCtx || secCtx.readOnlyRootFilesystem !== true) {
32
- diagnostics.push({
33
- checkId: "WK8203",
34
- severity: "warning",
35
- message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" does not set readOnlyRootFilesystem: true — set it to reduce the attack surface`,
36
- entity: resourceName,
37
- lexicon: "k8s",
38
- });
39
- }
19
+ for (const manifest of docsToManifests(ctx)) {
20
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
21
+
22
+ const containers = extractContainers(manifest);
23
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
24
+
25
+ for (const container of containers) {
26
+ const secCtx = container.securityContext;
27
+ if (!secCtx || secCtx.readOnlyRootFilesystem !== true) {
28
+ diagnostics.push({
29
+ checkId: "WK8203",
30
+ severity: "warning",
31
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" does not set readOnlyRootFilesystem: true — set it to reduce the attack surface`,
32
+ entity: resourceName,
33
+ lexicon: "k8s",
34
+ });
40
35
  }
41
36
  }
42
37
  }
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
14
- import { getPrimaryOutput, parseK8sManifests, extractContainers, extractPodSpec, WORKLOAD_KINDS } from "./k8s-helpers";
14
+ import { docsToManifests, extractContainers, extractPodSpec, WORKLOAD_KINDS } from "./k8s-helpers";
15
15
 
16
16
  export const wk8204: PostSynthCheck = {
17
17
  id: "WK8204",
@@ -20,63 +20,58 @@ export const wk8204: PostSynthCheck = {
20
20
  check(ctx: PostSynthContext): PostSynthDiagnostic[] {
21
21
  const diagnostics: PostSynthDiagnostic[] = [];
22
22
 
23
- for (const [, output] of ctx.outputs) {
24
- const yaml = getPrimaryOutput(output);
25
- const manifests = parseK8sManifests(yaml);
23
+ for (const manifest of docsToManifests(ctx)) {
24
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
26
25
 
27
- for (const manifest of manifests) {
28
- if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
26
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
27
+ const podSpec = extractPodSpec(manifest);
28
+ if (!podSpec) continue;
29
29
 
30
- const resourceName = manifest.metadata?.name ?? manifest.kind;
31
- const podSpec = extractPodSpec(manifest);
32
- if (!podSpec) continue;
30
+ // Check pod-level securityContext
31
+ const podSecCtx = podSpec.securityContext as Record<string, unknown> | undefined;
32
+ const podRunAsNonRoot = podSecCtx?.runAsNonRoot === true;
33
+ const podRunAsUser = podSecCtx?.runAsUser;
33
34
 
34
- // Check pod-level securityContext
35
- const podSecCtx = podSpec.securityContext as Record<string, unknown> | undefined;
36
- const podRunAsNonRoot = podSecCtx?.runAsNonRoot === true;
37
- const podRunAsUser = podSecCtx?.runAsUser;
35
+ const containers = extractContainers(manifest);
36
+ for (const container of containers) {
37
+ const secCtx = container.securityContext;
38
+ const containerRunAsNonRoot = secCtx?.runAsNonRoot === true;
39
+ const containerRunAsUser = secCtx?.runAsUser;
38
40
 
39
- const containers = extractContainers(manifest);
40
- for (const container of containers) {
41
- const secCtx = container.securityContext;
42
- const containerRunAsNonRoot = secCtx?.runAsNonRoot === true;
43
- const containerRunAsUser = secCtx?.runAsUser;
41
+ const hasRunAsNonRoot = podRunAsNonRoot || containerRunAsNonRoot;
44
42
 
45
- const hasRunAsNonRoot = podRunAsNonRoot || containerRunAsNonRoot;
46
-
47
- if (!hasRunAsNonRoot) {
48
- diagnostics.push({
49
- checkId: "WK8204",
50
- severity: "warning",
51
- message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" does not set runAsNonRoot: true — set it at container or pod level`,
52
- entity: resourceName,
53
- lexicon: "k8s",
54
- });
55
- continue;
56
- }
43
+ if (!hasRunAsNonRoot) {
44
+ diagnostics.push({
45
+ checkId: "WK8204",
46
+ severity: "warning",
47
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" does not set runAsNonRoot: true — set it at container or pod level`,
48
+ entity: resourceName,
49
+ lexicon: "k8s",
50
+ });
51
+ continue;
52
+ }
57
53
 
58
- // runAsNonRoot is true — check for explicit runAsUser
59
- const effectiveRunAsUser = containerRunAsUser ?? podRunAsUser;
54
+ // runAsNonRoot is true — check for explicit runAsUser
55
+ const effectiveRunAsUser = containerRunAsUser ?? podRunAsUser;
60
56
 
61
- if (effectiveRunAsUser === 0) {
62
- // Contradictory: runAsNonRoot: true + runAsUser: 0
63
- diagnostics.push({
64
- checkId: "WK8204",
65
- severity: "warning",
66
- 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`,
67
- entity: resourceName,
68
- lexicon: "k8s",
69
- });
70
- } else if (effectiveRunAsUser === undefined || effectiveRunAsUser === null) {
71
- // runAsNonRoot: true but no explicit UID
72
- diagnostics.push({
73
- checkId: "WK8204",
74
- severity: "warning",
75
- 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`,
76
- entity: resourceName,
77
- lexicon: "k8s",
78
- });
79
- }
57
+ if (effectiveRunAsUser === 0) {
58
+ // Contradictory: runAsNonRoot: true + runAsUser: 0
59
+ diagnostics.push({
60
+ checkId: "WK8204",
61
+ severity: "warning",
62
+ 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`,
63
+ entity: resourceName,
64
+ lexicon: "k8s",
65
+ });
66
+ } else if (effectiveRunAsUser === undefined || effectiveRunAsUser === null) {
67
+ // runAsNonRoot: true but no explicit UID
68
+ diagnostics.push({
69
+ checkId: "WK8204",
70
+ severity: "warning",
71
+ 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`,
72
+ entity: resourceName,
73
+ lexicon: "k8s",
74
+ });
80
75
  }
81
76
  }
82
77
  }
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
10
- import { getPrimaryOutput, parseK8sManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
10
+ import { docsToManifests, extractContainers, WORKLOAD_KINDS } from "./k8s-helpers";
11
11
 
12
12
  export const wk8205: PostSynthCheck = {
13
13
  id: "WK8205",
@@ -16,37 +16,32 @@ export const wk8205: PostSynthCheck = {
16
16
  check(ctx: PostSynthContext): PostSynthDiagnostic[] {
17
17
  const diagnostics: PostSynthDiagnostic[] = [];
18
18
 
19
- for (const [, output] of ctx.outputs) {
20
- const yaml = getPrimaryOutput(output);
21
- const manifests = parseK8sManifests(yaml);
22
-
23
- for (const manifest of manifests) {
24
- if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
25
-
26
- const containers = extractContainers(manifest);
27
- const resourceName = manifest.metadata?.name ?? manifest.kind;
28
-
29
- for (const container of containers) {
30
- const secCtx = container.securityContext;
31
- const capabilities = secCtx?.capabilities as Record<string, unknown> | undefined;
32
- const drop = capabilities?.drop;
33
-
34
- const dropsAll =
35
- Array.isArray(drop) &&
36
- drop.some(
37
- (cap) =>
38
- (typeof cap === "string" && cap.toUpperCase() === "ALL"),
39
- );
40
-
41
- if (!dropsAll) {
42
- diagnostics.push({
43
- checkId: "WK8205",
44
- severity: "warning",
45
- message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" does not drop ALL capabilities — add securityContext.capabilities.drop: ["ALL"]`,
46
- entity: resourceName,
47
- lexicon: "k8s",
48
- });
49
- }
19
+ for (const manifest of docsToManifests(ctx)) {
20
+ if (!manifest.kind || !WORKLOAD_KINDS.has(manifest.kind)) continue;
21
+
22
+ const containers = extractContainers(manifest);
23
+ const resourceName = manifest.metadata?.name ?? manifest.kind;
24
+
25
+ for (const container of containers) {
26
+ const secCtx = container.securityContext;
27
+ const capabilities = secCtx?.capabilities as Record<string, unknown> | undefined;
28
+ const drop = capabilities?.drop;
29
+
30
+ const dropsAll =
31
+ Array.isArray(drop) &&
32
+ drop.some(
33
+ (cap) =>
34
+ (typeof cap === "string" && cap.toUpperCase() === "ALL"),
35
+ );
36
+
37
+ if (!dropsAll) {
38
+ diagnostics.push({
39
+ checkId: "WK8205",
40
+ severity: "warning",
41
+ message: `Container "${container.name ?? "(unnamed)"}" in ${manifest.kind} "${resourceName}" does not drop ALL capabilities — add securityContext.capabilities.drop: ["ALL"]`,
42
+ entity: resourceName,
43
+ lexicon: "k8s",
44
+ });
50
45
  }
51
46
  }
52
47
  }