@friggframework/devtools 2.0.0--canary.461.b8c7c6b.0 → 2.0.0--canary.461.0afe1c4.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.
@@ -7,7 +7,7 @@
7
7
  * - KMS key creation or discovery
8
8
  * - KMS key configuration for field-level encryption
9
9
  * - IAM permissions for KMS operations
10
- * - KMS grants via serverless-kms-grants plugin
10
+ * - KMS key policy configuration for Lambda execution role
11
11
  */
12
12
 
13
13
  const { InfrastructureBuilder, ValidationResult } = require('../shared/base-builder');
@@ -73,28 +73,21 @@ class KmsBuilder extends InfrastructureBuilder {
73
73
  console.log(' Creating new KMS key...');
74
74
  result.resources = this.createKmsKey(appDefinition);
75
75
  result.environment.KMS_KEY_ARN = { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] };
76
- result.pluginConfig.kmsGrants = {
77
- kmsKeyId: { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] },
78
- };
79
76
  console.log(' ✅ KMS key resources created');
80
77
  } else {
81
78
  // Use discovered KMS key
82
79
  const kmsKeyId = discoveredResources.defaultKmsKeyId || '${env:AWS_DISCOVERY_KMS_KEY_ID}';
83
80
  console.log(` Using ${discoveredResources.defaultKmsKeyId ? 'discovered' : 'environment variable'} KMS key`);
84
81
  result.environment.KMS_KEY_ARN = kmsKeyId;
85
- result.pluginConfig.kmsGrants = { kmsKeyId };
86
82
  }
87
83
 
88
- // Add IAM permissions
84
+ // Add IAM permissions for Lambda role
89
85
  result.iamStatements.push({
90
86
  Effect: 'Allow',
91
- Action: ['kms:GenerateDataKey', 'kms:Decrypt'],
87
+ Action: ['kms:GenerateDataKey', 'kms:Decrypt', 'kms:Encrypt', 'kms:DescribeKey'],
92
88
  Resource: result.environment.KMS_KEY_ARN,
93
89
  });
94
90
 
95
- // Enable KMS grants plugin
96
- result.plugins.push('serverless-kms-grants');
97
-
98
91
  console.log(`[${this.name}] ✅ KMS configuration completed`);
99
92
  return result;
100
93
  }
@@ -110,12 +103,13 @@ class KmsBuilder extends InfrastructureBuilder {
110
103
  UpdateReplacePolicy: 'Retain',
111
104
  Properties: {
112
105
  Description: 'Frigg Field-Level Encryption Key for ${self:service}-${self:provider.stage}',
106
+ EnableKeyRotation: true,
113
107
  KeyPolicy: {
114
108
  Version: '2012-10-17',
115
109
  Id: 'key-policy-1',
116
110
  Statement: [
117
111
  {
118
- Sid: 'Enable IAM User Permissions',
112
+ Sid: 'AllowRootAccountAdmin',
119
113
  Effect: 'Allow',
120
114
  Principal: {
121
115
  AWS: {
@@ -126,7 +120,7 @@ class KmsBuilder extends InfrastructureBuilder {
126
120
  Resource: '*',
127
121
  },
128
122
  {
129
- Sid: 'Allow Lambda to use the key',
123
+ Sid: 'AllowLambdaService',
130
124
  Effect: 'Allow',
131
125
  Principal: {
132
126
  Service: 'lambda.amazonaws.com',
@@ -143,6 +137,20 @@ class KmsBuilder extends InfrastructureBuilder {
143
137
  },
144
138
  },
145
139
  },
140
+ {
141
+ Sid: 'AllowLambdaExecutionRole',
142
+ Effect: 'Allow',
143
+ Principal: {
144
+ AWS: { 'Fn::GetAtt': ['IamRoleLambdaExecution', 'Arn'] },
145
+ },
146
+ Action: [
147
+ 'kms:Decrypt',
148
+ 'kms:GenerateDataKey',
149
+ 'kms:Encrypt',
150
+ 'kms:DescribeKey',
151
+ ],
152
+ Resource: '*',
153
+ },
146
154
  ],
147
155
  },
148
156
  Tags: [
@@ -148,7 +148,7 @@ describe('KmsBuilder', () => {
148
148
  const result = await kmsBuilder.build(appDefinition, discoveredResources);
149
149
 
150
150
  expect(result.environment.KMS_KEY_ARN).toBe('arn:aws:kms:us-east-1:123456:key/abc-123');
151
- expect(result.pluginConfig.kmsGrants.kmsKeyId).toBe('arn:aws:kms:us-east-1:123456:key/abc-123');
151
+ expect(result.pluginConfig.kmsGrants).toBeUndefined();
152
152
  });
153
153
 
154
154
  it('should add IAM permissions for KMS operations', async () => {
@@ -167,12 +167,12 @@ describe('KmsBuilder', () => {
167
167
  expect(result.iamStatements).toHaveLength(1);
168
168
  expect(result.iamStatements[0]).toEqual({
169
169
  Effect: 'Allow',
170
- Action: ['kms:GenerateDataKey', 'kms:Decrypt'],
170
+ Action: ['kms:GenerateDataKey', 'kms:Decrypt', 'kms:Encrypt', 'kms:DescribeKey'],
171
171
  Resource: 'arn:aws:kms:us-east-1:123456:key/abc',
172
172
  });
173
173
  });
174
174
 
175
- it('should enable serverless-kms-grants plugin', async () => {
175
+ it('should NOT use serverless-kms-grants plugin (deprecated)', async () => {
176
176
  const appDefinition = {
177
177
  encryption: {
178
178
  fieldLevelEncryptionMethod: 'kms',
@@ -185,7 +185,8 @@ describe('KmsBuilder', () => {
185
185
 
186
186
  const result = await kmsBuilder.build(appDefinition, discoveredResources);
187
187
 
188
- expect(result.plugins).toContain('serverless-kms-grants');
188
+ expect(result.plugins).not.toContain('serverless-kms-grants');
189
+ expect(result.pluginConfig.kmsGrants).toBeUndefined();
189
190
  });
190
191
  });
191
192
 
@@ -316,6 +317,28 @@ describe('KmsBuilder', () => {
316
317
  expect(lambdaStatement.Action).toContain('kms:GenerateDataKey');
317
318
  expect(lambdaStatement.Action).toContain('kms:Decrypt');
318
319
  });
320
+
321
+ it('should create key policy allowing Lambda execution role direct access', async () => {
322
+ const appDefinition = {
323
+ encryption: {
324
+ fieldLevelEncryptionMethod: 'kms',
325
+ createResourceIfNoneFound: true,
326
+ },
327
+ };
328
+
329
+ const result = await kmsBuilder.build(appDefinition, {});
330
+
331
+ const policy = result.resources.FriggKMSKey.Properties.KeyPolicy;
332
+ const roleStatement = policy.Statement.find(s => s.Sid === 'AllowLambdaExecutionRole');
333
+
334
+ expect(roleStatement).toBeDefined();
335
+ expect(roleStatement.Effect).toBe('Allow');
336
+ expect(roleStatement.Principal.AWS).toEqual({ 'Fn::GetAtt': ['IamRoleLambdaExecution', 'Arn'] });
337
+ expect(roleStatement.Action).toContain('kms:GenerateDataKey');
338
+ expect(roleStatement.Action).toContain('kms:Decrypt');
339
+ expect(roleStatement.Action).toContain('kms:Encrypt');
340
+ expect(roleStatement.Action).toContain('kms:DescribeKey');
341
+ });
319
342
  });
320
343
 
321
344
  describe('Error handling', () => {
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.461.b8c7c6b.0",
4
+ "version": "2.0.0--canary.461.0afe1c4.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-ec2": "^3.835.0",
7
7
  "@aws-sdk/client-kms": "^3.835.0",
@@ -11,8 +11,8 @@
11
11
  "@babel/eslint-parser": "^7.18.9",
12
12
  "@babel/parser": "^7.25.3",
13
13
  "@babel/traverse": "^7.25.3",
14
- "@friggframework/schemas": "2.0.0--canary.461.b8c7c6b.0",
15
- "@friggframework/test": "2.0.0--canary.461.b8c7c6b.0",
14
+ "@friggframework/schemas": "2.0.0--canary.461.0afe1c4.0",
15
+ "@friggframework/test": "2.0.0--canary.461.0afe1c4.0",
16
16
  "@hapi/boom": "^10.0.1",
17
17
  "@inquirer/prompts": "^5.3.8",
18
18
  "axios": "^1.7.2",
@@ -34,8 +34,8 @@
34
34
  "serverless-http": "^2.7.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@friggframework/eslint-config": "2.0.0--canary.461.b8c7c6b.0",
38
- "@friggframework/prettier-config": "2.0.0--canary.461.b8c7c6b.0",
37
+ "@friggframework/eslint-config": "2.0.0--canary.461.0afe1c4.0",
38
+ "@friggframework/prettier-config": "2.0.0--canary.461.0afe1c4.0",
39
39
  "aws-sdk-client-mock": "^4.1.0",
40
40
  "aws-sdk-client-mock-jest": "^4.1.0",
41
41
  "jest": "^30.1.3",
@@ -70,5 +70,5 @@
70
70
  "publishConfig": {
71
71
  "access": "public"
72
72
  },
73
- "gitHead": "b8c7c6bedb12c3dd0c055eedae89456050458d48"
73
+ "gitHead": "0afe1c4c284cd5ee939bdcb7ef011f36098f76fc"
74
74
  }