@intentius/chant-lexicon-aws 0.46.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/read-client.d.ts +10 -2
- package/dist/api/read-client.d.ts.map +1 -1
- package/dist/composites/agentcore-agent.d.ts +26 -1
- package/dist/composites/agentcore-agent.d.ts.map +1 -1
- package/dist/composites/bucket-deployment.d.ts +82 -0
- package/dist/composites/bucket-deployment.d.ts.map +1 -0
- package/dist/composites/dynamodb-table.d.ts +45 -0
- package/dist/composites/dynamodb-table.d.ts.map +1 -0
- package/dist/composites/ec2-instance-bundle.d.ts +61 -0
- package/dist/composites/ec2-instance-bundle.d.ts.map +1 -0
- package/dist/composites/ecr-repository.d.ts +49 -0
- package/dist/composites/ecr-repository.d.ts.map +1 -0
- package/dist/composites/fargate-service.d.ts +16 -0
- package/dist/composites/fargate-service.d.ts.map +1 -1
- package/dist/composites/index.d.ts +16 -2
- package/dist/composites/index.d.ts.map +1 -1
- package/dist/composites/monitoring-stack.d.ts +99 -0
- package/dist/composites/monitoring-stack.d.ts.map +1 -0
- package/dist/composites/nlb-service.d.ts +48 -0
- package/dist/composites/nlb-service.d.ts.map +1 -0
- package/dist/composites/step-functions-workflow.d.ts +36 -0
- package/dist/composites/step-functions-workflow.d.ts.map +1 -0
- package/dist/deep-observe.d.ts +45 -0
- package/dist/deep-observe.d.ts.map +1 -1
- package/dist/deep-topology.d.ts +104 -0
- package/dist/deep-topology.d.ts.map +1 -0
- package/dist/effect-receipt-row.d.ts +92 -0
- package/dist/effect-receipt-row.d.ts.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +7 -4
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/post-synth/cf-refs.d.ts +17 -0
- package/dist/lint/post-synth/cf-refs.d.ts.map +1 -1
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/waw061.d.ts +19 -0
- package/dist/lint/post-synth/waw061.d.ts.map +1 -0
- package/dist/lint/post-synth/waw062.d.ts +19 -0
- package/dist/lint/post-synth/waw062.d.ts.map +1 -0
- package/dist/lint/post-synth/waw063.d.ts +35 -0
- package/dist/lint/post-synth/waw063.d.ts.map +1 -0
- package/dist/manifest.json +1 -1
- package/dist/okf/index.md +3 -0
- package/dist/okf/rules/WAW061.md +17 -0
- package/dist/okf/rules/WAW062.md +27 -0
- package/dist/okf/rules/WAW063.md +19 -0
- package/dist/okf/types/Action.md +1 -0
- package/dist/okf/types/AutoScalingGroup.md +1 -0
- package/dist/okf/types/Bucket.md +1 -0
- package/dist/okf/types/ECRRepository.md +1 -0
- package/dist/okf/types/Function.md +1 -0
- package/dist/okf/types/IamPolicy.md +1 -0
- package/dist/okf/types/LaunchTemplate.md +1 -0
- package/dist/okf/types/LoadBalancer.md +1 -0
- package/dist/okf/types/ManagedPolicy.md +2 -0
- package/dist/okf/types/Map.md +1 -0
- package/dist/okf/types/Role.md +2 -0
- package/dist/okf/types/SecurityGroup.md +1 -0
- package/dist/okf/types/StateMachine.md +4 -0
- package/dist/okf/types/Subnet.md +4 -0
- package/dist/okf/types/Table.md +1 -0
- package/dist/okf/types/Type.md +3 -0
- package/dist/okf/types/Vpc.md +1 -0
- package/dist/op/activities/index.d.ts +6 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/receipt-store.d.ts +109 -0
- package/dist/receipt-store.d.ts.map +1 -0
- package/dist/rules/cf-refs.ts +40 -0
- package/dist/rules/waw061.ts +67 -0
- package/dist/rules/waw062.ts +0 -0
- package/dist/rules/waw063.ts +0 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/skills/chant-aws-carve-terraform.md +26 -23
- package/package.json +3 -3
- package/src/api/read-client.test.ts +9 -0
- package/src/api/read-client.ts +15 -2
- package/src/composites/agentcore-agent.ts +35 -13
- package/src/composites/bucket-deployment.test.ts +84 -0
- package/src/composites/bucket-deployment.ts +153 -0
- package/src/composites/composites.test.ts +325 -0
- package/src/composites/dynamodb-table.test.ts +154 -0
- package/src/composites/dynamodb-table.ts +124 -0
- package/src/composites/ec2-instance-bundle.ts +134 -0
- package/src/composites/ecr-repository.test.ts +118 -0
- package/src/composites/ecr-repository.ts +117 -0
- package/src/composites/fargate-service.ts +19 -3
- package/src/composites/index.ts +21 -2
- package/src/composites/monitoring-stack.test.ts +130 -0
- package/src/composites/monitoring-stack.ts +164 -0
- package/src/composites/nlb-service.ts +117 -0
- package/src/composites/step-functions-workflow.ts +127 -0
- package/src/deep-observe.test.ts +201 -1
- package/src/deep-observe.ts +204 -0
- package/src/deep-topology.test.ts +392 -0
- package/src/deep-topology.ts +237 -0
- package/src/effect-receipt-row.test.ts +164 -0
- package/src/effect-receipt-row.ts +147 -0
- package/src/index.ts +28 -7
- package/src/lint/audit-catalog.ts +6 -0
- package/src/lint/post-synth/cf-refs.ts +40 -0
- package/src/lint/post-synth/index.ts +6 -0
- package/src/lint/post-synth/waw061.test.ts +99 -0
- package/src/lint/post-synth/waw061.ts +67 -0
- package/src/lint/post-synth/waw062.test.ts +109 -0
- package/src/lint/post-synth/waw062.ts +0 -0
- package/src/lint/post-synth/waw063.test.ts +220 -0
- package/src/lint/post-synth/waw063.ts +0 -0
- package/src/op/activities/index.ts +15 -0
- package/src/plugin.ts +41 -10
- package/src/receipt-store.test.ts +204 -0
- package/src/receipt-store.ts +315 -0
- package/src/serializer.ts +105 -2
- package/src/skills/chant-aws-carve-terraform.md +26 -23
|
@@ -57,3 +57,4 @@ resource_type: AWS::AutoScaling::AutoScalingGroup
|
|
|
57
57
|
## Governed by
|
|
58
58
|
|
|
59
59
|
- [WAW036](/rules/WAW036.md): Non-ASCII characters in EC2/IAM/CW string properties — rejected at changeset time
|
|
60
|
+
- [WAW062](/rules/WAW062.md): Duplicate export name or explicit resource name within a template — fails at deploy time
|
package/dist/okf/types/Bucket.md
CHANGED
|
@@ -64,3 +64,4 @@ resource_type: AWS::S3::Bucket
|
|
|
64
64
|
- [WAW018](/rules/WAW018.md): S3 bucket missing public access block — all public access should be blocked
|
|
65
65
|
- [WAW042](/rules/WAW042.md): S3 bucket missing a TLS-only bucket policy — deny requests over plaintext
|
|
66
66
|
- [WAW059](/rules/WAW059.md): Wildcard Resource where the declared graph enumerates the touched set — tighten to the consumers' declared Arns
|
|
67
|
+
- [WAW062](/rules/WAW062.md): Duplicate export name or explicit resource name within a template — fails at deploy time
|
|
@@ -31,3 +31,4 @@ resource_type: AWS::ECR::Repository
|
|
|
31
31
|
|
|
32
32
|
- [WAW053](/rules/WAW053.md): ECR repository does not scan images on push
|
|
33
33
|
- [WAW054](/rules/WAW054.md): ECR repository does not have immutable image tags
|
|
34
|
+
- [WAW062](/rules/WAW062.md): Duplicate export name or explicit resource name within a template — fails at deploy time
|
|
@@ -56,3 +56,4 @@ resource_type: AWS::Lambda::Function
|
|
|
56
56
|
- [WAW011](/rules/WAW011.md): Deprecated Lambda runtime — flags deprecated or approaching-EOL Lambda runtimes
|
|
57
57
|
- [WAW022](/rules/WAW022.md): Lambda function is not configured with a VPC — consider adding VpcConfig for network isolation
|
|
58
58
|
- [WAW036](/rules/WAW036.md): Non-ASCII characters in EC2/IAM/CW string properties — rejected at changeset time
|
|
59
|
+
- [WAW062](/rules/WAW062.md): Duplicate export name or explicit resource name within a template — fails at deploy time
|
|
@@ -26,3 +26,4 @@ resource_type: AWS::IAM::Policy
|
|
|
26
26
|
- [WAW020](/rules/WAW020.md): IAM policy uses wildcard Action — use specific actions following least privilege
|
|
27
27
|
- [WAW059](/rules/WAW059.md): Wildcard Resource where the declared graph enumerates the touched set — tighten to the consumers' declared Arns
|
|
28
28
|
- [WAW060](/rules/WAW060.md): IAM policy attached to no principal — it grants nothing
|
|
29
|
+
- [WAW063](/rules/WAW063.md): IAM policy denies an action another attached policy on the same role allows — explicit Deny wins, runtime 403
|
|
@@ -27,3 +27,4 @@ resource_type: AWS::EC2::LaunchTemplate
|
|
|
27
27
|
## Governed by
|
|
28
28
|
|
|
29
29
|
- [WAW036](/rules/WAW036.md): Non-ASCII characters in EC2/IAM/CW string properties — rejected at changeset time
|
|
30
|
+
- [WAW062](/rules/WAW062.md): Duplicate export name or explicit resource name within a template — fails at deploy time
|
|
@@ -41,3 +41,4 @@ resource_type: AWS::ElasticLoadBalancingV2::LoadBalancer
|
|
|
41
41
|
## Governed by
|
|
42
42
|
|
|
43
43
|
- [WAW024](/rules/WAW024.md): Application Load Balancer does not have access logging enabled
|
|
44
|
+
- [WAW062](/rules/WAW062.md): Duplicate export name or explicit resource name within a template — fails at deploy time
|
|
@@ -42,3 +42,5 @@ resource_type: AWS::IAM::ManagedPolicy
|
|
|
42
42
|
- [WAW020](/rules/WAW020.md): IAM policy uses wildcard Action — use specific actions following least privilege
|
|
43
43
|
- [WAW059](/rules/WAW059.md): Wildcard Resource where the declared graph enumerates the touched set — tighten to the consumers' declared Arns
|
|
44
44
|
- [WAW060](/rules/WAW060.md): IAM policy attached to no principal — it grants nothing
|
|
45
|
+
- [WAW062](/rules/WAW062.md): Duplicate export name or explicit resource name within a template — fails at deploy time
|
|
46
|
+
- [WAW063](/rules/WAW063.md): IAM policy denies an action another attached policy on the same role allows — explicit Deny wins, runtime 403
|
package/dist/okf/types/Map.md
CHANGED
|
@@ -35,3 +35,4 @@ resource_type: AWS::Location::Map
|
|
|
35
35
|
- [WAW016](/rules/WAW016.md): Deprecated property usage — flags properties marked as deprecated in the CloudFormation Registry
|
|
36
36
|
- [WAW036](/rules/WAW036.md): Non-ASCII characters in EC2/IAM/CW string properties — rejected at changeset time
|
|
37
37
|
- [WAW059](/rules/WAW059.md): Wildcard Resource where the declared graph enumerates the touched set — tighten to the consumers' declared Arns
|
|
38
|
+
- [WAW062](/rules/WAW062.md): Duplicate export name or explicit resource name within a template — fails at deploy time
|
package/dist/okf/types/Role.md
CHANGED
|
@@ -32,3 +32,5 @@ resource_type: AWS::IAM::Role
|
|
|
32
32
|
- [WAW020](/rules/WAW020.md): IAM policy uses wildcard Action — use specific actions following least privilege
|
|
33
33
|
- [WAW036](/rules/WAW036.md): Non-ASCII characters in EC2/IAM/CW string properties — rejected at changeset time
|
|
34
34
|
- [WAW059](/rules/WAW059.md): Wildcard Resource where the declared graph enumerates the touched set — tighten to the consumers' declared Arns
|
|
35
|
+
- [WAW062](/rules/WAW062.md): Duplicate export name or explicit resource name within a template — fails at deploy time
|
|
36
|
+
- [WAW063](/rules/WAW063.md): IAM policy denies an action another attached policy on the same role allows — explicit Deny wins, runtime 403
|
|
@@ -29,3 +29,4 @@ resource_type: AWS::EC2::SecurityGroup
|
|
|
29
29
|
- [WAW019](/rules/WAW019.md): Security group allows unrestricted ingress on sensitive ports (SSH, RDP, database)
|
|
30
30
|
- [WAW036](/rules/WAW036.md): Non-ASCII characters in EC2/IAM/CW string properties — rejected at changeset time
|
|
31
31
|
- [WAW049](/rules/WAW049.md): Security group allows unrestricted ingress on a port other than ALB:80/443
|
|
32
|
+
- [WAW062](/rules/WAW062.md): Duplicate export name or explicit resource name within a template — fails at deploy time
|
package/dist/okf/types/Subnet.md
CHANGED
package/dist/okf/types/Table.md
CHANGED
|
@@ -43,3 +43,4 @@ resource_type: AWS::DynamoDB::Table
|
|
|
43
43
|
- [WAW027](/rules/WAW027.md): DynamoDB table does not have point-in-time recovery enabled
|
|
44
44
|
- [WAW030](/rules/WAW030.md): Missing DependsOn for known CloudFormation ordering patterns
|
|
45
45
|
- [WAW059](/rules/WAW059.md): Wildcard Resource where the declared graph enumerates the touched set — tighten to the consumers' declared Arns
|
|
46
|
+
- [WAW062](/rules/WAW062.md): Duplicate export name or explicit resource name within a template — fails at deploy time
|
package/dist/okf/types/Type.md
CHANGED
|
@@ -75,3 +75,6 @@ resource_type: AWS::Cassandra::Type
|
|
|
75
75
|
- [WAW058](/rules/WAW058.md): Organization audit trail missing, not logging, or scoped down to a single region
|
|
76
76
|
- [WAW059](/rules/WAW059.md): Wildcard Resource where the declared graph enumerates the touched set — tighten to the consumers' declared Arns
|
|
77
77
|
- [WAW060](/rules/WAW060.md): IAM policy attached to no principal — it grants nothing
|
|
78
|
+
- [WAW061](/rules/WAW061.md): Subnet CidrBlock falls outside its VPC's CidrBlock — fails at deploy time
|
|
79
|
+
- [WAW062](/rules/WAW062.md): Duplicate export name or explicit resource name within a template — fails at deploy time
|
|
80
|
+
- [WAW063](/rules/WAW063.md): IAM policy denies an action another attached policy on the same role allows — explicit Deny wins, runtime 403
|
package/dist/okf/types/Vpc.md
CHANGED
|
@@ -18,6 +18,12 @@ export { flociUp, flociDown, flociRunCommand, flociRmCommand, flociExistsCommand
|
|
|
18
18
|
export type { FlociUpArgs, FlociDownArgs } from "./floci.js";
|
|
19
19
|
export { awsApply, awsDelete, rollbackStack, waitForStackSettled, cfnUrl, cfnForm, capabilityParams, xmlField, stackStatus, stackId, cfnErrorMessage, isStackMissing, isNoUpdates, isSuccessStatus, isFailureStatus, isTerminalStatus, } from "./aws-apply.js";
|
|
20
20
|
export type { AwsApplyArgs, RollbackStackArgs, AwsHttp } from "./aws-apply.js";
|
|
21
|
+
export declare const receiptRead: (args: import("@intentius/chant/op/receipt-store").ReceiptReadArgs, signal?: AbortSignal) => Promise<import("@intentius/chant/op/receipt-store").ReceiptReadResult>;
|
|
22
|
+
export declare const receiptWrite: (args: import("@intentius/chant/op/receipt-store").ReceiptWriteArgs, signal?: AbortSignal) => Promise<{
|
|
23
|
+
written: true;
|
|
24
|
+
receipt: string;
|
|
25
|
+
}>;
|
|
26
|
+
export declare const receiptStaleness: (args: import("@intentius/chant/op/receipt-store").ReceiptStalenessArgs, signal?: AbortSignal) => Promise<import("@intentius/chant/op/receipt-store").ReceiptStalenessResult>;
|
|
21
27
|
export { awsAgentCoreFetchTrace } from "../../agentcore/trace-fetch.js";
|
|
22
28
|
export type { AgentCoreTraceSource, AwsAgentCoreFetchTraceArgs, AwsAgentCoreFetchTraceResult, } from "../../agentcore/trace-fetch.js";
|
|
23
29
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/op/activities/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EACL,OAAO,EACP,SAAS,EACT,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,QAAQ,EACR,YAAY,GACb,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE1D,OAAO,EACL,QAAQ,EACR,SAAS,EACT,aAAa,EACb,mBAAmB,EACnB,MAAM,EACN,OAAO,EACP,gBAAgB,EAChB,QAAQ,EACR,WAAW,EACX,OAAO,EACP,eAAe,EACf,cAAc,EACd,WAAW,EACX,eAAe,EACf,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/op/activities/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EACL,OAAO,EACP,SAAS,EACT,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,cAAc,EACd,QAAQ,EACR,YAAY,GACb,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE1D,OAAO,EACL,QAAQ,EACR,SAAS,EACT,aAAa,EACb,mBAAmB,EACnB,MAAM,EACN,OAAO,EACP,gBAAgB,EAChB,QAAQ,EACR,WAAW,EACX,OAAO,EACP,eAAe,EACf,cAAc,EACd,WAAW,EACX,eAAe,EACf,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAa5E,eAAO,MAAM,WAAW,qKAAqC,CAAC;AAC9D,eAAO,MAAM,YAAY;;;EAAsC,CAAC;AAChE,eAAO,MAAM,gBAAgB,+KAA0C,CAAC;AAExE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,YAAY,EACV,oBAAoB,EACpB,0BAA0B,EAC1B,4BAA4B,GAC7B,MAAM,6BAA6B,CAAC"}
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAgL,MAAM,0BAA0B,CAAC;AAqC5O;yEACyE;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAiBnD,eAAO,MAAM,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAgL,MAAM,0BAA0B,CAAC;AAqC5O;yEACyE;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAiBnD,eAAO,MAAM,SAAS,EAAE,aAk9BvB,CAAC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The aws `ReceiptStore` (#1835, epic #1703) — core's injectable receipt seam
|
|
3
|
+
* (#1834, `@intentius/chant/op/receipt-store`) implemented over SSM Parameter
|
|
4
|
+
* Store, plain `String`, at the path identity ./effect-receipt-row.ts derives
|
|
5
|
+
* from the ownership marker fields.
|
|
6
|
+
*
|
|
7
|
+
* The transport is the lexicon's own read/apply transport (./api/read-client,
|
|
8
|
+
* #1206), pointed at the SSM JSON API: `fetch`, SigV4 when credentials
|
|
9
|
+
* resolve, and the one endpoint-override rule (#1694) — the `endpoint`
|
|
10
|
+
* option, else `AWS_ENDPOINT_URL_SSM`, else `AWS_ENDPOINT_URL` — so a local
|
|
11
|
+
* emulator lane reads and writes receipts without any store-specific wiring.
|
|
12
|
+
*
|
|
13
|
+
* Write discipline (epic decision 3): `write` exists for the `effect()` step
|
|
14
|
+
* alone — the step's read-compare-run-write is the only path that reaches it,
|
|
15
|
+
* on success, last. `PutParameter` is `Type: "String"` always; the first
|
|
16
|
+
* write creates the parameter with the ownership tags, and a later write
|
|
17
|
+
* overwrites the value (SSM refuses `Overwrite` and `Tags` in one call, so
|
|
18
|
+
* tags ride creation only — they never change after).
|
|
19
|
+
*
|
|
20
|
+
* Identity: the parameter name needs `<stack>` and `<env>`, which the
|
|
21
|
+
* activity args deliberately do not carry (the `EffectReceiptRef` is
|
|
22
|
+
* identity-of-the-effect, not identity-of-the-deployment). The store resolves
|
|
23
|
+
* them once, lazily, at first use: an explicit option, else `CHANT_ENV` (what
|
|
24
|
+
* `chant run --env` sets) and the project's `ownership` block — the same
|
|
25
|
+
* fields that stamp markers (epic decision 4). Nothing resolving is an error,
|
|
26
|
+
* never a guessed segment.
|
|
27
|
+
*/
|
|
28
|
+
import type { ReceiptStore } from "@intentius/chant/op/receipt-store";
|
|
29
|
+
import type { ResourceMetadata } from "@intentius/chant/lexicon";
|
|
30
|
+
import type { UnobservedReason } from "@intentius/chant/observation";
|
|
31
|
+
import { type AwsCredentialSource, type AwsReadClientOptions, type AwsReadHttp } from "./api/read-client.js";
|
|
32
|
+
/** Options for {@link awsReceiptStore}. All optional: the default store reads
|
|
33
|
+
* its identity from the project and its endpoint from the environment. */
|
|
34
|
+
export interface AwsReceiptStoreOptions {
|
|
35
|
+
/** The path's `<stack>` segment. Omitted, the project's `ownership.stack`
|
|
36
|
+
* (chant.config.ts, found upward from `cwd`) answers. */
|
|
37
|
+
stack?: string;
|
|
38
|
+
/** The path's `<env>` segment — explicit by decision 4. Omitted, `CHANT_ENV`
|
|
39
|
+
* (set by `chant run --env`) answers, then a literal `ownership.env`. */
|
|
40
|
+
environment?: string;
|
|
41
|
+
/** Where to look for chant.config.ts. Defaults to the working directory. */
|
|
42
|
+
cwd?: string;
|
|
43
|
+
/** Endpoint override; omitted, `AWS_ENDPOINT_URL[_SSM]` answers (#1694). */
|
|
44
|
+
endpoint?: string;
|
|
45
|
+
/** Region for the real-AWS host. */
|
|
46
|
+
region?: string;
|
|
47
|
+
/** Injectable HTTP, mirroring the read client's. Tests avoid the network. */
|
|
48
|
+
http?: AwsReadHttp;
|
|
49
|
+
/** Environment record the endpoint/credential/identity fallbacks read.
|
|
50
|
+
* Defaults to `process.env`; injectable for tests. */
|
|
51
|
+
env?: Record<string, string | undefined>;
|
|
52
|
+
/** What to sign with — same seam as the read client. */
|
|
53
|
+
credentials?: AwsCredentialSource;
|
|
54
|
+
/** Sign even against an endpoint override — for an override that is real AWS. */
|
|
55
|
+
signEndpointOverride?: boolean;
|
|
56
|
+
}
|
|
57
|
+
/** One SSM JSON call. Exported for the observation leg (plugin.ts), which
|
|
58
|
+
* reads the same parameters the store writes. */
|
|
59
|
+
export declare function ssmCall(action: string, payload: Record<string, unknown>, options: AwsReadClientOptions): Promise<{
|
|
60
|
+
status: number;
|
|
61
|
+
json: Record<string, unknown>;
|
|
62
|
+
}>;
|
|
63
|
+
/** The API's own error code from a JSON-protocol error body — `__type`, with
|
|
64
|
+
* any `namespace#` prefix stripped. */
|
|
65
|
+
export declare function ssmErrorCode(json: Record<string, unknown>): string | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* `GetParameter` by name. Absent (`ParameterNotFound`) is `undefined` — a real
|
|
68
|
+
* answer, distinct from a failed read, which throws.
|
|
69
|
+
*/
|
|
70
|
+
export declare function ssmGetParameter(name: string, options?: AwsReadClientOptions): Promise<string | undefined>;
|
|
71
|
+
/**
|
|
72
|
+
* `PutParameter`, plain `String`. Creation carries `tags`; an existing
|
|
73
|
+
* parameter is overwritten (`Overwrite: true`) without them — SSM refuses
|
|
74
|
+
* `Overwrite` and `Tags` in the same call, and ownership tags never change.
|
|
75
|
+
*/
|
|
76
|
+
export declare function ssmPutParameter(name: string, value: string, tags: Record<string, string>, options?: AwsReadClientOptions): Promise<void>;
|
|
77
|
+
/** What the observation leg learned about the declared receipt rows. */
|
|
78
|
+
export interface ReceiptRowObservation {
|
|
79
|
+
resources: Record<string, ResourceMetadata>;
|
|
80
|
+
unobserved: Record<string, {
|
|
81
|
+
type: string;
|
|
82
|
+
reason: UnobservedReason;
|
|
83
|
+
detail: string;
|
|
84
|
+
}>;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The plan-side live read of the receipt rows (#1835's observation leg).
|
|
88
|
+
*
|
|
89
|
+
* A receipt is not a stack member — the applier never writes it (#1832) — so
|
|
90
|
+
* `describe-stack-resources` honestly reports it absent even while the
|
|
91
|
+
* parameter exists. The serializer renders each receipt's derived path into
|
|
92
|
+
* the template's `Metadata` (./serializer.ts), so this leg reads the paths
|
|
93
|
+
* back from the build output — one derivation, decision 4 — and asks SSM
|
|
94
|
+
* `GetParameter` for each. Present maps the stored value onto
|
|
95
|
+
* `attributes.value` (core's `RECEIPT_VALUE_ATTRIBUTE`); `ParameterNotFound`
|
|
96
|
+
* is a real absence and stays one; a failed read is an `unobserved` hole,
|
|
97
|
+
* never a wrong answer — a receipt nobody could read must not arrive
|
|
98
|
+
* downstream as "the effect never ran".
|
|
99
|
+
*/
|
|
100
|
+
export declare function observeReceiptRows(entityNames: string[], buildOutput: string, options?: AwsReadClientOptions): Promise<ReceiptRowObservation>;
|
|
101
|
+
/**
|
|
102
|
+
* The `ReceiptStore` over SSM. Bind it once in the op activities barrel —
|
|
103
|
+
* `receiptActivities(awsReceiptStore())` — and the registry resolves
|
|
104
|
+
* `receiptRead`/`receiptWrite`/`receiptStaleness` by name, exactly like
|
|
105
|
+
* `ensureSecret` (#1830). Identity and endpoint resolve lazily at first use,
|
|
106
|
+
* so module load never reads the project or the environment.
|
|
107
|
+
*/
|
|
108
|
+
export declare function awsReceiptStore(options?: AwsReceiptStoreOptions): ReceiptStore;
|
|
109
|
+
//# sourceMappingURL=receipt-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt-store.d.ts","sourceRoot":"","sources":["../src/receipt-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,KAAK,EAAoB,YAAY,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAKL,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,WAAW,EACjB,MAAM,mBAAmB,CAAC;AAO3B;0EAC0E;AAC1E,MAAM,WAAW,sBAAsB;IACrC;6DACyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;6EACyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB;0DACsD;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,wDAAwD;IACxD,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,iFAAiF;IACjF,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED;iDACiD;AACjD,wBAAsB,OAAO,CAC3B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAyB5D;AAED;uCACuC;AACvC,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,SAAS,CAI9E;AAQD;;;GAGG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAQ7B;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAiBf;AA4CD,wEAAwE;AACxE,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC5C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,gBAAgB,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACxF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EAAE,EACrB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,qBAAqB,CAAC,CA+ChC;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,YAAY,CAyBlF"}
|
package/dist/rules/cf-refs.ts
CHANGED
|
@@ -169,6 +169,46 @@ export function isFullTierEnv(env: string | undefined): boolean {
|
|
|
169
169
|
return env === "prod" || env === "production" || env === "full";
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Parse an IPv4 CIDR literal ("10.0.0.0/16") into its numeric base address
|
|
174
|
+
* and prefix length. Returns null for anything that isn't a plain IPv4 CIDR
|
|
175
|
+
* (IPv6, malformed octets, out-of-range prefix) — callers treat that as
|
|
176
|
+
* "can't prove statically" rather than an error.
|
|
177
|
+
*/
|
|
178
|
+
export function parseIpv4Cidr(cidr: string): { base: number; prefix: number } | null {
|
|
179
|
+
const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/.exec(cidr.trim());
|
|
180
|
+
if (!m) return null;
|
|
181
|
+
const octets = m.slice(1, 5).map(Number);
|
|
182
|
+
if (octets.some((o) => o < 0 || o > 255)) return null;
|
|
183
|
+
const prefix = Number(m[5]);
|
|
184
|
+
if (prefix < 0 || prefix > 32) return null;
|
|
185
|
+
const base = ((octets[0] << 24) | (octets[1] << 16) | (octets[2] << 8) | octets[3]) >>> 0;
|
|
186
|
+
return { base, prefix };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** [networkAddress, broadcastAddress] for a parsed IPv4 CIDR block. */
|
|
190
|
+
function ipv4Range(cidr: { base: number; prefix: number }): [number, number] {
|
|
191
|
+
const maskBits = cidr.prefix === 0 ? 0 : (0xffffffff << (32 - cidr.prefix)) >>> 0;
|
|
192
|
+
const network = (cidr.base & maskBits) >>> 0;
|
|
193
|
+
const broadcast = (network | (~maskBits >>> 0)) >>> 0;
|
|
194
|
+
return [network, broadcast];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Whether `inner` (an IPv4 CIDR literal) falls entirely within `outer`.
|
|
199
|
+
* Returns null — not false — when either literal isn't a plain, parseable
|
|
200
|
+
* IPv4 CIDR (e.g. IPv6, or a CloudFormation intrinsic already stringified
|
|
201
|
+
* elsewhere); a null means "statically unprovable", not "violation".
|
|
202
|
+
*/
|
|
203
|
+
export function ipv4CidrContains(outer: string, inner: string): boolean | null {
|
|
204
|
+
const outerCidr = parseIpv4Cidr(outer);
|
|
205
|
+
const innerCidr = parseIpv4Cidr(inner);
|
|
206
|
+
if (!outerCidr || !innerCidr) return null;
|
|
207
|
+
const [oStart, oEnd] = ipv4Range(outerCidr);
|
|
208
|
+
const [iStart, iEnd] = ipv4Range(innerCidr);
|
|
209
|
+
return iStart >= oStart && iEnd <= oEnd;
|
|
210
|
+
}
|
|
211
|
+
|
|
172
212
|
/**
|
|
173
213
|
* Check if a port range [fromPort, toPort] contains any of the sensitive ports.
|
|
174
214
|
*/
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WAW061: Subnet CIDR Not Contained In VPC CIDR
|
|
3
|
+
*
|
|
4
|
+
* A subnet's CidrBlock must fall entirely inside its VPC's CidrBlock.
|
|
5
|
+
* Stock CDK does not check this — CloudFormation accepts the template and
|
|
6
|
+
* only fails the `AWS::EC2::Subnet` resource at deploy time
|
|
7
|
+
* ("The CIDR ... is invalid" / "does not fall within the CIDR range of the
|
|
8
|
+
* VPC"), after the rest of the stack may have already started rolling out.
|
|
9
|
+
* This is a cheap static graph check: follow the subnet's VpcId Ref to a
|
|
10
|
+
* declared VPC in the same template and do IPv4 CIDR-range math.
|
|
11
|
+
*
|
|
12
|
+
* Stays quiet whenever the graph can't prove it: intrinsic CIDR blocks,
|
|
13
|
+
* IPv6, a VpcId that isn't a simple Ref to a declared AWS::EC2::VPC, or a
|
|
14
|
+
* VPC whose own CidrBlock isn't a literal string.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
18
|
+
import { parseCFTemplate, findResourceRefs, ipv4CidrContains } from "./cf-refs";
|
|
19
|
+
|
|
20
|
+
export function checkSubnetCidrContainment(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
21
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
22
|
+
|
|
23
|
+
for (const [_lexicon, output] of ctx.outputs) {
|
|
24
|
+
const template = parseCFTemplate(output);
|
|
25
|
+
if (!template?.Resources) continue;
|
|
26
|
+
|
|
27
|
+
for (const [logicalId, resource] of Object.entries(template.Resources)) {
|
|
28
|
+
if (resource.Type !== "AWS::EC2::Subnet") continue;
|
|
29
|
+
|
|
30
|
+
const props = resource.Properties ?? {};
|
|
31
|
+
const subnetCidr = props.CidrBlock;
|
|
32
|
+
if (typeof subnetCidr !== "string") continue;
|
|
33
|
+
|
|
34
|
+
const vpcRefs = findResourceRefs(props.VpcId);
|
|
35
|
+
if (vpcRefs.size !== 1) continue;
|
|
36
|
+
const [vpcId] = vpcRefs;
|
|
37
|
+
|
|
38
|
+
const vpc = template.Resources[vpcId];
|
|
39
|
+
if (!vpc || vpc.Type !== "AWS::EC2::VPC") continue;
|
|
40
|
+
|
|
41
|
+
const vpcCidr = vpc.Properties?.CidrBlock;
|
|
42
|
+
if (typeof vpcCidr !== "string") continue;
|
|
43
|
+
|
|
44
|
+
const contained = ipv4CidrContains(vpcCidr, subnetCidr);
|
|
45
|
+
if (contained === false) {
|
|
46
|
+
diagnostics.push({
|
|
47
|
+
checkId: "WAW061",
|
|
48
|
+
severity: "error",
|
|
49
|
+
message: `Subnet "${logicalId}" CidrBlock ${subnetCidr} is not contained within VPC "${vpcId}"'s CidrBlock ${vpcCidr} — CloudFormation rejects this at deploy time`,
|
|
50
|
+
entity: logicalId,
|
|
51
|
+
lexicon: "aws",
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return diagnostics;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const waw061: PostSynthCheck = {
|
|
61
|
+
id: "WAW061",
|
|
62
|
+
description: "Subnet CidrBlock falls outside its VPC's CidrBlock — fails at deploy time",
|
|
63
|
+
|
|
64
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
65
|
+
return checkSubnetCidrContainment(ctx);
|
|
66
|
+
},
|
|
67
|
+
};
|
|
Binary file
|
|
Binary file
|
package/dist/serializer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAsC,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAsC,MAAM,6BAA6B,CAAC;AAiflG;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,UA4E3B,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
skill: chant-aws-carve-terraform
|
|
3
|
-
description: Demo carving a resource out of Terraform into native chant — advise, emit, bridge, apply — fully offline
|
|
3
|
+
description: Demo carving a resource out of Terraform into native chant — advise, emit, audit, bridge, apply — fully offline
|
|
4
4
|
user-invocable: true
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -15,7 +15,7 @@ graduation.
|
|
|
15
15
|
## When to use
|
|
16
16
|
|
|
17
17
|
- Someone asks "how does chant move things off Terraform?"
|
|
18
|
-
- You want to show the advise → emit → bridge → apply loop end to end.
|
|
18
|
+
- You want to show the advise → emit → audit → bridge → apply loop end to end.
|
|
19
19
|
- You are evaluating whether a Terraform estate is worth carving.
|
|
20
20
|
|
|
21
21
|
## Preconditions
|
|
@@ -27,7 +27,7 @@ graduation.
|
|
|
27
27
|
## The fastest path: run the bundled demo
|
|
28
28
|
|
|
29
29
|
The `examples/terraform-carve-out` example ships a runnable estate + state and a
|
|
30
|
-
script that runs all
|
|
30
|
+
script that runs all five steps with commentary:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
33
|
cd examples/terraform-carve-out
|
|
@@ -54,28 +54,30 @@ From `examples/terraform-carve-out`, with `TF=./terraform`:
|
|
|
54
54
|
--state ./terraform/terraform.tfstate --output ./carveout
|
|
55
55
|
```
|
|
56
56
|
Show `./carveout/src/assets.ts` — a real `new Bucket({ BucketName, Tags })` with
|
|
57
|
-
CloudFormation-style properties mapped from the Terraform state attributes
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
CloudFormation-style properties mapped from the Terraform state attributes,
|
|
58
|
+
and the `aws_s3_bucket_versioning` sub-resource folded into
|
|
59
|
+
`VersioningConfiguration`. Emit also scaffolds `./carveout` into a buildable
|
|
60
|
+
chant project (`chant.config.ts`, `package.json`) and persists a carve
|
|
61
|
+
manifest (`*.carve.json`) — bridge and apply read the target from it, so
|
|
62
|
+
`--select` is only needed once. Explain: a Terraform-managed resource is not
|
|
63
|
+
in any CloudFormation stack, so the correct source of its live shape is the
|
|
64
|
+
state file, not a cloud read.
|
|
65
|
+
|
|
66
|
+
3. **Audit the inherited resource.**
|
|
65
67
|
```bash
|
|
66
|
-
chant
|
|
68
|
+
chant build ./carveout/src --lexicon aws
|
|
67
69
|
```
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
The first build fails, deliberately: the post-synth audit refuses the
|
|
71
|
+
adopted bucket because Terraform managed it without a public-access block or
|
|
72
|
+
a TLS-only policy. This is a feature of carving: chant immediately tells the
|
|
73
|
+
person what is wrong with what they adopted. Show the fix (add
|
|
74
|
+
`PublicAccessBlockConfiguration` and a companion `S3BucketPolicy` with an
|
|
75
|
+
`aws:SecureTransport = false` Deny — the tutorial page has the exact code),
|
|
76
|
+
then re-run the build and show the valid CloudFormation template.
|
|
75
77
|
|
|
76
78
|
4. **Bridge — patch the surviving Terraform.**
|
|
77
79
|
```bash
|
|
78
|
-
chant carve bridge --from ./terraform --
|
|
80
|
+
chant carve bridge --from ./terraform --output ./carveout
|
|
79
81
|
```
|
|
80
82
|
Show the generated `data "aws_s3_bucket" "assets"` and the rewired survivor.
|
|
81
83
|
Emphasize it is dry-run: nothing in `./terraform` changed. `--apply-rewrites`
|
|
@@ -83,11 +85,12 @@ From `examples/terraform-carve-out`, with `TF=./terraform`:
|
|
|
83
85
|
|
|
84
86
|
5. **Apply — graduation plan.**
|
|
85
87
|
```bash
|
|
86
|
-
chant carve apply --from ./terraform --
|
|
88
|
+
chant carve apply --from ./terraform --output ./carveout --env prod --stack assets
|
|
87
89
|
```
|
|
88
90
|
Show the ownership marker (`chant:managed-by/stack/env`) and the finalized
|
|
89
|
-
runbook.
|
|
90
|
-
|
|
91
|
+
runbook. `--write-source` additionally stamps the marker tags into the
|
|
92
|
+
emitted source. Stress this makes no cloud call — the apply is the person's
|
|
93
|
+
own lifecycle; chant just plans it.
|
|
91
94
|
|
|
92
95
|
## Key points to land
|
|
93
96
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-aws",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.49.0",
|
|
4
4
|
"description": "AWS CloudFormation lexicon for chant — declarative IaC in TypeScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -73,14 +73,14 @@
|
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
75
|
"fflate": "^0.8.2",
|
|
76
|
-
"js-yaml": "^4.1
|
|
76
|
+
"js-yaml": "^4.3.1"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
79
|
"@intentius/chant": "*",
|
|
80
80
|
"typescript": "^5.9.3"
|
|
81
81
|
},
|
|
82
82
|
"peerDependencies": {
|
|
83
|
-
"@intentius/chant": "^0.
|
|
83
|
+
"@intentius/chant": "^0.49.0",
|
|
84
84
|
"typescript": "^5.9.3"
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -198,6 +198,15 @@ describe("Cloud Control", () => {
|
|
|
198
198
|
expect(await listResources("AWS::S3::Bucket", { http })).toEqual([]);
|
|
199
199
|
});
|
|
200
200
|
|
|
201
|
+
test("listResources scopes a child listing with ResourceModel, JSON-encoded as the API takes it", async () => {
|
|
202
|
+
const { http, calls } = recording(() => respond(JSON.stringify({ ResourceDescriptions: [] })));
|
|
203
|
+
await listResources("AWS::IAM::RolePolicy", { http }, { RoleName: "app-role" });
|
|
204
|
+
expect(JSON.parse(calls[0].body)).toEqual({
|
|
205
|
+
TypeName: "AWS::IAM::RolePolicy",
|
|
206
|
+
ResourceModel: JSON.stringify({ RoleName: "app-role" }),
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
201
210
|
test("parseResourceDescription refuses anything that is not a model object", () => {
|
|
202
211
|
expect(parseResourceDescription(null)).toBeNull();
|
|
203
212
|
expect(parseResourceDescription({ Identifier: "a" })).toBeNull();
|
package/src/api/read-client.ts
CHANGED
|
@@ -434,17 +434,30 @@ export async function getResource(
|
|
|
434
434
|
return parseResourceDescription(body.ResourceDescription);
|
|
435
435
|
}
|
|
436
436
|
|
|
437
|
-
/**
|
|
437
|
+
/**
|
|
438
|
+
* `ListResources` — every live resource of one type, paginated to exhaustion.
|
|
439
|
+
*
|
|
440
|
+
* `resourceModel` is Cloud Control's `ResourceModel`: the scope a child-typed
|
|
441
|
+
* listing requires (`{ RoleName }` for `AWS::IAM::RolePolicy`,
|
|
442
|
+
* `{ TopicArn }` for `AWS::SNS::Subscription`). The API takes it as a JSON
|
|
443
|
+
* *string*, the same double encoding `GetResource` answers with; this takes
|
|
444
|
+
* the object and encodes it so no caller repeats that detail.
|
|
445
|
+
*/
|
|
438
446
|
export async function listResources(
|
|
439
447
|
typeName: string,
|
|
440
448
|
options: AwsReadClientOptions = {},
|
|
449
|
+
resourceModel?: Record<string, unknown>,
|
|
441
450
|
): Promise<CloudControlDescription[]> {
|
|
442
451
|
const out: CloudControlDescription[] = [];
|
|
443
452
|
let nextToken: string | undefined;
|
|
444
453
|
do {
|
|
445
454
|
const body = await cloudControl(
|
|
446
455
|
"ListResources",
|
|
447
|
-
{
|
|
456
|
+
{
|
|
457
|
+
TypeName: typeName,
|
|
458
|
+
...(resourceModel ? { ResourceModel: JSON.stringify(resourceModel) } : {}),
|
|
459
|
+
...(nextToken ? { NextToken: nextToken } : {}),
|
|
460
|
+
},
|
|
448
461
|
options,
|
|
449
462
|
);
|
|
450
463
|
const descriptions = Array.isArray(body.ResourceDescriptions) ? body.ResourceDescriptions : [];
|
|
@@ -45,19 +45,36 @@ export function agentCoreDefaultEndpointArn(runtime: InstanceType<typeof Runtime
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
|
-
* AgentCore's
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
|
|
48
|
+
* Bedrock AgentCore's real limits, verified against the CloudFormation
|
|
49
|
+
* registry schema for `AWS::BedrockAgentCore::{Runtime,RuntimeEndpoint,Memory}`
|
|
50
|
+
* (#882).
|
|
51
|
+
*
|
|
52
|
+
* Exported for the same reason as `MICROVM_LIMITS` (#1374, #1420): a
|
|
53
|
+
* consumer driving AgentCore through another control plane needs the same
|
|
54
|
+
* numbers, and copying them is how two sources of truth start.
|
|
55
|
+
*/
|
|
56
|
+
export const AGENTCORE_LIMITS = {
|
|
57
|
+
/**
|
|
58
|
+
* `Runtime`/`RuntimeEndpoint`/`Memory` `Name`/`AgentRuntimeName` fields
|
|
59
|
+
* are bound to this pattern — no hyphens, unlike chant's usual kebab-case
|
|
60
|
+
* component names. `Gateway`/`GatewayTarget`/`WorkloadIdentity` names
|
|
61
|
+
* accept hyphens, so only the Runtime family needs sanitizing.
|
|
62
|
+
*/
|
|
63
|
+
runtimeNamePattern: /^[a-zA-Z][a-zA-Z0-9_]{0,47}$/,
|
|
64
|
+
maxRuntimeNameLength: 48,
|
|
65
|
+
/** `Memory` event retention bounds, in days. */
|
|
66
|
+
memoryEventExpiryDays: { min: 3, max: 365, default: 30 },
|
|
67
|
+
} as const;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Non-identifier characters become `_`, and a leading digit/underscore gets
|
|
71
|
+
* an `A` prefix so the result always starts with a letter — then truncated
|
|
72
|
+
* to {@link AGENTCORE_LIMITS}'s `maxRuntimeNameLength`.
|
|
56
73
|
*/
|
|
57
74
|
function toRuntimeIdentifier(name: string): string {
|
|
58
75
|
const cleaned = name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
59
76
|
const identifier = /^[a-zA-Z]/.test(cleaned) ? cleaned : `A${cleaned}`;
|
|
60
|
-
return identifier.slice(0,
|
|
77
|
+
return identifier.slice(0, AGENTCORE_LIMITS.maxRuntimeNameLength);
|
|
61
78
|
}
|
|
62
79
|
|
|
63
80
|
/**
|
|
@@ -117,7 +134,7 @@ export interface AgentCoreAgentProps {
|
|
|
117
134
|
* managed one and fails on a real apply (#978, see the composite doc).
|
|
118
135
|
*/
|
|
119
136
|
endpointName?: string;
|
|
120
|
-
/** Memory event retention, in days.
|
|
137
|
+
/** Memory event retention, in days. Bounds: {@link AGENTCORE_LIMITS.memoryEventExpiryDays}. */
|
|
121
138
|
memoryEventExpiryDays?: number;
|
|
122
139
|
/** Gateway authorizer. Mirrors the generated `BedrockAgentCoreGateway_AuthorizerType` CFN enum. Default: "AWS_IAM". */
|
|
123
140
|
gatewayAuthorizerType?: "CUSTOM_JWT" | "AWS_IAM" | "NONE" | "AUTHENTICATE_ONLY";
|
|
@@ -194,9 +211,14 @@ export const AgentCoreAgent = Composite<AgentCoreAgentProps, AgentCoreAgentResul
|
|
|
194
211
|
if (networkMode === "VPC" && (props.vpcSubnetIds === undefined || props.vpcSecurityGroupIds === undefined)) {
|
|
195
212
|
throw new Error("AgentCoreAgent requires vpcSubnetIds and vpcSecurityGroupIds when networkMode is \"VPC\"");
|
|
196
213
|
}
|
|
197
|
-
const memoryEventExpiryDays = props.memoryEventExpiryDays ??
|
|
198
|
-
if (
|
|
199
|
-
|
|
214
|
+
const memoryEventExpiryDays = props.memoryEventExpiryDays ?? AGENTCORE_LIMITS.memoryEventExpiryDays.default;
|
|
215
|
+
if (
|
|
216
|
+
memoryEventExpiryDays < AGENTCORE_LIMITS.memoryEventExpiryDays.min ||
|
|
217
|
+
memoryEventExpiryDays > AGENTCORE_LIMITS.memoryEventExpiryDays.max
|
|
218
|
+
) {
|
|
219
|
+
throw new Error(
|
|
220
|
+
`AgentCoreAgent memoryEventExpiryDays must be between ${AGENTCORE_LIMITS.memoryEventExpiryDays.min} and ${AGENTCORE_LIMITS.memoryEventExpiryDays.max}`,
|
|
221
|
+
);
|
|
200
222
|
}
|
|
201
223
|
|
|
202
224
|
const runtimeName = toRuntimeIdentifier(props.name);
|