@friggframework/devtools 2.0.0--canary.625.3f500f2.0 → 2.0.0--canary.625.0de0db4.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.
@@ -123,6 +123,10 @@ class VpcDiscovery {
123
123
  const sqsEndpoint = rawResources.vpcEndpoints.find(
124
124
  ep => ep.ServiceName && ep.ServiceName.includes('.sqs')
125
125
  );
126
+ // endsWith avoids matching sibling services like `.ssmmessages`
127
+ const ssmEndpoint = rawResources.vpcEndpoints.find(
128
+ ep => ep.ServiceName && ep.ServiceName.endsWith('.ssm')
129
+ );
126
130
 
127
131
  if (s3Endpoint) {
128
132
  result.s3VpcEndpointId = s3Endpoint.VpcEndpointId;
@@ -139,6 +143,9 @@ class VpcDiscovery {
139
143
  if (sqsEndpoint) {
140
144
  result.sqsVpcEndpointId = sqsEndpoint.VpcEndpointId;
141
145
  }
146
+ if (ssmEndpoint) {
147
+ result.ssmVpcEndpointId = ssmEndpoint.VpcEndpointId;
148
+ }
142
149
  }
143
150
 
144
151
  console.log(` ✓ Found VPC: ${result.defaultVpcId}`);
@@ -151,8 +158,8 @@ class VpcDiscovery {
151
158
  if (result.existingNatGatewayId) {
152
159
  console.log(` ✓ Found NAT Gateway: ${result.existingNatGatewayId}`);
153
160
  }
154
- if (result.s3VpcEndpointId || result.dynamodbVpcEndpointId || result.kmsVpcEndpointId || result.secretsManagerVpcEndpointId || result.sqsVpcEndpointId) {
155
- console.log(` ✓ Found VPC Endpoints: S3=${result.s3VpcEndpointId ? 'Yes' : 'No'}, DynamoDB=${result.dynamodbVpcEndpointId ? 'Yes' : 'No'}, KMS=${result.kmsVpcEndpointId ? 'Yes' : 'No'}, SecretsManager=${result.secretsManagerVpcEndpointId ? 'Yes' : 'No'}, SQS=${result.sqsVpcEndpointId ? 'Yes' : 'No'}`);
161
+ if (result.s3VpcEndpointId || result.dynamodbVpcEndpointId || result.kmsVpcEndpointId || result.secretsManagerVpcEndpointId || result.sqsVpcEndpointId || result.ssmVpcEndpointId) {
162
+ console.log(` ✓ Found VPC Endpoints: S3=${result.s3VpcEndpointId ? 'Yes' : 'No'}, DynamoDB=${result.dynamodbVpcEndpointId ? 'Yes' : 'No'}, KMS=${result.kmsVpcEndpointId ? 'Yes' : 'No'}, SecretsManager=${result.secretsManagerVpcEndpointId ? 'Yes' : 'No'}, SQS=${result.sqsVpcEndpointId ? 'Yes' : 'No'}, SSM=${result.ssmVpcEndpointId ? 'Yes' : 'No'}`);
156
163
  }
157
164
 
158
165
  return result;
@@ -253,7 +253,7 @@ describe('VpcDiscovery', () => {
253
253
  expect(mockProvider.discoverVpc).toHaveBeenCalledWith(config);
254
254
  });
255
255
 
256
- it('should discover all VPC endpoints (S3, DynamoDB, KMS, Secrets Manager)', async () => {
256
+ it('should discover all VPC endpoints (S3, DynamoDB, KMS, Secrets Manager, SQS, SSM)', async () => {
257
257
  mockProvider.discoverVpc.mockResolvedValue({
258
258
  vpcId: 'vpc-123',
259
259
  vpcCidr: '10.0.0.0/16',
@@ -283,6 +283,22 @@ describe('VpcDiscovery', () => {
283
283
  ServiceName: 'com.amazonaws.us-east-1.secretsmanager',
284
284
  State: 'available',
285
285
  },
286
+ {
287
+ VpcEndpointId: 'vpce-sqs-def',
288
+ ServiceName: 'com.amazonaws.us-east-1.sqs',
289
+ State: 'available',
290
+ },
291
+ // ssmmessages must NOT be mistaken for the ssm endpoint
292
+ {
293
+ VpcEndpointId: 'vpce-ssmmessages-000',
294
+ ServiceName: 'com.amazonaws.us-east-1.ssmmessages',
295
+ State: 'available',
296
+ },
297
+ {
298
+ VpcEndpointId: 'vpce-ssm-ghi',
299
+ ServiceName: 'com.amazonaws.us-east-1.ssm',
300
+ State: 'available',
301
+ },
286
302
  ],
287
303
  });
288
304
 
@@ -292,6 +308,8 @@ describe('VpcDiscovery', () => {
292
308
  expect(result.dynamodbVpcEndpointId).toBe('vpce-ddb-456');
293
309
  expect(result.kmsVpcEndpointId).toBe('vpce-kms-789');
294
310
  expect(result.secretsManagerVpcEndpointId).toBe('vpce-sm-abc');
311
+ expect(result.sqsVpcEndpointId).toBe('vpce-sqs-def');
312
+ expect(result.ssmVpcEndpointId).toBe('vpce-ssm-ghi');
295
313
  });
296
314
 
297
315
  it('should handle partial VPC endpoint discovery', async () => {
@@ -94,7 +94,7 @@ function getOffloadedKeys(appDefinition = {}) {
94
94
  keys.add(key);
95
95
  }
96
96
 
97
- return [...keys].sort();
97
+ return [...keys].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
98
98
  }
99
99
 
100
100
  /**
@@ -703,7 +703,10 @@ function generateIAMCloudFormation(options = {}) {
703
703
  },
704
704
  ],
705
705
  Condition: {
706
- StringEquals: {
706
+ // StringLike: kms:ViaService is regional
707
+ // (ssm.us-east-1.amazonaws.com), so the wildcard
708
+ // must be matched, not compared literally.
709
+ StringLike: {
707
710
  'kms:ViaService': [
708
711
  'ssm.*.amazonaws.com',
709
712
  ],
@@ -166,6 +166,16 @@ describe('IAM Generator', () => {
166
166
  expect(yaml).toContain('ssm.*.amazonaws.com');
167
167
  // No customer-managed key configured: falls back to the account key wildcard
168
168
  expect(yaml).toContain('arn:aws:kms:*:${AWS::AccountId}:key/*');
169
+
170
+ // The regional ViaService wildcard must be matched with StringLike;
171
+ // StringEquals would compare literally and never match
172
+ // ssm.<region>.amazonaws.com, denying the SecureString KMS call.
173
+ const ssmKmsBlock = yaml.slice(
174
+ yaml.indexOf('FriggSSMParameterKMSEncryption'),
175
+ yaml.indexOf('FriggSSMParameterKMSEncryption') + 600
176
+ );
177
+ expect(ssmKmsBlock).toContain('StringLike');
178
+ expect(ssmKmsBlock).not.toContain('StringEquals');
169
179
  });
170
180
 
171
181
  it('should scope the SSM KMS grant to ssm.kmsKeyArn when provided', () => {
@@ -375,7 +375,9 @@ Resources:
375
375
  Resource:
376
376
  - !Sub 'arn:aws:kms:*:${AWS::AccountId}:key/*'
377
377
  Condition:
378
- StringEquals:
378
+ # StringLike: kms:ViaService is regional (ssm.us-east-1.amazonaws.com),
379
+ # so the wildcard must be matched, not compared literally.
380
+ StringLike:
379
381
  'kms:ViaService':
380
382
  - 'ssm.*.amazonaws.com'
381
383
 
@@ -264,7 +264,7 @@
264
264
  "arn:aws:kms:*:*:key/*"
265
265
  ],
266
266
  "Condition": {
267
- "StringEquals": {
267
+ "StringLike": {
268
268
  "kms:ViaService": [
269
269
  "lambda.*.amazonaws.com",
270
270
  "s3.*.amazonaws.com",
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.625.3f500f2.0",
4
+ "version": "2.0.0--canary.625.0de0db4.0",
5
5
  "bin": {
6
6
  "frigg": "./frigg-cli/index.js"
7
7
  },
@@ -26,9 +26,9 @@
26
26
  "@babel/eslint-parser": "^7.18.9",
27
27
  "@babel/parser": "^7.25.3",
28
28
  "@babel/traverse": "^7.25.3",
29
- "@friggframework/core": "2.0.0--canary.625.3f500f2.0",
30
- "@friggframework/schemas": "2.0.0--canary.625.3f500f2.0",
31
- "@friggframework/test": "2.0.0--canary.625.3f500f2.0",
29
+ "@friggframework/core": "2.0.0--canary.625.0de0db4.0",
30
+ "@friggframework/schemas": "2.0.0--canary.625.0de0db4.0",
31
+ "@friggframework/test": "2.0.0--canary.625.0de0db4.0",
32
32
  "@hapi/boom": "^10.0.1",
33
33
  "@inquirer/prompts": "^5.3.8",
34
34
  "axios": "^1.18.0",
@@ -56,8 +56,8 @@
56
56
  "validate-npm-package-name": "^5.0.0"
57
57
  },
58
58
  "devDependencies": {
59
- "@friggframework/eslint-config": "2.0.0--canary.625.3f500f2.0",
60
- "@friggframework/prettier-config": "2.0.0--canary.625.3f500f2.0",
59
+ "@friggframework/eslint-config": "2.0.0--canary.625.0de0db4.0",
60
+ "@friggframework/prettier-config": "2.0.0--canary.625.0de0db4.0",
61
61
  "aws-sdk-client-mock": "^4.1.0",
62
62
  "aws-sdk-client-mock-jest": "^4.1.0",
63
63
  "jest": "^30.1.3",
@@ -89,5 +89,5 @@
89
89
  "publishConfig": {
90
90
  "access": "public"
91
91
  },
92
- "gitHead": "3f500f202c8dcf2210c07835c76abbab30e0d0ee"
92
+ "gitHead": "0de0db43a6403eb99de205e24fcde7e9761ba4f6"
93
93
  }