@apexops/capabilities 0.10.0-next.4 → 0.11.0-next.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 (93) hide show
  1. package/README.md +26 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/archetype-source.d.ts +49 -0
  4. package/dist/archetype-source.d.ts.map +1 -0
  5. package/dist/archetype-source.js +421 -0
  6. package/dist/archetype-source.js.map +1 -0
  7. package/dist/bound-envelope-transport.js +1 -1
  8. package/dist/bound-envelope-transport.js.map +1 -1
  9. package/dist/executable-resolution.d.ts +21 -0
  10. package/dist/executable-resolution.d.ts.map +1 -0
  11. package/dist/executable-resolution.js +87 -0
  12. package/dist/executable-resolution.js.map +1 -0
  13. package/dist/governance-baseline.d.ts +87 -0
  14. package/dist/governance-baseline.d.ts.map +1 -0
  15. package/dist/governance-baseline.js +639 -0
  16. package/dist/governance-baseline.js.map +1 -0
  17. package/dist/governance-setup.d.ts +13 -0
  18. package/dist/governance-setup.d.ts.map +1 -0
  19. package/dist/governance-setup.js +231 -0
  20. package/dist/governance-setup.js.map +1 -0
  21. package/dist/iac-generation.d.ts.map +1 -1
  22. package/dist/iac-generation.js +56 -20
  23. package/dist/iac-generation.js.map +1 -1
  24. package/dist/iac-normalizers.d.ts.map +1 -1
  25. package/dist/iac-normalizers.js +19 -1
  26. package/dist/iac-normalizers.js.map +1 -1
  27. package/dist/iac.d.ts +30 -2
  28. package/dist/iac.d.ts.map +1 -1
  29. package/dist/iac.js.map +1 -1
  30. package/dist/index.d.ts +6 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +6 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/native-iac-providers.d.ts +20 -4
  35. package/dist/native-iac-providers.d.ts.map +1 -1
  36. package/dist/native-iac-providers.js +591 -27
  37. package/dist/native-iac-providers.js.map +1 -1
  38. package/dist/pack-manager.d.ts.map +1 -1
  39. package/dist/pack-manager.js +15 -14
  40. package/dist/pack-manager.js.map +1 -1
  41. package/dist/policy-validation.d.ts +68 -0
  42. package/dist/policy-validation.d.ts.map +1 -0
  43. package/dist/policy-validation.js +653 -0
  44. package/dist/policy-validation.js.map +1 -0
  45. package/dist/process-runner.d.ts.map +1 -1
  46. package/dist/process-runner.js +10 -1
  47. package/dist/process-runner.js.map +1 -1
  48. package/dist/rename-retry.d.ts +12 -0
  49. package/dist/rename-retry.d.ts.map +1 -0
  50. package/dist/rename-retry.js +27 -0
  51. package/dist/rename-retry.js.map +1 -0
  52. package/dist/repository-publish.d.ts +17 -0
  53. package/dist/repository-publish.d.ts.map +1 -0
  54. package/dist/repository-publish.js +124 -0
  55. package/dist/repository-publish.js.map +1 -0
  56. package/dist/terraform-provider-schema.d.ts.map +1 -1
  57. package/dist/terraform-provider-schema.js +6 -2
  58. package/dist/terraform-provider-schema.js.map +1 -1
  59. package/dist/test/archetype-source.test.d.ts +2 -0
  60. package/dist/test/archetype-source.test.d.ts.map +1 -0
  61. package/dist/test/archetype-source.test.js +258 -0
  62. package/dist/test/archetype-source.test.js.map +1 -0
  63. package/dist/test/executable-resolution.test.d.ts +2 -0
  64. package/dist/test/executable-resolution.test.d.ts.map +1 -0
  65. package/dist/test/executable-resolution.test.js +87 -0
  66. package/dist/test/executable-resolution.test.js.map +1 -0
  67. package/dist/test/governance-baseline.test.d.ts +2 -0
  68. package/dist/test/governance-baseline.test.d.ts.map +1 -0
  69. package/dist/test/governance-baseline.test.js +841 -0
  70. package/dist/test/governance-baseline.test.js.map +1 -0
  71. package/dist/test/governance-setup.test.d.ts +2 -0
  72. package/dist/test/governance-setup.test.d.ts.map +1 -0
  73. package/dist/test/governance-setup.test.js +160 -0
  74. package/dist/test/governance-setup.test.js.map +1 -0
  75. package/dist/test/iac-generation.test.js +203 -14
  76. package/dist/test/iac-generation.test.js.map +1 -1
  77. package/dist/test/native-iac-providers.test.js +1482 -5
  78. package/dist/test/native-iac-providers.test.js.map +1 -1
  79. package/dist/test/policy-validation.test.d.ts +2 -0
  80. package/dist/test/policy-validation.test.d.ts.map +1 -0
  81. package/dist/test/policy-validation.test.js +1061 -0
  82. package/dist/test/policy-validation.test.js.map +1 -0
  83. package/dist/test/rename-retry.test.d.ts +2 -0
  84. package/dist/test/rename-retry.test.d.ts.map +1 -0
  85. package/dist/test/rename-retry.test.js +25 -0
  86. package/dist/test/rename-retry.test.js.map +1 -0
  87. package/dist/test/repository-publish.test.d.ts +2 -0
  88. package/dist/test/repository-publish.test.d.ts.map +1 -0
  89. package/dist/test/repository-publish.test.js +123 -0
  90. package/dist/test/repository-publish.test.js.map +1 -0
  91. package/dist/test/terraform-provider-schema.test.js +12 -6
  92. package/dist/test/terraform-provider-schema.test.js.map +1 -1
  93. package/package.json +3 -3
@@ -0,0 +1,639 @@
1
+ import { GOVERNANCE_MAX_AGE_MS } from "@apexops/contracts";
2
+ import { createHash } from "node:crypto";
3
+ export class GovernanceBaselineError extends Error {
4
+ code;
5
+ constructor(code) {
6
+ super(`Governance baseline rejected: ${code}`);
7
+ this.code = code;
8
+ this.name = "GovernanceBaselineError";
9
+ }
10
+ }
11
+ const SUBSCRIPTION_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
12
+ const MAX_INPUT_BYTES = 20_000_000;
13
+ function fail(code = "invalid-input") {
14
+ throw new GovernanceBaselineError(code);
15
+ }
16
+ function record(value) {
17
+ if (value === null || typeof value !== "object" || Array.isArray(value))
18
+ fail();
19
+ return value;
20
+ }
21
+ function text(value) {
22
+ if (typeof value !== "string" || value.length === 0 || value.length > 4096)
23
+ fail();
24
+ return value;
25
+ }
26
+ function count(value) {
27
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
28
+ fail();
29
+ return value;
30
+ }
31
+ function array(value) {
32
+ if (!Array.isArray(value))
33
+ fail();
34
+ return value;
35
+ }
36
+ function strings(value) {
37
+ return [...new Set(array(value).map(text))].sort();
38
+ }
39
+ function subscription(value) {
40
+ const result = text(value);
41
+ if (!SUBSCRIPTION_ID.test(result))
42
+ fail("target-mismatch");
43
+ return result.toLowerCase();
44
+ }
45
+ function boundedJson(value) {
46
+ let nodes = 0;
47
+ const pending = [{ value, depth: 0 }];
48
+ while (pending.length > 0) {
49
+ const current = pending.pop();
50
+ if (++nodes > 4096 || current.depth > 16)
51
+ fail();
52
+ if (typeof current.value === "number" && !Number.isFinite(current.value))
53
+ fail();
54
+ if (current.value !== null && typeof current.value === "object") {
55
+ for (const child of Object.values(current.value))
56
+ pending.push({ value: child, depth: current.depth + 1 });
57
+ }
58
+ else if (current.value !== null && !["string", "number", "boolean"].includes(typeof current.value))
59
+ fail();
60
+ }
61
+ if (Buffer.byteLength(JSON.stringify(value)) > 65_536)
62
+ fail();
63
+ return value;
64
+ }
65
+ function timestamp(value) {
66
+ const result = text(value);
67
+ if (!/^\d{4}-\d{2}-\d{2}T(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$/u.test(result))
68
+ fail();
69
+ const parsed = Date.parse(result);
70
+ const date = result.slice(0, 10);
71
+ if (!Number.isFinite(parsed) || new Date(`${date}T00:00:00Z`).toISOString().slice(0, 10) !== date)
72
+ fail();
73
+ return parsed;
74
+ }
75
+ function canonical(value) {
76
+ if (Array.isArray(value))
77
+ return `[${value.map(canonical).join(",")}]`;
78
+ if (value !== null && typeof value === "object") {
79
+ return `{${Object.entries(value)
80
+ .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
81
+ .map(([key, entry]) => `${JSON.stringify(key)}:${canonical(entry)}`)
82
+ .join(",")}}`;
83
+ }
84
+ return JSON.stringify(value);
85
+ }
86
+ function jsonInput(input) {
87
+ try {
88
+ if (input instanceof Uint8Array && input.byteLength > MAX_INPUT_BYTES)
89
+ fail();
90
+ const encoded = input instanceof Uint8Array
91
+ ? new TextDecoder("utf-8", { fatal: true }).decode(input)
92
+ : typeof input === "string"
93
+ ? input
94
+ : JSON.stringify(input, (_key, value) => {
95
+ if (typeof value === "number" && !Number.isFinite(value))
96
+ fail();
97
+ return value;
98
+ });
99
+ if (typeof encoded !== "string" || Buffer.byteLength(encoded) > MAX_INPUT_BYTES)
100
+ fail();
101
+ const parsed = JSON.parse(encoded);
102
+ const pending = [{ value: parsed, depth: 0 }];
103
+ while (pending.length > 0) {
104
+ const { value, depth } = pending.pop();
105
+ if (depth > 64)
106
+ fail();
107
+ if (value !== null && typeof value === "object") {
108
+ for (const child of Object.values(value))
109
+ pending.push({ value: child, depth: depth + 1 });
110
+ }
111
+ }
112
+ return parsed;
113
+ }
114
+ catch {
115
+ return fail();
116
+ }
117
+ }
118
+ function scopeSegment(value) {
119
+ return value.length > 0 && value !== "." && value !== ".." && !/[?#%\s]/u.test(value);
120
+ }
121
+ // Policy assignment and exemption names may contain inner spaces (for example "(ArcBox) Tag resources").
122
+ const POLICY_RESOURCE_NAME = String.raw `[^/?#%\s\p{Cc}](?:[^/?#%\p{Cc}]*[^/?#%\s\p{Cc}])?`;
123
+ const POLICY_ASSIGNMENT_SUFFIX = new RegExp(`^/providers/microsoft\\.authorization/policyassignments/${POLICY_RESOURCE_NAME}$`, "iu");
124
+ const POLICY_EXEMPTION_SUFFIX = new RegExp(`^/providers/microsoft\\.authorization/policyexemptions/${POLICY_RESOURCE_NAME}$`, "iu");
125
+ function scope(value, subscriptionId) {
126
+ const result = text(value);
127
+ const normalized = result.toLowerCase();
128
+ const segments = normalized.split("/");
129
+ if (segments[0] !== "")
130
+ fail("target-mismatch");
131
+ if (segments.length === 5 &&
132
+ segments[1] === "providers" &&
133
+ segments[2] === "microsoft.management" &&
134
+ segments[3] === "managementgroups" &&
135
+ scopeSegment(segments[4])) {
136
+ return result;
137
+ }
138
+ if (segments[1] !== "subscriptions" || segments[2] !== subscriptionId || !SUBSCRIPTION_ID.test(segments[2] ?? ""))
139
+ fail("target-mismatch");
140
+ let offset = 3;
141
+ if (segments[offset] === "resourcegroups") {
142
+ if (!scopeSegment(segments[offset + 1] ?? ""))
143
+ fail("target-mismatch");
144
+ offset += 2;
145
+ }
146
+ if (offset === segments.length)
147
+ return result;
148
+ if (segments[offset] !== "providers" || !scopeSegment(segments[offset + 1] ?? ""))
149
+ fail("target-mismatch");
150
+ offset += 2;
151
+ const resourceSegments = segments.slice(offset);
152
+ if (resourceSegments.length < 2 || resourceSegments.length % 2 !== 0 || !resourceSegments.every(scopeSegment)) {
153
+ fail("target-mismatch");
154
+ }
155
+ return result;
156
+ }
157
+ function enforcementMode(source) {
158
+ if (!Object.hasOwn(source, "enforcementMode"))
159
+ return undefined;
160
+ const mode = source.enforcementMode;
161
+ if (mode !== "Default" && mode !== "DoNotEnforce")
162
+ fail();
163
+ return mode;
164
+ }
165
+ function finding(value, subscriptionId, descendants = false) {
166
+ const source = record(value);
167
+ const mode = enforcementMode(source);
168
+ const effect = text(source.effect);
169
+ if (!["deny", "audit", "auditIfNotExists", "append", "modify", "deployIfNotExists", "disabled"].includes(effect))
170
+ fail();
171
+ const classification = text(source.classification);
172
+ if (!["blocker", "auto-remediate", "informational"].includes(classification))
173
+ fail();
174
+ function readExemption(value) {
175
+ const reported = record(value);
176
+ const category = text(reported.category);
177
+ if (category !== "Waiver" && category !== "Mitigated")
178
+ fail();
179
+ let provenance = {};
180
+ if (descendants || ["id", "scope", "expiresOn"].some((key) => Object.hasOwn(reported, key))) {
181
+ const exemptionId = text(reported.id);
182
+ const exemptionScope = scope(reported.scope, subscriptionId);
183
+ const assignmentScope = scope(source.scope, subscriptionId).toLowerCase();
184
+ if (descendants &&
185
+ !assignmentScope.startsWith("/providers/microsoft.management/managementgroups/") &&
186
+ exemptionScope.toLowerCase() !== assignmentScope &&
187
+ !exemptionScope.toLowerCase().startsWith(`${assignmentScope}/`))
188
+ fail("target-mismatch");
189
+ const suffix = exemptionId.slice(exemptionScope.length);
190
+ if (exemptionId.slice(0, exemptionScope.length).toLowerCase() !== exemptionScope.toLowerCase() ||
191
+ !POLICY_EXEMPTION_SUFFIX.test(suffix) ||
192
+ (!(descendants && exemptionScope.toLowerCase().startsWith(`/subscriptions/${subscriptionId}/`)) &&
193
+ exemptionScope.toLowerCase() !== `/subscriptions/${subscriptionId}` &&
194
+ (!/^\/providers\/microsoft\.management\/managementgroups\/[^/?#\s]+$/iu.test(exemptionScope) ||
195
+ exemptionScope.toLowerCase() !== text(source.scope).toLowerCase())))
196
+ fail("target-mismatch");
197
+ const expiresOn = reported.expiresOn === null ? null : text(reported.expiresOn);
198
+ if (expiresOn !== null)
199
+ timestamp(expiresOn);
200
+ provenance = { id: exemptionId, scope: exemptionScope, expiresOn };
201
+ }
202
+ return {
203
+ ...provenance,
204
+ category,
205
+ policyDefinitionReferenceIds: reported.policyDefinitionReferenceIds === null ? [] : strings(reported.policyDefinitionReferenceIds),
206
+ verificationStatus: "unverified",
207
+ };
208
+ }
209
+ const exemption = source.exemption === null ? null : readExemption(source.exemption);
210
+ const reportedExemptions = descendants ? array(source.reported_exemptions).map(readExemption) : undefined;
211
+ if (descendants &&
212
+ (exemption !== null ||
213
+ classification !==
214
+ (effect === "deny"
215
+ ? "blocker"
216
+ : ["modify", "deployIfNotExists"].includes(effect)
217
+ ? "auto-remediate"
218
+ : "informational")))
219
+ fail("incomplete");
220
+ if (reportedExemptions &&
221
+ new Set(reportedExemptions.map((item) => item.id.toLowerCase())).size !== reportedExemptions.length)
222
+ fail("incomplete");
223
+ const findingScope = scope(source.scope, subscriptionId);
224
+ const assignmentId = text(source.assignment_id);
225
+ if (assignmentId.slice(0, findingScope.length).toLowerCase() !== findingScope.toLowerCase() ||
226
+ !POLICY_ASSIGNMENT_SUFFIX.test(assignmentId.slice(findingScope.length)))
227
+ fail("target-mismatch");
228
+ return {
229
+ policyId: text(source.policy_id),
230
+ assignmentId,
231
+ ...(source.policyDefinitionReferenceId === undefined
232
+ ? {}
233
+ : { policyDefinitionReferenceId: text(source.policyDefinitionReferenceId) }),
234
+ ...(source.required_value === undefined ? {} : { requiredValue: boundedJson(source.required_value) }),
235
+ ...(source.assignment_parameters === undefined
236
+ ? {}
237
+ : {
238
+ effectiveParameters: boundedJson(record(source.assignment_parameters)),
239
+ }),
240
+ displayName: text(source.display_name),
241
+ scope: findingScope,
242
+ effect,
243
+ ...(mode === undefined ? {} : { enforcementMode: mode }),
244
+ classification,
245
+ resourceTypes: strings(source.resource_types),
246
+ mappingStatus: "unmapped",
247
+ exemption,
248
+ ...(reportedExemptions === undefined ? {} : { reportedExemptions }),
249
+ };
250
+ }
251
+ function selectEntry(value, subscriptionId, root, now, requireFresh) {
252
+ const entry = record(value);
253
+ if (entry.schema_version !== "governance-constraints-v1" || entry.source !== "github-actions-baseline")
254
+ fail();
255
+ const metadata = record(entry.discovery_metadata);
256
+ if (entry.discovery_status !== "COMPLETE" ||
257
+ metadata.discovery_status !== "COMPLETE" ||
258
+ "error" in entry ||
259
+ "errors" in entry ||
260
+ "error" in metadata ||
261
+ "errors" in metadata)
262
+ fail("incomplete");
263
+ const metadataScope = record(metadata.scope);
264
+ const descendants = metadataScope.coverage === "subscription-and-descendants-v1";
265
+ if (metadataScope.coverage !== undefined && !descendants)
266
+ fail("incomplete");
267
+ if (subscription(entry.subscription_id) !== subscriptionId ||
268
+ subscription(metadataScope.subscription_id) !== subscriptionId)
269
+ fail("target-mismatch");
270
+ const managementGroups = strings(metadataScope.management_groups);
271
+ const discoveredAt = timestamp(entry.discovered_at);
272
+ if (timestamp(metadata.discovered_at) !== discoveredAt)
273
+ fail("incomplete");
274
+ const ttlDays = count(metadata.ttl_days);
275
+ if (ttlDays < 1 || ttlDays > 90)
276
+ fail();
277
+ const expiresAt = discoveredAt + GOVERNANCE_MAX_AGE_MS;
278
+ if (now < discoveredAt || (requireFresh && now >= expiresAt))
279
+ fail("stale");
280
+ const signature = metadata.completeness_signature;
281
+ if (typeof signature !== "string" || (signature !== "" && !/^sha256:[0-9a-f]{64}$/u.test(signature)))
282
+ fail();
283
+ const apiVersions = record(metadata.api_versions);
284
+ const pageCounts = record(metadata.page_counts);
285
+ for (const endpoint of ["policyAssignments", "policyDefinitions", "policyExemptions"]) {
286
+ text(apiVersions[endpoint]);
287
+ count(pageCounts[endpoint]);
288
+ }
289
+ const rawFindings = array(entry.findings);
290
+ // governance-baseline-v1 duplicated findings as policies; v2 drops the copy and v1 files must be recollected.
291
+ if (Object.hasOwn(entry, "policies"))
292
+ fail();
293
+ const findings = rawFindings.map((item) => finding(item, subscriptionId, descendants));
294
+ const findingIdentities = findings.map((item) => canonical([item.assignmentId.toLowerCase(), item.policyId.toLowerCase(), item.policyDefinitionReferenceId ?? null]));
295
+ if (new Set(findingIdentities).size !== findings.length)
296
+ fail("incomplete");
297
+ const assignmentModes = new Map();
298
+ for (const item of findings) {
299
+ if (item.enforcementMode === undefined)
300
+ continue;
301
+ const mode = assignmentModes.get(item.assignmentId.toLowerCase());
302
+ if (mode !== undefined && mode !== item.enforcementMode)
303
+ fail("incomplete");
304
+ assignmentModes.set(item.assignmentId.toLowerCase(), item.enforcementMode);
305
+ }
306
+ findings.sort((left, right) => canonical(left) < canonical(right) ? -1 : canonical(left) > canonical(right) ? 1 : 0);
307
+ for (const item of findings) {
308
+ const inherited = /\/managementgroups\/([^/]+)$/iu.exec(item.scope)?.[1];
309
+ if (inherited && !managementGroups.some((group) => group.toLowerCase() === inherited.toLowerCase()))
310
+ fail("target-mismatch");
311
+ }
312
+ const inventory = array(entry.assignment_inventory).map((item) => {
313
+ const assignment = record(item);
314
+ enforcementMode(assignment);
315
+ const assignmentScope = scope(assignment.scope, subscriptionId);
316
+ const inherited = /\/managementgroups\/([^/]+)$/iu.exec(assignmentScope)?.[1];
317
+ if (inherited && !managementGroups.some((group) => group.toLowerCase() === inherited.toLowerCase()))
318
+ fail("target-mismatch");
319
+ text(assignment.displayName);
320
+ text(assignment.policyDefinitionId);
321
+ const expectedType = assignmentScope.toLowerCase().startsWith("/providers/") ? "management-group" : "subscription";
322
+ if (assignment.assignmentType !== expectedType)
323
+ fail("incomplete");
324
+ return expectedType;
325
+ });
326
+ if (descendants) {
327
+ const inventoryIds = array(entry.assignment_inventory).map((item) => {
328
+ const assignment = record(item);
329
+ const assignmentId = text(assignment.assignmentId).toLowerCase();
330
+ const assignmentScope = scope(assignment.scope, subscriptionId).toLowerCase();
331
+ if (!assignmentId.startsWith(assignmentScope) ||
332
+ !POLICY_ASSIGNMENT_SUFFIX.test(assignmentId.slice(assignmentScope.length)))
333
+ fail("target-mismatch");
334
+ return assignmentId;
335
+ });
336
+ const findingIds = new Set(findings.map((item) => item.assignmentId.toLowerCase()));
337
+ if (new Set(inventoryIds).size !== inventoryIds.length ||
338
+ inventoryIds.length !== findingIds.size ||
339
+ inventoryIds.some((id) => !findingIds.has(id)))
340
+ fail("incomplete");
341
+ }
342
+ const summary = record(entry.discovery_summary);
343
+ for (const key of [
344
+ "assignment_total",
345
+ "assignment_kept",
346
+ "defender_auto_filtered",
347
+ "subscription_scope_count",
348
+ "management_group_inherited_count",
349
+ "blocker_count",
350
+ "auto_remediate_count",
351
+ "informational_count",
352
+ "audit_count",
353
+ "disabled_count",
354
+ "exempted_count",
355
+ ])
356
+ count(summary[key]);
357
+ const notScopeExcluded = summary.not_scope_excluded === undefined ? 0 : count(summary.not_scope_excluded);
358
+ const findingAssignments = new Set(findings.map((item) => item.assignmentId.toLowerCase())).size;
359
+ const classifiedMembers = findings.length + (descendants ? 0 : Number(summary.audit_count) + Number(summary.disabled_count));
360
+ if (descendants &&
361
+ (count(summary.audit_count) !==
362
+ findings.filter((item) => item.effect === "audit" || item.effect === "auditIfNotExists").length ||
363
+ count(summary.disabled_count) !== findings.filter((item) => item.effect === "disabled").length ||
364
+ count(summary.classified_policy_count) !== findings.length ||
365
+ count(summary.other_effect_count) !== 0 ||
366
+ count(summary.defender_auto_filtered) !== 0 ||
367
+ findingAssignments !== inventory.length))
368
+ fail("incomplete");
369
+ if (findingAssignments > inventory.length ||
370
+ findingAssignments + Number(summary.audit_count) + Number(summary.disabled_count) < inventory.length ||
371
+ (inventory.length === 0 && classifiedMembers !== 0))
372
+ fail("incomplete");
373
+ if (summary.classification_coverage !== undefined) {
374
+ const coverage = record(summary.classification_coverage);
375
+ if (count(coverage.assignment_count) !== inventory.length ||
376
+ count(coverage.member_count) !== classifiedMembers ||
377
+ count(coverage.finding_count) !== findings.length ||
378
+ count(coverage.audit_count) !== summary.audit_count ||
379
+ count(coverage.disabled_count) !== summary.disabled_count ||
380
+ count(coverage.unclassified_count) !== 0)
381
+ fail("incomplete");
382
+ }
383
+ if (summary.assignment_kept !== inventory.length ||
384
+ summary.assignment_total !==
385
+ Number(summary.assignment_kept) + Number(summary.defender_auto_filtered) + notScopeExcluded ||
386
+ pageCounts.policyAssignments !== summary.assignment_total ||
387
+ summary.subscription_scope_count !== inventory.filter((kind) => kind === "subscription").length ||
388
+ summary.management_group_inherited_count !== inventory.filter((kind) => kind === "management-group").length ||
389
+ summary.blocker_count !== findings.filter((item) => item.classification === "blocker").length ||
390
+ summary.auto_remediate_count !== findings.filter((item) => item.classification === "auto-remediate").length ||
391
+ summary.informational_count !== findings.filter((item) => item.classification === "informational").length ||
392
+ summary.exempted_count !== findings.filter((item) => item.exemption !== null).length ||
393
+ (inventory.length === 0 && findings.length > 0))
394
+ fail("incomplete");
395
+ const allowedLocations = strings(entry.allowed_locations);
396
+ if (allowedLocations.length > 256 || array(entry.tags_required).length > 256)
397
+ fail();
398
+ const tagsRequired = array(entry.tags_required)
399
+ .map((tag) => {
400
+ const reported = record(tag);
401
+ const allowedValues = reported.allowed_values === undefined ? undefined : strings(reported.allowed_values);
402
+ if (allowedValues && allowedValues.length > 256)
403
+ fail();
404
+ return {
405
+ name: text(reported.name),
406
+ ...(allowedValues === undefined ? {} : { allowedValues }),
407
+ ...(reported.default === undefined ? {} : { default: text(reported.default) }),
408
+ };
409
+ })
410
+ .sort((left, right) => (canonical(left) < canonical(right) ? -1 : canonical(left) > canonical(right) ? 1 : 0));
411
+ return {
412
+ snapshot: {
413
+ schemaVersion: "governance-baseline-selection-v2",
414
+ subscriptionId,
415
+ targetScope: `/subscriptions/${subscriptionId}`,
416
+ contentHash: createHash("sha256")
417
+ .update(canonical({
418
+ root,
419
+ entry: Object.fromEntries(Object.entries(entry)
420
+ .filter(([key]) => key !== "discovered_at" && key !== "ttl_days")
421
+ .map(([key, value]) => [
422
+ key,
423
+ key === "discovery_metadata"
424
+ ? Object.fromEntries(Object.entries(metadata).filter(([field]) => field !== "discovered_at" && field !== "ttl_days"))
425
+ : value,
426
+ ])),
427
+ }))
428
+ .digest("hex"),
429
+ provenance: {
430
+ root,
431
+ source: "github-actions-baseline",
432
+ discoveryStatus: "COMPLETE",
433
+ managementGroups,
434
+ completenessSignature: signature,
435
+ signatureStatus: "unverified",
436
+ },
437
+ findings,
438
+ tagsRequired,
439
+ allowedLocations,
440
+ reconciliationRequired: true,
441
+ },
442
+ constraints: {
443
+ schemaVersion: "1.0.0",
444
+ targetScope: `/subscriptions/${subscriptionId}`,
445
+ discoveredAt: new Date(discoveredAt).toISOString(),
446
+ expiresAt: new Date(expiresAt).toISOString(),
447
+ summary: {
448
+ assignmentCount: count(summary.assignment_total),
449
+ denyCount: findings.filter((item) => item.effect === "deny").length,
450
+ modifyCount: findings.filter((item) => item.effect === "modify").length,
451
+ auditCount: count(summary.audit_count),
452
+ exemptionCount: count(pageCounts.policyExemptions),
453
+ },
454
+ },
455
+ };
456
+ }
457
+ function validateGovernanceBaseline(input, options, validateBaseline, requireFresh) {
458
+ let subscriptionId;
459
+ let now;
460
+ try {
461
+ subscriptionId = subscription(options.subscriptionId);
462
+ now = timestamp(options.now);
463
+ if (typeof validateBaseline !== "function")
464
+ fail();
465
+ }
466
+ catch {
467
+ return fail("invalid-options");
468
+ }
469
+ const baseline = record(jsonInput(input));
470
+ try {
471
+ if (validateBaseline(baseline) !== true)
472
+ fail();
473
+ }
474
+ catch {
475
+ return fail();
476
+ }
477
+ if (baseline.schema_version !== "governance-baseline-v2")
478
+ fail();
479
+ if (baseline.coverage_status !== "COMPLETE" || array(baseline.subscriptions_skipped).length !== 0)
480
+ fail("incomplete");
481
+ const hasManagementGroup = Object.hasOwn(baseline, "management_group_id");
482
+ if (hasManagementGroup === Object.hasOwn(baseline, "subscription_id"))
483
+ fail();
484
+ const root = hasManagementGroup
485
+ ? { kind: "management-group", managementGroupId: text(baseline.management_group_id) }
486
+ : { kind: "subscription", subscriptionId: subscription(baseline.subscription_id) };
487
+ if (root.kind === "subscription" && root.subscriptionId !== subscriptionId)
488
+ fail("target-mismatch");
489
+ const entries = Object.entries(record(baseline.subscriptions));
490
+ const excluded = array(baseline.subscriptions_excluded).map((item) => {
491
+ const exclusion = record(item);
492
+ if (exclusion.reason !== "disabled" && exclusion.reason !== "AAD_quota")
493
+ fail("incomplete");
494
+ return subscription(exclusion.subscription_id);
495
+ });
496
+ const ids = entries.map(([key]) => subscription(key));
497
+ if (new Set([...ids, ...excluded]).size !== ids.length + excluded.length || !ids.includes(subscriptionId))
498
+ fail("target-mismatch");
499
+ if (root.kind === "subscription" && (entries.length !== 1 || excluded.length !== 0))
500
+ fail("target-mismatch");
501
+ const summary = record(baseline.summary);
502
+ if (count(baseline.subscriptions_processed) !== entries.length ||
503
+ count(baseline.subscriptions_discovered) !== entries.length + excluded.length ||
504
+ count(summary.subscriptions_complete) !== entries.length)
505
+ fail("incomplete");
506
+ let selected;
507
+ let totalFindings = 0;
508
+ let totalBlockers = 0;
509
+ let totalAutoRemediate = 0;
510
+ for (const [key, entry] of entries) {
511
+ const currentId = subscription(key);
512
+ const result = selectEntry(entry, currentId, root, now, requireFresh);
513
+ totalFindings += result.snapshot.findings.length;
514
+ totalBlockers += result.snapshot.findings.filter((item) => item.classification === "blocker").length;
515
+ totalAutoRemediate += result.snapshot.findings.filter((item) => item.classification === "auto-remediate").length;
516
+ if (currentId === subscriptionId)
517
+ selected = result;
518
+ }
519
+ if (count(summary.total_findings) !== totalFindings ||
520
+ count(summary.total_blockers) !== totalBlockers ||
521
+ count(summary.total_auto_remediate) !== totalAutoRemediate)
522
+ fail("incomplete");
523
+ if (!selected)
524
+ fail("target-mismatch");
525
+ if (options.targetScope === undefined)
526
+ return selected;
527
+ const targetScope = text(options.targetScope).toLowerCase();
528
+ const subscriptionScope = `/subscriptions/${subscriptionId}`;
529
+ if (targetScope !== subscriptionScope &&
530
+ !new RegExp(`^${subscriptionScope}/resourcegroups/[^/?#\\s]+$`, "u").test(targetScope))
531
+ fail("invalid-options");
532
+ scope(targetScope, subscriptionId);
533
+ const selectedEntry = record(entries.find(([key]) => subscription(key) === subscriptionId)[1]);
534
+ const selectedMetadata = record(selectedEntry.discovery_metadata);
535
+ if (record(selectedMetadata.scope).coverage !== "subscription-and-descendants-v1")
536
+ fail("incomplete");
537
+ const applies = (value) => {
538
+ const candidate = scope(value, subscriptionId).toLowerCase();
539
+ return (candidate.startsWith("/providers/microsoft.management/managementgroups/") ||
540
+ candidate === targetScope ||
541
+ candidate.startsWith(`${targetScope}/`) ||
542
+ targetScope.startsWith(`${candidate}/`));
543
+ };
544
+ const findings = selected.snapshot.findings
545
+ .filter((item) => applies(item.scope))
546
+ .map((item) => ({
547
+ ...item,
548
+ reportedExemptions: (item.reportedExemptions ?? []).filter((exemption) => applies(exemption.scope)),
549
+ }));
550
+ return {
551
+ snapshot: {
552
+ ...selected.snapshot,
553
+ schemaVersion: "governance-baseline-selection-v2",
554
+ targetScope,
555
+ contentHash: createHash("sha256")
556
+ .update(canonical({ sourceHash: selected.snapshot.contentHash, targetScope }))
557
+ .digest("hex"),
558
+ findings,
559
+ tagsRequired: [],
560
+ allowedLocations: [],
561
+ },
562
+ constraints: {
563
+ ...selected.constraints,
564
+ targetScope,
565
+ summary: {
566
+ assignmentCount: array(selectedEntry.assignment_inventory).filter((item) => applies(record(item).scope)).length,
567
+ denyCount: findings.filter((item) => item.effect === "deny").length,
568
+ modifyCount: findings.filter((item) => item.effect === "modify").length,
569
+ auditCount: findings.filter((item) => item.effect === "audit" || item.effect === "auditIfNotExists").length,
570
+ exemptionCount: new Set(findings.flatMap((item) => (item.reportedExemptions ?? []).map((exemption) => exemption.id.toLowerCase()))).size,
571
+ },
572
+ },
573
+ };
574
+ }
575
+ export function importGovernanceBaseline(input, options, validateBaseline) {
576
+ return validateGovernanceBaseline(input, options, validateBaseline, true);
577
+ }
578
+ export function inspectGovernanceBaseline(input, options, validateBaseline) {
579
+ const selection = validateGovernanceBaseline(input, options, validateBaseline, false);
580
+ return {
581
+ observedAt: selection.constraints.discoveredAt,
582
+ refreshRequired: Date.parse(options.now) >= Date.parse(selection.constraints.expiresAt),
583
+ };
584
+ }
585
+ export function importGovernanceReference(input, options) {
586
+ const source = record(jsonInput(input));
587
+ if (source.schema_version !== "governance-reference-v1" || source.source !== "alz-corp-reference")
588
+ fail();
589
+ const library = record(source.library);
590
+ const release = text(library.release);
591
+ const commit = text(library.commit);
592
+ if (!/^[0-9a-f]{40}$/u.test(commit))
593
+ fail();
594
+ const now = timestamp(options.now);
595
+ const findings = array(source.policies).map((item) => finding(item, ""));
596
+ const identities = findings.map((item) => canonical([item.assignmentId.toLowerCase(), item.policyId.toLowerCase(), item.policyDefinitionReferenceId ?? null]));
597
+ if (findings.length === 0 || new Set(identities).size !== findings.length)
598
+ fail("incomplete");
599
+ const managementGroups = [...new Set(findings.map((item) => item.scope.split("/").at(-1).toLowerCase()))].sort();
600
+ const targetScope = text(options.targetScope);
601
+ const subscriptionId = /^\/subscriptions\/([0-9a-f-]{36})(?:\/|$)/iu.exec(targetScope)?.[1]?.toLowerCase() ?? "";
602
+ return {
603
+ snapshot: {
604
+ schemaVersion: "governance-baseline-selection-v2",
605
+ subscriptionId,
606
+ targetScope: targetScope.toLowerCase(),
607
+ contentHash: createHash("sha256")
608
+ .update(canonical({ release, commit, findings, targetScope: targetScope.toLowerCase() }))
609
+ .digest("hex"),
610
+ provenance: {
611
+ root: { kind: "management-group", managementGroupId: managementGroups[0] },
612
+ source: "alz-corp-reference",
613
+ reference: { release, commit },
614
+ discoveryStatus: "COMPLETE",
615
+ managementGroups,
616
+ completenessSignature: `alz:${release}@${commit}`,
617
+ signatureStatus: "unverified",
618
+ },
619
+ findings,
620
+ tagsRequired: [],
621
+ allowedLocations: [],
622
+ reconciliationRequired: true,
623
+ },
624
+ constraints: {
625
+ schemaVersion: "1.0.0",
626
+ targetScope,
627
+ discoveredAt: new Date(now).toISOString(),
628
+ expiresAt: new Date(now + GOVERNANCE_MAX_AGE_MS).toISOString(),
629
+ summary: {
630
+ assignmentCount: new Set(findings.map((item) => item.assignmentId.toLowerCase())).size,
631
+ denyCount: findings.filter((item) => item.effect === "deny").length,
632
+ modifyCount: findings.filter((item) => item.effect === "modify").length,
633
+ auditCount: findings.filter((item) => item.effect === "audit" || item.effect === "auditIfNotExists").length,
634
+ exemptionCount: 0,
635
+ },
636
+ },
637
+ };
638
+ }
639
+ //# sourceMappingURL=governance-baseline.js.map