diff --git a/packages/aws-cdk-lib/core/lib/metadata-resource.ts b/packages/aws-cdk-lib/core/lib/metadata-resource.ts new file mode 100644 index 0000000000000..3d18f592b3054 --- /dev/null +++ b/packages/aws-cdk-lib/core/lib/metadata-resource.ts @@ -0,0 +1,22 @@ +/** + * Enumeration of metadata types used for tracking analytics in AWS CDK. + */ +export enum MetadataType { + /** + * Metadata type for construct properties. + * This is used to represent properties of CDK constructs. + */ + CONSTRUCT = 'aws:cdk:analytics:construct', + + /** + * Metadata type for method properties. + * This is used to track parameters and details of CDK method calls. + */ + METHOD = 'aws:cdk:analytics:method', + + /** + * Metadata type for feature flags. + * This is used to track analytics related to feature flags in the CDK. + */ + FEATURE_FLAG = 'aws:cdk:analytics:featureflag', +} diff --git a/packages/aws-cdk-lib/core/lib/private/metadata-resource.ts b/packages/aws-cdk-lib/core/lib/private/metadata-resource.ts index 494fd33bf7790..6c808bdc28872 100644 --- a/packages/aws-cdk-lib/core/lib/private/metadata-resource.ts +++ b/packages/aws-cdk-lib/core/lib/private/metadata-resource.ts @@ -1,11 +1,13 @@ import * as zlib from 'zlib'; import { Construct } from 'constructs'; import { ConstructInfo, constructInfoFromStack } from './runtime-info'; +import * as cxapi from '../../../cx-api'; import { RegionInfo } from '../../../region-info'; import { CfnCondition } from '../cfn-condition'; import { Fn } from '../cfn-fn'; import { Aws } from '../cfn-pseudo'; import { CfnResource } from '../cfn-resource'; +import { FeatureFlags } from '../feature-flags'; import { Lazy } from '../lazy'; import { Stack } from '../stack'; import { Token } from '../token'; @@ -17,11 +19,13 @@ export class MetadataResource extends Construct { constructor(scope: Stack, id: string) { super(scope, id); const metadataServiceExists = Token.isUnresolved(scope.region) || RegionInfo.get(scope.region).cdkMetadataResourceAvailable; + const enableAdditionalTelemtry = FeatureFlags.of(scope).isEnabled(cxapi.ENABLE_ADDITIONAL_METADATA_COLLECTION) ?? false; if (metadataServiceExists) { + const constructInfo = constructInfoFromStack(scope); const resource = new CfnResource(this, 'Default', { type: 'AWS::CDK::Metadata', properties: { - Analytics: Lazy.string({ produce: () => formatAnalytics(constructInfoFromStack(scope)) }), + Analytics: Lazy.string({ produce: () => formatAnalytics(constructInfo, enableAdditionalTelemtry) }), }, }); @@ -76,9 +80,16 @@ class Trie extends Map { } * * Exported/visible for ease of testing. */ -export function formatAnalytics(infos: ConstructInfo[]) { +export function formatAnalytics(infos: ConstructInfo[], enableAdditionalTelemtry: boolean = false) { const trie = new Trie(); - infos.forEach(info => insertFqnInTrie(`${info.version}!${info.fqn}`, trie)); + + // only append additional telemetry information to prefix encoding and gzip compress + // if feature flag is enabled; otherwise keep the old behaviour. + if (enableAdditionalTelemtry) { + infos.forEach(info => insertFqnInTrie(`${info.version}!${info.fqn}`, trie, info.metadata)); + } else { + infos.forEach(info => insertFqnInTrie(`${info.version}!${info.fqn}`, trie)); + } const plaintextEncodedConstructs = prefixEncodeTrie(trie); const compressedConstructsBuffer = zlib.gzipSync(Buffer.from(plaintextEncodedConstructs)); @@ -103,12 +114,17 @@ export function formatAnalytics(infos: ConstructInfo[]) { * Splits after non-alphanumeric characters (e.g., '.', '/') in the FQN * and insert each piece of the FQN in nested map (i.e., simple trie). */ -function insertFqnInTrie(fqn: string, trie: Trie) { +function insertFqnInTrie(fqn: string, trie: Trie, metadata?: Record[]) { for (const fqnPart of fqn.replace(/[^a-z0-9]/gi, '$& ').split(' ')) { const nextLevelTreeRef = trie.get(fqnPart) ?? new Trie(); trie.set(fqnPart, nextLevelTreeRef); trie = nextLevelTreeRef; } + + // if 'metadata' is defined, add it to end of Trie + if (metadata) { + trie.set(JSON.stringify(metadata), new Trie()); + } return trie; } diff --git a/packages/aws-cdk-lib/core/lib/private/runtime-info.ts b/packages/aws-cdk-lib/core/lib/private/runtime-info.ts index 6371b6bc84138..ef282c55fb5b5 100644 --- a/packages/aws-cdk-lib/core/lib/private/runtime-info.ts +++ b/packages/aws-cdk-lib/core/lib/private/runtime-info.ts @@ -1,5 +1,7 @@ -import { IConstruct } from 'constructs'; +import { IConstruct, MetadataEntry } from 'constructs'; import { App } from '../app'; +import { MetadataType } from '../metadata-resource'; +import { Resource } from '../resource'; import { Stack } from '../stack'; import { Stage } from '../stage'; import { IPolicyValidationPluginBeta1 } from '../validation'; @@ -24,6 +26,7 @@ const JSII_RUNTIME_SYMBOL = Symbol.for('jsii.rtti'); export interface ConstructInfo { readonly fqn: string; readonly version: string; + readonly metadata?: Record[]; } export function constructInfoFromConstruct(construct: IConstruct): ConstructInfo | undefined { @@ -32,7 +35,11 @@ export function constructInfoFromConstruct(construct: IConstruct): ConstructInfo && jsiiRuntimeInfo !== null && typeof jsiiRuntimeInfo.fqn === 'string' && typeof jsiiRuntimeInfo.version === 'string') { - return { fqn: jsiiRuntimeInfo.fqn, version: jsiiRuntimeInfo.version }; + return { + fqn: jsiiRuntimeInfo.fqn, + version: jsiiRuntimeInfo.version, + metadata: isResource(construct) ? redactTelemetryData(construct.node.metadata) : undefined, + }; } else if (jsiiRuntimeInfo) { // There is something defined, but doesn't match our expectations. Fail fast and hard. throw new Error(`malformed jsii runtime info for construct: '${construct.node.path}'`); @@ -40,6 +47,62 @@ export function constructInfoFromConstruct(construct: IConstruct): ConstructInfo return undefined; } +/** + * Filter for Construct, Method, and Feature flag metadata. Redact values from it. + * + * @param metadata a list of metadata entries + */ +export function redactTelemetryData(metadata: MetadataEntry[]): Record[] { + const validTypes = new Set([ + MetadataType.CONSTRUCT, + MetadataType.METHOD, + MetadataType.FEATURE_FLAG, + ]); + + return metadata + .filter((entry) => validTypes.has(entry.type as MetadataType)) + .map((entry) => ({ + type: entry.type, + data: redactTelemetryDataHelper(entry.data), + })); +} + +/** + * Redact values from dictionary values other than Boolean and ENUM-type values. + * @TODO we will build a JSON blueprint of ENUM-type values in the codebase and + * do not redact the ENUM-type values if it match any key in the blueprint. + */ +function redactTelemetryDataHelper(data: any): any { + if (typeof data === 'boolean') { + return data; // Return booleans as-is + } + + if (Array.isArray(data)) { + // Handle arrays by recursively redacting each element + return data.map((item) => redactTelemetryDataHelper(item)); + } + + if (data && typeof data === 'object') { + // Handle objects by iterating over their key-value pairs + if (isResource(data)) { + return '*'; + } + + /** + * @TODO we need to build a JSON blueprint of class and props. If 'data' matches + * any leaf node in the blueprint, then redact the value to avoid logging customer + * data. + */ + const result: Record = {}; + for (const [key, value] of Object.entries(data)) { + result[key] = redactTelemetryDataHelper(value); + } + return result; + } + + return '*'; +} + /** * Add analytics data for any validation plugins that are used. * Since validation plugins are not constructs we have to handle them @@ -106,14 +169,31 @@ export function constructInfoFromStack(stack: Stack): ConstructInfo[] { addValidationPluginInfo(stack, allConstructInfos); - // Filter out duplicate values - const uniqKeys = new Set(); - return allConstructInfos.filter(construct => { - const constructKey = `${construct.fqn}@${construct.version}`; - const isDuplicate = uniqKeys.has(constructKey); - uniqKeys.add(constructKey); - return !isDuplicate; + // Filter out duplicate values and append the metadata information to the array + const uniqueMap = new Map(); + allConstructInfos.forEach(info => { + const key = `${info.fqn}@${info.version}`; + if (uniqueMap.has(key)) { + const existingInfo = uniqueMap.get(key); + if (existingInfo && existingInfo.metadata && info.metadata) { + existingInfo.metadata.push(...info.metadata); + } + } else { + uniqueMap.set(key, info); + } }); + + return Array.from(uniqueMap.values()); +} + +/** + * Check whether the given construct is a Resource. Note that this is + * duplicated function from 'core/lib/resource.ts' to avoid circular + * dependencies in imports. + */ +function isResource(construct: IConstruct): construct is Resource { + const RESOURCE_SYMBOL = Symbol.for('@aws-cdk/core.Resource'); + return construct !== null && typeof(construct) === 'object' && RESOURCE_SYMBOL in construct; } /** diff --git a/packages/aws-cdk-lib/core/test/metadata-resource.test.ts b/packages/aws-cdk-lib/core/test/metadata-resource.test.ts index fa05b0d501f16..265a6cf22ff76 100644 --- a/packages/aws-cdk-lib/core/test/metadata-resource.test.ts +++ b/packages/aws-cdk-lib/core/test/metadata-resource.test.ts @@ -1,6 +1,10 @@ import * as zlib from 'zlib'; import { Construct } from 'constructs'; -import { App, Stack, IPolicyValidationPluginBeta1, IPolicyValidationContextBeta1, Stage, PolicyValidationPluginReportBeta1 } from '../lib'; +import { Code, Function, Runtime } from '../../aws-lambda'; +import { Queue } from '../../aws-sqs'; +import { ENABLE_ADDITIONAL_METADATA_COLLECTION } from '../../cx-api'; +import { App, Stack, IPolicyValidationPluginBeta1, IPolicyValidationContextBeta1, Stage, PolicyValidationPluginReportBeta1, FeatureFlags, Duration } from '../lib'; +import { MetadataType } from '../lib/metadata-resource'; import { formatAnalytics } from '../lib/private/metadata-resource'; import { ConstructInfo } from '../lib/private/runtime-info'; @@ -49,6 +53,74 @@ describe('MetadataResource', () => { expect(stackTemplate.Resources?.CDKMetadata?.Condition).toBeDefined(); }); + test('when no metadata is added by default, CDKMetadata should be the same', () => { + const myApps = [ + new App({ + analyticsReporting: true, + postCliContext: { + [ENABLE_ADDITIONAL_METADATA_COLLECTION]: true, + }, + }), + new App({ + analyticsReporting: true, + postCliContext: { + [ENABLE_ADDITIONAL_METADATA_COLLECTION]: false, + }, + }), + new App({ + analyticsReporting: true, + postCliContext: { + [ENABLE_ADDITIONAL_METADATA_COLLECTION]: undefined, + }, + }), + ]; + + for (const myApp of myApps) { + const myStack = new Stack(myApp, 'MyStack'); + new Function(myStack, 'MyFunction', { + runtime: Runtime.PYTHON_3_9, + handler: 'index.handler', + code: Code.fromInline( + "def handler(event, context):\n\tprint('The function has been invoked.')", + ), + }); + } + + const stackTemplate1 = myApps[0].synth().getStackByName('MyStack').template; + const stackTemplate2 = myApps[1].synth().getStackByName('MyStack').template; + const stackTemplate3 = myApps[2].synth().getStackByName('MyStack').template; + expect(stackTemplate1.Resources?.CDKMetadata).toEqual(stackTemplate2.Resources?.CDKMetadata); + expect(stackTemplate1.Resources?.CDKMetadata).toEqual(stackTemplate3.Resources?.CDKMetadata); + }); + + test('enable additional metadata with metadata', () => { + const myApp = new App({ + analyticsReporting: true, + postCliContext: { + [ENABLE_ADDITIONAL_METADATA_COLLECTION]: true, + }, + }); + + const myStack = new Stack(myApp, 'EnableTelemtryStack'); + const queueProp = { + visibilityTimeout: Duration.seconds(300), + }; + const queue = new Queue(myStack, '01234test', queueProp); + queue.node.addMetadata(MetadataType.CONSTRUCT, queueProp); + + const funcProp = { + runtime: Runtime.PYTHON_3_9, + handler: 'index.handler', + code: Code.fromInline('def handler(event, context):\n\tprint(\'The function has been invoked.\')'), + }; + const func = new Function(myStack, 'MyFunction', funcProp); + func.node.addMetadata(MetadataType.CONSTRUCT, funcProp); + + const template = myApp.synth().getStackByName('EnableTelemtryStack').template; + expect(template.Resources?.CDKMetadata).toBeDefined(); + expect(template.Resources?.CDKMetadata?.Properties?.Analytics).toEqual('v2:deflate64:H4sIAAAAAAAA/8vLT0nVyyrWLzO00DMy0DNQzCrOzNQtKs0rycxN1QuC0ADZIqxKJQAAAA=='); + }); + test('includes the formatted Analytics property', () => { // A very simple check that the jsii runtime psuedo-construct is present. // This check works whether we're running locally or on CodeBuild, on v1 or v2. @@ -162,6 +234,18 @@ describe('formatAnalytics', () => { expectAnalytics(constructInfo, '1.2.3!aws-cdk-lib.{Construct,CfnResource,Stack},0.1.2!aws-cdk-lib.{CoolResource,OtherResource}'); }); + it.each([ + [true, '1.2.3!aws-cdk-lib.Construct[{\"custom\":{\"foo\":\"bar\"}}]'], + [false, '1.2.3!aws-cdk-lib.Construct'], + [undefined, '1.2.3!aws-cdk-lib.Construct'], + ])('format analytics with metadata and enabled additional telemetry', (enableAdditionalTelemtry, output) => { + const constructInfo = [ + { fqn: 'aws-cdk-lib.Construct', version: '1.2.3', metadata: [{ custom: { foo: 'bar' } }] }, + ]; + + expect(plaintextConstructsFromAnalytics(formatAnalytics(constructInfo, enableAdditionalTelemtry))).toMatch(output); + }); + test('ensure gzip is encoded with "unknown" operating system to maintain consistent output across systems', () => { const constructInfo = [{ fqn: 'aws-cdk-lib.Construct', version: '1.2.3' }]; const analytics = formatAnalytics(constructInfo); diff --git a/packages/aws-cdk-lib/core/test/private/runtime-info.test.ts b/packages/aws-cdk-lib/core/test/private/runtime-info.test.ts new file mode 100644 index 0000000000000..52bd8c33b143a --- /dev/null +++ b/packages/aws-cdk-lib/core/test/private/runtime-info.test.ts @@ -0,0 +1,95 @@ +import { MetadataEntry } from 'constructs'; +import { Code, Function, Runtime } from '../../../aws-lambda'; +import { Stack } from '../../lib'; +import { MetadataType } from '../../lib/metadata-resource'; +import { + constructInfoFromConstruct, + redactTelemetryData, +} from '../../lib/private/runtime-info'; + +test('test constructInfoFromConstruct can correctly get metadata information', () => { + const stack = new Stack(); + const myFunction = new Function(stack, 'MyFunction', { + runtime: Runtime.PYTHON_3_9, + handler: 'index.handler', + code: Code.fromInline( + "def handler(event, context):\n\tprint('The function has been invoked.')", + ), + }); + myFunction.node.addMetadata('hello', 'foo'); + myFunction.node.addMetadata(MetadataType.CONSTRUCT, { foo: 'bar' }); + + const constructInfo = constructInfoFromConstruct(myFunction); + expect(constructInfo?.metadata).toEqual([ + { type: MetadataType.CONSTRUCT, data: { foo: '*' } }, + ]); +}); + +test('test metadata is redacted correctly', () => { + const stack = new Stack(); + const myFunction = new Function(stack, 'MyFunction', { + runtime: Runtime.PYTHON_3_9, + handler: 'index.handler', + code: Code.fromInline( + "def handler(event, context):\n\tprint('The function has been invoked.')", + ), + }); + + const metadata: MetadataEntry[] = [ + { type: 'foo', data: { hello: 'world' } }, + { + type: MetadataType.CONSTRUCT, + data: { + bool: true, + nested: { foo: 'bar' }, + arr: [1, 2, 3], + str: 'foo', + arrOfObjects: [{ foo: { hello: 'world' } }, { myFunc: myFunction }], + }, + }, + { + type: MetadataType.METHOD, + data: { bool: true, nested: { foo: 'bar' }, arr: [1, 2, 3], str: 'foo' }, + }, + { + type: MetadataType.FEATURE_FLAG, + data: 'foobar', + }, + { + type: 'aws:cdk:analytics:construct', + data: 'foo', + }, + ]; + + // TODO: change this test case to verify that we only collect objects + // that's part of CDK and redact any customer provided object. + expect(redactTelemetryData(metadata)).toEqual([ + { + type: MetadataType.CONSTRUCT, + data: { + bool: true, + nested: { foo: '*' }, + arr: ['*', '*', '*'], + str: '*', + arrOfObjects: [{ foo: { hello: '*' } }, { myFunc: '*' }], + }, + }, + { + type: MetadataType.METHOD, + data: { + bool: true, + nested: { foo: '*' }, + arr: ['*', '*', '*'], + str: '*', + }, + }, + { + type: MetadataType.FEATURE_FLAG, + data: '*', + }, + { + type: 'aws:cdk:analytics:construct', + data: '*', + }, + ]); +}); diff --git a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md index 12075ff16f731..1ad9b50f6be19 100644 --- a/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md +++ b/packages/aws-cdk-lib/cx-api/FEATURE_FLAGS.md @@ -87,6 +87,7 @@ Flags come in three types: | [@aws-cdk/aws-ecs:disableEcsImdsBlocking](#aws-cdkaws-ecsdisableecsimdsblocking) | When set to true, CDK synth will throw exception if canContainersAccessInstanceRole is false. **IMPORTANT: See [details.](#aws-cdkaws-ecsdisableEcsImdsBlocking)** | V2NEXT | (temporary) | | [@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature](#aws-cdkaws-ecsenableimdsblockingdeprecatedfeature) | When set to true along with canContainersAccessInstanceRole=false in ECS cluster, new updated commands will be added to UserData to block container accessing IMDS. **Applicable to Linux only. IMPORTANT: See [details.](#aws-cdkaws-ecsenableImdsBlockingDeprecatedFeature)** | V2NEXT | (temporary) | | [@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault](#aws-cdkaws-elasticloadbalancingv2albdualstackwithoutpublicipv4securitygrouprulesdefault) | When enabled, the default security group ingress rules will allow IPv6 ingress from anywhere | V2NEXT | (fix) | +| [@aws-cdk/core:enableAdditionalMetadataCollection](#aws-cdkcoreenableadditionalmetadatacollection) | When enabled, CDK will expand the scope of usage data collected to better inform CDK development and improve communication for security concerns and emerging issues. | V2NEXT | (config) | @@ -161,7 +162,8 @@ The following json shows the current recommended set of flags, as `cdk init` wou "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true, "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true, "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true, - "@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true + "@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true, + "@aws-cdk/core:enableAdditionalMetadataCollection": true } } ``` @@ -1439,22 +1441,6 @@ If the flag is set to false then a custom resource will be created when using `U | (not in v1) | | | | 2.174.0 | `false` | `true` | -### @aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource - -*When enabled, use a new method for DNS Name of user pool domain target without creating a custom resource.* (fix) - -When this feature flag is enabled, a new method will be used to get the DNS Name of the user pool domain target. The old method -creates a custom resource internally, but the new method doesn't need a custom resource. - -If the flag is set to false then a custom resource will be created when using `UserPoolDomainTarget`. - - -| Since | Default | Recommended | -| ----- | ----- | ----- | -| (not in v1) | | | -| 2.174.0 | `false` | `true` | - - ### @aws-cdk/aws-ecs:disableEcsImdsBlocking *When set to true, CDK synth will throw exception if canContainersAccessInstanceRole is false. **IMPORTANT: See [details.](#aws-cdkaws-ecsdisableEcsImdsBlocking)*** (temporary) @@ -1507,4 +1493,20 @@ on the overly restrictive permissions are not broken. **Compatibility with old behavior:** Disable the feature flag to only allow IPv4 ingress in the default security group rules. +### @aws-cdk/core:enableAdditionalMetadataCollection + +*When enabled, CDK will expand the scope of usage data collected to better inform CDK development and improve communication for security concerns and emerging issues.* (config) + +When this feature flag is enabled, CDK expands the scope of usage data collection to include the following: + * L2 construct property keys - Collect which property keys you use from the L2 constructs in your app. This includes property keys nested in dictionary objects. + * L2 construct property values of BOOL and ENUM types - Collect property key values of only BOOL and ENUM types. All other types, such as string values or construct references will be redacted. + * L2 construct method usage - Collection method name, parameter keys and parameter values of BOOL and ENUM type. + + +| Since | Default | Recommended | +| ----- | ----- | ----- | +| (not in v1) | | | +| V2NEXT | `false` | `true` | + + diff --git a/packages/aws-cdk-lib/cx-api/README.md b/packages/aws-cdk-lib/cx-api/README.md index 46da524a70ede..bb03f06fa7a24 100644 --- a/packages/aws-cdk-lib/cx-api/README.md +++ b/packages/aws-cdk-lib/cx-api/README.md @@ -576,3 +576,21 @@ _cdk.json_ } } ``` + +* `@aws-cdk/core:enableAdditionalMetadataCollection` + +When this feature flag is enabled, CDK expands the scope of usage data collection to include the: + +* L2 construct property keys - Collect which property keys you use from the L2 constructs in your app. This includes property keys nested in dictionary objects. +* L2 construct property values of BOOL and ENUM types - Collect property key values of only BOOL and ENUM types. All other types, such as string values or construct references will be redacted. +* L2 construct method usage - Collection method name, parameter keys and parameter values of BOOL and ENUM type. + +_cdk.json_ + +```json +{ + "context": { + "@aws-cdk/core:enableAdditionalMetadataCollection": true + } +} +``` diff --git a/packages/aws-cdk-lib/cx-api/lib/features.ts b/packages/aws-cdk-lib/cx-api/lib/features.ts index b50c93df54da0..9d20a24930233 100644 --- a/packages/aws-cdk-lib/cx-api/lib/features.ts +++ b/packages/aws-cdk-lib/cx-api/lib/features.ts @@ -121,6 +121,7 @@ export const USER_POOL_DOMAIN_NAME_METHOD_WITHOUT_CUSTOM_RESOURCE = '@aws-cdk/aw export const Enable_IMDS_Blocking_Deprecated_Feature = '@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature'; export const Disable_ECS_IMDS_Blocking = '@aws-cdk/aws-ecs:disableEcsImdsBlocking'; export const ALB_DUALSTACK_WITHOUT_PUBLIC_IPV4_SECURITY_GROUP_RULES_DEFAULT = '@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault'; +export const ENABLE_ADDITIONAL_METADATA_COLLECTION = '@aws-cdk/core:enableAdditionalMetadataCollection'; export const FLAGS: Record = { ////////////////////////////////////////////////////////////////////// @@ -1355,6 +1356,20 @@ export const FLAGS: Record = { recommendedValue: true, compatibilityWithOldBehaviorMd: 'Disable the feature flag to only allow IPv4 ingress in the default security group rules.', }, + + ////////////////////////////////////////////////////////////////////// + [ENABLE_ADDITIONAL_METADATA_COLLECTION]: { + type: FlagType.VisibleContext, + summary: 'When enabled, CDK will expand the scope of usage data collected to better inform CDK development and improve communication for security concerns and emerging issues.', + detailsMd: ` + When this feature flag is enabled, CDK expands the scope of usage data collection to include the following: + * L2 construct property keys - Collect which property keys you use from the L2 constructs in your app. This includes property keys nested in dictionary objects. + * L2 construct property values of BOOL and ENUM types - Collect property key values of only BOOL and ENUM types. All other types, such as string values or construct references will be redacted. + * L2 construct method usage - Collection method name, parameter keys and parameter values of BOOL and ENUM type. + `, + introducedIn: { v2: 'V2NEXT' }, + recommendedValue: true, + }, }; const CURRENT_MV = 'v2'; diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index 724c217e5f760..aec2e60db7757 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -497,6 +497,7 @@ "./cloudformation-include": "./cloudformation-include/index.js", "./core": "./core/index.js", "./core/lib/helpers-internal": "./core/lib/helpers-internal/index.js", + "./core/lib/metadata-resource": "./core/lib/metadata-resource.js", "./custom-resources": "./custom-resources/index.js", "./custom-resources/lib/helpers-internal": "./custom-resources/lib/helpers-internal/index.js", "./cx-api": "./cx-api/index.js", diff --git a/packages/aws-cdk-lib/recommended-feature-flags.json b/packages/aws-cdk-lib/recommended-feature-flags.json index 71285e804b547..84da0b493278f 100644 --- a/packages/aws-cdk-lib/recommended-feature-flags.json +++ b/packages/aws-cdk-lib/recommended-feature-flags.json @@ -61,5 +61,7 @@ "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true, "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true, "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true, - "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true + "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true, + "@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true, + "@aws-cdk/core:enableAdditionalMetadataCollection": true } \ No newline at end of file