diff --git a/backend/Dockerfile.pe b/backend/Dockerfile.pe index c3956d29c..b092d4656 100644 --- a/backend/Dockerfile.pe +++ b/backend/Dockerfile.pe @@ -9,6 +9,8 @@ COPY src ./src RUN apt update && apt install git zlib1g-dev +RUN apt-get update && apt-get install -y jq + RUN wget -c https://www.python.org/ftp/python/3.10.11/Python-3.10.11.tar.xz && tar -Jxvf Python-3.10.11.tar.xz RUN cd Python-3.10.11 && ./configure && make -j4 && make altinstall RUN update-alternatives --install /usr/bin/python python /usr/local/bin/python3.10 1 @@ -17,8 +19,25 @@ RUN pip3.10 install --upgrade pip RUN apt remove dav1d && apt autoclean && apt autoremove +# Install AWS CLI +RUN curl --insecure "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" +RUN unzip awscliv2.zip +RUN ./aws/install + # Install pe-source module # Sync the latest from cf-staging branch -RUN git clone -b cf-source-staging https://github.com/cisagov/pe-reports.git && cd pe-reports && git checkout c9cbbd73b22ef38cabe1da6ba50aeb2dc0be4f99 && pip install . +RUN git clone -b AL-staging-SQS https://github.com/cisagov/pe-reports.git && \ + cd pe-reports && \ + git checkout 6dcd017551ba14022e110c073e1bdbc804c795f8 && \ + pip install . + +RUN python -m spacy download en_core_web_lg + +# Create database.ini +RUN echo "[database]" > database.ini \ + && echo "user=$(cat db_user.txt)" >> database.ini \ + && echo "password=$(cat db_password.txt)" >> database.ini + +COPY worker worker -CMD ["./worker/pe-worker-entry.sh"] +CMD ["./worker/generate_config.sh", "./worker/pe-worker-entry.sh"] diff --git a/backend/env.yml b/backend/env.yml index 115246cef..c23708aa3 100644 --- a/backend/env.yml +++ b/backend/env.yml @@ -42,11 +42,11 @@ staging: PE_API_URL: ${ssm:/crossfeed/staging/PE_API_URL} REPORTS_BUCKET_NAME: cisa-crossfeed-staging-reports CLOUDWATCH_BUCKET_NAME: cisa-crossfeed-staging-cloudwatch - SQS_QUEUE_URL: { Ref: WorkerQueue } STAGE: staging PE_CLUSTER_NAME: pe-staging-worker SHODAN_QUEUE_URL: ${ssm:/crossfeed/staging/SHODAN_QUEUE_URL} SHODAN_SERVICE_NAME: pe-staging-shodan + EMAIL_BUCKET_NAME: cisa-crossfeed-staging-html-email prod: DB_DIALECT: 'postgres' @@ -83,11 +83,11 @@ prod: PE_API_URL: ${ssm:/crossfeed/staging/PE_API_URL} REPORTS_BUCKET_NAME: cisa-crossfeed-prod-reports CLOUDWATCH_BUCKET_NAME: cisa-crossfeed-prod-cloudwatch - SQS_QUEUE_URL: { Ref: WorkerQueue } STAGE: prod PE_CLUSTER_NAME: pe-prod-worker SHODAN_QUEUE_URL: ${ssm:/crossfeed/prod/SHODAN_QUEUE_URL} SHODAN_SERVICE_NAME: pe-prod-shodan + EMAIL_BUCKET_NAME: cisa-crossfeed-staging-html-email dev-vpc: securityGroupIds: diff --git a/backend/package-lock.json b/backend/package-lock.json index 8899093ec..deed41902 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -25,6 +25,7 @@ "express": "^4.18.1", "global-agent": "^2.2.0", "got": "^11.8.5", + "handlebars": "^4.7.8", "helmet": "^4.1.1", "http-proxy-middleware": "^2.0.6", "jsdom": "^22.1", @@ -387,47 +388,50 @@ } }, "node_modules/@aws-sdk/client-cloudformation": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudformation/-/client-cloudformation-3.398.0.tgz", - "integrity": "sha512-NQAjm0FYmmMT9jn7y2kYpa5MtANwe2c+65IJATXCJC9YqmDxDveZce6YmOpZ3Qflf7Z1J2A7rYAwo0qO35/Zcg==", + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudformation/-/client-cloudformation-3.451.0.tgz", + "integrity": "sha512-rc8MWRsWA1OgOq/hASLONtVTEbRggjf8VFYmW7UdL1g+oRQoDFWEWPv7kW5868UTpS6SmHdjCrXP8YREtR4ZSQ==", "dev": true, "dependencies": { "@aws-crypto/sha256-browser": "3.0.0", "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.398.0", - "@aws-sdk/credential-provider-node": "3.398.0", - "@aws-sdk/middleware-host-header": "3.398.0", - "@aws-sdk/middleware-logger": "3.398.0", - "@aws-sdk/middleware-recursion-detection": "3.398.0", - "@aws-sdk/middleware-signing": "3.398.0", - "@aws-sdk/middleware-user-agent": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@aws-sdk/util-user-agent-browser": "3.398.0", - "@aws-sdk/util-user-agent-node": "3.398.0", - "@smithy/config-resolver": "^2.0.5", - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/hash-node": "^2.0.5", - "@smithy/invalid-dependency": "^2.0.5", - "@smithy/middleware-content-length": "^2.0.5", - "@smithy/middleware-endpoint": "^2.0.5", - "@smithy/middleware-retry": "^2.0.5", - "@smithy/middleware-serde": "^2.0.5", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/protocol-http": "^2.0.5", - "@smithy/smithy-client": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-base64": "^2.0.0", + "@aws-sdk/client-sts": "3.451.0", + "@aws-sdk/core": "3.451.0", + "@aws-sdk/credential-provider-node": "3.451.0", + "@aws-sdk/middleware-host-header": "3.451.0", + "@aws-sdk/middleware-logger": "3.451.0", + "@aws-sdk/middleware-recursion-detection": "3.451.0", + "@aws-sdk/middleware-signing": "3.451.0", + "@aws-sdk/middleware-user-agent": "3.451.0", + "@aws-sdk/region-config-resolver": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@aws-sdk/util-endpoints": "3.451.0", + "@aws-sdk/util-user-agent-browser": "3.451.0", + "@aws-sdk/util-user-agent-node": "3.451.0", + "@smithy/config-resolver": "^2.0.18", + "@smithy/fetch-http-handler": "^2.2.6", + "@smithy/hash-node": "^2.0.15", + "@smithy/invalid-dependency": "^2.0.13", + "@smithy/middleware-content-length": "^2.0.15", + "@smithy/middleware-endpoint": "^2.2.0", + "@smithy/middleware-retry": "^2.0.20", + "@smithy/middleware-serde": "^2.0.13", + "@smithy/middleware-stack": "^2.0.7", + "@smithy/node-config-provider": "^2.1.5", + "@smithy/node-http-handler": "^2.1.9", + "@smithy/protocol-http": "^3.0.9", + "@smithy/smithy-client": "^2.1.15", + "@smithy/types": "^2.5.0", + "@smithy/url-parser": "^2.0.13", + "@smithy/util-base64": "^2.0.1", "@smithy/util-body-length-browser": "^2.0.0", "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.5", - "@smithy/util-defaults-mode-node": "^2.0.5", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "@smithy/util-waiter": "^2.0.5", + "@smithy/util-defaults-mode-browser": "^2.0.19", + "@smithy/util-defaults-mode-node": "^2.0.25", + "@smithy/util-endpoints": "^1.0.4", + "@smithy/util-retry": "^2.0.6", + "@smithy/util-utf8": "^2.0.2", + "@smithy/util-waiter": "^2.0.13", "fast-xml-parser": "4.2.5", "tslib": "^2.5.0", "uuid": "^8.3.2" @@ -436,6 +440,443 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/client-sso": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.451.0.tgz", + "integrity": "sha512-KkYSke3Pdv3MfVH/5fT528+MKjMyPKlcLcd4zQb0x6/7Bl7EHrPh1JZYjzPLHelb+UY5X0qN8+cb8iSu1eiwIQ==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.451.0", + "@aws-sdk/middleware-host-header": "3.451.0", + "@aws-sdk/middleware-logger": "3.451.0", + "@aws-sdk/middleware-recursion-detection": "3.451.0", + "@aws-sdk/middleware-user-agent": "3.451.0", + "@aws-sdk/region-config-resolver": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@aws-sdk/util-endpoints": "3.451.0", + "@aws-sdk/util-user-agent-browser": "3.451.0", + "@aws-sdk/util-user-agent-node": "3.451.0", + "@smithy/config-resolver": "^2.0.18", + "@smithy/fetch-http-handler": "^2.2.6", + "@smithy/hash-node": "^2.0.15", + "@smithy/invalid-dependency": "^2.0.13", + "@smithy/middleware-content-length": "^2.0.15", + "@smithy/middleware-endpoint": "^2.2.0", + "@smithy/middleware-retry": "^2.0.20", + "@smithy/middleware-serde": "^2.0.13", + "@smithy/middleware-stack": "^2.0.7", + "@smithy/node-config-provider": "^2.1.5", + "@smithy/node-http-handler": "^2.1.9", + "@smithy/protocol-http": "^3.0.9", + "@smithy/smithy-client": "^2.1.15", + "@smithy/types": "^2.5.0", + "@smithy/url-parser": "^2.0.13", + "@smithy/util-base64": "^2.0.1", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.19", + "@smithy/util-defaults-mode-node": "^2.0.25", + "@smithy/util-endpoints": "^1.0.4", + "@smithy/util-retry": "^2.0.6", + "@smithy/util-utf8": "^2.0.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/client-sts": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.451.0.tgz", + "integrity": "sha512-48NcIRxWBdP1fom6RSjwn2R2u7SE7eeV3p+c4s7ukEOfrHhBxJfn3EpqBVQMGzdiU55qFImy+Fe81iA2lXq3Jw==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.451.0", + "@aws-sdk/credential-provider-node": "3.451.0", + "@aws-sdk/middleware-host-header": "3.451.0", + "@aws-sdk/middleware-logger": "3.451.0", + "@aws-sdk/middleware-recursion-detection": "3.451.0", + "@aws-sdk/middleware-sdk-sts": "3.451.0", + "@aws-sdk/middleware-signing": "3.451.0", + "@aws-sdk/middleware-user-agent": "3.451.0", + "@aws-sdk/region-config-resolver": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@aws-sdk/util-endpoints": "3.451.0", + "@aws-sdk/util-user-agent-browser": "3.451.0", + "@aws-sdk/util-user-agent-node": "3.451.0", + "@smithy/config-resolver": "^2.0.18", + "@smithy/fetch-http-handler": "^2.2.6", + "@smithy/hash-node": "^2.0.15", + "@smithy/invalid-dependency": "^2.0.13", + "@smithy/middleware-content-length": "^2.0.15", + "@smithy/middleware-endpoint": "^2.2.0", + "@smithy/middleware-retry": "^2.0.20", + "@smithy/middleware-serde": "^2.0.13", + "@smithy/middleware-stack": "^2.0.7", + "@smithy/node-config-provider": "^2.1.5", + "@smithy/node-http-handler": "^2.1.9", + "@smithy/protocol-http": "^3.0.9", + "@smithy/smithy-client": "^2.1.15", + "@smithy/types": "^2.5.0", + "@smithy/url-parser": "^2.0.13", + "@smithy/util-base64": "^2.0.1", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.19", + "@smithy/util-defaults-mode-node": "^2.0.25", + "@smithy/util-endpoints": "^1.0.4", + "@smithy/util-retry": "^2.0.6", + "@smithy/util-utf8": "^2.0.2", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.451.0.tgz", + "integrity": "sha512-9dAav7DcRgaF7xCJEQR5ER9ErXxnu/tdnVJ+UPmb1NPeIZdESv1A3lxFDEq1Fs8c4/lzAj9BpshGyJVIZwZDKg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.451.0.tgz", + "integrity": "sha512-TySt64Ci5/ZbqFw1F9Z0FIGvYx5JSC9e6gqDnizIYd8eMnn8wFRUscRrD7pIHKfrhvVKN5h0GdYovmMO/FMCBw==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.451.0", + "@aws-sdk/credential-provider-process": "3.451.0", + "@aws-sdk/credential-provider-sso": "3.451.0", + "@aws-sdk/credential-provider-web-identity": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.451.0.tgz", + "integrity": "sha512-AEwM1WPyxUdKrKyUsKyFqqRFGU70e4qlDyrtBxJnSU9NRLZI8tfEZ67bN7fHSxBUBODgDXpMSlSvJiBLh5/3pw==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.451.0", + "@aws-sdk/credential-provider-ini": "3.451.0", + "@aws-sdk/credential-provider-process": "3.451.0", + "@aws-sdk/credential-provider-sso": "3.451.0", + "@aws-sdk/credential-provider-web-identity": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.451.0.tgz", + "integrity": "sha512-HQywSdKeD5PErcLLnZfSyCJO+6T+ZyzF+Lm/QgscSC+CbSUSIPi//s15qhBRVely/3KBV6AywxwNH+5eYgt4lQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.451.0.tgz", + "integrity": "sha512-Usm/N51+unOt8ID4HnQzxIjUJDrkAQ1vyTOC0gSEEJ7h64NSSPGD5yhN7il5WcErtRd3EEtT1a8/GTC5TdBctg==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sso": "3.451.0", + "@aws-sdk/token-providers": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.451.0.tgz", + "integrity": "sha512-Xtg3Qw65EfDjWNG7o2xD6sEmumPfsy3WDGjk2phEzVg8s7hcZGxf5wYwe6UY7RJvlEKrU0rFA+AMn6Hfj5oOzg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.451.0.tgz", + "integrity": "sha512-j8a5jAfhWmsK99i2k8oR8zzQgXrsJtgrLxc3js6U+525mcZytoiDndkWTmD5fjJ1byU1U2E5TaPq+QJeDip05Q==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/protocol-http": "^3.0.9", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-logger": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.451.0.tgz", + "integrity": "sha512-0kHrYEyVeB2QBfP6TfbI240aRtatLZtcErJbhpiNUb+CQPgEL3crIjgVE8yYiJumZ7f0jyjo8HLPkwD1/2APaw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.451.0.tgz", + "integrity": "sha512-J6jL6gJ7orjHGM70KDRcCP7so/J2SnkN4vZ9YRLTeeZY6zvBuHDjX8GCIgSqPn/nXFXckZO8XSnA7u6+3TAT0w==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/protocol-http": "^3.0.9", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.451.0.tgz", + "integrity": "sha512-UJ6UfVUEgp0KIztxpAeelPXI5MLj9wUtUCqYeIMP7C1ZhoEMNm3G39VLkGN43dNhBf1LqjsV9jkKMZbVfYXuwg==", + "dev": true, + "dependencies": { + "@aws-sdk/middleware-signing": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-signing": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.451.0.tgz", + "integrity": "sha512-s5ZlcIoLNg1Huj4Qp06iKniE8nJt/Pj1B/fjhWc6cCPCM7XJYUCejCnRh6C5ZJoBEYodjuwZBejPc1Wh3j+znA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^3.0.9", + "@smithy/signature-v4": "^2.0.0", + "@smithy/types": "^2.5.0", + "@smithy/util-middleware": "^2.0.6", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.451.0.tgz", + "integrity": "sha512-8NM/0JiKLNvT9wtAQVl1DFW0cEO7OvZyLSUBLNLTHqyvOZxKaZ8YFk7d8PL6l76LeUKRxq4NMxfZQlUIRe0eSA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@aws-sdk/util-endpoints": "3.451.0", + "@smithy/protocol-http": "^3.0.9", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.451.0.tgz", + "integrity": "sha512-3iMf4OwzrFb4tAAmoROXaiORUk2FvSejnHIw/XHvf/jjR4EqGGF95NZP/n/MeFZMizJWVssrwS412GmoEyoqhg==", + "dev": true, + "dependencies": { + "@smithy/node-config-provider": "^2.1.5", + "@smithy/types": "^2.5.0", + "@smithy/util-config-provider": "^2.0.0", + "@smithy/util-middleware": "^2.0.6", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/token-providers": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.451.0.tgz", + "integrity": "sha512-ij1L5iUbn6CwxVOT1PG4NFjsrsKN9c4N1YEM0lkl6DwmaNOscjLKGSNyj9M118vSWsOs1ZDbTwtj++h0O/BWrQ==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.451.0", + "@aws-sdk/middleware-logger": "3.451.0", + "@aws-sdk/middleware-recursion-detection": "3.451.0", + "@aws-sdk/middleware-user-agent": "3.451.0", + "@aws-sdk/region-config-resolver": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@aws-sdk/util-endpoints": "3.451.0", + "@aws-sdk/util-user-agent-browser": "3.451.0", + "@aws-sdk/util-user-agent-node": "3.451.0", + "@smithy/config-resolver": "^2.0.18", + "@smithy/fetch-http-handler": "^2.2.6", + "@smithy/hash-node": "^2.0.15", + "@smithy/invalid-dependency": "^2.0.13", + "@smithy/middleware-content-length": "^2.0.15", + "@smithy/middleware-endpoint": "^2.2.0", + "@smithy/middleware-retry": "^2.0.20", + "@smithy/middleware-serde": "^2.0.13", + "@smithy/middleware-stack": "^2.0.7", + "@smithy/node-config-provider": "^2.1.5", + "@smithy/node-http-handler": "^2.1.9", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^3.0.9", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/smithy-client": "^2.1.15", + "@smithy/types": "^2.5.0", + "@smithy/url-parser": "^2.0.13", + "@smithy/util-base64": "^2.0.1", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.19", + "@smithy/util-defaults-mode-node": "^2.0.25", + "@smithy/util-endpoints": "^1.0.4", + "@smithy/util-retry": "^2.0.6", + "@smithy/util-utf8": "^2.0.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/types": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.451.0.tgz", + "integrity": "sha512-rhK+qeYwCIs+laJfWCcrYEjay2FR/9VABZJ2NRM89jV/fKqGVQR52E5DQqrI+oEIL5JHMhhnr4N4fyECMS35lw==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/util-endpoints": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.451.0.tgz", + "integrity": "sha512-giqLGBTnRIcKkDqwU7+GQhKbtJ5Ku35cjGQIfMyOga6pwTBUbaK0xW1Sdd8sBQ1GhApscnChzI9o/R9x0368vw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/util-endpoints": "^1.0.4", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.451.0.tgz", + "integrity": "sha512-Ws5mG3J0TQifH7OTcMrCTexo7HeSAc3cBgjfhS/ofzPUzVCtsyg0G7I6T7wl7vJJETix2Kst2cpOsxygPgPD9w==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/types": "^2.5.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.451.0.tgz", + "integrity": "sha512-TBzm6P+ql4mkGFAjPlO1CI+w3yUT+NulaiALjl/jNX/nnUp6HsJsVxJf4nVFQTG5KRV0iqMypcs7I3KIhH+LmA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/node-config-provider": "^2.1.5", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/client-cloudformation/node_modules/@smithy/protocol-http": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.9.tgz", + "integrity": "sha512-U1wl+FhYu4/BC+rjwh1lg2gcJChQhytiNQSggREgQ9G2FzmoK9sACBZvx7thyWMvRyHQTE22mO2d5UM8gMKDBg==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/client-cloudwatch-logs": { "version": "3.417.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.417.0.tgz", @@ -1641,6 +2082,19 @@ "node": ">=14.0.0" } }, + "node_modules/@aws-sdk/core": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.451.0.tgz", + "integrity": "sha512-SamWW2zHEf1ZKe3j1w0Piauryl8BQIlej0TBS18A4ACzhjhWXhCs13bO1S88LvPR5mBFXok3XOT6zPOnKDFktw==", + "dev": true, + "dependencies": { + "@smithy/smithy-client": "^2.1.15", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@aws-sdk/credential-provider-cognito-identity": { "version": "3.398.0", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.398.0.tgz", @@ -2630,12 +3084,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.3", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -2876,9 +3330,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -3079,19 +3533,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", + "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/generator": "^7.23.3", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", + "@babel/parser": "^7.23.3", + "@babel/types": "^7.23.3", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -3109,9 +3563,9 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", @@ -4034,135 +4488,591 @@ "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", "dev": true }, - "node_modules/@kwsites/file-exists": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", - "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/utils": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", + "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "fast-glob": "^3.3.0", + "is-glob": "^4.0.3", + "open": "^9.1.0", + "picocolors": "^1.0.0", + "tslib": "^2.6.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@pkgr/utils/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@pkgr/utils/node_modules/open": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", + "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "dev": true, + "dependencies": { + "default-browser": "^4.0.0", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@serverless/dashboard-plugin": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@serverless/dashboard-plugin/-/dashboard-plugin-7.1.0.tgz", + "integrity": "sha512-mAiTU2ERsDHdCrXJa/tihh/r+8ZwSuYYBqln3SkwuBD/49ct9QrK7S00cpiqFoY/geMFlHpOkriGzCPz6UP/rw==", + "dev": true, + "dependencies": { + "@aws-sdk/client-cloudformation": "^3.410.0", + "@aws-sdk/client-sts": "^3.410.0", + "@serverless/event-mocks": "^1.1.1", + "@serverless/platform-client": "^4.4.0", + "@serverless/utils": "^6.14.0", + "child-process-ext": "^3.0.1", + "chokidar": "^3.5.3", + "flat": "^5.0.2", + "fs-extra": "^9.1.0", + "js-yaml": "^4.1.0", + "jszip": "^3.10.1", + "lodash": "^4.17.21", + "memoizee": "^0.4.15", + "ncjsm": "^4.3.2", + "node-dir": "^0.1.17", + "node-fetch": "^2.6.8", + "open": "^7.4.2", + "semver": "^7.3.8", + "simple-git": "^3.16.0", + "timers-ext": "^0.1.7", + "type": "^2.7.2", + "uuid": "^8.3.2", + "yamljs": "^0.3.0" + }, + "engines": { + "node": ">=12.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/client-sso": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.451.0.tgz", + "integrity": "sha512-KkYSke3Pdv3MfVH/5fT528+MKjMyPKlcLcd4zQb0x6/7Bl7EHrPh1JZYjzPLHelb+UY5X0qN8+cb8iSu1eiwIQ==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.451.0", + "@aws-sdk/middleware-host-header": "3.451.0", + "@aws-sdk/middleware-logger": "3.451.0", + "@aws-sdk/middleware-recursion-detection": "3.451.0", + "@aws-sdk/middleware-user-agent": "3.451.0", + "@aws-sdk/region-config-resolver": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@aws-sdk/util-endpoints": "3.451.0", + "@aws-sdk/util-user-agent-browser": "3.451.0", + "@aws-sdk/util-user-agent-node": "3.451.0", + "@smithy/config-resolver": "^2.0.18", + "@smithy/fetch-http-handler": "^2.2.6", + "@smithy/hash-node": "^2.0.15", + "@smithy/invalid-dependency": "^2.0.13", + "@smithy/middleware-content-length": "^2.0.15", + "@smithy/middleware-endpoint": "^2.2.0", + "@smithy/middleware-retry": "^2.0.20", + "@smithy/middleware-serde": "^2.0.13", + "@smithy/middleware-stack": "^2.0.7", + "@smithy/node-config-provider": "^2.1.5", + "@smithy/node-http-handler": "^2.1.9", + "@smithy/protocol-http": "^3.0.9", + "@smithy/smithy-client": "^2.1.15", + "@smithy/types": "^2.5.0", + "@smithy/url-parser": "^2.0.13", + "@smithy/util-base64": "^2.0.1", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.19", + "@smithy/util-defaults-mode-node": "^2.0.25", + "@smithy/util-endpoints": "^1.0.4", + "@smithy/util-retry": "^2.0.6", + "@smithy/util-utf8": "^2.0.2", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/client-sts": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.451.0.tgz", + "integrity": "sha512-48NcIRxWBdP1fom6RSjwn2R2u7SE7eeV3p+c4s7ukEOfrHhBxJfn3EpqBVQMGzdiU55qFImy+Fe81iA2lXq3Jw==", + "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/core": "3.451.0", + "@aws-sdk/credential-provider-node": "3.451.0", + "@aws-sdk/middleware-host-header": "3.451.0", + "@aws-sdk/middleware-logger": "3.451.0", + "@aws-sdk/middleware-recursion-detection": "3.451.0", + "@aws-sdk/middleware-sdk-sts": "3.451.0", + "@aws-sdk/middleware-signing": "3.451.0", + "@aws-sdk/middleware-user-agent": "3.451.0", + "@aws-sdk/region-config-resolver": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@aws-sdk/util-endpoints": "3.451.0", + "@aws-sdk/util-user-agent-browser": "3.451.0", + "@aws-sdk/util-user-agent-node": "3.451.0", + "@smithy/config-resolver": "^2.0.18", + "@smithy/fetch-http-handler": "^2.2.6", + "@smithy/hash-node": "^2.0.15", + "@smithy/invalid-dependency": "^2.0.13", + "@smithy/middleware-content-length": "^2.0.15", + "@smithy/middleware-endpoint": "^2.2.0", + "@smithy/middleware-retry": "^2.0.20", + "@smithy/middleware-serde": "^2.0.13", + "@smithy/middleware-stack": "^2.0.7", + "@smithy/node-config-provider": "^2.1.5", + "@smithy/node-http-handler": "^2.1.9", + "@smithy/protocol-http": "^3.0.9", + "@smithy/smithy-client": "^2.1.15", + "@smithy/types": "^2.5.0", + "@smithy/url-parser": "^2.0.13", + "@smithy/util-base64": "^2.0.1", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.19", + "@smithy/util-defaults-mode-node": "^2.0.25", + "@smithy/util-endpoints": "^1.0.4", + "@smithy/util-retry": "^2.0.6", + "@smithy/util-utf8": "^2.0.2", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.451.0.tgz", + "integrity": "sha512-9dAav7DcRgaF7xCJEQR5ER9ErXxnu/tdnVJ+UPmb1NPeIZdESv1A3lxFDEq1Fs8c4/lzAj9BpshGyJVIZwZDKg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.451.0.tgz", + "integrity": "sha512-TySt64Ci5/ZbqFw1F9Z0FIGvYx5JSC9e6gqDnizIYd8eMnn8wFRUscRrD7pIHKfrhvVKN5h0GdYovmMO/FMCBw==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.451.0", + "@aws-sdk/credential-provider-process": "3.451.0", + "@aws-sdk/credential-provider-sso": "3.451.0", + "@aws-sdk/credential-provider-web-identity": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.451.0.tgz", + "integrity": "sha512-AEwM1WPyxUdKrKyUsKyFqqRFGU70e4qlDyrtBxJnSU9NRLZI8tfEZ67bN7fHSxBUBODgDXpMSlSvJiBLh5/3pw==", + "dev": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.451.0", + "@aws-sdk/credential-provider-ini": "3.451.0", + "@aws-sdk/credential-provider-process": "3.451.0", + "@aws-sdk/credential-provider-sso": "3.451.0", + "@aws-sdk/credential-provider-web-identity": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.451.0.tgz", + "integrity": "sha512-HQywSdKeD5PErcLLnZfSyCJO+6T+ZyzF+Lm/QgscSC+CbSUSIPi//s15qhBRVely/3KBV6AywxwNH+5eYgt4lQ==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.451.0.tgz", + "integrity": "sha512-Usm/N51+unOt8ID4HnQzxIjUJDrkAQ1vyTOC0gSEEJ7h64NSSPGD5yhN7il5WcErtRd3EEtT1a8/GTC5TdBctg==", + "dev": true, + "dependencies": { + "@aws-sdk/client-sso": "3.451.0", + "@aws-sdk/token-providers": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.451.0.tgz", + "integrity": "sha512-Xtg3Qw65EfDjWNG7o2xD6sEmumPfsy3WDGjk2phEzVg8s7hcZGxf5wYwe6UY7RJvlEKrU0rFA+AMn6Hfj5oOzg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.451.0.tgz", + "integrity": "sha512-j8a5jAfhWmsK99i2k8oR8zzQgXrsJtgrLxc3js6U+525mcZytoiDndkWTmD5fjJ1byU1U2E5TaPq+QJeDip05Q==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/protocol-http": "^3.0.9", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/middleware-logger": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.451.0.tgz", + "integrity": "sha512-0kHrYEyVeB2QBfP6TfbI240aRtatLZtcErJbhpiNUb+CQPgEL3crIjgVE8yYiJumZ7f0jyjo8HLPkwD1/2APaw==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.451.0.tgz", + "integrity": "sha512-J6jL6gJ7orjHGM70KDRcCP7so/J2SnkN4vZ9YRLTeeZY6zvBuHDjX8GCIgSqPn/nXFXckZO8XSnA7u6+3TAT0w==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/protocol-http": "^3.0.9", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/middleware-sdk-sts": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.451.0.tgz", + "integrity": "sha512-UJ6UfVUEgp0KIztxpAeelPXI5MLj9wUtUCqYeIMP7C1ZhoEMNm3G39VLkGN43dNhBf1LqjsV9jkKMZbVfYXuwg==", + "dev": true, + "dependencies": { + "@aws-sdk/middleware-signing": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/middleware-signing": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.451.0.tgz", + "integrity": "sha512-s5ZlcIoLNg1Huj4Qp06iKniE8nJt/Pj1B/fjhWc6cCPCM7XJYUCejCnRh6C5ZJoBEYodjuwZBejPc1Wh3j+znA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^3.0.9", + "@smithy/signature-v4": "^2.0.0", + "@smithy/types": "^2.5.0", + "@smithy/util-middleware": "^2.0.6", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.451.0.tgz", + "integrity": "sha512-8NM/0JiKLNvT9wtAQVl1DFW0cEO7OvZyLSUBLNLTHqyvOZxKaZ8YFk7d8PL6l76LeUKRxq4NMxfZQlUIRe0eSA==", "dev": true, "dependencies": { - "debug": "^4.1.1" + "@aws-sdk/types": "3.451.0", + "@aws-sdk/util-endpoints": "3.451.0", + "@smithy/protocol-http": "^3.0.9", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/@kwsites/promise-deferred": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", - "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", - "dev": true - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.451.0.tgz", + "integrity": "sha512-3iMf4OwzrFb4tAAmoROXaiORUk2FvSejnHIw/XHvf/jjR4EqGGF95NZP/n/MeFZMizJWVssrwS412GmoEyoqhg==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@smithy/node-config-provider": "^2.1.5", + "@smithy/types": "^2.5.0", + "@smithy/util-config-provider": "^2.0.0", + "@smithy/util-middleware": "^2.0.6", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 8" + "node": ">=14.0.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/token-providers": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.451.0.tgz", + "integrity": "sha512-ij1L5iUbn6CwxVOT1PG4NFjsrsKN9c4N1YEM0lkl6DwmaNOscjLKGSNyj9M118vSWsOs1ZDbTwtj++h0O/BWrQ==", "dev": true, + "dependencies": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.451.0", + "@aws-sdk/middleware-logger": "3.451.0", + "@aws-sdk/middleware-recursion-detection": "3.451.0", + "@aws-sdk/middleware-user-agent": "3.451.0", + "@aws-sdk/region-config-resolver": "3.451.0", + "@aws-sdk/types": "3.451.0", + "@aws-sdk/util-endpoints": "3.451.0", + "@aws-sdk/util-user-agent-browser": "3.451.0", + "@aws-sdk/util-user-agent-node": "3.451.0", + "@smithy/config-resolver": "^2.0.18", + "@smithy/fetch-http-handler": "^2.2.6", + "@smithy/hash-node": "^2.0.15", + "@smithy/invalid-dependency": "^2.0.13", + "@smithy/middleware-content-length": "^2.0.15", + "@smithy/middleware-endpoint": "^2.2.0", + "@smithy/middleware-retry": "^2.0.20", + "@smithy/middleware-serde": "^2.0.13", + "@smithy/middleware-stack": "^2.0.7", + "@smithy/node-config-provider": "^2.1.5", + "@smithy/node-http-handler": "^2.1.9", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^3.0.9", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/smithy-client": "^2.1.15", + "@smithy/types": "^2.5.0", + "@smithy/url-parser": "^2.0.13", + "@smithy/util-base64": "^2.0.1", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.19", + "@smithy/util-defaults-mode-node": "^2.0.25", + "@smithy/util-endpoints": "^1.0.4", + "@smithy/util-retry": "^2.0.6", + "@smithy/util-utf8": "^2.0.2", + "tslib": "^2.5.0" + }, "engines": { - "node": ">= 8" + "node": ">=14.0.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/types": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.451.0.tgz", + "integrity": "sha512-rhK+qeYwCIs+laJfWCcrYEjay2FR/9VABZJ2NRM89jV/fKqGVQR52E5DQqrI+oEIL5JHMhhnr4N4fyECMS35lw==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">= 8" + "node": ">=14.0.0" } }, - "node_modules/@pkgr/utils": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", - "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/util-endpoints": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.451.0.tgz", + "integrity": "sha512-giqLGBTnRIcKkDqwU7+GQhKbtJ5Ku35cjGQIfMyOga6pwTBUbaK0xW1Sdd8sBQ1GhApscnChzI9o/R9x0368vw==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.3.0", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.6.0" + "@aws-sdk/types": "3.451.0", + "@smithy/util-endpoints": "^1.0.4", + "tslib": "^2.5.0" }, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" + "node": ">=14.0.0" } }, - "node_modules/@pkgr/utils/node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.451.0.tgz", + "integrity": "sha512-Ws5mG3J0TQifH7OTcMrCTexo7HeSAc3cBgjfhS/ofzPUzVCtsyg0G7I6T7wl7vJJETix2Kst2cpOsxygPgPD9w==", "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/types": "^2.5.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@serverless/dashboard-plugin/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.451.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.451.0.tgz", + "integrity": "sha512-TBzm6P+ql4mkGFAjPlO1CI+w3yUT+NulaiALjl/jNX/nnUp6HsJsVxJf4nVFQTG5KRV0iqMypcs7I3KIhH+LmA==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "3.451.0", + "@smithy/node-config-provider": "^2.1.5", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, "engines": { - "node": ">=12" + "node": ">=14.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } } }, - "node_modules/@pkgr/utils/node_modules/open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", + "node_modules/@serverless/dashboard-plugin/node_modules/@smithy/protocol-http": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.9.tgz", + "integrity": "sha512-U1wl+FhYu4/BC+rjwh1lg2gcJChQhytiNQSggREgQ9G2FzmoK9sACBZvx7thyWMvRyHQTE22mO2d5UM8gMKDBg==", "dev": true, "dependencies": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" }, "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=14.0.0" } }, - "node_modules/@serverless/dashboard-plugin": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/@serverless/dashboard-plugin/-/dashboard-plugin-6.2.3.tgz", - "integrity": "sha512-iTZhpZbiVl6G2AyfgoqxemqqpG4pUceWys3GsyZtjimnfnGd2UFBOMVUMTavLhYia7lQc4kQVuXQ+afLlkg+pQ==", + "node_modules/@serverless/dashboard-plugin/node_modules/child-process-ext": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/child-process-ext/-/child-process-ext-3.0.2.tgz", + "integrity": "sha512-oBePsLbQpTJFxzwyCvs9yWWF0OEM6vGGepHwt1stqmX7QQqOuDc8j2ywdvAs9Tvi44TT7d9ackqhR4Q10l1u8w==", "dev": true, "dependencies": { - "@serverless/event-mocks": "^1.1.1", - "@serverless/platform-client": "^4.3.2", - "@serverless/utils": "^6.8.2", - "child-process-ext": "^2.1.1", - "chokidar": "^3.5.3", - "flat": "^5.0.2", - "fs-extra": "^9.1.0", - "js-yaml": "^4.1.0", - "jszip": "^3.10.1", - "lodash": "^4.17.21", - "memoizee": "^0.4.15", - "ncjsm": "^4.3.2", - "node-dir": "^0.1.17", - "node-fetch": "^2.6.8", - "open": "^7.4.2", - "semver": "^7.3.8", - "simple-git": "^3.16.0", - "type": "^2.7.2", - "uuid": "^8.3.2", - "yamljs": "^0.3.0" + "cross-spawn": "^7.0.3", + "es5-ext": "^0.10.62", + "log": "^6.3.1", + "split2": "^3.2.2", + "stream-promise": "^3.2.0" }, "engines": { - "node": ">=12.0" + "node": ">=8.0" } }, "node_modules/@serverless/dashboard-plugin/node_modules/fs-extra": { @@ -4196,6 +5106,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@serverless/dashboard-plugin/node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "dependencies": { + "readable-stream": "^3.0.0" + } + }, "node_modules/@serverless/dashboard-plugin/node_modules/type": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", @@ -4213,9 +5132,9 @@ } }, "node_modules/@serverless/platform-client": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@serverless/platform-client/-/platform-client-4.3.2.tgz", - "integrity": "sha512-DAa5Z0JAZc6UfrTZLYwqoZxgAponZpFwaqd7WzzMA+loMCkYWyJNwxrAmV6cr2UUJpkko4toPZuJ3vM9Ie+NDA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@serverless/platform-client/-/platform-client-4.4.0.tgz", + "integrity": "sha512-urL7SNefRqC2EOFDcpvm8fyn/06B5yXWneKpyGw7ylGt0Qr9JHZCB9TiUeTkIpPUNz0jTvKUaJ2+M/JNEiaVIA==", "dev": true, "dependencies": { "adm-zip": "^0.5.5", @@ -4313,9 +5232,9 @@ } }, "node_modules/@serverless/utils": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@serverless/utils/-/utils-6.13.1.tgz", - "integrity": "sha512-yokWzlsIaAd3TWzNgIDz6l8HZmtYZs9caaLuheZ0IiZ/bDWSCLBWn84HKkdWZOmFnYxejyPNJEOwE59mtSR3Ow==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/@serverless/utils/-/utils-6.15.0.tgz", + "integrity": "sha512-7eDbqKv/OBd11jjdZjUwFGN8sHWkeUqLeHXHQxQ1azja2IM7WIH+z/aLgzR6LhB3/MINNwtjesDpjGqTMj2JKQ==", "dev": true, "dependencies": { "archive-type": "^4.0.0", @@ -4444,11 +5363,11 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.9.tgz", - "integrity": "sha512-8liHOEbx99xcy4VndeQNQhyA0LS+e7UqsuRnDTSIA26IKBv/7vA9w09KOd4fgNULrvX0r3WpA6cwsQTRJpSWkg==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.13.tgz", + "integrity": "sha512-eeOPD+GF9BzF/Mjy3PICLePx4l0f3rG/nQegQHRLTloN5p1lSJJNZsyn+FzDnW8P2AduragZqJdtKNCxXozB1Q==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -4475,14 +5394,14 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.10.tgz", - "integrity": "sha512-MwToDsCltHjumkCuRn883qoNeJUawc2b8sX9caSn5vLz6J5crU1IklklNxWCaMO2z2nDL91Po4b/aI1eHv5PfA==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.18.tgz", + "integrity": "sha512-761sJSgNbvsqcsKW6/WZbrZr4H+0Vp/QKKqwyrxCPwD8BsiPEXNHyYnqNgaeK9xRWYswjon0Uxbpe3DWQo0j/g==", "dependencies": { - "@smithy/node-config-provider": "^2.0.12", - "@smithy/types": "^2.3.3", + "@smithy/node-config-provider": "^2.1.5", + "@smithy/types": "^2.5.0", "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.2", + "@smithy/util-middleware": "^2.0.6", "tslib": "^2.5.0" }, "engines": { @@ -4490,14 +5409,14 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.12.tgz", - "integrity": "sha512-S3lUNe+2fEFwKcmiQniXGPXt69vaHvQCw8kYQOBL4OvJsgwfpkIYDZdroHbTshYi0M6WaKL26Mw+hvgma6dZqA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.1.1.tgz", + "integrity": "sha512-gw5G3FjWC6sNz8zpOJgPpH5HGKrpoVFQpToNAwLwJVyI/LJ2jDJRjSKEsM6XI25aRpYjMSE/Qptxx305gN1vHw==", "dependencies": { - "@smithy/node-config-provider": "^2.0.12", - "@smithy/property-provider": "^2.0.10", - "@smithy/types": "^2.3.3", - "@smithy/url-parser": "^2.0.9", + "@smithy/node-config-provider": "^2.1.5", + "@smithy/property-provider": "^2.0.14", + "@smithy/types": "^2.5.0", + "@smithy/url-parser": "^2.0.13", "tslib": "^2.5.0" }, "engines": { @@ -4571,23 +5490,23 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.1.5.tgz", - "integrity": "sha512-BIeCHGfr5JCGN+EMTwZK74ELvjPXOIrI7OLM5OhZJJ6AmZyRv2S9ANJk18AtLwht0TsSm+8WoXIEp8LuxNgUyA==", - "dependencies": { - "@smithy/protocol-http": "^3.0.5", - "@smithy/querystring-builder": "^2.0.9", - "@smithy/types": "^2.3.3", - "@smithy/util-base64": "^2.0.0", + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.2.6.tgz", + "integrity": "sha512-PStY3XO1Ksjwn3wMKye5U6m6zxXpXrXZYqLy/IeCbh3nM9QB3Jgw/B0PUSLUWKdXg4U8qgEu300e3ZoBvZLsDg==", + "dependencies": { + "@smithy/protocol-http": "^3.0.9", + "@smithy/querystring-builder": "^2.0.13", + "@smithy/types": "^2.5.0", + "@smithy/util-base64": "^2.0.1", "tslib": "^2.5.0" } }, "node_modules/@smithy/fetch-http-handler/node_modules/@smithy/protocol-http": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.5.tgz", - "integrity": "sha512-3t3fxj+ip4EPHRC2fQ0JimMxR/qCQ1LSQJjZZVZFgROnFLYWPDgUZqpoi7chr+EzatxJVXF/Rtoi5yLHOWCoZQ==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.9.tgz", + "integrity": "sha512-U1wl+FhYu4/BC+rjwh1lg2gcJChQhytiNQSggREgQ9G2FzmoK9sACBZvx7thyWMvRyHQTE22mO2d5UM8gMKDBg==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -4607,13 +5526,13 @@ } }, "node_modules/@smithy/hash-node": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.9.tgz", - "integrity": "sha512-XP3yWd5wyCtiVmsY5Nuq/FUwyCEQ6YG7DsvRh7ThldNukGpCzyFdP8eivZJVjn4Fx7oYrrOnVoYZ0WEgpW1AvQ==", + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.15.tgz", + "integrity": "sha512-t/qjEJZu/G46A22PAk1k/IiJZT4ncRkG5GOCNWN9HPPy5rCcSZUbh7gwp7CGKgJJ7ATMMg+0Td7i9o1lQTwOfQ==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", + "@smithy/util-utf8": "^2.0.2", "tslib": "^2.5.0" }, "engines": { @@ -4635,11 +5554,11 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.9.tgz", - "integrity": "sha512-RuJqhYf8nViK96IIO9JbTtjDUuFItVfuuJhWw2yk7fv67yltQ7fZD6IQ2OsHHluoVmstnQJuCg5raXJR696Ubw==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.13.tgz", + "integrity": "sha512-XsGYhVhvEikX1Yz0kyIoLssJf2Rs6E0U2w2YuKdT4jSra5A/g8V2oLROC1s56NldbgnpesTYB2z55KCHHbKyjw==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" } }, @@ -4666,12 +5585,12 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.11.tgz", - "integrity": "sha512-Malj4voNTL4+a5ZL3a6+Ij7JTUMTa2R7c3ZIBzMxN5OUUgAspU7uFi1Q97f4B0afVh2joQBAWH5IQJUG25nl8g==", + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.15.tgz", + "integrity": "sha512-xH4kRBw01gJgWiU+/mNTrnyFXeozpZHw39gLb3JKGsFDVmSrJZ8/tRqu27tU/ki1gKkxr2wApu+dEYjI3QwV1Q==", "dependencies": { - "@smithy/protocol-http": "^3.0.5", - "@smithy/types": "^2.3.3", + "@smithy/protocol-http": "^3.0.9", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -4679,11 +5598,11 @@ } }, "node_modules/@smithy/middleware-content-length/node_modules/@smithy/protocol-http": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.5.tgz", - "integrity": "sha512-3t3fxj+ip4EPHRC2fQ0JimMxR/qCQ1LSQJjZZVZFgROnFLYWPDgUZqpoi7chr+EzatxJVXF/Rtoi5yLHOWCoZQ==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.9.tgz", + "integrity": "sha512-U1wl+FhYu4/BC+rjwh1lg2gcJChQhytiNQSggREgQ9G2FzmoK9sACBZvx7thyWMvRyHQTE22mO2d5UM8gMKDBg==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -4691,14 +5610,16 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.0.9.tgz", - "integrity": "sha512-72/o8R6AAO4+nyTI6h4z6PYGTSA4dr1M7tZz29U8DEUHuh1YkhC77js0P6RyF9G0wDLuYqxb+Yh0crI5WG2pJg==", - "dependencies": { - "@smithy/middleware-serde": "^2.0.9", - "@smithy/types": "^2.3.3", - "@smithy/url-parser": "^2.0.9", - "@smithy/util-middleware": "^2.0.2", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.2.0.tgz", + "integrity": "sha512-tddRmaig5URk2106PVMiNX6mc5BnKIKajHHDxb7K0J5MLdcuQluHMGnjkv18iY9s9O0tF+gAcPd/pDXA5L9DZw==", + "dependencies": { + "@smithy/middleware-serde": "^2.0.13", + "@smithy/node-config-provider": "^2.1.5", + "@smithy/shared-ini-file-loader": "^2.2.4", + "@smithy/types": "^2.5.0", + "@smithy/url-parser": "^2.0.13", + "@smithy/util-middleware": "^2.0.6", "tslib": "^2.5.0" }, "engines": { @@ -4706,16 +5627,16 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.12.tgz", - "integrity": "sha512-YQ/ufXX4/d9/+Jf1QQ4J+CVeupC7BW52qldBTvRV33PDX9vxndlAwkFwzBcmnUFC3Hjf1//HW6I77EItcjNSCA==", - "dependencies": { - "@smithy/node-config-provider": "^2.0.12", - "@smithy/protocol-http": "^3.0.5", - "@smithy/service-error-classification": "^2.0.2", - "@smithy/types": "^2.3.3", - "@smithy/util-middleware": "^2.0.2", - "@smithy/util-retry": "^2.0.2", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.20.tgz", + "integrity": "sha512-X2yrF/SHDk2WDd8LflRNS955rlzQ9daz9UWSp15wW8KtzoTXg3bhHM78HbK1cjr48/FWERSJKh9AvRUUGlIawg==", + "dependencies": { + "@smithy/node-config-provider": "^2.1.5", + "@smithy/protocol-http": "^3.0.9", + "@smithy/service-error-classification": "^2.0.6", + "@smithy/types": "^2.5.0", + "@smithy/util-middleware": "^2.0.6", + "@smithy/util-retry": "^2.0.6", "tslib": "^2.5.0", "uuid": "^8.3.2" }, @@ -4724,11 +5645,11 @@ } }, "node_modules/@smithy/middleware-retry/node_modules/@smithy/protocol-http": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.5.tgz", - "integrity": "sha512-3t3fxj+ip4EPHRC2fQ0JimMxR/qCQ1LSQJjZZVZFgROnFLYWPDgUZqpoi7chr+EzatxJVXF/Rtoi5yLHOWCoZQ==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.9.tgz", + "integrity": "sha512-U1wl+FhYu4/BC+rjwh1lg2gcJChQhytiNQSggREgQ9G2FzmoK9sACBZvx7thyWMvRyHQTE22mO2d5UM8gMKDBg==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -4736,11 +5657,11 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.9.tgz", - "integrity": "sha512-GVbauxrr6WmtCaesakktg3t5LR/yDbajpC7KkWc8rtCpddMI4ShAVO5Q6DqwX8MDFi4CLaY8H7eTGcxhl3jbLg==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.13.tgz", + "integrity": "sha512-tBGbeXw+XsE6pPr4UaXOh+UIcXARZeiA8bKJWxk2IjJcD1icVLhBSUQH9myCIZLNNzJIH36SDjUX8Wqk4xJCJg==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -4748,11 +5669,11 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.3.tgz", - "integrity": "sha512-AlhPmbwpkC4lQBVaVHXczmjFvsAhDHhrakqLt038qFLotnJcvDLhmMzAtu23alBeOSkKxkTQq0LsAt2N0WpAbw==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.7.tgz", + "integrity": "sha512-L1KLAAWkXbGx1t2jjCI/mDJ2dDNq+rp4/ifr/HcC6FHngxho5O7A5bQLpKHGlkfATH6fUnOEx0VICEVFA4sUzw==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -4760,13 +5681,13 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.0.12.tgz", - "integrity": "sha512-df9y9ywv+JmS40Y60ZqJ4jfZiTCmyHQffwzIqjBjLJLJl0imf9F6DWBd+jiEWHvlohR+sFhyY+KL/qzKgnAq1A==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.1.5.tgz", + "integrity": "sha512-3Omb5/h4tOCuKRx4p4pkYTvEYRCYoKk52bOYbKUyz/G/8gERbagsN8jFm4FjQubkrcIqQEghTpQaUw6uk+0edw==", "dependencies": { - "@smithy/property-provider": "^2.0.10", - "@smithy/shared-ini-file-loader": "^2.0.11", - "@smithy/types": "^2.3.3", + "@smithy/property-provider": "^2.0.14", + "@smithy/shared-ini-file-loader": "^2.2.4", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -4774,14 +5695,14 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.1.5.tgz", - "integrity": "sha512-52uF+BrZaFiBh+NT/bADiVDCQO91T+OwDRsuaAeWZC1mlCXFjAPPQdxeQohtuYOe9m7mPP/xIMNiqbe8jvndHA==", - "dependencies": { - "@smithy/abort-controller": "^2.0.9", - "@smithy/protocol-http": "^3.0.5", - "@smithy/querystring-builder": "^2.0.9", - "@smithy/types": "^2.3.3", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.1.9.tgz", + "integrity": "sha512-+K0q3SlNcocmo9OZj+fz67gY4lwhOCvIJxVbo/xH+hfWObvaxrMTx7JEzzXcluK0thnnLz++K3Qe7Z/8MDUreA==", + "dependencies": { + "@smithy/abort-controller": "^2.0.13", + "@smithy/protocol-http": "^3.0.9", + "@smithy/querystring-builder": "^2.0.13", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -4789,11 +5710,11 @@ } }, "node_modules/@smithy/node-http-handler/node_modules/@smithy/protocol-http": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.5.tgz", - "integrity": "sha512-3t3fxj+ip4EPHRC2fQ0JimMxR/qCQ1LSQJjZZVZFgROnFLYWPDgUZqpoi7chr+EzatxJVXF/Rtoi5yLHOWCoZQ==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.9.tgz", + "integrity": "sha512-U1wl+FhYu4/BC+rjwh1lg2gcJChQhytiNQSggREgQ9G2FzmoK9sACBZvx7thyWMvRyHQTE22mO2d5UM8gMKDBg==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -4801,11 +5722,11 @@ } }, "node_modules/@smithy/property-provider": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.10.tgz", - "integrity": "sha512-YMBVfh0ZMmJtbsUn+WfSwR32iRljZPdRN0Tn2GAcdJ+ejX8WrBXD7Z0jIkQDrQZr8fEuuv5x8WxMIj+qVbsPQw==", + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.14.tgz", + "integrity": "sha512-k3D2qp9o6imTrLaXRj6GdLYEJr1sXqS99nLhzq8fYmJjSVOeMg/G+1KVAAc7Oxpu71rlZ2f8SSZxcSxkevuR0A==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -4826,11 +5747,11 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.9.tgz", - "integrity": "sha512-Yt6CPF4j3j1cuwod/DRflbuXxBFjJm7gAjy6W1RE21Rz5/kfGFqiZBXWmmXwGtnnhiLThYwoHK4S6/TQtnx0Fg==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.13.tgz", + "integrity": "sha512-JhXKwp3JtsFUe96XLHy/nUPEbaXqn6r7xE4sNaH8bxEyytE5q1fwt0ew/Ke6+vIC7gP87HCHgQpJHg1X1jN2Fw==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "@smithy/util-uri-escape": "^2.0.0", "tslib": "^2.5.0" }, @@ -4839,11 +5760,11 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.9.tgz", - "integrity": "sha512-U6z4N743s4vrcxPW8p8+reLV0PjMCYEyb1/wtMVvv3VnbJ74gshdI8SR1sBnEh95cF8TxonmX5IxY25tS9qGfg==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.13.tgz", + "integrity": "sha512-TEiT6o8CPZVxJ44Rly/rrsATTQsE+b/nyBVzsYn2sa75xAaZcurNxsFd8z1haoUysONiyex24JMHoJY6iCfLdA==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -4851,22 +5772,22 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.2.tgz", - "integrity": "sha512-GTUd2j63gKy7A+ggvSdn2hc4sejG7LWfE+ZMF17vzWoNyqERWbRP7HTPS0d0Lwg1p6OQCAzvNigSrEIWVFt6iA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.6.tgz", + "integrity": "sha512-fCQ36frtYra2fqY2/DV8+3/z2d0VB/1D1hXbjRcM5wkxTToxq6xHbIY/NGGY6v4carskMyG8FHACxgxturJ9Pg==", "dependencies": { - "@smithy/types": "^2.3.3" + "@smithy/types": "^2.5.0" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.0.11.tgz", - "integrity": "sha512-Sf0u5C5px6eykXi6jImDTp+edvG3REtPjXnFWU/J+b7S2wkXwUqFXqBL5DdM4zC1F+M8u57ZT7NRqDwMOw7/Tw==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.2.4.tgz", + "integrity": "sha512-9dRknGgvYlRIsoTcmMJXuoR/3ekhGwhRq4un3ns2/byre4Ql5hyUN4iS0x8eITohjU90YOnUCsbRwZRvCkbRfw==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -4892,13 +5813,13 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.7.tgz", - "integrity": "sha512-r6T/oiBQ8vCbGqObH4/h0YqD0jFB1hAS9KFRmuTfaNJueu/L2hjmjqFjv3PV5lkbNHTgUYraSv4cFQ1naxiELQ==", + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.15.tgz", + "integrity": "sha512-rngZcQu7Jvs9UbHihK1EI67RMPuzkc3CJmu4MBgB7D7yBnMGuFR86tq5rqHfL2gAkNnMelBN/8kzQVvZjNKefQ==", "dependencies": { - "@smithy/middleware-stack": "^2.0.3", - "@smithy/types": "^2.3.3", - "@smithy/util-stream": "^2.0.12", + "@smithy/middleware-stack": "^2.0.7", + "@smithy/types": "^2.5.0", + "@smithy/util-stream": "^2.0.20", "tslib": "^2.5.0" }, "engines": { @@ -4906,9 +5827,9 @@ } }, "node_modules/@smithy/types": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.3.3.tgz", - "integrity": "sha512-zTdIPR9PvFVNRdIKMQu4M5oyTaycIbUqLheQqaOi9rTWPkgjGO2wDBxMA1rBHQB81aqAEv+DbSS4jfKyQMnXRA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.5.0.tgz", + "integrity": "sha512-/a31lYofrMBkJb3BuPlYJTMKDj0hUmKUP6JFZQu6YVuQVoAjubiY0A52U9S0Uysd33n/djexCUSNJ+G9bf3/aA==", "dependencies": { "tslib": "^2.5.0" }, @@ -4917,19 +5838,19 @@ } }, "node_modules/@smithy/url-parser": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.9.tgz", - "integrity": "sha512-NBnJ0NiY8z6E82Xd5VYUFQfKwK/wA/+QkKmpYUYP+cpH3aCzE6g2gvixd9vQKYjsIdRfNPCf+SFAozt8ljozOw==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.13.tgz", + "integrity": "sha512-okWx2P/d9jcTsZWTVNnRMpFOE7fMkzloSFyM53fA7nLKJQObxM2T4JlZ5KitKKuXq7pxon9J6SF2kCwtdflIrA==", "dependencies": { - "@smithy/querystring-parser": "^2.0.9", - "@smithy/types": "^2.3.3", + "@smithy/querystring-parser": "^2.0.13", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" } }, "node_modules/@smithy/util-base64": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", - "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.1.tgz", + "integrity": "sha512-DlI6XFYDMsIVN+GH9JtcRp3j02JEVuWIn/QOZisVzpIAprdsxGveFed0bjbMRCqmIFe8uetn5rxzNrBtIGrPIQ==", "dependencies": { "@smithy/util-buffer-from": "^2.0.0", "tslib": "^2.5.0" @@ -4981,13 +5902,13 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.11.tgz", - "integrity": "sha512-0syV1Mz/mCQ7CG/MHKQfH+w86xq59jpD0EOXv5oe0WBXLmq2lWPpVHl2Y6+jQ+/9fYzyZ5NF+NC/WEIuiv690A==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.19.tgz", + "integrity": "sha512-VHP8xdFR7/orpiABJwgoTB0t8Zhhwpf93gXhNfUBiwAE9O0rvsv7LwpQYjgvbOUDDO8JfIYQB2GYJNkqqGWsXw==", "dependencies": { - "@smithy/property-provider": "^2.0.10", - "@smithy/smithy-client": "^2.1.7", - "@smithy/types": "^2.3.3", + "@smithy/property-provider": "^2.0.14", + "@smithy/smithy-client": "^2.1.15", + "@smithy/types": "^2.5.0", "bowser": "^2.11.0", "tslib": "^2.5.0" }, @@ -4996,22 +5917,36 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.13.tgz", - "integrity": "sha512-6BtCHYdw5Z8r6KpW8tRCc3yURgvcQwfIEeHhR70BeSOfx8T/TXPPjb8A+K45+KASspa3fzrsSxeIwB0sAeMoHA==", - "dependencies": { - "@smithy/config-resolver": "^2.0.10", - "@smithy/credential-provider-imds": "^2.0.12", - "@smithy/node-config-provider": "^2.0.12", - "@smithy/property-provider": "^2.0.10", - "@smithy/smithy-client": "^2.1.7", - "@smithy/types": "^2.3.3", + "version": "2.0.25", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.25.tgz", + "integrity": "sha512-jkmep6/JyWmn2ADw9VULDeGbugR4N/FJCKOt+gYyVswmN1BJOfzF2umaYxQ1HhQDvna3kzm1Dbo1qIfBW4iuHA==", + "dependencies": { + "@smithy/config-resolver": "^2.0.18", + "@smithy/credential-provider-imds": "^2.1.1", + "@smithy/node-config-provider": "^2.1.5", + "@smithy/property-provider": "^2.0.14", + "@smithy/smithy-client": "^2.1.15", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { "node": ">= 10.0.0" } }, + "node_modules/@smithy/util-endpoints": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-1.0.4.tgz", + "integrity": "sha512-FPry8j1xye5yzrdnf4xKUXVnkQErxdN7bUIaqC0OFoGsv2NfD9b2UUMuZSSt+pr9a8XWAqj0HoyVNUfPiZ/PvQ==", + "dev": true, + "dependencies": { + "@smithy/node-config-provider": "^2.1.5", + "@smithy/types": "^2.5.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@smithy/util-hex-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", @@ -5024,11 +5959,11 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.2.tgz", - "integrity": "sha512-UGPZM+Ja/vke5pc/S8G0LNiHpVirtjppsXO+GK9m9wbzRGzPJTfnZA/gERUUN/AfxEy/8SL7U1kd7u4t2X8K1w==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.6.tgz", + "integrity": "sha512-7W4uuwBvSLgKoLC1x4LfeArCVcbuHdtVaC4g30kKsD1erfICyQ45+tFhhs/dZNeQg+w392fhunCm/+oCcb6BSA==", "dependencies": { - "@smithy/types": "^2.3.3", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -5036,12 +5971,12 @@ } }, "node_modules/@smithy/util-retry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.2.tgz", - "integrity": "sha512-ovWiayUB38moZcLhSFFfUgB2IMb7R1JfojU20qSahjxAgfOZvDWme3eOYUMtAVnouZ9kYJiFgHLy27qRH4NeeA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.6.tgz", + "integrity": "sha512-PSO41FofOBmyhPQJwBQJ6mVlaD7Sp9Uff9aBbnfBJ9eqXOE/obrqQjn0PNdkfdvViiPXl49BINfnGcFtSP4kYw==", "dependencies": { - "@smithy/service-error-classification": "^2.0.2", - "@smithy/types": "^2.3.3", + "@smithy/service-error-classification": "^2.0.6", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -5049,17 +5984,17 @@ } }, "node_modules/@smithy/util-stream": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.12.tgz", - "integrity": "sha512-FOCpRLaj6gvSyUC5mJAACT+sPMPmp9sD1o+hVbUH/QxwZfulypA3ZIFdAg/59/IY0d/1Q4CTztsiHEB5LgjN4g==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.20.tgz", + "integrity": "sha512-tT8VASuD8jJu0yjHEMTCPt1o5E3FVzgdsxK6FQLAjXKqVv5V8InCnc0EOsYrijgspbfDqdAJg7r0o2sySfcHVg==", "dependencies": { - "@smithy/fetch-http-handler": "^2.1.5", - "@smithy/node-http-handler": "^2.1.5", - "@smithy/types": "^2.3.3", - "@smithy/util-base64": "^2.0.0", + "@smithy/fetch-http-handler": "^2.2.6", + "@smithy/node-http-handler": "^2.1.9", + "@smithy/types": "^2.5.0", + "@smithy/util-base64": "^2.0.1", "@smithy/util-buffer-from": "^2.0.0", "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", + "@smithy/util-utf8": "^2.0.2", "tslib": "^2.5.0" }, "engines": { @@ -5078,9 +6013,9 @@ } }, "node_modules/@smithy/util-utf8": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", - "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.2.tgz", + "integrity": "sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==", "dependencies": { "@smithy/util-buffer-from": "^2.0.0", "tslib": "^2.5.0" @@ -5090,12 +6025,12 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.0.9.tgz", - "integrity": "sha512-Hy9Cs0FtIacC1aVFk98bm/7CYqim9fnHAPRnV/SB2mj02ExYs/9Dn5SrNQmtTBTLCn65KqYnNVBNS8GuGpZOOw==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-2.0.13.tgz", + "integrity": "sha512-YovIQatiuM7giEsRFotqJa2i3EbU2EE3PgtpXgtLgpx5rXiZMAwPxXYDFVFhuO0lbqvc/Zx4n+ZIisXOHPSqyg==", "dependencies": { - "@smithy/abort-controller": "^2.0.9", - "@smithy/types": "^2.3.3", + "@smithy/abort-controller": "^2.0.13", + "@smithy/types": "^2.5.0", "tslib": "^2.5.0" }, "engines": { @@ -7352,9 +8287,9 @@ "dev": true }, "node_modules/cli-spinners": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", - "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", + "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==", "dev": true, "engines": { "node": ">=6" @@ -9901,6 +10836,26 @@ "lodash": "^4.17.15" } }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -14054,7 +15009,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -14217,8 +15171,7 @@ "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, "node_modules/next-tick": { "version": "1.1.0", @@ -16074,15 +17027,15 @@ } }, "node_modules/serverless": { - "version": "3.34.0", - "resolved": "https://registry.npmjs.org/serverless/-/serverless-3.34.0.tgz", - "integrity": "sha512-xtWAg78NGgboolP/GArdorx+UHyESJgriGSE6Qpgg9trTYsKMyd8YKaMIM3sidy89e45XZopRSpvnPYoQCJOBA==", + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/serverless/-/serverless-3.36.0.tgz", + "integrity": "sha512-VY7UzP4u1/yuTNpF2Wssrru16qhhReLCjgL2oeHCvhujxPyTFv9TQGSlLhaT0ZUCXhRBphwVwITTRopo6NSUgA==", "dev": true, "hasInstallScript": true, "dependencies": { - "@serverless/dashboard-plugin": "^6.2.3", - "@serverless/platform-client": "^4.3.2", - "@serverless/utils": "^6.11.1", + "@serverless/dashboard-plugin": "^7.1.0", + "@serverless/platform-client": "^4.4.0", + "@serverless/utils": "^6.13.1", "abort-controller": "^3.0.0", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", @@ -16438,9 +17391,9 @@ "dev": true }, "node_modules/simple-git": { - "version": "3.19.1", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.19.1.tgz", - "integrity": "sha512-Ck+rcjVaE1HotraRAS8u/+xgTvToTuoMkT9/l9lvuP5jftwnYUp6DwuJzsKErHgfyRk8IB8pqGHWEbM3tLgV1w==", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.20.0.tgz", + "integrity": "sha512-ozK8tl2hvLts8ijTs18iFruE+RoqmC/mqZhjs/+V7gS5W68JpJ3+FCTmLVqmR59MaUQ52MfGQuWsIqfsTbbJ0Q==", "dev": true, "dependencies": { "@kwsites/file-exists": "^1.1.1", @@ -16504,7 +17457,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -17788,6 +18740,18 @@ "node": ">=4.2.0" } }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/unbzip2-stream": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", @@ -18347,6 +19311,11 @@ "node": ">=0.6.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", diff --git a/backend/package.json b/backend/package.json index 103d44ef2..d401a54ed 100644 --- a/backend/package.json +++ b/backend/package.json @@ -23,6 +23,7 @@ "express": "^4.18.1", "global-agent": "^2.2.0", "got": "^11.8.5", + "handlebars": "^4.7.8", "helmet": "^4.1.1", "http-proxy-middleware": "^2.0.6", "jsdom": "^22.1", diff --git a/backend/serverless.yml b/backend/serverless.yml index c8aad3d8d..2923c88b1 100644 --- a/backend/serverless.yml +++ b/backend/serverless.yml @@ -43,6 +43,9 @@ provider: Action: - ecs:RunTask - ecs:ListTasks + - ecs:DescribeTasks + - ecs:DescribeServices + - ecs:UpdateService - iam:PassRole Resource: '*' - Effect: Allow @@ -65,7 +68,9 @@ provider: - Effect: Allow Action: - sqs:ReceiveMessage + - sqs:DeleteMessage - sqs:SendMessage + - sqs:GetQueueAttributes Resource: '*' - Effect: Allow Action: @@ -108,7 +113,6 @@ resources: MaximumMessageSize: 262144 # 256 KB MessageRetentionPeriod: 604800 # 7 days - functions: - ${file(./src/tasks/functions.yml)} - ${file(./src/api/functions.yml)} diff --git a/backend/src/api/app.ts b/backend/src/api/app.ts index fd3a93fd6..e8f1e7979 100644 --- a/backend/src/api/app.ts +++ b/backend/src/api/app.ts @@ -63,6 +63,7 @@ app.use(cookieParser()); app.get('/', handlerToExpress(healthcheck)); app.post('/auth/login', handlerToExpress(auth.login)); app.post('/auth/callback', handlerToExpress(auth.callback)); +app.post('/users/register', handlerToExpress(users.register)); const checkUserLoggedIn = async (req, res, next) => { req.requestContext = { @@ -227,6 +228,7 @@ app.use( const authenticatedNoTermsRoute = express.Router(); authenticatedNoTermsRoute.use(checkUserLoggedIn); authenticatedNoTermsRoute.get('/users/me', handlerToExpress(users.me)); +// authenticatedNoTermsRoute.post('/users/register', handlerToExpress(users.register)); authenticatedNoTermsRoute.post( '/users/me/acceptTerms', handlerToExpress(users.acceptTerms) @@ -313,6 +315,14 @@ authenticatedRoute.get( '/organizations/:organizationId', handlerToExpress(organizations.get) ); +authenticatedRoute.get( + '/organizations/state/:state', + handlerToExpress(organizations.getByState) +); +authenticatedRoute.get( + '/organizations/regionId/:regionId', + handlerToExpress(organizations.getByRegionId) +); authenticatedRoute.post( '/organizations', handlerToExpress(organizations.create) @@ -325,6 +335,10 @@ authenticatedRoute.delete( '/organizations/:organizationId', handlerToExpress(organizations.del) ); +authenticatedRoute.post( + '/v2/organizations/:organizationId/users', + handlerToExpress(organizations.addUserV2) +); authenticatedRoute.post( '/organizations/:organizationId/roles/:roleId/approve', handlerToExpress(organizations.approveRole) @@ -349,6 +363,14 @@ authenticatedRoute.post('/stats', handlerToExpress(stats.get)); authenticatedRoute.post('/users', handlerToExpress(users.invite)); authenticatedRoute.get('/users', handlerToExpress(users.list)); authenticatedRoute.delete('/users/:userId', handlerToExpress(users.del)); +authenticatedRoute.get( + '/users/state/:state', + handlerToExpress(users.getByState) +); +authenticatedRoute.get( + '/users/regionId/:regionId', + handlerToExpress(users.getByRegionId) +); authenticatedRoute.post('/users/search', handlerToExpress(users.search)); authenticatedRoute.post( @@ -361,6 +383,35 @@ authenticatedRoute.post( handlerToExpress(reports.list_reports) ); +//Authenticated Registration Routes +authenticatedRoute.put( + '/users/:userId/register/approve', + handlerToExpress(users.registrationApproval) +); + +authenticatedRoute.put( + '/users/:userId/register/deny', + handlerToExpress(users.registrationDenial) +); + +//************* */ +// V2 Routes // +//************* */ + +// Users +authenticatedRoute.put('/v2/users/:userId', handlerToExpress(users.updateV2)); +authenticatedRoute.get('/v2/users', handlerToExpress(users.getAllV2)); + +// Organizations +authenticatedRoute.put( + '/v2/organizations/:organizationId', + handlerToExpress(organizations.updateV2) +); +authenticatedRoute.get( + '/v2/organizations', + handlerToExpress(organizations.getAllV2) +); + app.use(authenticatedRoute); export default app; diff --git a/backend/src/api/auth.ts b/backend/src/api/auth.ts index dbe3261b5..c4b280ebf 100644 --- a/backend/src/api/auth.ts +++ b/backend/src/api/auth.ts @@ -257,6 +257,15 @@ export const isGlobalViewAdmin = (event: APIGatewayProxyEvent) => { : false; }; +/** Check if a user has regionalAdmin view permissions */ +export const isRegionalAdmin = (event: APIGatewayProxyEvent) => { + return event.requestContext.authorizer && + (event.requestContext.authorizer.userType === UserType.REGIONAL_ADMIN || + event.requestContext.authorizer.userType === UserType.GLOBAL_ADMIN) + ? true + : false; +}; + /** Checks if the current user is allowed to access (modify) a user with id userId */ export const canAccessUser = (event: APIGatewayProxyEvent, userId?: string) => { return userId && (userId === getUserId(event) || isGlobalWriteAdmin(event)); diff --git a/backend/src/api/helpers.ts b/backend/src/api/helpers.ts index 121b66099..c8cf3c0f1 100644 --- a/backend/src/api/helpers.ts +++ b/backend/src/api/helpers.ts @@ -9,6 +9,10 @@ import { ClassType } from 'class-transformer/ClassTransformer'; import { plainToClass } from 'class-transformer'; import { SES } from 'aws-sdk'; import * as nodemailer from 'nodemailer'; +import * as fs from 'fs'; +import * as handlebars from 'handlebars'; +import * as util from 'util'; +import S3Client from '../tasks/s3-client'; export const validateBody = async ( obj: ClassType, @@ -71,12 +75,12 @@ export const wrapHandler: WrapHandler = export const NotFound: APIGatewayProxyResult = { statusCode: 404, - body: '' + body: 'Item not found. View logs for details.' }; export const Unauthorized: APIGatewayProxyResult = { statusCode: 403, - body: '' + body: 'Unauthorized access. View logs for details.' }; export const sendEmail = async ( @@ -96,3 +100,141 @@ export const sendEmail = async ( replyTo: process.env.CROSSFEED_SUPPORT_EMAIL_REPLYTO! }); }; + +export const sendUserNotificationEmail = async ( + recepient: string, + p_subject: string, + p_firstName: string, + p_lastname: string, + template_file: string +) => { + const transporter = nodemailer.createTransport({ + SES: new SES({ region: 'us-east-1' }) + }); + + const client = new S3Client(); + const html = await client.getEmailAsset(template_file); + const template = handlebars.compile(html); + const data = { + first_name: p_firstName, + last_name: p_lastname + }; + + const htmlToSend = template(data); + + const mailOptions = { + from: process.env.CROSSFEED_SUPPORT_EMAIL_SENDER, + to: recepient, + subject: p_subject, + html: htmlToSend, + attachments: [ + { + filename: 'banner.png', + content: await client.getEmailAsset('banner.png'), + cid: 'CISA Banner' + }, + { + filename: 'web.png', + content: await client.getEmailAsset('banner.png'), + cid: 'CISA Web' + }, + { + filename: 'email.png', + content: await client.getEmailAsset('email.png'), + cid: 'CISA Email' + }, + { + filename: 'linkedin.png', + content: await client.getEmailAsset('linkedin.png'), + cid: 'CISA LinkedIn' + }, + { + filename: 'twitter.png', + content: await client.getEmailAsset('twitter.png'), + cid: 'CISA Twitter' + }, + { + filename: 'facebook.png', + content: await client.getEmailAsset('facebooK.png'), + cid: 'CISA Facebook' + }, + { + filename: 'instagram.png', + content: await client.getEmailAsset('instagram.png'), + cid: 'CISA Instagram' + } + ] + }; + + await transporter.sendMail(mailOptions); +}; + +export const sendRegionalAdminNotificationEmail = async ( + recepient: string, + p_subject: string, + p_firstName: string, + p_lastname: string, + p_username: string +) => { + const transporter = nodemailer.createTransport({ + SES: new SES({ region: 'us-east-1' }) + }); + + const client = new S3Client(); + const html = await client.getEmailAsset( + 'crossfeed_regional_admin_notification.html' + ); + const template = handlebars.compile(html); + const data = { + first_name: p_firstName, + last_name: p_lastname + }; + + const htmlToSend = template(data); + + const mailOptions = { + from: process.env.CROSSFEED_SUPPORT_EMAIL_SENDER, + to: recepient, + subject: p_subject, + html: htmlToSend, + attachments: [ + { + filename: 'banner.png', + content: await client.getEmailAsset('banner.png'), + cid: 'CISA Banner' + }, + { + filename: 'web.png', + content: await client.getEmailAsset('banner.png'), + cid: 'CISA Web' + }, + { + filename: 'email.png', + content: await client.getEmailAsset('email.png'), + cid: 'CISA Email' + }, + { + filename: 'linkedin.png', + content: await client.getEmailAsset('linkedin.png'), + cid: 'CISA LinkedIn' + }, + { + filename: 'twitter.png', + content: await client.getEmailAsset('twitter.png'), + cid: 'CISA Twitter' + }, + { + filename: 'facebook.png', + content: await client.getEmailAsset('facebooK.png'), + cid: 'CISA Facebook' + }, + { + filename: 'instagram.png', + content: await client.getEmailAsset('instagram.png'), + cid: 'CISA Instagram' + } + ] + }; + + await transporter.sendMail(mailOptions); +}; diff --git a/backend/src/api/organizations.ts b/backend/src/api/organizations.ts index 0ea75ba8a..e5c395c1e 100644 --- a/backend/src/api/organizations.ts +++ b/backend/src/api/organizations.ts @@ -5,7 +5,9 @@ import { IsBoolean, IsUUID, IsOptional, - IsNotEmpty + IsNotEmpty, + IsNumber, + IsEnum } from 'class-validator'; import { Organization, @@ -21,6 +23,7 @@ import { validateBody, wrapHandler, NotFound, Unauthorized } from './helpers'; import { isOrgAdmin, isGlobalWriteAdmin, + isRegionalAdmin, getOrgMemberships, isGlobalViewAdmin } from './auth'; @@ -89,12 +92,96 @@ class NewOrganization extends NewOrganizationNonGlobalAdmins { parent?: string; } +// Type Validation Options +class UpdateOrganizationMetaV2 { + @IsString() + @IsNotEmpty() + @IsOptional() + name: string; + + @IsBoolean() + @IsOptional() + isPassive: boolean; + + @IsString() + @IsNotEmpty() + @IsOptional() + state: string; + + @IsString() + @IsNotEmpty() + @IsOptional() + regionId: string; + + @IsString() + @IsNotEmpty() + @IsOptional() + country: string; + + @IsNumber() + @IsOptional() + stateFips: number; + + @IsString() + @IsNotEmpty() + @IsOptional() + stateName: string; + + @IsString() + @IsNotEmpty() + @IsOptional() + county: string; + + @IsNumber() + @IsOptional() + countyFips: number; + + @IsString() + @IsNotEmpty() + @IsOptional() + acronym: string; + + @IsString() + @IsNotEmpty() + @IsOptional() + type: string; +} + class NewDomain { @IsString() @IsNotEmpty() domain: string; } +class NewOrganizationRoleDB { + @IsEnum(User) + user: User; + + @IsEnum(Organization) + organization: Organization; + + @IsBoolean() + approved: boolean; + + @IsString() + role: string; + + @IsEnum(User) + approvedBy: User; + + @IsEnum(User) + createdBy: User; +} + +class NewOrganizationRoleBody { + @IsString() + userId: string; + + @IsString() + @IsOptional() + role: any; +} + const findOrCreateTags = async ( tags: OrganizationTag[] ): Promise => { @@ -608,3 +695,238 @@ export const removeRole = wrapHandler(async (event) => { body: JSON.stringify(result) }; }); + +/** + * @swagger + * + * /organizations/regionId/{regionId}: + * get: + * description: List organizations with specific regionId. + * parameters: + * - in: path + * name: regionId + * description: Organization regionId + * tags: + * - Organizations + */ +export const getByRegionId = wrapHandler(async (event) => { + if (!isRegionalAdmin(event)) return Unauthorized; + const regionId = event.pathParameters?.regionId; + await connectToDatabase(); + const result = await Organization.find({ + where: { regionId: regionId } + }); + + if (result) { + return { + statusCode: 200, + body: JSON.stringify(result) + }; + } + return NotFound; +}); + +/** + * @swagger + * + * /organizations/state/{state}: + * get: + * description: List organizations with specific state. + * parameters: + * - in: path + * name: state + * description: Organization state + * tags: + * - Organizations + */ +export const getByState = wrapHandler(async (event) => { + if (!isRegionalAdmin(event)) return Unauthorized; + const state = event.pathParameters?.state; + await connectToDatabase(); + const result = await Organization.find({ + where: { state: state } + }); + + if (result) { + return { + statusCode: 200, + body: JSON.stringify(result) + }; + } + return NotFound; +}); + +//V2 Endpoints + +/** + * @swagger + * + * /v2/organizations: + * get: + * description: List all organizations with query parameters. + * tags: + * - Users + * parameters: + * - in: query + * name: state + * required: false + * schema: + * type: array + * items: + * type: string + * - in: query + * name: regionId + * required: false + * schema: + * type: array + * items: + * type: string + * + */ +export const getAllV2 = wrapHandler(async (event) => { + if (!isRegionalAdmin(event)) return Unauthorized; + const filterParams = {}; + + if (event.query && event.query.state) { + filterParams['state'] = event.query.state; + } + if (event.query && event.query.regionId) { + filterParams['regionId'] = event.query.regionId; + } + + await connectToDatabase(); + if (Object.entries(filterParams).length === 0) { + const result = await Organization.find({}); + return { + statusCode: 200, + body: JSON.stringify(result) + }; + } else { + const result = await Organization.find({ + where: filterParams + }); + return { + statusCode: 200, + body: JSON.stringify(result) + }; + } +}); + +/** + * @swagger + * + * /v2/organizations/{id}: + * put: + * description: Update a particular organization. + * parameters: + * - in: path + * name: id + * description: Organization id + * tags: + * - Organizations + */ + +export const updateV2 = wrapHandler(async (event) => { + if (!isRegionalAdmin(event)) return Unauthorized; + // Get the organization id from the path + const id = event.pathParameters?.organizationId; + + // confirm that the id is a valid UUID + if (!id || !isUUID(id)) { + return NotFound; + } + + // TODO: check permissions + // if (!isOrgAdmin(event, id)) return Unauthorized; + + // Validate the body + const validatedBody = await validateBody( + UpdateOrganizationMetaV2, + event.body + ); + + // Connect to the database + await connectToDatabase(); + + // Update the organization + const updateResp = await Organization.update(id, validatedBody); + + // Handle response + if (updateResp) { + const updatedOrg = await Organization.findOne(id); + return { + statusCode: 200, + body: JSON.stringify(updatedOrg) + }; + } + return NotFound; +}); + +/** + * @swagger + * + * /v2/organizations/{orgId}/users: + * post: + * description: Add a user to a particular organization. + * parameters: + * - in: path + * name: orgId + * description: Organization id + * tags: + * - Organizations + */ + +export const addUserV2 = wrapHandler(async (event) => { + // Permissions + if (!isRegionalAdmin(event)) return Unauthorized; + // TODO: check permissions + // if (!isOrgAdmin(event, id)) return Unauthorized; + + // Validate the body + const body = await validateBody(NewOrganizationRoleBody, event.body); + + // Connect to the database + await connectToDatabase(); + + // Get the organization id from the path + const orgId = event.pathParameters?.organizationId; + // confirm that the orgId is a valid UUID + if (!orgId || !isUUID(orgId)) { + return NotFound; + } + // Get Organization from the database + const org = await Organization.findOne(orgId); + + // Get the user id from the body + const userId = body.userId; + // confirm that the userId is a valid UUID + if (!userId || !isUUID(userId)) { + return NotFound; + } + // Get User from the database + const user = await User.findOneOrFail(userId); + + const newRoleData = { + user: user, + organization: org, + approved: true, + role: body.role, + approvedBy: event.requestContext.authorizer!.id, + createdBy: event.requestContext.authorizer!.id + }; + + // Add a role to make association to user/organization + const newRole = Role.create(newRoleData); + await Role.save(newRole); + // const roleId = newRole.id; + + // Handle response + if (newRole) { + // const roleResp = await Organization.findOne(roleId); + return { + statusCode: 200, + body: JSON.stringify(newRole) + }; + } + return NotFound; +}); diff --git a/backend/src/api/roles.ts b/backend/src/api/roles.ts new file mode 100644 index 000000000..46e71d8a5 --- /dev/null +++ b/backend/src/api/roles.ts @@ -0,0 +1,834 @@ +import { + IsString, + isUUID, + IsArray, + IsBoolean, + IsUUID, + IsOptional, + IsNotEmpty, + IsNumber +} from 'class-validator'; +import { + Organization, + connectToDatabase, + Role, + ScanTask, + Scan, + User, + OrganizationTag, + PendingDomain +} from '../models'; +import { validateBody, wrapHandler, NotFound, Unauthorized } from './helpers'; +import { + isOrgAdmin, + isGlobalWriteAdmin, + isRegionalAdmin, + getOrgMemberships, + isGlobalViewAdmin +} from './auth'; +import { In } from 'typeorm'; +import { plainToClass } from 'class-transformer'; +import { randomBytes } from 'crypto'; +import { promises } from 'dns'; + +/** + * @swagger + * + * /roless/{id}: + * delete: + * description: Delete a particular organization. + * parameters: + * - in: path + * name: id + * description: Organization id + * tags: + * - Organizations + */ +export const del = wrapHandler(async (event) => { + const id = event.pathParameters?.organizationId; + + if (!id || !isUUID(id)) { + return NotFound; + } + + if (!isGlobalWriteAdmin(event)) return Unauthorized; + + await connectToDatabase(); + const result = await Organization.delete(id); + return { + statusCode: 200, + body: JSON.stringify(result) + }; +}); + +// Used exclusively for deleting pending domains +class PendingDomainBody { + @IsArray() + @IsOptional() + pendingDomains: PendingDomain[]; +} + +class NewOrganizationNonGlobalAdmins { + @IsString() + name: string; + + @IsBoolean() + isPassive: boolean; +} + +class NewOrganization extends NewOrganizationNonGlobalAdmins { + @IsArray() + rootDomains: string[]; + + @IsArray() + ipBlocks: string[]; + + @IsArray() + tags: OrganizationTag[]; + + @IsUUID() + @IsOptional() + parent?: string; +} + +// Type Validation Options +class UpdateOrganizationMetaV2 { + @IsString() + @IsNotEmpty() + @IsOptional() + name: string; + + @IsBoolean() + @IsOptional() + isPassive: boolean; + + @IsString() + @IsNotEmpty() + @IsOptional() + state: string; + + @IsString() + @IsNotEmpty() + @IsOptional() + regionId: string; + + @IsString() + @IsNotEmpty() + @IsOptional() + country: string; + + @IsNumber() + @IsOptional() + stateFips: number; + + @IsString() + @IsNotEmpty() + @IsOptional() + stateName: string; + + @IsString() + @IsNotEmpty() + @IsOptional() + county: string; + + @IsNumber() + @IsOptional() + countyFips: number; + + @IsString() + @IsNotEmpty() + @IsOptional() + acronym: string; + + @IsString() + @IsNotEmpty() + @IsOptional() + type: string; +} + +class NewDomain { + @IsString() + @IsNotEmpty() + domain: string; +} + +const findOrCreateTags = async ( + tags: OrganizationTag[] +): Promise => { + const finalTags: OrganizationTag[] = []; + for (const tag of tags) { + if (!tag.id) { + // If no id is supplied, first check to see if a tag with this name exists + const found = await OrganizationTag.findOne({ + where: { name: tag.name } + }); + if (found) { + finalTags.push(found); + } else { + // If not, create it + const created = OrganizationTag.create({ name: tag.name }); + await created.save(); + finalTags.push(created); + } + } else { + finalTags.push(tag); + } + } + return finalTags; +}; + +/** + * @swagger + * + * /organizations/{id}: + * put: + * description: Update a particular organization. + * parameters: + * - in: path + * name: id + * description: Organization id + * tags: + * - Organizations + */ +export const update = wrapHandler(async (event) => { + const id = event.pathParameters?.organizationId; + + if (!id || !isUUID(id)) { + return NotFound; + } + + if (!isOrgAdmin(event, id)) return Unauthorized; + const body = await validateBody< + NewOrganization | NewOrganizationNonGlobalAdmins + >( + isGlobalWriteAdmin(event) + ? NewOrganization + : NewOrganizationNonGlobalAdmins, + event.body + ); + const pendingBody = await validateBody( + PendingDomainBody, + event.body + ); + + await connectToDatabase(); + const org = await Organization.findOne( + { + id + }, + { + relations: ['userRoles', 'granularScans'] + } + ); + if (org) { + if ('tags' in body) { + body.tags = await findOrCreateTags(body.tags); + } + + let newPendingDomains: PendingDomain[] = []; + if (pendingBody.pendingDomains) { + for (const domain of org.pendingDomains) { + if (pendingBody.pendingDomains.find((d) => d.name === domain.name)) { + // Don't delete + newPendingDomains.push(domain); + } + } + } else { + newPendingDomains = org.pendingDomains; + } + + Organization.merge(org, { + ...body, + pendingDomains: newPendingDomains, + parent: undefined + }); + await Organization.save(org); + return { + statusCode: 200, + body: JSON.stringify(org) + }; + } + return NotFound; +}); + +/** + * @swagger + * + * /organizations: + * post: + * description: Create a new organization. + * tags: + * - Organizations + */ +export const create = wrapHandler(async (event) => { + if (!isGlobalWriteAdmin(event)) return Unauthorized; + const body = await validateBody(NewOrganization, event.body); + await connectToDatabase(); + + if ('tags' in body) { + body.tags = await findOrCreateTags(body.tags); + } + const organization = await Organization.create({ + ...body, + createdBy: { id: event.requestContext.authorizer!.id }, + parent: { id: body.parent } + }); + const res = await organization.save(); + return { + statusCode: 200, + body: JSON.stringify(res) + }; +}); + +/** + * @swagger + * + * /organizations: + * get: + * description: List organizations that the user is a member of or has access to. + * tags: + * - Organizations + */ +export const list = wrapHandler(async (event) => { + if (!isGlobalViewAdmin(event) && getOrgMemberships(event).length === 0) { + return { + statusCode: 200, + body: JSON.stringify([]) + }; + } + await connectToDatabase(); + let where: any = { parent: null }; + if (!isGlobalViewAdmin(event)) { + where = { id: In(getOrgMemberships(event)), parent: null }; + } + const result = await Organization.find({ + where, + relations: ['userRoles', 'tags'], + order: { name: 'ASC' } + }); + + return { + statusCode: 200, + body: JSON.stringify(result) + }; +}); + +/** + * @swagger + * + * /organizations/tags: + * get: + * description: Fetchs all possible organization tags (must be global admin) + * tags: + * - Organizations + */ +export const getTags = wrapHandler(async (event) => { + if (!isGlobalViewAdmin(event)) { + return { + statusCode: 200, + body: JSON.stringify([]) + }; + } + await connectToDatabase(); + const result = await OrganizationTag.find({ + select: ['id', 'name'] + }); + + return { + statusCode: 200, + body: JSON.stringify(result) + }; +}); + +/** + * @swagger + * + * /organizations/{id}: + * get: + * description: Get information about a particular organization. + * parameters: + * - in: path + * name: id + * description: Organization id + * tags: + * - Organizations + */ +export const get = wrapHandler(async (event) => { + const id = event.pathParameters?.organizationId; + + if (!isOrgAdmin(event, id)) return Unauthorized; + + await connectToDatabase(); + const result = await Organization.findOne(id, { + relations: [ + 'userRoles', + 'userRoles.user', + 'granularScans', + 'tags', + 'parent', + 'children' + ] + }); + + if (result) { + result.scanTasks = await ScanTask.find({ + where: { + organization: { id } + }, + take: 10, + order: { + createdAt: 'DESC' + }, + relations: ['scan'] + }); + } + + return { + statusCode: result ? 200 : 404, + body: result ? JSON.stringify(result) : '' + }; +}); + +class UpdateBody { + @IsBoolean() + enabled: boolean; +} + +/** + * @swagger + * + * /organizations/{id}/granularScans/{scanId}/update: + * post: + * description: Enable or disable a scan for a particular organization; this endpoint can be called by organization admins and only works for user-modifiable scans. + * parameters: + * - in: path + * name: id + * description: Organization id + * - in: path + * name: scanId + * description: Role id + * tags: + * - Organizations + */ +export const updateScan = wrapHandler(async (event) => { + const organizationId = event.pathParameters?.organizationId; + + if (!organizationId || !isUUID(organizationId)) { + return NotFound; + } + + if (!isOrgAdmin(event, organizationId) && !isGlobalWriteAdmin(event)) + return Unauthorized; + + await connectToDatabase(); + const scanId = event.pathParameters?.scanId; + if (!scanId || !isUUID(scanId)) { + return NotFound; + } + const scan = await Scan.findOne({ + id: scanId, + isGranular: true, + isUserModifiable: true + }); + const organization = await Organization.findOne( + { + id: organizationId + }, + { + relations: ['granularScans'] + } + ); + if (!scan || !organization) { + return NotFound; + } + const body = await validateBody(UpdateBody, event.body); + if (body.enabled) { + organization?.granularScans.push(); + } + const existing = organization?.granularScans.find((s) => s.id === scanId); + if (body.enabled && !existing) { + organization.granularScans.push(plainToClass(Scan, { id: scanId })); + } else if (!body.enabled && existing) { + organization.granularScans = organization.granularScans.filter( + (s) => s.id !== scanId + ); + } + const res = await Organization.save(organization); + return { + statusCode: 200, + body: JSON.stringify(res) + }; +}); + +/** + * @swagger + * + * /organizations/{id}/initiateDomainVerification: + * post: + * description: Generates a token to verify a new domain via a DNS record + * parameters: + * - in: path + * name: id + * description: Organization id + * tags: + * - Organizations + */ +export const initiateDomainVerification = wrapHandler(async (event) => { + const organizationId = event.pathParameters?.organizationId; + + if (!organizationId || !isUUID(organizationId)) { + return NotFound; + } + + if (!isOrgAdmin(event, organizationId) && !isGlobalWriteAdmin(event)) + return Unauthorized; + const body = await validateBody(NewDomain, event.body); + + await connectToDatabase(); + const token = 'crossfeed-verification=' + randomBytes(16).toString('hex'); + + const organization = await Organization.findOne({ + id: organizationId + }); + if (!organization) { + return NotFound; + } + if (organization.rootDomains.find((domain) => domain === body.domain)) { + return { + statusCode: 422, + body: 'Domain already exists.' + }; + } + if ( + !organization.pendingDomains.find( + (domain) => domain['name'] === body.domain + ) + ) { + const domain: PendingDomain = { + name: body.domain, + token: token + }; + organization.pendingDomains.push(domain); + + const res = await Organization.save(organization); + } + return { + statusCode: 200, + body: JSON.stringify(organization.pendingDomains) + }; +}); + +/** + * @swagger + * + * /organizations/{id}/checkDomainVerification: + * post: + * description: Checks whether the DNS record has been created for the supplied domain + * parameters: + * - in: path + * name: id + * description: Organization id + * tags: + * - Organizations + */ +export const checkDomainVerification = wrapHandler(async (event) => { + const organizationId = event.pathParameters?.organizationId; + + if (!organizationId || !isUUID(organizationId)) { + return NotFound; + } + + if (!isOrgAdmin(event, organizationId) && !isGlobalWriteAdmin(event)) + return Unauthorized; + const body = await validateBody(NewDomain, event.body); + + await connectToDatabase(); + + const organization = await Organization.findOne({ + id: organizationId + }); + if (!organization) { + return NotFound; + } + const pendingDomain = organization.pendingDomains.find( + (domain) => domain['name'] === body.domain + ); + if (!pendingDomain) { + return { + statusCode: 422, + body: 'Please initiate the domain verification first.' + }; + } + try { + const res = await promises.resolveTxt(pendingDomain.name); + for (const record of res) { + for (const val of record) { + if (val === pendingDomain.token) { + // Success! + organization.rootDomains.push(pendingDomain.name); + organization.pendingDomains = organization.pendingDomains.filter( + (domain) => domain.name !== pendingDomain.name + ); + organization.save(); + return { + statusCode: 200, + body: JSON.stringify({ success: true, organization }) + }; + } + } + } + } catch (e) {} + + return { + statusCode: 200, + body: JSON.stringify({ success: false }) + }; +}); + +/** + * @swagger + * + * /organizations/{id}/roles/{roleId}/approve: + * post: + * description: Approve a role within an organization. + * parameters: + * - in: path + * name: id + * description: Organization id + * - in: path + * name: roleId + * description: Role id + * tags: + * - Organizations + */ +export const approveRole = wrapHandler(async (event) => { + const organizationId = event.pathParameters?.organizationId; + if (!isOrgAdmin(event, organizationId)) return Unauthorized; + + const id = event.pathParameters?.roleId; + if (!isUUID(id)) { + return NotFound; + } + + await connectToDatabase(); + const role = await Role.findOne({ + organization: { id: organizationId }, + id + }); + if (role) { + role.approved = true; + role.approvedBy = plainToClass(User, { + id: event.requestContext.authorizer!.id + }); + const result = await role.save(); + return { + statusCode: result ? 200 : 404, + body: JSON.stringify({}) + }; + } + + return NotFound; +}); + +/** + * @swagger + * + * /organizations/{id}/roles/{roleId}/remove: + * post: + * description: Remove a role within an organization. + * parameters: + * - in: path + * name: id + * description: Organization id + * - in: path + * name: roleId + * description: Role id + * tags: + * - Organizations + */ +export const removeRole = wrapHandler(async (event) => { + const organizationId = event.pathParameters?.organizationId; + if (!isOrgAdmin(event, organizationId)) return Unauthorized; + + const id = event.pathParameters?.roleId; + if (!id || !isUUID(id)) { + return NotFound; + } + + await connectToDatabase(); + const result = await Role.delete({ + organization: { id: organizationId }, + id + }); + return { + statusCode: 200, + body: JSON.stringify(result) + }; +}); + +/** + * @swagger + * + * /organizations/regionId/{regionId}: + * get: + * description: List organizations with specific regionId. + * parameters: + * - in: path + * name: regionId + * description: Organization regionId + * tags: + * - Organizations + */ +export const getByRegionId = wrapHandler(async (event) => { + if (!isRegionalAdmin(event)) return Unauthorized; + const regionId = event.pathParameters?.regionId; + await connectToDatabase(); + const result = await Organization.find({ + where: { regionId: regionId } + }); + + if (result) { + return { + statusCode: 200, + body: JSON.stringify(result) + }; + } + return NotFound; +}); + +/** + * @swagger + * + * /organizations/state/{state}: + * get: + * description: List organizations with specific state. + * parameters: + * - in: path + * name: state + * description: Organization state + * tags: + * - Organizations + */ +export const getByState = wrapHandler(async (event) => { + if (!isRegionalAdmin(event)) return Unauthorized; + const state = event.pathParameters?.state; + await connectToDatabase(); + const result = await Organization.find({ + where: { state: state } + }); + + if (result) { + return { + statusCode: 200, + body: JSON.stringify(result) + }; + } + return NotFound; +}); + +// V2 Endpoints + +/** + * @swagger + * + * /v2/organizations: + * get: + * description: List all organizations with query parameters. + * tags: + * - Users + * parameters: + * - in: query + * name: state + * required: false + * schema: + * type: array + * items: + * type: string + * - in: query + * name: regionId + * required: false + * schema: + * type: array + * items: + * type: string + * + */ +export const getAllV2 = wrapHandler(async (event) => { + if (!isRegionalAdmin(event)) return Unauthorized; + + const filterParams = {}; + + if (event.query && event.query.state) { + filterParams['state'] = event.query.state; + } + if (event.query && event.query.regionId) { + filterParams['regionId'] = event.query.regionId; + } + + await connectToDatabase(); + if (Object.entries(filterParams).length === 0) { + const result = await Organization.find({}); + return { + statusCode: 200, + body: JSON.stringify(result) + }; + } else { + const result = await Organization.find({ + where: filterParams + }); + return { + statusCode: 200, + body: JSON.stringify(result) + }; + } +}); + +/** + * @swagger + * + * /v2/organizations/{id}: + * put: + * description: Update a particular organization. + * parameters: + * - in: path + * name: id + * description: Organization id + * tags: + * - Organizations + */ + +export const updateV2 = wrapHandler(async (event) => { + if (!isRegionalAdmin(event)) return Unauthorized; + // Get the organization id from the path + const id = event.pathParameters?.organizationId; + + // confirm that the id is a valid UUID + if (!id || !isUUID(id)) { + return NotFound; + } + + // TODO: check permissions + // if (!isOrgAdmin(event, id)) return Unauthorized; + + // Validate the body + const validatedBody = await validateBody( + UpdateOrganizationMetaV2, + event.body + ); + + // Connect to the database + await connectToDatabase(); + + // Update the organization + const updateResp = await Organization.update(id, validatedBody); + + // Handle response + if (updateResp) { + const updatedOrg = await Organization.findOne(id); + return { + statusCode: 200, + body: JSON.stringify(updatedOrg) + }; + } + return NotFound; +}); diff --git a/backend/src/api/users.ts b/backend/src/api/users.ts index 821337f54..c82fb5aae 100644 --- a/backend/src/api/users.ts +++ b/backend/src/api/users.ts @@ -7,6 +7,7 @@ import { IsEnum, IsInt, IsIn, + IsNumber, IsObject, IsPositive, ValidateNested, @@ -18,18 +19,21 @@ import { wrapHandler, NotFound, Unauthorized, - sendEmail + sendEmail, + sendUserNotificationEmail } from './helpers'; import { UserType } from '../models/user'; import { getUserId, canAccessUser, isGlobalViewAdmin, + isRegionalAdmin, isOrgAdmin, isGlobalWriteAdmin } from './auth'; import { Type, plainToClass } from 'class-transformer'; import { IsNull } from 'typeorm'; +import { create } from './organizations'; class UserSearch { @IsInt() @@ -46,7 +50,9 @@ class UserSearch { 'userType', 'dateAcceptedTerms', 'lastLoggedIn', - 'acceptedTermsVersion' + 'acceptedTermsVersion', + 'state', + 'regionId' ]) @IsOptional() sort: string = 'fullName'; @@ -74,6 +80,127 @@ class UserSearch { } } +// New User +class NewUser { + @IsString() + firstName: string; + + @IsString() + lastName: string; + + @IsString() + @IsOptional() + state: string; + + @IsString() + @IsOptional() + regionId: string; + + @IsEmail() + @IsOptional() + email: string; + + @IsString() + @IsOptional() + organization: string; + + @IsBoolean() + @IsOptional() + organizationAdmin: string; + + @IsEnum(UserType) + @IsOptional() + userType: UserType; +} + +class UpdateUser { + @IsString() + @IsOptional() + state: string; + + @IsString() + @IsOptional() + regionId: string; + + @IsBoolean() + @IsOptional() + invitePending: boolean; + + @IsEnum(UserType) + @IsOptional() + userType: UserType; + + // @IsString() + @IsEnum(Organization) + @IsOptional() + organization: Organization; + + @IsString() + @IsOptional() + role: string; +} + +const REGION_STATE_MAP = { + Connecticut: '1', + Maine: '1', + Massachusetts: '1', + 'New Hampshire': '1', + 'Rhode Island': '1', + Vermont: '1', + 'New Jersey': '2', + 'New York': '2', + 'Puerto Rico': '2', + 'Virgin Islands': '2', + Delaware: '3', + Maryland: '3', + Pennsylvania: '3', + Virginia: '3', + 'District of Columbia': '3', + 'West Virginia': '3', + Alabama: '4', + Florida: '4', + Georgia: '4', + Kentucky: '4', + Mississippi: '4', + 'North Carolina': '4', + 'South Carolina': '4', + Tennessee: '4', + Illinois: '5', + Indiana: '5', + Michigan: '5', + Minnesota: '5', + Ohio: '5', + Wisconsin: '5', + Arkansas: '6', + Louisiana: '6', + 'New Mexico': '6', + Oklahoma: '6', + Texas: '6', + Iowa: '7', + Kansas: '7', + Missouri: '7', + Nebraska: '7', + Colorado: '8', + Montana: '8', + 'North Dakota': '8', + 'South Dakota': '8', + Utah: '8', + Wyoming: '8', + Arizona: '9', + California: '9', + Hawaii: '9', + Nevada: '9', + Guam: '9', + 'American Samoa': '9', + 'Commonwealth Northern Mariana Islands': '9', + 'Republic of Marshall Islands': '9', + 'Federal States of Micronesia': '9', + Alaska: '10', + Idaho: '10', + Oregon: '10', + Washington: '10' +}; + /** * @swagger * @@ -150,30 +277,6 @@ export const update = wrapHandler(async (event) => { return NotFound; }); -class NewUser { - @IsString() - firstName: string; - - @IsString() - lastName: string; - - @IsEmail() - @IsOptional() - email: string; - - @IsString() - @IsOptional() - organization: string; - - @IsBoolean() - @IsOptional() - organizationAdmin: string; - - @IsEnum(UserType) - @IsOptional() - userType: UserType; -} - const sendInviteEmail = async (email: string, organization?: Organization) => { const staging = process.env.NODE_ENV !== 'production'; @@ -353,6 +456,7 @@ export const acceptTerms = wrapHandler(async (event) => { * description: List users. * tags: * - Users + * */ export const list = wrapHandler(async (event) => { if (!isGlobalViewAdmin(event)) return Unauthorized; @@ -385,3 +489,454 @@ export const search = wrapHandler(async (event) => { body: JSON.stringify({ result, count }) }; }); + +/** + * @swagger + * + * /users/regionId/{regionId}: + * get: + * description: List users with specific regionId. + * parameters: + * - in: path + * name: regionId + * description: User regionId + * tags: + * - Users + */ +export const getByRegionId = wrapHandler(async (event) => { + if (!isRegionalAdmin(event)) return Unauthorized; + const regionId = event.pathParameters?.regionId; + await connectToDatabase(); + const result = await User.find({ + where: { regionId: regionId }, + relations: ['roles', 'roles.organization'] + }); + if (result) { + return { + statusCode: 200, + body: JSON.stringify(result) + }; + } + return NotFound; +}); + +/** + * @swagger + * + * /users/state/{state}: + * get: + * description: List users with specific state. + * parameters: + * - in: path + * name: state + * description: User state + * tags: + * - Users + */ +export const getByState = wrapHandler(async (event) => { + if (!isRegionalAdmin(event)) return Unauthorized; + const state = event.pathParameters?.state; + await connectToDatabase(); + const result = await User.find({ + where: { state: state }, + relations: ['roles', 'roles.organization'] + }); + if (result) { + return { + statusCode: 200, + body: JSON.stringify(result) + }; + } + return NotFound; +}); + +/** + * @swagger + * + * /users/register: + * post: + * description: New user registration. + * tags: + * - Users + */ +export const register = wrapHandler(async (event) => { + const body = await validateBody(NewUser, event.body); + const newUser = { + firstName: body.firstName, + lastName: body.lastName, + email: body.email.toLowerCase(), + userType: UserType.STANDARD, + state: body.state, + regionId: REGION_STATE_MAP[body.state], + invitePending: true + }; + console.log(JSON.stringify(newUser)); + + await connectToDatabase(); + + // Check if user already exists + const userCheck = await User.findOne({ + where: { email: newUser.email } + }); + + let id = ''; + // Create if user does not exist + // if (!user) { + if (userCheck) { + console.log('User already exists.'); + return { + statusCode: 422, + body: 'User email already exists. Registration failed.' + }; + } + + const createdUser = await User.create(newUser); + await User.save(createdUser); + id = createdUser.id; + + // const savedUser = await User.save(createdUser); + // id = createdUser.id; + + // Send Registration confirmation email to user + // TODO: replace with html email function to user + sendUserNotificationEmail( + newUser.email, + 'Crossfeed Registration Pending', + newUser.firstName, + newUser.lastName, + '/app/src/email_templates/crossfeed_registration_notification.html' + ); + // Send new user pending approval email to regionalAdmin + // TODO: replace with html email function to regianlAdmin + const savedUser = await User.findOne(id, { + relations: ['roles', 'roles.organization'] + }); + + return { + statusCode: 200, + body: JSON.stringify(savedUser) + }; +}); + +/** + * @swagger + * + * /users/{id}/register/approve: + * put: + * description: Approve a particular users registration. + * parameters: + * - in: path + * name: id + * description: User id + * tags: + * - Users + */ +export const registrationApproval = wrapHandler(async (event) => { + // Get the user id from the path + const userId = event.pathParameters?.userId; + + // Confirm that the id is a valid UUID + if (!userId || !isUUID(userId)) { + return NotFound; + } + + // TODO: add user registration approval logic + // Validate the body + // const body = await approvalBody(UpdateUser, event.body); + + // TODO: verify permissions + // User type permissions check + // if (!isRegionalAdmin(event)) return Unauthorized; + + // TODO: finalize validation + // // Validate the body + // const validatedBody = await validateBody( + // UpdateUser, + // event.body + // ); + + // Connect to the database + await connectToDatabase(); + + const user = await User.findOne(userId); + if (!user) { + return NotFound; + } + + // Send email notification + sendUserNotificationEmail( + user.email, + 'Crossfeed Registration Approved', + user.firstName, + user.lastName, + '/app/src/email_templates/crossfeed_approval_notification.html' + ); + + // TODO: Handle Response Output + return { + statusCode: 200, + body: 'User registration approved.' + }; +}); + +/** + * @swagger + * + * /users/{id}/register/deny: + * put: + * description: Deny a particular users registration. + * parameters: + * - in: path + * name: id + * description: User id + * tags: + * - Users + */ +export const registrationDenial = wrapHandler(async (event) => { + // Get the user id from the path + const userId = event.pathParameters?.userId; + + // Confirm that the id is a valid UUID + if (!userId || !isUUID(userId)) { + return NotFound; + } + + // TODO: add user registration denial logic + // Validate the body + // const body = await approvalBody(UpdateUser, event.body); + + // TODO: verify permissions + // User type permissions check + // if (!isRegionalAdmin(event)) return Unauthorized; + + // TODO: finalize validation + // // Validate the body + // const validatedBody = await validateBody( + // UpdateUser, + // event.body + // ); + + // TODO: Handle Email notificaitons + // Add email notification logic + + // TODO: Handle Response Output + return { + statusCode: 200, + body: 'User registration denied.' + }; +}); + +//***************// +// V2 Endpoints // +//***************// + +/** + * @swagger + * + * /v2/users: + * get: + * description: List all users with query parameters. + * tags: + * - Users + * parameters: + * - in: query + * name: state + * required: false + * schema: + * type: array + * items: + * type: string + * - in: query + * name: regionId + * required: false + * schema: + * type: array + * items: + * type: string + * - in: query + * name: invitePending + * required: false + * schema: + * type: array + * items: + * type: string + * + */ +export const getAllV2 = wrapHandler(async (event) => { + if (!isRegionalAdmin(event)) return Unauthorized; + + const filterParams = {}; + + if (event.query && event.query.state) { + filterParams['state'] = event.query.state; + } + if (event.query && event.query.regionId) { + filterParams['regionId'] = event.query.regionId; + } + if (event.query && event.query.invitePending) { + filterParams['invitePending'] = event.query.invitePending; + } + + await connectToDatabase(); + if (Object.entries(filterParams).length === 0) { + const result = await User.find({ + relations: ['roles', 'roles.organization'] + }); + return { + statusCode: 200, + body: JSON.stringify(result) + }; + } else { + const result = await User.find({ + where: filterParams, + relations: ['roles', 'roles.organization'] + }); + return { + statusCode: 200, + body: JSON.stringify(result) + }; + } +}); + +/** + * @swagger + * + * /v2/users: + * post: + * description: Create a new user. + * tags: + * - Users + */ +export const inviteV2 = wrapHandler(async (event) => { + const body = await validateBody(NewUser, event.body); + // Invoker must be either an organization or global admin + if (body.organization) { + if (!isOrgAdmin(event, body.organization)) return Unauthorized; + } else { + if (!isGlobalWriteAdmin(event)) return Unauthorized; + } + if (!isGlobalWriteAdmin(event) && body.userType) { + // Non-global admins can't set userType + return Unauthorized; + } + + await connectToDatabase(); + + body.email = body.email.toLowerCase(); + + // Check if user already exists + let user = await User.findOne({ + email: body.email + }); + + let organization: Organization | undefined; + + if (body.organization) { + organization = await Organization.findOne(body.organization); + } + + if (!user) { + user = await User.create({ + invitePending: true, + ...body + }); + await User.save(user); + await sendInviteEmail(user.email, organization); + } else if (!user.firstName && !user.lastName) { + // Only set the user first name and last name the first time the user is invited. + user.firstName = body.firstName; + user.lastName = body.lastName; + await User.save(user); + } + + // Always update the userType, if specified in the request. + if (body.userType) { + user.userType = body.userType; + await User.save(user); + } + + if (organization) { + // Create approved role if organization supplied + await Role.createQueryBuilder() + .insert() + .values({ + user: user, + organization: { id: body.organization }, + approved: true, + createdBy: { id: event.requestContext.authorizer!.id }, + approvedBy: { id: event.requestContext.authorizer!.id }, + role: body.organizationAdmin ? 'admin' : 'user' + }) + .onConflict( + ` + ("userId", "organizationId") DO UPDATE + SET "role" = excluded."role", + "approved" = excluded."approved", + "approvedById" = excluded."approvedById" + ` + ) + .execute(); + } + + const updated = await User.findOne( + { + id: user.id + }, + { + relations: ['roles', 'roles.organization'] + } + ); + return { + statusCode: 200, + body: JSON.stringify(updated) + }; +}); + +/** + * @swagger + * + * /v2/users/{id}: + * put: + * description: Update a particular user. + * parameters: + * - in: path + * name: id + * description: User id + * tags: + * - Users + */ +export const updateV2 = wrapHandler(async (event) => { + // Get the user id from the path + const userId = event.pathParameters?.userId; + + // Confirm that the id is a valid UUID + if (!userId || !isUUID(userId)) { + return NotFound; + } + + // Validate the body + const body = await validateBody(UpdateUser, event.body); + + // Connect to the database + await connectToDatabase(); + + const user = await User.findOne(userId); + if (!user) { + return NotFound; + } + + // Update the user + const updatedResp = await User.update(userId, body); + + // Handle response + if (updatedResp) { + const updatedUser = await User.findOne(userId, { + relations: ['roles', 'roles.organization'] + }); + return { + statusCode: 200, + body: JSON.stringify(updatedUser) + }; + } + return NotFound; +}); diff --git a/backend/src/email_templates/email_templates/banner.png b/backend/src/email_templates/email_templates/banner.png new file mode 100644 index 000000000..33ebcd278 Binary files /dev/null and b/backend/src/email_templates/email_templates/banner.png differ diff --git a/backend/src/email_templates/email_templates/crossfeed_approval_notification.html b/backend/src/email_templates/email_templates/crossfeed_approval_notification.html new file mode 100644 index 000000000..0d9ee4cbb --- /dev/null +++ b/backend/src/email_templates/email_templates/crossfeed_approval_notification.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

CROSSFEED USER ACCOUNT APPROVAL

+
+

{{firstname}} {{lastname}},

+
+

Your request for a Crossfeed account was approved by your Regional Manager.

+
+

Thanks,

+

The Crossfeed Team

+

+

CISA | Defend Today | Secure Tomorrow

+
+ + + + + + + + + + + + + + + +

cisa.gov

vulnerability@cisa.dhs.gov

Linkedin.com/company/cisagov

@CISAgov | @cyber | @uscert_gov

Facebook.com/CISA

@cisagov

+ + \ No newline at end of file diff --git a/backend/src/email_templates/email_templates/crossfeed_denial_notification.html b/backend/src/email_templates/email_templates/crossfeed_denial_notification.html new file mode 100644 index 000000000..8f22d05af --- /dev/null +++ b/backend/src/email_templates/email_templates/crossfeed_denial_notification.html @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

CROSSFEED USER ACCOUNT APPROVAL

+
+

{{firstname}} {{lastname}},

+
+

Your request for a Crossfeed account was denied by your Regional Manager.

+
+

Thanks,

+

The Crossfeed Team

+

+

CISA | Defend Today | Secure Tomorrow

+
+ + + + + + + + + + + + + + + +

cisa.gov

vulnerability@cisa.dhs.gov

Linkedin.com/company/cisagov

@CISAgov | @cyber | @uscert_gov

Facebook.com/CISA

@cisagov

+ + \ No newline at end of file diff --git a/backend/src/email_templates/email_templates/crossfeed_registration_notification.html b/backend/src/email_templates/email_templates/crossfeed_registration_notification.html new file mode 100644 index 000000000..95339c4b4 --- /dev/null +++ b/backend/src/email_templates/email_templates/crossfeed_registration_notification.html @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

CROSSFEED USER ACCOUNT CREATION

+
+

{{firstname}} {{lastname}},

+
+

Your request to create an account with Crossfeed has been received. After approval from your Regional Manager, you will receive another email with your approval status and further steps.

+
+

Thanks,

+

The Crossfeed Team

+

+

CISA | Defend Today | Secure Tomorrow

+
+
+ + + + + + + + + + + + + + + +

cisa.gov

vulnerability@cisa.dhs.gov

Linkedin.com/company/cisagov

@CISAgov | @cyber | @uscert_gov

Facebook.com/CISA

@cisagov

+ + \ No newline at end of file diff --git a/backend/src/email_templates/email_templates/email.png b/backend/src/email_templates/email_templates/email.png new file mode 100644 index 000000000..55fa88860 Binary files /dev/null and b/backend/src/email_templates/email_templates/email.png differ diff --git a/backend/src/email_templates/email_templates/facebook.png b/backend/src/email_templates/email_templates/facebook.png new file mode 100644 index 000000000..220681df7 Binary files /dev/null and b/backend/src/email_templates/email_templates/facebook.png differ diff --git a/backend/src/email_templates/email_templates/instagram.png b/backend/src/email_templates/email_templates/instagram.png new file mode 100644 index 000000000..f5004fd0a Binary files /dev/null and b/backend/src/email_templates/email_templates/instagram.png differ diff --git a/backend/src/email_templates/email_templates/linkedin.png b/backend/src/email_templates/email_templates/linkedin.png new file mode 100644 index 000000000..cb1dc321d Binary files /dev/null and b/backend/src/email_templates/email_templates/linkedin.png differ diff --git a/backend/src/email_templates/email_templates/twitter.png b/backend/src/email_templates/email_templates/twitter.png new file mode 100644 index 000000000..50eaf9764 Binary files /dev/null and b/backend/src/email_templates/email_templates/twitter.png differ diff --git a/backend/src/email_templates/email_templates/web.png b/backend/src/email_templates/email_templates/web.png new file mode 100644 index 000000000..2f46bc154 Binary files /dev/null and b/backend/src/email_templates/email_templates/web.png differ diff --git a/backend/src/models/organization.ts b/backend/src/models/organization.ts index 33a80068c..1bff2ed2b 100644 --- a/backend/src/models/organization.ts +++ b/backend/src/models/organization.ts @@ -111,4 +111,49 @@ export class Organization extends BaseEntity { onUpdate: 'CASCADE' }) createdBy: User; + + @Column({ + nullable: true + }) + country: string; + + @Column({ + nullable: true + }) + state: string; + + @Column({ + nullable: true + }) + regionId: string; + + @Column({ + nullable: true + }) + stateFips: number; + + @Column({ + nullable: true + }) + stateName: string; + + @Column({ + nullable: true + }) + county: string; + + @Column({ + nullable: true + }) + countyFips: number; + + @Column({ + nullable: true + }) + acronym: string; + + @Column({ + nullable: true + }) + type: string; } diff --git a/backend/src/models/role.ts b/backend/src/models/role.ts index d3176608a..666eb09de 100644 --- a/backend/src/models/role.ts +++ b/backend/src/models/role.ts @@ -50,6 +50,12 @@ export class Role extends BaseEntity { }) user: User; + // @ManyToOne((type) => User, (user) => user.organizations, { + // onDelete: 'CASCADE', + // onUpdate: 'CASCADE' + // }) + // userOrgs: User; + @ManyToOne((type) => Organization, (organization) => organization.userRoles, { onDelete: 'CASCADE', onUpdate: 'CASCADE' diff --git a/backend/src/models/user.ts b/backend/src/models/user.ts index b83f513ad..8a9cdc5fb 100644 --- a/backend/src/models/user.ts +++ b/backend/src/models/user.ts @@ -10,13 +10,14 @@ import { PrimaryGeneratedColumn, BeforeUpdate } from 'typeorm'; -import { Role } from './'; +import { Organization, Role } from './'; import { ApiKey } from './api-key'; export enum UserType { STANDARD = 'standard', GLOBAL_VIEW = 'globalView', - GLOBAL_ADMIN = 'globalAdmin' + GLOBAL_ADMIN = 'globalAdmin', + REGIONAL_ADMIN = 'regionalAdmin' } @Entity() export class User extends BaseEntity { @@ -104,4 +105,26 @@ export class User extends BaseEntity { setFullName() { this.fullName = this.firstName + ' ' + this.lastName; } + + @Column({ + nullable: true + }) + regionId: string; + + @Column({ + nullable: true + }) + state: string; + + // @Column({ + // nullable: true, + // default: 0 + // }) + // numberOfOrganizations: number; + + // @Column({ + // nullable: true, + // default: [] + // }) + // organizationIds: Array; } diff --git a/backend/src/tasks/s3-client.ts b/backend/src/tasks/s3-client.ts index fa4f03e28..923713c89 100644 --- a/backend/src/tasks/s3-client.ts +++ b/backend/src/tasks/s3-client.ts @@ -74,6 +74,7 @@ class S3Client { throw e; } } + async listReports(orgId: string) { try { const params = { @@ -93,6 +94,27 @@ class S3Client { throw e; } } + + async getEmailAsset(fileName: string) { + try { + const params = { + Bucket: process.env.EMAIL_BUCKET_NAME!, + Key: fileName + }; + + const data = await this.s3 + .getObject(params, function (err, data) { + if (err) throw err; + }) + .promise(); + if (data && data.Body) { + return data.Body.toString('utf-8'); + } + } catch (e) { + console.error(e); + throw e; + } + } } export default S3Client; diff --git a/backend/src/tasks/scanExecution.ts b/backend/src/tasks/scanExecution.ts index 5bff875c8..cb4fad528 100644 --- a/backend/src/tasks/scanExecution.ts +++ b/backend/src/tasks/scanExecution.ts @@ -29,6 +29,24 @@ export const handler: Handler = async (event) => { } else { console.log('Shodan is the only script type available right now.'); } + + // After processing each message, check if the SQS queue is empty + const sqsAttributes = await sqs + .getQueueAttributes({ + QueueUrl: process.env.SHODAN_QUEUE_URL!, + AttributeNames: ['ApproximateNumberOfMessages'] + }) + .promise(); + + const approximateNumberOfMessages = parseInt( + sqsAttributes.Attributes?.ApproximateNumberOfMessages || '0', + 10 + ); + + // If the queue is empty, scale down to zero tasks + if (approximateNumberOfMessages === 0) { + await startFargateTask(clusterName, process.env.SHODAN_SERVICE_NAME!, 0); + } } catch (error) { console.error(error); return { @@ -59,7 +77,7 @@ export async function startFargateTask( const service = serviceDescription.services[0]; // Check if the desired task count is less than # provided - if (service.desiredCount! < desiredCountNum) { + if (service.desiredCount! !== desiredCountNum) { const updateServiceParams = { cluster: clusterName, service: serviceName, diff --git a/backend/src/tasks/syncdb.ts b/backend/src/tasks/syncdb.ts index 4420065f4..4d14cd977 100644 --- a/backend/src/tasks/syncdb.ts +++ b/backend/src/tasks/syncdb.ts @@ -24,6 +24,8 @@ const NUM_SAMPLE_ORGS = 10; // Number of sample orgs const NUM_SAMPLE_DOMAINS = 10; // Number of sample domains per org const PROB_SAMPLE_SERVICES = 0.5; // Higher number means more services per domain const PROB_SAMPLE_VULNERABILITIES = 0.5; // Higher number means more vulnerabilities per domain +const SAMPLE_STATES = ['VA', 'CA', 'CO']; +const SAMPLE_REGIONIDS = ['1', '2', '3']; export const handler: Handler = async (event) => { const connection = await connectToDatabase(false); @@ -78,7 +80,10 @@ export const handler: Handler = async (event) => { rootDomains: ['crossfeed.local'], ipBlocks: [], isPassive: false, - tags: [tag] + tags: [tag], + state: SAMPLE_STATES[Math.floor(Math.random() * SAMPLE_STATES.length)], + regionId: + SAMPLE_REGIONIDS[Math.floor(Math.random() * SAMPLE_REGIONIDS.length)] }).save(); console.log(organization.name); organizationIds.push(organization.id); diff --git a/backend/test/scan-tasks.test.ts b/backend/test/scan-tasks.test.ts index 31a0b3846..72e8995c9 100644 --- a/backend/test/scan-tasks.test.ts +++ b/backend/test/scan-tasks.test.ts @@ -232,7 +232,9 @@ describe('domains', () => { .get(`/scan-tasks/${scanTask.id}/logs`) .set('Authorization', createUserToken({})) .expect(403); - expect(response.text).toEqual(''); + expect(response.text).toEqual( + 'Unauthorized access. View logs for details.' + ); expect(getLogs).not.toHaveBeenCalled(); }); }); diff --git a/backend/worker/generate_config.sh b/backend/worker/generate_config.sh new file mode 100755 index 000000000..3791d5f24 --- /dev/null +++ b/backend/worker/generate_config.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Generate database.ini +cat < pe-reports/src/pe_reports/data/database.ini +[postgres] +host=$(DB_HOST) +database=$(PE_DB_NAME) +user=$(PE_DB_USERNAME) +password=$(PE_DB_PASSWORD) +port=5432 + +[shodan] +key1=$(PE_SHODAN_API_KEYS) + +[staging] +[cyhy_mongo] +[sixgill] +[whoisxml] +key= +[intelx] +[dnsmonitor] +[pe_db_password_key] +[blocklist] +[dehashed] +[dnstwist] +[hibp] +[API_Client_ID] +[API_Client_secret] +[API_WHOIS] +[pe_api] + +EOF + +# Find the path to the pe_reports package in site-packages +pe_reports_path=$(pip show pe-reports | grep -E '^Location:' | awk '{print $2}') + +# Ensure pe_reports_path ends with /pe_reports +pe_reports_path="${pe_reports_path%/pe-reports}/pe_reports" + + +# Copy database.ini to the module's installation directory +cp /app/pe-reports/src/pe_reports/data/database.ini "${pe_reports_path}/data/" + +exec "$@" \ No newline at end of file diff --git a/backend/worker/pe-worker-entry.sh b/backend/worker/pe-worker-entry.sh old mode 100644 new mode 100755 index a50779725..c5392092d --- a/backend/worker/pe-worker-entry.sh +++ b/backend/worker/pe-worker-entry.sh @@ -1,7 +1,9 @@ -#!/bin/sh +#!/bin/bash set -e +echo "Starting pe-worker-entry.sh script" + # Check if the SHODAN_QUEUE_URL environment variable is set if [ -z "$SHODAN_QUEUE_URL" ]; then echo "SHODAN_QUEUE_URL environment variable is not set. Exiting." diff --git a/build.sh b/build.sh new file mode 100755 index 000000000..c8e87c312 --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +docker-compose down --volumes --rmi all +cd backend && npm run build-worker && cd .. && npm start +cd backend && npm run syncdb && npm run syncdb -- -d populate \ No newline at end of file diff --git a/dev.env.example b/dev.env.example index 4443cf737..6e02b6096 100644 --- a/dev.env.example +++ b/dev.env.example @@ -77,4 +77,6 @@ EXPORT_BUCKET_NAME=crossfeed-local-exports REPORTS_BUCKET_NAME=crossfeed-local-reports +EMAIL_BUCKET_NAME=cisa-crossfeed-staging-html-email + IS_LOCAL=1 diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4c78f9be6..952ca70b8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -18,12 +18,13 @@ "@mui/lab": "^5.0.0-alpha.135", "@mui/material": "^5.14.2", "@mui/system": "^5.14.0", + "@mui/x-data-grid": "^6.18.0", "@nivo/bar": "^0.80.0", "@nivo/core": "^0.80.0", "@nivo/pie": "^0.80.0", "@trussworks/react-uswds": "^5.1.1", "autoprefixer": "^10.4.13", - "aws-amplify": "^5.0.4", + "aws-amplify": "^5.3.12", "classnames": "^2.3.2", "d3-scale": "^4.0.2", "date-fns": "^2.29.3", @@ -140,12 +141,12 @@ } }, "node_modules/@aws-amplify/analytics": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/@aws-amplify/analytics/-/analytics-6.5.4.tgz", - "integrity": "sha512-MwHRGshhgw7BOZW+amdaNdCjJP8cp2GWL4V0uFgdWdkbe6p7ONN4cfoAnWmv9EIPpg412mWTk1iWZck2dT4q5A==", + "version": "6.5.6", + "resolved": "https://registry.npmjs.org/@aws-amplify/analytics/-/analytics-6.5.6.tgz", + "integrity": "sha512-RsvsH5BY3geRg0eU62ZX6jPcIcrEqW2VOMrRAj2wwm1csoa7J5ww72pjuFRCsyxL/MelMaIOJjqozTKaNZhK1Q==", "dependencies": { - "@aws-amplify/cache": "5.1.10", - "@aws-amplify/core": "5.8.4", + "@aws-amplify/cache": "5.1.12", + "@aws-amplify/core": "5.8.6", "@aws-sdk/client-firehose": "3.6.1", "@aws-sdk/client-kinesis": "3.6.1", "@aws-sdk/client-personalize-events": "3.6.1", @@ -161,25 +162,25 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-amplify/api": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/@aws-amplify/api/-/api-5.4.4.tgz", - "integrity": "sha512-Q7191OOkfT9SvJff85xQUscG3CkzEEzcWCShcNrnovocy46IgxIlsviMuHh9smpasv+2RqPovYE4LnVIC1h79A==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/@aws-amplify/api/-/api-5.4.6.tgz", + "integrity": "sha512-Wn9kQJ/uf0CYW1zoBueaCsoF5v3wJZpMxkpdEBYwD10GqhSt5baoghq2oW72JyDrGS8Hfq71o4a2vpXiIL/8hg==", "dependencies": { - "@aws-amplify/api-graphql": "3.4.10", - "@aws-amplify/api-rest": "3.5.4", + "@aws-amplify/api-graphql": "3.4.12", + "@aws-amplify/api-rest": "3.5.6", "tslib": "^1.8.0" } }, "node_modules/@aws-amplify/api-graphql": { - "version": "3.4.10", - "resolved": "https://registry.npmjs.org/@aws-amplify/api-graphql/-/api-graphql-3.4.10.tgz", - "integrity": "sha512-v4D6x+632N776pnAiIO312J91OFtt5wSHXk30GBQ99s0c4HDmFOyYpZFTQaAmrp24XtjyPLb3RHoEAc15vIwsA==", - "dependencies": { - "@aws-amplify/api-rest": "3.5.4", - "@aws-amplify/auth": "5.6.4", - "@aws-amplify/cache": "5.1.10", - "@aws-amplify/core": "5.8.4", - "@aws-amplify/pubsub": "5.5.4", + "version": "3.4.12", + "resolved": "https://registry.npmjs.org/@aws-amplify/api-graphql/-/api-graphql-3.4.12.tgz", + "integrity": "sha512-lEKXl0hM7nyulCRUQNIQ6I0+DCD16hR+V4XUdMmVgnlVnPVows5H17wM/Hu24lX84G1jFi/i14umH8+qvpk1fQ==", + "dependencies": { + "@aws-amplify/api-rest": "3.5.6", + "@aws-amplify/auth": "5.6.6", + "@aws-amplify/cache": "5.1.12", + "@aws-amplify/core": "5.8.6", + "@aws-amplify/pubsub": "5.5.6", "graphql": "15.8.0", "tslib": "^1.8.0", "uuid": "^3.2.1", @@ -192,12 +193,12 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-amplify/api-rest": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@aws-amplify/api-rest/-/api-rest-3.5.4.tgz", - "integrity": "sha512-JY0j2y8bqqIgRxoKQcKV8BDIRh4bYD1WKXYEBzrZly3rPaXXmQfe1UJF6QRFv7m9tqelAo1r/wvWuINM7iaEnA==", + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/@aws-amplify/api-rest/-/api-rest-3.5.6.tgz", + "integrity": "sha512-WAv7q05G2ypE59ebVMvIih5aTIi+jOTu+ApkEI/E5mNM2Lj33TnaOrM5Vt2oja0aHT37AQHvIF1gYYf6gF1iRQ==", "dependencies": { - "@aws-amplify/core": "5.8.4", - "axios": "0.26.0", + "@aws-amplify/core": "5.8.6", + "axios": "1.6.0", "tslib": "^1.8.0", "url": "0.11.0" } @@ -213,12 +214,12 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-amplify/auth": { - "version": "5.6.4", - "resolved": "https://registry.npmjs.org/@aws-amplify/auth/-/auth-5.6.4.tgz", - "integrity": "sha512-6b+ojQpwkrzWnyTVTsYtpPPSUo/B+F9bj4oS8bo1DXf40/3XnCeEZQehME2a7fDolVU24E1+pWEzVRqsadLSeQ==", + "version": "5.6.6", + "resolved": "https://registry.npmjs.org/@aws-amplify/auth/-/auth-5.6.6.tgz", + "integrity": "sha512-v4qiOTW7dTObEoHPjQMl0diL+E7KKk6HrKd1O+roE9f93U1ZDbcIkeY5Yih+I/U8VbS+hQ5D9oHyFbeLZWsDcg==", "dependencies": { - "@aws-amplify/core": "5.8.4", - "amazon-cognito-identity-js": "6.3.5", + "@aws-amplify/core": "5.8.6", + "amazon-cognito-identity-js": "6.3.7", "buffer": "4.9.2", "tslib": "^1.8.0", "url": "0.11.0" @@ -230,11 +231,11 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-amplify/cache": { - "version": "5.1.10", - "resolved": "https://registry.npmjs.org/@aws-amplify/cache/-/cache-5.1.10.tgz", - "integrity": "sha512-4svwr6CEhOwBf2PlUcE6Tl6HI5qL1gJa7X4vhOyYMBfsDaQzPbP4QxnvGMWM8O9OCdAIBCIWCdJPCMvDSYpOwQ==", + "version": "5.1.12", + "resolved": "https://registry.npmjs.org/@aws-amplify/cache/-/cache-5.1.12.tgz", + "integrity": "sha512-bOhkqABOUehPCWy5x5XHMHfJtuQ7tBOGD7gAhDo6leY1O7NIHjDBto+U6FZmCE2bS8u/QcgJ94PRZ1E5/3rsng==", "dependencies": { - "@aws-amplify/core": "5.8.4", + "@aws-amplify/core": "5.8.6", "tslib": "^1.8.0" } }, @@ -244,9 +245,9 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-amplify/core": { - "version": "5.8.4", - "resolved": "https://registry.npmjs.org/@aws-amplify/core/-/core-5.8.4.tgz", - "integrity": "sha512-xFLciGRhRGSzLNqQoS/rFA2PAhggVvh9AFljlAuPyKykmFhxhGKx/k8a/q3GBcF8HiBAlJ6jpNz5X8RONr9Nkw==", + "version": "5.8.6", + "resolved": "https://registry.npmjs.org/@aws-amplify/core/-/core-5.8.6.tgz", + "integrity": "sha512-fDu4aduCagJb8uwQd+S0FBhuYgCgnpwfRsmO0/t01h68JR+KS3Cny0pQa7Qzk6cW/HY3rtPLELOqbdF6doA6oA==", "dependencies": { "@aws-crypto/sha256-js": "1.2.2", "@aws-sdk/client-cloudwatch-logs": "3.6.1", @@ -288,15 +289,15 @@ } }, "node_modules/@aws-amplify/datastore": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/@aws-amplify/datastore/-/datastore-4.7.4.tgz", - "integrity": "sha512-OhKASdljD94VQ1/15IanIJH9o1UexgsvjpJ/ybQKAL6xng4E9Ht0DK2D0ApU/5zNY+gN9mUKlQ3JZOdjBHSxnw==", - "dependencies": { - "@aws-amplify/api": "5.4.4", - "@aws-amplify/auth": "5.6.4", - "@aws-amplify/core": "5.8.4", - "@aws-amplify/pubsub": "5.5.4", - "amazon-cognito-identity-js": "6.3.5", + "version": "4.7.6", + "resolved": "https://registry.npmjs.org/@aws-amplify/datastore/-/datastore-4.7.6.tgz", + "integrity": "sha512-vuozosYTmWUgYzR9tqBaP1CyusPQG9/caDeqQkHCDogqJRHIkWG9INon/EUZQu4bR7W5/V8y1Dv2bLqUJSUWZA==", + "dependencies": { + "@aws-amplify/api": "5.4.6", + "@aws-amplify/auth": "5.6.6", + "@aws-amplify/core": "5.8.6", + "@aws-amplify/pubsub": "5.5.6", + "amazon-cognito-identity-js": "6.3.7", "buffer": "4.9.2", "idb": "5.0.6", "immer": "9.0.6", @@ -307,11 +308,11 @@ } }, "node_modules/@aws-amplify/geo": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@aws-amplify/geo/-/geo-2.3.4.tgz", - "integrity": "sha512-EW5KQA+YFK1k00vG13kF+ADiEw+eNgvrxsn4N2dOY1VcpK9cnNXSX1ZOTXGnfdtoI+B7D6LY5veigpQRvFIqxQ==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@aws-amplify/geo/-/geo-2.3.6.tgz", + "integrity": "sha512-Zs6bnMCfCA7Sof++tzM6kUYW5pnohmqUlchivTzSIH2n0spQypxRbgkFsMIuCBNBctga0p04VCSlE4cjG1FHSQ==", "dependencies": { - "@aws-amplify/core": "5.8.4", + "@aws-amplify/core": "5.8.6", "@aws-sdk/client-location": "3.186.3", "@turf/boolean-clockwise": "6.5.0", "camelcase-keys": "6.2.2", @@ -324,11 +325,11 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-amplify/interactions": { - "version": "5.2.10", - "resolved": "https://registry.npmjs.org/@aws-amplify/interactions/-/interactions-5.2.10.tgz", - "integrity": "sha512-T/eM2155TQXk+zviCp1ytWv5PRcZ2apXapTISbGQ2XONPv8PH75RdOWG8FO6ZGfj6vwj8IKXa1ZY+LiXDlemgQ==", + "version": "5.2.12", + "resolved": "https://registry.npmjs.org/@aws-amplify/interactions/-/interactions-5.2.12.tgz", + "integrity": "sha512-T81ioWmxKpjFbgH2nUW/Fm5Saysjt2IGFrY2vfrYJ5ROaNxu52tMMP801tegCtV+pSRFsXBFV+Kex8o/dP7EqA==", "dependencies": { - "@aws-amplify/core": "5.8.4", + "@aws-amplify/core": "5.8.6", "@aws-sdk/client-lex-runtime-service": "3.186.3", "@aws-sdk/client-lex-runtime-v2": "3.186.3", "base-64": "1.0.0", @@ -343,24 +344,24 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-amplify/notifications": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/@aws-amplify/notifications/-/notifications-1.6.4.tgz", - "integrity": "sha512-W87rF235FeGmJ539vDOAC2lO9CElB59VWlyw2ixPVLRww6wwcYj3Q+Ub4LxnQdj2C3WDkDF0JkyHXcXr22GOqQ==", + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@aws-amplify/notifications/-/notifications-1.6.6.tgz", + "integrity": "sha512-GLTJZ88xafzE3A3s7KFJtteYv05qSc0SiWgS3DWclsehqk40g9/M4G7sQ8BJq+hFMfVh1D1utNOz1LNc7Qve4g==", "dependencies": { - "@aws-amplify/cache": "5.1.10", - "@aws-amplify/core": "5.8.4", - "@aws-amplify/rtn-push-notification": "1.1.6", + "@aws-amplify/cache": "5.1.12", + "@aws-amplify/core": "5.8.6", + "@aws-amplify/rtn-push-notification": "1.1.8", "lodash": "^4.17.21", "uuid": "^3.2.1" } }, "node_modules/@aws-amplify/predictions": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/@aws-amplify/predictions/-/predictions-5.5.4.tgz", - "integrity": "sha512-71J5QCa6lQmy62WRoXqIAsOt79LOarz/gl+ZIsfkzCY8BzHFigIirGmwJobNOZ108ghoYTmPsXvcYEvhnZwGFw==", + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@aws-amplify/predictions/-/predictions-5.5.6.tgz", + "integrity": "sha512-QF1Qc6mHEMKHIHDEOik1L/5stUDoi5C3g/bNN0uq7kyWPLfvQRbQU73KmrAdqcKeW+sKBwyhHejv0rFCSaMMGw==", "dependencies": { - "@aws-amplify/core": "5.8.4", - "@aws-amplify/storage": "5.9.4", + "@aws-amplify/core": "5.8.6", + "@aws-amplify/storage": "5.9.6", "@aws-sdk/client-comprehend": "3.6.1", "@aws-sdk/client-polly": "3.6.1", "@aws-sdk/client-rekognition": "3.6.1", @@ -379,13 +380,13 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-amplify/pubsub": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/@aws-amplify/pubsub/-/pubsub-5.5.4.tgz", - "integrity": "sha512-C8ommVQM7R/rj7ZHAr/c2FK9DFgejH54zwK1cm3ECI4DRrYGy51eqAsJJsvAEJosbyKpXZw1prjI9Qmj1cpFzw==", + "version": "5.5.6", + "resolved": "https://registry.npmjs.org/@aws-amplify/pubsub/-/pubsub-5.5.6.tgz", + "integrity": "sha512-IxM2hjGwLQm2ICFKyr2jdpLVje5Z/m0wsLD0laDfF441NkGwSP1AEpds1I5ozAJ18ETsnxRS/6731H+nSdLVgQ==", "dependencies": { - "@aws-amplify/auth": "5.6.4", - "@aws-amplify/cache": "5.1.10", - "@aws-amplify/core": "5.8.4", + "@aws-amplify/auth": "5.6.6", + "@aws-amplify/cache": "5.1.12", + "@aws-amplify/core": "5.8.6", "buffer": "4.9.2", "graphql": "15.8.0", "tslib": "^1.8.0", @@ -400,16 +401,16 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/@aws-amplify/rtn-push-notification": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@aws-amplify/rtn-push-notification/-/rtn-push-notification-1.1.6.tgz", - "integrity": "sha512-TKrnmERaVGAZqsQ/eiSB1sF7IGCmkpNzYqzwGJtnWBERhLs+a/391sVztNZ4A34x/HlYID3o1Q868051beJ85w==" + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@aws-amplify/rtn-push-notification/-/rtn-push-notification-1.1.8.tgz", + "integrity": "sha512-O0TnT0GVSMMLHXQ7BUh04RpNeXPQdSsw5xLtK+GyXCx3MlAZ6vfOPKKdomzzXfzxC2FUSyV1G4LlUVJAHmFP2A==" }, "node_modules/@aws-amplify/storage": { - "version": "5.9.4", - "resolved": "https://registry.npmjs.org/@aws-amplify/storage/-/storage-5.9.4.tgz", - "integrity": "sha512-W9ST5HekL9UXVRi1vByh3sWt83qe7Sft+BeQ0zbXBz8GAroDb8NhPlgnrqQz/6D3h344mzkNLZmjOrTmLp/+1g==", + "version": "5.9.6", + "resolved": "https://registry.npmjs.org/@aws-amplify/storage/-/storage-5.9.6.tgz", + "integrity": "sha512-PiSwsa3hRp8kfZ2EAK7Bd8mm0plJYtuQ39KgOQ1dalWcN4RCzfgxzVTPabTxHlq89lJWTkC03SNV/Yn2Zxj5JA==", "dependencies": { - "@aws-amplify/core": "5.8.4", + "@aws-amplify/core": "5.8.6", "@aws-sdk/md5-js": "3.6.1", "@aws-sdk/types": "3.6.1", "buffer": "4.9.2", @@ -537,11 +538,11 @@ } }, "node_modules/@aws-crypto/crc32/node_modules/@aws-sdk/types": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.398.0.tgz", - "integrity": "sha512-r44fkS+vsEgKCuEuTV+TIk0t0m5ZlXHNjSDYEUvzLStbbfUFiNus/YG4UCa0wOk9R7VuQI67badsvvPeVPCGDQ==", + "version": "3.449.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.449.0.tgz", + "integrity": "sha512-tSQPAvknheB6XnRoc+AuEgdzn2KhY447hddeVW0Mbg8Yl9es4u4TKVINloKDEyUrCKhB/1f93Hb5uJkPe/e/Ww==", "dependencies": { - "@smithy/types": "^2.2.2", + "@smithy/types": "^2.4.0", "tslib": "^2.5.0" }, "engines": { @@ -5647,21 +5648,21 @@ } }, "node_modules/@babel/core": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz", - "integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", + "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-compilation-targets": "^7.22.10", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.11", - "@babel/parser": "^7.22.11", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11", - "convert-source-map": "^1.7.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.3", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.3", + "@babel/types": "^7.23.3", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", @@ -5675,6 +5676,11 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" + }, "node_modules/@babel/eslint-parser": { "version": "7.22.11", "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.22.11.tgz", @@ -5701,11 +5707,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.3", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -5737,12 +5743,12 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", - "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", "dependencies": { "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", + "@babel/helper-validator-option": "^7.22.15", "browserslist": "^4.21.9", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -5752,14 +5758,14 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.11.tgz", - "integrity": "sha512-y1grdYL4WzmUDBRGK0pDbIoFd7UZKoDurDzWEoNMYoj1EL+foGRQNyPWDcC+YyegN5y1DUsFFmzjGijB3nSVAQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz", + "integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.15", "@babel/helper-optimise-call-expression": "^7.22.5", "@babel/helper-replace-supers": "^7.22.9", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", @@ -5836,37 +5842,37 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", - "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", + "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", "@babel/helper-simple-access": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" + "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { "node": ">=6.9.0" @@ -5976,9 +5982,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", "engines": { "node": ">=6.9.0" } @@ -5997,13 +6003,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz", - "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", + "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", "dependencies": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11" + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -6023,9 +6029,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", "bin": { "parser": "bin/babel-parser.js" }, @@ -6067,6 +6073,7 @@ "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", "peer": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", @@ -6115,13 +6122,13 @@ } }, "node_modules/@babel/plugin-proposal-export-default-from": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.22.5.tgz", - "integrity": "sha512-UCe1X/hplyv6A5g2WnQ90tnHRvYL29dabCWww92lO7VdfMVTVReBTRrhiMrKQejHD9oVkdnRdwYuzUZkBVQisg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.23.3.tgz", + "integrity": "sha512-Q23MpLZfSGZL1kU7fWqV262q65svLSCIP5kZ/JCW/rKTCm/FrLjpvEd2kfUYMVeHh4QhV/xzyoRAHWrAZJrE3Q==", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-default-from": "^7.22.5" + "@babel/plugin-syntax-export-default-from": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -6164,6 +6171,7 @@ "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", "peer": true, "dependencies": { "@babel/compat-data": "^7.20.5", @@ -6183,6 +6191,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.18.6", @@ -6317,9 +6326,9 @@ } }, "node_modules/@babel/plugin-syntax-export-default-from": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.22.5.tgz", - "integrity": "sha512-ODAqWWXB/yReh/jVQDag/3/tl6lgBueQkk/TcfW/59Oykm4c8a55XloX0CTk2k2VJiFWMgHby9xNX29IbCv9dQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.23.3.tgz", + "integrity": "sha512-KeENO5ck1IeZ/l2lFZNy+mpobV3D2Zy5C1YFnWm+YuY5mQiAWc4yAp13dqgguwsBsFVLh4LPCEqCa5qW13N+hw==", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -6343,9 +6352,9 @@ } }, "node_modules/@babel/plugin-syntax-flow": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz", - "integrity": "sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.23.3.tgz", + "integrity": "sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -6407,9 +6416,9 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -6515,9 +6524,9 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -6775,12 +6784,12 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.22.5.tgz", - "integrity": "sha512-tujNbZdxdG0/54g/oua8ISToaXTFBf8EnSb5PgQSciIXWOWKX3S4+JR7ZE9ol8FZwf9kxitzkGQ+QWeov/mCiA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.23.3.tgz", + "integrity": "sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-flow": "^7.22.5" + "@babel/plugin-syntax-flow": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -6893,11 +6902,11 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.11.tgz", - "integrity": "sha512-o2+bg7GDS60cJMgz9jWqRUsWkMzLCxp+jFDeDUT5sjRlAxcJWZ2ylNdI7QQ2+CH5hWu7OnN+Cv3htt7AkSf96g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", + "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", "dependencies": { - "@babel/helper-module-transforms": "^7.22.9", + "@babel/helper-module-transforms": "^7.23.3", "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-simple-access": "^7.22.5" }, @@ -7048,9 +7057,9 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.22.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.12.tgz", - "integrity": "sha512-7XXCVqZtyFWqjDsYDY4T45w4mlx1rf7aOgkc/Ww76xkgBiOlmjPkx36PBLHa1k1rwWvVgYMPsbuVnIamx2ZQJw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.3.tgz", + "integrity": "sha512-zvL8vIfIUgMccIAK1lxjvNv572JHFJIKb4MWBz5OGdBQA0fB0Xluix5rmOby48exiJc987neOmP/m9Fnpkz3Tg==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", @@ -7184,9 +7193,9 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz", - "integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", + "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -7199,9 +7208,9 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz", - "integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", + "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -7348,14 +7357,14 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.11.tgz", - "integrity": "sha512-0E4/L+7gfvHub7wsbTv03oRtD69X31LByy44fGmFzbZScpupFByMcgCJ0VbBTkzyjSJKuRoGN8tcijOWKTmqOA==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.3.tgz", + "integrity": "sha512-ogV0yWnq38CFwH20l2Afz0dfKuZBx9o/Y2Rmh5vuSS0YD1hswgEgTfyTzuSrT2q9btmHRSqYoSfwFUVaC1M1Jw==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.11", + "@babel/helper-create-class-features-plugin": "^7.22.15", "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.22.5" + "@babel/plugin-syntax-typescript": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -7528,14 +7537,14 @@ } }, "node_modules/@babel/preset-flow": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.22.5.tgz", - "integrity": "sha512-ta2qZ+LSiGCrP5pgcGt8xMnnkXQrq8Sa4Ulhy06BOlF5QbLw9q5hIx7bn5MrsvyTGAfh6kTOo07Q+Pfld/8Y5Q==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.23.3.tgz", + "integrity": "sha512-7yn6hl8RIv+KNk6iIrGZ+D06VhVY35wLVf23Cz/mMu1zOr7u4MMP4j0nZ9tLf8+4ZFpnib8cFYgB/oYg9hfswA==", "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-transform-flow-strip-types": "^7.22.5" + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-transform-flow-strip-types": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -7577,15 +7586,15 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.22.11.tgz", - "integrity": "sha512-tWY5wyCZYBGY7IlalfKI1rLiGlIfnwsRHZqlky0HVv8qviwQ1Uo/05M6+s+TcTCVa6Bmoo2uJW5TMFX6Wa4qVg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", + "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", "dependencies": { "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.11", - "@babel/plugin-transform-typescript": "^7.22.11" + "@babel/helper-validator-option": "^7.22.15", + "@babel/plugin-syntax-jsx": "^7.23.3", + "@babel/plugin-transform-modules-commonjs": "^7.23.3", + "@babel/plugin-transform-typescript": "^7.23.3" }, "engines": { "node": ">=6.9.0" @@ -7595,9 +7604,9 @@ } }, "node_modules/@babel/register": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.22.5.tgz", - "integrity": "sha512-vV6pm/4CijSQ8Y47RH5SopXzursN35RQINfGJkmOlcpAtGuf94miFvIPhCKGQN7WGIcsgG1BHEX2KVdTYwTwUQ==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.22.15.tgz", + "integrity": "sha512-V3Q3EqoQdn65RCgTLwauZaTfd1ShhwPmbBv+1dkZV/HpCGMKVyn6oFcRlI7RaKqiDQjX2Qd3AuoEguBgdjIKlg==", "peer": true, "dependencies": { "clone-deep": "^4.0.1", @@ -7660,9 +7669,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz", - "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -7684,18 +7693,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", + "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", "dependencies": { "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/generator": "^7.23.3", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", + "@babel/parser": "^7.23.3", + "@babel/types": "^7.23.3", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -7704,9 +7713,9 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", "dependencies": { "@babel/helper-string-parser": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20", @@ -8690,9 +8699,9 @@ } }, "node_modules/@jest/create-cache-key-function": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.6.3.tgz", - "integrity": "sha512-kzSK9XAxtD1kRPJKxsmD0YKw2fyXveP+5ikeQkCYCHeacWW1EGYMTgjDIM/Di4Uhttx7lnHwrNpz2xn+0rTp8g==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", + "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", "peer": true, "dependencies": { "@jest/types": "^29.6.3" @@ -9490,13 +9499,12 @@ } }, "node_modules/@mui/utils": { - "version": "5.14.6", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.6.tgz", - "integrity": "sha512-AznpqLu6hrFnpHgcvsSSMCG+cDbkcCYfo+daUwBVReNYv4l+NQ8+wvBAF4aUMi155N7xWbbgh0cyKs6Wdsm3aA==", + "version": "5.14.16", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.16.tgz", + "integrity": "sha512-3xV31GposHkwRbQzwJJuooWpK2ybWdEdeUPtRjv/6vjomyi97F3+68l+QVj9tPTvmfSbr2sx5c/NuvDulrdRmA==", "dependencies": { - "@babel/runtime": "^7.22.10", - "@types/prop-types": "^15.7.5", - "@types/react-is": "^18.2.1", + "@babel/runtime": "^7.23.2", + "@types/prop-types": "^15.7.9", "prop-types": "^15.8.1", "react-is": "^18.2.0" }, @@ -9508,7 +9516,38 @@ "url": "https://opencollective.com/mui" }, "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/x-data-grid": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-6.18.0.tgz", + "integrity": "sha512-js7Qhv+8XLgXilghKZmfBgUbkP7dYt7V1HLkM4C9285jFRUDFgAM9L6PVlRyUMn+YhVVPbvy+SWT+VWC8rYeQQ==", + "dependencies": { + "@babel/runtime": "^7.23.2", + "@mui/utils": "^5.14.16", + "clsx": "^2.0.0", + "prop-types": "^15.8.1", + "reselect": "^4.1.8" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@mui/material": "^5.4.1", + "@mui/system": "^5.4.1", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" } }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { @@ -10349,20 +10388,20 @@ } }, "node_modules/@react-native-community/cli": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-11.3.6.tgz", - "integrity": "sha512-bdwOIYTBVQ9VK34dsf6t3u6vOUU5lfdhKaAxiAVArjsr7Je88Bgs4sAbsOYsNK3tkE8G77U6wLpekknXcanlww==", - "peer": true, - "dependencies": { - "@react-native-community/cli-clean": "11.3.6", - "@react-native-community/cli-config": "11.3.6", - "@react-native-community/cli-debugger-ui": "11.3.6", - "@react-native-community/cli-doctor": "11.3.6", - "@react-native-community/cli-hermes": "11.3.6", - "@react-native-community/cli-plugin-metro": "11.3.6", - "@react-native-community/cli-server-api": "11.3.6", - "@react-native-community/cli-tools": "11.3.6", - "@react-native-community/cli-types": "11.3.6", + "version": "11.3.7", + "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-11.3.7.tgz", + "integrity": "sha512-Ou8eDlF+yh2rzXeCTpMPYJ2fuqsusNOhmpYPYNQJQ2h6PvaF30kPomflgRILems+EBBuggRtcT+I+1YH4o/q6w==", + "peer": true, + "dependencies": { + "@react-native-community/cli-clean": "11.3.7", + "@react-native-community/cli-config": "11.3.7", + "@react-native-community/cli-debugger-ui": "11.3.7", + "@react-native-community/cli-doctor": "11.3.7", + "@react-native-community/cli-hermes": "11.3.7", + "@react-native-community/cli-plugin-metro": "11.3.7", + "@react-native-community/cli-server-api": "11.3.7", + "@react-native-community/cli-tools": "11.3.7", + "@react-native-community/cli-types": "11.3.7", "chalk": "^4.1.2", "commander": "^9.4.1", "execa": "^5.0.0", @@ -10380,12 +10419,12 @@ } }, "node_modules/@react-native-community/cli-clean": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-11.3.6.tgz", - "integrity": "sha512-jOOaeG5ebSXTHweq1NznVJVAFKtTFWL4lWgUXl845bCGX7t1lL8xQNWHKwT8Oh1pGR2CI3cKmRjY4hBg+pEI9g==", + "version": "11.3.7", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-clean/-/cli-clean-11.3.7.tgz", + "integrity": "sha512-twtsv54ohcRyWVzPXL3F9VHGb4Qhn3slqqRs3wEuRzjR7cTmV2TIO2b1VhaqF4HlCgNd+cGuirvLtK2JJyaxMg==", "peer": true, "dependencies": { - "@react-native-community/cli-tools": "11.3.6", + "@react-native-community/cli-tools": "11.3.7", "chalk": "^4.1.2", "execa": "^5.0.0", "prompts": "^2.4.0" @@ -10462,12 +10501,12 @@ } }, "node_modules/@react-native-community/cli-config": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-11.3.6.tgz", - "integrity": "sha512-edy7fwllSFLan/6BG6/rznOBCLPrjmJAE10FzkEqNLHowi0bckiAPg1+1jlgQ2qqAxV5kuk+c9eajVfQvPLYDA==", + "version": "11.3.7", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-config/-/cli-config-11.3.7.tgz", + "integrity": "sha512-FDBLku9xskS+bx0YFJFLCmUJhEZ4/MMSC9qPYOGBollWYdgE7k/TWI0IeYFmMALAnbCdKQAYP5N29N55Tad8lg==", "peer": true, "dependencies": { - "@react-native-community/cli-tools": "11.3.6", + "@react-native-community/cli-tools": "11.3.7", "chalk": "^4.1.2", "cosmiconfig": "^5.1.0", "deepmerge": "^4.3.0", @@ -10627,24 +10666,24 @@ } }, "node_modules/@react-native-community/cli-debugger-ui": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-11.3.6.tgz", - "integrity": "sha512-jhMOSN/iOlid9jn/A2/uf7HbC3u7+lGktpeGSLnHNw21iahFBzcpuO71ekEdlmTZ4zC/WyxBXw9j2ka33T358w==", + "version": "11.3.7", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-11.3.7.tgz", + "integrity": "sha512-aVmKuPKHZENR8SrflkMurZqeyLwbKieHdOvaZCh1Nn/0UC5CxWcyST2DB2XQboZwsvr3/WXKJkSUO+SZ1J9qTQ==", "peer": true, "dependencies": { "serve-static": "^1.13.1" } }, "node_modules/@react-native-community/cli-doctor": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-11.3.6.tgz", - "integrity": "sha512-UT/Tt6omVPi1j6JEX+CObc85eVFghSZwy4GR9JFMsO7gNg2Tvcu1RGWlUkrbmWMAMHw127LUu6TGK66Ugu1NLA==", + "version": "11.3.7", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-doctor/-/cli-doctor-11.3.7.tgz", + "integrity": "sha512-YEHUqWISOHnsl5+NM14KHelKh68Sr5/HeEZvvNdIcvcKtZic3FU7Xd1WcbNdo3gCq5JvzGFfufx02Tabh5zmrg==", "peer": true, "dependencies": { - "@react-native-community/cli-config": "11.3.6", - "@react-native-community/cli-platform-android": "11.3.6", - "@react-native-community/cli-platform-ios": "11.3.6", - "@react-native-community/cli-tools": "11.3.6", + "@react-native-community/cli-config": "11.3.7", + "@react-native-community/cli-platform-android": "11.3.7", + "@react-native-community/cli-platform-ios": "11.3.7", + "@react-native-community/cli-tools": "11.3.7", "chalk": "^4.1.2", "command-exists": "^1.2.8", "envinfo": "^7.7.2", @@ -10753,13 +10792,13 @@ } }, "node_modules/@react-native-community/cli-hermes": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-11.3.6.tgz", - "integrity": "sha512-O55YAYGZ3XynpUdePPVvNuUPGPY0IJdctLAOHme73OvS80gNwfntHDXfmY70TGHWIfkK2zBhA0B+2v8s5aTyTA==", + "version": "11.3.7", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-hermes/-/cli-hermes-11.3.7.tgz", + "integrity": "sha512-chkKd8n/xeZkinRvtH6QcYA8rjNOKU3S3Lw/3Psxgx+hAYV0Gyk95qJHTalx7iu+PwjOOqqvCkJo5jCkYLkoqw==", "peer": true, "dependencies": { - "@react-native-community/cli-platform-android": "11.3.6", - "@react-native-community/cli-tools": "11.3.6", + "@react-native-community/cli-platform-android": "11.3.7", + "@react-native-community/cli-tools": "11.3.7", "chalk": "^4.1.2", "hermes-profile-transformer": "^0.0.6", "ip": "^1.1.5" @@ -10836,12 +10875,12 @@ } }, "node_modules/@react-native-community/cli-platform-android": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-11.3.6.tgz", - "integrity": "sha512-ZARrpLv5tn3rmhZc//IuDM1LSAdYnjUmjrp58RynlvjLDI4ZEjBAGCQmgysRgXAsK7ekMrfkZgemUczfn9td2A==", + "version": "11.3.7", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-android/-/cli-platform-android-11.3.7.tgz", + "integrity": "sha512-WGtXI/Rm178UQb8bu1TAeFC/RJvYGnbHpULXvE20GkmeJ1HIrMjkagyk6kkY3Ej25JAP2R878gv+TJ/XiRhaEg==", "peer": true, "dependencies": { - "@react-native-community/cli-tools": "11.3.6", + "@react-native-community/cli-tools": "11.3.7", "chalk": "^4.1.2", "execa": "^5.0.0", "glob": "^7.1.3", @@ -10919,12 +10958,12 @@ } }, "node_modules/@react-native-community/cli-platform-ios": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-11.3.6.tgz", - "integrity": "sha512-tZ9VbXWiRW+F+fbZzpLMZlj93g3Q96HpuMsS6DRhrTiG+vMQ3o6oPWSEEmMGOvJSYU7+y68Dc9ms2liC7VD6cw==", + "version": "11.3.7", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-platform-ios/-/cli-platform-ios-11.3.7.tgz", + "integrity": "sha512-Z/8rseBput49EldX7MogvN6zJlWzZ/4M97s2P+zjS09ZoBU7I0eOKLi0N9wx+95FNBvGQQ/0P62bB9UaFQH2jw==", "peer": true, "dependencies": { - "@react-native-community/cli-tools": "11.3.6", + "@react-native-community/cli-tools": "11.3.7", "chalk": "^4.1.2", "execa": "^5.0.0", "fast-xml-parser": "^4.0.12", @@ -11003,21 +11042,21 @@ } }, "node_modules/@react-native-community/cli-plugin-metro": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-11.3.6.tgz", - "integrity": "sha512-D97racrPX3069ibyabJNKw9aJpVcaZrkYiEzsEnx50uauQtPDoQ1ELb/5c6CtMhAEGKoZ0B5MS23BbsSZcLs2g==", + "version": "11.3.7", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-11.3.7.tgz", + "integrity": "sha512-0WhgoBVGF1f9jXcuagQmtxpwpfP+2LbLZH4qMyo6OtYLWLG13n2uRep+8tdGzfNzl1bIuUTeE9yZSAdnf9LfYQ==", "peer": true, "dependencies": { - "@react-native-community/cli-server-api": "11.3.6", - "@react-native-community/cli-tools": "11.3.6", + "@react-native-community/cli-server-api": "11.3.7", + "@react-native-community/cli-tools": "11.3.7", "chalk": "^4.1.2", "execa": "^5.0.0", - "metro": "0.76.7", - "metro-config": "0.76.7", - "metro-core": "0.76.7", - "metro-react-native-babel-transformer": "0.76.7", - "metro-resolver": "0.76.7", - "metro-runtime": "0.76.7", + "metro": "0.76.8", + "metro-config": "0.76.8", + "metro-core": "0.76.8", + "metro-react-native-babel-transformer": "0.76.8", + "metro-resolver": "0.76.8", + "metro-runtime": "0.76.8", "readline": "^1.3.0" } }, @@ -11079,19 +11118,6 @@ "node": ">=8" } }, - "node_modules/@react-native-community/cli-plugin-metro/node_modules/metro-runtime": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.76.7.tgz", - "integrity": "sha512-MuWHubQHymUWBpZLwuKZQgA/qbb35WnDAKPo83rk7JRLIFPvzXSvFaC18voPuzJBt1V98lKQIonh6MiC9gd8Ug==", - "peer": true, - "dependencies": { - "@babel/runtime": "^7.0.0", - "react-refresh": "^0.4.0" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/@react-native-community/cli-plugin-metro/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -11105,13 +11131,13 @@ } }, "node_modules/@react-native-community/cli-server-api": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-11.3.6.tgz", - "integrity": "sha512-8GUKodPnURGtJ9JKg8yOHIRtWepPciI3ssXVw5jik7+dZ43yN8P5BqCoDaq8e1H1yRer27iiOfT7XVnwk8Dueg==", + "version": "11.3.7", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-server-api/-/cli-server-api-11.3.7.tgz", + "integrity": "sha512-yoFyGdvR3HxCnU6i9vFqKmmSqFzCbnFSnJ29a+5dppgPRetN+d//O8ard/YHqHzToFnXutAFf2neONn23qcJAg==", "peer": true, "dependencies": { - "@react-native-community/cli-debugger-ui": "11.3.6", - "@react-native-community/cli-tools": "11.3.6", + "@react-native-community/cli-debugger-ui": "11.3.7", + "@react-native-community/cli-tools": "11.3.7", "compression": "^1.7.1", "connect": "^3.6.5", "errorhandler": "^1.5.1", @@ -11138,9 +11164,9 @@ } }, "node_modules/@react-native-community/cli-server-api/node_modules/@types/yargs": { - "version": "15.0.15", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.15.tgz", - "integrity": "sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg==", + "version": "15.0.18", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.18.tgz", + "integrity": "sha512-DDi2KmvAnNsT/EvU8jp1UR7pOJojBtJ3GLZ/uw1MUq4VbbESppPWoHUY4h0OB4BbEbGJiyEsmUcuZDZtoR+ZwQ==", "peer": true, "dependencies": { "@types/yargs-parser": "*" @@ -11259,9 +11285,9 @@ } }, "node_modules/@react-native-community/cli-tools": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-11.3.6.tgz", - "integrity": "sha512-JpmUTcDwAGiTzLsfMlIAYpCMSJ9w2Qlf7PU7mZIRyEu61UzEawyw83DkqfbzDPBuRwRnaeN44JX2CP/yTO3ThQ==", + "version": "11.3.7", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-tools/-/cli-tools-11.3.7.tgz", + "integrity": "sha512-peyhP4TV6Ps1hk+MBHTFaIR1eI3u+OfGBvr5r0wPwo3FAJvldRinMgcB/TcCcOBXVORu7ba1XYjkubPeYcqAyA==", "peer": true, "dependencies": { "appdirsjs": "^1.2.4", @@ -11346,9 +11372,9 @@ } }, "node_modules/@react-native-community/cli-types": { - "version": "11.3.6", - "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-11.3.6.tgz", - "integrity": "sha512-6DxjrMKx5x68N/tCJYVYRKAtlRHbtUVBZrnAvkxbRWFD9v4vhNgsPM0RQm8i2vRugeksnao5mbnRGpS6c0awCw==", + "version": "11.3.7", + "resolved": "https://registry.npmjs.org/@react-native-community/cli-types/-/cli-types-11.3.7.tgz", + "integrity": "sha512-OhSr/TiDQkXjL5YOs8+hvGSB+HltLn5ZI0+A3DCiMsjUgTTsYh+Z63OtyMpNjrdCEFcg0MpfdU2uxstCS6Dc5g==", "peer": true, "dependencies": { "joi": "^17.2.1" @@ -11483,9 +11509,9 @@ "peer": true }, "node_modules/@react-native/codegen": { - "version": "0.72.6", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.72.6.tgz", - "integrity": "sha512-idTVI1es/oopN0jJT/0jB6nKdvTUKE3757zA5+NPXZTeB46CIRbmmos4XBiAec8ufu9/DigLPbHTYAaMNZJ6Ig==", + "version": "0.72.7", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.72.7.tgz", + "integrity": "sha512-O7xNcGeXGbY+VoqBGNlZ3O05gxfATlwE1Q1qQf5E38dK+tXn5BY4u0jaQ9DPjfE8pBba8g/BYI1N44lynidMtg==", "peer": true, "dependencies": { "@babel/parser": "^7.20.0", @@ -11719,9 +11745,9 @@ } }, "node_modules/@smithy/types": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.2.2.tgz", - "integrity": "sha512-4PS0y1VxDnELGHGgBWlDksB2LJK8TG8lcvlWxIsgR+8vROI7Ms8h1P4FQUx+ftAX2QZv5g1CJCdhdRmQKyonyw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.4.0.tgz", + "integrity": "sha512-iH1Xz68FWlmBJ9vvYeHifVMWJf82ONx+OybPW8ZGf5wnEv2S0UXcU4zwlwJkRXuLKpcSLHrraHbn2ucdVXLb4g==", "dependencies": { "tslib": "^2.5.0" }, @@ -12668,9 +12694,9 @@ "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==" }, "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + "version": "15.7.9", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.9.tgz", + "integrity": "sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==" }, "node_modules/@types/qs": { "version": "6.9.7", @@ -12716,14 +12742,6 @@ "@types/react": "*" } }, - "node_modules/@types/react-is": { - "version": "18.2.1", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.1.tgz", - "integrity": "sha512-wyUkmaaSZEzFZivD8F2ftSyAfk6L+DfFliVj/mYdOXbVjRcS87fQJLTnhk6dRZPuJjI+9g6RZJO4PNCngUrmyw==", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-lazylog": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/@types/react-lazylog/-/react-lazylog-4.5.1.tgz", @@ -13441,9 +13459,9 @@ } }, "node_modules/amazon-cognito-identity-js": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.5.tgz", - "integrity": "sha512-bRAiw6uQuttufRD0TFcrWvA5hxAgPIwNzM0crmWniPdkmCxRoa68yxRaViZUbwAcGu9YPLCLqM87b1060BRddw==", + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.7.tgz", + "integrity": "sha512-tSjnM7KyAeOZ7UMah+oOZ6cW4Gf64FFcc7BE2l7MTcp7ekAPrXaCbpcW2xEpH1EiDS4cPcAouHzmCuc2tr72vQ==", "dependencies": { "@aws-crypto/sha256-js": "1.2.2", "buffer": "4.9.2", @@ -13751,15 +13769,16 @@ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==" }, "node_modules/assert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", - "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "peer": true, "dependencies": { - "es6-object-assign": "^1.1.0", - "is-nan": "^1.2.1", - "object-is": "^1.0.1", - "util": "^0.12.0" + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" } }, "node_modules/ast-types": { @@ -13879,22 +13898,22 @@ } }, "node_modules/aws-amplify": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/aws-amplify/-/aws-amplify-5.3.10.tgz", - "integrity": "sha512-vwm97np/ni37WIWuE9xalj92vctgzIJRYX+FXcP9rAQgF4F2eEAjYNIkHGErpYqodmbeYF5gN1f/diKnhdGaRw==", - "dependencies": { - "@aws-amplify/analytics": "6.5.4", - "@aws-amplify/api": "5.4.4", - "@aws-amplify/auth": "5.6.4", - "@aws-amplify/cache": "5.1.10", - "@aws-amplify/core": "5.8.4", - "@aws-amplify/datastore": "4.7.4", - "@aws-amplify/geo": "2.3.4", - "@aws-amplify/interactions": "5.2.10", - "@aws-amplify/notifications": "1.6.4", - "@aws-amplify/predictions": "5.5.4", - "@aws-amplify/pubsub": "5.5.4", - "@aws-amplify/storage": "5.9.4", + "version": "5.3.12", + "resolved": "https://registry.npmjs.org/aws-amplify/-/aws-amplify-5.3.12.tgz", + "integrity": "sha512-vJAgUvtgYBKuKmtz0zhKGnb7yXH1rLn/NE/GV2ti3ysKTFiNp1hhoE8ftz0gNXN9JFtk5AfKYdMHBCVnw8rLGQ==", + "dependencies": { + "@aws-amplify/analytics": "6.5.6", + "@aws-amplify/api": "5.4.6", + "@aws-amplify/auth": "5.6.6", + "@aws-amplify/cache": "5.1.12", + "@aws-amplify/core": "5.8.6", + "@aws-amplify/datastore": "4.7.6", + "@aws-amplify/geo": "2.3.6", + "@aws-amplify/interactions": "5.2.12", + "@aws-amplify/notifications": "1.6.6", + "@aws-amplify/predictions": "5.5.6", + "@aws-amplify/pubsub": "5.5.6", + "@aws-amplify/storage": "5.9.6", "tslib": "^2.0.0" } }, @@ -13907,11 +13926,26 @@ } }, "node_modules/axios": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz", - "integrity": "sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", + "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dependencies": { - "follow-redirects": "^1.14.8" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, "node_modules/axobject-query": { @@ -15054,9 +15088,9 @@ } }, "node_modules/cli-spinners": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", - "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.1.tgz", + "integrity": "sha512-jHgecW0pxkonBJdrKsqxgRX9AcG+u/5k0Q7WPDfi8AogLAdwxEkyYYNWwZ5GvVFoFx2uiY1eNcSK00fh+1+FyQ==", "peer": true, "engines": { "node": ">=6" @@ -16005,9 +16039,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.9", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz", - "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==", + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", "peer": true }, "node_modules/debug": { @@ -16559,9 +16593,9 @@ } }, "node_modules/envinfo": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.10.0.tgz", - "integrity": "sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.0.tgz", + "integrity": "sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==", "peer": true, "bin": { "envinfo": "dist/cli.js" @@ -16738,12 +16772,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es6-object-assign": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", - "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", - "peer": true - }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -17830,17 +17858,17 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, "node_modules/fast-xml-parser": { - "version": "4.2.7", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.7.tgz", - "integrity": "sha512-J8r6BriSLO1uj2miOk1NW0YVm8AGOOu3Si2HQp/cSmo6EA4m3fcwu2WKjJ4RK9wMLBtg69y1kS8baDiQBR41Ig==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.3.2.tgz", + "integrity": "sha512-rmrXUXwbJedoXkStenj1kkljNF7ugn5ZjR9FJcwmCfcCbtOMDghPajbc+Tck6vE6F5XsDmx+Pr2le9fw8+pXBg==", "funding": [ - { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" - }, { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" } ], "dependencies": { @@ -22781,9 +22809,9 @@ } }, "node_modules/joi": { - "version": "17.10.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.10.0.tgz", - "integrity": "sha512-hrazgRSlhzacZ69LdcKfhi3Vu13z2yFfoAzmEov3yFIJlatTdVGUW6vle1zjH8qkzdCn/qGw8rapjqsObbYXAg==", + "version": "17.11.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", + "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", "peer": true, "dependencies": { "@hapi/hoek": "^9.0.0", @@ -22827,20 +22855,21 @@ "peer": true }, "node_modules/jscodeshift": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.15.0.tgz", - "integrity": "sha512-t337Wx7Vy1ffhas7E1KZUHaR9YPdeCfxPvxz9k6DKwYW88pcs1piR1eR9d+7GQZGSQIZd6a+cfIM3XpMe9rFKQ==", - "peer": true, - "dependencies": { - "@babel/core": "^7.13.16", - "@babel/parser": "^7.13.16", - "@babel/plugin-proposal-class-properties": "^7.13.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.13.8", - "@babel/plugin-proposal-optional-chaining": "^7.13.12", - "@babel/plugin-transform-modules-commonjs": "^7.13.8", - "@babel/preset-flow": "^7.13.13", - "@babel/preset-typescript": "^7.13.0", - "@babel/register": "^7.13.16", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.15.1.tgz", + "integrity": "sha512-hIJfxUy8Rt4HkJn/zZPU9ChKfKZM1342waJ1QC2e2YsPcWhM+3BJ4dcfQCzArTrk1jJeNLB341H+qOcEHRxJZg==", + "peer": true, + "dependencies": { + "@babel/core": "^7.23.0", + "@babel/parser": "^7.23.0", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.23.0", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", + "@babel/plugin-transform-optional-chaining": "^7.23.0", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/preset-flow": "^7.22.15", + "@babel/preset-typescript": "^7.23.0", + "@babel/register": "^7.22.15", "babel-core": "^7.0.0-bridge.0", "chalk": "^4.1.2", "flow-parser": "0.*", @@ -22848,7 +22877,7 @@ "micromatch": "^4.0.4", "neo-async": "^2.5.0", "node-dir": "^0.1.17", - "recast": "^0.23.1", + "recast": "^0.23.3", "temp": "^0.8.4", "write-file-atomic": "^2.3.0" }, @@ -23811,9 +23840,9 @@ } }, "node_modules/metro": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro/-/metro-0.76.7.tgz", - "integrity": "sha512-67ZGwDeumEPnrHI+pEDSKH2cx+C81Gx8Mn5qOtmGUPm/Up9Y4I1H2dJZ5n17MWzejNo0XAvPh0QL0CrlJEODVQ==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.76.8.tgz", + "integrity": "sha512-oQA3gLzrrYv3qKtuWArMgHPbHu8odZOD9AoavrqSFllkPgOtmkBvNNDLCELqv5SjBfqjISNffypg+5UGG3y0pg==", "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", @@ -23838,22 +23867,22 @@ "jest-worker": "^27.2.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.76.7", - "metro-cache": "0.76.7", - "metro-cache-key": "0.76.7", - "metro-config": "0.76.7", - "metro-core": "0.76.7", - "metro-file-map": "0.76.7", - "metro-inspector-proxy": "0.76.7", - "metro-minify-terser": "0.76.7", - "metro-minify-uglify": "0.76.7", - "metro-react-native-babel-preset": "0.76.7", - "metro-resolver": "0.76.7", - "metro-runtime": "0.76.7", - "metro-source-map": "0.76.7", - "metro-symbolicate": "0.76.7", - "metro-transform-plugins": "0.76.7", - "metro-transform-worker": "0.76.7", + "metro-babel-transformer": "0.76.8", + "metro-cache": "0.76.8", + "metro-cache-key": "0.76.8", + "metro-config": "0.76.8", + "metro-core": "0.76.8", + "metro-file-map": "0.76.8", + "metro-inspector-proxy": "0.76.8", + "metro-minify-terser": "0.76.8", + "metro-minify-uglify": "0.76.8", + "metro-react-native-babel-preset": "0.76.8", + "metro-resolver": "0.76.8", + "metro-runtime": "0.76.8", + "metro-source-map": "0.76.8", + "metro-symbolicate": "0.76.8", + "metro-transform-plugins": "0.76.8", + "metro-transform-worker": "0.76.8", "mime-types": "^2.1.27", "node-fetch": "^2.2.0", "nullthrows": "^1.1.1", @@ -23873,9 +23902,9 @@ } }, "node_modules/metro-babel-transformer": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.76.7.tgz", - "integrity": "sha512-bgr2OFn0J4r0qoZcHrwEvccF7g9k3wdgTOgk6gmGHrtlZ1Jn3oCpklW/DfZ9PzHfjY2mQammKTc19g/EFGyOJw==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.76.8.tgz", + "integrity": "sha512-Hh6PW34Ug/nShlBGxkwQJSgPGAzSJ9FwQXhUImkzdsDgVu6zj5bx258J8cJVSandjNoQ8nbaHK6CaHlnbZKbyA==", "peer": true, "dependencies": { "@babel/core": "^7.20.0", @@ -23887,12 +23916,12 @@ } }, "node_modules/metro-cache": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.76.7.tgz", - "integrity": "sha512-nWBMztrs5RuSxZRI7hgFgob5PhYDmxICh9FF8anm9/ito0u0vpPvRxt7sRu8fyeD2AHdXqE7kX32rWY0LiXgeg==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.76.8.tgz", + "integrity": "sha512-QBJSJIVNH7Hc/Yo6br/U/qQDUpiUdRgZ2ZBJmvAbmAKp2XDzsapnMwK/3BGj8JNWJF7OLrqrYHsRsukSbUBpvQ==", "peer": true, "dependencies": { - "metro-core": "0.76.7", + "metro-core": "0.76.8", "rimraf": "^3.0.2" }, "engines": { @@ -23900,27 +23929,27 @@ } }, "node_modules/metro-cache-key": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.76.7.tgz", - "integrity": "sha512-0pecoIzwsD/Whn/Qfa+SDMX2YyasV0ndbcgUFx7w1Ct2sLHClujdhQ4ik6mvQmsaOcnGkIyN0zcceMDjC2+BFQ==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.76.8.tgz", + "integrity": "sha512-buKQ5xentPig9G6T37Ww/R/bC+/V1MA5xU/D8zjnhlelsrPG6w6LtHUS61ID3zZcMZqYaELWk5UIadIdDsaaLw==", "peer": true, "engines": { "node": ">=16" } }, "node_modules/metro-config": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.76.7.tgz", - "integrity": "sha512-CFDyNb9bqxZemiChC/gNdXZ7OQkIwmXzkrEXivcXGbgzlt/b2juCv555GWJHyZSlorwnwJfY3uzAFu4A9iRVfg==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.76.8.tgz", + "integrity": "sha512-SL1lfKB0qGHALcAk2zBqVgQZpazDYvYFGwCK1ikz0S6Y/CM2i2/HwuZN31kpX6z3mqjv/6KvlzaKoTb1otuSAA==", "peer": true, "dependencies": { "connect": "^3.6.5", "cosmiconfig": "^5.0.5", "jest-validate": "^29.2.1", - "metro": "0.76.7", - "metro-cache": "0.76.7", - "metro-core": "0.76.7", - "metro-runtime": "0.76.7" + "metro": "0.76.8", + "metro-cache": "0.76.8", + "metro-core": "0.76.8", + "metro-runtime": "0.76.8" }, "engines": { "node": ">=16" @@ -23976,19 +24005,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/metro-config/node_modules/metro-runtime": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.76.7.tgz", - "integrity": "sha512-MuWHubQHymUWBpZLwuKZQgA/qbb35WnDAKPo83rk7JRLIFPvzXSvFaC18voPuzJBt1V98lKQIonh6MiC9gd8Ug==", - "peer": true, - "dependencies": { - "@babel/runtime": "^7.0.0", - "react-refresh": "^0.4.0" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/metro-config/node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -24012,22 +24028,22 @@ } }, "node_modules/metro-core": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.76.7.tgz", - "integrity": "sha512-0b8KfrwPmwCMW+1V7ZQPkTy2tsEKZjYG9Pu1PTsu463Z9fxX7WaR0fcHFshv+J1CnQSUTwIGGjbNvj1teKe+pw==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.76.8.tgz", + "integrity": "sha512-sl2QLFI3d1b1XUUGxwzw/KbaXXU/bvFYrSKz6Sg19AdYGWFyzsgZ1VISRIDf+HWm4R/TJXluhWMEkEtZuqi3qA==", "peer": true, "dependencies": { "lodash.throttle": "^4.1.1", - "metro-resolver": "0.76.7" + "metro-resolver": "0.76.8" }, "engines": { "node": ">=16" } }, "node_modules/metro-file-map": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.76.7.tgz", - "integrity": "sha512-s+zEkTcJ4mOJTgEE2ht4jIo1DZfeWreQR3tpT3gDV/Y/0UQ8aJBTv62dE775z0GLsWZApiblAYZsj7ZE8P06nw==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.76.8.tgz", + "integrity": "sha512-A/xP1YNEVwO1SUV9/YYo6/Y1MmzhL4ZnVgcJC3VmHp/BYVOXVStzgVbWv2wILe56IIMkfXU+jpXrGKKYhFyHVw==", "peer": true, "dependencies": { "anymatch": "^3.0.3", @@ -24067,9 +24083,9 @@ } }, "node_modules/metro-file-map/node_modules/@types/yargs": { - "version": "16.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz", - "integrity": "sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==", + "version": "16.0.8", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.8.tgz", + "integrity": "sha512-1GwLEkmFafeb/HbE6pC7tFlgYSQ4Iqh2qlWCq8xN+Qfaiaxr2PcLfuhfRFRYqI6XJyeFoLYyKnhFbNsst9FMtQ==", "peer": true, "dependencies": { "@types/yargs-parser": "*" @@ -24216,9 +24232,9 @@ } }, "node_modules/metro-inspector-proxy": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.76.7.tgz", - "integrity": "sha512-rNZ/6edTl/1qUekAhAbaFjczMphM50/UjtxiKulo6vqvgn/Mjd9hVqDvVYfAMZXqPvlusD88n38UjVYPkruLSg==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-inspector-proxy/-/metro-inspector-proxy-0.76.8.tgz", + "integrity": "sha512-Us5o5UEd4Smgn1+TfHX4LvVPoWVo9VsVMn4Ldbk0g5CQx3Gu0ygc/ei2AKPGTwsOZmKxJeACj7yMH2kgxQP/iw==", "peer": true, "dependencies": { "connect": "^3.6.5", @@ -24271,9 +24287,9 @@ } }, "node_modules/metro-minify-terser": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.76.7.tgz", - "integrity": "sha512-FQiZGhIxCzhDwK4LxyPMLlq0Tsmla10X7BfNGlYFK0A5IsaVKNJbETyTzhpIwc+YFRT4GkFFwgo0V2N5vxO5HA==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.76.8.tgz", + "integrity": "sha512-Orbvg18qXHCrSj1KbaeSDVYRy/gkro2PC7Fy2tDSH1c9RB4aH8tuMOIXnKJE+1SXxBtjWmQ5Yirwkth2DyyEZA==", "peer": true, "dependencies": { "terser": "^5.15.0" @@ -24283,9 +24299,9 @@ } }, "node_modules/metro-minify-uglify": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.76.7.tgz", - "integrity": "sha512-FuXIU3j2uNcSvQtPrAJjYWHruPiQ+EpE++J9Z+VznQKEHcIxMMoQZAfIF2IpZSrZYfLOjVFyGMvj41jQMxV1Vw==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-minify-uglify/-/metro-minify-uglify-0.76.8.tgz", + "integrity": "sha512-6l8/bEvtVaTSuhG1FqS0+Mc8lZ3Bl4RI8SeRIifVLC21eeSDp4CEBUWSGjpFyUDfi6R5dXzYaFnSgMNyfxADiQ==", "peer": true, "dependencies": { "uglify-es": "^3.1.9" @@ -24295,9 +24311,9 @@ } }, "node_modules/metro-react-native-babel-preset": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.7.tgz", - "integrity": "sha512-R25wq+VOSorAK3hc07NW0SmN8z9S/IR0Us0oGAsBcMZnsgkbOxu77Mduqf+f4is/wnWHc5+9bfiqdLnaMngiVw==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.8.tgz", + "integrity": "sha512-Ptza08GgqzxEdK8apYsjTx2S8WDUlS2ilBlu9DR1CUcHmg4g3kOkFylZroogVAUKtpYQNYwAvdsjmrSdDNtiAg==", "peer": true, "dependencies": { "@babel/core": "^7.20.0", @@ -24348,15 +24364,15 @@ } }, "node_modules/metro-react-native-babel-transformer": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.76.7.tgz", - "integrity": "sha512-W6lW3J7y/05ph3c2p3KKJNhH0IdyxdOCbQ5it7aM2MAl0SM4wgKjaV6EYv9b3rHklpV6K3qMH37UKVcjMooWiA==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.76.8.tgz", + "integrity": "sha512-3h+LfS1WG1PAzhq8QF0kfXjxuXetbY/lgz8vYMQhgrMMp17WM1DNJD0gjx8tOGYbpbBC1qesJ45KMS4o5TA73A==", "peer": true, "dependencies": { "@babel/core": "^7.20.0", "babel-preset-fbjs": "^3.4.0", "hermes-parser": "0.12.0", - "metro-react-native-babel-preset": "0.76.7", + "metro-react-native-babel-preset": "0.76.8", "nullthrows": "^1.1.1" }, "engines": { @@ -24367,9 +24383,9 @@ } }, "node_modules/metro-resolver": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.76.7.tgz", - "integrity": "sha512-pC0Wgq29HHIHrwz23xxiNgylhI8Rq1V01kQaJ9Kz11zWrIdlrH0ZdnJ7GC6qA0ErROG+cXmJ0rJb8/SW1Zp2IA==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.76.8.tgz", + "integrity": "sha512-KccOqc10vrzS7ZhG2NSnL2dh3uVydarB7nOhjreQ7C4zyWuiW9XpLC4h47KtGQv3Rnv/NDLJYeDqaJ4/+140HQ==", "peer": true, "engines": { "node": ">=16" @@ -24407,7 +24423,7 @@ "node": ">=16" } }, - "node_modules/metro-source-map/node_modules/metro-symbolicate": { + "node_modules/metro-symbolicate": { "version": "0.76.8", "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.76.8.tgz", "integrity": "sha512-LrRL3uy2VkzrIXVlxoPtqb40J6Bf1mlPNmUQewipc3qfKKFgtPHBackqDy1YL0njDsWopCKcfGtFYLn0PTUn3w==", @@ -24427,58 +24443,10 @@ "node": ">=16" } }, - "node_modules/metro-symbolicate": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.76.7.tgz", - "integrity": "sha512-p0zWEME5qLSL1bJb93iq+zt5fz3sfVn9xFYzca1TJIpY5MommEaS64Va87lp56O0sfEIvh4307Oaf/ZzRjuLiQ==", - "peer": true, - "dependencies": { - "invariant": "^2.2.4", - "metro-source-map": "0.76.7", - "nullthrows": "^1.1.1", - "source-map": "^0.5.6", - "through2": "^2.0.1", - "vlq": "^1.0.0" - }, - "bin": { - "metro-symbolicate": "src/index.js" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/metro-symbolicate/node_modules/metro-source-map": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.76.7.tgz", - "integrity": "sha512-Prhx7PeRV1LuogT0Kn5VjCuFu9fVD68eefntdWabrksmNY6mXK8pRqzvNJOhTojh6nek+RxBzZeD6MIOOyXS6w==", - "peer": true, - "dependencies": { - "@babel/traverse": "^7.20.0", - "@babel/types": "^7.20.0", - "invariant": "^2.2.4", - "metro-symbolicate": "0.76.7", - "nullthrows": "^1.1.1", - "ob1": "0.76.7", - "source-map": "^0.5.6", - "vlq": "^1.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/metro-symbolicate/node_modules/ob1": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.76.7.tgz", - "integrity": "sha512-BQdRtxxoUNfSoZxqeBGOyuT9nEYSn18xZHwGMb0mMVpn2NBcYbnyKY4BK2LIHRgw33CBGlUmE+KMaNvyTpLLtQ==", - "peer": true, - "engines": { - "node": ">=16" - } - }, "node_modules/metro-transform-plugins": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.76.7.tgz", - "integrity": "sha512-iSmnjVApbdivjuzb88Orb0JHvcEt5veVyFAzxiS5h0QB+zV79w6JCSqZlHCrbNOkOKBED//LqtKbFVakxllnNg==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.76.8.tgz", + "integrity": "sha512-PlkGTQNqS51Bx4vuufSQCdSn2R2rt7korzngo+b5GCkeX5pjinPjnO2kNhQ8l+5bO0iUD/WZ9nsM2PGGKIkWFA==", "peer": true, "dependencies": { "@babel/core": "^7.20.0", @@ -24492,9 +24460,9 @@ } }, "node_modules/metro-transform-worker": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.76.7.tgz", - "integrity": "sha512-cGvELqFMVk9XTC15CMVzrCzcO6sO1lURfcbgjuuPdzaWuD11eEyocvkTX0DPiRjsvgAmicz4XYxVzgYl3MykDw==", + "version": "0.76.8", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.76.8.tgz", + "integrity": "sha512-mE1fxVAnJKmwwJyDtThildxxos9+DGs9+vTrx2ktSFMEVTtXS/bIv2W6hux1pqivqAfyJpTeACXHk5u2DgGvIQ==", "peer": true, "dependencies": { "@babel/core": "^7.20.0", @@ -24502,46 +24470,18 @@ "@babel/parser": "^7.20.0", "@babel/types": "^7.20.0", "babel-preset-fbjs": "^3.4.0", - "metro": "0.76.7", - "metro-babel-transformer": "0.76.7", - "metro-cache": "0.76.7", - "metro-cache-key": "0.76.7", - "metro-source-map": "0.76.7", - "metro-transform-plugins": "0.76.7", + "metro": "0.76.8", + "metro-babel-transformer": "0.76.8", + "metro-cache": "0.76.8", + "metro-cache-key": "0.76.8", + "metro-source-map": "0.76.8", + "metro-transform-plugins": "0.76.8", "nullthrows": "^1.1.1" }, "engines": { "node": ">=16" } }, - "node_modules/metro-transform-worker/node_modules/metro-source-map": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.76.7.tgz", - "integrity": "sha512-Prhx7PeRV1LuogT0Kn5VjCuFu9fVD68eefntdWabrksmNY6mXK8pRqzvNJOhTojh6nek+RxBzZeD6MIOOyXS6w==", - "peer": true, - "dependencies": { - "@babel/traverse": "^7.20.0", - "@babel/types": "^7.20.0", - "invariant": "^2.2.4", - "metro-symbolicate": "0.76.7", - "nullthrows": "^1.1.1", - "ob1": "0.76.7", - "source-map": "^0.5.6", - "vlq": "^1.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/metro-transform-worker/node_modules/ob1": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.76.7.tgz", - "integrity": "sha512-BQdRtxxoUNfSoZxqeBGOyuT9nEYSn18xZHwGMb0mMVpn2NBcYbnyKY4BK2LIHRgw33CBGlUmE+KMaNvyTpLLtQ==", - "peer": true, - "engines": { - "node": ">=16" - } - }, "node_modules/metro/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -24644,53 +24584,12 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/metro/node_modules/metro-runtime": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.76.7.tgz", - "integrity": "sha512-MuWHubQHymUWBpZLwuKZQgA/qbb35WnDAKPo83rk7JRLIFPvzXSvFaC18voPuzJBt1V98lKQIonh6MiC9gd8Ug==", - "peer": true, - "dependencies": { - "@babel/runtime": "^7.0.0", - "react-refresh": "^0.4.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/metro/node_modules/metro-source-map": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.76.7.tgz", - "integrity": "sha512-Prhx7PeRV1LuogT0Kn5VjCuFu9fVD68eefntdWabrksmNY6mXK8pRqzvNJOhTojh6nek+RxBzZeD6MIOOyXS6w==", - "peer": true, - "dependencies": { - "@babel/traverse": "^7.20.0", - "@babel/types": "^7.20.0", - "invariant": "^2.2.4", - "metro-symbolicate": "0.76.7", - "nullthrows": "^1.1.1", - "ob1": "0.76.7", - "source-map": "^0.5.6", - "vlq": "^1.0.0" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/metro/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "peer": true }, - "node_modules/metro/node_modules/ob1": { - "version": "0.76.7", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.76.7.tgz", - "integrity": "sha512-BQdRtxxoUNfSoZxqeBGOyuT9nEYSn18xZHwGMb0mMVpn2NBcYbnyKY4BK2LIHRgw33CBGlUmE+KMaNvyTpLLtQ==", - "peer": true, - "engines": { - "node": ">=16" - } - }, "node_modules/metro/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -27403,6 +27302,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -27955,9 +27859,9 @@ } }, "node_modules/react-devtools-core": { - "version": "4.28.4", - "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.28.4.tgz", - "integrity": "sha512-IUZKLv3CimeM07G3vX4H4loxVpByrzq3HvfTX7v9migalwvLs9ZY5D3S3pKR33U+GguYfBBdMMZyToFhsSE/iQ==", + "version": "4.28.5", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-4.28.5.tgz", + "integrity": "sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA==", "peer": true, "dependencies": { "shell-quote": "^1.6.1", @@ -28103,17 +28007,17 @@ } }, "node_modules/react-native": { - "version": "0.72.4", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.72.4.tgz", - "integrity": "sha512-+vrObi0wZR+NeqL09KihAAdVlQ9IdplwznJWtYrjnQ4UbCW6rkzZJebRsugwUneSOKNFaHFEo1uKU89HsgtYBg==", + "version": "0.72.6", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.72.6.tgz", + "integrity": "sha512-RafPY2gM7mcrFySS8TL8x+TIO3q7oAlHpzEmC7Im6pmXni6n1AuufGaVh0Narbr1daxstw7yW7T9BKW5dpVc2A==", "peer": true, "dependencies": { "@jest/create-cache-key-function": "^29.2.1", - "@react-native-community/cli": "11.3.6", - "@react-native-community/cli-platform-android": "11.3.6", - "@react-native-community/cli-platform-ios": "11.3.6", + "@react-native-community/cli": "11.3.7", + "@react-native-community/cli-platform-android": "11.3.7", + "@react-native-community/cli-platform-ios": "11.3.7", "@react-native/assets-registry": "^0.72.0", - "@react-native/codegen": "^0.72.6", + "@react-native/codegen": "^0.72.7", "@react-native/gradle-plugin": "^0.72.11", "@react-native/js-polyfills": "^0.72.1", "@react-native/normalize-colors": "^0.72.0", @@ -28194,9 +28098,9 @@ } }, "node_modules/react-native/node_modules/@types/yargs": { - "version": "15.0.15", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.15.tgz", - "integrity": "sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg==", + "version": "15.0.18", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.18.tgz", + "integrity": "sha512-DDi2KmvAnNsT/EvU8jp1UR7pOJojBtJ3GLZ/uw1MUq4VbbESppPWoHUY4h0OB4BbEbGJiyEsmUcuZDZtoR+ZwQ==", "peer": true, "dependencies": { "@types/yargs-parser": "*" @@ -28309,9 +28213,9 @@ } }, "node_modules/react-native/node_modules/whatwg-fetch": { - "version": "3.6.17", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.17.tgz", - "integrity": "sha512-c4ghIvG6th0eudYwKZY5keb81wtFz9/WeAHAoy8+r18kcWlitUIrmGFQ2rWEl4UCKUilD3zCLHOIPheHx5ypRQ==", + "version": "3.6.19", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.19.tgz", + "integrity": "sha512-d67JP4dHSbm2TrpFj8AbO8DnL1JXL5J9u0Kq2xW6d0TFDbCA3Muhdt8orXC22utleTVj7Prqt82baN6RBvnEgw==", "peer": true }, "node_modules/react-refresh": { @@ -30314,6 +30218,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "node_modules/reselect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + }, "node_modules/resolve": { "version": "1.22.4", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index 50a4de771..9cd716104 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,11 @@ { "name": "crossfeed-front", "version": "0.1.0", + "jest" : { + "moduleNameMapper": { + "axios": "axios/dist/node/axios.cjs" + } + }, "private": true, "engines": { "node": ">=18.0.0" @@ -16,12 +21,13 @@ "@mui/lab": "^5.0.0-alpha.135", "@mui/material": "^5.14.2", "@mui/system": "^5.14.0", + "@mui/x-data-grid": "^6.18.0", "@nivo/bar": "^0.80.0", "@nivo/core": "^0.80.0", "@nivo/pie": "^0.80.0", "@trussworks/react-uswds": "^5.1.1", "autoprefixer": "^10.4.13", - "aws-amplify": "^5.0.4", + "aws-amplify": "^5.3.12", "classnames": "^2.3.2", "d3-scale": "^4.0.2", "date-fns": "^2.29.3", @@ -125,10 +131,10 @@ "eslint-config-react-app": "^7.0.1", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.31.10", - "sass": "^1.55.0", "jest-date-mock": "^1.0.8", "prettier": "^2.7.1", + "sass": "^1.55.0", "ts-jest": "^29.1.1", "typescript": "^4.8.4" } -} \ No newline at end of file +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 9daa449d6..20f0e5c7d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -16,6 +16,7 @@ import { import { Domain, AuthLogin, + AuthLoginCreate, AuthCreateAccount, Scans, Scan, @@ -31,7 +32,8 @@ import { LoginGovCallback, Feeds, Domains, - Reports + Reports, + RegionUsers } from 'pages'; import { Layout, RouteGuard } from 'components'; import { CrossfeedFooter } from './components/Footer'; @@ -108,6 +110,15 @@ const App: React.FC = () => ( )} component={Risk} /> + } + unauth={(props) => ( + + )} + component={Risk} + /> ( + {/* */} + diff --git a/frontend/src/components/Dialog/ConfirmDialog.tsx b/frontend/src/components/Dialog/ConfirmDialog.tsx new file mode 100644 index 000000000..a9cfb8c73 --- /dev/null +++ b/frontend/src/components/Dialog/ConfirmDialog.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { + Button, + Dialog as MuiDialog, + DialogActions, + DialogContent, + DialogTitle +} from '@mui/material'; + +type DialogComponentProps = { + isOpen: boolean; + onClose?: (...args: any[]) => void; + onConfirm: () => void; + onCancel: () => void; + title: string; + content: React.ReactNode; + disabled?: boolean; + screenWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'; +}; + +const ConfirmDialog: React.FC = ({ + isOpen, + onClose, + onConfirm, + onCancel, + title, + content, + disabled = false, + screenWidth = 'sm' +}) => { + return ( + + {title} + {content} + + + + + + ); +}; + +export default ConfirmDialog; diff --git a/frontend/src/components/Dialog/InfoDialog.tsx b/frontend/src/components/Dialog/InfoDialog.tsx new file mode 100644 index 000000000..eefacb4ff --- /dev/null +++ b/frontend/src/components/Dialog/InfoDialog.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { + Button, + Dialog, + DialogContent, + DialogTitle, + Grid +} from '@mui/material'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; + +type DialogComponentProps = { + isOpen: boolean; + handleClick: () => void; + icon: React.ReactNode; + title: React.ReactNode; + content: React.ReactNode; + screenWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'; +}; + +const InfoDialog: React.FC = ({ + isOpen, + handleClick, + title, + content, + screenWidth = 'sm' +}) => { + return ( + + + + + + + {title} + + + + {content} + + + + + + + + ); +}; + +export default InfoDialog; diff --git a/frontend/src/components/Register/RegisterForm.tsx b/frontend/src/components/Register/RegisterForm.tsx new file mode 100644 index 000000000..7b954b885 --- /dev/null +++ b/frontend/src/components/Register/RegisterForm.tsx @@ -0,0 +1,319 @@ +import React, { useEffect, useState } from 'react'; +import * as registerFormStyles from './registerFormStyle'; +import { useAuthContext } from 'context'; +import { + CircularProgress, + DialogTitle, + DialogContent, + TextField, + DialogActions, + Button +} from '@mui/material'; +import InputLabel from '@mui/material/InputLabel'; +import MenuItem from '@mui/material/MenuItem'; +import FormControl from '@mui/material/FormControl'; +import { Save } from '@mui/icons-material'; +import Select, { SelectChangeEvent } from '@mui/material/Select'; +import { User } from 'types'; + +const StyledDialog = registerFormStyles.StyledDialog; + +// State options (Keep casing as it matches region options in backend) +const STATE_OPTIONS = [ + 'Connecticut', + 'Maine', + 'Massachusetts', + 'New Hampshire', + 'Rhode Island', + 'Vermont', + 'New Jersey', + 'New York', + 'Puerto Rico', + 'Virgin Islands', + 'Delaware', + 'Maryland', + 'Pennsylvania', + 'Virginia', + 'District of Columbia', + 'West Virginia', + 'Alabama', + 'Florida', + 'Georgia', + 'Kentucky', + 'Mississippi', + 'North Carolina', + 'South Carolina', + 'Tennessee', + 'Illinois', + 'Indiana', + 'Michigan', + 'Minnesota', + 'Ohio', + 'Wisconsin', + 'Arkansas', + 'Louisiana', + 'New Mexico', + 'Oklahoma', + 'Texas', + 'Iowa', + 'Kansas', + 'Missouri', + 'Nebraska', + 'Colorado', + 'Montana', + 'North Dakota', + 'South Dakota', + 'Utah', + 'Wyoming', + 'Arizona', + 'California', + 'Hawaii', + 'Nevada', + 'Guam', + 'American Samoa', + 'Commonwealth Northern Mariana Islands', + 'Republic of Marshall Islands', + 'Federal States of Micronesia', + 'Alaska', + 'Idaho', + 'Oregon', + 'Washington' +]; + +export interface RegisterFormValues { + firstName: string; + lastName: string; + email: string; + state: string; +} + +export interface ApiResponse { + result: User; + count: number; + url?: string; +} + +export const RegisterForm: React.FC<{ + open: boolean; + onClose: () => void; +}> = ({ open, onClose }) => { + // Set default Values + const defaultValues = () => ({ + firstName: '', + lastName: '', + email: '', + state: '' + }); + + const { apiPost, setFeedbackMessage } = useAuthContext(); + console.log('apiPost: ', apiPost); + console.log('setFeedbackMessage: ', setFeedbackMessage); + + const registerUserPost = async (body: Object) => { + try { + const requestOptions: RequestInit = { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body) + }; + const response = await fetch( + process.env.REACT_APP_API_URL + '/users/register', + requestOptions + ); + const data = await response.json(); + // Handle the response data here + console.log(data); + return data; + } catch (error) { + // Handle any errors here + console.error(error); + } + }; + + const [values, setValues] = useState(defaultValues); + const [errorRequestMessage, setErrorRequestMessage] = useState(''); + const [errorEmailMessage, setEmailErrorMessage] = useState( + 'Email entry error. Please try again.' + ); + const [isLoading, setIsLoading] = useState(false); + + // Register User API Post + // const registerUserApiPost = async (body: Object) => { + // try { + // const user = await registerUserPost('/users/register', { + // body + // }); + // return user; + // } catch (e: any) { + // setFeedbackMessage({ + // message: + // e.status === 422 + // ? 'Error registering user.' + // : e.message ?? e.toString(), + // type: 'error' + // }); + // console.error(e); + // } + // }; + + const onTextChange: React.ChangeEventHandler< + HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement + > = (e) => onChange(e.target.name, e.target.value); + + const onChange = (name: string, value: any) => { + setValues((values) => ({ + ...values, + [name]: value + })); + }; + + const handleChange = (event: SelectChangeEvent) => { + setValues((values) => ({ + ...values, + [event.target.name]: event.target.value + })); + }; + + const isDisabled = () => { + if (Object.values(values).every((value) => value) && !errorEmailMessage) { + return false; + } else { + return true; + } + }; + + const onSave = async () => { + setIsLoading(true); + console.log('values: ', values); + console.log('This is where we will send the values to post.'); + const body = { + firstName: values.firstName, + lastName: values.lastName, + email: values.email, + state: values.state + }; + const registeredUser = await registerUserPost(body); + if (registeredUser !== undefined) { + console.log('User Registered Successfully'); + setIsLoading(false); + onClose(); + } else { + console.log('User Register Failed'); + setErrorRequestMessage( + 'Something went wrong registering. Please try again.' + ); + setIsLoading(false); + } + }; + + const validateEmail = (email: string) => { + // email format + // const regexEmail = /\S+@\S+\.\S+/; + const regexEmail = + /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/; + let error = false; + if (email && regexEmail.test(email)) { + setEmailErrorMessage(''); + error = false; + } else { + setEmailErrorMessage('Email is not valid.'); + error = true; + } + return error; + }; + + useEffect(() => { + if (values && values.email) { + validateEmail(values.email); + } + }, [values]); + + return ( + onClose} + aria-labelledby="form-dialog-title" + maxWidth="xs" + fullWidth + > + Register with Crossfeed + + {errorRequestMessage && ( +

{errorRequestMessage}

+ )} + + + + + State + + +
+ + + + +
+ ); +}; diff --git a/frontend/src/components/Register/index.ts b/frontend/src/components/Register/index.ts new file mode 100644 index 000000000..f4a0ba134 --- /dev/null +++ b/frontend/src/components/Register/index.ts @@ -0,0 +1 @@ +export * from './RegisterForm'; diff --git a/frontend/src/components/Register/registerFormStyle.ts b/frontend/src/components/Register/registerFormStyle.ts new file mode 100644 index 000000000..f8ee2f1fd --- /dev/null +++ b/frontend/src/components/Register/registerFormStyle.ts @@ -0,0 +1,48 @@ +import { styled } from '@mui/material/styles'; +import { Dialog } from '@mui/material'; + +const PREFIX = 'RegisterForm'; + +export const classes = { + chip: `${PREFIX}-chip`, + headerRow: `${PREFIX}-headerRow` +}; + +export const StyledDialog = styled(Dialog)(({ theme }) => ({ + [`& .${classes.chip}`]: { + backgroundColor: '#C4C4C4', + color: 'white', + marginRight: '10px' + }, + + [`& .${classes.headerRow}`]: { + padding: '0.5rem 0', + width: '100%', + display: 'flex', + alignItems: 'center', + fontSize: '16px', + flexWrap: 'wrap', + '& label': { + flex: '1 0 100%', + fontWeight: 'bolder', + display: 'flex', + alignItems: 'center', + padding: '0.5rem 0', + '@media screen and (min-width: 640px)': { + flex: '0 0 220px', + padding: 0 + } + }, + '& span': { + display: 'block', + flex: '1 1 auto', + marginLeft: 'calc(1rem + 20px)', + '@media screen and (min-width: 640px)': { + marginLeft: 'calc(1rem + 20px)' + }, + '@media screen and (min-width: 1024px)': { + marginLeft: 0 + } + } + } +})); diff --git a/frontend/src/components/Register/styles.module.scss b/frontend/src/components/Register/styles.module.scss new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/src/pages/AuthLogin/AuthLogin.tsx b/frontend/src/pages/AuthLogin/AuthLogin.tsx index d9e7f4e6e..29648418f 100644 --- a/frontend/src/pages/AuthLogin/AuthLogin.tsx +++ b/frontend/src/pages/AuthLogin/AuthLogin.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from 'react'; -import { Link } from 'react-router-dom'; import { AuthForm } from 'components'; import { Button } from '@trussworks/react-uswds'; +import { Box, Link, Typography } from '@mui/material'; import { useAuthContext } from 'context'; import { Authenticator, @@ -10,6 +10,8 @@ import { } from '@aws-amplify/ui-react'; import { I18n } from 'aws-amplify'; +import { RegisterForm } from 'components/Register/RegisterForm'; + const TOTP_ISSUER = process.env.REACT_APP_TOTP_ISSUER; // Strings come from https://github.com/aws-amplify/amplify-ui/blob/main/packages/ui/src/i18n/dictionaries/authenticator/en.ts @@ -31,6 +33,7 @@ export const AuthLogin: React.FC<{ showSignUp?: boolean }> = ({ }) => { const { apiPost, refreshUser } = useAuthContext(); const [errors, setErrors] = useState({}); + const [open, setOpen] = useState(false); // Once a user signs in, call refreshUser() so that the callback is called and the user gets signed in. const { authStatus } = useAuthenticator((context) => [context.isPending]); @@ -80,19 +83,55 @@ export const AuthLogin: React.FC<{ showSignUp?: boolean }> = ({ } }; + // const components = { + // Footer() { + // const { tokens } = useTheme(); + // return ( + // + // + // © All Rights Reserved + // + // + // ); + // }, + // } + + const onClose = () => { + setOpen(false); + }; + if (process.env.REACT_APP_USE_COGNITO) { return (

Welcome to Crossfeed

+ + {/* alert('hello')}> + Register + */} + + {open && } + + New to Crossfeed?  + setOpen(true)} + > + Register Now + + + +
**Warning**
PLATFORM NOTIFICATION
@@ -159,9 +198,13 @@ export const AuthLogin: React.FC<{ showSignUp?: boolean }> = ({ - - New to Crossfeed? Register with Login.gov - + +
New to Crossfeed? Register with Login.gov
+
+ {open && } + ); }; diff --git a/frontend/src/pages/AuthLogin/AuthLoginCreate.tsx b/frontend/src/pages/AuthLogin/AuthLoginCreate.tsx new file mode 100644 index 000000000..e20ac16de --- /dev/null +++ b/frontend/src/pages/AuthLogin/AuthLoginCreate.tsx @@ -0,0 +1,170 @@ +import React, { useEffect, useState } from 'react'; +import { AuthForm } from 'components'; +import { Button } from '@trussworks/react-uswds'; +import { Box, Link, Typography } from '@mui/material'; +import { useAuthContext } from 'context'; +import { + Authenticator, + ThemeProvider, + useAuthenticator +} from '@aws-amplify/ui-react'; +import { I18n } from 'aws-amplify'; + +import { RegisterForm } from 'components/Register/RegisterForm'; + +const TOTP_ISSUER = process.env.REACT_APP_TOTP_ISSUER; + +// Strings come from https://github.com/aws-amplify/amplify-ui/blob/main/packages/ui/src/i18n/dictionaries/authenticator/en.ts +I18n.putVocabulariesForLanguage('en-US', { + 'Setup TOTP': 'Set up 2FA', + 'Confirm TOTP Code': 'Enter 2FA Code' +}); + +const amplifyTheme = { + name: 'my-theme' +}; + +interface Errors extends Partial { + global?: string; +} + +export const AuthLoginCreate: React.FC<{ showSignUp?: boolean }> = ({ + showSignUp = false +}) => { + const { apiPost, refreshUser } = useAuthContext(); + const [errors, setErrors] = useState({}); + const [open, setOpen] = useState(false); + + // Once a user signs in, call refreshUser() so that the callback is called and the user gets signed in. + const { authStatus } = useAuthenticator((context) => [context.isPending]); + useEffect(() => { + refreshUser(); + }, [refreshUser, authStatus]); + + const formFields = { + confirmSignIn: { + confirmation_code: { + label: 'Enter 2FA Code from your authenticator app' + } + }, + confirmResetPassword: { + confirmation_code: { + label: 'Enter code sent to your email address' + } + }, + setupTOTP: { + QR: { + // Set the issuer and name so that the authenticator app shows them. + // TODO: Set the issuer to the email, once this is resolved: https://github.com/aws-amplify/amplify-ui/issues/3387. + totpIssuer: TOTP_ISSUER + // totpUsername: email, + }, + confirmation_code: { + label: + 'Set up 2FA by scanning the QR code with an authenticator app on your phone.' + } + } + }; + + const onSubmit: React.FormEventHandler = async (e) => { + e.preventDefault(); + try { + const { redirectUrl, state, nonce } = await apiPost('/auth/login', { + body: {} + }); + localStorage.setItem('state', state); + localStorage.setItem('nonce', nonce); + window.location.href = redirectUrl; + } catch (e) { + console.error(e); + setErrors({ + global: 'Something went wrong logging in.' + }); + } + }; + + const onClose = () => { + setOpen(false); + }; + + if (process.env.REACT_APP_USE_COGNITO) { + return ( + +

Welcome to Crossfeed

+ + + + {/* alert('hello')}> + Register + */} + + {open && } + + New to Crossfeed?  + setOpen(true)} + > + Register Now + + + +
**Warning**
+
+ {' '} + This system contains U.S. Government Data. Unauthorized use of this + system is prohibited. Use of this computer system, authorized or + unauthorized, constitutes consent to monitoring of this system. +
+
+ {' '} + This computer system, including all related equipment, networks, and + network devices (specifically including Internet access) are + provided only for authorized U.S. Government use. U.S. Government + computer systems may be monitored for all lawful purposes, including + to ensure that their use is authorized, for management of the + system, to facilitate protection against unauthorized access, and to + verify security procedures, survivability, and operational security. + Monitoring includes active attacks by authorized U.S. Government + entities to test or verify the security of this system. During + monitoring, information may be examined, recorded, copied and used + for authorized purposes. All information, including personal + information, placed or sent over this system may be monitored. +
+
+ {' '} + Unauthorized use may subject you to criminal prosecution. Evidence + of unauthorized use collected during monitoring may be used for + administrative, criminal, or other adverse action. Use of this + system constitutes consent to monitoring for these purposes. +
+
+
+ ); + } + + return ( + +

Welcome to Crossfeed

+ {errors.global &&

{errors.global}

} + + +
New to Crossfeed? Register with Login.gov
+
+ {open && } + +
+ ); +}; diff --git a/frontend/src/pages/RegionUsers/RegionUsers.tsx b/frontend/src/pages/RegionUsers/RegionUsers.tsx new file mode 100644 index 000000000..e5a6a95ca --- /dev/null +++ b/frontend/src/pages/RegionUsers/RegionUsers.tsx @@ -0,0 +1,463 @@ +import React, { useState, useEffect, useCallback } from 'react'; +import { initializeUser, User, Organization as OrganizationType } from 'types'; +import ConfirmDialog from 'components/Dialog/ConfirmDialog'; +import InfoDialog from 'components/Dialog/InfoDialog'; +import { useAuthContext } from 'context'; +import { Alert, Box, Button, Stack, Typography } from '@mui/material'; +import { + DataGrid, + GridColDef, + GridRenderCellParams, + GridRowSelectionModel, + GridToolbar, + useGridApiRef +} from '@mui/x-data-grid'; +import DoneIcon from '@mui/icons-material/Done'; +import { CheckCircleOutline as CheckIcon } from '@mui/icons-material'; +import CloseIcon from '@mui/icons-material/Close'; + +type DialogStates = { + isOrgDialogOpen: boolean; + isDenyDialogOpen: boolean; + isApproveDialogOpen: boolean; + isInfoDialogOpen: boolean; +}; + +type ErrorStates = { + getOrgsError: string; + getUsersError: string; + getUpdateError: string; + getDeleteError: string; +}; + +type CloseReason = 'backdropClick' | 'escapeKeyDown' | 'closeButtonClick'; + +const transformData = (data: User[]): User[] => { + return data.map(({ roles, ...user }) => ({ + ...user, + roles, + organizations: roles.map((role) => ' ' + role.organization.name) + })); +}; +export const RegionUsers: React.FC = () => { + const { apiDelete, apiGet, apiPost, apiPut, user } = useAuthContext(); + const apiRefPendingUsers = useGridApiRef(); + const apiRefCurrentUsers = useGridApiRef(); + const regionId = user?.regionId; + const getOrgsURL = `/organizations/regionId/${regionId}`; + const getUsersURL = `/v2/users?regionId=${regionId}&invitePending=`; + const pendingCols: GridColDef[] = [ + { field: 'fullName', headerName: 'Name', minWidth: 100, flex: 1 }, + { field: 'email', headerName: 'Email', minWidth: 100, flex: 2 }, + { field: 'state', headerName: 'State', minWidth: 100, flex: 1 }, + { field: 'createdAt', headerName: 'Created At', minWidth: 100, flex: 1.5 }, + { + field: 'status', + headerName: 'Registration Status', + minWidth: 250, + flex: 2, + renderCell: (cellValues: GridRenderCellParams) => { + return ( + + + + + ); + } + } + ]; + const memberCols: GridColDef[] = [ + { field: 'fullName', headerName: 'Name', minWidth: 100, flex: 1 }, + { field: 'email', headerName: 'Email', minWidth: 100, flex: 2 }, + { field: 'state', headerName: 'State', minWidth: 100, flex: 1 }, + { + field: 'lastLoggedIn', + headerName: 'Last Logged In', + minWidth: 100, + flex: 1.5 + }, + { + field: 'organizations', + headerName: 'Organizations', + minWidth: 250, + flex: 2 + } + ]; + const orgCols: GridColDef[] = [ + { field: 'name', headerName: 'Name', minWidth: 100, flex: 2 }, + { field: 'updatedAt', headerName: 'Updated At', minWidth: 100, flex: 1 }, + { field: 'stateName', headerName: 'State', minWidth: 100, flex: 1 } + ]; + const [dialogStates, setDialogStates] = useState({ + isOrgDialogOpen: false, + isDenyDialogOpen: false, + isApproveDialogOpen: false, + isInfoDialogOpen: false + }); + const [errorStates, setErrorStates] = useState({ + getOrgsError: '', + getUsersError: '', + getUpdateError: '', + getDeleteError: '' + }); + const [selectedUser, selectUser] = useState(initializeUser); + const [selectedOrgRows, selectOrgRows] = useState([]); + const [organizations, setOrganizations] = useState([]); + const [pendingUsers, setPendingUsers] = useState([]); + const [currentUsers, setCurrentUsers] = useState([]); + const [infoDialogContent, setInfoDialogContent] = useState(''); + + const fetchOrganizations = useCallback(async () => { + try { + const rows = await apiGet(getOrgsURL); + setOrganizations(rows); + setErrorStates({ ...errorStates, getOrgsError: '' }); + } catch (e: any) { + setErrorStates({ ...errorStates, getOrgsError: e.message }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [apiGet]); + const fetchPendingUsers = useCallback(async () => { + try { + const rows = await apiGet(`${getUsersURL}true`); + setPendingUsers(rows); + setErrorStates({ ...errorStates, getUsersError: '' }); + } catch (e: any) { + setErrorStates({ ...errorStates, getUsersError: e.message }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [apiGet]); + const fetchCurrentUsers = useCallback(async () => { + try { + const rows = await apiGet(`${getUsersURL}false`); + setCurrentUsers(transformData(rows)); + setErrorStates({ ...errorStates, getUsersError: '' }); + } catch (e: any) { + setErrorStates({ ...errorStates, getUsersError: e.message }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [apiGet]); + + useEffect(() => { + fetchOrganizations(); + fetchPendingUsers(); + fetchCurrentUsers(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const deleteUser = useCallback( + (userId: string): Promise => { + return apiDelete(`/users/${userId}`).then( + () => { + apiRefPendingUsers.current.updateRows([ + { id: userId, _action: 'delete' } + ]); + setPendingUsers((prevPendingUsers) => + prevPendingUsers.filter((user) => user.id !== userId) + ); + setInfoDialogContent('This user has been successfully removed.'); + return true; + }, + (e) => { + setErrorStates({ ...errorStates, getDeleteError: e.message }); + return false; + } + ); + }, // eslint-disable-next-line react-hooks/exhaustive-deps + [apiDelete] + ); + + const addOrgToUser = useCallback( + (userId: string, selectedOrgId: any): Promise => { + return apiPost(`/v2/organizations/${selectedOrgId}/users`, { + body: { userId, role: 'user' } + }).then( + (res) => { + return updateUser(userId, res.organization.name); + }, + (e) => { + setErrorStates({ ...errorStates, getUpdateError: e.message }); + return false; + } + ); + }, // eslint-disable-next-line react-hooks/exhaustive-deps + [apiPost] + ); + + const updateUser = useCallback( + (userId: string, orgName: string): Promise => { + return apiPut(`/v2/users/${userId}`, { + body: { invitePending: false } + }).then( + (res) => { + apiRefPendingUsers.current.updateRows([ + { id: userId, _action: 'delete' } + ]); + setPendingUsers((prevPendingUsers) => + prevPendingUsers.filter((user) => user.id !== userId) + ); + res['organizations'] = orgName; + apiRefCurrentUsers.current.updateRows([res]); + setCurrentUsers((prevCurrentUsers) => [...prevCurrentUsers, res]); + return sendApprovalEmail(userId); + }, + (e) => { + setErrorStates({ ...errorStates, getUpdateError: e.message }); + return false; + } + ); + }, // eslint-disable-next-line react-hooks/exhaustive-deps + [apiPut] + ); + + const sendApprovalEmail = useCallback( + (userId: string): Promise => { + return apiPut(`/users/${userId}/register/approve`).then( + (res) => { + console.log(res); + return true; + }, + (e) => { + console.log(e); + return false; + } + ); + }, // eslint-disable-next-line react-hooks/exhaustive-deps + [apiPut] + ); + + const handleCloseDialog = (value: CloseReason) => { + if (value === 'backdropClick' || value === 'escapeKeyDown') { + return; + } + setDialogStates({ + ...dialogStates, + isOrgDialogOpen: false + }); + selectUser(initializeUser); + }; + + const handleConfirmDenyClick = async () => { + const success = await deleteUser(selectedUser.id); + if (success) { + setDialogStates({ + ...dialogStates, + isDenyDialogOpen: false, + isInfoDialogOpen: true + }); + } + }; + + const handleApproveClick = (row: typeof initializeUser) => { + selectOrgRows([]); + setDialogStates({ + ...dialogStates, + isOrgDialogOpen: true + }); + selectUser(row); + }; + + const handleDenyClick = (row: typeof initializeUser) => { + setDialogStates({ + ...dialogStates, + isDenyDialogOpen: true + }); + selectUser(row); + }; + + const handleDenyCancelClick = () => { + setDialogStates((prevState) => ({ + ...prevState, + isDenyDialogOpen: false + })); + }; + + const handleApproveCancelClick = () => { + setDialogStates((prevState) => ({ + ...prevState, + isOrgDialogOpen: false + })); + selectUser(initializeUser); + }; + + const handleApproveConfirmClick = async () => { + const success = await addOrgToUser(selectedUser.id, selectedOrgRows[0]); + if (success) { + handleCloseDialog('closeButtonClick'); + setDialogStates((prevState) => ({ + ...prevState, + isInfoDialogOpen: true + })); + setInfoDialogContent( + `The user has been approved and is a member of Region ${regionId}.` + ); + } + }; + + const onRowSelectionModelChange = (newRowSelectionModel: any) => { + if (newRowSelectionModel.length > 1) { + const selectionSet = new Set(selectedOrgRows); + const result = newRowSelectionModel.filter( + (s: any) => !selectionSet.has(s) + ); + selectOrgRows(result); + } else { + selectOrgRows(newRowSelectionModel); + } + }; + if (user?.userType !== ('globalAdmin' || 'regionAdmin')) { + return ( + + Access Prohibited: You are not authorized to view this page. + Contact an administrator for access. + + ); + } + return ( + + + + + Region {regionId} Admin Dashboard + +
+ + Pending Requests + + + + + {errorStates.getUsersError && ( + + Error retrieving users from the database:{' '} + {errorStates.getUsersError} + + )} + + Members of Region {regionId} + + + + +
+
+ handleCloseDialog(reason)} + onConfirm={handleApproveConfirmClick} + onCancel={handleApproveCancelClick} + title={`Add ${selectedUser.fullName} to an organization`} + content={ + <> + + To complete the approval process, select one organization for this + user to join. + + + + + {errorStates.getOrgsError && ( + + Error retrieving organizations: {errorStates.getOrgsError} + + )} + {selectedOrgRows.length !== 0 && + errorStates.getUpdateError.length === 0 && ( + + {selectedUser.fullName} will become a member of the selected + organization. + + )} + {errorStates.getUpdateError.length !== 0 && ( + + Error updating user: {errorStates.getUpdateError}. See the + network tab for more details. + + )} + + } + disabled={selectedOrgRows.length === 0 ? true : false} + screenWidth="lg" + /> + + + Denying this request will permanently remove{' '} + {selectedUser.fullName} from the records and cannot be undone. + + {errorStates.getDeleteError && ( + + Error removing user: {errorStates.getDeleteError}. See the + network tab for more details. + + )} + + } + /> + { + setDialogStates((prevState) => ({ + ...prevState, + isInfoDialogOpen: false + })); + }} + icon={} + title={Success } + content={{infoDialogContent}} + /> +
+ ); +}; + +export default RegionUsers; diff --git a/frontend/src/pages/RegionUsers/index.ts b/frontend/src/pages/RegionUsers/index.ts new file mode 100644 index 000000000..69269a9be --- /dev/null +++ b/frontend/src/pages/RegionUsers/index.ts @@ -0,0 +1 @@ +export { default } from './RegionUsers'; diff --git a/frontend/src/pages/RegionUsers/testData.tsx b/frontend/src/pages/RegionUsers/testData.tsx new file mode 100644 index 000000000..3f0ed838f --- /dev/null +++ b/frontend/src/pages/RegionUsers/testData.tsx @@ -0,0 +1,340 @@ +import { User } from 'types'; + +export const testUsers: User[] = [ + { + id: 'abc-123', + createdAt: '2023-10-18T16:51:30.906Z', + lastLoggedIn: '2023-11-03T21:17:11.774Z', + updatedAt: '2023-11-03T21:17:11.774Z', + dateAcceptedTerms: '2023-11-03T21:17:11.774Z', + fullName: 'Joe Johnson', + firstName: 'Joe', + lastName: 'Johnson', + email: 'joe.johnson@test.gov', + invitePending: false, + userType: 'globalAdmin', + roles: [], + acceptedTermsVersion: null, + apiKeys: [], + state: 'Virginia', + regionId: '1', + organizations: [ + 'Organization 1', + 'Organization 2', + 'Organization 3', + 'Organization 4', + 'Organization 5' + ] + }, + { + id: 'def-456', + createdAt: '2023-10-18T16:51:30.906Z', + lastLoggedIn: '2023-11-03T21:17:11.774Z', + updatedAt: '2023-11-03T21:17:11.774Z', + dateAcceptedTerms: '2023-11-03T21:17:11.774Z', + fullName: 'John Smith', + firstName: 'John', + lastName: 'Smith', + email: 'john.smith@test.gov', + invitePending: true, + userType: 'globalAdmin', + roles: [], + acceptedTermsVersion: null, + apiKeys: [], + regionId: '1', + state: 'New Jersey', + organizations: [] + }, + { + id: 'ghi-789', + createdAt: '2023-10-18T16:51:30.906Z', + lastLoggedIn: '2023-11-03T21:17:11.774Z', + updatedAt: '2023-11-03T21:17:11.774Z', + dateAcceptedTerms: '2023-11-03T21:17:11.774Z', + fullName: 'Jane Doe', + firstName: 'Jane', + lastName: 'Doe', + email: 'jane.doe@test.gov', + invitePending: true, + userType: 'globalAdmin', + roles: [], + acceptedTermsVersion: null, + apiKeys: [], + regionId: '1', + state: 'Virginia', + organizations: [] + }, + { + id: 'jkl-123', + createdAt: '2023-10-18T16:51:30.906Z', + lastLoggedIn: '2023-11-03T21:17:11.774Z', + updatedAt: '2023-11-03T21:17:11.774Z', + dateAcceptedTerms: '2023-11-03T21:17:11.774Z', + fullName: 'Harry Jones', + firstName: 'Harry', + lastName: 'Jones', + email: 'harry.jones@test.gov', + invitePending: false, + userType: 'globalAdmin', + roles: [], + acceptedTermsVersion: null, + apiKeys: [], + regionId: '1', + state: 'Virginia', + organizations: [] + }, + { + id: 'mno-456', + createdAt: '2023-10-18T16:51:30.906Z', + lastLoggedIn: '2023-11-03T21:17:11.774Z', + updatedAt: '2023-11-03T21:17:11.774Z', + dateAcceptedTerms: '2023-11-03T21:17:11.774Z', + fullName: 'Ronald Potter', + firstName: 'Ronald', + lastName: 'Potter', + email: 'ronald.potter@test.gov', + invitePending: false, + userType: 'globalAdmin', + roles: [], + acceptedTermsVersion: null, + apiKeys: [], + regionId: '2', + state: 'California', + organizations: [] + } +]; +type organizations = { + id: string; + createdAt: string; + updatedAt: string; + name: string; + userType?: string; + rootDomains: Array; + ipBlocks: Array; + isPassive: boolean; + pendingDomains: Array; + userRoles: Array; + members?: number; + tags: Array; +}; +export const testOrganizations: organizations[] = [ + { + id: 'xyz-123', + createdAt: '2023-11-03T20:19:08.411Z', + updatedAt: '2023-11-03T20:19:08.411Z', + name: 'Affectionate Agency', + rootDomains: ['crossfeed.local'], + ipBlocks: [], + isPassive: false, + pendingDomains: [], + userRoles: [], + members: 5, + tags: [ + { + id: 'abc-789', + createdAt: '2023-10-03T20:38:45.889Z', + updatedAt: '2023-10-03T20:38:45.889Z', + name: 'Sample Data' + } + ] + }, + { + id: 'qrs-123', + createdAt: '2023-11-03T20:19:09.899Z', + updatedAt: '2023-11-03T20:19:09.899Z', + name: 'Boring City', + rootDomains: ['crossfeed.local'], + ipBlocks: [], + isPassive: false, + pendingDomains: [], + userRoles: [], + members: 30, + tags: [ + { + id: 'lmn-123', + createdAt: '2023-10-03T20:38:45.889Z', + updatedAt: '2023-10-03T20:38:45.889Z', + name: 'Sample Data' + } + ] + }, + { + id: 'efg-456', + createdAt: '2023-11-03T20:19:10.872Z', + updatedAt: '2023-11-03T20:19:10.872Z', + name: 'Brave Agency', + rootDomains: ['crossfeed.local'], + ipBlocks: [], + isPassive: false, + pendingDomains: [], + userRoles: [], + members: 6, + tags: [ + { + id: 'hij-789', + createdAt: '2023-10-03T20:38:45.889Z', + updatedAt: '2023-10-03T20:38:45.889Z', + name: 'Sample Data' + } + ] + }, + { + id: 'bcd-234', + createdAt: '2023-11-03T20:19:07.327Z', + updatedAt: '2023-11-03T20:19:07.327Z', + name: 'Competent County', + rootDomains: ['crossfeed.local'], + ipBlocks: [], + isPassive: false, + pendingDomains: [], + userRoles: [], + members: 15, + tags: [ + { + id: 'nop-567', + createdAt: '2023-10-03T20:38:45.889Z', + updatedAt: '2023-10-03T20:38:45.889Z', + name: 'Sample Data' + } + ] + }, + { + id: 'qrs-345', + createdAt: '2023-11-03T20:19:11.724Z', + updatedAt: '2023-11-03T20:19:11.724Z', + name: 'Condescending City', + rootDomains: ['crossfeed.local'], + ipBlocks: [], + isPassive: false, + pendingDomains: [], + userRoles: [], + members: 14, + tags: [ + { + id: 'tuv-678', + createdAt: '2023-10-03T20:38:45.889Z', + updatedAt: '2023-10-03T20:38:45.889Z', + name: 'Sample Data' + } + ] + }, + { + id: 'wxy-789', + createdAt: '2023-11-03T20:19:08.930Z', + updatedAt: '2023-11-03T20:19:08.930Z', + name: 'Distracted Agency', + rootDomains: ['crossfeed.local'], + ipBlocks: [], + isPassive: false, + pendingDomains: [], + userRoles: [], + tags: [ + { + id: 'efg-567', + createdAt: '2023-10-03T20:38:45.889Z', + updatedAt: '2023-10-03T20:38:45.889Z', + name: 'Sample Data' + } + ] + }, + { + id: 'hij-123', + createdAt: '2023-11-03T20:19:07.901Z', + updatedAt: '2023-11-03T20:19:07.901Z', + name: 'Optimistic County', + rootDomains: ['crossfeed.local'], + ipBlocks: [], + isPassive: false, + pendingDomains: [], + userRoles: [], + members: 12, + tags: [ + { + id: 'cde-234', + createdAt: '2023-10-03T20:38:45.889Z', + updatedAt: '2023-10-03T20:38:45.889Z', + name: 'Sample Data' + } + ] + }, + { + id: 'rst-123', + createdAt: '2023-11-03T20:19:10.343Z', + updatedAt: '2023-11-03T20:19:10.343Z', + name: 'Peaceful Department', + rootDomains: ['crossfeed.local'], + ipBlocks: [], + isPassive: false, + pendingDomains: [], + userRoles: [], + members: 0, + tags: [ + { + id: 'efg-567', + createdAt: '2023-10-03T20:38:45.889Z', + updatedAt: '2023-10-03T20:38:45.889Z', + name: 'Sample Data' + } + ] + }, + { + id: 'klm-456', + createdAt: '2023-11-03T20:19:11.296Z', + updatedAt: '2023-11-03T20:19:11.296Z', + name: 'Sharp County', + rootDomains: ['crossfeed.local'], + ipBlocks: [], + isPassive: false, + pendingDomains: [], + userRoles: [], + members: 30, + tags: [ + { + id: 'lmn-567', + createdAt: '2023-10-03T20:38:45.889Z', + updatedAt: '2023-10-03T20:38:45.889Z', + name: 'Sample Data' + } + ] + }, + { + id: 'ghi-678', + createdAt: '2023-11-03T20:19:06.677Z', + updatedAt: '2023-11-03T20:19:06.677Z', + name: 'Tender Department', + rootDomains: ['crossfeed.local'], + ipBlocks: [], + isPassive: false, + pendingDomains: [], + userRoles: [], + members: 10, + tags: [ + { + id: 'ijk-789', + createdAt: '2023-10-03T20:38:45.889Z', + updatedAt: '2023-10-03T20:38:45.889Z', + name: 'Sample Data' + } + ] + }, + { + id: 'mno-345', + createdAt: '2023-11-03T20:19:09.380Z', + updatedAt: '2023-11-03T20:19:09.380Z', + name: 'Zealous Agency', + rootDomains: ['crossfeed.local'], + ipBlocks: [], + isPassive: false, + pendingDomains: [], + userRoles: [], + members: 3, + tags: [ + { + id: 'stu-234', + createdAt: '2023-10-03T20:38:45.889Z', + updatedAt: '2023-10-03T20:38:45.889Z', + name: 'Sample Data' + } + ] + } +]; diff --git a/frontend/src/pages/Users/Users.tsx b/frontend/src/pages/Users/Users.tsx index 89703c053..d6692fef9 100644 --- a/frontend/src/pages/Users/Users.tsx +++ b/frontend/src/pages/Users/Users.tsx @@ -143,7 +143,6 @@ export const Users: React.FC = () => { email: '', userType: '' }); - const userSearch = useCallback( async ({ sort, diff --git a/frontend/src/pages/index.ts b/frontend/src/pages/index.ts index 70b428be4..c33920022 100644 --- a/frontend/src/pages/index.ts +++ b/frontend/src/pages/index.ts @@ -1,4 +1,5 @@ export * from './AuthLogin'; +export * from './AuthLogin/AuthLoginCreate'; export * from './AuthCreateAccount'; export * from './Domains'; export * from './Domain'; @@ -16,3 +17,4 @@ export { default as Users } from './Users'; export { default as Settings } from './Settings'; export { default as Feeds } from './Feeds'; export { default as Reports } from './Reports'; +export { default as RegionUsers } from './RegionUsers'; diff --git a/frontend/src/test-utils/organization.ts b/frontend/src/test-utils/organization.ts index 60042b454..7b60b74b0 100644 --- a/frontend/src/test-utils/organization.ts +++ b/frontend/src/test-utils/organization.ts @@ -10,5 +10,13 @@ export const testOrganization = { tags: [], parent: null, children: [], - pendingDomains: [] + pendingDomains: [], + country: 'US', + state: 'LA', + stateFips: 22, + stateName: 'Louisiana', + county: 'Calcasieu Parish', + countyFips: 22019, + acronym: 'TEST', + type: 'Federal' }; diff --git a/frontend/src/test-utils/user.ts b/frontend/src/test-utils/user.ts index 9cbbb4ee4..c74bafbd8 100644 --- a/frontend/src/test-utils/user.ts +++ b/frontend/src/test-utils/user.ts @@ -15,5 +15,7 @@ export const testUser: AuthUser = { dateAcceptedTerms: new Date().toISOString(), acceptedTermsVersion: 'v1-user', isRegistered: true, - apiKeys: [] + apiKeys: [], + regionId: '1234', + state: 'LA' }; diff --git a/frontend/src/types/organization.ts b/frontend/src/types/organization.ts index cf484f838..a544ba00b 100644 --- a/frontend/src/types/organization.ts +++ b/frontend/src/types/organization.ts @@ -15,6 +15,14 @@ export interface Organization { parent: Organization | null; children: Organization[]; pendingDomains: PendingDomain[]; + country?: string; + state: string; + stateFips: number; + stateName: string; + county: string; + countyFips: number; + acronym: string; + type: string; } export interface PendingDomain { diff --git a/frontend/src/types/user.ts b/frontend/src/types/user.ts index eb9678619..327f6e877 100644 --- a/frontend/src/types/user.ts +++ b/frontend/src/types/user.ts @@ -9,11 +9,36 @@ export interface User { lastName: string; fullName: string; invitePending: boolean; - userType: 'standard' | 'globalView' | 'globalAdmin'; + userType: 'standard' | 'globalView' | 'globalAdmin' | 'regionalAdmin'; email: string; roles: Role[]; dateAcceptedTerms: string | null; acceptedTermsVersion: string | null; lastLoggedIn: string | null; apiKeys: ApiKey[]; + regionId?: string | null; + state?: string | null; + organizations?: Array; + isRegistered?: boolean | null; } + +export const initializeUser: User = { + id: '', + createdAt: '', + updatedAt: '', + firstName: '', + lastName: '', + fullName: '', + invitePending: true, + userType: 'standard', + email: '', + roles: [], + dateAcceptedTerms: null, + acceptedTermsVersion: null, + lastLoggedIn: null, + apiKeys: [], + regionId: null, + state: null, + organizations: [], + isRegistered: null +}; diff --git a/infrastructure/alarms.tf b/infrastructure/alarms.tf deleted file mode 100644 index c654bbbc5..000000000 --- a/infrastructure/alarms.tf +++ /dev/null @@ -1,20 +0,0 @@ -resource "aws_cloudwatch_log_metric_filter" "cloudwatch1" { - log_group_name = var.cloudtrail_log_group_name - name = var.log_metric_name_cloudwatch1 - pattern = "{$.userIdentity.type=\"Root\" && $.userIdentity.invokedBy NOT EXISTS && $.eventType !=\"AwsServiceEvent\"}" - metric_transformation { - name = var.log_metric_name_cloudwatch1 - namespace = var.log_metric_namespace_cloudwatch - default_value = 0 - value = 1 - } -} - -resource "aws_cloudwatch_metric_alarm" "cloudwatch1" { - alarm_name = "${var.log_metric_name_cloudwatch1}-alarm" - metric_name = var.log_metric_name_cloudwatch1 - alarm_actions = [aws_sns_topic.alarms.arn] - comparison_operator = "GreaterThanOrEqualToThreshold" - evaluation_periods = 1 - threshold = 1 -} \ No newline at end of file diff --git a/infrastructure/cloudwatch.tf b/infrastructure/cloudwatch.tf index 83711d6fd..dd8fdb2d0 100644 --- a/infrastructure/cloudwatch.tf +++ b/infrastructure/cloudwatch.tf @@ -1,8 +1,8 @@ resource "aws_s3_bucket" "cloudwatch_bucket" { bucket = var.cloudwatch_bucket_name tags = { - project = var.project - stage = var.stage + Project = var.project + Stage = var.stage } } @@ -12,8 +12,8 @@ resource "aws_cloudwatch_log_group" "cloudwatch_bucket" { retention_in_days = 365 kms_key_id = aws_kms_key.key.arn tags = { - project = var.project - stage = var.stage + Project = var.project + Stage = var.stage } } diff --git a/infrastructure/log_alarms.tf b/infrastructure/log_alarms.tf new file mode 100644 index 000000000..e75aeb2cc --- /dev/null +++ b/infrastructure/log_alarms.tf @@ -0,0 +1,305 @@ +resource "aws_cloudwatch_metric_alarm" "root_user" { + alarm_name = "${var.log_metric_root_user}-alarm" + metric_name = var.log_metric_root_user + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_high + } +} + +resource "aws_cloudwatch_metric_alarm" "unauthorized_api_call" { + alarm_name = "${var.log_metric_unauthorized_api_call}-alarm" + metric_name = var.log_metric_unauthorized_api_call + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_low + } +} + +resource "aws_cloudwatch_metric_alarm" "login_without_mfa" { + alarm_name = "${var.log_metric_login_without_mfa}-alarm" + metric_name = var.log_metric_login_without_mfa + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_high + } +} + +resource "aws_cloudwatch_metric_alarm" "iam_policy" { + alarm_name = "${var.log_metric_iam_policy}-alarm" + metric_name = var.log_metric_iam_policy + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_high + } +} + +resource "aws_cloudwatch_metric_alarm" "cloudtrail" { + alarm_name = "${var.log_metric_cloudtrail}-alarm" + metric_name = var.log_metric_cloudtrail + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_high + } +} + +resource "aws_cloudwatch_metric_alarm" "login_failure" { + alarm_name = "${var.log_metric_login_failure}-alarm" + metric_name = var.log_metric_login_failure + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_low + } +} + +resource "aws_cloudwatch_metric_alarm" "cmk_delete_disable" { + alarm_name = "${var.log_metric_cmk_delete_disable}-alarm" + metric_name = var.log_metric_cmk_delete_disable + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_critical + } +} + +resource "aws_cloudwatch_metric_alarm" "s3_bucket_policy" { + alarm_name = "${var.log_metric_s3_bucket_policy}-alarm" + metric_name = var.log_metric_s3_bucket_policy + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_high + } +} + +resource "aws_cloudwatch_metric_alarm" "aws_config" { + alarm_name = "${var.log_metric_aws_config}-alarm" + metric_name = var.log_metric_aws_config + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_high + } +} + +resource "aws_cloudwatch_metric_alarm" "security_group" { + alarm_name = "${var.log_metric_security_group}-alarm" + metric_name = var.log_metric_security_group + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_high + } +} + +resource "aws_cloudwatch_metric_alarm" "nacl" { + alarm_name = "${var.log_metric_nacl}-alarm" + metric_name = var.log_metric_nacl + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_high + } +} + +resource "aws_cloudwatch_metric_alarm" "network_gateway" { + alarm_name = "${var.log_metric_network_gateway}-alarm" + metric_name = var.log_metric_network_gateway + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_high + } +} + +resource "aws_cloudwatch_metric_alarm" "route_table" { + alarm_name = "${var.log_metric_route_table}-alarm" + metric_name = var.log_metric_route_table + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_high + } +} + +resource "aws_cloudwatch_metric_alarm" "vpc" { + alarm_name = "${var.log_metric_vpc}-alarm" + metric_name = var.log_metric_vpc + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_high + } +} + +resource "aws_cloudwatch_metric_alarm" "ec2_shutdown" { + alarm_name = "${var.log_metric_ec2_shutdown}-alarm" + metric_name = var.log_metric_ec2_shutdown + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_critical + } +} + +resource "aws_cloudwatch_metric_alarm" "db_shutdown" { + alarm_name = "${var.log_metric_db_shutdown}-alarm" + metric_name = var.log_metric_db_shutdown + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_critical + } +} + +resource "aws_cloudwatch_metric_alarm" "db_deletion" { + alarm_name = "${var.log_metric_db_deletion}-alarm" + metric_name = var.log_metric_db_deletion + namespace = var.log_metric_namespace + alarm_actions = [aws_sns_topic.alarms.arn] + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = 1 + period = 60 + threshold = 1 + statistic = "Sum" + + tags = { + Project = var.project + Stage = var.stage + Severity = var.severity_critical + } +} \ No newline at end of file diff --git a/infrastructure/log_filters.tf b/infrastructure/log_filters.tf new file mode 100644 index 000000000..7c84e9614 --- /dev/null +++ b/infrastructure/log_filters.tf @@ -0,0 +1,203 @@ +resource "aws_cloudwatch_log_metric_filter" "root_user" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_root_user + pattern = "{$.userIdentity.type=\"Root\" && $.userIdentity.invokedBy NOT EXISTS && $.eventType !=\"AwsServiceEvent\"}" + metric_transformation { + name = var.log_metric_root_user + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "unauthorized_api_call" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_unauthorized_api_call + pattern = "{($.errorCode=\"*UnauthorizedOperation\") || ($.errorCode=\"AccessDenied*\")}" + metric_transformation { + name = var.log_metric_unauthorized_api_call + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "login_without_mfa" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_login_without_mfa + pattern = "{($.eventName=\"ConsoleLogin\") && ($.additionalEventData.MFAUsed !=\"Yes\") && ($.userIdentity.type=\"IAMUser\") && ($.responseElements.ConsoleLogin=\"Success\")}" + metric_transformation { + name = var.log_metric_login_without_mfa + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "iam_policy" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_iam_policy + pattern = "{($.eventSource=iam.amazonaws.com) && (($.eventName=DeleteGroupPolicy) || ($.eventName=DeleteRolePolicy) || ($.eventName=DeleteUserPolicy) || ($.eventName=PutGroupPolicy) || ($.eventName=PutRolePolicy) || ($.eventName=PutUserPolicy) || ($.eventName=CreatePolicy) || ($.eventName=DeletePolicy) || ($.eventName=CreatePolicyVersion) || ($.eventName=DeletePolicyVersion) || ($.eventName=AttachRolePolicy) || ($.eventName=DetachRolePolicy) || ($.eventName=AttachUserPolicy) || ($.eventName=DetachUserPolicy) || ($.eventName=AttachGroupPolicy) || ($.eventName=DetachGroupPolicy))}" + metric_transformation { + name = var.log_metric_iam_policy + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "cloudtrail" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_cloudtrail + pattern = "{($.eventName=CreateTrail) || ($.eventName=UpdateTrail) || ($.eventName=DeleteTrail) || ($.eventName=StartLogging) || ($.eventName=StopLogging)}" + metric_transformation { + name = var.log_metric_cloudtrail + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "login_failure" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_login_failure + pattern = "{($.eventName=ConsoleLogin) && ($.errorMessage=\"Failed authentication\")}" + metric_transformation { + name = var.log_metric_login_failure + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "cmk_delete_disable" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_cmk_delete_disable + pattern = "{($.eventSource=kms.amazonaws.com) && (($.eventName=DisableKey) || ($.eventName=ScheduleKeyDeletion))}" + metric_transformation { + name = var.log_metric_cmk_delete_disable + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "s3_bucket_policy" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_s3_bucket_policy + pattern = "{($.eventSource=s3.amazonaws.com) && (($.eventName=PutBucketAcl) || ($.eventName=PutBucketPolicy) || ($.eventName=PutBucketCors) || ($.eventName=PutBucketLifecycle) || ($.eventName=PutBucketReplication) || ($.eventName=DeleteBucketPolicy) || ($.eventName=DeleteBucketCors) || ($.eventName=DeleteBucketLifecycle) || ($.eventName=DeleteBucketReplication))}" + metric_transformation { + name = var.log_metric_s3_bucket_policy + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "aws_config" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_aws_config + pattern = "{($.eventSource=config.amazonaws.com) && (($.eventName=StopConfigurationRecorder) || ($.eventName=DeleteDeliveryChannel) || ($.eventName=PutDeliveryChannel) || ($.eventName=PutConfigurationRecorder))}" + metric_transformation { + name = var.log_metric_aws_config + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "security_group" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_security_group + pattern = "{($.eventName=AuthorizeSecurityGroupIngress) || ($.eventName=AuthorizeSecurityGroupEgress) || ($.eventName=RevokeSecurityGroupIngress) || ($.eventName=RevokeSecurityGroupEgress) || ($.eventName=CreateSecurityGroup) || ($.eventName=DeleteSecurityGroup)}" + metric_transformation { + name = var.log_metric_security_group + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "nacl" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_nacl + pattern = "{($.eventName=CreateNetworkAcl) || ($.eventName=CreateNetworkAclEntry) || ($.eventName=DeleteNetworkAcl) || ($.eventName=DeleteNetworkAclEntry) || ($.eventName=ReplaceNetworkAclEntry) || ($.eventName=ReplaceNetworkAclAssociation)}" + metric_transformation { + name = var.log_metric_nacl + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "network_gateway" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_network_gateway + pattern = "{($.eventName=CreateCustomerGateway) || ($.eventName=DeleteCustomerGateway) || ($.eventName=AttachInternetGateway) || ($.eventName=CreateInternetGateway) || ($.eventName=DeleteInternetGateway) || ($.eventName=DetachInternetGateway)}" + metric_transformation { + name = var.log_metric_network_gateway + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "route_table" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_route_table + pattern = "{($.eventSource=ec2.amazonaws.com) && (($.eventName=CreateRoute) || ($.eventName=CreateRouteTable) || ($.eventName=ReplaceRoute) || ($.eventName=ReplaceRouteTableAssociation) || ($.eventName=DeleteRouteTable) || ($.eventName=DeleteRoute) || ($.eventName=DisassociateRouteTable))}" + metric_transformation { + name = var.log_metric_route_table + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "vpc" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_vpc + pattern = "{($.eventName=CreateVpc) || ($.eventName=DeleteVpc) || ($.eventName=ModifyVpcAttribute) || ($.eventName=AcceptVpcPeeringConnection) || ($.eventName=CreateVpcPeeringConnection) || ($.eventName=DeleteVpcPeeringConnection) || ($.eventName=RejectVpcPeeringConnection) || ($.eventName=AttachClassicLinkVpc) || ($.eventName=DetachClassicLinkVpc) || ($.eventName=DisableVpcClassicLink) || ($.eventName=EnableVpcClassicLink)}" + metric_transformation { + name = var.log_metric_vpc + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "ec2_shutdown" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_ec2_shutdown + pattern = "{($.eventName=StopInstances) || ($.eventName=TerminateInstances)}" + metric_transformation { + name = var.log_metric_ec2_shutdown + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "db_shutdown" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_db_shutdown + pattern = "{$.eventName=StopDBInstance}" + metric_transformation { + name = var.log_metric_db_shutdown + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} + +resource "aws_cloudwatch_log_metric_filter" "db_deletion" { + log_group_name = var.cloudtrail_log_group_name + name = var.log_metric_db_deletion + pattern = "{$.eventName=DeleteDBInstance}" + metric_transformation { + name = var.log_metric_db_deletion + namespace = var.log_metric_namespace + default_value = 0 + value = 1 + } +} \ No newline at end of file diff --git a/infrastructure/pe_worker.tf b/infrastructure/pe_worker.tf index cf039c9fd..8c0c1d6d7 100644 --- a/infrastructure/pe_worker.tf +++ b/infrastructure/pe_worker.tf @@ -177,7 +177,20 @@ resource "aws_ecs_service" "shodan_service" { launch_type = "FARGATE" desired_count = 0 # Initially set to 0, plan to start it dynamically network_configuration { - subnets = aws_subnet.worker.*.id - security_groups = [aws_security_group.worker.id] + subnets = [aws_subnet.worker.id] + security_groups = [aws_security_group.worker.id] + assign_public_ip = true + } +} + +# Create the log group +resource "aws_cloudwatch_log_group" "pe_worker" { + name = var.pe_worker_ecs_log_group_name + retention_in_days = 3653 + kms_key_id = aws_kms_key.key.arn + tags = { + Project = var.project + Stage = var.stage + Owner = "Crossfeed managed resource" } } \ No newline at end of file diff --git a/infrastructure/prod.tfvars b/infrastructure/prod.tfvars index 03ed42db1..c6e1607b3 100644 --- a/infrastructure/prod.tfvars +++ b/infrastructure/prod.tfvars @@ -10,6 +10,25 @@ db_name = "crossfeed-prod-db2" db_port = 5432 db_table_name = "cfproddb" db_instance_class = "db.t3.2xlarge" +log_metric_namespace = "LogMetrics" +log_metric_root_user = "crossfeed-prod-RootUserAccess" +log_metric_unauthorized_api_call = "crossfeed-prod-UnauthorizedApiCall" +log_metric_login_without_mfa = "crossfeed-prod-ConsoleSignInWithoutMFA" +log_metric_iam_policy = "crossfeed-prod-IAMPolicyChange" +log_metric_cloudtrail = "crossfeed-prod-CloudTrailConfigurationChanges" +log_metric_login_failure = "crossfeed-prod-ConsoleLoginFailure" +log_metric_cmk_delete_disable = "crossfeed-prod-DisablingOrScheduledDeletionOfCMK" +log_metric_s3_bucket_policy = "crossfeed-prod-S3BucketPolicyChanges" +log_metric_aws_config = "crossfeed-prod-AWSConfigConfigurationChange" +log_metric_security_group = "crossfeed-prod-SecurityGroupChange" +log_metric_nacl = "crossfeed-prod-NACLChange" +log_metric_network_gateway = "crossfeed-prod-NetworkGatewayChange" +log_metric_route_table = "crossfeed-prod-RouteTableChange" +log_metric_vpc = "crossfeed-prod-VPCChange" +log_metric_ec2_shutdown = "crossfeed-prod-EC2Shutdown" +log_metric_db_shutdown = "crossfeed-prod-DBShutdown" +log_metric_db_deletion = "crossfeed-prod-DBDeletion" +sns_topic_alarms = "crossfeed-prod-cis-alarms" ssm_lambda_subnet = "/crossfeed/prod/SUBNET_ID" ssm_lambda_sg = "/crossfeed/prod/SG_ID" ssm_worker_subnet = "/crossfeed/prod/WORKER_SUBNET_ID" diff --git a/infrastructure/sns.tf b/infrastructure/sns.tf index cb0dd789b..58fbe284f 100644 --- a/infrastructure/sns.tf +++ b/infrastructure/sns.tf @@ -1,3 +1,9 @@ resource "aws_sns_topic" "alarms" { - name = var.sns_topic_alarms + name = var.sns_topic_alarms + kms_master_key_id = "alias/aws/sns" + + tags = { + Project = var.project + Stage = var.stage + } } diff --git a/infrastructure/stage.tfvars b/infrastructure/stage.tfvars index 615feddbd..4fb8988b8 100644 --- a/infrastructure/stage.tfvars +++ b/infrastructure/stage.tfvars @@ -10,6 +10,25 @@ db_name = "crossfeed-stage-db" db_port = 5432 db_table_name = "cfstagingdb" db_instance_class = "db.t3.2xlarge" +log_metric_namespace = "LogMetrics" +log_metric_root_user = "crossfeed-staging-RootUserAccess" +log_metric_unauthorized_api_call = "crossfeed-staging-UnauthorizedApiCall" +log_metric_login_without_mfa = "crossfeed-staging-ConsoleSignInWithoutMFA" +log_metric_iam_policy = "crossfeed-staging-IAMPolicyChange" +log_metric_cloudtrail = "crossfeed-staging-CloudTrailConfigurationChanges" +log_metric_login_failure = "crossfeed-staging-ConsoleLoginFailure" +log_metric_cmk_delete_disable = "crossfeed-staging-DisablingOrScheduledDeletionOfCMK" +log_metric_s3_bucket_policy = "crossfeed-staging-S3BucketPolicyChanges" +log_metric_aws_config = "crossfeed-staging-AWSConfigConfigurationChange" +log_metric_security_group = "crossfeed-staging-SecurityGroupChange" +log_metric_nacl = "crossfeed-staging-NACLChange" +log_metric_network_gateway = "crossfeed-staging-NetworkGatewayChange" +log_metric_route_table = "crossfeed-staging-RouteTableChange" +log_metric_vpc = "crossfeed-staging-VPCChange" +log_metric_ec2_shutdown = "crossfeed-staging-EC2Shutdown" +log_metric_db_shutdown = "crossfeed-staging-DBShutdown" +log_metric_db_deletion = "crossfeed-staging-DBDeletion" +sns_topic_alarms = "crossfeed-staging-cis-alarms" ssm_lambda_subnet = "/crossfeed/staging/SUBNET_ID" ssm_lambda_sg = "/crossfeed/staging/SG_ID" ssm_worker_subnet = "/crossfeed/staging/WORKER_SUBNET_ID" diff --git a/infrastructure/vars.tf b/infrastructure/vars.tf index 4984d48dd..6ef316437 100644 --- a/infrastructure/vars.tf +++ b/infrastructure/vars.tf @@ -64,18 +64,114 @@ variable "frontend_cert_arn" { default = "arn:aws:acm:us-east-1:563873274798:certificate/7c6a5980-80e3-47a4-9f21-cbda44b6f34c" } -variable "log_metric_namespace_cloudwatch" { +variable "log_metric_namespace" { description = "log_metric_namespace" type = string - default = "crossfeed-staging-cloudwatch-controls" + default = "LogMetrics" } -variable "log_metric_name_cloudwatch1" { - description = "log_metric_filter_cloudwatch1" +variable "log_metric_root_user" { + description = "log_metric_filter_root_user" type = string default = "crossfeed-staging-RootUserAccess" } +variable "log_metric_unauthorized_api_call" { + description = "log_metric_filter_unauthorized_api_call" + type = string + default = "crossfeed-staging-UnauthorizedAPICall" +} + +variable "log_metric_login_without_mfa" { + description = "log_metric_filter_login_without_mfa" + type = string + default = "crossfeed-staging-ConsoleLoginWithoutMFA" +} + +variable "log_metric_iam_policy" { + description = "log_metric_filter_iam_policy" + type = string + default = "crossfeed-staging-IAMPolicyChange" +} + +variable "log_metric_cloudtrail" { + description = "log_metric_filter_cloudtrail" + type = string + default = "crossfeed-staging-CloudTrailConfigurationChange" +} + +variable "log_metric_login_failure" { + description = "log_metric_filter_login_failure" + type = string + default = "crossfeed-staging-ConsoleLoginFailure" +} + +variable "log_metric_cmk_delete_disable" { + description = "log_metric_filter_cmk_delete_disable" + type = string + default = "crossfeed-staging-DisablingOrScheduledDeletionOfCMK" +} + +variable "log_metric_s3_bucket_policy" { + description = "log_metric_filter_s3_bucket_policy" + type = string + default = "crossfeed-staging-S3BucketPolicyChange" +} + +variable "log_metric_aws_config" { + description = "log_metric_filter_aws_config" + type = string + default = "crossfeed-staging-AWSConfigConfigurationChange" +} + +variable "log_metric_security_group" { + description = "log_metric_filter_security_group" + type = string + default = "crossfeed-staging-SecurityGroupChange" +} + +variable "log_metric_nacl" { + description = "log_metric_filter_nacl" + type = string + default = "crossfeed-staging-NACLChange" +} + +variable "log_metric_network_gateway" { + description = "log_metric_filter_network_gateway" + type = string + default = "crossfeed-staging-NetworkGatewayChange" +} + +variable "log_metric_route_table" { + description = "log_metric_filter_route_table" + type = string + default = "crossfeed-staging-RouteTableChange" +} + +variable "log_metric_vpc" { + description = "log_metric_filter_vpc" + type = string + default = "crossfeed-staging-VPCChange" +} + +variable "log_metric_ec2_shutdown" { + description = "log_metric_filter_ec2_shutdown" + type = string + default = "crossfeed-staging-EC2Shutdown" +} + +variable "log_metric_db_shutdown" { + description = "log_metric_filter_DB_shutdown" + type = string + default = "crossfeed-staging-DBShutdown" +} + +variable "log_metric_db_deletion" { + description = "log_metric_filter_db_deletion" + type = string + default = "crossfeed-staging-DBDeletion" +} + variable "sns_topic_alarms" { description = "sns_alarm_topic_name" type = string @@ -454,6 +550,29 @@ variable "create_elk_instance" { default = false } +variable "severity_critical" { + description = "severity_critical" + type = string + default = "CRITICAL" +} + +variable "severity_high" { + description = "severity_high" + type = string + default = "HIGH" +} + +variable "severity_medium" { + description = "severity_medium" + type = string + default = "MEDIUM" +} + +variable "severity_low" { + description = "severity_low" + type = string + default = "LOW" +} variable "pe_worker_ecs_repository_name" { description = "pe_worker_ecs_repository_name" type = string diff --git a/package-lock.json b/package-lock.json index 6e38bf150..4495e8ef0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -176,12 +176,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.3", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -440,9 +440,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -643,19 +643,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", + "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/generator": "^7.23.3", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", + "@babel/parser": "^7.23.3", + "@babel/types": "^7.23.3", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -673,9 +673,9 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", diff --git a/rebuild.sh b/rebuild.sh new file mode 100755 index 000000000..d41a5fae9 --- /dev/null +++ b/rebuild.sh @@ -0,0 +1 @@ +docker-compose up -d --build \ No newline at end of file