@aws/nx-plugin 1.0.0-rc.51 → 1.0.0-rc.53
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 +1 -0
- package/generators.json +2 -2
- package/migrations.json +5 -0
- package/package.json +1 -1
- package/src/migrations/latest/user-identity-waf-allow-localhost-callback/migration.d.ts +6 -0
- package/src/migrations/latest/user-identity-waf-allow-localhost-callback/migration.js +217 -0
- package/src/migrations/latest/user-identity-waf-allow-localhost-callback/migration.js.map +1 -0
- package/src/preset/__snapshots__/generator.spec.ts.snap +2 -0
- package/src/smithy/project/__snapshots__/generator.spec.ts.snap +109 -4
- package/src/smithy/project/files/shapes/build.Dockerfile.template +33 -0
- package/src/smithy/project/files/shapes/smithy-build.json.template +7 -0
- package/src/smithy/project/files/shapes/src/main.smithy.template +11 -0
- package/src/smithy/project/generator.js +15 -4
- package/src/smithy/project/generator.js.map +1 -1
- package/src/smithy/project/schema.d.js.map +1 -1
- package/src/smithy/project/schema.d.ts +1 -0
- package/src/smithy/project/schema.json +23 -1
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +3 -1
- package/src/ts/react-website/cognito-auth/__snapshots__/generator.spec.ts.snap +25 -3
- package/src/utils/identity-constructs/files/cdk/core/user-identity.ts.template +25 -3
- package/src/utils/identity-constructs/files/terraform/core/user-identity/identity/identity.tf.template +25 -8
- /package/src/smithy/project/files/{build.Dockerfile.template → service/build.Dockerfile.template} +0 -0
- /package/src/smithy/project/files/{smithy-build.json.template → service/smithy-build.json.template} +0 -0
- /package/src/smithy/project/files/{src → service/src}/main.smithy.template +0 -0
- /package/src/smithy/project/files/{src → service/src}/operations/echo.smithy.template +0 -0
package/README.md
CHANGED
|
@@ -171,6 +171,7 @@ pnpm nx g @aws/nx-plugin:ts#infra
|
|
|
171
171
|
| `ts#mcp-server` | MCP server (TypeScript) |
|
|
172
172
|
| `ts#agent` | [Strands Agent](https://strandsagents.com/) (TypeScript) |
|
|
173
173
|
| `ts#nx-generator` | Nx generator scaffold |
|
|
174
|
+
| `smithy#project` | Smithy model project — a service model, or a shape library shared between Smithy projects |
|
|
174
175
|
| `py#project` | Python project (uv) |
|
|
175
176
|
| `py#api` | Python API (FastAPI) with API Gateway + Lambda + [Powertools](https://github.com/aws-powertools/powertools-lambda-python) |
|
|
176
177
|
| `py#lambda-function` | Python Lambda with type-safe event sources |
|
package/generators.json
CHANGED
|
@@ -165,9 +165,9 @@
|
|
|
165
165
|
"smithy#project": {
|
|
166
166
|
"factory": "./src/smithy/project/generator",
|
|
167
167
|
"schema": "./src/smithy/project/schema.json",
|
|
168
|
-
"description": "Generate a Smithy model project",
|
|
168
|
+
"description": "Generate a Smithy model project, either defining a service or a library of reusable shapes",
|
|
169
169
|
"metric": "g27",
|
|
170
|
-
"
|
|
170
|
+
"guidePages": ["smithy-project"]
|
|
171
171
|
},
|
|
172
172
|
"smithy#react-connection": {
|
|
173
173
|
"factory": "./src/smithy/react-connection/generator",
|
package/migrations.json
CHANGED
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
"version": "1.0.0-rc.50",
|
|
17
17
|
"description": "Adopt an existing Terraform state bucket in the vended bootstrap script when its state object is missing",
|
|
18
18
|
"implementation": "./src/migrations/latest/terraform-bootstrap-adopt-existing-bucket/migration"
|
|
19
|
+
},
|
|
20
|
+
"latest-user-identity-waf-allow-localhost-callback": {
|
|
21
|
+
"version": "1.0.0-rc.52",
|
|
22
|
+
"description": "Count the EC2MetaDataSSRF_QUERYARGUMENTS WAF rule on the UserIdentity Web ACL so sign-in from a local dev server is not blocked",
|
|
23
|
+
"implementation": "./src/migrations/latest/user-identity-waf-allow-localhost-callback/migration"
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
26
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/ import { applyGritQL, matchGritQL } from "../../../utils/ast.js";
|
|
5
|
+
import { formatFilesInSubtree } from "../../../utils/format.js";
|
|
6
|
+
import { PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, SHARED_TERRAFORM_DIR } from "../../../utils/shared-constructs-constants.js";
|
|
7
|
+
/**
|
|
8
|
+
* Count the EC2MetaDataSSRF_QUERYARGUMENTS WAF rule on the UserIdentity Web ACL
|
|
9
|
+
*
|
|
10
|
+
* `AWSManagedRulesCommonRuleSet` treats the loopback `redirect_uri` the Cognito
|
|
11
|
+
* Hosted UI receives during local sign-in as an SSRF attempt, so sign-in against
|
|
12
|
+
* a local dev server fails with an opaque 403 before the login page renders. The
|
|
13
|
+
* rule is overridden to Count while a local callback URL is allowed; every other
|
|
14
|
+
* rule in the group still blocks.
|
|
15
|
+
*
|
|
16
|
+
* The local callback URLs are lifted into a named constant so the override can be
|
|
17
|
+
* derived from them rather than restating the condition.
|
|
18
|
+
*
|
|
19
|
+
* How to write a migration:
|
|
20
|
+
* - https://nx.dev/docs/kb/migration-generators
|
|
21
|
+
* - What `nextSteps` means: https://nx.dev/docs/reference/devkit/MigrationReturnObject
|
|
22
|
+
*
|
|
23
|
+
* Guardrails:
|
|
24
|
+
* - Pattern-match before writing: skip files that have diverged from the shape
|
|
25
|
+
* your generators produce and report them via `nextSteps`, rather than
|
|
26
|
+
* clobbering the user's changes.
|
|
27
|
+
* - Idempotent: re-running must be a no-op.
|
|
28
|
+
* - Format what you write: finish with `formatFilesInSubtree` so the files your
|
|
29
|
+
* migration wrote are formatted correctly.
|
|
30
|
+
*/ const CDK_USER_IDENTITY_FILE = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/core/user-identity.ts`;
|
|
31
|
+
const TERRAFORM_IDENTITY_FILE = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/core/user-identity/identity/identity.tf`;
|
|
32
|
+
const SSRF_RULE_NAME = 'EC2MetaDataSSRF_QUERYARGUMENTS';
|
|
33
|
+
// Guards each file: present only once the override has been added.
|
|
34
|
+
const CDK_MIGRATED_PATTERN = `\`ruleActionOverrides: allowsLocalCallback ? $_ : undefined\``;
|
|
35
|
+
const TERRAFORM_MIGRATED_PATTERN = `language hcl\n\`name = "${SSRF_RULE_NAME}"\``;
|
|
36
|
+
// Every CDK edit site, matched structurally so formatting doesn't affect whether
|
|
37
|
+
// the construct is recognised.
|
|
38
|
+
const CDK_CONSTANT_ANCHOR_PATTERN = "`const WEB_CLIENT_ID = 'WebClient'`";
|
|
39
|
+
// Scoped to the `.concat` callsite so it can't also rewrite the array literal
|
|
40
|
+
// inside the constant this migration inserts.
|
|
41
|
+
const CDK_LOCAL_URLS_PATTERN = "`['http://localhost:4200', 'http://localhost:4300'].concat($rest)`";
|
|
42
|
+
const CDK_CALL_PATTERN = '`this.createWebAcl($id, this.userPool)`';
|
|
43
|
+
const CDK_SIGNATURE_PATTERN = '`private createWebAcl = ($id: string, $pool: UserPool) => $body`';
|
|
44
|
+
const CDK_STATEMENT_PATTERN = "`managedRuleGroupStatement: { name: 'AWSManagedRulesCommonRuleSet', vendorName: 'AWS' }`";
|
|
45
|
+
const CDK_EDITS = [
|
|
46
|
+
// Local callback URLs become a named constant the override can key off.
|
|
47
|
+
[
|
|
48
|
+
CDK_CONSTANT_ANCHOR_PATTERN,
|
|
49
|
+
`${CDK_CONSTANT_ANCHOR_PATTERN} => \`const WEB_CLIENT_ID = 'WebClient';
|
|
50
|
+
|
|
51
|
+
/** Local dev server origins permitted to complete the sign-in redirect */
|
|
52
|
+
const LOCAL_CALLBACK_URLS = ['http://localhost:4200', 'http://localhost:4300']\``
|
|
53
|
+
],
|
|
54
|
+
[
|
|
55
|
+
CDK_LOCAL_URLS_PATTERN,
|
|
56
|
+
`${CDK_LOCAL_URLS_PATTERN} => \`LOCAL_CALLBACK_URLS.concat($rest)\``
|
|
57
|
+
],
|
|
58
|
+
[
|
|
59
|
+
CDK_CALL_PATTERN,
|
|
60
|
+
`${CDK_CALL_PATTERN} => \`this.createWebAcl($id, this.userPool, LOCAL_CALLBACK_URLS.length > 0)\``
|
|
61
|
+
],
|
|
62
|
+
[
|
|
63
|
+
CDK_SIGNATURE_PATTERN,
|
|
64
|
+
`${CDK_SIGNATURE_PATTERN} => \`private createWebAcl = (
|
|
65
|
+
$id: string,
|
|
66
|
+
$pool: UserPool,
|
|
67
|
+
allowsLocalCallback: boolean
|
|
68
|
+
) => $body\``
|
|
69
|
+
],
|
|
70
|
+
[
|
|
71
|
+
CDK_STATEMENT_PATTERN,
|
|
72
|
+
`${CDK_STATEMENT_PATTERN} => \`managedRuleGroupStatement: {
|
|
73
|
+
name: 'AWSManagedRulesCommonRuleSet',
|
|
74
|
+
vendorName: 'AWS',
|
|
75
|
+
// ${SSRF_RULE_NAME} treats the loopback redirect_uri the
|
|
76
|
+
// Hosted UI receives during local sign-in as an SSRF attempt. Counted
|
|
77
|
+
// only while a local callback URL is allowed; every other rule blocks.
|
|
78
|
+
ruleActionOverrides: allowsLocalCallback
|
|
79
|
+
? [
|
|
80
|
+
{
|
|
81
|
+
name: '${SSRF_RULE_NAME}',
|
|
82
|
+
actionToUse: { count: {} },
|
|
83
|
+
},
|
|
84
|
+
]
|
|
85
|
+
: undefined,
|
|
86
|
+
}\``
|
|
87
|
+
]
|
|
88
|
+
];
|
|
89
|
+
const TERRAFORM_DATA_SOURCES_PATTERN = [
|
|
90
|
+
'language hcl',
|
|
91
|
+
'`data "aws_region" "current" {}`'
|
|
92
|
+
].join('\n');
|
|
93
|
+
const TERRAFORM_EDITS = [
|
|
94
|
+
// Local callback URLs become a local the override can key off.
|
|
95
|
+
[
|
|
96
|
+
TERRAFORM_DATA_SOURCES_PATTERN,
|
|
97
|
+
[
|
|
98
|
+
'language hcl',
|
|
99
|
+
'`data "aws_region" "current" {}` => `data "aws_region" "current" {}',
|
|
100
|
+
'',
|
|
101
|
+
'locals {',
|
|
102
|
+
' # Local dev server origins permitted to complete the sign-in redirect',
|
|
103
|
+
' local_callback_urls = [',
|
|
104
|
+
' "http://localhost:4200",',
|
|
105
|
+
' "http://localhost:4300"',
|
|
106
|
+
' ]',
|
|
107
|
+
'}`'
|
|
108
|
+
].join('\n')
|
|
109
|
+
],
|
|
110
|
+
[
|
|
111
|
+
[
|
|
112
|
+
'language hcl',
|
|
113
|
+
'`callback_urls = concat([',
|
|
114
|
+
' "http://localhost:4200",',
|
|
115
|
+
' "http://localhost:4300"',
|
|
116
|
+
' ], var.callback_urls)`'
|
|
117
|
+
].join('\n'),
|
|
118
|
+
[
|
|
119
|
+
'language hcl',
|
|
120
|
+
'`callback_urls = concat([',
|
|
121
|
+
' "http://localhost:4200",',
|
|
122
|
+
' "http://localhost:4300"',
|
|
123
|
+
' ], var.callback_urls)` => `callback_urls = concat(local.local_callback_urls, var.callback_urls)`'
|
|
124
|
+
].join('\n')
|
|
125
|
+
],
|
|
126
|
+
[
|
|
127
|
+
[
|
|
128
|
+
'language hcl',
|
|
129
|
+
'`logout_urls = concat([',
|
|
130
|
+
' "http://localhost:4200",',
|
|
131
|
+
' "http://localhost:4300"',
|
|
132
|
+
' ], var.logout_urls)`'
|
|
133
|
+
].join('\n'),
|
|
134
|
+
[
|
|
135
|
+
'language hcl',
|
|
136
|
+
'`logout_urls = concat([',
|
|
137
|
+
' "http://localhost:4200",',
|
|
138
|
+
' "http://localhost:4300"',
|
|
139
|
+
' ], var.logout_urls)` => `logout_urls = concat(local.local_callback_urls, var.logout_urls)`'
|
|
140
|
+
].join('\n')
|
|
141
|
+
],
|
|
142
|
+
[
|
|
143
|
+
[
|
|
144
|
+
'language hcl',
|
|
145
|
+
'`managed_rule_group_statement {',
|
|
146
|
+
' name = "AWSManagedRulesCommonRuleSet"',
|
|
147
|
+
' vendor_name = "AWS"',
|
|
148
|
+
' }`'
|
|
149
|
+
].join('\n'),
|
|
150
|
+
[
|
|
151
|
+
'language hcl',
|
|
152
|
+
'`managed_rule_group_statement {',
|
|
153
|
+
' name = "AWSManagedRulesCommonRuleSet"',
|
|
154
|
+
' vendor_name = "AWS"',
|
|
155
|
+
' }` => `managed_rule_group_statement {',
|
|
156
|
+
' name = "AWSManagedRulesCommonRuleSet"',
|
|
157
|
+
' vendor_name = "AWS"',
|
|
158
|
+
'',
|
|
159
|
+
` # ${SSRF_RULE_NAME} treats the loopback redirect_uri the`,
|
|
160
|
+
' # Hosted UI receives during local sign-in as an SSRF attempt. Counted',
|
|
161
|
+
' # only while a local callback URL is allowed; every other rule blocks.',
|
|
162
|
+
' dynamic "rule_action_override" {',
|
|
163
|
+
' for_each = length(local.local_callback_urls) > 0 ? [1] : []',
|
|
164
|
+
'',
|
|
165
|
+
' content {',
|
|
166
|
+
` name = "${SSRF_RULE_NAME}"`,
|
|
167
|
+
'',
|
|
168
|
+
' action_to_use {',
|
|
169
|
+
' count {}',
|
|
170
|
+
' }',
|
|
171
|
+
' }',
|
|
172
|
+
' }',
|
|
173
|
+
' }`'
|
|
174
|
+
].join('\n')
|
|
175
|
+
]
|
|
176
|
+
];
|
|
177
|
+
const divergedNextStep = (filePath)=>`${filePath}: the UserIdentity Web ACL has diverged from the generated shape - left untouched. To sign in against a local dev server, override ${SSRF_RULE_NAME} in the AWSManagedRulesCommonRuleSet rule group to Count, otherwise the Cognito Hosted UI returns 403 for localhost redirect URIs.`;
|
|
178
|
+
const migratedNextStep = (filePath)=>`${filePath}: ${SSRF_RULE_NAME} is now counted rather than blocked while a local callback URL is allowed, so signing in against a local dev server works. Redeploy to apply it.`;
|
|
179
|
+
export default async function migration(tree) {
|
|
180
|
+
const nextSteps = [];
|
|
181
|
+
for (const [filePath, migratedPattern, edits] of [
|
|
182
|
+
[
|
|
183
|
+
CDK_USER_IDENTITY_FILE,
|
|
184
|
+
CDK_MIGRATED_PATTERN,
|
|
185
|
+
CDK_EDITS
|
|
186
|
+
],
|
|
187
|
+
[
|
|
188
|
+
TERRAFORM_IDENTITY_FILE,
|
|
189
|
+
TERRAFORM_MIGRATED_PATTERN,
|
|
190
|
+
TERRAFORM_EDITS
|
|
191
|
+
]
|
|
192
|
+
]){
|
|
193
|
+
if (!tree.exists(filePath)) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (await matchGritQL(tree, filePath, migratedPattern)) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
// Confirm every edit site is present before writing any of them, so a file
|
|
200
|
+
// that only partly matches is left whole rather than half-edited.
|
|
201
|
+
const allSitesPresent = (await Promise.all(edits.map(([match])=>matchGritQL(tree, filePath, match)))).every(Boolean);
|
|
202
|
+
if (!allSitesPresent) {
|
|
203
|
+
nextSteps.push(divergedNextStep(filePath));
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
for (const [, rewrite] of edits){
|
|
207
|
+
await applyGritQL(tree, filePath, rewrite);
|
|
208
|
+
}
|
|
209
|
+
nextSteps.push(migratedNextStep(filePath));
|
|
210
|
+
}
|
|
211
|
+
await formatFilesInSubtree(tree);
|
|
212
|
+
return {
|
|
213
|
+
nextSteps
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
//# sourceMappingURL=migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/user-identity-waf-allow-localhost-callback/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { MigrationReturnObject, Tree } from '@nx/devkit';\nimport { applyGritQL, matchGritQL } from '../../../utils/ast';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants';\n\n/**\n * Count the EC2MetaDataSSRF_QUERYARGUMENTS WAF rule on the UserIdentity Web ACL\n *\n * `AWSManagedRulesCommonRuleSet` treats the loopback `redirect_uri` the Cognito\n * Hosted UI receives during local sign-in as an SSRF attempt, so sign-in against\n * a local dev server fails with an opaque 403 before the login page renders. The\n * rule is overridden to Count while a local callback URL is allowed; every other\n * rule in the group still blocks.\n *\n * The local callback URLs are lifted into a named constant so the override can be\n * derived from them rather than restating the condition.\n *\n * How to write a migration:\n * - https://nx.dev/docs/kb/migration-generators\n * - What `nextSteps` means: https://nx.dev/docs/reference/devkit/MigrationReturnObject\n *\n * Guardrails:\n * - Pattern-match before writing: skip files that have diverged from the shape\n * your generators produce and report them via `nextSteps`, rather than\n * clobbering the user's changes.\n * - Idempotent: re-running must be a no-op.\n * - Format what you write: finish with `formatFilesInSubtree` so the files your\n * migration wrote are formatted correctly.\n */\n\nconst CDK_USER_IDENTITY_FILE = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/core/user-identity.ts`;\nconst TERRAFORM_IDENTITY_FILE = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/core/user-identity/identity/identity.tf`;\n\nconst SSRF_RULE_NAME = 'EC2MetaDataSSRF_QUERYARGUMENTS';\n\n// Guards each file: present only once the override has been added.\nconst CDK_MIGRATED_PATTERN = `\\`ruleActionOverrides: allowsLocalCallback ? $_ : undefined\\``;\nconst TERRAFORM_MIGRATED_PATTERN = `language hcl\\n\\`name = \"${SSRF_RULE_NAME}\"\\``;\n\n// Every CDK edit site, matched structurally so formatting doesn't affect whether\n// the construct is recognised.\nconst CDK_CONSTANT_ANCHOR_PATTERN = \"`const WEB_CLIENT_ID = 'WebClient'`\";\n// Scoped to the `.concat` callsite so it can't also rewrite the array literal\n// inside the constant this migration inserts.\nconst CDK_LOCAL_URLS_PATTERN =\n \"`['http://localhost:4200', 'http://localhost:4300'].concat($rest)`\";\nconst CDK_CALL_PATTERN = '`this.createWebAcl($id, this.userPool)`';\nconst CDK_SIGNATURE_PATTERN =\n '`private createWebAcl = ($id: string, $pool: UserPool) => $body`';\nconst CDK_STATEMENT_PATTERN =\n \"`managedRuleGroupStatement: { name: 'AWSManagedRulesCommonRuleSet', vendorName: 'AWS' }`\";\n\nconst CDK_EDITS: Array<[string, string]> = [\n // Local callback URLs become a named constant the override can key off.\n [\n CDK_CONSTANT_ANCHOR_PATTERN,\n `${CDK_CONSTANT_ANCHOR_PATTERN} => \\`const WEB_CLIENT_ID = 'WebClient';\n\n/** Local dev server origins permitted to complete the sign-in redirect */\nconst LOCAL_CALLBACK_URLS = ['http://localhost:4200', 'http://localhost:4300']\\``,\n ],\n [\n CDK_LOCAL_URLS_PATTERN,\n `${CDK_LOCAL_URLS_PATTERN} => \\`LOCAL_CALLBACK_URLS.concat($rest)\\``,\n ],\n [\n CDK_CALL_PATTERN,\n `${CDK_CALL_PATTERN} => \\`this.createWebAcl($id, this.userPool, LOCAL_CALLBACK_URLS.length > 0)\\``,\n ],\n [\n CDK_SIGNATURE_PATTERN,\n `${CDK_SIGNATURE_PATTERN} => \\`private createWebAcl = (\n $id: string,\n $pool: UserPool,\n allowsLocalCallback: boolean\n ) => $body\\``,\n ],\n [\n CDK_STATEMENT_PATTERN,\n `${CDK_STATEMENT_PATTERN} => \\`managedRuleGroupStatement: {\n name: 'AWSManagedRulesCommonRuleSet',\n vendorName: 'AWS',\n // ${SSRF_RULE_NAME} treats the loopback redirect_uri the\n // Hosted UI receives during local sign-in as an SSRF attempt. Counted\n // only while a local callback URL is allowed; every other rule blocks.\n ruleActionOverrides: allowsLocalCallback\n ? [\n {\n name: '${SSRF_RULE_NAME}',\n actionToUse: { count: {} },\n },\n ]\n : undefined,\n }\\``,\n ],\n];\n\nconst TERRAFORM_DATA_SOURCES_PATTERN = [\n 'language hcl',\n '`data \"aws_region\" \"current\" {}`',\n].join('\\n');\n\nconst TERRAFORM_EDITS: Array<[string, string]> = [\n // Local callback URLs become a local the override can key off.\n [\n TERRAFORM_DATA_SOURCES_PATTERN,\n [\n 'language hcl',\n '`data \"aws_region\" \"current\" {}` => `data \"aws_region\" \"current\" {}',\n '',\n 'locals {',\n ' # Local dev server origins permitted to complete the sign-in redirect',\n ' local_callback_urls = [',\n ' \"http://localhost:4200\",',\n ' \"http://localhost:4300\"',\n ' ]',\n '}`',\n ].join('\\n'),\n ],\n [\n [\n 'language hcl',\n '`callback_urls = concat([',\n ' \"http://localhost:4200\",',\n ' \"http://localhost:4300\"',\n ' ], var.callback_urls)`',\n ].join('\\n'),\n [\n 'language hcl',\n '`callback_urls = concat([',\n ' \"http://localhost:4200\",',\n ' \"http://localhost:4300\"',\n ' ], var.callback_urls)` => `callback_urls = concat(local.local_callback_urls, var.callback_urls)`',\n ].join('\\n'),\n ],\n [\n [\n 'language hcl',\n '`logout_urls = concat([',\n ' \"http://localhost:4200\",',\n ' \"http://localhost:4300\"',\n ' ], var.logout_urls)`',\n ].join('\\n'),\n [\n 'language hcl',\n '`logout_urls = concat([',\n ' \"http://localhost:4200\",',\n ' \"http://localhost:4300\"',\n ' ], var.logout_urls)` => `logout_urls = concat(local.local_callback_urls, var.logout_urls)`',\n ].join('\\n'),\n ],\n [\n [\n 'language hcl',\n '`managed_rule_group_statement {',\n ' name = \"AWSManagedRulesCommonRuleSet\"',\n ' vendor_name = \"AWS\"',\n ' }`',\n ].join('\\n'),\n [\n 'language hcl',\n '`managed_rule_group_statement {',\n ' name = \"AWSManagedRulesCommonRuleSet\"',\n ' vendor_name = \"AWS\"',\n ' }` => `managed_rule_group_statement {',\n ' name = \"AWSManagedRulesCommonRuleSet\"',\n ' vendor_name = \"AWS\"',\n '',\n ` # ${SSRF_RULE_NAME} treats the loopback redirect_uri the`,\n ' # Hosted UI receives during local sign-in as an SSRF attempt. Counted',\n ' # only while a local callback URL is allowed; every other rule blocks.',\n ' dynamic \"rule_action_override\" {',\n ' for_each = length(local.local_callback_urls) > 0 ? [1] : []',\n '',\n ' content {',\n ` name = \"${SSRF_RULE_NAME}\"`,\n '',\n ' action_to_use {',\n ' count {}',\n ' }',\n ' }',\n ' }',\n ' }`',\n ].join('\\n'),\n ],\n];\n\nconst divergedNextStep = (filePath: string) =>\n `${filePath}: the UserIdentity Web ACL has diverged from the generated shape - left untouched. To sign in against a local dev server, override ${SSRF_RULE_NAME} in the AWSManagedRulesCommonRuleSet rule group to Count, otherwise the Cognito Hosted UI returns 403 for localhost redirect URIs.`;\n\nconst migratedNextStep = (filePath: string) =>\n `${filePath}: ${SSRF_RULE_NAME} is now counted rather than blocked while a local callback URL is allowed, so signing in against a local dev server works. Redeploy to apply it.`;\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const [filePath, migratedPattern, edits] of [\n [CDK_USER_IDENTITY_FILE, CDK_MIGRATED_PATTERN, CDK_EDITS],\n [TERRAFORM_IDENTITY_FILE, TERRAFORM_MIGRATED_PATTERN, TERRAFORM_EDITS],\n ] as const) {\n if (!tree.exists(filePath)) {\n // This workspace doesn't use this IaC provider, or has no UserIdentity.\n continue;\n }\n\n if (await matchGritQL(tree, filePath, migratedPattern)) {\n // Already migrated - silent skip keeps re-runs a no-op.\n continue;\n }\n\n // Confirm every edit site is present before writing any of them, so a file\n // that only partly matches is left whole rather than half-edited.\n const allSitesPresent = (\n await Promise.all(\n edits.map(([match]) => matchGritQL(tree, filePath, match)),\n )\n ).every(Boolean);\n\n if (!allSitesPresent) {\n nextSteps.push(divergedNextStep(filePath));\n continue;\n }\n\n for (const [, rewrite] of edits) {\n await applyGritQL(tree, filePath, rewrite);\n }\n nextSteps.push(migratedNextStep(filePath));\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["applyGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","CDK_USER_IDENTITY_FILE","TERRAFORM_IDENTITY_FILE","SSRF_RULE_NAME","CDK_MIGRATED_PATTERN","TERRAFORM_MIGRATED_PATTERN","CDK_CONSTANT_ANCHOR_PATTERN","CDK_LOCAL_URLS_PATTERN","CDK_CALL_PATTERN","CDK_SIGNATURE_PATTERN","CDK_STATEMENT_PATTERN","CDK_EDITS","TERRAFORM_DATA_SOURCES_PATTERN","join","TERRAFORM_EDITS","divergedNextStep","filePath","migratedNextStep","migration","tree","nextSteps","migratedPattern","edits","exists","allSitesPresent","Promise","all","map","match","every","Boolean","push","rewrite"],"mappings":"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"}
|
|
@@ -108,6 +108,8 @@ The following list of generators are what is currently available in the \`@aws/n
|
|
|
108
108
|
|
|
109
109
|
- **py#agent**: Add an AI Agent to a Python project
|
|
110
110
|
|
|
111
|
+
- **smithy#project**: Generate a Smithy model project, either defining a service or a library of reusable shapes
|
|
112
|
+
|
|
111
113
|
- **terraform#project**: Generates a Terraform project
|
|
112
114
|
|
|
113
115
|
- **ts#docs**: Generates a documentation site
|
|
@@ -1,5 +1,102 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
+
exports[`smithyProjectGenerator > shape libraries > should generate a shape library without a service > shapes-build.Dockerfile 1`] = `
|
|
4
|
+
"FROM public.ecr.aws/docker/library/node:24 AS builder
|
|
5
|
+
ENV CI=true
|
|
6
|
+
|
|
7
|
+
# Output directory
|
|
8
|
+
RUN mkdir /out
|
|
9
|
+
|
|
10
|
+
# Install Smithy CLI
|
|
11
|
+
# https://smithy.io/2.0/guides/smithy-cli/cli_installation.html
|
|
12
|
+
WORKDIR /smithy
|
|
13
|
+
ARG TARGETPLATFORM
|
|
14
|
+
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then ARCH="aarch64"; else ARCH="x86_64"; fi && \\
|
|
15
|
+
mkdir -p smithy-install/smithy && \\
|
|
16
|
+
curl -L https://github.com/smithy-lang/smithy/releases/download/1.61.0/smithy-cli-linux-$ARCH.zip -o smithy-install/smithy-cli-linux-$ARCH.zip && \\
|
|
17
|
+
unzip -qo smithy-install/smithy-cli-linux-$ARCH.zip -d smithy-install && \\
|
|
18
|
+
mv smithy-install/smithy-cli-linux-$ARCH/* smithy-install/smithy
|
|
19
|
+
RUN smithy-install/smithy/install
|
|
20
|
+
|
|
21
|
+
# Copy project files
|
|
22
|
+
WORKDIR /project
|
|
23
|
+
COPY smithy-build.json .
|
|
24
|
+
COPY src src
|
|
25
|
+
|
|
26
|
+
# Validate the model and assemble it into a single JSON model file
|
|
27
|
+
RUN --mount=type=cache,target=/root/.m2/repository,id=maven-cache \\
|
|
28
|
+
smithy build
|
|
29
|
+
|
|
30
|
+
# Copy the assembled model to the output location
|
|
31
|
+
RUN mkdir -p /out/model
|
|
32
|
+
RUN cp /project/build/smithy/source/model/model.json /out/model/model.json
|
|
33
|
+
|
|
34
|
+
# Export the /out directory
|
|
35
|
+
FROM scratch AS export
|
|
36
|
+
COPY --from=builder /out /
|
|
37
|
+
"
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
exports[`smithyProjectGenerator > shape libraries > should generate a shape library without a service > shapes-main.smithy 1`] = `
|
|
41
|
+
"$version: "2.0"
|
|
42
|
+
|
|
43
|
+
namespace proj
|
|
44
|
+
|
|
45
|
+
/// TODO: define the shapes you would like to share between your Smithy projects
|
|
46
|
+
structure ExampleShape {
|
|
47
|
+
@required
|
|
48
|
+
id: String
|
|
49
|
+
|
|
50
|
+
description: String
|
|
51
|
+
}
|
|
52
|
+
"
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
exports[`smithyProjectGenerator > shape libraries > should generate a shape library without a service > shapes-project.json 1`] = `
|
|
56
|
+
"{
|
|
57
|
+
"name": "@proj/test-shapes",
|
|
58
|
+
"$schema": "../node_modules/nx/schemas/project-schema.json",
|
|
59
|
+
"sourceRoot": "test-shapes/src",
|
|
60
|
+
"projectType": "library",
|
|
61
|
+
"metadata": {
|
|
62
|
+
"generator": "smithy#project",
|
|
63
|
+
"smithyType": "shapes",
|
|
64
|
+
"namespace": "proj"
|
|
65
|
+
},
|
|
66
|
+
"targets": {
|
|
67
|
+
"build": {
|
|
68
|
+
"dependsOn": ["compile"]
|
|
69
|
+
},
|
|
70
|
+
"compile": {
|
|
71
|
+
"cache": true,
|
|
72
|
+
"outputs": ["{workspaceRoot}/dist/{projectRoot}/build"],
|
|
73
|
+
"executor": "nx:run-commands",
|
|
74
|
+
"options": {
|
|
75
|
+
"commands": [
|
|
76
|
+
"rimraf dist/{projectRoot}/build",
|
|
77
|
+
"make-dir dist/{projectRoot}/build",
|
|
78
|
+
"docker build -f {projectRoot}/build.Dockerfile --build-context workspace=. --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}"
|
|
79
|
+
],
|
|
80
|
+
"parallel": false,
|
|
81
|
+
"cwd": "{workspaceRoot}"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
"
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
exports[`smithyProjectGenerator > shape libraries > should generate a shape library without a service > shapes-smithy-build.json 1`] = `
|
|
90
|
+
"{
|
|
91
|
+
"version": "1.0",
|
|
92
|
+
"sources": ["src/"],
|
|
93
|
+
"maven": {
|
|
94
|
+
"dependencies": ["software.amazon.smithy:smithy-model:1.61.0"]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
"
|
|
98
|
+
`;
|
|
99
|
+
|
|
3
100
|
exports[`smithyProjectGenerator > should generate smithy project with all custom options > all-custom-main.smithy 1`] = `
|
|
4
101
|
"$version: "2.0"
|
|
5
102
|
|
|
@@ -31,6 +128,8 @@ exports[`smithyProjectGenerator > should generate smithy project with all custom
|
|
|
31
128
|
"projectType": "library",
|
|
32
129
|
"metadata": {
|
|
33
130
|
"generator": "smithy#project",
|
|
131
|
+
"smithyType": "service",
|
|
132
|
+
"namespace": "com.mycompany.api",
|
|
34
133
|
"apiName": "test-api"
|
|
35
134
|
},
|
|
36
135
|
"targets": {
|
|
@@ -45,7 +144,7 @@ exports[`smithyProjectGenerator > should generate smithy project with all custom
|
|
|
45
144
|
"commands": [
|
|
46
145
|
"rimraf dist/{projectRoot}/build",
|
|
47
146
|
"make-dir dist/{projectRoot}/build",
|
|
48
|
-
"docker build -f {projectRoot}/build.Dockerfile --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}"
|
|
147
|
+
"docker build -f {projectRoot}/build.Dockerfile --build-context workspace=. --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}"
|
|
49
148
|
],
|
|
50
149
|
"parallel": false,
|
|
51
150
|
"cwd": "{workspaceRoot}"
|
|
@@ -64,6 +163,8 @@ exports[`smithyProjectGenerator > should generate smithy project with custom dir
|
|
|
64
163
|
"projectType": "library",
|
|
65
164
|
"metadata": {
|
|
66
165
|
"generator": "smithy#project",
|
|
166
|
+
"smithyType": "service",
|
|
167
|
+
"namespace": "proj",
|
|
67
168
|
"apiName": "test-api"
|
|
68
169
|
},
|
|
69
170
|
"targets": {
|
|
@@ -78,7 +179,7 @@ exports[`smithyProjectGenerator > should generate smithy project with custom dir
|
|
|
78
179
|
"commands": [
|
|
79
180
|
"rimraf dist/{projectRoot}/build",
|
|
80
181
|
"make-dir dist/{projectRoot}/build",
|
|
81
|
-
"docker build -f {projectRoot}/build.Dockerfile --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}"
|
|
182
|
+
"docker build -f {projectRoot}/build.Dockerfile --build-context workspace=. --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}"
|
|
82
183
|
],
|
|
83
184
|
"parallel": false,
|
|
84
185
|
"cwd": "{workspaceRoot}"
|
|
@@ -288,6 +389,8 @@ exports[`smithyProjectGenerator > should generate smithy project with default op
|
|
|
288
389
|
"projectType": "library",
|
|
289
390
|
"metadata": {
|
|
290
391
|
"generator": "smithy#project",
|
|
392
|
+
"smithyType": "service",
|
|
393
|
+
"namespace": "proj",
|
|
291
394
|
"apiName": "test-api"
|
|
292
395
|
},
|
|
293
396
|
"targets": {
|
|
@@ -302,7 +405,7 @@ exports[`smithyProjectGenerator > should generate smithy project with default op
|
|
|
302
405
|
"commands": [
|
|
303
406
|
"rimraf dist/{projectRoot}/build",
|
|
304
407
|
"make-dir dist/{projectRoot}/build",
|
|
305
|
-
"docker build -f {projectRoot}/build.Dockerfile --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}"
|
|
408
|
+
"docker build -f {projectRoot}/build.Dockerfile --build-context workspace=. --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}"
|
|
306
409
|
],
|
|
307
410
|
"parallel": false,
|
|
308
411
|
"cwd": "{workspaceRoot}"
|
|
@@ -350,6 +453,8 @@ exports[`smithyProjectGenerator > should generate smithy project with subdirecto
|
|
|
350
453
|
"projectType": "library",
|
|
351
454
|
"metadata": {
|
|
352
455
|
"generator": "smithy#project",
|
|
456
|
+
"smithyType": "service",
|
|
457
|
+
"namespace": "proj",
|
|
353
458
|
"apiName": "test-api"
|
|
354
459
|
},
|
|
355
460
|
"targets": {
|
|
@@ -364,7 +469,7 @@ exports[`smithyProjectGenerator > should generate smithy project with subdirecto
|
|
|
364
469
|
"commands": [
|
|
365
470
|
"rimraf dist/{projectRoot}/build",
|
|
366
471
|
"make-dir dist/{projectRoot}/build",
|
|
367
|
-
"docker build -f {projectRoot}/build.Dockerfile --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}"
|
|
472
|
+
"docker build -f {projectRoot}/build.Dockerfile --build-context workspace=. --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}"
|
|
368
473
|
],
|
|
369
474
|
"parallel": false,
|
|
370
475
|
"cwd": "{workspaceRoot}"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
FROM public.ecr.aws/docker/library/node:24 AS builder
|
|
2
|
+
ENV CI=true
|
|
3
|
+
|
|
4
|
+
# Output directory
|
|
5
|
+
RUN mkdir /out
|
|
6
|
+
|
|
7
|
+
# Install Smithy CLI
|
|
8
|
+
# https://smithy.io/2.0/guides/smithy-cli/cli_installation.html
|
|
9
|
+
WORKDIR /smithy
|
|
10
|
+
ARG TARGETPLATFORM
|
|
11
|
+
RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then ARCH="aarch64"; else ARCH="x86_64"; fi && \
|
|
12
|
+
mkdir -p smithy-install/smithy && \
|
|
13
|
+
curl -L https://github.com/smithy-lang/smithy/releases/download/1.61.0/smithy-cli-linux-$ARCH.zip -o smithy-install/smithy-cli-linux-$ARCH.zip && \
|
|
14
|
+
unzip -qo smithy-install/smithy-cli-linux-$ARCH.zip -d smithy-install && \
|
|
15
|
+
mv smithy-install/smithy-cli-linux-$ARCH/* smithy-install/smithy
|
|
16
|
+
RUN smithy-install/smithy/install
|
|
17
|
+
|
|
18
|
+
# Copy project files
|
|
19
|
+
WORKDIR /project
|
|
20
|
+
COPY smithy-build.json .
|
|
21
|
+
COPY src src
|
|
22
|
+
|
|
23
|
+
# Validate the model and assemble it into a single JSON model file
|
|
24
|
+
RUN --mount=type=cache,target=/root/.m2/repository,id=maven-cache \
|
|
25
|
+
smithy build
|
|
26
|
+
|
|
27
|
+
# Copy the assembled model to the output location
|
|
28
|
+
RUN mkdir -p /out/model
|
|
29
|
+
RUN cp /project/build/smithy/source/model/model.json /out/model/model.json
|
|
30
|
+
|
|
31
|
+
# Export the /out directory
|
|
32
|
+
FROM scratch AS export
|
|
33
|
+
COPY --from=builder /out /
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
*/ import { addProjectConfiguration, generateFiles, joinPathFragments } from "@nx/devkit";
|
|
4
|
+
*/ import { addProjectConfiguration, generateFiles, joinPathFragments, OverwriteStrategy } from "@nx/devkit";
|
|
5
5
|
import { getTsLibDetails } from "../../ts/lib/generator.js";
|
|
6
6
|
import { resolveContainers } from "../../utils/containers.js";
|
|
7
7
|
import { formatFilesInSubtree } from "../../utils/format.js";
|
|
@@ -15,6 +15,7 @@ export const SMITHY_PROJECT_GENERATOR_INFO = getGeneratorInfo(import.meta.filena
|
|
|
15
15
|
export const smithyProjectGenerator = async (tree, options)=>{
|
|
16
16
|
const cmd = new FsCommands(tree);
|
|
17
17
|
const containers = await resolveContainers(tree, 'inherit');
|
|
18
|
+
const type = options.type ?? 'service';
|
|
18
19
|
// Create project.json
|
|
19
20
|
const { fullyQualifiedName, dir } = getTsLibDetails(tree, options);
|
|
20
21
|
if (!projectExists(tree, fullyQualifiedName)) {
|
|
@@ -39,7 +40,10 @@ export const smithyProjectGenerator = async (tree, options)=>{
|
|
|
39
40
|
commands: [
|
|
40
41
|
cmd.rm('dist/{projectRoot}/build'),
|
|
41
42
|
cmd.mkdir('dist/{projectRoot}/build'),
|
|
42
|
-
|
|
43
|
+
// The workspace build context lets a project's Dockerfile copy in
|
|
44
|
+
// the built models of shape libraries it depends on. Commands run
|
|
45
|
+
// from the workspace root, so it is the current directory.
|
|
46
|
+
`${containers} build -f {projectRoot}/build.Dockerfile --build-context workspace=. --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}`
|
|
43
47
|
],
|
|
44
48
|
parallel: false,
|
|
45
49
|
cwd: '{workspaceRoot}'
|
|
@@ -53,14 +57,21 @@ export const smithyProjectGenerator = async (tree, options)=>{
|
|
|
53
57
|
const serviceNameKebabCase = toKebabCase(serviceName);
|
|
54
58
|
const scope = getNpmScope(tree);
|
|
55
59
|
const namespace = options.namespace ?? toKebabCase(scope).replace(/-/g, '.');
|
|
56
|
-
generateFiles(tree, joinPathFragments(import.meta.dirname, 'files'), dir, {
|
|
60
|
+
generateFiles(tree, joinPathFragments(import.meta.dirname, 'files', type), dir, {
|
|
57
61
|
namespace,
|
|
58
62
|
serviceNameClassName,
|
|
59
63
|
serviceNameKebabCase,
|
|
60
64
|
scope
|
|
65
|
+
}, {
|
|
66
|
+
// Smithy models are user-owned — a re-run must not discard edits
|
|
67
|
+
overwriteStrategy: OverwriteStrategy.KeepExisting
|
|
61
68
|
});
|
|
62
69
|
addGeneratorMetadata(tree, fullyQualifiedName, SMITHY_PROJECT_GENERATOR_INFO, {
|
|
63
|
-
|
|
70
|
+
smithyType: type,
|
|
71
|
+
namespace,
|
|
72
|
+
...type === 'service' ? {
|
|
73
|
+
apiName: options.name
|
|
74
|
+
} : {}
|
|
64
75
|
});
|
|
65
76
|
await addGeneratorMetricsIfApplicable(tree, [
|
|
66
77
|
SMITHY_PROJECT_GENERATOR_INFO
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/smithy/project/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n addProjectConfiguration,\n type GeneratorCallback,\n generateFiles,\n joinPathFragments,\n type Tree,\n} from '@nx/devkit';\nimport { getTsLibDetails } from '../../ts/lib/generator';\nimport { resolveContainers } from '../../utils/containers';\nimport { formatFilesInSubtree } from '../../utils/format';\nimport { FsCommands } from '../../utils/fs';\nimport { installDependencies } from '../../utils/install';\nimport { addGeneratorMetricsIfApplicable } from '../../utils/metrics';\nimport { toClassName, toKebabCase } from '../../utils/names';\nimport { getNpmScope } from '../../utils/npm-scope';\nimport {\n addGeneratorMetadata,\n getGeneratorInfo,\n type NxGeneratorInfo,\n projectExists,\n} from '../../utils/nx';\nimport type { SmithyProjectGeneratorSchema } from './schema';\n\nexport const SMITHY_PROJECT_GENERATOR_INFO: NxGeneratorInfo = getGeneratorInfo(\n import.meta.filename,\n);\n\nexport const smithyProjectGenerator = async (\n tree: Tree,\n options: SmithyProjectGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const cmd = new FsCommands(tree);\n const containers = await resolveContainers(tree, 'inherit');\n\n // Create project.json\n const { fullyQualifiedName, dir } = getTsLibDetails(tree, options);\n\n if (!projectExists(tree, fullyQualifiedName)) {\n addProjectConfiguration(tree, fullyQualifiedName, {\n name: fullyQualifiedName,\n root: dir,\n sourceRoot: joinPathFragments(dir, 'src'),\n projectType: 'library',\n targets: {\n build: {\n dependsOn: ['compile'],\n },\n compile: {\n cache: true,\n outputs: ['{workspaceRoot}/dist/{projectRoot}/build'],\n executor: 'nx:run-commands',\n options: {\n commands: [\n cmd.rm('dist/{projectRoot}/build'),\n cmd.mkdir('dist/{projectRoot}/build'),\n `${containers} build -f {projectRoot}/build.Dockerfile --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}`,\n ],\n parallel: false,\n cwd: '{workspaceRoot}',\n },\n },\n },\n });\n }\n\n const serviceName = options.serviceName ?? options.name;\n const serviceNameClassName = toClassName(serviceName);\n const serviceNameKebabCase = toKebabCase(serviceName);\n const scope = getNpmScope(tree);\n const namespace = options.namespace ?? toKebabCase(scope).replace(/-/g, '.');\n\n generateFiles(tree
|
|
1
|
+
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/smithy/project/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n addProjectConfiguration,\n type GeneratorCallback,\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type Tree,\n} from '@nx/devkit';\nimport { getTsLibDetails } from '../../ts/lib/generator';\nimport { resolveContainers } from '../../utils/containers';\nimport { formatFilesInSubtree } from '../../utils/format';\nimport { FsCommands } from '../../utils/fs';\nimport { installDependencies } from '../../utils/install';\nimport { addGeneratorMetricsIfApplicable } from '../../utils/metrics';\nimport { toClassName, toKebabCase } from '../../utils/names';\nimport { getNpmScope } from '../../utils/npm-scope';\nimport {\n addGeneratorMetadata,\n getGeneratorInfo,\n type NxGeneratorInfo,\n projectExists,\n} from '../../utils/nx';\nimport type { SmithyProjectGeneratorSchema } from './schema';\n\nexport const SMITHY_PROJECT_GENERATOR_INFO: NxGeneratorInfo = getGeneratorInfo(\n import.meta.filename,\n);\n\nexport const smithyProjectGenerator = async (\n tree: Tree,\n options: SmithyProjectGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const cmd = new FsCommands(tree);\n const containers = await resolveContainers(tree, 'inherit');\n const type = options.type ?? 'service';\n\n // Create project.json\n const { fullyQualifiedName, dir } = getTsLibDetails(tree, options);\n\n if (!projectExists(tree, fullyQualifiedName)) {\n addProjectConfiguration(tree, fullyQualifiedName, {\n name: fullyQualifiedName,\n root: dir,\n sourceRoot: joinPathFragments(dir, 'src'),\n projectType: 'library',\n targets: {\n build: {\n dependsOn: ['compile'],\n },\n compile: {\n cache: true,\n outputs: ['{workspaceRoot}/dist/{projectRoot}/build'],\n executor: 'nx:run-commands',\n options: {\n commands: [\n cmd.rm('dist/{projectRoot}/build'),\n cmd.mkdir('dist/{projectRoot}/build'),\n // The workspace build context lets a project's Dockerfile copy in\n // the built models of shape libraries it depends on. Commands run\n // from the workspace root, so it is the current directory.\n `${containers} build -f {projectRoot}/build.Dockerfile --build-context workspace=. --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}`,\n ],\n parallel: false,\n cwd: '{workspaceRoot}',\n },\n },\n },\n });\n }\n\n const serviceName = options.serviceName ?? options.name;\n const serviceNameClassName = toClassName(serviceName);\n const serviceNameKebabCase = toKebabCase(serviceName);\n const scope = getNpmScope(tree);\n const namespace = options.namespace ?? toKebabCase(scope).replace(/-/g, '.');\n\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', type),\n dir,\n {\n namespace,\n serviceNameClassName,\n serviceNameKebabCase,\n scope,\n },\n {\n // Smithy models are user-owned — a re-run must not discard edits\n overwriteStrategy: OverwriteStrategy.KeepExisting,\n },\n );\n\n addGeneratorMetadata(\n tree,\n fullyQualifiedName,\n SMITHY_PROJECT_GENERATOR_INFO,\n {\n smithyType: type,\n namespace,\n ...(type === 'service' ? { apiName: options.name } : {}),\n },\n );\n\n await addGeneratorMetricsIfApplicable(tree, [SMITHY_PROJECT_GENERATOR_INFO]);\n\n await formatFilesInSubtree(tree);\n return () =>\n installDependencies(tree, options.preferInstallDependencies, {\n languages: ['typescript'],\n });\n};\n\nexport default smithyProjectGenerator;\n"],"names":["addProjectConfiguration","generateFiles","joinPathFragments","OverwriteStrategy","getTsLibDetails","resolveContainers","formatFilesInSubtree","FsCommands","installDependencies","addGeneratorMetricsIfApplicable","toClassName","toKebabCase","getNpmScope","addGeneratorMetadata","getGeneratorInfo","projectExists","SMITHY_PROJECT_GENERATOR_INFO","filename","smithyProjectGenerator","tree","options","cmd","containers","type","fullyQualifiedName","dir","name","root","sourceRoot","projectType","targets","build","dependsOn","compile","cache","outputs","executor","commands","rm","mkdir","parallel","cwd","serviceName","serviceNameClassName","serviceNameKebabCase","scope","namespace","replace","dirname","overwriteStrategy","KeepExisting","smithyType","apiName","preferInstallDependencies","languages"],"mappings":"AAAA;;;CAGC,GACD,SACEA,uBAAuB,EAEvBC,aAAa,EACbC,iBAAiB,EACjBC,iBAAiB,QAEZ,aAAa;AACpB,SAASC,eAAe,QAAQ,4BAAyB;AACzD,SAASC,iBAAiB,QAAQ,4BAAyB;AAC3D,SAASC,oBAAoB,QAAQ,wBAAqB;AAC1D,SAASC,UAAU,QAAQ,oBAAiB;AAC5C,SAASC,mBAAmB,QAAQ,yBAAsB;AAC1D,SAASC,+BAA+B,QAAQ,yBAAsB;AACtE,SAASC,WAAW,EAAEC,WAAW,QAAQ,uBAAoB;AAC7D,SAASC,WAAW,QAAQ,2BAAwB;AACpD,SACEC,oBAAoB,EACpBC,gBAAgB,EAEhBC,aAAa,QACR,oBAAiB;AAGxB,OAAO,MAAMC,gCAAiDF,iBAC5D,YAAYG,QAAQ,EACpB;AAEF,OAAO,MAAMC,yBAAyB,OACpCC,MACAC;IAEA,MAAMC,MAAM,IAAId,WAAWY;IAC3B,MAAMG,aAAa,MAAMjB,kBAAkBc,MAAM;IACjD,MAAMI,OAAOH,QAAQG,IAAI,IAAI;IAE7B,sBAAsB;IACtB,MAAM,EAAEC,kBAAkB,EAAEC,GAAG,EAAE,GAAGrB,gBAAgBe,MAAMC;IAE1D,IAAI,CAACL,cAAcI,MAAMK,qBAAqB;QAC5CxB,wBAAwBmB,MAAMK,oBAAoB;YAChDE,MAAMF;YACNG,MAAMF;YACNG,YAAY1B,kBAAkBuB,KAAK;YACnCI,aAAa;YACbC,SAAS;gBACPC,OAAO;oBACLC,WAAW;wBAAC;qBAAU;gBACxB;gBACAC,SAAS;oBACPC,OAAO;oBACPC,SAAS;wBAAC;qBAA2C;oBACrDC,UAAU;oBACVhB,SAAS;wBACPiB,UAAU;4BACRhB,IAAIiB,EAAE,CAAC;4BACPjB,IAAIkB,KAAK,CAAC;4BACV,kEAAkE;4BAClE,kEAAkE;4BAClE,2DAA2D;4BAC3D,GAAGjB,WAAW,oJAAoJ,CAAC;yBACpK;wBACDkB,UAAU;wBACVC,KAAK;oBACP;gBACF;YACF;QACF;IACF;IAEA,MAAMC,cAActB,QAAQsB,WAAW,IAAItB,QAAQM,IAAI;IACvD,MAAMiB,uBAAuBjC,YAAYgC;IACzC,MAAME,uBAAuBjC,YAAY+B;IACzC,MAAMG,QAAQjC,YAAYO;IAC1B,MAAM2B,YAAY1B,QAAQ0B,SAAS,IAAInC,YAAYkC,OAAOE,OAAO,CAAC,MAAM;IAExE9C,cACEkB,MACAjB,kBAAkB,YAAY8C,OAAO,EAAE,SAASzB,OAChDE,KACA;QACEqB;QACAH;QACAC;QACAC;IACF,GACA;QACE,iEAAiE;QACjEI,mBAAmB9C,kBAAkB+C,YAAY;IACnD;IAGFrC,qBACEM,MACAK,oBACAR,+BACA;QACEmC,YAAY5B;QACZuB;QACA,GAAIvB,SAAS,YAAY;YAAE6B,SAAShC,QAAQM,IAAI;QAAC,IAAI,CAAC,CAAC;IACzD;IAGF,MAAMjB,gCAAgCU,MAAM;QAACH;KAA8B;IAE3E,MAAMV,qBAAqBa;IAC3B,OAAO,IACLX,oBAAoBW,MAAMC,QAAQiC,yBAAyB,EAAE;YAC3DC,WAAW;gBAAC;aAAa;QAC3B;AACJ,EAAE;AAEF,eAAepC,uBAAuB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/smithy/project/schema.d.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nexport interface SmithyProjectGeneratorSchema {\n name: string;\n serviceName?: string;\n namespace?: string;\n directory?: string;\n subDirectory?: string;\n preferInstallDependencies?: boolean;\n}\n"],"names":[],"mappings":"AAAA;;;CAGC,GACD,
|
|
1
|
+
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/smithy/project/schema.d.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nexport interface SmithyProjectGeneratorSchema {\n name: string;\n type?: 'service' | 'shapes';\n serviceName?: string;\n namespace?: string;\n directory?: string;\n subDirectory?: string;\n preferInstallDependencies?: boolean;\n}\n"],"names":[],"mappings":"AAAA;;;CAGC,GACD,WAQC"}
|
|
@@ -15,9 +15,31 @@
|
|
|
15
15
|
"x-priority": "important",
|
|
16
16
|
"x-prompt": "What would you like to call your Smithy project?"
|
|
17
17
|
},
|
|
18
|
+
"type": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "The type of Smithy project to create. Choose between service (a model with a service shape, ready for an implementation) and shapes (a shape library of reusable shapes, shared between multiple Smithy projects).",
|
|
21
|
+
"default": "service",
|
|
22
|
+
"enum": ["service", "shapes"],
|
|
23
|
+
"x-priority": "important",
|
|
24
|
+
"x-prompt": {
|
|
25
|
+
"message": "What type of Smithy project would you like to create?",
|
|
26
|
+
"type": "list",
|
|
27
|
+
"items": [
|
|
28
|
+
{
|
|
29
|
+
"value": "service",
|
|
30
|
+
"label": "service (a model which defines a service and its operations)"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"value": "shapes",
|
|
34
|
+
"label": "shapes (a library of reusable shapes, shared between Smithy projects)"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"default": "service"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
18
40
|
"serviceName": {
|
|
19
41
|
"type": "string",
|
|
20
|
-
"description": "The name of your Smithy service. Uses the supplied name by default.",
|
|
42
|
+
"description": "The name of your Smithy service. Uses the supplied name by default. Not applicable to shape libraries.",
|
|
21
43
|
"x-prompt": "What name would you like your Smithy Service to have? i.e: MyService"
|
|
22
44
|
},
|
|
23
45
|
"namespace": {
|
|
@@ -1743,6 +1743,8 @@ exports[`tsSmithyApiGenerator > should generate smithy ts api with default optio
|
|
|
1743
1743
|
"projectType": "library",
|
|
1744
1744
|
"metadata": {
|
|
1745
1745
|
"generator": "smithy#project",
|
|
1746
|
+
"smithyType": "service",
|
|
1747
|
+
"namespace": "proj",
|
|
1746
1748
|
"apiName": "test-api-model",
|
|
1747
1749
|
"backendProject": "@proj/test-api"
|
|
1748
1750
|
},
|
|
@@ -1758,7 +1760,7 @@ exports[`tsSmithyApiGenerator > should generate smithy ts api with default optio
|
|
|
1758
1760
|
"commands": [
|
|
1759
1761
|
"rimraf dist/{projectRoot}/build",
|
|
1760
1762
|
"make-dir dist/{projectRoot}/build",
|
|
1761
|
-
"docker build -f {projectRoot}/build.Dockerfile --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}"
|
|
1763
|
+
"docker build -f {projectRoot}/build.Dockerfile --build-context workspace=. --target export --output type=local,dest=dist/{projectRoot}/build {projectRoot}"
|
|
1762
1764
|
],
|
|
1763
1765
|
"parallel": false,
|
|
1764
1766
|
"cwd": "{workspaceRoot}"
|
|
@@ -125,6 +125,9 @@ import { suppressRules } from './checkov.js';
|
|
|
125
125
|
|
|
126
126
|
const WEB_CLIENT_ID = 'WebClient';
|
|
127
127
|
|
|
128
|
+
/** Local dev server origins permitted to complete the sign-in redirect */
|
|
129
|
+
const LOCAL_CALLBACK_URLS = ['http://localhost:4200', 'http://localhost:4300'];
|
|
130
|
+
|
|
128
131
|
export interface UserIdentityProps {
|
|
129
132
|
/**
|
|
130
133
|
* Whether to enable AWS WAFv2 with the default managed ruleset
|
|
@@ -160,7 +163,11 @@ export class UserIdentity extends Construct {
|
|
|
160
163
|
this.userPool = this.createUserPool();
|
|
161
164
|
|
|
162
165
|
if (enableWaf) {
|
|
163
|
-
this.webAcl = this.createWebAcl(
|
|
166
|
+
this.webAcl = this.createWebAcl(
|
|
167
|
+
id,
|
|
168
|
+
this.userPool,
|
|
169
|
+
LOCAL_CALLBACK_URLS.length > 0,
|
|
170
|
+
);
|
|
164
171
|
}
|
|
165
172
|
this.userPoolDomain = this.createUserPoolDomain(this.userPool);
|
|
166
173
|
this.userPoolClient = this.createUserPoolClient(this.userPool);
|
|
@@ -248,7 +255,11 @@ export class UserIdentity extends Construct {
|
|
|
248
255
|
return userPool;
|
|
249
256
|
};
|
|
250
257
|
|
|
251
|
-
private createWebAcl = (
|
|
258
|
+
private createWebAcl = (
|
|
259
|
+
id: string,
|
|
260
|
+
userPool: UserPool,
|
|
261
|
+
allowsLocalCallback: boolean,
|
|
262
|
+
) => {
|
|
252
263
|
const webAcl = new CfnWebACL(this, 'WebAcl', {
|
|
253
264
|
defaultAction: { allow: {} },
|
|
254
265
|
scope: 'REGIONAL',
|
|
@@ -265,6 +276,17 @@ export class UserIdentity extends Construct {
|
|
|
265
276
|
managedRuleGroupStatement: {
|
|
266
277
|
name: 'AWSManagedRulesCommonRuleSet',
|
|
267
278
|
vendorName: 'AWS',
|
|
279
|
+
// EC2MetaDataSSRF_QUERYARGUMENTS treats the loopback redirect_uri the
|
|
280
|
+
// Hosted UI receives during local sign-in as an SSRF attempt. Counted
|
|
281
|
+
// only while a local callback URL is allowed; every other rule blocks.
|
|
282
|
+
ruleActionOverrides: allowsLocalCallback
|
|
283
|
+
? [
|
|
284
|
+
{
|
|
285
|
+
name: 'EC2MetaDataSSRF_QUERYARGUMENTS',
|
|
286
|
+
actionToUse: { count: {} },
|
|
287
|
+
},
|
|
288
|
+
]
|
|
289
|
+
: undefined,
|
|
268
290
|
},
|
|
269
291
|
},
|
|
270
292
|
visibilityConfig: {
|
|
@@ -335,7 +357,7 @@ export class UserIdentity extends Construct {
|
|
|
335
357
|
private createUserPoolClient = (userPool: UserPool) => {
|
|
336
358
|
const lazilyComputedCallbackUrls = Lazy.list({
|
|
337
359
|
produce: () =>
|
|
338
|
-
|
|
360
|
+
LOCAL_CALLBACK_URLS.concat(
|
|
339
361
|
Stack.of(this)
|
|
340
362
|
.node.findAll()
|
|
341
363
|
.filter(
|
|
@@ -36,6 +36,9 @@ import { suppressRules } from './checkov<% if (esm) { %>.js<% } %>';
|
|
|
36
36
|
|
|
37
37
|
const WEB_CLIENT_ID = 'WebClient';
|
|
38
38
|
|
|
39
|
+
/** Local dev server origins permitted to complete the sign-in redirect */
|
|
40
|
+
const LOCAL_CALLBACK_URLS = ['http://localhost:4200', 'http://localhost:4300'];
|
|
41
|
+
|
|
39
42
|
export interface UserIdentityProps {
|
|
40
43
|
/**
|
|
41
44
|
* Whether to enable AWS WAFv2 with the default managed ruleset
|
|
@@ -67,7 +70,11 @@ export class UserIdentity extends Construct {
|
|
|
67
70
|
this.userPool = this.createUserPool();
|
|
68
71
|
|
|
69
72
|
if (enableWaf) {
|
|
70
|
-
this.webAcl = this.createWebAcl(
|
|
73
|
+
this.webAcl = this.createWebAcl(
|
|
74
|
+
id,
|
|
75
|
+
this.userPool,
|
|
76
|
+
LOCAL_CALLBACK_URLS.length > 0
|
|
77
|
+
);
|
|
71
78
|
}
|
|
72
79
|
this.userPoolDomain = this.createUserPoolDomain(this.userPool);
|
|
73
80
|
this.userPoolClient = this.createUserPoolClient(this.userPool);
|
|
@@ -155,7 +162,11 @@ export class UserIdentity extends Construct {
|
|
|
155
162
|
return userPool;
|
|
156
163
|
};
|
|
157
164
|
|
|
158
|
-
private createWebAcl = (
|
|
165
|
+
private createWebAcl = (
|
|
166
|
+
id: string,
|
|
167
|
+
userPool: UserPool,
|
|
168
|
+
allowsLocalCallback: boolean
|
|
169
|
+
) => {
|
|
159
170
|
const webAcl = new CfnWebACL(this, 'WebAcl', {
|
|
160
171
|
defaultAction: { allow: {} },
|
|
161
172
|
scope: 'REGIONAL',
|
|
@@ -172,6 +183,17 @@ export class UserIdentity extends Construct {
|
|
|
172
183
|
managedRuleGroupStatement: {
|
|
173
184
|
name: 'AWSManagedRulesCommonRuleSet',
|
|
174
185
|
vendorName: 'AWS',
|
|
186
|
+
// EC2MetaDataSSRF_QUERYARGUMENTS treats the loopback redirect_uri the
|
|
187
|
+
// Hosted UI receives during local sign-in as an SSRF attempt. Counted
|
|
188
|
+
// only while a local callback URL is allowed; every other rule blocks.
|
|
189
|
+
ruleActionOverrides: allowsLocalCallback
|
|
190
|
+
? [
|
|
191
|
+
{
|
|
192
|
+
name: 'EC2MetaDataSSRF_QUERYARGUMENTS',
|
|
193
|
+
actionToUse: { count: {} },
|
|
194
|
+
},
|
|
195
|
+
]
|
|
196
|
+
: undefined,
|
|
175
197
|
},
|
|
176
198
|
},
|
|
177
199
|
visibilityConfig: {
|
|
@@ -242,7 +264,7 @@ export class UserIdentity extends Construct {
|
|
|
242
264
|
private createUserPoolClient = (userPool: UserPool) => {
|
|
243
265
|
const lazilyComputedCallbackUrls = Lazy.list({
|
|
244
266
|
produce: () =>
|
|
245
|
-
|
|
267
|
+
LOCAL_CALLBACK_URLS.concat(
|
|
246
268
|
Stack.of(this)
|
|
247
269
|
.node.findAll()
|
|
248
270
|
.filter((child): child is Distribution => child instanceof Distribution)
|
|
@@ -44,6 +44,14 @@ variable "logout_urls" {
|
|
|
44
44
|
data "aws_caller_identity" "current" {}
|
|
45
45
|
data "aws_region" "current" {}
|
|
46
46
|
|
|
47
|
+
locals {
|
|
48
|
+
# Local dev server origins permitted to complete the sign-in redirect
|
|
49
|
+
local_callback_urls = [
|
|
50
|
+
"http://localhost:4200",
|
|
51
|
+
"http://localhost:4300"
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
|
|
47
55
|
# Random suffix for resource names
|
|
48
56
|
resource "random_id" "unique_suffix" {
|
|
49
57
|
byte_length = 4
|
|
@@ -260,6 +268,21 @@ resource "aws_wafv2_web_acl" "user_pool_waf" {
|
|
|
260
268
|
managed_rule_group_statement {
|
|
261
269
|
name = "AWSManagedRulesCommonRuleSet"
|
|
262
270
|
vendor_name = "AWS"
|
|
271
|
+
|
|
272
|
+
# EC2MetaDataSSRF_QUERYARGUMENTS treats the loopback redirect_uri the
|
|
273
|
+
# Hosted UI receives during local sign-in as an SSRF attempt. Counted
|
|
274
|
+
# only while a local callback URL is allowed; every other rule blocks.
|
|
275
|
+
dynamic "rule_action_override" {
|
|
276
|
+
for_each = length(local.local_callback_urls) > 0 ? [1] : []
|
|
277
|
+
|
|
278
|
+
content {
|
|
279
|
+
name = "EC2MetaDataSSRF_QUERYARGUMENTS"
|
|
280
|
+
|
|
281
|
+
action_to_use {
|
|
282
|
+
count {}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
263
286
|
}
|
|
264
287
|
}
|
|
265
288
|
|
|
@@ -349,15 +372,9 @@ resource "aws_cognito_user_pool_client" "web_client" {
|
|
|
349
372
|
allowed_oauth_scopes = ["email", "openid", "profile"]
|
|
350
373
|
|
|
351
374
|
# OAuth-dependent URLs - set after OAuth configuration
|
|
352
|
-
callback_urls = concat(
|
|
353
|
-
"http://localhost:4200",
|
|
354
|
-
"http://localhost:4300"
|
|
355
|
-
], var.callback_urls)
|
|
375
|
+
callback_urls = concat(local.local_callback_urls, var.callback_urls)
|
|
356
376
|
|
|
357
|
-
logout_urls = concat(
|
|
358
|
-
"http://localhost:4200",
|
|
359
|
-
"http://localhost:4300"
|
|
360
|
-
], var.logout_urls)
|
|
377
|
+
logout_urls = concat(local.local_callback_urls, var.logout_urls)
|
|
361
378
|
|
|
362
379
|
# Security settings
|
|
363
380
|
prevent_user_existence_errors = "ENABLED"
|
/package/src/smithy/project/files/{build.Dockerfile.template → service/build.Dockerfile.template}
RENAMED
|
File without changes
|
/package/src/smithy/project/files/{smithy-build.json.template → service/smithy-build.json.template}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|