@intentius/chant-lexicon-gcp 0.124.30 → 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 (103) hide show
  1. package/dist/actions/index.js +45 -0
  2. package/dist/api/read-client.js +105 -0
  3. package/dist/codegen/docs-cli.js +6 -0
  4. package/dist/codegen/docs.js +115 -0
  5. package/dist/codegen/generate-cli.js +17 -0
  6. package/dist/codegen/generate.js +214 -0
  7. package/dist/codegen/naming.js +164 -0
  8. package/dist/codegen/package.js +43 -0
  9. package/dist/composites/catalog.js +1245 -0
  10. package/dist/composites/cloud-function.js +65 -0
  11. package/dist/composites/cloud-run-service.js +71 -0
  12. package/dist/composites/cloud-sql-instance.js +85 -0
  13. package/dist/composites/gcs-bucket.js +64 -0
  14. package/dist/composites/gke-cluster.js +113 -0
  15. package/dist/composites/gke-crdb-region.js +137 -0
  16. package/dist/composites/gpu-node-pool.js +74 -0
  17. package/dist/composites/index.js +15 -0
  18. package/dist/composites/landing-zone.js +89 -0
  19. package/dist/composites/managed-certificate.js +46 -0
  20. package/dist/composites/memorystore-redis.js +52 -0
  21. package/dist/composites/multi-region-vpc.js +108 -0
  22. package/dist/composites/private-service.js +50 -0
  23. package/dist/composites/pubsub-pipeline.js +63 -0
  24. package/dist/composites/secure-project.js +85 -0
  25. package/dist/composites/vpc-network.js +113 -0
  26. package/dist/coverage.js +28 -0
  27. package/dist/deep-observe-hooks.js +152 -0
  28. package/dist/deep-observe.js +273 -0
  29. package/dist/default-labels.js +67 -0
  30. package/dist/describe-resources.js +264 -0
  31. package/dist/detect.js +16 -0
  32. package/dist/export-resources.js +59 -0
  33. package/dist/generated/index.js +1804 -0
  34. package/dist/generated/runtime.js +4 -0
  35. package/dist/governance.js +98 -0
  36. package/dist/import/generator.js +68 -0
  37. package/dist/import/live-export.js +73 -0
  38. package/dist/import/parser.js +61 -0
  39. package/dist/index.js +36 -0
  40. package/dist/integrity.json +3 -3
  41. package/dist/lint/audit-catalog.js +38 -0
  42. package/dist/lint/audit-lineage.js +63 -0
  43. package/dist/lint/post-synth/gcp-helpers.js +91 -0
  44. package/dist/lint/post-synth/index.js +54 -0
  45. package/dist/lint/post-synth/schema-registry.d.ts.map +1 -1
  46. package/dist/lint/post-synth/schema-registry.js +69 -0
  47. package/dist/lint/post-synth/wgc101.js +47 -0
  48. package/dist/lint/post-synth/wgc102.js +29 -0
  49. package/dist/lint/post-synth/wgc103.js +37 -0
  50. package/dist/lint/post-synth/wgc104.js +34 -0
  51. package/dist/lint/post-synth/wgc105.js +39 -0
  52. package/dist/lint/post-synth/wgc106.js +30 -0
  53. package/dist/lint/post-synth/wgc107.js +33 -0
  54. package/dist/lint/post-synth/wgc108.js +34 -0
  55. package/dist/lint/post-synth/wgc109.js +33 -0
  56. package/dist/lint/post-synth/wgc110.js +32 -0
  57. package/dist/lint/post-synth/wgc111.js +47 -0
  58. package/dist/lint/post-synth/wgc112.js +46 -0
  59. package/dist/lint/post-synth/wgc113.js +35 -0
  60. package/dist/lint/post-synth/wgc201.js +30 -0
  61. package/dist/lint/post-synth/wgc202.js +33 -0
  62. package/dist/lint/post-synth/wgc203.js +35 -0
  63. package/dist/lint/post-synth/wgc204.js +33 -0
  64. package/dist/lint/post-synth/wgc301.js +28 -0
  65. package/dist/lint/post-synth/wgc302.js +28 -0
  66. package/dist/lint/post-synth/wgc303.js +29 -0
  67. package/dist/lint/post-synth/wgc401.js +50 -0
  68. package/dist/lint/post-synth/wgc402.js +46 -0
  69. package/dist/lint/post-synth/wgc403.js +74 -0
  70. package/dist/lint/post-synth/wgc501.js +63 -0
  71. package/dist/lint/post-synth/wgc502.js +37 -0
  72. package/dist/lint/post-synth/wgc503.js +49 -0
  73. package/dist/lint/rules/hardcoded-project.js +51 -0
  74. package/dist/lint/rules/hardcoded-region.js +51 -0
  75. package/dist/lint/rules/index.js +3 -0
  76. package/dist/lint/rules/public-iam.js +37 -0
  77. package/dist/lsp/completions.d.ts.map +1 -1
  78. package/dist/lsp/completions.js +15 -0
  79. package/dist/lsp/hover.d.ts.map +1 -1
  80. package/dist/lsp/hover.js +46 -0
  81. package/dist/manifest.json +1 -1
  82. package/dist/op/activities/floci-gcp.js +49 -0
  83. package/dist/op/activities/gcp-apply.js +618 -0
  84. package/dist/op/activities/index.js +10 -0
  85. package/dist/op/builders.js +50 -0
  86. package/dist/ownership.js +38 -0
  87. package/dist/package-cli.js +20 -0
  88. package/dist/plugin.js +371 -0
  89. package/dist/pseudo.js +15 -0
  90. package/dist/rules/schema-registry.ts +2 -1
  91. package/dist/serializer.d.ts.map +1 -1
  92. package/dist/serializer.js +233 -0
  93. package/dist/spec/fetch.js +107 -0
  94. package/dist/spec/parse.js +323 -0
  95. package/dist/validate-cli.js +11 -0
  96. package/dist/validate.js +24 -0
  97. package/dist/variables.js +15 -0
  98. package/package.json +8 -8
  99. package/src/codegen/package.ts +2 -2
  100. package/src/lint/post-synth/schema-registry.ts +2 -1
  101. package/src/lsp/completions.ts +2 -1
  102. package/src/lsp/hover.ts +2 -1
  103. package/src/serializer.ts +2 -1
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Runtime factory constructors — re-exported from core.
3
+ */
4
+ export { createResource, createProperty } from "@intentius/chant/runtime";
@@ -0,0 +1,98 @@
1
+ /**
2
+ * GCP governance authoring: the desired-state config
3
+ * shape an external governance reconciler reconciles, and `landingZoneConfig()` — the
4
+ * typed authoring layer that emits it.
5
+ *
6
+ * This is deliberately NOT a composite. The evaluability rules (EVL002/004,
7
+ * #916/#952) require composites to declare a fixed set of resources, so a
8
+ * data-driven folder tree walker cannot be one. The split mirrors the AWS
9
+ * slice (lexicons/aws/src/governance.ts): authoring emits config (this
10
+ * module, arbitrary cardinality); resources for greenfield bootstrap are the
11
+ * fixed-shape composites in `composites/landing-zone.ts`; the reconciler
12
+ * consumes only the config.
13
+ *
14
+ * GCP has no OrganizationRoot analog — organizations are created through
15
+ * Cloud Identity / Workspace, never through the resource API.
16
+ */
17
+ // ---------------------------------------------------------------------------
18
+ // Baseline guardrails (shared with the bootstrap composites)
19
+ // ---------------------------------------------------------------------------
20
+ export const DISABLE_SA_KEY_CREATION = {
21
+ description: "User-managed service account keys cannot be created.",
22
+ constraint: "iam.disableServiceAccountKeyCreation",
23
+ rules: [{ enforce: "TRUE" }],
24
+ };
25
+ export const SKIP_DEFAULT_NETWORK = {
26
+ description: "New projects do not get the auto-mode default VPC.",
27
+ constraint: "compute.skipDefaultNetworkCreation",
28
+ rules: [{ enforce: "TRUE" }],
29
+ };
30
+ /** Allow resource creation only in `locations` (value-group syntax like "in:us-locations" works too). */
31
+ export function resourceLocationRestriction(locations) {
32
+ return {
33
+ description: `Resources may only be created in [${locations.join(", ")}].`,
34
+ constraint: "gcp.resourceLocations",
35
+ rules: [{ values: { allowedValues: [...locations] } }],
36
+ };
37
+ }
38
+ /** The audit-log coverage `auditAllServices` declares. */
39
+ export const AUDIT_ALL_SERVICES = {
40
+ service: "allServices",
41
+ logTypes: ["ADMIN_READ", "DATA_READ", "DATA_WRITE"],
42
+ };
43
+ /**
44
+ * The recommended foundation folder structure. Exported so callers can
45
+ * extend it rather than restate it.
46
+ */
47
+ export const FOUNDATION_FOLDERS = {
48
+ Security: {},
49
+ Infrastructure: {},
50
+ Sandbox: {},
51
+ Workloads: {},
52
+ };
53
+ /**
54
+ * Author the desired-state tree an external governance reconciler reconciles. Pure.
55
+ * Throws when the tree attaches an org-policy name with no definition; only
56
+ * policies the tree actually attaches are included in the output.
57
+ */
58
+ export function landingZoneConfig(props = {}) {
59
+ const foundation = props.foundation ?? true;
60
+ const orgPolicies = {
61
+ ...(foundation
62
+ ? { "disable-sa-key-creation": DISABLE_SA_KEY_CREATION, "skip-default-network": SKIP_DEFAULT_NETWORK }
63
+ : {}),
64
+ ...(props.allowedLocations?.length
65
+ ? { "resource-location-restriction": resourceLocationRestriction(props.allowedLocations) }
66
+ : {}),
67
+ ...props.orgPolicies,
68
+ };
69
+ const folders = { ...(foundation ? FOUNDATION_FOLDERS : {}), ...props.folders };
70
+ const rootOrgPolicies = [
71
+ ...(foundation ? ["disable-sa-key-creation", "skip-default-network"] : []),
72
+ ...(props.allowedLocations?.length ? ["resource-location-restriction"] : []),
73
+ ...(props.rootOrgPolicies ?? []),
74
+ ];
75
+ const attached = new Set(rootOrgPolicies);
76
+ const collect = (tree) => {
77
+ for (const node of Object.values(tree)) {
78
+ for (const p of node.orgPolicies ?? [])
79
+ attached.add(p);
80
+ if (node.children)
81
+ collect(node.children);
82
+ }
83
+ };
84
+ collect(folders);
85
+ for (const name of attached) {
86
+ if (!orgPolicies[name]) {
87
+ throw new Error(`landingZoneConfig: org policy "${name}" is attached but not defined in orgPolicies`);
88
+ }
89
+ }
90
+ return {
91
+ organization: rootOrgPolicies.length ? { orgPolicies: rootOrgPolicies } : {},
92
+ folders,
93
+ orgPolicies: Object.fromEntries(Object.entries(orgPolicies).filter(([n]) => attached.has(n))),
94
+ ...(props.auditAllServices
95
+ ? { auditSinks: { auditConfig: { service: AUDIT_ALL_SERVICES.service, logTypes: [...AUDIT_ALL_SERVICES.logTypes] } } }
96
+ : {}),
97
+ };
98
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Config Connector TypeScript generator.
3
+ *
4
+ * Converts import IR from the parser into typed chant TypeScript code.
5
+ */
6
+ export class GcpGenerator {
7
+ generate(ir) {
8
+ const lines = [];
9
+ const imports = new Set();
10
+ // Collect imports
11
+ for (const resource of ir.resources) {
12
+ const parts = resource.type.split("::");
13
+ if (parts.length >= 3) {
14
+ // Use the short name for the import
15
+ imports.add(parts[2]);
16
+ }
17
+ }
18
+ if (imports.size > 0) {
19
+ lines.push(`import { ${[...imports].sort().join(", ")} } from "@intentius/chant-lexicon-gcp";`);
20
+ lines.push("");
21
+ }
22
+ // Generate resource declarations
23
+ for (const resource of ir.resources) {
24
+ const parts = resource.type.split("::");
25
+ const className = parts.length >= 3 ? parts[2] : resource.type;
26
+ const varName = camelCase(resource.logicalId);
27
+ lines.push(`export const ${varName} = new ${className}(${formatProps(resource.properties, 0)});`);
28
+ lines.push("");
29
+ }
30
+ return [{ path: "main.ts", content: lines.join("\n") + "\n" }];
31
+ }
32
+ }
33
+ function camelCase(str) {
34
+ return str
35
+ .replace(/[-_](.)/g, (_, c) => c.toUpperCase())
36
+ .replace(/^(.)/, (_, c) => c.toLowerCase());
37
+ }
38
+ function formatProps(props, indent) {
39
+ const entries = Object.entries(props);
40
+ if (entries.length === 0)
41
+ return "{}";
42
+ const pad = " ".repeat(indent + 1);
43
+ const closePad = " ".repeat(indent);
44
+ const lines = entries.map(([key, value]) => {
45
+ return `${pad}${key}: ${formatValue(value, indent + 1)},`;
46
+ });
47
+ return `{\n${lines.join("\n")}\n${closePad}}`;
48
+ }
49
+ function formatValue(value, indent) {
50
+ if (value === null || value === undefined)
51
+ return "undefined";
52
+ if (typeof value === "string")
53
+ return JSON.stringify(value);
54
+ if (typeof value === "number" || typeof value === "boolean")
55
+ return String(value);
56
+ if (Array.isArray(value)) {
57
+ if (value.length === 0)
58
+ return "[]";
59
+ const pad = " ".repeat(indent + 1);
60
+ const closePad = " ".repeat(indent);
61
+ const items = value.map((v) => `${pad}${formatValue(v, indent + 1)},`);
62
+ return `[\n${items.join("\n")}\n${closePad}]`;
63
+ }
64
+ if (typeof value === "object") {
65
+ return formatProps(value, indent);
66
+ }
67
+ return String(value);
68
+ }
@@ -0,0 +1,73 @@
1
+ import { hasOwnershipMarker, LABEL_OWNERSHIP_KEYS } from "@intentius/chant/ownership";
2
+ import { GcpParser } from "./parser.js";
3
+ /** Server-written metadata fields, stripped unless `verbatim`. */
4
+ const SERVER_METADATA_FIELDS = [
5
+ "managedFields",
6
+ "uid",
7
+ "resourceVersion",
8
+ "generation",
9
+ "creationTimestamp",
10
+ "selfLink",
11
+ "ownerReferences",
12
+ "finalizers",
13
+ ];
14
+ function isRecord(v) {
15
+ return typeof v === "object" && v !== null && !Array.isArray(v);
16
+ }
17
+ /**
18
+ * Strip `status` and server-written metadata to reach the declared shape of a
19
+ * Config Connector object. Config Connector writes back several
20
+ * `cnrm.cloud.google.com/*` annotations that are not authored config; those are
21
+ * dropped too. Returns a new object; the input is not mutated.
22
+ */
23
+ export function stripServerFields(obj) {
24
+ const clone = JSON.parse(JSON.stringify(obj));
25
+ delete clone.status;
26
+ if (isRecord(clone.metadata)) {
27
+ const md = clone.metadata;
28
+ for (const f of SERVER_METADATA_FIELDS)
29
+ delete md[f];
30
+ if (isRecord(md.annotations)) {
31
+ for (const key of Object.keys(md.annotations)) {
32
+ // Config Connector's own bookkeeping annotations, plus the apply
33
+ // round-trip annotation — none are authored config.
34
+ if (key.startsWith("cnrm.cloud.google.com/") ||
35
+ key === "kubectl.kubernetes.io/last-applied-configuration") {
36
+ delete md.annotations[key];
37
+ }
38
+ }
39
+ if (Object.keys(md.annotations).length === 0)
40
+ delete md.annotations;
41
+ }
42
+ }
43
+ return clone;
44
+ }
45
+ /**
46
+ * Build full-fidelity export IR from a list of live Config Connector objects
47
+ * (each live object is already its manifest). Reuses the import GcpParser by
48
+ * feeding cleaned objects as JSON documents. Pure: all I/O stays in the caller.
49
+ */
50
+ export function buildExportFromObjects(objects, opts = {}) {
51
+ const owning = opts.owned
52
+ ? objects.filter((o) => {
53
+ if (!isRecord(o))
54
+ return false;
55
+ const labels = isRecord(o.metadata) ? o.metadata.labels : undefined;
56
+ return hasOwnershipMarker(labels, LABEL_OWNERSHIP_KEYS);
57
+ })
58
+ : objects;
59
+ const cleaned = owning
60
+ .filter(isRecord)
61
+ .map((o) => (opts.verbatim ? o : stripServerFields(o)));
62
+ const content = cleaned.map((o) => JSON.stringify(o, null, 2)).join("\n---\n");
63
+ const ir = new GcpParser().parse(content);
64
+ const selector = opts.selector;
65
+ if (!selector || (selector.type === undefined && selector.name === undefined)) {
66
+ return ir;
67
+ }
68
+ return {
69
+ ...ir,
70
+ resources: ir.resources.filter((r) => (selector.type === undefined || r.type === selector.type) &&
71
+ (selector.name === undefined || r.logicalId === selector.name)),
72
+ };
73
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Config Connector YAML template parser.
3
+ *
4
+ * Parses Config Connector YAML manifests into the import IR
5
+ * for conversion to chant TypeScript.
6
+ */
7
+ import { BaseValueParser } from "@intentius/chant/import/base-parser";
8
+ import { parseYAML } from "@intentius/chant/yaml";
9
+ import { gcpTypeName } from "../spec/parse.js";
10
+ /**
11
+ * Parser for Config Connector YAML manifests.
12
+ */
13
+ export class GcpParser extends BaseValueParser {
14
+ dispatchIntrinsic(_key, _value, _obj) {
15
+ // Config Connector YAML has no intrinsic functions
16
+ return null;
17
+ }
18
+ parse(input) {
19
+ const resources = [];
20
+ const documents = input
21
+ .split(/^---\s*$/m)
22
+ .map((d) => d.trim())
23
+ .filter((d) => d.length > 0);
24
+ for (const docStr of documents) {
25
+ const doc = parseYAML(docStr);
26
+ if (!doc)
27
+ continue;
28
+ const apiVersion = doc.apiVersion;
29
+ const kind = doc.kind;
30
+ if (!apiVersion || !kind)
31
+ continue;
32
+ // Only handle Config Connector resources
33
+ if (!apiVersion.includes("cnrm.cloud.google.com"))
34
+ continue;
35
+ const group = apiVersion.split("/")[0];
36
+ const typeName = gcpTypeName(group, kind);
37
+ const metadata = doc.metadata;
38
+ const spec = doc.spec;
39
+ const logicalId = metadata?.name ?? kind;
40
+ // Build properties from spec
41
+ const properties = {};
42
+ if (metadata) {
43
+ properties.metadata = this.parseValue(metadata);
44
+ }
45
+ if (spec) {
46
+ for (const [key, value] of Object.entries(spec)) {
47
+ properties[key] = this.parseValue(value);
48
+ }
49
+ }
50
+ resources.push({
51
+ logicalId,
52
+ type: typeName,
53
+ properties,
54
+ });
55
+ }
56
+ return {
57
+ resources,
58
+ parameters: [],
59
+ };
60
+ }
61
+ }
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ // Typed Op step-builder wrappers (chant #1288 Stage 2) — gcpApply/gcpDelete/
2
+ // flociGcpUp/flociGcpDown with authoring-time types derived from this
3
+ // lexicon's own *Args interfaces (see ./op/builders.ts's module doc for why
4
+ // these live here rather than in core). Opt-in: `@intentius/chant/op`'s
5
+ // same-named exports are core's original untyped builders, unchanged.
6
+ export { gcpApply, gcpDelete, flociGcpUp, flociGcpDown } from "./op/builders.js";
7
+ // Serializer
8
+ export { gcpSerializer } from "./serializer.js";
9
+ // Plugin
10
+ export { gcpPlugin } from "./plugin.js";
11
+ // Default labels & annotations
12
+ export { defaultLabels, defaultAnnotations, isDefaultLabels, isDefaultAnnotations } from "./default-labels.js";
13
+ export { DEFAULT_LABELS_MARKER, DEFAULT_ANNOTATIONS_MARKER } from "./default-labels.js";
14
+ // Pseudo-parameters
15
+ export { GCP, ProjectId, Region, Zone } from "./pseudo.js";
16
+ // Variables / annotation constants
17
+ export { GcpAnnotations } from "./variables.js";
18
+ // Generated entities — export everything from generated index
19
+ // After running `npm run generate`, this re-exports all Config Connector resource classes
20
+ export * from "./generated/index.js";
21
+ // Composites
22
+ export { GkeCluster, CloudRunServiceComposite, CloudSqlInstance, GcsBucket, VpcNetwork, PubSubPipeline, CloudFunctionWithTrigger, PrivateService, ManagedCertificate, SecureProject, MemorystoreRedis, MultiRegionVpc, GkeCrdbRegion, GpuNodePool, GovernanceFoundation, LocationRestriction, OrganizationAuditConfig, } from "./composites/index.js";
23
+ // GCP governance authoring: the desired-state config an external governance
24
+ // reconciler applies, and the typed landing-zone authoring layer.
25
+ export { landingZoneConfig, resourceLocationRestriction, DISABLE_SA_KEY_CREATION, SKIP_DEFAULT_NETWORK, AUDIT_ALL_SERVICES, FOUNDATION_FOLDERS, } from "./governance.js";
26
+ // IAM role constants
27
+ export { StorageRoles, ComputeRoles, ContainerRoles, IAMRoles, SQLRoles, RunRoles, PubSubRoles } from "./actions/index.js";
28
+ // Spec utilities (for tooling)
29
+ export { fetchCRDBundle, getCachePath, clearCache, KCC_VERSION } from "./spec/fetch.js";
30
+ export { parseGcpCRD, gcpServiceName, gcpShortName, gcpTypeName, stripServicePrefix } from "./spec/parse.js";
31
+ // Code generation pipeline
32
+ export { generate, writeGeneratedFiles } from "./codegen/generate.js";
33
+ export { packageLexicon } from "./codegen/package.js";
34
+ // LSP providers
35
+ export { gcpCompletions } from "./lsp/completions.js";
36
+ export { gcpHover } from "./lsp/hover.js";
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "ae0eab325af048dc37c4bf874c2bacd1e1557e9de3b6f0414ee14f6074a98cf4",
4
+ "manifest.json": "7ae4134a4744904433410962da429f7de99f88088ba05ecf80d11f68f9686037",
5
5
  "meta.json": "475f10909854d059a2e2d4fb541c058803422e38b9c64bd4d6858c0623eedb8c",
6
6
  "types/index.d.ts": "5433b54c04b57d6fda8f3d81ef92ed0aca87f7e8e5dd3ea92d8de6e62a3ccfab",
7
7
  "rules/hardcoded-project.ts": "6d5ae9efa74d73800eebe1a0a0d43c619ee0fdebe3347195df93fcbe63b34f0d",
8
8
  "rules/hardcoded-region.ts": "d7f8c48571f37600cb583a08aef7eb07245fbcbdda048874bb074c94c849ae0f",
9
9
  "rules/public-iam.ts": "9c43b2d148dba1aea3494983e042d7b038d92a8309173e7e35bb17f6ee21db9e",
10
10
  "rules/gcp-helpers.ts": "0420adb73527b29a288e219b24742f4f29ef83859228d61fdaff83b1fb800bf1",
11
- "rules/schema-registry.ts": "0d90aa97ca893478b4e0b7ac02fb017786929abd1f241b12c90915bffa8499d8",
11
+ "rules/schema-registry.ts": "fa9d8e3c1ebee2b95b3385044c7c24c50e71626f5b2936b9412aeb03b0ce7e7a",
12
12
  "rules/wgc101.ts": "679f245ea4665d687848382ff1b934ae133a3c320cc051011cc4d95802e66b38",
13
13
  "rules/wgc102.ts": "22cb997d40a59ce3e0ff833ce946a5571e13d4811b255d4b67db570d58575e07",
14
14
  "rules/wgc103.ts": "589741dbe5828508b5424a74f63f0202ba8452237c366173af283073f81e0317",
@@ -40,5 +40,5 @@
40
40
  "skills/chant-gcp-patterns.md": "4c6809fc7c37fa1879379ad4c0e2d114dec9497bc6eb9d1bca4e79fddea4f509",
41
41
  "skills/chant-gcp-gke.md": "be277019da9a722c851e47cd2dfb9c9536668948c3535fb20db7697e934c4e2b"
42
42
  },
43
- "composite": "9225fc26ad4bedac00ce150ef40a6e26319d15ba91d73b04cdbda13f6b2d63ac"
43
+ "composite": "91088333d0ea3e2df519e219268dc206fc5438c63c163b37fbfbaf58967fbc43"
44
44
  }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The gcp lexicon's `chant audit` catalog — metadata for the WGC* Config
3
+ * Connector post-synth rules. Contributed via `gcpPlugin.auditCatalog()` (#687).
4
+ */
5
+ import { auditRule, applyLineage } from "@intentius/chant/audit/catalog";
6
+ import { gcpAuditLineage } from "./audit-lineage.js";
7
+ const GCP_SEC = { name: "Google Cloud — Security best practices", url: "https://cloud.google.com/security/best-practices" };
8
+ export const gcpAuditCatalog = {
9
+ WGC101: auditRule("WGC101", "merge-worthy", "guidance", "Storage/SQL without encryption configuration", "Configure encryption (e.g. a CMEK key) for data at rest.", { authority: [GCP_SEC] }),
10
+ WGC102: auditRule("WGC102", "merge-worthy", "guidance", "Public IAM member (allUsers/allAuthenticatedUsers)", "Remove allUsers/allAuthenticatedUsers bindings.", { authority: [GCP_SEC] }),
11
+ WGC103: auditRule("WGC103", "report-only", "guidance", "Missing project-id annotation", "Add the cnrm.cloud.google.com/project-id annotation.", { category: "best-practice" }),
12
+ WGC104: auditRule("WGC104", "merge-worthy", "guidance", "Bucket without uniform bucket-level access", "Enable uniformBucketLevelAccess.", { authority: [GCP_SEC] }),
13
+ WGC105: auditRule("WGC105", "merge-worthy", "guidance", "Cloud SQL open to 0.0.0.0/0", "Restrict authorizedNetworks to known sources.", { authority: [GCP_SEC] }),
14
+ WGC106: auditRule("WGC106", "report-only", "guidance", "Missing deletion-policy annotation", "Add the cnrm.cloud.google.com/deletion-policy annotation.", { category: "best-practice" }),
15
+ WGC107: auditRule("WGC107", "report-only", "guidance", "Bucket versioning disabled", "Enable object versioning.", { category: "best-practice" }),
16
+ WGC108: auditRule("WGC108", "report-only", "guidance", "Cloud SQL backups disabled", "Enable backup configuration.", { category: "best-practice" }),
17
+ WGC109: auditRule("WGC109", "merge-worthy", "guidance", "Firewall open to 0.0.0.0/0", "Restrict sourceRanges to known sources.", { authority: [GCP_SEC] }),
18
+ WGC110: auditRule("WGC110", "merge-worthy", "guidance", "KMS key without rotation", "Set a rotationPeriod on the CryptoKey.", { authority: [GCP_SEC] }),
19
+ WGC111: auditRule("WGC111", "merge-worthy", "guidance", "Reference to an undefined resource", "Point the reference at a resource in the output.", { category: "correctness" }),
20
+ WGC112: auditRule("WGC112", "merge-worthy", "guidance", "Missing or invalid apiVersion", "Set a valid cnrm.cloud.google.com apiVersion.", { category: "correctness" }),
21
+ WGC113: auditRule("WGC113", "report-only", "guidance", "Alpha API version", "Move to a beta/GA API version.", { category: "best-practice" }),
22
+ WGC201: auditRule("WGC201", "report-only", "guidance", "Missing managed-by label", "Add the app.kubernetes.io/managed-by label.", { category: "best-practice" }),
23
+ WGC202: auditRule("WGC202", "merge-worthy", "guidance", "Cluster without Workload Identity", "Enable Workload Identity on the ContainerCluster.", { authority: [GCP_SEC] }),
24
+ WGC203: auditRule("WGC203", "merge-worthy", "guidance", "Node pool uses broad cloud-platform scope", "Use narrowly-scoped OAuth scopes instead of cloud-platform.", { authority: [GCP_SEC] }),
25
+ WGC204: auditRule("WGC204", "report-only", "guidance", "Compute instance without Shielded VM", "Enable Shielded VM configuration.", { category: "best-practice" }),
26
+ WGC301: auditRule("WGC301", "report-only", "guidance", "No IAMAuditConfig found", "Configure audit logging via IAMAuditConfig.", { category: "best-practice" }),
27
+ WGC302: auditRule("WGC302", "report-only", "guidance", "No Service (enabled APIs) found", "Declare the GCP APIs you depend on.", { category: "best-practice" }),
28
+ WGC303: auditRule("WGC303", "report-only", "guidance", "No VPC Service Controls perimeter", "Consider an AccessContextManager ServicePerimeter.", { category: "best-practice" }),
29
+ WGC401: auditRule("WGC401", "merge-worthy", "guidance", "Unknown field in resource spec", "Remove the unknown spec field.", { category: "correctness" }),
30
+ WGC402: auditRule("WGC402", "merge-worthy", "guidance", "Missing required spec field", "Add the required spec field.", { category: "correctness" }),
31
+ WGC403: auditRule("WGC403", "merge-worthy", "guidance", "Spec field has wrong type/structure", "Fix the field's type/structure.", { category: "correctness" }),
32
+ // governance posture regressions.
33
+ WGC501: auditRule("WGC501", "merge-worthy", "guidance", "Org Policy guardrail not enforced", "Enable enforce on the rule (or remove the policy deliberately) — a disabled org policy constrains nothing.", { authority: [GCP_SEC] }),
34
+ WGC502: auditRule("WGC502", "merge-worthy", "guidance", "Org Policy guardrail defines no rules", "Add rules to the OrgPolicyPolicy — without them the constraint binds nothing.", { authority: [GCP_SEC] }),
35
+ WGC503: auditRule("WGC503", "merge-worthy", "guidance", "Audit logging dropped or scoped down", "Keep auditLogConfigs populated and remove exemptedMembers from the IAMAuditConfig.", { authority: [GCP_SEC] }),
36
+ };
37
+ // Prior art credits live beside the rules in ./audit-lineage.ts (see core audit/prior-art.ts).
38
+ applyLineage(gcpAuditCatalog, gcpAuditLineage);
@@ -0,0 +1,63 @@
1
+ export const gcpAuditLineage = {
2
+ WGC101: [
3
+ { tool: "gcp-policy-library", rule: "GCPStorageCMEKEncryptionConstraintV1", url: "https://github.com/GoogleCloudPlatform/policy-library/blob/master/policies/templates/gcp_storage_cmek_encryption_v1.yaml", relation: "overlaps" },
4
+ ],
5
+ WGC102: [
6
+ { tool: "checkov", rule: "CKV_GCP_28", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
7
+ { tool: "kics", rule: "Cloud Storage Anonymous or Publicly Accessible", url: "https://docs.kics.io/latest/queries/terraform-queries/gcp/a6cd52a1-3056-4910-96a5-894de9f3f3b3/", relation: "overlaps" },
8
+ { tool: "gcp-policy-library", rule: "GCPStorageBucketWorldReadableConstraintV1", url: "https://github.com/GoogleCloudPlatform/policy-library/blob/master/policies/templates/gcp_storage_bucket_world_readable_v1.yaml", relation: "overlaps" },
9
+ ],
10
+ WGC104: [
11
+ { tool: "checkov", rule: "CKV_GCP_29", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
12
+ { tool: "kics", rule: "Google Storage Bucket Level Access Disabled", url: "https://docs.kics.io/latest/queries/terraform-queries/gcp/bb0db090-5509-4853-a827-75ced0b3caa0/", relation: "overlaps" },
13
+ { tool: "gcp-policy-library", rule: "GCPStorageBucketPolicyOnlyConstraintV1", url: "https://github.com/GoogleCloudPlatform/policy-library/blob/master/policies/templates/gcp_storage_bucket_policy_only_v1.yaml", relation: "overlaps" },
14
+ ],
15
+ WGC105: [
16
+ { tool: "checkov", rule: "CKV_GCP_11", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
17
+ { tool: "kics", rule: "SQL DB Instance Publicly Accessible", url: "https://docs.kics.io/latest/queries/terraform-queries/gcp/b187edca-b81e-4fdc-aff4-aab57db45edb/", relation: "overlaps" },
18
+ { tool: "gcp-policy-library", rule: "GCPSQLWorldReadableConstraintV1", url: "https://github.com/GoogleCloudPlatform/policy-library/blob/master/policies/templates/gcp_sql_world_readable_v1.yaml", relation: "overlaps" },
19
+ ],
20
+ WGC107: [
21
+ { tool: "checkov", rule: "CKV_GCP_78", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
22
+ { tool: "kics", rule: "Cloud Storage Bucket Versioning Disabled", url: "https://docs.kics.io/latest/queries/terraform-queries/gcp/e7e961ac-d17e-4413-84bc-8a1fbe242944/", relation: "overlaps" },
23
+ ],
24
+ WGC108: [
25
+ { tool: "checkov", rule: "CKV_GCP_14", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
26
+ { tool: "kics", rule: "SQL DB Instance Backup Disabled", url: "https://docs.kics.io/latest/queries/terraform-queries/gcp/cf3c7631-cd1e-42f3-8801-a561214a6e79/", relation: "overlaps" },
27
+ { tool: "gcp-policy-library", rule: "GCPSQLBackupConstraintV1", url: "https://github.com/GoogleCloudPlatform/policy-library/blob/master/policies/templates/gcp_sql_backup_v1.yaml", relation: "overlaps" },
28
+ ],
29
+ WGC109: [
30
+ { tool: "gcp-policy-library", rule: "GCPRestrictedFirewallRulesConstraintV1", url: "https://github.com/GoogleCloudPlatform/policy-library/blob/master/policies/templates/gcp_restricted_firewall_rules_v1.yaml", relation: "overlaps" },
31
+ { tool: "checkov", rule: "CKV2_GCP_12", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
32
+ ],
33
+ WGC110: [
34
+ { tool: "checkov", rule: "CKV_GCP_43", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
35
+ { tool: "kics", rule: "High Google KMS Crypto Key Rotation Period", url: "https://docs.kics.io/latest/queries/terraform-queries/gcp/d8c57c4e-bf6f-4e32-a2bf-8643532de77b/", relation: "overlaps" },
36
+ { tool: "gcp-policy-library", rule: "GCPCMEKRotationConstraintV1", url: "https://github.com/GoogleCloudPlatform/policy-library/blob/master/policies/templates/gcp_cmek_rotation_v1.yaml", relation: "overlaps" },
37
+ ],
38
+ WGC202: [
39
+ { tool: "gcp-policy-library", rule: "GCPGKEEnableWorkloadIdentityConstraintV1", url: "https://github.com/GoogleCloudPlatform/policy-library/blob/master/policies/templates/gcp_gke_enable_workload_identity_v1.yaml", relation: "overlaps" },
40
+ { tool: "checkov", rule: "CKV_GCP_69", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
41
+ ],
42
+ WGC203: [
43
+ { tool: "gcp-policy-library", rule: "GCPGKEAllowedNodeSAConstraintV1", url: "https://github.com/GoogleCloudPlatform/policy-library/blob/master/policies/templates/gcp_gke_allowed_node_sa_v1.yaml", relation: "overlaps" },
44
+ { tool: "checkov", rule: "CKV_GCP_31", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
45
+ { tool: "kics", rule: "VM With Full Cloud Access", url: "https://docs.kics.io/latest/queries/terraform-queries/gcp/bc280331-27b9-4acb-a010-018e8098aa5d/", relation: "overlaps" },
46
+ ],
47
+ WGC204: [
48
+ { tool: "checkov", rule: "CKV_GCP_39", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
49
+ { tool: "kics", rule: "Shielded VM Disabled", url: "https://docs.kics.io/latest/queries/terraform-queries/gcp/1b44e234-3d73-41a8-9954-0b154135280e/", relation: "overlaps" },
50
+ ],
51
+ WGC301: [
52
+ { tool: "gcp-policy-library", rule: "GCPIAMAuditLogConstraint", url: "https://github.com/GoogleCloudPlatform/policy-library/blob/master/policies/templates/gcp_iam_audit_log.yaml", relation: "overlaps" },
53
+ { tool: "checkov", rule: "CKV2_GCP_5", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
54
+ ],
55
+ WGC303: [
56
+ { tool: "gcp-policy-library", rule: "GCPVPCSCEnsureProjectConstraintV1", url: "https://github.com/GoogleCloudPlatform/policy-library/blob/master/policies/templates/gcp_vpc_sc_ensure_project_v1.yaml", relation: "overlaps" },
57
+ ],
58
+ WGC503: [
59
+ { tool: "gcp-policy-library", rule: "GCPIAMAuditLogConstraint", url: "https://github.com/GoogleCloudPlatform/policy-library/blob/master/policies/templates/gcp_iam_audit_log.yaml", relation: "overlaps" },
60
+ { tool: "checkov", rule: "CKV2_GCP_5", url: "https://www.checkov.io/5.Policy%20Index/terraform.html", relation: "overlaps" },
61
+ { tool: "kics", rule: "IAM Audit Not Properly Configured", url: "https://docs.kics.io/latest/queries/terraform-queries/gcp/89fe890f-b480-460c-8b6b-7d8b1468adb4/", relation: "overlaps" },
62
+ ],
63
+ };
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Shared helpers for GCP Config Connector post-synthesis lint rules.
3
+ *
4
+ * Provides YAML parsing for multi-document Config Connector manifests
5
+ * and accessor utilities for common manifest fields.
6
+ */
7
+ import { parseYAML } from "@intentius/chant/yaml";
8
+ export { getPrimaryOutput } from "@intentius/chant/lint/post-synth";
9
+ /**
10
+ * Split a multi-document YAML string on `---` boundaries and parse each
11
+ * document into a GcpManifest.
12
+ */
13
+ export function parseGcpManifests(yaml) {
14
+ const documents = yaml.split(/^---\s*$/m);
15
+ const manifests = [];
16
+ for (const doc of documents) {
17
+ const trimmed = doc.trim();
18
+ if (trimmed === "" || trimmed === "---")
19
+ continue;
20
+ try {
21
+ const parsed = parseYAML(trimmed);
22
+ if (typeof parsed === "object" && parsed !== null) {
23
+ manifests.push(parsed);
24
+ }
25
+ }
26
+ catch {
27
+ // Skip unparseable documents
28
+ }
29
+ }
30
+ return manifests;
31
+ }
32
+ /**
33
+ * Check if a manifest is a Config Connector resource
34
+ * (apiVersion contains cnrm.cloud.google.com).
35
+ */
36
+ export function isConfigConnectorResource(manifest) {
37
+ return typeof manifest.apiVersion === "string" &&
38
+ manifest.apiVersion.includes("cnrm.cloud.google.com");
39
+ }
40
+ /**
41
+ * Safely extract the spec from a manifest.
42
+ */
43
+ export function getSpec(manifest) {
44
+ return manifest.spec ?? undefined;
45
+ }
46
+ /**
47
+ * Safely extract annotations from a manifest's metadata.
48
+ */
49
+ export function getAnnotations(manifest) {
50
+ return manifest.metadata?.annotations ?? undefined;
51
+ }
52
+ /**
53
+ * Get the resource name from metadata.
54
+ */
55
+ export function getResourceName(manifest) {
56
+ return manifest.metadata?.name ?? "unknown";
57
+ }
58
+ /**
59
+ * Recursively walk a spec object looking for keys ending in `Ref`
60
+ * (e.g. `networkRef`, `topicRef`, `clusterRef`) that have a `name`
61
+ * sub-field. Returns the set of referenced names.
62
+ *
63
+ * Skips `external` refs (cross-project references outside the template).
64
+ */
65
+ export function findResourceRefs(obj) {
66
+ const refs = new Set();
67
+ walkForRefs(obj, refs);
68
+ return refs;
69
+ }
70
+ function walkForRefs(value, refs) {
71
+ if (value === null || value === undefined)
72
+ return;
73
+ if (Array.isArray(value)) {
74
+ for (const item of value) {
75
+ walkForRefs(item, refs);
76
+ }
77
+ return;
78
+ }
79
+ if (typeof value === "object") {
80
+ const obj = value;
81
+ for (const [key, val] of Object.entries(obj)) {
82
+ if (key.endsWith("Ref") && typeof val === "object" && val !== null) {
83
+ const refObj = val;
84
+ if (typeof refObj.name === "string" && !refObj.external) {
85
+ refs.add(refObj.name);
86
+ }
87
+ }
88
+ walkForRefs(val, refs);
89
+ }
90
+ }
91
+ }
@@ -0,0 +1,54 @@
1
+ import { wgc101 } from "./wgc101.js";
2
+ import { wgc102 } from "./wgc102.js";
3
+ import { wgc103 } from "./wgc103.js";
4
+ import { wgc104 } from "./wgc104.js";
5
+ import { wgc105 } from "./wgc105.js";
6
+ import { wgc106 } from "./wgc106.js";
7
+ import { wgc107 } from "./wgc107.js";
8
+ import { wgc108 } from "./wgc108.js";
9
+ import { wgc109 } from "./wgc109.js";
10
+ import { wgc110 } from "./wgc110.js";
11
+ import { wgc111 } from "./wgc111.js";
12
+ import { wgc112 } from "./wgc112.js";
13
+ import { wgc113 } from "./wgc113.js";
14
+ import { wgc201 } from "./wgc201.js";
15
+ import { wgc202 } from "./wgc202.js";
16
+ import { wgc203 } from "./wgc203.js";
17
+ import { wgc204 } from "./wgc204.js";
18
+ import { wgc301 } from "./wgc301.js";
19
+ import { wgc302 } from "./wgc302.js";
20
+ import { wgc303 } from "./wgc303.js";
21
+ import { wgc401 } from "./wgc401.js";
22
+ import { wgc402 } from "./wgc402.js";
23
+ import { wgc403 } from "./wgc403.js";
24
+ import { wgc501 } from "./wgc501.js";
25
+ import { wgc502 } from "./wgc502.js";
26
+ import { wgc503 } from "./wgc503.js";
27
+ export const postSynthChecks = [
28
+ wgc101,
29
+ wgc102,
30
+ wgc103,
31
+ wgc104,
32
+ wgc105,
33
+ wgc106,
34
+ wgc107,
35
+ wgc108,
36
+ wgc109,
37
+ wgc110,
38
+ wgc111,
39
+ wgc112,
40
+ wgc113,
41
+ wgc201,
42
+ wgc202,
43
+ wgc203,
44
+ wgc204,
45
+ wgc301,
46
+ wgc302,
47
+ wgc303,
48
+ wgc401,
49
+ wgc402,
50
+ wgc403,
51
+ wgc501,
52
+ wgc502,
53
+ wgc503,
54
+ ];
@@ -1 +1 @@
1
- {"version":3,"file":"schema-registry.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/schema-registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAUD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAoB/D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAiBxD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAavF"}
1
+ {"version":3,"file":"schema-registry.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/schema-registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAUD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAoB/D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAiBxD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,CAavF"}