@aws/cloudformation-validate 1.9.0-beta → 1.10.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/README.md CHANGED
@@ -1,10 +1,15 @@
1
1
  # CloudFormation Validate for Node.js
2
2
 
3
- Validate AWS CloudFormation templates from JavaScript or TypeScript and catch schema violations, security risks, and
4
- best-practice findings before deployment - in your editor, build, or CI.
3
+ Validate AWS CloudFormation templates from JavaScript or TypeScript and catch schema violations, semantic errors,
4
+ security risks, and best-practice findings before deployment - in your editor, build, service, or CI.
5
5
 
6
- - **Offline** - all rules and resource schemas are bundled.
7
- - **Fast** - sub-second validation per template.
6
+ - **Offline** - all rules and CloudFormation resource schemas are bundled; nothing is fetched at runtime and no AWS
7
+ credentials are needed.
8
+ - **Fast** - engines and schemas compile once and are reused across validations; typical templates validate in under a
9
+ second.
10
+ - **Self-contained** - the WebAssembly module is bundled in the package; there are no native dependencies.
11
+
12
+ All types are exported from the `@aws/cloudformation-validate` package.
8
13
 
9
14
  ## Installation
10
15
 
@@ -14,6 +19,8 @@ Available on [npm](https://www.npmjs.com/package/@aws/cloudformation-validate) a
14
19
  npm install @aws/cloudformation-validate
15
20
  ```
16
21
 
22
+ Requires Node.js 20 or later. The package has no runtime dependencies.
23
+
17
24
  ## Quick start
18
25
 
19
26
  Engines, models, and validators hold off-heap memory - call `.free()` when done with each object:
@@ -23,7 +30,7 @@ import { RegoEngine, TemplateFile } from "@aws/cloudformation-validate";
23
30
 
24
31
  const engine = new RegoEngine();
25
32
  try {
26
- const report = engine.validateStandard(new TemplateFile("template.yaml"));
33
+ const report = engine.validateTemplate(new TemplateFile("template.yaml"));
27
34
  for (const d of report.diagnostics) {
28
35
  console.log(`[${d.severity}] ${d.ruleId}: ${d.message}`);
29
36
  }
@@ -32,35 +39,54 @@ try {
32
39
  }
33
40
  ```
34
41
 
35
- Each diagnostic identifies the rule, severity, affected resource and property, and source location - see
36
- [StandardDiagnostic](#standarddiagnostic). A complete, runnable project is in
42
+ Each diagnostic identifies the rule, severity, affected entity and property, and source location - see
43
+ [Diagnostic](#diagnostic). A complete, runnable project is in
37
44
  [examples](https://github.com/aws-cloudformation/cloudformation-validate/tree/main/src/bindings-wasm/examples).
38
45
 
46
+ Engines are expensive to construct (rules compile once) and cheap to reuse - create one engine and validate many
47
+ templates. Every fallible call throws on failure - the thrown value is the error message string from the validation
48
+ core, so handle it with `catch (error) { String(error) }`; internal panics are caught at the WASM boundary and surface
49
+ the same way, never a process abort. `version()` returns the version of the bundled validation core.
50
+
51
+ A template is passed as a `TemplateFile`, which wraps a filesystem path: the engine reads the bytes and uses the path
52
+ for diagnostic source locations.
53
+
39
54
  ## Engine
40
55
 
41
56
  `RegoEngine` and `CelEngine` both implement the `Engine` interface and are interchangeable - they produce identical
42
- diagnostics for the same template and config.
57
+ diagnostics for the same template and config. `CompositeEngine` implements the same interface and layers custom Rego,
58
+ CEL, and Guard rules on top of the built-in rules - see [CompositeEngine](#compositeengine).
43
59
 
44
60
  ### `Engine` interface
45
61
 
46
- | Method | Returns | Description |
47
- |---------------------------------------|------------------|------------------------------------------------------------------------------------------------------------------|
48
- | `validateStandard(template, config?)` | `StandardReport` | Validates and returns diagnostics without extended context |
49
- | `validateDetailed(template, config?)` | `DetailedReport` | Validates and returns diagnostics with documentation URLs, rule descriptions, phase tags, and `ViolationContext` |
50
- | `listRules()` | `RuleInfo[]` | Returns metadata for every built-in and loaded custom rule |
51
- | `engineName()` | `string` | `"rego"` or `"cel"` |
52
- | `free()` | `void` | Releases the engine's off-heap memory |
62
+ ```typescript
63
+ interface Engine {
64
+ validateTemplate(template: TemplateFile, config?: ValidateConfig): ValidationReport;
65
+ validateAwsCliCommand(request: AwsCliCommand): AwsCliCommandValidation;
66
+ listRules(): RuleInfo[];
67
+ engineName(): string;
68
+ free(): void;
69
+ }
70
+ ```
71
+
72
+ | Method | Returns | Description |
73
+ |---------------------------------------|---------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
74
+ | `validateTemplate(template, config?)` | `ValidationReport` | Validates the template and returns a report. `config.detailLevel` (default `DETAILED`) selects how much per-diagnostic context is populated: `DETAILED` adds documentation URLs, rule descriptions, phase tags, and `ViolationContext`; `STANDARD` leaves those enrichment fields absent |
75
+ | `validateAwsCliCommand(request)` | `AwsCliCommandValidation` | Models an AWS CLI command as CloudFormation resource state and validates it - see [AWS CLI command validation](#aws-cli-command-validation) |
76
+ | `listRules()` | `RuleInfo[]` | Returns metadata for every built-in and loaded custom rule |
77
+ | `engineName()` | `string` | `"rego"`, `"cel"`, or `"composite"` |
78
+ | `free()` | `void` | Releases the engine's off-heap memory; the engine must not be used afterwards |
53
79
 
54
80
  ### `EngineConfig`
55
81
 
56
- Passed to the constructor. All fields are optional; omitted rule arrays are empty and an omitted
82
+ Passed to the constructor. All fields are optional: the rule lists default to empty and an omitted
57
83
  `schemaValidatorConfig` uses only the bundled schemas.
58
84
 
59
85
  ```typescript
60
86
  interface EngineConfig {
61
- customRules?: RuleSource[]; // engine-native rules (Rego for RegoEngine, CEL for CelEngine)
62
- guardRules?: RuleSource[]; // CloudFormation Guard DSL rules - translated internally
63
- schemaValidatorConfig?: SchemaValidatorConfig; // schema validation and overlay configuration
87
+ customRules?: RuleSource[]; // engine-native rules (Rego for RegoEngine, CEL for CelEngine)
88
+ guardRules?: RuleSource[]; // CloudFormation Guard DSL rules - evaluated by the Guard evaluator
89
+ schemaValidatorConfig?: SchemaValidatorConfig; // additional resource provider schemas
64
90
  }
65
91
 
66
92
  interface SchemaValidatorConfig {
@@ -70,14 +96,6 @@ interface SchemaValidatorConfig {
70
96
  type RuleSource = ExternalRuleSource | RuleFile;
71
97
  type SchemaSource = AdditionalSchemaSource | SchemaFile;
72
98
 
73
- class RuleFile {
74
- constructor(path: string); // rule file read from disk; the path becomes the rule source name
75
- }
76
-
77
- class SchemaFile {
78
- constructor(path: string, typeName?: string); // schema file; typeName defaults to the value inside the JSON
79
- }
80
-
81
99
  interface ExternalRuleSource {
82
100
  name: string; // identifier shown in diagnostics (e.g. file path)
83
101
  content: string; // full rule source text
@@ -87,17 +105,35 @@ interface AdditionalSchemaSource {
87
105
  typeName?: string; // omit to use the typeName inside the schema JSON
88
106
  schema: string; // complete resource provider schema JSON
89
107
  }
90
- ```
91
108
 
92
- Pass a `RuleFile` to load a rule from disk - the same pattern as `TemplateFile` for templates - or an
93
- `ExternalRuleSource` when you already have the rule text in memory. `SchemaFile` does the same for an additional
94
- resource provider schema. Its optional constructor `typeName` may be omitted when the schema JSON contains its own
95
- `typeName`.
109
+ class RuleFile {
110
+ constructor(path: string); // rule file read from disk; the path becomes the rule source name
111
+ }
96
112
 
97
- The generated `AdditionalSchemaSource` record exposes `typeName` as an optional field. Omit it (or leave the
98
- `SchemaFile` constructor argument unset) for an in-memory schema whose JSON already contains its own `typeName`.
113
+ class SchemaFile {
114
+ constructor(path: string, typeName?: string); // schema file; typeName defaults to the value inside the JSON
115
+ }
116
+ ```
117
+
118
+ | Field | Default | Description |
119
+ |-------------------------|-------------|-----------------------------------------------------------------------------------------------|
120
+ | `customRules` | `[]` | Engine-native rules: Rego source for `RegoEngine`, CEL JSON for `CelEngine` |
121
+ | `guardRules` | `[]` | CloudFormation Guard DSL rules, evaluated by the Guard evaluator identically in every engine |
122
+ | `schemaValidatorConfig` | `undefined` | Optional `SchemaValidatorConfig` whose `additionalSchemas` are merged over the bundled schemas |
123
+
124
+ Each rule is an `ExternalRuleSource` - `name` identifies the rule in diagnostics and `content` is the full rule source
125
+ text. Pass a `RuleFile` to load one from disk (the same pattern as `TemplateFile` for templates), or an
126
+ `ExternalRuleSource` when you already have the rule text in memory. Each additional schema is an
127
+ `AdditionalSchemaSource` - a complete resource provider schema JSON plus an optional `typeName` that may be omitted when
128
+ the schema JSON contains its own `typeName`; `SchemaFile` loads one from disk. Additional schemas extend the bundled
129
+ schemas or register resource types CloudFormation has not published yet; a malformed, contradictory, or unsupported
130
+ schema fails engine construction rather than silently weakening validation. Guard rules are evaluated by the
131
+ CloudFormation Guard evaluator itself against the template as written, so every engine reports exactly what `cfn-guard
132
+ validate` reports; a Guard file that does not parse also fails engine construction. The two forms can be mixed freely:
99
133
 
100
134
  ```typescript
135
+ import { CelEngine, RuleFile, SchemaFile } from "@aws/cloudformation-validate";
136
+
101
137
  const engine = new CelEngine({
102
138
  customRules: [new RuleFile("rules/s3_encryption.json")],
103
139
  guardRules: [new RuleFile("rules/compliance.guard")],
@@ -107,15 +143,66 @@ const engine = new CelEngine({
107
143
  });
108
144
  ```
109
145
 
146
+ See [Custom Rules](../CUSTOM_RULES.md) for the Rego, CEL, and Guard rule formats and
147
+ [Additional Resource Provider Schemas](../validation-engine/API.md#additional-resource-provider-schemas) for the schema
148
+ merge model.
149
+
150
+ ### `CompositeEngine`
151
+
152
+ `CompositeEngine` implements the same `Engine` interface but takes a `CompositeEngineConfig`. It evaluates every
153
+ built-in rule with a fixed built-in CEL evaluator and layers the caller-supplied custom rules on top: custom CEL and
154
+ Guard rules run alongside that built-in engine, while custom Rego rules run in a separate external engine that is
155
+ constructed only when Rego rules are supplied. With no custom rules it produces the same built-in diagnostics as
156
+ `RegoEngine` and `CelEngine`, and `engineName()` returns `"composite"`. Because the composite fixes which engine owns
157
+ the built-ins, the config has no `customRules` field - it carries only the custom rules layered on top:
158
+
159
+ ```typescript
160
+ interface CompositeEngineConfig {
161
+ regoRules?: RuleSource[]; // custom Rego rules, run by the external engine
162
+ celRules?: RuleSource[]; // custom CEL rules, run by the built-in engine
163
+ guardRules?: RuleSource[]; // CloudFormation Guard DSL rules, evaluated alongside the built-in engine
164
+ schemaValidatorConfig?: SchemaValidatorConfig; // additional resource provider schemas, observed by both inner engines
165
+ }
166
+ ```
167
+
168
+ | Field | Default | Description |
169
+ |-------------------------|-------------|--------------------------------------------------------------------------------------------------|
170
+ | `regoRules` | `[]` | Custom Rego rules layered on top of the built-in rules, run by the external engine |
171
+ | `celRules` | `[]` | Custom CEL rules layered on top of the built-in rules, run by the built-in engine |
172
+ | `guardRules` | `[]` | CloudFormation Guard DSL rules layered on top of the built-in rules, evaluated alongside the built-in engine |
173
+ | `schemaValidatorConfig` | `undefined` | Optional `SchemaValidatorConfig`, observed by both inner engines |
174
+
175
+ ```typescript
176
+ import { CompositeEngine, RuleFile, TemplateFile } from "@aws/cloudformation-validate";
177
+
178
+ const engine = new CompositeEngine({
179
+ regoRules: [new RuleFile("rules/s3_naming.rego")],
180
+ guardRules: [new RuleFile("rules/compliance.guard")],
181
+ });
182
+ try {
183
+ const report = engine.validateTemplate(new TemplateFile("template.yaml"));
184
+ } finally {
185
+ engine.free();
186
+ }
187
+ ```
188
+
110
189
  ## ValidateConfig
111
190
 
112
- Controls filtering, severity, parameter overrides, and behavior. All fields optional - omitting the config or passing
113
- `{}` uses defaults.
191
+ Controls filtering, detail, severity, parameter overrides, and behavior for one validation call. All fields have
192
+ defaults - omitting the config or passing `{}` uses them.
193
+
194
+ ```typescript
195
+ const report = engine.validateTemplate(new TemplateFile("template.yaml"), {
196
+ exclude: { ids: ["I1002"] },
197
+ severityLevel: "WARN",
198
+ });
199
+ ```
114
200
 
115
201
  ```typescript
116
202
  interface ValidateConfig {
117
203
  include?: RuleFilterConfig;
118
204
  exclude?: RuleFilterConfig;
205
+ detailLevel?: DetailLevel;
119
206
  severityLevel?: Severity;
120
207
  parameterOverrides?: Record<string, string>;
121
208
  pseudoParameterOverrides?: PseudoParameterOverrides;
@@ -124,15 +211,16 @@ interface ValidateConfig {
124
211
  }
125
212
  ```
126
213
 
127
- | Field | Default | Description |
128
- |----------------------------|-------------------------|--------------------------------------------------------------------------------------------------------------------------|
129
- | `include` | `{}` (all rules) | When set, only matching rules produce diagnostics. Empty means include everything. |
130
- | `exclude` | `{}` (nothing excluded) | Matching rules are suppressed. Applied after `include`. |
131
- | `severityLevel` | `"INFO"` | Minimum severity threshold. Diagnostics below this level are dropped. Values: `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`. |
132
- | `parameterOverrides` | `{}` | Override template parameter values during resolution. Keys are parameter logical IDs. |
133
- | `pseudoParameterOverrides` | all `undefined` | Override CloudFormation pseudo-parameters (`AWS::AccountId`, `AWS::Region`, etc.). |
134
- | `strict` | `false` | When `true`, `WARN`-severity diagnostics are upgraded to `ERROR`. |
135
- | `disableBuiltinRules` | `false` | When `true`, all built-in rules (schema validation, Step Functions, engine rules) are skipped; only custom and Guard rules are evaluated. |
214
+ | Field | Default | Description |
215
+ |----------------------------|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
216
+ | `include` | `{}` (all rules) | When set, only matching rules produce diagnostics. Empty means include everything. |
217
+ | `exclude` | `{}` (nothing excluded) | Matching rules are suppressed. Applied after `include`. |
218
+ | `detailLevel` | `"DETAILED"` | Per-diagnostic context. `"DETAILED"` populates documentation URLs, rule descriptions, phase tags, and `ViolationContext`; `"STANDARD"` leaves those enrichment fields absent. |
219
+ | `severityLevel` | `"INFO"` | Minimum severity threshold. Diagnostics below this level are dropped. Values: `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`. |
220
+ | `parameterOverrides` | `{}` | Override template parameter values during resolution. Keys are parameter logical IDs. |
221
+ | `pseudoParameterOverrides` | all `undefined` | Override CloudFormation pseudo-parameters (`AWS::AccountId`, `AWS::Region`, etc.). |
222
+ | `strict` | `false` | When `true`, `WARN`-severity diagnostics are upgraded to `ERROR`. |
223
+ | `disableBuiltinRules` | `false` | When `true`, all built-in rules (schema validation, Step Functions, engine rules) are skipped; only custom and Guard rules are evaluated. |
136
224
 
137
225
  ### RuleFilterConfig
138
226
 
@@ -140,14 +228,14 @@ Both `include` and `exclude` use this structure. All fields are additive - a rul
140
228
 
141
229
  ```typescript
142
230
  interface RuleFilterConfig {
143
- ids?: string[]; // exact rule IDs, e.g. ["E3012", "W3010"]
144
- categories?: string[]; // category names, e.g. ["security", "best_practices"]
145
- idRanges?: IdRange[]; // numeric ranges, e.g. { prefix: "E", start: 3000, end: 3099 }
146
- idPatterns?: string[]; // regex patterns matched against rule IDs
147
- resourceIds?: ResourceIdFilter[]; // a rule (or every rule) on a logical resource ID
148
- logicalIds?: LogicalIdFilter[]; // a rule (or every rule) on a named template entity
231
+ ids?: string[]; // exact rule IDs, e.g. ["E3012", "W3010"]
232
+ categories?: string[]; // category names, e.g. ["security", "best_practices"]
233
+ idRanges?: IdRange[]; // numeric ranges, e.g. { prefix: "E", start: 3000, end: 3099 }
234
+ idPatterns?: string[]; // regex patterns matched against rule IDs
235
+ resourceIds?: ResourceIdFilter[]; // a rule (or every rule) on a logical resource ID
236
+ logicalIds?: LogicalIdFilter[]; // a rule (or every rule) on a named template entity
149
237
  resourceTypes?: ResourceTypeFilter[]; // a rule (or every rule) on a resource type
150
- services?: ServiceFilter[]; // a rule (or every rule) on a service, e.g. "AWS::AutoScaling"
238
+ services?: ServiceFilter[]; // a rule (or every rule) on a service, e.g. "AWS::AutoScaling"
151
239
  }
152
240
 
153
241
  // resourceIds / logicalIds / resourceTypes / services each carry an optional ruleId:
@@ -168,7 +256,7 @@ the same value). An optional `entityType` scopes a `LogicalIdFilter` to entities
168
256
 
169
257
  ### PseudoParameterOverrides
170
258
 
171
- Override CloudFormation pseudo-parameters used during intrinsic function resolution. All fields optional - when
259
+ Override CloudFormation pseudo-parameters used during intrinsic function resolution. All fields are optional - when
172
260
  `undefined`, the engine uses built-in defaults (e.g. region defaults to `us-east-1`).
173
261
 
174
262
  ```typescript
@@ -183,14 +271,6 @@ interface PseudoParameterOverrides {
183
271
  }
184
272
  ```
185
273
 
186
- ## TemplateFile
187
-
188
- Wraps a filesystem path. Engines read the file bytes internally.
189
-
190
- ```typescript
191
- const template = new TemplateFile("path/to/template.yaml");
192
- ```
193
-
194
274
  ## TemplateModel
195
275
 
196
276
  Parses a template into the resolved `SemanticModel` for direct inspection - the same model the engines evaluate rules
@@ -207,58 +287,135 @@ const model = new TemplateModel(new TemplateFile("template.yaml"));
207
287
  | `outputs()` | `Record<string, ResolvedOutput>` | Outputs with resolved values and export names |
208
288
  | `conditions()` | `string[]` | Condition names defined in the template |
209
289
  | `transforms()` | `string[]` | Transform declarations (e.g. `AWS::Serverless-2016-10-31`) |
210
- | `formatVersion()` | `string \ undefined` | `AWSTemplateFormatVersion` value |
211
- | `description()` | `string \ undefined` | Template description |
290
+ | `formatVersion()` | `string \| undefined` | `AWSTemplateFormatVersion` value |
291
+ | `description()` | `string \| undefined` | Template description |
212
292
  | `toDiagnosticModel()` | `DiagnosticModel` | Full diagnostic model including reference graph, condition implications, and resolution sources |
213
- | `sourceLocation(path)` | `SourceSpan \ null` | Source line/column span for a JSON path (e.g. `Resources/MyBucket/Properties/BucketName`) |
214
- | `free()` | `void` | Releases WASM memory |
293
+ | `sourceLocation(path)` | `SourceSpan \| null` | Source line/column span for a JSON path (e.g. `Resources/MyBucket/Properties/BucketName`) |
294
+ | `free()` | `void` | Releases the model's off-heap memory; the model must not be used afterwards |
215
295
 
216
296
  ## SchemaValidator
217
297
 
218
- Runs schema validation independently from the rule engines. Checks each resource against compiled CloudFormation
219
- provider schemas and produces `FATAL`-severity diagnostics for structural violations.
298
+ Runs schema validation independently from the rule engines. Checks each resource against the compiled CloudFormation
299
+ provider schemas and produces `FATAL`-severity diagnostics for structural violations. The optional constructor argument
300
+ is the same `SchemaValidatorConfig` accepted by `EngineConfig`; omitting it uses only the bundled schemas.
220
301
 
221
302
  ```typescript
222
303
  const validator = new SchemaValidator();
223
- const diagnostics = validator.validate(new TemplateFile("template.yaml"), "us-east-1");
224
- validator.free();
304
+ try {
305
+ const diagnostics = validator.validate(new TemplateFile("template.yaml"), "us-east-1");
306
+ } finally {
307
+ validator.free();
308
+ }
225
309
  ```
226
310
 
227
- | Method | Returns | Description |
228
- |-------------------------------|------------------------|---------------------------------------------------------|
229
- | `validate(template, region?)` | `StandardDiagnostic[]` | Schema diagnostics. `region` defaults to `"us-east-1"`. |
230
- | `listRules()` | `RuleInfo[]` | Schema rule metadata |
231
- | `schemaCount()` | `number` | Number of compiled provider schemas |
232
- | `free()` | `void` | Releases WASM memory |
311
+ | Method | Returns | Description |
312
+ |--------------------------------|-------------------|------------------------------------------------------------------------------------------------------------------|
313
+ | `new SchemaValidator(config?)` | `SchemaValidator` | Constructs a validator; an omitted `SchemaValidatorConfig` uses only the bundled schemas |
314
+ | `validate(template, region?)` | `Diagnostic[]` | Schema diagnostics at `STANDARD` detail - the enrichment fields are absent. `region` defaults to `"us-east-1"`. |
315
+ | `listRules()` | `RuleInfo[]` | Schema rule metadata |
316
+ | `schemaCount()` | `number` | Number of compiled provider schemas |
317
+ | `free()` | `void` | Releases the validator's off-heap memory; the validator must not be used afterwards |
318
+
319
+ ## AWS CLI command validation
320
+
321
+ `validateAwsCliCommand` models an AWS CLI (or SDK) API call as CloudFormation resource state and validates it offline
322
+ before it is sent. It classifies the operation, maps it to a CloudFormation resource type through a closed, generated
323
+ adapter catalog, synthesizes a template from the supplied parameters, and runs the normal template pipeline on it. A
324
+ `TemplateBody` parameter of a CloudFormation operation is validated as-is. Any request that cannot be modeled exactly -
325
+ an unregistered operation, a parameter without a lossless property mapping, or a value outside a CloudFormation
326
+ constraint the API itself does not enforce - is skipped with a reason, never guessed.
327
+
328
+ ```typescript
329
+ import { AwsCliCommand, RegoEngine } from "@aws/cloudformation-validate";
330
+
331
+ const engine = new RegoEngine();
332
+ try {
333
+ const request = new AwsCliCommand("s3", "CreateBucket", { Bucket: "example-bucket" });
334
+ const validation = engine.validateAwsCliCommand(request);
335
+ if (validation.status === "VALIDATED") {
336
+ for (const d of validation.report!.diagnostics) {
337
+ console.log(`[${d.severity}] ${d.ruleId}: ${d.message}`);
338
+ }
339
+ } else {
340
+ console.log(`skipped (${validation.operationKind}): ${validation.reason}`);
341
+ }
342
+ } finally {
343
+ engine.free();
344
+ }
345
+ ```
346
+
347
+ ```typescript
348
+ class AwsCliCommand {
349
+ constructor(
350
+ serviceName: string, // canonical botocore service name, e.g. "s3" or "cloudformation"
351
+ operationName: string, // API operation name, e.g. "CreateBucket"
352
+ parameters: Record<string, unknown>, // request parameters (a plain object with string keys)
353
+ options?: AwsCliCommandOptions,
354
+ );
355
+ }
356
+
357
+ interface AwsCliCommandOptions {
358
+ servicePrefix?: string; // signing prefix; context only
359
+ httpMethod?: string; // classification hint ("GET"/"HEAD"/"DELETE") for unrecognized verbs
360
+ isReadOnly?: boolean; // true classifies the operation as READ_ONLY
361
+ }
362
+ ```
363
+
364
+ - `serviceName` is matched case-insensitively. Signing names, endpoint aliases, and ARN prefixes are never resolved;
365
+ translate an SDK's service identity first.
366
+ - `parameters` accepts nested plain objects and arrays, strings, numbers, `bigint`, booleans, `null`, `Uint8Array`,
367
+ and `Date` (serialized as ISO 8601). Any other value is carried as an explicit unsupported marker, and because
368
+ synthesis is all-or-nothing the request is then skipped with a reason naming the offending parameter - no parameter
369
+ is ever silently dropped.
370
+
371
+ The result is an `AwsCliCommandValidation`:
372
+
373
+ | Field | Description |
374
+ |------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
375
+ | `operationKind` | `AwsCliOperationKind`: `"READ_ONLY"`, `"CLOUD_FORMATION_CREATE"`, `"CLOUD_FORMATION_UPDATE"`, `"CLOUD_FORMATION_DELETE"`, `"DATA_PLANE_MUTATION"`, or `"UNMAPPED_MUTATION"` |
376
+ | `status` | `AwsCliCommandValidationStatus`: `"VALIDATED"` when the modeled template ran through the pipeline, `"SKIPPED"` otherwise |
377
+ | `templateSource` | `AwsCliTemplateSource \| null`: `"TEMPLATE_BODY"`, `"CLOUD_CONTROL_DESIRED_STATE"`, `"SYNTHESIZED_CREATE"`, or `"SYNTHESIZED_UPDATE"`; `null` when skipped |
378
+ | `resourceTypes` | `string[]` - CloudFormation resource types the operation maps to |
379
+ | `reason` | `string` - why the request was validated or skipped |
380
+ | `report` | `ValidationReport \| null` - present when `"VALIDATED"`. The configuration is fixed: `STANDARD` detail level and a `WARN` severity floor |
381
+ | `template` | `Uint8Array \| null` - the exact template bytes that were validated (the caller's `TemplateBody` unchanged, or the synthesized JSON); `null` when skipped |
382
+
383
+ The full contract - the adapter catalog, all-or-nothing mapping, and which rules are dropped for synthesized state -
384
+ is documented in [validation-engine/API.md](../validation-engine/API.md#validating-an-aws-cli-command).
233
385
 
234
386
  ## Report Types
235
387
 
236
- ### StandardReport / DetailedReport
388
+ ### ValidationReport
389
+
390
+ `validateTemplate` always returns a `ValidationReport` - a template syntax failure is returned as a report with
391
+ status `"ERROR"` and an `F1101` diagnostic; only infrastructure or engine failures throw:
237
392
 
238
393
  ```typescript
239
- interface StandardReport {
394
+ interface ValidationReport {
240
395
  filePath: string;
241
396
  status: "OK" | "ANALYSIS_INCOMPLETE" | "ERROR"; // ERROR is a pipeline failure; ANALYSIS_INCOMPLETE may omit findings
242
397
  version: string;
243
398
  metadata: ReportMetadata;
244
399
  performance: PerformanceMetrics;
245
- diagnostics: StandardDiagnostic[];
400
+ diagnostics: Diagnostic[];
246
401
  }
247
402
  ```
248
403
 
249
- `DetailedReport` has the same structure but its diagnostics include additional fields: `documentationUrl`,
250
- `ruleDescription`, `phase` (`PARSE` | `SCHEMA` | `LINT`), and `context` (`ViolationContext` with
251
- `actualValue`, `expectedConstraint`, `resolutionSource`, etc.).
404
+ Every finding is a `Diagnostic` (see [Diagnostic](#diagnostic)). Its enrichment fields - `documentationUrl`,
405
+ `ruleDescription`, `phase` (`PARSE` | `SCHEMA` | `LINT`), and `context` (`ViolationContext` with `actualValue`,
406
+ `expectedConstraint`, `resolutionSource`, etc.) - are populated only at `detailLevel` `"DETAILED"` (the default);
407
+ validating at `"STANDARD"` leaves them absent, keeping the base diagnostic fields.
252
408
 
253
- Each optional budget-exhaustion record retains a stable machine-readable kind and also includes a
254
- human-readable description sentence, the numeric limit, and whether that specific exhaustion makes analysis
255
- incomplete. `requiredPropertyCombinations` is context-only, so its `analysisIncomplete` value is `false` and the
256
- report can remain `"OK"`.
409
+ `metadata` carries the summary counts, the number of suppressed diagnostics, the resources scanned and rules
410
+ evaluated, the strict flag and severity threshold used, and optional budget-exhaustion records. Each budget-exhaustion
411
+ record retains a stable machine-readable kind and also includes a human-readable description sentence, the numeric
412
+ limit, and whether that specific exhaustion makes analysis incomplete. `requiredPropertyCombinations` is context-only,
413
+ so its `analysisIncomplete` value is `false` and the report can remain `"OK"`.
257
414
 
258
- ### StandardDiagnostic
415
+ ### Diagnostic
259
416
 
260
417
  ```typescript
261
- interface StandardDiagnostic {
418
+ interface Diagnostic {
262
419
  ruleId: string; // e.g. "E3012", "F1001", "W3010"
263
420
  severity: Severity; // "FATAL" | "ERROR" | "WARN" | "INFO" | "DEBUG"
264
421
  message: string;
@@ -273,6 +430,11 @@ interface StandardDiagnostic {
273
430
  endColumn?: number;
274
431
  relatedResources?: RelatedResource[];
275
432
  conditionScenario?: Record<string, boolean>; // condition truth assignment that triggers this diagnostic
433
+ // Enrichment fields: populated at detailLevel "DETAILED" (the default), absent at "STANDARD".
434
+ documentationUrl?: string;
435
+ ruleDescription?: string;
436
+ phase?: "PARSE" | "SCHEMA" | "LINT"; // pipeline stage that produced the finding
437
+ context?: ViolationContext; // actualValue, expectedConstraint, resolutionSource, etc.
276
438
  }
277
439
 
278
440
  // The named template entity a diagnostic is attributed to. The entity type is the
@@ -286,3 +448,6 @@ interface Entity {
286
448
  type EntityType = "Resource" | "Parameter" | "Output" | "Mapping" | "Metadata"
287
449
  | "Rule" | "Condition" | "Transform" | "FormatVersion" | "Description";
288
450
  ```
451
+
452
+ `Severity`, `RuleOrigin`, and `DetailLevel` are string-literal union types (`"WARN"`, `"GUARD"`, `"STANDARD"`, ...), as
453
+ is the report `status` (`"OK"`, `"ANALYSIS_INCOMPLETE"`, `"ERROR"`).
@@ -2380,6 +2380,85 @@ See the License for the specific language governing permissions and
2380
2380
  limitations under the License.
2381
2381
 
2382
2382
 
2383
+ ******************************
2384
+
2385
+ cloudformation-validate-composite-engine
2386
+ 1.10.0 <https://github.com/aws-cloudformation/cloudformation-validate>
2387
+ Apache License
2388
+ Version 2.0, January 2004
2389
+ http://www.apache.org/licenses/
2390
+
2391
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
2392
+
2393
+ 1. Definitions.
2394
+
2395
+ "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
2396
+
2397
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
2398
+
2399
+ "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
2400
+
2401
+ "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
2402
+
2403
+ "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
2404
+
2405
+ "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
2406
+
2407
+ "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
2408
+
2409
+ "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
2410
+
2411
+ "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
2412
+
2413
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2414
+
2415
+ 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
2416
+
2417
+ 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
2418
+
2419
+ 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
2420
+
2421
+ (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
2422
+
2423
+ (b) You must cause any modified files to carry prominent notices stating that You changed the files; and
2424
+
2425
+ (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
2426
+
2427
+ (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
2428
+
2429
+ You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
2430
+
2431
+ 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
2432
+
2433
+ 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
2434
+
2435
+ 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
2436
+
2437
+ 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
2438
+
2439
+ 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
2440
+
2441
+ END OF TERMS AND CONDITIONS
2442
+
2443
+ APPENDIX: How to apply the Apache License to your work.
2444
+
2445
+ To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
2446
+
2447
+ Copyright [yyyy] [name of copyright owner]
2448
+
2449
+ Licensed under the Apache License, Version 2.0 (the "License");
2450
+ you may not use this file except in compliance with the License.
2451
+ You may obtain a copy of the License at
2452
+
2453
+ http://www.apache.org/licenses/LICENSE-2.0
2454
+
2455
+ Unless required by applicable law or agreed to in writing, software
2456
+ distributed under the License is distributed on an "AS IS" BASIS,
2457
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2458
+ See the License for the specific language governing permissions and
2459
+ limitations under the License.
2460
+
2461
+
2383
2462
  ******************************
2384
2463
 
2385
2464
  colored