From dc3e031c5af6f5d5abca5bd0bbc3689871686d12 Mon Sep 17 00:00:00 2001 From: Galib Sarayev Date: Mon, 13 Apr 2026 13:11:13 +0000 Subject: [PATCH 1/3] fix: resolve high severity dependabot security vulnerabilities Fixes the following Dependabot HIGH severity alerts: - #175: lodash Code Injection via _.template (bumped to ^4.18.0) - #170: lodash-es Code Injection via _.template (added resolution ^4.18.0) - #177: basic-ftp CRLF Injection (added resolution >=5.2.2) - #172: basic-ftp FTP Command Injection via CRLF (added resolution >=5.2.2) - #152: picomatch ReDoS via extglob quantifiers (added resolution ^2.3.2) Also fixes additional HIGH vulnerabilities from yarn audit: - tar: Symlink Path Traversal (bumped to ^7.5.11) - fast-xml-parser v4: Entity Expansion bypass (bumped to ^4.5.5) - fast-xml-parser v5: Entity Expansion bypass (bumped to 5.5.12) Resolved versions: - lodash: 4.17.23 -> 4.18.1 - lodash-es: 4.17.23 -> 4.18.1 - picomatch: 2.3.1 -> 2.3.2 - basic-ftp: 5.2.0 -> 5.2.2 - tar: 7.5.8 -> 7.5.13 - fast-xml-parser (v4): 4.4.1 -> 4.5.6 - fast-xml-parser (v5): 5.3.8 -> 5.5.12 --- dependency_licenses.txt | 52 +++++++++++++++++++++++++ package.json | 13 ++++--- yarn.lock | 85 +++++++++++++++++++++++------------------ 3 files changed, 107 insertions(+), 43 deletions(-) diff --git a/dependency_licenses.txt b/dependency_licenses.txt index 8a97cb2d4..f0948d112 100644 --- a/dependency_licenses.txt +++ b/dependency_licenses.txt @@ -13761,6 +13761,32 @@ THE SOFTWARE. ----- +The following software may be included in this product: fast-xml-builder. A copy of the source code may be downloaded from git+https://github.com/NaturalIntelligence/fast-xml-builder.git. This software contains the following license and notice below: + +MIT License + +Copyright (c) 2026 Natural Intelligence + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----- + The following software may be included in this product: fast-xml-parser. A copy of the source code may be downloaded from git+https://github.com/NaturalIntelligence/fast-xml-parser.git. This software contains the following license and notice below: MIT License @@ -18662,6 +18688,32 @@ THE SOFTWARE. ----- +The following software may be included in this product: path-expression-matcher. A copy of the source code may be downloaded from https://github.com/NaturalIntelligence/path-expression-matcher. This software contains the following license and notice below: + +MIT License + +Copyright (c) 2024 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----- + The following software may be included in this product: path-parse. A copy of the source code may be downloaded from https://github.com/jbgutierrez/path-parse.git. This software contains the following license and notice below: The MIT License (MIT) diff --git a/package.json b/package.json index a4c8e924f..a4c0969cb 100644 --- a/package.json +++ b/package.json @@ -130,8 +130,8 @@ }, "resolutions": { "**/@aws-amplify/amplify-codegen-e2e-tests/**/cookie": "^0.7.0", - "**/@aws-amplify/amplify-codegen-e2e-tests/**/fast-xml-parser": "^4.4.1", - "**/@aws-sdk/xml-builder/**/fast-xml-parser": "5.3.8", + "**/@aws-amplify/amplify-codegen-e2e-tests/**/fast-xml-parser": "^4.5.5", + "**/@aws-sdk/xml-builder/**/fast-xml-parser": "5.5.12", "@smithy/config-resolver": "^4.4.0", "@octokit/plugin-paginate-rest": "^9.2.2", "@octokit/request": "^8.4.1", @@ -145,15 +145,18 @@ "**/codecov/**/js-yaml": "^3.14.2", "**/nx/**/js-yaml": "^4.1.1", "immutable": "^4.3.8", - "lodash": "^4.17.21", + "lodash": "^4.18.0", + "lodash-es": "^4.18.0", "minimist": "^1.2.6", "node-fetch": "^2.6.7", "parse-url": "^8.1.0", - "tar": "^7.5.8", + "tar": "^7.5.11", "xml2js": "0.5.0", "tmp": "^0.2.4", "minimatch": "^3.1.3", - "http-proxy-agent": "^7.0.0" + "http-proxy-agent": "^7.0.0", + "picomatch": "^2.3.2", + "basic-ftp": ">=5.2.2" }, "config": { "commitizen": { diff --git a/yarn.lock b/yarn.lock index 64e0f8cef..9cfaef3cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10813,10 +10813,10 @@ base64-js@^1.0.2, base64-js@^1.3.1: resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -basic-ftp@^5.0.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.2.0.tgz#7c2dff63c918bde60e6bad1f2ff93dcf5137a40a" - integrity sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw== +basic-ftp@>=5.2.2, basic-ftp@^5.0.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/basic-ftp/-/basic-ftp-5.2.2.tgz#4cb2422deddf432896bdb3c9b8f13b944ad4842c" + integrity sha512-1tDrzKsdCg70WGvbFss/ulVAxupNauGnOlgpyjKzeQxzyllBLS0CGLV7tjIXTK3ZQA9/FBEm9qyFFN1bciA6pw== before-after-hook@^2.2.0: version "2.2.3" @@ -12690,19 +12690,28 @@ fast-url-parser@^1.1.3: dependencies: punycode "^1.3.2" -fast-xml-parser@4.2.5, fast-xml-parser@^4.2.5, fast-xml-parser@^4.4.1: - version "4.5.4" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.4.tgz#64e52ddf1308001893bd225d5b1768840511c797" - integrity sha512-jE8ugADnYOBsu1uaoayVl1tVKAMNOXyjwvv2U6udEA2ORBhDooJDWoGxTkhd4Qn4yh59JVVt/pKXtjPwx9OguQ== +fast-xml-builder@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/fast-xml-builder/-/fast-xml-builder-1.1.4.tgz#0c407a1d9d5996336c0cd76f7ff785cac6413017" + integrity sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg== + dependencies: + path-expression-matcher "^1.1.3" + +fast-xml-parser@4.2.5, fast-xml-parser@^4.2.5, fast-xml-parser@^4.5.5: + version "4.5.6" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.5.6.tgz#4ff57d4aca13a2d11aa42ad460495cf00f32b655" + integrity sha512-Yd4vkROfJf8AuJrDIVMVmYfULKmIJszVsMv7Vo71aocsKgFxpdlpSHXSaInvyYfgw2PRuObQSW2GFpVMUjxu9A== dependencies: strnum "^1.0.5" -fast-xml-parser@5.3.6, fast-xml-parser@5.3.8: - version "5.3.8" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-5.3.8.tgz#b5bc2045620d1b9cf342a2aa4d72391ef0b36a9e" - integrity sha512-53jIF4N6u/pxvaL1eb/hEZts/cFLWZ92eCfLrNyCI0k38lettCG/Bs40W9pPwoPXyHQlKu2OUbQtiEIZK/J6Vw== +fast-xml-parser@5.3.6, fast-xml-parser@5.5.12: + version "5.5.12" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-5.5.12.tgz#6e50e5a5bbb03c1dc72a9268a9bfe677b1b623b2" + integrity sha512-nUR0q8PPfoA/svPM43Gup7vLOZWppaNrYgGmrVqrAVJa7cOH4hMG6FX9M4mQ8dZA1/ObGZHzES7Ed88hxEBSJg== dependencies: - strnum "^2.1.2" + fast-xml-builder "^1.1.4" + path-expression-matcher "^1.5.0" + strnum "^2.2.3" fastq@^1.6.0: version "1.15.0" @@ -15074,10 +15083,10 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash-es@^4.17.4, lodash-es@^4.2.1: - version "4.17.23" - resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.23.tgz#58c4360fd1b5d33afc6c0bbd3d1149349b1138e0" - integrity sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg== +lodash-es@^4.17.4, lodash-es@^4.18.0, lodash-es@^4.2.1: + version "4.18.1" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.18.1.tgz#b962eeb80d9d983a900bf342961fb7418ca10b1d" + integrity sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A== lodash.camelcase@^4.3.0: version "4.3.0" @@ -15159,10 +15168,10 @@ lodash.upperfirst@^4.3.1: resolved "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== -lodash@4.17.21, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.7.0, lodash@~4.17.0, lodash@~4.17.15: - version "4.17.23" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" - integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== +lodash@4.17.21, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.18.0, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.7.0, lodash@~4.17.0, lodash@~4.17.15: + version "4.18.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" + integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q== log-symbols@^3.0.0: version "3.0.0" @@ -16323,6 +16332,11 @@ path-exists@^4.0.0: resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== +path-expression-matcher@^1.1.3, path-expression-matcher@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz#3b98545dc88ffebb593e2d8458d0929da9275f4a" + integrity sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -16377,15 +16391,10 @@ picocolors@^1.0.0: resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -picomatch@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz#817033161def55ec9638567a2f3bbc876b3e7516" - integrity sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag== +picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1, picomatch@^2.3.2, picomatch@^3.0.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.2.tgz#5a942915e26b372dc0f0e6753149a16e6b1c5601" + integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA== pify@^2.3, pify@^2.3.0: version "2.3.0" @@ -17548,10 +17557,10 @@ strnum@^1.0.5: resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.1.2.tgz#57bca4fbaa6f271081715dbc9ed7cee5493e28e4" integrity sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA== -strnum@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz#a5e00ba66ab25f9cafa3726b567ce7a49170937a" - integrity sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ== +strnum@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-2.2.3.tgz#0119fce02749a11bb126a4d686ac5dbdf6e57586" + integrity sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg== strong-log-transformer@^2.1.0: version "2.1.0" @@ -17651,10 +17660,10 @@ tar-stream@~2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.1.0, tar@^6.1.11, tar@^6.1.2, tar@^7.5.8: - version "7.5.10" - resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.10.tgz#2281541123f5507db38bc6eb22619f4bbaef73ad" - integrity sha512-8mOPs1//5q/rlkNSPcCegA6hiHJYDmSLEI8aMH/CdSQJNWztHC9WHNam5zdQlfpTwB9Xp7IBEsHfV5LKMJGVAw== +tar@^6.1.0, tar@^6.1.11, tar@^6.1.2, tar@^7.5.11, tar@^7.5.8: + version "7.5.13" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.13.tgz#0d214ed56781a26edc313581c0e2d929ceeb866d" + integrity sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng== dependencies: "@isaacs/fs-minipass" "^4.0.0" chownr "^3.0.0" From 820198724b4e21335b24f44f14defc42c97d487f Mon Sep 17 00:00:00 2001 From: Galib Sarayev Date: Tue, 14 Apr 2026 07:05:13 +0000 Subject: [PATCH 2/3] fix: add process-level safety nets and XML parsing error handling to cleanup script --- .../src/cleanup-e2e-resources.ts | 208 +++++++++++++----- 1 file changed, 154 insertions(+), 54 deletions(-) diff --git a/packages/amplify-codegen-e2e-tests/src/cleanup-e2e-resources.ts b/packages/amplify-codegen-e2e-tests/src/cleanup-e2e-resources.ts index c2087e05c..a29c2332b 100644 --- a/packages/amplify-codegen-e2e-tests/src/cleanup-e2e-resources.ts +++ b/packages/amplify-codegen-e2e-tests/src/cleanup-e2e-resources.ts @@ -1,4 +1,16 @@ /* eslint-disable spellcheck/spell-checker, camelcase, @typescript-eslint/no-explicit-any */ + +// Ultimate safety nets - cleanup must NEVER fail the build +process.on('uncaughtException', (err) => { + console.error('Uncaught exception in cleanup (non-fatal):', err.message); + process.exit(0); +}); + +process.on('unhandledRejection', (reason) => { + console.error('Unhandled rejection in cleanup (non-fatal):', reason); + process.exit(0); +}); + import { CodeBuildClient, BatchGetBuildsCommand, Build } from '@aws-sdk/client-codebuild'; import { AccountClient, ListRegionsCommand, ListRegionsRequest } from '@aws-sdk/client-account'; import { AmplifyClient, ListAppsCommand, ListBackendEnvironmentsCommand, DeleteAppCommand } from '@aws-sdk/client-amplify'; @@ -154,6 +166,14 @@ const isNonJsonResponseError = (e: any): boolean => { return e instanceof SyntaxError || (e?.name === 'SyntaxError' && e?.message?.includes('Unexpected token')); }; +const isXmlParsingError = (e: any): boolean => { + const message = e?.message || ''; + return message.includes('Entity expansion limit exceeded') || + message.includes('Deserialization error') || + message.includes('entity expansion') || + (e?.name === 'Error' && message.includes('fast-xml-parser')); +}; + const isNetworkError = (e: any): boolean => { const code = e?.code || e?.name || ''; return ['ETIMEDOUT', 'ECONNREFUSED', 'ECONNRESET', 'EPIPE', 'EAI_AGAIN', 'NetworkingError', 'TimeoutError'].includes(code) @@ -196,29 +216,45 @@ const testRoleStalenessFilter = (resource: Role): boolean => { * Get all S3 buckets in the account, and filter down to the ones we consider stale. */ const getOrphanS3TestBuckets = async (account: AWSAccountInfo): Promise => { - const s3Client = new S3Client(getAWSConfig(account)); - const listBucketResponse = await s3Client.send(new ListBucketsCommand({})); - const staleBuckets = listBucketResponse.Buckets.filter(testBucketStalenessFilter); - const bucketInfos = await Promise.all( - staleBuckets.map(async (staleBucket): Promise => { - const region = await getBucketRegion(account, staleBucket.Name); - return { - name: staleBucket.Name, - region, - }; - }), - ); - return bucketInfos; + try { + const s3Client = new S3Client(getAWSConfig(account)); + const listBucketResponse = await s3Client.send(new ListBucketsCommand({})); + const staleBuckets = listBucketResponse.Buckets.filter(testBucketStalenessFilter); + const bucketInfos = await Promise.all( + staleBuckets.map(async (staleBucket): Promise => { + const region = await getBucketRegion(account, staleBucket.Name); + return { + name: staleBucket.Name, + region, + }; + }), + ); + return bucketInfos; + } catch (e) { + if (isXmlParsingError(e)) { + console.log(`XML parsing error listing orphan S3 buckets (non-fatal, skipping): ${e.message}`); + return []; + } + throw e; + } }; /** * Get all iam roles in the account, and filter down to the ones we consider stale. */ const getOrphanTestIamRoles = async (account: AWSAccountInfo): Promise => { - const iamClient = new IAMClient(getAWSConfig(account)); - const listRoleResponse = await iamClient.send(new ListRolesCommand({MaxItems: 1000})); - const staleRoles = listRoleResponse.Roles.filter(testRoleStalenessFilter); - return staleRoles.map(it => ({ name: it.RoleName })); + try { + const iamClient = new IAMClient(getAWSConfig(account)); + const listRoleResponse = await iamClient.send(new ListRolesCommand({MaxItems: 1000})); + const staleRoles = listRoleResponse.Roles.filter(testRoleStalenessFilter); + return staleRoles.map(it => ({ name: it.RoleName })); + } catch (e) { + if (isXmlParsingError(e)) { + console.log(`XML parsing error listing IAM roles (non-fatal, skipping): ${e.message}`); + return []; + } + throw e; + } }; /** @@ -240,26 +276,34 @@ const getOrphanTestIamRoles = async (account: AWSAccountInfo): Promise a list of AWS regions enabled by the account */ const getRegionsEnabled = async (accountInfo: AWSAccountInfo): Promise => { - // Specify service region to avoid possible endpoint unavailable error - const account = new AccountClient(getAWSConfig(accountInfo, 'us-east-1')); + try { + // Specify service region to avoid possible endpoint unavailable error + const account = new AccountClient(getAWSConfig(accountInfo, 'us-east-1')); - const enabledRegions: string[] = []; - let nextToken: string | undefined = undefined; + const enabledRegions: string[] = []; + let nextToken: string | undefined = undefined; - do { - const input: ListRegionsRequest = { - RegionOptStatusContains: ['ENABLED', 'ENABLED_BY_DEFAULT'], - NextToken: nextToken, - }; + do { + const input: ListRegionsRequest = { + RegionOptStatusContains: ['ENABLED', 'ENABLED_BY_DEFAULT'], + NextToken: nextToken, + }; - const response = await account.send(new ListRegionsCommand(input)); - nextToken = response.NextToken; + const response = await account.send(new ListRegionsCommand(input)); + nextToken = response.NextToken; - enabledRegions.push(...response.Regions.map(r => r.RegionName).filter(Boolean)); - } while (nextToken); + enabledRegions.push(...response.Regions.map(r => r.RegionName).filter(Boolean)); + } while (nextToken); - console.log('All enabled regions fetched: ', enabledRegions); - return enabledRegions; + console.log('All enabled regions fetched: ', enabledRegions); + return enabledRegions; + } catch (e) { + if (isXmlParsingError(e)) { + console.log(`XML parsing error listing regions (non-fatal, using default test regions): ${e.message}`); + return AWS_REGIONS_TO_RUN_TESTS; + } + throw e; + } }; /** @@ -277,7 +321,17 @@ const getAmplifyApps = async (account: AWSAccountInfo, region: string, regionsEn return []; } - const amplifyApps = await amplifyClient.send(new ListAppsCommand({ maxResults: 50 })); // keeping it to 50 as max supported is 50 + let amplifyApps; + try { + amplifyApps = await amplifyClient.send(new ListAppsCommand({ maxResults: 50 })); + } catch (e) { + if (isXmlParsingError(e)) { + console.log(`XML parsing error listing Amplify apps in ${region} (non-fatal, skipping): ${e.message}`); + return []; + } + throw e; + } + const result: AmplifyAppInfo[] = []; for (const app of amplifyApps.apps) { const backends: Record = {}; @@ -349,6 +403,10 @@ const getStackDetails = async (stackName: string, account: AWSAccountInfo, regio console.log(`Stack ${stackName} does not exist in ${region}. Skipping.`); return; } + if (isXmlParsingError(e)) { + console.log(`XML parsing error describing stack ${stackName} in ${region} (non-fatal, skipping): ${e.message}`); + return; + } throw e; } }; @@ -361,19 +419,28 @@ const getStacks = async (account: AWSAccountInfo, region: string, regionsEnabled return []; } - const stacks = await cfnClient.send(new ListStacksCommand({ - StackStatusFilter: [ - 'CREATE_COMPLETE', - 'ROLLBACK_FAILED', - 'DELETE_FAILED', - 'UPDATE_COMPLETE', - 'UPDATE_ROLLBACK_FAILED', - 'UPDATE_ROLLBACK_COMPLETE', - 'IMPORT_COMPLETE', - 'IMPORT_ROLLBACK_FAILED', - 'IMPORT_ROLLBACK_COMPLETE', - ], - })); + let stacks; + try { + stacks = await cfnClient.send(new ListStacksCommand({ + StackStatusFilter: [ + 'CREATE_COMPLETE', + 'ROLLBACK_FAILED', + 'DELETE_FAILED', + 'UPDATE_COMPLETE', + 'UPDATE_ROLLBACK_FAILED', + 'UPDATE_ROLLBACK_COMPLETE', + 'IMPORT_COMPLETE', + 'IMPORT_ROLLBACK_FAILED', + 'IMPORT_ROLLBACK_COMPLETE', + ], + })); + } catch (e) { + if (isXmlParsingError(e)) { + console.log(`XML parsing error listing stacks in ${region} (non-fatal, skipping): ${e.message}`); + return []; + } + throw e; + } // We are interested in only the root stacks that are deployed by amplify-cli const rootStacks = stacks.StackSummaries.filter(stack => !stack.RootId); @@ -430,7 +497,16 @@ const getBucketRegion = async (account: AWSAccountInfo, bucketName: string): Pro const getS3Buckets = async (account: AWSAccountInfo): Promise => { const awsConfig = getAWSConfig(account); const s3Client = new S3Client(awsConfig); - const buckets = await s3Client.send(new ListBucketsCommand({})); + let buckets; + try { + buckets = await s3Client.send(new ListBucketsCommand({})); + } catch (e) { + if (isXmlParsingError(e)) { + console.log(`XML parsing error listing S3 buckets (non-fatal, skipping): ${e.message}`); + return []; + } + throw e; + } const result: S3BucketInfo[] = []; for (const bucket of buckets.Buckets) { let region: string | undefined; @@ -460,6 +536,8 @@ const getS3Buckets = async (account: AWSAccountInfo): Promise => console.error(`Skipping processing ${account.accountId}, bucket ${bucket.Name}`, e); } else if (isNonJsonResponseError(e)) { console.warn(`Received non-JSON response for bucket ${bucket.Name}. Skipping.`, e.message); + } else if (isXmlParsingError(e)) { + console.log(`XML parsing error for bucket ${bucket.Name} (non-fatal, skipping): ${e.message}`); } else { throw e; } @@ -625,9 +703,17 @@ const deleteAttachedRolePolicies = async ( accountIndex: number, roleName: string, ): Promise => { - const iamClient = new IAMClient(getAWSConfig(account)); - const rolePolicies = await iamClient.send(new ListAttachedRolePoliciesCommand({ RoleName: roleName })); - await Promise.all(rolePolicies.AttachedPolicies.map(policy => detachIamAttachedRolePolicy(account, accountIndex, roleName, policy))); + try { + const iamClient = new IAMClient(getAWSConfig(account)); + const rolePolicies = await iamClient.send(new ListAttachedRolePoliciesCommand({ RoleName: roleName })); + await Promise.all(rolePolicies.AttachedPolicies.map(policy => detachIamAttachedRolePolicy(account, accountIndex, roleName, policy))); + } catch (e) { + if (isXmlParsingError(e)) { + console.log(`${generateAccountInfo(account, accountIndex)} XML parsing error listing attached policies for ${roleName} (non-fatal, skipping): ${e.message}`); + return; + } + throw e; + } }; const detachIamAttachedRolePolicy = async ( @@ -653,9 +739,17 @@ const deleteRolePolicies = async ( accountIndex: number, roleName: string, ): Promise => { - const iamClient = new IAMClient(getAWSConfig(account)); - const rolePolicies = await iamClient.send(new ListRolePoliciesCommand({ RoleName: roleName })); - await Promise.all(rolePolicies.PolicyNames.map(policy => deleteIamRolePolicy(account, accountIndex, roleName, policy))); + try { + const iamClient = new IAMClient(getAWSConfig(account)); + const rolePolicies = await iamClient.send(new ListRolePoliciesCommand({ RoleName: roleName })); + await Promise.all(rolePolicies.PolicyNames.map(policy => deleteIamRolePolicy(account, accountIndex, roleName, policy))); + } catch (e) { + if (isXmlParsingError(e)) { + console.log(`${generateAccountInfo(account, accountIndex)} XML parsing error listing policies for ${roleName} (non-fatal, skipping): ${e.message}`); + return; + } + throw e; + } }; const deleteIamRolePolicy = async ( @@ -904,6 +998,11 @@ const cleanupAccount = async (account: AWSAccountInfo, accountIndex: number, fil summary.skippedReason = 'network error'; return; } + if (isXmlParsingError(e)) { + console.warn(`${generateAccountInfo(account, accountIndex)} XML parsing error encountered. Skipping this account.`, e.message); + summary.skippedReason = 'XML parsing error'; + return; + } console.error(`${generateAccountInfo(account, accountIndex)} Cleanup failed with unexpected error:`, e); summary.skippedReason = 'unexpected error'; } @@ -999,9 +1098,10 @@ const printCleanupSummary = (): void => { cleanup() .catch((e) => { - console.log(`Cleanup encountered an error but completing gracefully: ${e.message}`); + console.error('Top-level cleanup error (non-fatal):', e?.message || e); }) .finally(() => { printCleanupSummary(); + console.log('Cleanup complete. Exiting with code 0.'); process.exit(0); }); From 265467b5aad9491b4859a91b90045bec481ee539 Mon Sep 17 00:00:00 2001 From: Galib Sarayev Date: Tue, 14 Apr 2026 12:38:47 +0000 Subject: [PATCH 3/3] fix: update tar direct dependency to ^7.5.13 --- package.json | 4 ++-- yarn.lock | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index a4c0969cb..e9e723197 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "license": "Apache-2.0", "dependencies": { "lerna": "^5.1.6", - "tar": "^7.5.8" + "tar": "^7.5.13" }, "workspaces": [ "packages/*" @@ -150,7 +150,7 @@ "minimist": "^1.2.6", "node-fetch": "^2.6.7", "parse-url": "^8.1.0", - "tar": "^7.5.11", + "tar": "^7.5.13", "xml2js": "0.5.0", "tmp": "^0.2.4", "minimatch": "^3.1.3", diff --git a/yarn.lock b/yarn.lock index 9cfaef3cd..a36d90fe5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17660,7 +17660,7 @@ tar-stream@~2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^6.1.0, tar@^6.1.11, tar@^6.1.2, tar@^7.5.11, tar@^7.5.8: +tar@^6.1.0, tar@^6.1.11, tar@^6.1.2, tar@^7.5.13: version "7.5.13" resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.13.tgz#0d214ed56781a26edc313581c0e2d929ceeb866d" integrity sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==