@friggframework/devtools 2.0.0-next.36 → 2.0.0-next.38

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.
@@ -49,7 +49,7 @@ describe('VPC/KMS/SSM Integration Tests', () => {
49
49
  const appDefinition = {
50
50
  name: 'test-frigg-app',
51
51
  vpc: { enable: true },
52
- encryption: { useDefaultKMSForFieldLevelEncryption: true },
52
+ encryption: { fieldLevelEncryptionMethod: 'kms' },
53
53
  ssm: { enable: true },
54
54
  integrations: [{
55
55
  Definition: {
@@ -67,7 +67,7 @@ describe('VPC/KMS/SSM Integration Tests', () => {
67
67
  process.env.AWS_DISCOVERY_SUBNET_ID_1 = discoveredResources.privateSubnetId1;
68
68
  process.env.AWS_DISCOVERY_SUBNET_ID_2 = discoveredResources.privateSubnetId2;
69
69
  process.env.AWS_DISCOVERY_ROUTE_TABLE_ID = discoveredResources.privateRouteTableId;
70
- process.env.AWS_DISCOVERY_KMS_KEY_ID = discoveredResources.defaultKmsKeyId;
70
+ process.env.AWS_DISCOVERY_KMS_KEY_ID =discoveredResources.defaultKmsKeyId;
71
71
 
72
72
  // Generate serverless configuration
73
73
  const serverlessConfig = composeServerlessDefinition(appDefinition);
@@ -173,11 +173,11 @@ describe('VPC/KMS/SSM Integration Tests', () => {
173
173
  it('should generate config with only KMS enabled', async () => {
174
174
  const appDefinition = {
175
175
  name: 'kms-only-app',
176
- encryption: { useDefaultKMSForFieldLevelEncryption: true },
176
+ encryption: { fieldLevelEncryptionMethod: 'kms' },
177
177
  integrations: []
178
178
  };
179
179
 
180
- process.env.AWS_DISCOVERY_KMS_KEY_ID = mockAWSResources.defaultKmsKeyId;
180
+ process.env.AWS_DISCOVERY_KMS_KEY_ID =mockAWSResources.defaultKmsKeyId;
181
181
 
182
182
  const serverlessConfig = composeServerlessDefinition(appDefinition);
183
183
 
@@ -231,7 +231,7 @@ describe('VPC/KMS/SSM Integration Tests', () => {
231
231
  expect(mockBuildTimeDiscovery.preBuildHook).toHaveBeenCalledWith(
232
232
  expect.objectContaining({
233
233
  vpc: { enable: true },
234
- encryption: { useDefaultKMSForFieldLevelEncryption: true }
234
+ encryption: { fieldLevelEncryptionMethod: 'kms' }
235
235
  }),
236
236
  'us-east-1'
237
237
  );
@@ -302,7 +302,7 @@ describe('VPC/KMS/SSM Integration Tests', () => {
302
302
  process.env.AWS_DISCOVERY_SECURITY_GROUP_ID = mockAWSResources.defaultSecurityGroupId;
303
303
  process.env.AWS_DISCOVERY_SUBNET_ID_1 = mockAWSResources.privateSubnetId1;
304
304
  process.env.AWS_DISCOVERY_SUBNET_ID_2 = mockAWSResources.privateSubnetId2;
305
- process.env.AWS_DISCOVERY_KMS_KEY_ID = mockAWSResources.defaultKmsKeyId;
305
+ process.env.AWS_DISCOVERY_KMS_KEY_ID =mockAWSResources.defaultKmsKeyId;
306
306
 
307
307
  // In a real deployment, serverless framework would resolve these environment variables
308
308
  // For testing, we can verify the placeholders are correctly formatted
@@ -353,7 +353,7 @@ describe('VPC/KMS/SSM Integration Tests', () => {
353
353
 
354
354
  const appDefinition = {
355
355
  vpc: { enable: true },
356
- encryption: { useDefaultKMSForFieldLevelEncryption: true },
356
+ encryption: { fieldLevelEncryptionMethod: 'kms' },
357
357
  integrations: []
358
358
  };
359
359
 
@@ -41,7 +41,7 @@ async function runDiscovery() {
41
41
 
42
42
  // Check if discovery is needed
43
43
  const needsDiscovery = appDefinition.vpc?.enable ||
44
- appDefinition.encryption?.useDefaultKMSForFieldLevelEncryption ||
44
+ appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms' ||
45
45
  appDefinition.ssm?.enable;
46
46
 
47
47
  if (!needsDiscovery) {
@@ -51,7 +51,7 @@ async function runDiscovery() {
51
51
 
52
52
  console.log('📋 App requires AWS discovery for:');
53
53
  if (appDefinition.vpc?.enable) console.log(' ✅ VPC support');
54
- if (appDefinition.encryption?.useDefaultKMSForFieldLevelEncryption) console.log(' ✅ KMS encryption');
54
+ if (appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms') console.log(' ✅ KMS encryption');
55
55
  if (appDefinition.ssm?.enable) console.log(' ✅ SSM parameters');
56
56
 
57
57
  // Run discovery
@@ -82,7 +82,7 @@ async function runDiscovery() {
82
82
  } else {
83
83
  console.error('🚨 Discovery is required because your AppDefinition has these features enabled:');
84
84
  if (appDefinition.vpc?.enable) console.error(' ❌ VPC support (vpc.enable: true)');
85
- if (appDefinition.encryption?.useDefaultKMSForFieldLevelEncryption) console.error(' ❌ KMS encryption (encryption.useDefaultKMSForFieldLevelEncryption: true)');
85
+ if (appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms') console.error(' ❌ KMS encryption (encryption.fieldLevelEncryptionMethod: \'kms\')');
86
86
  if (appDefinition.ssm?.enable) console.error(' ❌ SSM parameters (ssm.enable: true)');
87
87
  console.error('');
88
88
  console.error('💡 To fix this issue:');
@@ -95,7 +95,7 @@ async function runDiscovery() {
95
95
 
96
96
  console.error('🔧 Or disable features in backend/index.js:');
97
97
  console.error(' vpc: { enable: false }');
98
- console.error(' encryption: { useDefaultKMSForFieldLevelEncryption: false }');
98
+ console.error(' encryption: { fieldLevelEncryptionMethod: \'aes\' }');
99
99
  console.error(' ssm: { enable: false }');
100
100
 
101
101
  process.exit(1);
@@ -10,8 +10,7 @@ const { AWSDiscovery } = require('./aws-discovery');
10
10
  const shouldRunDiscovery = (AppDefinition) => {
11
11
  return (
12
12
  AppDefinition.vpc?.enable === true ||
13
- AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption ===
14
- true ||
13
+ AppDefinition.encryption?.fieldLevelEncryptionMethod === 'kms' ||
15
14
  AppDefinition.ssm?.enable === true
16
15
  );
17
16
  };
@@ -450,6 +449,13 @@ const createVPCInfrastructure = (AppDefinition) => {
450
449
  CidrIp: '0.0.0.0/0',
451
450
  Description: 'DNS UDP',
452
451
  },
452
+ {
453
+ IpProtocol: 'tcp',
454
+ FromPort: 27017,
455
+ ToPort: 27017,
456
+ CidrIp: '0.0.0.0/0',
457
+ Description: 'MongoDB outbound',
458
+ },
453
459
  ],
454
460
  Tags: [
455
461
  {
@@ -486,10 +492,7 @@ const createVPCInfrastructure = (AppDefinition) => {
486
492
  };
487
493
 
488
494
  // KMS Interface Endpoint (paid, but useful if using KMS)
489
- if (
490
- AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption ===
491
- true
492
- ) {
495
+ if (AppDefinition.encryption?.fieldLevelEncryptionMethod === 'kms') {
493
496
  vpcResources.FriggKMSVPCEndpoint = {
494
497
  Type: 'AWS::EC2::VPCEndpoint',
495
498
  Properties: {
@@ -884,9 +887,7 @@ const composeServerlessDefinition = async (AppDefinition) => {
884
887
  };
885
888
 
886
889
  // KMS Configuration based on App Definition
887
- if (
888
- AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption === true
889
- ) {
890
+ if (AppDefinition.encryption?.fieldLevelEncryptionMethod === 'kms') {
890
891
  // Check if a KMS key was discovered
891
892
  if (discoveredResources.defaultKmsKeyId) {
892
893
  // Use the existing discovered KMS key
@@ -900,55 +901,112 @@ const composeServerlessDefinition = async (AppDefinition) => {
900
901
  Resource: [discoveredResources.defaultKmsKeyId],
901
902
  });
902
903
 
903
- definition.provider.environment.KMS_KEY_ARN =
904
- discoveredResources.defaultKmsKeyId;
904
+ // KMS_KEY_ARN will be set later from custom.kmsGrants for consistency
905
905
  } else {
906
- // No existing key found, provision a dedicated KMS key
907
- console.log('No existing KMS key found, creating a new one...');
906
+ // No existing key found - check if we should create one or error
907
+ if (AppDefinition.encryption?.createResourceIfNoneFound === true) {
908
+ // Create a new KMS key
909
+ console.log('No existing KMS key found, creating a new one...');
908
910
 
909
- definition.resources.Resources.FriggKMSKey = {
910
- Type: 'AWS::KMS::Key',
911
- Properties: {
912
- EnableKeyRotation: true,
913
- KeyPolicy: {
914
- Version: '2012-10-17',
915
- Statement: [
916
- {
917
- Sid: 'AllowRootAccountAdmin',
918
- Effect: 'Allow',
919
- Principal: {
920
- AWS: {
921
- 'Fn::Sub':
922
- 'arn:aws:iam::${AWS::AccountId}:root',
911
+ definition.resources.Resources.FriggKMSKey = {
912
+ Type: 'AWS::KMS::Key',
913
+ Properties: {
914
+ EnableKeyRotation: true,
915
+ Description: 'Frigg KMS key for field-level encryption',
916
+ KeyPolicy: {
917
+ Version: '2012-10-17',
918
+ Statement: [
919
+ {
920
+ Sid: 'AllowRootAccountAdmin',
921
+ Effect: 'Allow',
922
+ Principal: {
923
+ AWS: {
924
+ 'Fn::Sub':
925
+ 'arn:aws:iam::${AWS::AccountId}:root',
926
+ },
923
927
  },
928
+ Action: 'kms:*',
929
+ Resource: '*',
924
930
  },
925
- Action: 'kms:*',
926
- Resource: '*',
931
+ {
932
+ Sid: 'AllowLambdaService',
933
+ Effect: 'Allow',
934
+ Principal: {
935
+ Service: 'lambda.amazonaws.com',
936
+ },
937
+ Action: [
938
+ 'kms:GenerateDataKey',
939
+ 'kms:Decrypt',
940
+ 'kms:DescribeKey',
941
+ ],
942
+ Resource: '*',
943
+ Condition: {
944
+ StringEquals: {
945
+ 'kms:ViaService': `lambda.${
946
+ process.env.AWS_REGION ||
947
+ 'us-east-1'
948
+ }.amazonaws.com`,
949
+ },
950
+ },
951
+ },
952
+ ],
953
+ },
954
+ Tags: [
955
+ {
956
+ Key: 'Name',
957
+ Value: '${self:service}-${self:provider.stage}-frigg-kms-key',
958
+ },
959
+ {
960
+ Key: 'Purpose',
961
+ Value: 'Field-level encryption for Frigg application',
927
962
  },
928
963
  ],
929
964
  },
930
- },
931
- };
965
+ };
932
966
 
933
- definition.provider.iamRoleStatements.push({
934
- Effect: 'Allow',
935
- Action: ['kms:GenerateDataKey', 'kms:Decrypt'],
936
- Resource: [{ 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }],
937
- });
967
+ definition.provider.iamRoleStatements.push({
968
+ Effect: 'Allow',
969
+ Action: ['kms:GenerateDataKey', 'kms:Decrypt'],
970
+ Resource: [{ 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }],
971
+ });
938
972
 
939
- definition.provider.environment.KMS_KEY_ARN = {
940
- 'Fn::GetAtt': ['FriggKMSKey', 'Arn'],
941
- };
973
+ definition.provider.environment.KMS_KEY_ARN = {
974
+ 'Fn::GetAtt': ['FriggKMSKey', 'Arn'],
975
+ };
976
+
977
+ // Configure KMS grants to reference the created key
978
+ definition.custom.kmsGrants = {
979
+ kmsKeyId: { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] }
980
+ };
981
+ } else {
982
+ // No key found and createIfNoneFound is not enabled - error
983
+ throw new Error(
984
+ 'KMS field-level encryption is enabled but no KMS key was found. ' +
985
+ 'Either provide an existing KMS key or set encryption.createResourceIfNoneFound to true to create a new key.'
986
+ );
987
+ }
942
988
  }
943
989
 
944
990
  definition.plugins.push('serverless-kms-grants');
945
991
 
946
- // Configure KMS grants with discovered default key or environment variable
947
- definition.custom.kmsGrants = {
948
- kmsKeyId:
992
+ // Configure KMS grants if not already set (when using existing key)
993
+ if (!definition.custom.kmsGrants) {
994
+ definition.custom.kmsGrants = {
995
+ kmsKeyId:
996
+ discoveredResources.defaultKmsKeyId ||
997
+ '${env:AWS_DISCOVERY_KMS_KEY_ID}',
998
+ };
999
+ }
1000
+
1001
+ // Always set KMS_KEY_ARN from custom.kmsGrants for consistency
1002
+ // This translates AWS_DISCOVERY_KMS_KEY_ID to the runtime variable KMS_KEY_ARN
1003
+ if (!definition.provider.environment.KMS_KEY_ARN) {
1004
+ // Use the discovered value directly when available (from in-process discovery)
1005
+ // Otherwise fall back to environment variable (from separate discovery process)
1006
+ definition.provider.environment.KMS_KEY_ARN =
949
1007
  discoveredResources.defaultKmsKeyId ||
950
- '${env:AWS_DISCOVERY_KMS_KEY_ID}',
951
- };
1008
+ '${env:AWS_DISCOVERY_KMS_KEY_ID}';
1009
+ }
952
1010
  }
953
1011
 
954
1012
  // VPC Configuration based on App Definition
@@ -1245,62 +1303,6 @@ const composeServerlessDefinition = async (AppDefinition) => {
1245
1303
  definition.custom[queueReference] = queueName;
1246
1304
  }
1247
1305
  }
1248
-
1249
- // Discovery has already run successfully at this point if needed
1250
- // The discoveredResources object contains all the necessary AWS resources
1251
-
1252
- // Add websocket function if enabled
1253
- if (AppDefinition.websockets?.enable === true) {
1254
- definition.functions.defaultWebsocket = {
1255
- handler:
1256
- 'node_modules/@friggframework/core/handlers/routers/websocket.handler',
1257
- events: [
1258
- {
1259
- websocket: {
1260
- route: '$connect',
1261
- },
1262
- },
1263
- {
1264
- websocket: {
1265
- route: '$default',
1266
- },
1267
- },
1268
- {
1269
- websocket: {
1270
- route: '$disconnect',
1271
- },
1272
- },
1273
- ],
1274
- };
1275
- }
1276
-
1277
- // Discovery has already run successfully at this point if needed
1278
- // The discoveredResources object contains all the necessary AWS resources
1279
-
1280
- // Add websocket function if enabled
1281
- if (AppDefinition.websockets?.enable === true) {
1282
- definition.functions.defaultWebsocket = {
1283
- handler:
1284
- 'node_modules/@friggframework/core/handlers/routers/websocket.handler',
1285
- events: [
1286
- {
1287
- websocket: {
1288
- route: '$connect',
1289
- },
1290
- },
1291
- {
1292
- websocket: {
1293
- route: '$default',
1294
- },
1295
- },
1296
- {
1297
- websocket: {
1298
- route: '$disconnect',
1299
- },
1300
- },
1301
- ],
1302
- };
1303
- }
1304
1306
  }
1305
1307
 
1306
1308
  // Discovery has already run successfully at this point if needed
@@ -1331,9 +1333,6 @@ const composeServerlessDefinition = async (AppDefinition) => {
1331
1333
  };
1332
1334
  }
1333
1335
 
1334
- // Discovery has already run successfully at this point if needed
1335
- // The discoveredResources object contains all the necessary AWS resources
1336
-
1337
1336
  // Modify handler paths to point to the correct node_modules location
1338
1337
  definition.functions = modifyHandlerPaths(definition.functions);
1339
1338