az2aws 1.1.3 → 1.3.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.
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "1.1.3"
2
+ ".": "1.3.0"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.3.0](https://github.com/kuma0128/az2aws/compare/v1.2.0...v1.3.0) (2026-01-27)
4
+
5
+
6
+ ### Features
7
+
8
+ * Add Comprehensive Loginstate Tests and Implementation ([#110](https://github.com/kuma0128/az2aws/issues/110)) ([09fce50](https://github.com/kuma0128/az2aws/commit/09fce501050731e2b0e5063ef8d2d5e4076827a1))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * Apply Prettier formatting and add region logging to login functions ([#109](https://github.com/kuma0128/az2aws/issues/109)) ([26a5ef6](https://github.com/kuma0128/az2aws/commit/26a5ef636014f56ad77813f42e866a25b08be2f3))
14
+ * clarify session duration validation message ([#91](https://github.com/kuma0128/az2aws/issues/91)) ([22d87ec](https://github.com/kuma0128/az2aws/commit/22d87ec57318c6dc69d09e098d7e123d07df882e))
15
+ * correct typo 'occured' to 'occurred' in error message ([#86](https://github.com/kuma0128/az2aws/issues/86)) ([b0a75d2](https://github.com/kuma0128/az2aws/commit/b0a75d2c7cecaef7888d17a9880bcd63000713b8))
16
+ * enforce defaults for no-prompt role selection ([#90](https://github.com/kuma0128/az2aws/issues/90)) ([bea40f6](https://github.com/kuma0128/az2aws/commit/bea40f64a9dc6851b34a396fdd315752669c1d9d))
17
+
18
+ ## [1.2.0](https://github.com/kuma0128/az2aws/compare/v1.1.3...v1.2.0) (2026-01-22)
19
+
20
+
21
+ ### Features
22
+
23
+ * Add version flag to CLI command ([#67](https://github.com/kuma0128/az2aws/issues/67)) ([b89290f](https://github.com/kuma0128/az2aws/commit/b89290fe16d554f6a8a1bf09df0194a64d692b55))
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * Add default duration of 1 hour if parsing fails ([#70](https://github.com/kuma0128/az2aws/issues/70)) ([337d12c](https://github.com/kuma0128/az2aws/commit/337d12c90ab0eccf4034bd438ecfb2f0ad98db47))
29
+ * Fix SAML assertion decoding to use UTF-8 encoding ([#72](https://github.com/kuma0128/az2aws/issues/72)) ([14e44a7](https://github.com/kuma0128/az2aws/commit/14e44a7ffe19e48177078aa6d87feb7e8ecf0c94))
30
+ * resolve --no-verify-ssl and proxy settings conflict ([#73](https://github.com/kuma0128/az2aws/issues/73)) ([3fd9adf](https://github.com/kuma0128/az2aws/commit/3fd9adf6dd39109bb428b56215200ad1df3752a9))
31
+
3
32
  ## [1.1.3](https://github.com/kuma0128/az2aws/compare/v1.1.2...v1.1.3) (2026-01-19)
4
33
 
5
34
 
package/README.md CHANGED
@@ -5,13 +5,13 @@
5
5
 
6
6
  # az2aws
7
7
 
8
- If your organization uses [Azure Active Directory](https://azure.microsoft.com) to provide SSO login to the AWS console, then there is no easy way to log in on the command line or to use the [AWS CLI](https://aws.amazon.com/cli/). This tool fixes that. It lets you use the normal Azure AD login (including MFA) from a command line to create a federated AWS session and places the temporary credentials in the proper place for the AWS CLI and SDKs.
8
+ Log in to AWS CLI using [Azure Active Directory](https://azure.microsoft.com) SSO. Supports MFA and places temporary credentials in the proper location for AWS CLI and SDKs.
9
9
 
10
10
  ## Installation
11
11
 
12
12
  ### mise (Recommended)
13
13
 
14
- [mise](https://mise.jdx.dev/) is a polyglot version manager that can install az2aws directly.
14
+ [mise](https://mise.jdx.dev/) is a version manager that can install az2aws directly.
15
15
 
16
16
  Install mise:
17
17
 
@@ -66,12 +66,10 @@ Run az2aws with a volume mounted to your AWS configuration directory:
66
66
 
67
67
  docker run --rm -it -v ~/.aws:/root/.aws az2aws/az2aws
68
68
 
69
- The Docker image is configured with an entrypoint so you can just feed any arguments in at the end.
70
-
71
- You can also put the docker-launch.sh script into your bin directory for the az2aws command to function as usual:
69
+ You can also install the docker-launch.sh script to your PATH:
72
70
 
73
71
  # Download the script (replace VERSION with a specific release tag, e.g., v1.0.0)
74
- curl -o /tmp/az2aws https://raw.githubusercontent.com/az2aws/az2aws/VERSION/docker-launch.sh -L
72
+ curl -o /tmp/az2aws https://raw.githubusercontent.com/kuma0128/az2aws/VERSION/docker-launch.sh -L
75
73
 
76
74
  # IMPORTANT: Review the script before installing
77
75
  cat /tmp/az2aws
@@ -80,9 +78,7 @@ You can also put the docker-launch.sh script into your bin directory for the az2
80
78
  sudo mv /tmp/az2aws /usr/local/bin/az2aws
81
79
  sudo chmod +x /usr/local/bin/az2aws
82
80
 
83
- > **Security Note:** Always download from a specific release tag (not `main`) and review the script contents before installing. Downloading and executing scripts directly from mutable branch heads poses a supply chain risk.
84
-
85
- Now just run `az2aws`.
81
+ > **Security Note:** Always download from a specific release tag (not `main`) and review the script before installing.
86
82
 
87
83
  ### Snap
88
84
 
@@ -104,13 +100,12 @@ https://snapcraft.io/az2aws
104
100
  | `--enable-chrome-seamless-sso` | Enable Azure AD Seamless SSO |
105
101
  | `--no-disable-extensions` | Keep browser extensions enabled |
106
102
  | `--disable-gpu` | Disable GPU acceleration |
103
+ | `--version (-v)` | Show version number |
107
104
 
108
105
  ## Usage
109
106
 
110
107
  ### Configuration
111
108
 
112
- #### AWS
113
-
114
109
  To configure the az2aws client run:
115
110
 
116
111
  az2aws --configure
@@ -119,22 +114,16 @@ You'll need your [Azure Tenant ID and the App ID URI](#getting-your-tenant-id-an
119
114
 
120
115
  az2aws --configure --profile foo
121
116
 
122
- ##### GovCloud Support
123
-
124
- To use az2aws with AWS GovCloud, set the `region` profile property in your ~/.aws/config to the one of the GovCloud regions:
117
+ #### GovCloud / China Region Support
125
118
 
126
- - us-gov-west-1
127
- - us-gov-east-1
119
+ Set the `region` in your ~/.aws/config to use non-standard AWS partitions:
128
120
 
129
- ##### China Region Support
130
-
131
- To use az2aws with AWS China Cloud, set the `region` profile property in your ~/.aws/config to the China region:
132
-
133
- - cn-north-1
121
+ - **GovCloud**: us-gov-west-1, us-gov-east-1
122
+ - **China**: cn-north-1, cn-northwest-1
134
123
 
135
124
  #### Stay Logged In
136
125
 
137
- During configuration, you can enable "Stay logged in" to skip username/password/MFA on subsequent logins. Session cookies will remember your identity, allowing you to use `--no-prompt` without storing passwords:
126
+ Enable "Stay logged in" during configuration to use `--no-prompt` without storing passwords:
138
127
 
139
128
  az2aws --no-prompt
140
129
  az2aws --profile foo --no-prompt
@@ -147,6 +136,10 @@ You can set defaults via environment variables (use with `--no-prompt`):
147
136
  - `AZURE_DEFAULT_USERNAME` / `AZURE_DEFAULT_PASSWORD` - Credentials
148
137
  - `AZURE_DEFAULT_ROLE_ARN` / `AZURE_DEFAULT_DURATION_HOURS` - AWS role settings
149
138
 
139
+ When using `--no-prompt` with multiple available roles, you must set
140
+ `AZURE_DEFAULT_ROLE_ARN` (or configure `azure_default_role_arn`) so the CLI can
141
+ select a role without prompting.
142
+
150
143
  To avoid storing passwords in bash history, use a leading space:
151
144
 
152
145
  HISTCONTROL=ignoreboth
@@ -154,28 +147,26 @@ To avoid storing passwords in bash history, use a leading space:
154
147
 
155
148
  #### Use an Existing Chrome Install and Profile
156
149
 
157
- Instead of using the bundled Chromium, you can use an existing Chrome installation with your own user profile by setting the following environment variables:
150
+ Use your own Chrome installation by setting these environment variables:
158
151
 
159
152
  - `BROWSER_CHROME_BIN` - Path to Chrome executable
160
153
  - `BROWSER_USER_DATA_DIR` - Chrome user data directory
161
154
  - `BROWSER_PROFILE_DIR` - Chrome profile name (e.g., "Default")
162
155
 
163
- Example (macOS):
156
+ Example:
164
157
 
158
+ # macOS
165
159
  export BROWSER_CHROME_BIN="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
166
- export BROWSER_USER_DATA_DIR="/Users/<user>/Library/Application Support/Google/Chrome"
167
- export BROWSER_PROFILE_DIR="Default"
168
- az2aws --mode gui --no-disable-extensions --no-sandbox
169
-
170
- Example (Linux):
160
+ export BROWSER_USER_DATA_DIR="$HOME/Library/Application Support/Google/Chrome"
171
161
 
162
+ # Linux
172
163
  export BROWSER_CHROME_BIN="/usr/bin/google-chrome"
173
- export BROWSER_USER_DATA_DIR="/home/<user>/.config/google-chrome"
164
+ export BROWSER_USER_DATA_DIR="$HOME/.config/google-chrome"
165
+
166
+ # Common
174
167
  export BROWSER_PROFILE_DIR="Default"
175
168
  az2aws --mode gui --no-disable-extensions --no-sandbox
176
169
 
177
- Using Chrome instead of Chromium allows you to use browser extensions such as password managers.
178
-
179
170
  ### Logging In
180
171
 
181
172
  az2aws # Default profile
@@ -187,12 +178,23 @@ You'll be prompted for username, password, and MFA if required. After login, use
187
178
  **Tips:**
188
179
  - Set `AWS_PROFILE` env var instead of using `--profile`
189
180
  - Use `--mode gui --disable-gpu` on VMs or if rendering fails
190
- - Use `--no-sandbox` on Linux
191
181
  - Set `https_proxy` env var for corporate proxy
192
182
 
183
+ #### Troubleshooting
184
+
185
+ If you see device compliance errors (e.g., "Device UnSecured Or Non-Compliant"),
186
+ Try:
187
+ `--mode gui` and use your system Chrome via `BROWSER_CHROME_BIN`.
188
+
189
+ If you see "Unable to recognize page state!", Azure's login pages may have
190
+ changed. Try:
191
+
192
+ - `--mode gui` or `--mode debug`
193
+ - Filing an issue with the screenshot (`az2aws-unrecognized-state.png`) to help maintainers update selectors
194
+
193
195
  ## Automation
194
196
 
195
- Renew all profiles at once (useful for short session limits):
197
+ Renew all profiles at once:
196
198
 
197
199
  az2aws --all-profiles
198
200
  az2aws --all-profiles --no-prompt # With "Stay logged in" enabled
@@ -201,21 +203,21 @@ Credentials are only refreshed if expiring within 11 minutes - safe to run as a
201
203
 
202
204
  ## Getting Your Tenant ID and App ID URI
203
205
 
204
- Your Azure AD system admin should be able to provide you with your Tenant ID and App ID URI. If you can't get it from them, you can scrape it from a login page from the myapps.microsoft.com page.
206
+ Ask your Azure AD admin for these values, or extract them from myapps.microsoft.com:
205
207
 
206
208
  1. Load the myapps.microsoft.com page.
207
- 2. Click the chicklet for the login you want.
208
- 3. In the window the pops open quickly copy the login.microsoftonline.com URL. (If you miss it just try again. You can also open the developer console with nagivation preservation to capture the URL.)
209
+ 2. Click the app tile for the login you want.
210
+ 3. In the window that pops open, quickly copy the login.microsoftonline.com URL. (You can also use browser DevTools with "Preserve log" enabled to capture it.)
209
211
  4. The GUID right after login.microsoftonline.com/ is the tenant ID.
210
212
  5. Copy the SAMLRequest URL param.
211
213
  6. Paste it into a URL decoder ([like this one](https://www.samltool.com/url.php)) and decode.
212
- 7. Paste the decoded output into the a SAML deflated and encoded XML decoder ([like this one](https://www.samltool.com/decode.php)).
214
+ 7. Paste the decoded output into a SAML deflated and encoded XML decoder ([like this one](https://www.samltool.com/decode.php)).
213
215
  8. In the decoded XML output the value of the `Audience` tag is the App ID URI.
214
- 9. You may double-check tenant ID using `Attribute` tag named `tenantid` provided in XML.
216
+ 9. Verify the tenant ID using the `tenantid` attribute in the XML.
215
217
 
216
218
  ## How It Works
217
219
 
218
- The Azure login page uses JavaScript, which requires a real web browser. To automate this from a command line, az2aws uses [Puppeteer](https://github.com/GoogleChrome/puppeteer), which automates a real Chromium browser. It loads the Azure login page behind the scenes, populates your username and password (and MFA token), parses the SAML assertion, uses the [AWS STS AssumeRoleWithSAML API](http://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithSAML.html) to get temporary credentials, and saves these in the CLI credentials file.
220
+ az2aws uses [Puppeteer](https://github.com/GoogleChrome/puppeteer) to automate a Chromium browser for Azure AD login. It parses the SAML response and calls [AWS STS AssumeRoleWithSAML](http://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithSAML.html) to get temporary credentials.
219
221
 
220
222
  ## Troubleshooting
221
223
 
@@ -227,7 +229,7 @@ If login fails, try these in order:
227
229
 
228
230
  ## Support for Other Authentication Providers
229
231
 
230
- Obviously, this tool only supports Azure AD as an identity provider. However, there is a lot of similarity with how other logins with other providers would work (especially if they are SAML providers). If you are interested in building support for a different provider let me know. It would be great to build a more generic AWS CLI login tool with plugins for the various providers.
232
+ This tool only supports Azure AD. Contributions for other SAML providers are welcome - open an issue on GitHub to discuss.
231
233
 
232
234
  ## Acknowledgements
233
235
 
package/issue/issues.md CHANGED
@@ -726,4 +726,444 @@ Created an issues documentation file (`issue/issues.md`) to track and document a
726
726
  **Benefits:**
727
727
  - Centralized documentation of all project issues
728
728
  - Easier onboarding for new contributors
729
- - Clear tracking of issue status and priorities
729
+ - Clear tracking of issue status and priorities
730
+
731
+ ---
732
+
733
+ ## Performance Improvements
734
+
735
+ ---
736
+
737
+ ### Issue #57: Optimize keyboard input loop for clearing input fields
738
+
739
+ **Labels:** `performance`, `priority: high`
740
+
741
+ **Description:**
742
+ The current implementation sends 100 individual keyboard backspace events to clear input fields, causing unnecessary delay.
743
+
744
+ **Location:** `src/login.ts:97-99`, `src/login.ts:377-379`
745
+
746
+ **Current Code:**
747
+ ```typescript
748
+ for (let i = 0; i < 100; i++) {
749
+ await page.keyboard.press("Backspace");
750
+ }
751
+ ```
752
+
753
+ **Problem:**
754
+ - Sends 100 individual keyboard events causing unnecessary delay
755
+ - Each `press` call is an async operation executed serially
756
+
757
+ **Proposed Fix:**
758
+ Use `page.evaluate()` to clear input via DOM directly, or use `Ctrl+A` to select all then delete:
759
+ ```typescript
760
+ // Option 1: Select all and delete
761
+ await page.keyboard.down('Control');
762
+ await page.keyboard.press('a');
763
+ await page.keyboard.up('Control');
764
+ await page.keyboard.press('Backspace');
765
+
766
+ // Option 2: Clear via DOM
767
+ await page.evaluate((selector) => {
768
+ const input = document.querySelector(selector) as HTMLInputElement;
769
+ if (input) input.value = '';
770
+ }, inputSelector);
771
+ ```
772
+
773
+ **Expected Impact:** 100ms - 500ms reduction per input field
774
+
775
+ ---
776
+
777
+ ### Issue #58: Optimize page state polling loop
778
+
779
+ **Labels:** `performance`, `priority: high`
780
+
781
+ **Description:**
782
+ The state detection loop checks all 9 states from the beginning on every iteration, which is inefficient.
783
+
784
+ **Location:** `src/login.ts:814-874`
785
+
786
+ **Current Code:**
787
+ ```typescript
788
+ while (true) {
789
+ if (samlResponseData) break;
790
+
791
+ let foundState = false;
792
+ for (let i = 0; i < states.length; i++) {
793
+ const state = states[i];
794
+ let selected;
795
+ try {
796
+ selected = await page.$(state.selector);
797
+ } catch (err) {
798
+ break;
799
+ }
800
+
801
+ if (selected) {
802
+ foundState = true;
803
+ // ...
804
+ break;
805
+ }
806
+ }
807
+
808
+ if (!foundState) {
809
+ totalUnrecognizedDelay += DELAY_ON_UNRECOGNIZED_PAGE;
810
+ await Bluebird.delay(DELAY_ON_UNRECOGNIZED_PAGE); // 1 second wait
811
+ }
812
+ }
813
+ ```
814
+
815
+ **Problem:**
816
+ - Checks all 9 states from the beginning on every iteration
817
+ - Frequently occurring patterns may be at the end of the array
818
+ - Maximum 30 seconds polling with 1-second intervals
819
+ - Repeats all DOM operations when no state is found
820
+
821
+ **Proposed Fix:**
822
+ - Place frequently occurring states at the front of the array
823
+ - Cache the last matched state and prioritize it in the next check
824
+ - Consider combining multiple selectors with `waitForSelector`
825
+
826
+ **Expected Impact:** Potentially up to 30 seconds reduction in worst case scenarios
827
+
828
+ ---
829
+
830
+ ### Issue #59: Eliminate duplicate profile loading in loginAll
831
+
832
+ **Labels:** `performance`, `priority: medium`
833
+
834
+ **Description:**
835
+ In `loginAll()`, profile information is loaded from disk for each profile in the loop, causing redundant file I/O and INI parsing.
836
+
837
+ **Location:** `src/login.ts:527-556`, `src/login.ts:589-610`
838
+
839
+ **Current Code:**
840
+ ```typescript
841
+ async loginAll(...) {
842
+ const profiles = await awsConfig.getAllProfileNames(); // Load 1
843
+
844
+ for (const profile of profiles) {
845
+ if (!forceRefresh && !(await awsConfig.isProfileAboutToExpireAsync(profile))) {
846
+ continue;
847
+ }
848
+
849
+ await this.loginAsync(profile, ...); // Calls _loadProfileAsync internally
850
+ }
851
+ }
852
+ ```
853
+
854
+ **Problem:**
855
+ - Profile information is loaded from disk for each profile in the loop
856
+ - INI parsing is repeated multiple times
857
+
858
+ **Proposed Fix:**
859
+ - Cache profile information when `getAllProfileNames()` is called
860
+ - Or create a separate method to load all profiles at once
861
+
862
+ ---
863
+
864
+ ### Issue #60: Replace Lodash with native array methods
865
+
866
+ **Labels:** `performance`, `priority: low`
867
+
868
+ **Description:**
869
+ Lodash is used for small-scale operations where native array methods would be more efficient.
870
+
871
+ **Location:** `src/login.ts:175`, `src/login.ts:968`, `src/login.ts:979`, `src/login.ts:1005`
872
+
873
+ **Current Usage:**
874
+ ```typescript
875
+ _.map(accounts, "message")
876
+ _.sortBy(_.map(roles, "roleArn"))
877
+ _.find(roles, ["roleArn", defaultRoleArn])
878
+ ```
879
+
880
+ **Problem:**
881
+ - Lodash dependency for small-scale operations (typically <10 items)
882
+ - Native array methods are more efficient for these cases
883
+
884
+ **Proposed Fix:**
885
+ ```typescript
886
+ // Replace _.map(accounts, "message")
887
+ accounts.map(a => a.message)
888
+
889
+ // Replace _.sortBy(_.map(roles, "roleArn"))
890
+ roles.map(r => r.roleArn).sort()
891
+
892
+ // Replace _.find(roles, ["roleArn", defaultRoleArn])
893
+ roles.find(r => r.roleArn === defaultRoleArn)
894
+ ```
895
+
896
+ Consider removing Lodash from package.json if no longer needed elsewhere.
897
+
898
+ ---
899
+
900
+ ## Additional Bug Reports
901
+
902
+ ---
903
+
904
+ ### Issue #61: Incomplete error handling in page navigation
905
+
906
+ **Labels:** `bug`, `priority: high`
907
+
908
+ **Description:**
909
+ Page navigation errors are logged but not properly handled, allowing the process to continue in an undefined state.
910
+
911
+ **Location:** `src/login.ts:803-809`
912
+
913
+ **Current Code:**
914
+ ```typescript
915
+ try {
916
+ if (headless || (!headless && cliProxy)) {
917
+ await page.goto(url, { waitUntil: "domcontentloaded" });
918
+ } else {
919
+ await page.waitForNavigation({ waitUntil: "networkidle0" });
920
+ }
921
+ } catch (err) {
922
+ if (err instanceof Error) {
923
+ debug(`Error occured during loading the first page: ${err.message}`);
924
+ // Error is swallowed, logic continues
925
+ }
926
+ }
927
+ ```
928
+
929
+ **Problem:**
930
+ - Error is only logged, not thrown or handled
931
+ - May continue in an undefined state when redirect fails
932
+ - Typo: "occured" should be "occurred"
933
+
934
+ **Proposed Fix:**
935
+ ```typescript
936
+ try {
937
+ if (headless || (!headless && cliProxy)) {
938
+ await page.goto(url, { waitUntil: "domcontentloaded" });
939
+ } else {
940
+ await page.waitForNavigation({ waitUntil: "networkidle0" });
941
+ }
942
+ } catch (err) {
943
+ if (err instanceof Error) {
944
+ debug(`Error occurred during loading the first page: ${err.message}`);
945
+ throw new CLIError(`Failed to load login page: ${err.message}`);
946
+ }
947
+ throw err;
948
+ }
949
+ ```
950
+
951
+ ---
952
+
953
+ ### Issue #62: Missing NaN validation in duration hours input
954
+
955
+ **Labels:** `bug`, `priority: medium`
956
+
957
+ **Description:**
958
+ The duration hours input validation does not check for NaN values when converting string input to number.
959
+
960
+ **Location:** `src/configureProfileAsync.ts:50-56`
961
+
962
+ **Current Code:**
963
+ ```typescript
964
+ validate: (input): boolean | string => {
965
+ input = Number(input);
966
+ if (input > 0 && input <= 12) return true;
967
+ return "Duration hours must be between 0 and 12";
968
+ },
969
+ ```
970
+
971
+ **Problem:**
972
+ - Only validates after String to Number conversion
973
+ - No NaN check - `Number("abc")` returns NaN which fails the condition silently
974
+ - Error message says "between 0 and 12" but code checks `> 0` (exclusive)
975
+
976
+ **Proposed Fix:**
977
+ ```typescript
978
+ validate: (input): boolean | string => {
979
+ const num = Number(input);
980
+ if (Number.isNaN(num)) return "Please enter a valid number";
981
+ if (num > 0 && num <= 12) return true;
982
+ return "Duration hours must be greater than 0 and at most 12";
983
+ },
984
+ ```
985
+
986
+ ---
987
+
988
+ ### Issue #63: Fixed delay for browser initialization is environment-dependent
989
+
990
+ **Labels:** `bug`, `priority: medium`
991
+
992
+ **Description:**
993
+ A fixed 200ms delay is used to wait for browser initialization, which may be insufficient in slower environments.
994
+
995
+ **Location:** `src/login.ts:750`
996
+
997
+ **Current Code:**
998
+ ```typescript
999
+ browser = await puppeteer.launch(launchParams);
1000
+
1001
+ // Wait for a bit as sometimes the browser isn't ready.
1002
+ await Bluebird.delay(200);
1003
+
1004
+ const pages = await browser.pages();
1005
+ ```
1006
+
1007
+ **Problem:**
1008
+ - 200ms fixed delay is environment-dependent
1009
+ - May cause instability in CI environments or slower machines
1010
+ - May cause unnecessary delay in faster environments
1011
+
1012
+ **Proposed Fix:**
1013
+ Use a more reliable wait mechanism for browser initialization:
1014
+ ```typescript
1015
+ browser = await puppeteer.launch(launchParams);
1016
+
1017
+ // Wait for browser to be ready
1018
+ const pages = await browser.pages();
1019
+ if (pages.length === 0) {
1020
+ // Wait for default page to be created
1021
+ await browser.waitForTarget(target => target.type() === 'page');
1022
+ }
1023
+ ```
1024
+
1025
+ ---
1026
+
1027
+ ### Issue #64: Event listener memory leak risk in SAML response handling
1028
+
1029
+ **Labels:** `bug`, `priority: medium`
1030
+
1031
+ **Description:**
1032
+ The request event listener for capturing SAML responses is not explicitly cleaned up after use.
1033
+
1034
+ **Location:** `src/login.ts:761-790`
1035
+
1036
+ **Current Code:**
1037
+ ```typescript
1038
+ const samlResponsePromise = new Promise((resolve) => {
1039
+ page.on("request", (req: HTTPRequest) => {
1040
+ // Event listener may remain registered
1041
+ const reqUrl = req.url();
1042
+ if (reqUrl === AWS_SAML_ENDPOINT || reqUrl === AWS_GOV_SAML_ENDPOINT || reqUrl === AWS_CN_SAML_ENDPOINT) {
1043
+ // ...
1044
+ resolve(samlResponse);
1045
+ }
1046
+ });
1047
+ });
1048
+ ```
1049
+
1050
+ **Problem:**
1051
+ - Event listener is not explicitly cleaned up
1052
+ - Potential memory leak if page is reused or not properly closed
1053
+
1054
+ **Proposed Fix:**
1055
+ ```typescript
1056
+ const samlResponsePromise = new Promise((resolve) => {
1057
+ const requestHandler = (req: HTTPRequest) => {
1058
+ const reqUrl = req.url();
1059
+ if (reqUrl === AWS_SAML_ENDPOINT || reqUrl === AWS_GOV_SAML_ENDPOINT || reqUrl === AWS_CN_SAML_ENDPOINT) {
1060
+ // ...
1061
+ page.off("request", requestHandler); // Clean up listener
1062
+ resolve(samlResponse);
1063
+ }
1064
+ };
1065
+ page.on("request", requestHandler);
1066
+ });
1067
+ ```
1068
+
1069
+ Or use `page.once()` if appropriate for the use case.
1070
+
1071
+ ---
1072
+
1073
+ ### Issue #65: Typo in error message
1074
+
1075
+ **Labels:** `bug`, `priority: low`
1076
+
1077
+ **Description:**
1078
+ There is a typo in the error debug message.
1079
+
1080
+ **Location:** `src/login.ts:806`
1081
+
1082
+ **Current:** `"Error occured during loading the first page"`
1083
+ **Should be:** `"Error occurred during loading the first page"`
1084
+
1085
+ **Proposed Fix:**
1086
+ ```typescript
1087
+ debug(`Error occurred during loading the first page: ${err.message}`);
1088
+ ```
1089
+
1090
+ ---
1091
+
1092
+ ## Code Quality Improvements
1093
+
1094
+ ---
1095
+
1096
+ ### Issue #66: Reduce `any` type usage and eslint-disable comments
1097
+
1098
+ **Labels:** `code-quality`, `priority: medium`
1099
+
1100
+ **Description:**
1101
+ There are several places where `any` type and `eslint-disable` comments are used, reducing type safety.
1102
+
1103
+ **Locations:**
1104
+ - `src/awsConfig.ts:149` - `any` type for parsed INI
1105
+ - `src/login.ts:141-144` - eslint-disable comment
1106
+
1107
+ **Current Code:**
1108
+ ```typescript
1109
+ // awsConfig.ts:149
1110
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1111
+ const parsedIni: any = ini.parse(data);
1112
+
1113
+ // login.ts:141-144
1114
+ const aadTileMessage: string = await page.evaluate(
1115
+ // eslint-disable-next-line
1116
+ (a) => a?.textContent ?? "",
1117
+ aadTile
1118
+ );
1119
+ ```
1120
+
1121
+ **Proposed Fix:**
1122
+ - Define proper TypeScript interfaces for INI parsing results
1123
+ - Use type guards instead of eslint-disable comments
1124
+ - Consider using a typed INI parser or create proper type definitions
1125
+
1126
+ ---
1127
+
1128
+ ### Issue #67: Add missing test coverage for critical functions
1129
+
1130
+ **Labels:** `testing`, `priority: medium`
1131
+
1132
+ **Description:**
1133
+ Several critical functions lack test coverage.
1134
+
1135
+ **Location:** `src/login.test.ts`
1136
+
1137
+ **Missing Tests:**
1138
+ - `_performLoginAsync()` - the most complex function with browser automation
1139
+ - `_parseRolesFromSamlResponse()` - SAML parsing logic
1140
+ - Edge cases: empty SAML response, zero roles, malformed XML, etc.
1141
+
1142
+ **Proposed Tests:**
1143
+ ```typescript
1144
+ describe("login._performLoginAsync", () => {
1145
+ // Mock puppeteer browser behavior
1146
+ // Test SAML session completion sequence
1147
+ });
1148
+
1149
+ describe("login._parseRolesFromSamlResponse", () => {
1150
+ it("should parse valid SAML response with multiple roles", () => {});
1151
+ it("should handle empty SAML response", () => {});
1152
+ it("should handle special characters in role names", () => {});
1153
+ it("should handle UTF-8 encoded content", () => {});
1154
+ });
1155
+ ```
1156
+
1157
+ ---
1158
+
1159
+ ### [RESOLVED] Issue #68: Prettier formatting issues in login files
1160
+
1161
+ **Labels:** `code-quality`, `priority: low`
1162
+
1163
+ **Description:**
1164
+ Prettier detected code style issues in `src/login.ts` and `src/login.test.ts` that caused `yarn lint` to fail.
1165
+
1166
+ **Location:** `src/login.ts`, `src/login.test.ts`
1167
+
1168
+ **Resolution:**
1169
+ Fixed by running `yarn prettier --write` on the affected files.
@@ -53,7 +53,7 @@ async function configureProfileAsync(profileName) {
53
53
  input = Number(input);
54
54
  if (input > 0 && input <= 12)
55
55
  return true;
56
- return "Duration hours must be between 0 and 12";
56
+ return "Duration hours must be between 1 and 12";
57
57
  },
58
58
  },
59
59
  {