@cloud-copilot/iam-expand 0.1.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.
Files changed (84) hide show
  1. package/LICENSE.txt +674 -0
  2. package/README.md +269 -0
  3. package/dist/cjs/cli.d.ts +3 -0
  4. package/dist/cjs/cli.d.ts.map +1 -0
  5. package/dist/cjs/cli.js +76 -0
  6. package/dist/cjs/cli.js.map +1 -0
  7. package/dist/cjs/cli_utils.d.ts +27 -0
  8. package/dist/cjs/cli_utils.d.ts.map +1 -0
  9. package/dist/cjs/cli_utils.js +69 -0
  10. package/dist/cjs/cli_utils.js.map +1 -0
  11. package/dist/cjs/expand.d.ts +69 -0
  12. package/dist/cjs/expand.d.ts.map +1 -0
  13. package/dist/cjs/expand.js +118 -0
  14. package/dist/cjs/expand.js.map +1 -0
  15. package/dist/cjs/index.d.ts +2 -0
  16. package/dist/cjs/index.d.ts.map +1 -0
  17. package/dist/cjs/index.js +18 -0
  18. package/dist/cjs/index.js.map +1 -0
  19. package/dist/cjs/package.json +3 -0
  20. package/dist/cjs/stdin.d.ts +7 -0
  21. package/dist/cjs/stdin.d.ts.map +1 -0
  22. package/dist/cjs/stdin.js +34 -0
  23. package/dist/cjs/stdin.js.map +1 -0
  24. package/dist/cli.d.ts +3 -0
  25. package/dist/cli.d.ts.map +1 -0
  26. package/dist/cli.js +67 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/cli_utils.d.ts +27 -0
  29. package/dist/cli_utils.d.ts.map +1 -0
  30. package/dist/cli_utils.js +57 -0
  31. package/dist/cli_utils.js.map +1 -0
  32. package/dist/cli_utils.test.d.ts +2 -0
  33. package/dist/cli_utils.test.d.ts.map +1 -0
  34. package/dist/cli_utils.test.js +90 -0
  35. package/dist/cli_utils.test.js.map +1 -0
  36. package/dist/esm/cli.d.ts +3 -0
  37. package/dist/esm/cli.d.ts.map +1 -0
  38. package/dist/esm/cli.js +74 -0
  39. package/dist/esm/cli.js.map +1 -0
  40. package/dist/esm/cli_utils.d.ts +27 -0
  41. package/dist/esm/cli_utils.d.ts.map +1 -0
  42. package/dist/esm/cli_utils.js +63 -0
  43. package/dist/esm/cli_utils.js.map +1 -0
  44. package/dist/esm/expand.d.ts +69 -0
  45. package/dist/esm/expand.d.ts.map +1 -0
  46. package/dist/esm/expand.js +114 -0
  47. package/dist/esm/expand.js.map +1 -0
  48. package/dist/esm/index.d.ts +2 -0
  49. package/dist/esm/index.d.ts.map +1 -0
  50. package/dist/esm/index.js +2 -0
  51. package/dist/esm/index.js.map +1 -0
  52. package/dist/esm/package.json +3 -0
  53. package/dist/esm/stdin.d.ts +7 -0
  54. package/dist/esm/stdin.d.ts.map +1 -0
  55. package/dist/esm/stdin.js +31 -0
  56. package/dist/esm/stdin.js.map +1 -0
  57. package/dist/expand.d.ts +55 -0
  58. package/dist/expand.d.ts.map +1 -0
  59. package/dist/expand.js +94 -0
  60. package/dist/expand.js.map +1 -0
  61. package/dist/expand.test.d.ts +2 -0
  62. package/dist/expand.test.d.ts.map +1 -0
  63. package/dist/expand.test.js +382 -0
  64. package/dist/expand.test.js.map +1 -0
  65. package/dist/index.d.ts +2 -0
  66. package/dist/index.d.ts.map +1 -0
  67. package/dist/index.js +18 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/stdin.d.ts +7 -0
  70. package/dist/stdin.d.ts.map +1 -0
  71. package/dist/stdin.js +35 -0
  72. package/dist/stdin.js.map +1 -0
  73. package/package.json +33 -0
  74. package/postbuild.sh +13 -0
  75. package/src/cli.ts +82 -0
  76. package/src/cli_utils.test.ts +131 -0
  77. package/src/cli_utils.ts +78 -0
  78. package/src/expand.test.ts +523 -0
  79. package/src/expand.ts +185 -0
  80. package/src/index.ts +1 -0
  81. package/src/stdin.ts +34 -0
  82. package/tsconfig.cjs.json +11 -0
  83. package/tsconfig.esm.json +14 -0
  84. package/tsconfig.json +22 -0
@@ -0,0 +1,131 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { convertOptions, dashToCamelCase, extractActionsFromLineOfInput, parseStdInActions } from "./cli_utils";
3
+ import { InvalidActionBehavior } from './expand.js';
4
+ import { readStdin } from './stdin';
5
+ vi.mock('./stdin')
6
+
7
+ beforeEach(() => {
8
+ vi.resetAllMocks()
9
+ })
10
+
11
+ const extractScenarios = [
12
+ {input: ' s3:Get* ', expected: ['s3:Get*']},
13
+ {input: ' s3:Get* s3:Put* ', expected: ['s3:Get*', 's3:Put*']},
14
+ {input: ' "s3:Get*", "s3:Put*"', expected: ['s3:Get*', 's3:Put*']},
15
+ {input: ' `s3:Get*`, `s3:Put*`', expected: ['s3:Get*', 's3:Put*']},
16
+ {input: ` 's3:Get*', 's3:Put*'`, expected: ['s3:Get*', 's3:Put*']},
17
+ {input: `s3:Get*, s3:Put*`, expected: ['s3:Get*', 's3:Put*']},
18
+ {input: "s3:Put*", expected: ['s3:Put*']},
19
+ {input: ":s3:Put*", expected: []},
20
+ {input: 'arn:aws:apigateway:*::/apis', expected: []},
21
+ {input: 'hamburger', expected: []},
22
+ ]
23
+
24
+ const dashToCamelCaseScenarios = [
25
+ {input: "--distinct", expected: "distinct"},
26
+ {input: "--sort", expected: "sort"},
27
+ {input: "--expand-asterik", expected: "expandAsterik"},
28
+ {input: "--expand-service-asterik", expected: "expandServiceAsterik"},
29
+ {input: "--error-on-missing-service", expected: "errorOnMissingService"},
30
+ {input: "--error-on-invalid-format", expected: "errorOnInvalidFormat"},
31
+ {input: "--show-data-version", expected: "showDataVersion"},
32
+ ]
33
+
34
+ describe('cli_utils', () => {
35
+ describe('extractActionsFromLineOfInput', () => {
36
+ extractScenarios.forEach((scenario, index) => {
37
+ it(`should return for scenario ${index}: ${scenario.input} `, () => {
38
+ // Given the input
39
+ const input = scenario.input
40
+
41
+ // When the actions are extracted
42
+ const result = extractActionsFromLineOfInput(input)
43
+
44
+ // Then I should get the expected result
45
+ expect(result).toEqual(scenario.expected)
46
+ })
47
+ })
48
+ })
49
+
50
+ describe('dashToCamelCase', () => {
51
+ dashToCamelCaseScenarios.forEach((scenario, index) => {
52
+ it(`should return for scenario ${index}: ${scenario.input} `, () => {
53
+ // Given the input
54
+ const input = scenario.input
55
+
56
+ // When the input is converted
57
+ const result = dashToCamelCase(input)
58
+
59
+ // Then I should get the expected result
60
+ expect(result).toEqual(scenario.expected)
61
+ })
62
+ })
63
+ })
64
+
65
+ describe('convertOptions', () => {
66
+ it('should convert the options to keys on an object', () => {
67
+ // Given options as an array of strings
68
+ const optionArgs = ['--distinct', '--sort', '--something-cool', '--key-with-value=10']
69
+
70
+ // When the options are converted
71
+ const result = convertOptions(optionArgs)
72
+
73
+ // Then each option should be a key on the object
74
+ expect(result).toEqual({
75
+ distinct: true,
76
+ sort: true,
77
+ somethingCool: true,
78
+ keyWithValue: "10",
79
+ })
80
+ })
81
+
82
+ it('should convert the invalidActionBehavior to an enum', () => {
83
+ // Given an invalid action behavior option
84
+ const optionArgs = ['--invalid-action-behavior=error']
85
+
86
+ // When the options are converted
87
+ const result = convertOptions(optionArgs)
88
+
89
+ // Then the invalidActionBehavior should be an enum
90
+ expect(result).toEqual({
91
+ invalidActionBehavior: InvalidActionBehavior.Error,
92
+ })
93
+ })
94
+
95
+ it('should remove the invalidActionBehavior if the value is not valid', () => {
96
+ // Given an invalid action behavior option
97
+ const optionArgs = ['--invalid-action-behavior=not-a-real-value']
98
+
99
+ // When the options are converted
100
+ const result = convertOptions(optionArgs)
101
+
102
+ // Then the invalidActionBehavior should be removed
103
+ expect(result).toEqual({})
104
+ })
105
+ })
106
+
107
+ describe('parseStdInActions', () => {
108
+ it('should return an empty array if no data is provided', async () => {
109
+ // Given no data is provided
110
+ vi.mocked(readStdin).mockResolvedValue('')
111
+
112
+ // When the actions are parsed
113
+ const result = await parseStdInActions({})
114
+
115
+ // Then I should get an empty array
116
+ expect(result).toEqual([])
117
+ })
118
+
119
+ it('should return an array of actions from the data', async () => {
120
+ // Given there is data with actions in multiple lines
121
+ vi.mocked(readStdin).mockResolvedValue('s3:GetObject\ns3:PutObject\ns3:DeleteObject\n')
122
+
123
+ // When the actions are parsed
124
+ const result = await parseStdInActions({})
125
+
126
+ // Then I should get the expected actions
127
+ expect(result).toEqual(['s3:GetObject', 's3:PutObject', 's3:DeleteObject'])
128
+ })
129
+ })
130
+ })
131
+
@@ -0,0 +1,78 @@
1
+
2
+ import { ExpandIamActionsOptions, InvalidActionBehavior } from "./expand.js";
3
+ import { readStdin } from "./stdin.js";
4
+
5
+ interface CliOptions extends ExpandIamActionsOptions {
6
+ showDataVersion: boolean
7
+ readWaitTime: string
8
+ }
9
+
10
+ /**
11
+ * Convert a dash-case string to camelCase
12
+ * @param str the string to convert
13
+ * @returns the camelCase string
14
+ */
15
+ export function dashToCamelCase(str: string): string {
16
+ str = str.substring(2)
17
+ return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
18
+ }
19
+
20
+ /**
21
+ * Convert an array of option strings to an object
22
+ *
23
+ * @param optionArgs the array of option strings to convert
24
+ * @returns the object representation of the options
25
+ */
26
+ export function convertOptions(optionArgs: string[]): Partial<CliOptions> {
27
+ const options: Record<string, string | boolean> = {} ;
28
+
29
+ for(const option of optionArgs) {
30
+ let key: string = option
31
+ let value: boolean | string = true
32
+ if(option.includes('=')) {
33
+ [key,value] = option.split('=')
34
+ }
35
+
36
+ options[dashToCamelCase(key)] = value
37
+ }
38
+
39
+ if(typeof(options.invalidActionBehavior) === 'string') {
40
+ const behaviorString = options.invalidActionBehavior as string
41
+ const cleanedInput = behaviorString.charAt(0).toUpperCase() + behaviorString.slice(1).toLowerCase();
42
+ const behavior = InvalidActionBehavior[cleanedInput as keyof typeof InvalidActionBehavior];
43
+ if(behavior) {
44
+ options.invalidActionBehavior = behavior
45
+ } else {
46
+ delete options['invalidActionBehavior']
47
+ }
48
+ }
49
+
50
+ return options
51
+ }
52
+
53
+ const actionPattern = /\:?([a-zA-Z0-9]+:[a-zA-Z0-9*]+)/g;
54
+ export function extractActionsFromLineOfInput(line: string): string[] {
55
+ const matches = line.matchAll(actionPattern)
56
+
57
+ return Array.from(matches)
58
+ .filter((match) => !match[0].startsWith('arn:') && !match[0].startsWith(':'))
59
+ .map((match) => match[1])
60
+ }
61
+
62
+ /**
63
+ * Parse the actions from stdin
64
+ *
65
+ * @returns an array of strings from stdin
66
+ */
67
+ export async function parseStdInActions(options: Partial<CliOptions>): Promise<string[]> {
68
+ const delay = options.readWaitTime ? parseInt(options.readWaitTime.replaceAll(/\D/g, '')) : undefined
69
+ const data = await readStdin(delay)
70
+ if(data.length === 0) {
71
+ return []
72
+ }
73
+
74
+ const lines = data.split('\n')
75
+
76
+ const actions = lines.flatMap(line => extractActionsFromLineOfInput(line))
77
+ return actions
78
+ }