@intentius/chant-lexicon-k8s 0.14.0 → 0.15.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.
- package/dist/integrity.json +2 -2
- package/dist/lint/audit-catalog.d.ts +7 -0
- package/dist/lint/audit-catalog.d.ts.map +1 -0
- package/dist/manifest.json +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/describe-resources.ts +3 -3
- package/src/import/live-export.ts +2 -2
- package/src/lint/audit-catalog.ts +37 -0
- package/src/plugin.ts +10 -0
- package/src/serializer.ts +2 -2
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "f3f247293df83075abe8c0ff4cdb1c11bd004b806e92ca19cd5e817f253670a4",
|
|
5
5
|
"meta.json": "560e496dee251a4fcc249373e50b7f16b5ee563ac8f9c832343a56f62bcfb656",
|
|
6
6
|
"types/index.d.ts": "3cba22f6b907ebcbad3efded303cb21ba82fb5b8ac0ec41883fee6c84f0895ee",
|
|
7
7
|
"rules/argo-appset-single-project.ts": "afa9f310753aa2d475f35012b13135b2dfaebed2a35d44edbe185ebc07673674",
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"skills/chant-k8s-aks.md": "e18f0e2b055f72cd7a37deaf258d7027c2d4d3e286e8fd4975b27a1f981a3ad9",
|
|
51
51
|
"skills/chant-k8s-argo.md": "b1a0b826559d8c5033a479c5781efaf650320f0aee4419d8841170bd3393cea5"
|
|
52
52
|
},
|
|
53
|
-
"composite": "
|
|
53
|
+
"composite": "f211e5ab09d5481f5268bc65a1960e2da9f81c74136f9b6681a24598305d37b6"
|
|
54
54
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The k8s lexicon's chant audit catalog — metadata for its post-synth rules
|
|
3
|
+
* (WK8 and ARGO Kubernetes-manifest rules). Contributed via k8sPlugin.auditCatalog() (#687).
|
|
4
|
+
*/
|
|
5
|
+
import { type RuleMeta } from "@intentius/chant/audit/catalog";
|
|
6
|
+
export declare const k8sAuditCatalog: Record<string, RuleMeta>;
|
|
7
|
+
//# sourceMappingURL=audit-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-catalog.d.ts","sourceRoot":"","sources":["../../src/lint/audit-catalog.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAqD,KAAK,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAElH,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CA8BpD,CAAC"}
|
package/dist/manifest.json
CHANGED
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAqC,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAqC,MAAM,0BAA0B,CAAC;AAmBjG,eAAO,MAAM,SAAS,EAAE,aAilBvB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-k8s",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Kubernetes lexicon for chant — declarative IaC in TypeScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -70,6 +70,6 @@
|
|
|
70
70
|
"typescript": "^5.9.3"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@intentius/chant": "^0.
|
|
73
|
+
"@intentius/chant": "^0.15.0"
|
|
74
74
|
}
|
|
75
75
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { exec } from "node:child_process";
|
|
16
16
|
import { promisify } from "node:util";
|
|
17
17
|
import type { ResourceMetadata } from "@intentius/chant/lexicon";
|
|
18
|
-
import { hasOwnershipMarker, classifyOwnership } from "@intentius/chant/ownership";
|
|
18
|
+
import { hasOwnershipMarker, classifyOwnership, LABEL_OWNERSHIP_KEYS } from "@intentius/chant/ownership";
|
|
19
19
|
|
|
20
20
|
const execAsync = promisify(exec);
|
|
21
21
|
|
|
@@ -109,7 +109,7 @@ export async function describeResources(options: {
|
|
|
109
109
|
const { stdout } = await execAsync(cmd);
|
|
110
110
|
const obj: KubectlResponse = JSON.parse(stdout);
|
|
111
111
|
// owned filter: skip resources not carrying chant's marker label.
|
|
112
|
-
if (options.owned && !hasOwnershipMarker(obj.metadata?.labels,
|
|
112
|
+
if (options.owned && !hasOwnershipMarker(obj.metadata?.labels, LABEL_OWNERSHIP_KEYS)) {
|
|
113
113
|
continue;
|
|
114
114
|
}
|
|
115
115
|
result[entityName] = {
|
|
@@ -117,7 +117,7 @@ export async function describeResources(options: {
|
|
|
117
117
|
physicalId: obj.metadata?.uid,
|
|
118
118
|
status: statusFromKubectl(obj),
|
|
119
119
|
lastUpdated: obj.metadata?.creationTimestamp,
|
|
120
|
-
ownership: classifyOwnership(obj.metadata?.labels,
|
|
120
|
+
ownership: classifyOwnership(obj.metadata?.labels, LABEL_OWNERSHIP_KEYS),
|
|
121
121
|
attributes: pruneUndefined({
|
|
122
122
|
namespace: obj.metadata?.namespace,
|
|
123
123
|
labels: obj.metadata?.labels,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExportedTemplate, ResourceSelector } from "@intentius/chant/lexicon";
|
|
2
|
-
import { hasOwnershipMarker } from "@intentius/chant/ownership";
|
|
2
|
+
import { hasOwnershipMarker, LABEL_OWNERSHIP_KEYS } from "@intentius/chant/ownership";
|
|
3
3
|
import { K8sParser } from "./parser";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -61,7 +61,7 @@ export function buildExportFromObjects(
|
|
|
61
61
|
? objects.filter((o) => {
|
|
62
62
|
if (!isRecord(o)) return false;
|
|
63
63
|
const labels = isRecord(o.metadata) ? (o.metadata.labels as Record<string, unknown>) : undefined;
|
|
64
|
-
return hasOwnershipMarker(labels,
|
|
64
|
+
return hasOwnershipMarker(labels, LABEL_OWNERSHIP_KEYS);
|
|
65
65
|
})
|
|
66
66
|
: objects;
|
|
67
67
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The k8s lexicon's chant audit catalog — metadata for its post-synth rules
|
|
3
|
+
* (WK8 and ARGO Kubernetes-manifest rules). Contributed via k8sPlugin.auditCatalog() (#687).
|
|
4
|
+
*/
|
|
5
|
+
import { auditRule, K8S_PSS, K8S_SECRETS, SCORECARD_PINNED, type RuleMeta } from "@intentius/chant/audit/catalog";
|
|
6
|
+
|
|
7
|
+
export const k8sAuditCatalog: Record<string, RuleMeta> = {
|
|
8
|
+
ARGO002: auditRule("ARGO002", "merge-worthy", "guidance", "Argo Application references an undeclared AppProject", "Declare the named AppProject or reference an existing project.", { category: "correctness" }),
|
|
9
|
+
ARGO003: auditRule("ARGO003", "merge-worthy", "guidance", "Argo Application targets an unregistered cluster", "Point spec.destination at a registered cluster or the in-cluster target.", { category: "correctness" }),
|
|
10
|
+
ARGO005: auditRule("ARGO005", "report-only", "guidance", "Argo source.path may not resolve", "Ensure the source path exists under the build root.", { category: "best-practice" }),
|
|
11
|
+
WK8005: auditRule("WK8005", "merge-worthy", "guidance", "Hardcoded secret in env var", "Use a secretKeyRef instead of a literal value, and rotate the secret.", { authority: [K8S_SECRETS] }),
|
|
12
|
+
WK8006: auditRule("WK8006", "merge-worthy", "guidance", "Image uses :latest or no tag", "Pin the image to an explicit version tag (ideally a digest).", { authority: [SCORECARD_PINNED] }),
|
|
13
|
+
WK8041: auditRule("WK8041", "merge-worthy", "guidance", "Hardcoded API key in env var", "Move the key to a Secret and rotate it.", { authority: [K8S_SECRETS] }),
|
|
14
|
+
WK8042: auditRule("WK8042", "merge-worthy", "guidance", "Private key stored in a ConfigMap", "Store private keys in a Secret, not a ConfigMap.", { authority: [K8S_SECRETS] }),
|
|
15
|
+
WK8101: auditRule("WK8101", "merge-worthy", "guidance", "Deployment selector does not match template labels", "Align spec.selector with the pod template labels.", { category: "best-practice" }),
|
|
16
|
+
WK8102: auditRule("WK8102", "report-only", "guidance", "Resource missing metadata labels", "Add metadata labels for filtering and tooling.", { category: "best-practice" }),
|
|
17
|
+
WK8103: auditRule("WK8103", "merge-worthy", "guidance", "Container missing name", "Add the required container `name`.", { category: "best-practice" }),
|
|
18
|
+
WK8104: auditRule("WK8104", "report-only", "guidance", "Container ports not named", "Name ports for clearer Service/NetworkPolicy config.", { category: "best-practice" }),
|
|
19
|
+
WK8105: auditRule("WK8105", "report-only", "guidance", "imagePullPolicy not explicit", "Set imagePullPolicy explicitly to avoid surprising defaults.", { category: "best-practice" }),
|
|
20
|
+
WK8201: auditRule("WK8201", "report-only", "guidance", "Container missing resource limits", "Set CPU and memory limits.", { category: "best-practice" }),
|
|
21
|
+
WK8202: auditRule("WK8202", "merge-worthy", "guidance", "Privileged container", "Remove privileged: true; grant only the specific capabilities needed.", { authority: [K8S_PSS] }),
|
|
22
|
+
WK8203: auditRule("WK8203", "merge-worthy", "guidance", "Root filesystem is writable", "Set readOnlyRootFilesystem: true.", { authority: [K8S_PSS] }),
|
|
23
|
+
WK8204: auditRule("WK8204", "merge-worthy", "guidance", "Container may run as root", "Set runAsNonRoot: true (and a non-zero runAsUser).", { authority: [K8S_PSS] }),
|
|
24
|
+
WK8205: auditRule("WK8205", "merge-worthy", "guidance", "Capabilities not dropped", "drop: [ALL] and add back only what is required.", { authority: [K8S_PSS] }),
|
|
25
|
+
WK8207: auditRule("WK8207", "merge-worthy", "guidance", "Pod uses host network", "Remove hostNetwork; it bypasses network isolation.", { authority: [K8S_PSS] }),
|
|
26
|
+
WK8208: auditRule("WK8208", "merge-worthy", "guidance", "Pod shares host PID namespace", "Remove hostPID.", { authority: [K8S_PSS] }),
|
|
27
|
+
WK8209: auditRule("WK8209", "merge-worthy", "guidance", "Pod shares host IPC namespace", "Remove hostIPC.", { authority: [K8S_PSS] }),
|
|
28
|
+
WK8301: auditRule("WK8301", "report-only", "guidance", "Container missing probes", "Add liveness and readiness probes.", { category: "best-practice" }),
|
|
29
|
+
WK8302: auditRule("WK8302", "report-only", "guidance", "Deployment has a single replica", "Use replicas >= 2 for availability.", { category: "best-practice" }),
|
|
30
|
+
WK8303: auditRule("WK8303", "report-only", "guidance", "No PodDisruptionBudget for an HA Deployment", "Add a PDB to protect availability during disruptions.", { category: "best-practice" }),
|
|
31
|
+
WK8304: auditRule("WK8304", "report-only", "guidance", "SSL redirect without a certificate", "Provide a certificate and HTTPS listen-ports for the ssl-redirect annotation.", { category: "best-practice" }),
|
|
32
|
+
WK8305: auditRule("WK8305", "merge-worthy", "guidance", "Ingress backend port does not match the Service", "Point the Ingress backend at a declared Service port.", { category: "best-practice" }),
|
|
33
|
+
WK8306: auditRule("WK8306", "merge-worthy", "guidance", "Container command starts with a flag", "The first command element should be a binary, not a flag.", { category: "best-practice" }),
|
|
34
|
+
WK8401: auditRule("WK8401", "merge-worthy", "guidance", "shmSize exceeds the container memory limit", "Lower shmSize or raise the memory limit so the pod can schedule.", { category: "best-practice" }),
|
|
35
|
+
WK8402: auditRule("WK8402", "report-only", "guidance", "RayCluster missing spec.rayVersion", "Set spec.rayVersion so KubeRay picks the right autoscaler image.", { category: "best-practice" }),
|
|
36
|
+
WK8403: auditRule("WK8403", "report-only", "guidance", "rayVersion does not match the head image tag", "Align spec.rayVersion with the Ray version in the head container image.", { category: "best-practice" }),
|
|
37
|
+
};
|
package/src/plugin.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type { LexiconPlugin, InitTemplateSet, ResourceMetadata } from "@intentiu
|
|
|
9
9
|
import { detectTemplate } from "./detect";
|
|
10
10
|
import type { LintRule } from "@intentius/chant/lint/rule";
|
|
11
11
|
import { postSynthChecks as postSynthCheckList } from "./lint/post-synth";
|
|
12
|
+
import { k8sAuditCatalog } from "./lint/audit-catalog";
|
|
12
13
|
import { createSkillsLoader, createDiffTool, createCatalogResource } from "@intentius/chant/lexicon-plugin-helpers";
|
|
13
14
|
import { join, dirname } from "path";
|
|
14
15
|
import { fileURLToPath } from "url";
|
|
@@ -25,7 +26,16 @@ import { K8sGenerator } from "./import/generator";
|
|
|
25
26
|
|
|
26
27
|
export const k8sPlugin: LexiconPlugin = {
|
|
27
28
|
name: "k8s",
|
|
29
|
+
auditCatalog: () => k8sAuditCatalog,
|
|
28
30
|
serializer: k8sSerializer,
|
|
31
|
+
// Self-upgrade: where the pinned Kubernetes schema version lives + its upstream (#685).
|
|
32
|
+
upstreamPin: {
|
|
33
|
+
file: "src/spec/fetch.ts",
|
|
34
|
+
pattern: /export const K8S_SCHEMA_VERSION\s*=\s*"([^"]+)"/,
|
|
35
|
+
replace: (v, line) =>
|
|
36
|
+
line.replace(/export const K8S_SCHEMA_VERSION\s*=\s*"[^"]+"/, `export const K8S_SCHEMA_VERSION = "${v}"`),
|
|
37
|
+
upstream: { owner: "kubernetes", repo: "kubernetes", kind: "releases" },
|
|
38
|
+
},
|
|
29
39
|
|
|
30
40
|
lintRules(): LintRule[] {
|
|
31
41
|
return [
|
package/src/serializer.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { createRequire } from "module";
|
|
|
9
9
|
import type { Declarable } from "@intentius/chant/declarable";
|
|
10
10
|
import { isPropertyDeclarable } from "@intentius/chant/declarable";
|
|
11
11
|
import type { Serializer, SerializerResult, SerializeContext } from "@intentius/chant/serializer";
|
|
12
|
-
import { ownershipEntries } from "@intentius/chant/ownership";
|
|
12
|
+
import { ownershipEntries, LABEL_OWNERSHIP_KEYS } from "@intentius/chant/ownership";
|
|
13
13
|
import type { LexiconOutput } from "@intentius/chant/lexicon-output";
|
|
14
14
|
import { walkValue, type SerializerVisitor } from "@intentius/chant/serializer-walker";
|
|
15
15
|
import { emitYAML } from "@intentius/chant/yaml";
|
|
@@ -165,7 +165,7 @@ export const k8sSerializer: Serializer = {
|
|
|
165
165
|
// labels, so they seed defaultLabelEntries and flow through the same merge
|
|
166
166
|
// (explicit resource labels still win).
|
|
167
167
|
let defaultLabelEntries: Record<string, unknown> = context?.ownership
|
|
168
|
-
? { ...ownershipEntries(
|
|
168
|
+
? { ...ownershipEntries(LABEL_OWNERSHIP_KEYS, context.ownership) }
|
|
169
169
|
: {};
|
|
170
170
|
let defaultAnnotationEntries: Record<string, unknown> = {};
|
|
171
171
|
|