@terra-graph/conventions-aws 1.0.0-rc.1

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.
Files changed (91) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +25 -0
  3. package/dist/cjs/conventions/dataflow/profiles/base.js +89 -0
  4. package/dist/cjs/conventions/dataflow/profiles/dot.js +44 -0
  5. package/dist/cjs/conventions/dataflow/profiles/dot.test.js +147 -0
  6. package/dist/cjs/conventions/dataflow/rules.js +4 -0
  7. package/dist/cjs/conventions/dataflow/rules.test.js +11 -0
  8. package/dist/cjs/conventions/dataflow/rulesets.js +751 -0
  9. package/dist/cjs/conventions/dataflow/rulesets.test.js +27 -0
  10. package/dist/cjs/conventions/index.js +7 -0
  11. package/dist/cjs/conventions/index.test.js +8 -0
  12. package/dist/cjs/index.js +70 -0
  13. package/dist/cjs/index.test.js +58 -0
  14. package/dist/cjs/namespaces.js +14 -0
  15. package/dist/cjs/namespaces.test.js +17 -0
  16. package/dist/cjs/package.json +1 -0
  17. package/dist/cjs/plugins/AwsApiGateway.js +218 -0
  18. package/dist/cjs/plugins/AwsApiGateway.test.js +427 -0
  19. package/dist/cjs/plugins/AwsIam.js +432 -0
  20. package/dist/cjs/plugins/AwsIam.test.js +468 -0
  21. package/dist/cjs/plugins/AwsS3.js +107 -0
  22. package/dist/cjs/plugins/AwsS3.test.js +102 -0
  23. package/dist/cjs/plugins/AwsSns.js +52 -0
  24. package/dist/cjs/plugins/AwsSns.test.js +55 -0
  25. package/dist/cjs/plugins/AwsTransferFamily.js +105 -0
  26. package/dist/cjs/plugins/AwsTransferFamily.test.js +279 -0
  27. package/dist/cjs/rules/dot.js +36 -0
  28. package/dist/cjs/rules/dot.test.js +42 -0
  29. package/dist/cjs/rules/general.js +69 -0
  30. package/dist/cjs/rules/general.test.js +47 -0
  31. package/dist/cjs/rulesets/dot.js +25 -0
  32. package/dist/cjs/rulesets/dot.test.js +48 -0
  33. package/dist/esm/conventions/dataflow/profiles/base.d.ts +4 -0
  34. package/dist/esm/conventions/dataflow/profiles/base.js +86 -0
  35. package/dist/esm/conventions/dataflow/profiles/dot.d.ts +4 -0
  36. package/dist/esm/conventions/dataflow/profiles/dot.js +38 -0
  37. package/dist/esm/conventions/dataflow/profiles/dot.test.d.ts +1 -0
  38. package/dist/esm/conventions/dataflow/profiles/dot.test.js +109 -0
  39. package/dist/esm/conventions/dataflow/rules.d.ts +3 -0
  40. package/dist/esm/conventions/dataflow/rules.js +2 -0
  41. package/dist/esm/conventions/dataflow/rules.test.d.ts +1 -0
  42. package/dist/esm/conventions/dataflow/rules.test.js +6 -0
  43. package/dist/esm/conventions/dataflow/rulesets.d.ts +3 -0
  44. package/dist/esm/conventions/dataflow/rulesets.js +749 -0
  45. package/dist/esm/conventions/dataflow/rulesets.test.d.ts +1 -0
  46. package/dist/esm/conventions/dataflow/rulesets.test.js +22 -0
  47. package/dist/esm/conventions/index.d.ts +3 -0
  48. package/dist/esm/conventions/index.js +4 -0
  49. package/dist/esm/conventions/index.test.d.ts +1 -0
  50. package/dist/esm/conventions/index.test.js +6 -0
  51. package/dist/esm/index.d.ts +3 -0
  52. package/dist/esm/index.js +32 -0
  53. package/dist/esm/index.test.d.ts +1 -0
  54. package/dist/esm/index.test.js +53 -0
  55. package/dist/esm/namespaces.d.ts +7 -0
  56. package/dist/esm/namespaces.js +6 -0
  57. package/dist/esm/namespaces.test.d.ts +1 -0
  58. package/dist/esm/namespaces.test.js +15 -0
  59. package/dist/esm/plugins/AwsApiGateway.d.ts +6 -0
  60. package/dist/esm/plugins/AwsApiGateway.js +214 -0
  61. package/dist/esm/plugins/AwsApiGateway.test.d.ts +1 -0
  62. package/dist/esm/plugins/AwsApiGateway.test.js +425 -0
  63. package/dist/esm/plugins/AwsIam.d.ts +32 -0
  64. package/dist/esm/plugins/AwsIam.js +428 -0
  65. package/dist/esm/plugins/AwsIam.test.d.ts +1 -0
  66. package/dist/esm/plugins/AwsIam.test.js +466 -0
  67. package/dist/esm/plugins/AwsS3.d.ts +12 -0
  68. package/dist/esm/plugins/AwsS3.js +103 -0
  69. package/dist/esm/plugins/AwsS3.test.d.ts +1 -0
  70. package/dist/esm/plugins/AwsS3.test.js +100 -0
  71. package/dist/esm/plugins/AwsSns.d.ts +6 -0
  72. package/dist/esm/plugins/AwsSns.js +48 -0
  73. package/dist/esm/plugins/AwsSns.test.d.ts +1 -0
  74. package/dist/esm/plugins/AwsSns.test.js +53 -0
  75. package/dist/esm/plugins/AwsTransferFamily.d.ts +6 -0
  76. package/dist/esm/plugins/AwsTransferFamily.js +101 -0
  77. package/dist/esm/plugins/AwsTransferFamily.test.d.ts +1 -0
  78. package/dist/esm/plugins/AwsTransferFamily.test.js +277 -0
  79. package/dist/esm/rules/dot.d.ts +3 -0
  80. package/dist/esm/rules/dot.js +34 -0
  81. package/dist/esm/rules/dot.test.d.ts +1 -0
  82. package/dist/esm/rules/dot.test.js +37 -0
  83. package/dist/esm/rules/general.d.ts +3 -0
  84. package/dist/esm/rules/general.js +67 -0
  85. package/dist/esm/rules/general.test.d.ts +1 -0
  86. package/dist/esm/rules/general.test.js +42 -0
  87. package/dist/esm/rulesets/dot.d.ts +3 -0
  88. package/dist/esm/rulesets/dot.js +23 -0
  89. package/dist/esm/rulesets/dot.test.d.ts +1 -0
  90. package/dist/esm/rulesets/dot.test.js +43 -0
  91. package/package.json +45 -0
@@ -0,0 +1,428 @@
1
+ import { ConvertNodeToEdge, EdgeReverse, GraphPlugin, NodeProperties, RemoveLeafChain, RemoveNodeAndReconnectEdges, } from '@terra-graph/core';
2
+ import { pluginId } from '../namespaces.js';
3
+ export const AWS_IAM_PLUGIN_MODES = ['roles_only', 'roles_policies', 'full'];
4
+ export const AWS_IAM_ATTACHMENT_MODES = ['remove', 'convert_to_edge', 'keep'];
5
+ export const AWS_IAM_POLICY_DOCUMENT_MODES = ['none', 'trust_only', 'all'];
6
+ const IAM_ROLE_RESOURCES = ['aws_iam_role'];
7
+ const IAM_POLICY_RESOURCES = ['aws_iam_policy', 'aws_iam_role_policy'];
8
+ const IAM_ATTACHMENT_RESOURCES = ['aws_iam_role_policy_attachment', 'aws_iam_policy_attachment'];
9
+ const IAM_POLICY_DOCUMENT_RESOURCE = 'aws_iam_policy_document';
10
+ const IAM_DATA_POLICY_RESOURCE = 'aws_iam_policy';
11
+ const IAM_ORPHAN_PRUNABLE_RESOURCES = [
12
+ ...IAM_ROLE_RESOURCES,
13
+ ...IAM_POLICY_RESOURCES,
14
+ ...IAM_ATTACHMENT_RESOURCES,
15
+ ];
16
+ const IAM_ORPHAN_PRUNABLE_DATA_RESOURCES = [IAM_POLICY_DOCUMENT_RESOURCE, IAM_DATA_POLICY_RESOURCE];
17
+ export class AwsIamGraphPlugin extends GraphPlugin {
18
+ static id = pluginId(`aws.${AwsIamGraphPlugin.name}`);
19
+ constructor() {
20
+ super(AwsIamGraphPlugin.id, {
21
+ mode: 'roles_only',
22
+ });
23
+ }
24
+ build({ options, }) {
25
+ const resolved = this.resolveOptions(options);
26
+ const phases = [
27
+ ...this.toPhases('normalize', this.buildIamDirectionPhases()),
28
+ ...this.toPhases('main', this.buildIamLabelHintPhases()),
29
+ ...this.toPhases('main', this.buildAttachmentPhases(resolved)),
30
+ ];
31
+ if (resolved.mode !== 'full') {
32
+ phases.push(...this.toPhases('main', [
33
+ [
34
+ new RemoveNodeAndReconnectEdges({
35
+ node: {
36
+ and: [
37
+ {
38
+ attr: {
39
+ key: 'terraform.resource',
40
+ startsWith: 'aws_iam_',
41
+ },
42
+ },
43
+ {
44
+ not: {
45
+ attr: {
46
+ key: 'terraform.resource',
47
+ in: this.buildKeepResourcesForMode(resolved),
48
+ },
49
+ },
50
+ },
51
+ ],
52
+ },
53
+ }),
54
+ ],
55
+ ]));
56
+ if (resolved.policyDocuments === 'trust_only') {
57
+ phases.push(...this.toPhases('main', this.buildPolicyDocumentPhases(resolved)));
58
+ }
59
+ phases.push(...this.buildCleanupPhases(resolved));
60
+ return { phases };
61
+ }
62
+ phases.push(...this.toPhases('main', this.buildPolicyDocumentPhases(resolved)));
63
+ phases.push(...this.buildCleanupPhases(resolved));
64
+ return { phases };
65
+ }
66
+ toPhases(phase, phases) {
67
+ return phases.map((rules) => ({ phase, rules }));
68
+ }
69
+ isEnumValue(value, values) {
70
+ return typeof value === 'string' && values.includes(value);
71
+ }
72
+ resolveOptions(options) {
73
+ const mode = options.mode ?? 'roles_only';
74
+ if (!this.isEnumValue(mode, AWS_IAM_PLUGIN_MODES)) {
75
+ throw new Error(`${this.name} options.mode must be one of: ${AWS_IAM_PLUGIN_MODES.join(', ')}`);
76
+ }
77
+ const attachments = options.attachments ?? (mode === 'full' ? 'keep' : 'remove');
78
+ if (!this.isEnumValue(attachments, AWS_IAM_ATTACHMENT_MODES)) {
79
+ throw new Error(`${this.name} options.attachments must be one of: ${AWS_IAM_ATTACHMENT_MODES.join(', ')}`);
80
+ }
81
+ const policyDocuments = options.policyDocuments ?? (mode === 'full' ? 'all' : 'none');
82
+ if (!this.isEnumValue(policyDocuments, AWS_IAM_POLICY_DOCUMENT_MODES)) {
83
+ throw new Error(`${this.name} options.policyDocuments must be one of: ${AWS_IAM_POLICY_DOCUMENT_MODES.join(', ')}`);
84
+ }
85
+ const removeOrphans = options.removeOrphans ?? false;
86
+ if (typeof removeOrphans !== 'boolean') {
87
+ throw new Error(`${this.name} options.removeOrphans must be a boolean`);
88
+ }
89
+ return {
90
+ mode,
91
+ attachments,
92
+ policyDocuments,
93
+ removeOrphans,
94
+ };
95
+ }
96
+ buildKeepResourcesForMode(options) {
97
+ const keep = new Set(IAM_ROLE_RESOURCES);
98
+ if (options.mode === 'roles_policies') {
99
+ for (const resource of IAM_POLICY_RESOURCES) {
100
+ keep.add(resource);
101
+ }
102
+ }
103
+ if (options.attachments === 'keep') {
104
+ for (const resource of IAM_ATTACHMENT_RESOURCES) {
105
+ keep.add(resource);
106
+ }
107
+ }
108
+ if (options.policyDocuments !== 'none') {
109
+ keep.add(IAM_POLICY_DOCUMENT_RESOURCE);
110
+ }
111
+ return [...keep];
112
+ }
113
+ buildAttachmentPhases(options) {
114
+ if (options.attachments === 'keep') {
115
+ return [];
116
+ }
117
+ const phases = [];
118
+ if (options.attachments === 'convert_to_edge') {
119
+ phases.push([
120
+ new ConvertNodeToEdge({
121
+ node: {
122
+ attr: {
123
+ key: 'terraform.resource',
124
+ in: IAM_ATTACHMENT_RESOURCES,
125
+ },
126
+ },
127
+ }),
128
+ ]);
129
+ }
130
+ phases.push([
131
+ new RemoveNodeAndReconnectEdges({
132
+ node: {
133
+ attr: {
134
+ key: 'terraform.resource',
135
+ in: IAM_ATTACHMENT_RESOURCES,
136
+ },
137
+ },
138
+ }),
139
+ ]);
140
+ return phases;
141
+ }
142
+ buildIamDirectionPhases() {
143
+ return [
144
+ [
145
+ new EdgeReverse({
146
+ edge: {
147
+ from: this.iamDataQuery(),
148
+ to: this.iamResourceQuery('aws_iam_role_policy'),
149
+ },
150
+ }),
151
+ new EdgeReverse({
152
+ edge: {
153
+ from: this.iamResourceQuery('aws_iam_role_policy'),
154
+ to: this.iamResourceQuery('aws_iam_role_policy_attachment'),
155
+ },
156
+ }),
157
+ new EdgeReverse({
158
+ edge: {
159
+ from: this.iamDataQuery(),
160
+ to: this.iamResourceQuery('aws_iam_policy'),
161
+ },
162
+ }),
163
+ new EdgeReverse({
164
+ edge: {
165
+ from: this.iamResourceQuery('aws_iam_policy'),
166
+ to: this.iamResourceQuery('aws_iam_role_policy_attachment'),
167
+ },
168
+ }),
169
+ new EdgeReverse({
170
+ edge: {
171
+ from: this.iamResourceQuery('aws_iam_policy'),
172
+ to: this.iamResourceQuery('aws_iam_policy_attachment'),
173
+ },
174
+ }),
175
+ new EdgeReverse({
176
+ edge: {
177
+ from: this.iamResourceQuery('aws_iam_role'),
178
+ to: this.iamAttachmentQuery(),
179
+ },
180
+ }),
181
+ new EdgeReverse({
182
+ edge: {
183
+ from: this.iamAttachmentQuery(),
184
+ to: this.anyNonIamResourceQuery(),
185
+ },
186
+ }),
187
+ new EdgeReverse({
188
+ edge: {
189
+ from: this.iamDataQuery(),
190
+ to: this.iamResourceQuery('aws_iam_role'),
191
+ },
192
+ }),
193
+ new EdgeReverse({
194
+ edge: {
195
+ from: this.iamResourceQuery('aws_iam_role'),
196
+ to: this.anyNonIamResourceQuery(),
197
+ },
198
+ }),
199
+ ],
200
+ ];
201
+ }
202
+ buildIamLabelHintPhases() {
203
+ return [
204
+ [
205
+ new NodeProperties({
206
+ node: {
207
+ and: [
208
+ {
209
+ attr: {
210
+ key: 'terraform.kind',
211
+ in: ['resource', 'data'],
212
+ },
213
+ },
214
+ {
215
+ attr: {
216
+ key: 'terraform.resource',
217
+ startsWith: 'aws_iam_',
218
+ },
219
+ },
220
+ {
221
+ attr: {
222
+ key: 'terraform.moduleAddress',
223
+ exists: true,
224
+ },
225
+ },
226
+ ],
227
+ },
228
+ options: {
229
+ hints: {
230
+ label: {
231
+ end: {
232
+ from: 'terraform.name',
233
+ },
234
+ },
235
+ },
236
+ },
237
+ }),
238
+ ],
239
+ ];
240
+ }
241
+ iamDataQuery() {
242
+ return {
243
+ and: [
244
+ {
245
+ attr: {
246
+ key: 'terraform.kind',
247
+ eq: 'data',
248
+ },
249
+ },
250
+ {
251
+ attr: {
252
+ key: 'terraform.resource',
253
+ startsWith: 'aws_iam_',
254
+ },
255
+ },
256
+ ],
257
+ };
258
+ }
259
+ iamResourceQuery(resource) {
260
+ return {
261
+ and: [
262
+ {
263
+ attr: {
264
+ key: 'terraform.kind',
265
+ eq: 'resource',
266
+ },
267
+ },
268
+ {
269
+ attr: {
270
+ key: 'terraform.resource',
271
+ eq: resource,
272
+ },
273
+ },
274
+ ],
275
+ };
276
+ }
277
+ iamAttachmentQuery() {
278
+ return {
279
+ and: [
280
+ {
281
+ attr: {
282
+ key: 'terraform.kind',
283
+ eq: 'resource',
284
+ },
285
+ },
286
+ {
287
+ attr: {
288
+ key: 'terraform.resource',
289
+ in: IAM_ATTACHMENT_RESOURCES,
290
+ },
291
+ },
292
+ ],
293
+ };
294
+ }
295
+ anyNonIamResourceQuery() {
296
+ return {
297
+ and: [
298
+ {
299
+ attr: {
300
+ key: 'terraform.kind',
301
+ eq: 'resource',
302
+ },
303
+ },
304
+ {
305
+ not: {
306
+ attr: {
307
+ key: 'terraform.resource',
308
+ startsWith: 'aws_iam_',
309
+ },
310
+ },
311
+ },
312
+ ],
313
+ };
314
+ }
315
+ orphanPrunableIamQuery() {
316
+ return {
317
+ or: [
318
+ {
319
+ and: [
320
+ {
321
+ attr: {
322
+ key: 'terraform.kind',
323
+ eq: 'resource',
324
+ },
325
+ },
326
+ {
327
+ attr: {
328
+ key: 'terraform.resource',
329
+ in: IAM_ORPHAN_PRUNABLE_RESOURCES,
330
+ },
331
+ },
332
+ ],
333
+ },
334
+ {
335
+ and: [
336
+ {
337
+ attr: {
338
+ key: 'terraform.kind',
339
+ eq: 'data',
340
+ },
341
+ },
342
+ {
343
+ attr: {
344
+ key: 'terraform.resource',
345
+ in: IAM_ORPHAN_PRUNABLE_DATA_RESOURCES,
346
+ },
347
+ },
348
+ ],
349
+ },
350
+ ],
351
+ };
352
+ }
353
+ buildCleanupPhases(options) {
354
+ const phases = [...this.toPhases('cleanup', this.buildIamDirectionPhases())];
355
+ if (!options.removeOrphans) {
356
+ return phases;
357
+ }
358
+ phases.push(...this.toPhases('cleanup', [
359
+ [
360
+ new RemoveLeafChain({
361
+ node: this.orphanPrunableIamQuery(),
362
+ }),
363
+ ],
364
+ ]));
365
+ return phases;
366
+ }
367
+ buildPolicyDocumentPhases(options) {
368
+ if (options.policyDocuments === 'all') {
369
+ return [];
370
+ }
371
+ if (options.policyDocuments === 'none') {
372
+ return [
373
+ [
374
+ new RemoveNodeAndReconnectEdges({
375
+ node: {
376
+ attr: {
377
+ key: 'terraform.resource',
378
+ eq: IAM_POLICY_DOCUMENT_RESOURCE,
379
+ },
380
+ },
381
+ }),
382
+ ],
383
+ ];
384
+ }
385
+ return [
386
+ [
387
+ new RemoveNodeAndReconnectEdges({
388
+ node: {
389
+ and: [
390
+ {
391
+ attr: {
392
+ key: 'terraform.resource',
393
+ eq: IAM_POLICY_DOCUMENT_RESOURCE,
394
+ },
395
+ },
396
+ {
397
+ not: {
398
+ or: [
399
+ {
400
+ edge: {
401
+ in: {
402
+ attr: {
403
+ key: 'terraform.resource',
404
+ eq: 'aws_iam_role',
405
+ },
406
+ },
407
+ },
408
+ },
409
+ {
410
+ edge: {
411
+ out: {
412
+ attr: {
413
+ key: 'terraform.resource',
414
+ eq: 'aws_iam_role',
415
+ },
416
+ },
417
+ },
418
+ },
419
+ ],
420
+ },
421
+ },
422
+ ],
423
+ },
424
+ }),
425
+ ],
426
+ ];
427
+ }
428
+ }
@@ -0,0 +1 @@
1
+ export {};