@friggframework/devtools 2.0.0--canary.428.5c4220d.0 → 2.0.0--canary.428.9de98cd.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.
@@ -1252,6 +1252,7 @@ class AWSDiscovery {
1252
1252
 
1253
1253
  return {
1254
1254
  defaultVpcId: vpc.VpcId,
1255
+ vpcCidr: vpc.CidrBlock, // Add VPC CIDR for security group configuration
1255
1256
  defaultSecurityGroupId: securityGroup.GroupId,
1256
1257
  privateSubnetId1: privateSubnets[0]?.SubnetId,
1257
1258
  privateSubnetId2:
@@ -56,6 +56,7 @@ describe('BuildTimeDiscovery', () => {
56
56
  describe('discoverAndCreateConfig', () => {
57
57
  const mockResources = {
58
58
  defaultVpcId: 'vpc-12345678',
59
+ vpcCidr: '172.31.0.0/16',
59
60
  defaultSecurityGroupId: 'sg-12345678',
60
61
  privateSubnetId1: 'subnet-1',
61
62
  privateSubnetId2: 'subnet-2',
@@ -100,6 +101,7 @@ describe('BuildTimeDiscovery', () => {
100
101
  describe('replaceTemplateVariables', () => {
101
102
  const mockResources = {
102
103
  defaultVpcId: 'vpc-12345678',
104
+ vpcCidr: '172.31.0.0/16',
103
105
  defaultSecurityGroupId: 'sg-12345678',
104
106
  privateSubnetId1: 'subnet-1',
105
107
  privateSubnetId2: 'subnet-2',
@@ -225,6 +227,7 @@ describe('BuildTimeDiscovery', () => {
225
227
  describe('preBuildHook', () => {
226
228
  const mockResources = {
227
229
  defaultVpcId: 'vpc-12345678',
230
+ vpcCidr: '172.31.0.0/16',
228
231
  defaultSecurityGroupId: 'sg-12345678',
229
232
  privateSubnetId1: 'subnet-1',
230
233
  privateSubnetId2: 'subnet-2',
@@ -1050,6 +1050,17 @@ const composeServerlessDefinition = async (AppDefinition) => {
1050
1050
  `Using existing KMS key: ${discoveredResources.defaultKmsKeyId}`
1051
1051
  );
1052
1052
 
1053
+ // Create a CloudFormation-managed alias to track the discovered key
1054
+ // This ensures CloudFormation always has a resource to manage, preventing deletion
1055
+ definition.resources.Resources.FriggKMSKeyAlias = {
1056
+ Type: 'AWS::KMS::Alias',
1057
+ DeletionPolicy: 'Retain',
1058
+ Properties: {
1059
+ AliasName: 'alias/${self:service}-${self:provider.stage}-frigg-kms',
1060
+ TargetKeyId: discoveredResources.defaultKmsKeyId
1061
+ }
1062
+ };
1063
+
1053
1064
  definition.provider.iamRoleStatements.push({
1054
1065
  Effect: 'Allow',
1055
1066
  Action: ['kms:GenerateDataKey', 'kms:Decrypt'],
@@ -1065,6 +1076,8 @@ const composeServerlessDefinition = async (AppDefinition) => {
1065
1076
 
1066
1077
  definition.resources.Resources.FriggKMSKey = {
1067
1078
  Type: 'AWS::KMS::Key',
1079
+ DeletionPolicy: 'Retain',
1080
+ UpdateReplacePolicy: 'Retain',
1068
1081
  Properties: {
1069
1082
  EnableKeyRotation: true,
1070
1083
  Description: 'Frigg KMS key for field-level encryption',
@@ -1110,6 +1123,10 @@ const composeServerlessDefinition = async (AppDefinition) => {
1110
1123
  Key: 'Name',
1111
1124
  Value: '${self:service}-${self:provider.stage}-frigg-kms-key',
1112
1125
  },
1126
+ {
1127
+ Key: 'ManagedBy',
1128
+ Value: 'Frigg',
1129
+ },
1113
1130
  {
1114
1131
  Key: 'Purpose',
1115
1132
  Value: 'Field-level encryption for Frigg application',
@@ -1118,6 +1135,16 @@ const composeServerlessDefinition = async (AppDefinition) => {
1118
1135
  },
1119
1136
  };
1120
1137
 
1138
+ // Create an alias for the new KMS key for consistent discovery
1139
+ definition.resources.Resources.FriggKMSKeyAlias = {
1140
+ Type: 'AWS::KMS::Alias',
1141
+ DeletionPolicy: 'Retain',
1142
+ Properties: {
1143
+ AliasName: 'alias/${self:service}-${self:provider.stage}-frigg-kms',
1144
+ TargetKeyId: { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }
1145
+ }
1146
+ };
1147
+
1121
1148
  definition.provider.iamRoleStatements.push({
1122
1149
  Effect: 'Allow',
1123
1150
  Action: ['kms:GenerateDataKey', 'kms:Decrypt'],
@@ -2001,6 +2028,13 @@ const composeServerlessDefinition = async (AppDefinition) => {
2001
2028
  AppDefinition.encryption?.fieldLevelEncryptionMethod ===
2002
2029
  'kms'
2003
2030
  ) {
2031
+ // Validate we have VPC CIDR for security group configuration
2032
+ if (!discoveredResources.vpcCidr) {
2033
+ console.warn(
2034
+ '⚠️ Warning: VPC CIDR not discovered. VPC endpoint security group may not work correctly.'
2035
+ );
2036
+ }
2037
+
2004
2038
  // Create security group for VPC endpoints if it doesn't exist
2005
2039
  if (
2006
2040
  !definition.resources.Resources
@@ -2013,16 +2047,16 @@ const composeServerlessDefinition = async (AppDefinition) => {
2013
2047
  GroupDescription:
2014
2048
  'Security group for VPC endpoints',
2015
2049
  VpcId: discoveredResources.defaultVpcId,
2016
- SecurityGroupIngress: [
2017
- {
2018
- IpProtocol: 'tcp',
2019
- FromPort: 443,
2020
- ToPort: 443,
2021
- CidrIp:
2022
- discoveredResources.vpcCidr ||
2023
- '10.0.0.0/16', // Dynamic VPC CIDR
2024
- },
2025
- ],
2050
+ SecurityGroupIngress: discoveredResources.vpcCidr
2051
+ ? [
2052
+ {
2053
+ IpProtocol: 'tcp',
2054
+ FromPort: 443,
2055
+ ToPort: 443,
2056
+ CidrIp: discoveredResources.vpcCidr, // Use discovered VPC CIDR
2057
+ },
2058
+ ]
2059
+ : [], // Empty array if no VPC CIDR discovered
2026
2060
  Tags: [
2027
2061
  {
2028
2062
  Key: 'Name',
@@ -7,6 +7,7 @@ jest.mock('./aws-discovery', () => {
7
7
  return {
8
8
  discoverResources: jest.fn().mockResolvedValue({
9
9
  defaultVpcId: 'vpc-123456',
10
+ vpcCidr: '172.31.0.0/16', // Add VPC CIDR for security group configuration
10
11
  defaultSecurityGroupId: 'sg-123456',
11
12
  privateSubnetId1: 'subnet-123456',
12
13
  privateSubnetId2: 'subnet-789012',
@@ -398,6 +399,17 @@ describe('composeServerlessDefinition', () => {
398
399
  expect(result.custom.kmsGrants).toEqual({
399
400
  kmsKeyId: 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012'
400
401
  });
402
+
403
+ // Check KMS Alias resource is created for discovered key
404
+ expect(result.resources.Resources.FriggKMSKeyAlias).toBeDefined();
405
+ expect(result.resources.Resources.FriggKMSKeyAlias).toEqual({
406
+ Type: 'AWS::KMS::Alias',
407
+ DeletionPolicy: 'Retain',
408
+ Properties: {
409
+ AliasName: 'alias/${self:service}-${self:provider.stage}-frigg-kms',
410
+ TargetKeyId: 'arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012'
411
+ }
412
+ });
401
413
  });
402
414
 
403
415
  it('should create new KMS key when encryption is enabled, no key found, and createResourceIfNoneFound is true', async () => {
@@ -426,9 +438,11 @@ describe('composeServerlessDefinition', () => {
426
438
 
427
439
  const result = await composeServerlessDefinition(appDefinition);
428
440
 
429
- // Check that KMS key resource was created
441
+ // Check that KMS key resource was created with DeletionPolicy
430
442
  expect(result.resources.Resources.FriggKMSKey).toEqual({
431
443
  Type: 'AWS::KMS::Key',
444
+ DeletionPolicy: 'Retain',
445
+ UpdateReplacePolicy: 'Retain',
432
446
  Properties: {
433
447
  EnableKeyRotation: true,
434
448
  Description: 'Frigg KMS key for field-level encryption',
@@ -471,6 +485,10 @@ describe('composeServerlessDefinition', () => {
471
485
  Key: 'Name',
472
486
  Value: '${self:service}-${self:provider.stage}-frigg-kms-key'
473
487
  },
488
+ {
489
+ Key: 'ManagedBy',
490
+ Value: 'Frigg'
491
+ },
474
492
  {
475
493
  Key: 'Purpose',
476
494
  Value: 'Field-level encryption for Frigg application'
@@ -479,6 +497,17 @@ describe('composeServerlessDefinition', () => {
479
497
  }
480
498
  });
481
499
 
500
+ // Check KMS Alias resource is created for the new key
501
+ expect(result.resources.Resources.FriggKMSKeyAlias).toBeDefined();
502
+ expect(result.resources.Resources.FriggKMSKeyAlias).toEqual({
503
+ Type: 'AWS::KMS::Alias',
504
+ DeletionPolicy: 'Retain',
505
+ Properties: {
506
+ AliasName: 'alias/${self:service}-${self:provider.stage}-frigg-kms',
507
+ TargetKeyId: { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }
508
+ }
509
+ });
510
+
482
511
  // Check IAM permissions for the new key
483
512
  const kmsPermission = result.provider.iamRoleStatements.find(
484
513
  statement => statement.Action.includes('kms:GenerateDataKey')
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.428.5c4220d.0",
4
+ "version": "2.0.0--canary.428.9de98cd.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-ec2": "^3.835.0",
7
7
  "@aws-sdk/client-kms": "^3.835.0",
@@ -9,8 +9,8 @@
9
9
  "@babel/eslint-parser": "^7.18.9",
10
10
  "@babel/parser": "^7.25.3",
11
11
  "@babel/traverse": "^7.25.3",
12
- "@friggframework/schemas": "2.0.0--canary.428.5c4220d.0",
13
- "@friggframework/test": "2.0.0--canary.428.5c4220d.0",
12
+ "@friggframework/schemas": "2.0.0--canary.428.9de98cd.0",
13
+ "@friggframework/test": "2.0.0--canary.428.9de98cd.0",
14
14
  "@hapi/boom": "^10.0.1",
15
15
  "@inquirer/prompts": "^5.3.8",
16
16
  "axios": "^1.7.2",
@@ -32,8 +32,8 @@
32
32
  "serverless-http": "^2.7.0"
33
33
  },
34
34
  "devDependencies": {
35
- "@friggframework/eslint-config": "2.0.0--canary.428.5c4220d.0",
36
- "@friggframework/prettier-config": "2.0.0--canary.428.5c4220d.0",
35
+ "@friggframework/eslint-config": "2.0.0--canary.428.9de98cd.0",
36
+ "@friggframework/prettier-config": "2.0.0--canary.428.9de98cd.0",
37
37
  "aws-sdk-client-mock": "^4.1.0",
38
38
  "aws-sdk-client-mock-jest": "^4.1.0",
39
39
  "jest": "^30.1.3",
@@ -68,5 +68,5 @@
68
68
  "publishConfig": {
69
69
  "access": "public"
70
70
  },
71
- "gitHead": "5c4220d4f787825af8c020f319236956d8ee3b65"
71
+ "gitHead": "9de98cdf6b43272a625ac0ff642f8abda89bb38a"
72
72
  }