@friggframework/devtools 2.0.0--canary.490.b68a7c8.0 → 2.0.0--canary.490.12406f7.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.
@@ -552,7 +552,7 @@ class VpcBuilder extends InfrastructureBuilder {
552
552
  this.buildNatGatewayFromDecision(decisions.natGateway, appDefinition, discoveredResources, result);
553
553
 
554
554
  // Build VPC Endpoints based on ownership decisions
555
- this.buildVpcEndpointsFromDecisions(decisions.vpcEndpoints, appDefinition, result);
555
+ this.buildVpcEndpointsFromDecisions(decisions.vpcEndpoints, decisions.securityGroup, appDefinition, result);
556
556
 
557
557
  // Set VPC_ENABLED environment variable
558
558
  result.environment.VPC_ENABLED = 'true';
@@ -920,7 +920,8 @@ class VpcBuilder extends InfrastructureBuilder {
920
920
  /**
921
921
  * Build VPC Endpoints based on ownership decisions
922
922
  */
923
- buildVpcEndpointsFromDecisions(decisions, appDefinition, result) {
923
+ buildVpcEndpointsFromDecisions(endpointDecisions, securityGroupDecision, appDefinition, result) {
924
+ const decisions = endpointDecisions; // For backwards compatibility with existing code
924
925
  const endpointsToCreate = [];
925
926
  const endpointsInStack = [];
926
927
  const externalEndpoints = [];
@@ -939,7 +940,7 @@ class VpcBuilder extends InfrastructureBuilder {
939
940
  if (endpointsInStack.length > 0) {
940
941
  console.log(` ✓ VPC Endpoints in stack: ${endpointsInStack.join(', ')}`);
941
942
  // CRITICAL: Must add stack-managed endpoints back to template or CloudFormation will DELETE them!
942
- this._addStackManagedEndpointsToTemplate(decisions, result);
943
+ this._addStackManagedEndpointsToTemplate(decisions, securityGroupDecision, result);
943
944
  }
944
945
 
945
946
  if (externalEndpoints.length > 0) {
@@ -1002,6 +1003,15 @@ class VpcBuilder extends InfrastructureBuilder {
1002
1003
  // Create security group for interface endpoints if needed
1003
1004
  const needsInterfaceEndpoints = endpointsToCreate.some(type => ['kms', 'secretsManager', 'sqs'].includes(type));
1004
1005
  if (needsInterfaceEndpoints) {
1006
+ // Determine source security group for ingress rule
1007
+ let sourceSgId;
1008
+ if (securityGroupDecision.ownership === ResourceOwnership.STACK) {
1009
+ sourceSgId = { Ref: 'FriggLambdaSecurityGroup' };
1010
+ } else {
1011
+ // External - use the physical ID
1012
+ sourceSgId = securityGroupDecision.physicalIds[0];
1013
+ }
1014
+
1005
1015
  result.resources.FriggVPCEndpointSecurityGroup = {
1006
1016
  Type: 'AWS::EC2::SecurityGroup',
1007
1017
  Properties: {
@@ -1012,7 +1022,7 @@ class VpcBuilder extends InfrastructureBuilder {
1012
1022
  IpProtocol: 'tcp',
1013
1023
  FromPort: 443,
1014
1024
  ToPort: 443,
1015
- SourceSecurityGroupId: { Ref: 'FriggLambdaSecurityGroup' },
1025
+ SourceSecurityGroupId: sourceSgId,
1016
1026
  Description: 'HTTPS from Lambda',
1017
1027
  },
1018
1028
  ],
@@ -1129,7 +1139,8 @@ class VpcBuilder extends InfrastructureBuilder {
1129
1139
  *
1130
1140
  * @private
1131
1141
  */
1132
- _addStackManagedEndpointsToTemplate(decisions, result) {
1142
+ _addStackManagedEndpointsToTemplate(endpointDecisions, securityGroupDecision, result) {
1143
+ const decisions = endpointDecisions; // For backwards compatibility
1133
1144
  const vpcId = result.vpcId;
1134
1145
  const logicalIdMap = {
1135
1146
  s3: 'FriggS3VPCEndpoint',
@@ -1193,6 +1204,17 @@ class VpcBuilder extends InfrastructureBuilder {
1193
1204
  );
1194
1205
 
1195
1206
  if (hasInterfaceEndpoints && !result.resources.FriggVPCEndpointSecurityGroup) {
1207
+ // Determine source security group for ingress rule
1208
+ // If Lambda SG is stack-managed, use CloudFormation Ref
1209
+ // If Lambda SG is external, use the physical ID directly
1210
+ let sourceSgId;
1211
+ if (securityGroupDecision.ownership === ResourceOwnership.STACK) {
1212
+ sourceSgId = { Ref: 'FriggLambdaSecurityGroup' };
1213
+ } else {
1214
+ // External - use the physical ID
1215
+ sourceSgId = securityGroupDecision.physicalIds[0];
1216
+ }
1217
+
1196
1218
  result.resources.FriggVPCEndpointSecurityGroup = {
1197
1219
  Type: 'AWS::EC2::SecurityGroup',
1198
1220
  Properties: {
@@ -1203,7 +1225,7 @@ class VpcBuilder extends InfrastructureBuilder {
1203
1225
  IpProtocol: 'tcp',
1204
1226
  FromPort: 443,
1205
1227
  ToPort: 443,
1206
- SourceSecurityGroupId: { Ref: 'FriggLambdaSecurityGroup' }
1228
+ SourceSecurityGroupId: sourceSgId
1207
1229
  }
1208
1230
  ],
1209
1231
  Tags: [
@@ -1472,6 +1472,76 @@ describe('VpcBuilder', () => {
1472
1472
  });
1473
1473
  });
1474
1474
 
1475
+ describe('VPC Endpoint Security Group with External Lambda SG', () => {
1476
+ it('should use external Lambda SG ID (not Ref) for VPC endpoint SG when Lambda SG is external', async () => {
1477
+ const appDefinition = {
1478
+ vpc: {
1479
+ enable: true,
1480
+ enableVPCEndpoints: true,
1481
+ ownership: {
1482
+ securityGroup: 'external' // External Lambda SG
1483
+ }
1484
+ },
1485
+ encryption: { fieldLevelEncryptionMethod: 'kms' }
1486
+ };
1487
+ const discoveredResources = {
1488
+ fromCloudFormationStack: true,
1489
+ defaultVpcId: 'vpc-123',
1490
+ defaultSecurityGroupId: 'sg-default-456', // Default VPC SG
1491
+ lambdaSecurityGroupId: 'sg-stack-789', // Stack-managed SG (will be ignored)
1492
+ privateSubnetId1: 'subnet-1',
1493
+ privateSubnetId2: 'subnet-2',
1494
+ natGatewayId: 'nat-123',
1495
+ existingLogicalIds: ['FriggS3VPCEndpoint', 'FriggKMSVPCEndpoint'],
1496
+ s3VpcEndpointId: 'vpce-s3-stack',
1497
+ kmsVpcEndpointId: 'vpce-kms-stack'
1498
+ };
1499
+
1500
+ const result = await vpcBuilder.build(appDefinition, discoveredResources);
1501
+
1502
+ // VPC Endpoint SG should be created
1503
+ expect(result.resources.FriggVPCEndpointSecurityGroup).toBeDefined();
1504
+
1505
+ // CRITICAL: Should use external Lambda SG ID directly, NOT a CloudFormation Ref
1506
+ const ingressRule = result.resources.FriggVPCEndpointSecurityGroup.Properties.SecurityGroupIngress[0];
1507
+ expect(ingressRule.SourceSecurityGroupId).toBe('sg-default-456'); // Direct ID, not { Ref: 'FriggLambdaSecurityGroup' }
1508
+ expect(typeof ingressRule.SourceSecurityGroupId).toBe('string');
1509
+
1510
+ // Verify FriggLambdaSecurityGroup is NOT in the template
1511
+ expect(result.resources.FriggLambdaSecurityGroup).toBeUndefined();
1512
+ });
1513
+
1514
+ it('should use CloudFormation Ref when Lambda SG is stack-managed', async () => {
1515
+ const appDefinition = {
1516
+ vpc: {
1517
+ enable: true,
1518
+ enableVPCEndpoints: true,
1519
+ ownership: {
1520
+ securityGroup: 'stack' // Stack-managed Lambda SG
1521
+ }
1522
+ },
1523
+ encryption: { fieldLevelEncryptionMethod: 'kms' }
1524
+ };
1525
+ const discoveredResources = {
1526
+ defaultVpcId: 'vpc-123',
1527
+ privateSubnetId1: 'subnet-1',
1528
+ privateSubnetId2: 'subnet-2'
1529
+ };
1530
+
1531
+ const result = await vpcBuilder.build(appDefinition, discoveredResources);
1532
+
1533
+ // VPC Endpoint SG should be created
1534
+ expect(result.resources.FriggVPCEndpointSecurityGroup).toBeDefined();
1535
+
1536
+ // Should use CloudFormation Ref when Lambda SG is in stack
1537
+ const ingressRule = result.resources.FriggVPCEndpointSecurityGroup.Properties.SecurityGroupIngress[0];
1538
+ expect(ingressRule.SourceSecurityGroupId).toEqual({ Ref: 'FriggLambdaSecurityGroup' });
1539
+
1540
+ // Verify FriggLambdaSecurityGroup IS in the template
1541
+ expect(result.resources.FriggLambdaSecurityGroup).toBeDefined();
1542
+ });
1543
+ });
1544
+
1475
1545
  describe('convertFlatDiscoveryToStructured - VPC Endpoints from CloudFormation', () => {
1476
1546
  it('should add VPC endpoints to stackManaged when in existingLogicalIds', () => {
1477
1547
  const flatDiscovery = {
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.490.b68a7c8.0",
4
+ "version": "2.0.0--canary.490.12406f7.0",
5
5
  "bin": {
6
6
  "frigg": "./frigg-cli/index.js"
7
7
  },
@@ -16,9 +16,9 @@
16
16
  "@babel/eslint-parser": "^7.18.9",
17
17
  "@babel/parser": "^7.25.3",
18
18
  "@babel/traverse": "^7.25.3",
19
- "@friggframework/core": "2.0.0--canary.490.b68a7c8.0",
20
- "@friggframework/schemas": "2.0.0--canary.490.b68a7c8.0",
21
- "@friggframework/test": "2.0.0--canary.490.b68a7c8.0",
19
+ "@friggframework/core": "2.0.0--canary.490.12406f7.0",
20
+ "@friggframework/schemas": "2.0.0--canary.490.12406f7.0",
21
+ "@friggframework/test": "2.0.0--canary.490.12406f7.0",
22
22
  "@hapi/boom": "^10.0.1",
23
23
  "@inquirer/prompts": "^5.3.8",
24
24
  "axios": "^1.7.2",
@@ -46,8 +46,8 @@
46
46
  "validate-npm-package-name": "^5.0.0"
47
47
  },
48
48
  "devDependencies": {
49
- "@friggframework/eslint-config": "2.0.0--canary.490.b68a7c8.0",
50
- "@friggframework/prettier-config": "2.0.0--canary.490.b68a7c8.0",
49
+ "@friggframework/eslint-config": "2.0.0--canary.490.12406f7.0",
50
+ "@friggframework/prettier-config": "2.0.0--canary.490.12406f7.0",
51
51
  "aws-sdk-client-mock": "^4.1.0",
52
52
  "aws-sdk-client-mock-jest": "^4.1.0",
53
53
  "jest": "^30.1.3",
@@ -79,5 +79,5 @@
79
79
  "publishConfig": {
80
80
  "access": "public"
81
81
  },
82
- "gitHead": "b68a7c826822de959fe02e798dad62be261485a1"
82
+ "gitHead": "12406f722951a015ba310ba6c47854d7fda9dee2"
83
83
  }