diff --git a/.codeclimate.yml b/.codeclimate.yml
index 8641ebe8a0..fcbdd16bea 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -28,7 +28,6 @@ exclude_patterns:
- "src/"
- "services/.sechub/"
- "services/database/"
- - "services/stream-functions/"
- "services/ui/"
- "services/ui-src/public/"
- "services/ui-src/src/libs/"
diff --git a/.env_example b/.env_example
index ea3ba2a632..70423f3516 100644
--- a/.env_example
+++ b/.env_example
@@ -22,4 +22,6 @@ MEASURE_TABLE_NAME=local-measures
# LAUNCHDARKLY
LD_PROJECT_KEY=mdct-qmr
-LD_SDK_KEY=sdk-25b0f45f-bb20-4223-aede-32d66b525721 #pragma allowlist secret
+LD_SDK_KEY=sdk-25b0f45f-bb20-4223-aede-32d66b525721 #pragma: allowlist secret
+
+docraptorApiKey=YOUR_API_KEY_HERE #pragma: allowlist secret
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 3ed3821a5a..b7882841fa 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,5 +1,5 @@
# Default repo owners
-* @BearHanded @braxex @cassandradanger
+* @BearHanded @braxex @ailZhou
# Rate label file changes require application owner approval
*rateLabelText.ts @davidkoger
diff --git a/.github/branchNameValidation.sh b/.github/branchNameValidation.sh
index 3f488c5fd0..e071a65aed 100755
--- a/.github/branchNameValidation.sh
+++ b/.github/branchNameValidation.sh
@@ -4,10 +4,18 @@ set -e
local_branch=${1}
-valid_branch="^[a-z][a-z-0-9-]*$"
+valid_branch='^[a-z][a-z0-9-]*$'
+reserved_words=(
+ cognito
+)
-if [[ ! $local_branch =~ $valid_branch ]] && [[ $local_branch -gt 128 ]]; then
+join_by() { local IFS='|'; echo "$*"; }
+
+#creates glob match to check for reserved words used in branch names which would trigger failures
+glob=$(join_by $(for i in ${reserved_words[@]}; do echo "^$i-|-$i$|-$i-|^$i$"; done;))
+
+if [[ ! $local_branch =~ $valid_branch ]] || [[ $local_branch =~ $glob ]] || [[ ${#local_branch} -gt 64 ]]; then
echo """
------------------------------------------------------------------------------------------------------------------------------
ERROR: Please read below
@@ -28,4 +36,4 @@ if [[ ! $local_branch =~ $valid_branch ]] && [[ $local_branch -gt 128 ]]; then
exit 1
fi
-exit 0
\ No newline at end of file
+exit 0
diff --git a/.github/build_vars.sh b/.github/build_vars.sh
index 1b5c603cba..380179a9cb 100755
--- a/.github/build_vars.sh
+++ b/.github/build_vars.sh
@@ -13,12 +13,14 @@ set_value() {
if [ ! -z "${!varname}" ]; then
echo "Setting $varname"
echo "${varname}=${!varname}" >> $GITHUB_ENV
+ echo "${varname}=${!varname}" >> $GITHUB_OUTPUT
fi
}
set_name() {
varname=${1}
echo "BRANCH_SPECIFIC_VARNAME_$varname=${branch_name//-/_}_$varname" >> $GITHUB_ENV
+ echo "BRANCH_SPECIFIC_VARNAME_$varname=${branch_name//-/_}_$varname" >> $GITHUB_OUTPUT
}
action=${1}
diff --git a/.github/setBranchName.sh b/.github/setBranchName.sh
new file mode 100755
index 0000000000..00ffd55ffe
--- /dev/null
+++ b/.github/setBranchName.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+set -e
+
+GITHUB_REFNAME="${1}"
+
+[ -z "${GITHUB_REFNAME}" ] && echo "Error setting branch name. No input given." && exit 1
+
+case ${GITHUB_REFNAME} in
+ $([[ "$GITHUB_REFNAME" =~ ^dependabot/.* ]] && echo ${GITHUB_REFNAME}))
+ echo ${GITHUB_REFNAME} | md5sum | head -c 10 | sed 's/^/x/'
+ ;;
+ $([[ "$GITHUB_REFNAME" =~ ^snyk-* ]] && echo ${GITHUB_REFNAME}))
+ echo ${GITHUB_REFNAME##*-} | head -c 10 | sed 's/^/s/'
+ ;;
+ *)
+ echo ${GITHUB_REFNAME}
+ ;;
+esac
\ No newline at end of file
diff --git a/.github/waf-controller.sh b/.github/waf-controller.sh
new file mode 100755
index 0000000000..3bdd242e38
--- /dev/null
+++ b/.github/waf-controller.sh
@@ -0,0 +1,132 @@
+#!/usr/bin/env bash
+
+CIRCUIT_BREAKER=10
+AWS_RETRY_ERROR=254
+AWS_THROTTLING_EXCEPTION=252
+#0, 1, 2 are the levels of debug, with 0 being off
+DEBUG=1
+
+set -o pipefail -o nounset -u
+
+case ${1} in
+ append)
+ OP=append
+ ;;
+ set)
+ OP=set
+ ;;
+ *)
+ echo "Error: unkown operation"
+ echo "Usage: ${0} [append|set]" && exit 1
+ ;;
+esac
+
+shift
+NAME="${1}"
+ID="${2}"
+shift; shift
+RUNNER_CIDRS="${@}"
+
+[[ $DEBUG -ge 1 ]] && echo "Inputs: NAME ${NAME}, ID ${ID}, RUNNER_CIDRS ${RUNNER_CIDRS}"
+
+#Exponential backoff with jitter
+jitter() {
+ #.5 seconds
+ SHORTEST=50
+ #10 seconds
+ LONGEST=1000
+ DIV=100
+ EXP=$(perl -e "use bigint; print $SHORTEST**$1")
+ MIN=$(($EXP>$LONGEST ? $LONGEST : $EXP))
+ RND=$(shuf -i$SHORTEST-$MIN -n1)
+ perl -e "print $RND/$DIV"
+}
+
+#Attempt to avoid resource contention from the start
+sleep $(jitter $(shuf -i1-10 -n1))
+
+for ((i=1; i <= $CIRCUIT_BREAKER; i++)); do
+ #This loop is ONLY for retrying if the retries exceeded exception is thrown
+ for ((j=1; j <= $CIRCUIT_BREAKER; j++)); do
+ #Read WAF configuration from AWS
+ WAF_CONFIG=$(aws wafv2 get-ip-set --scope CLOUDFRONT --id ${ID} --name ${NAME} 2>&1)
+ CMD_CD=$?
+ [[ $DEBUG -ge 1 ]] && echo "AWS CLI Read Response Code: ${CMD_CD}"
+ [[ $DEBUG -ge 2 ]] && echo "AWS CLI Read Response: ${WAF_CONFIG}"
+
+ #If the retries exceeded error code is returned, try again, otherwise exit the loop
+ [[ $CMD_CD -eq $AWS_RETRY_ERROR ]] || break
+
+ SLEEP_FOR=$(jitter ${j})
+ echo "CLI retries exceed. Waiting for ${SLEEP_FOR} seconds to execute read again...(${j})"
+ sleep ${SLEEP_FOR}
+ done
+
+ #Unable to get the lock tocken and IP set so there isn't any point in attempting the write op
+ [[ $j -ge $CIRCUIT_BREAKER ]] && echo “Attempts to read WAF IPSet exceeded” && sleep $(jitter ${i}) && continue
+
+ #The loop was short circuited with an error code other than 0, so something is wrong
+ [[ $CMD_CD -eq 0 ]] || ( echo "An unexpected read error occurred: ${CMD_CD}" && exit 2 )
+
+ echo "Read was successful."
+
+ if [ ${OP} == "append" ]; then
+ ##If this is used to whitelist individual ips or cidrs, using an additive approach is what is required
+ #Parse out IP set addresses to array
+ IP_ADDRESSES=($(jq -r '.IPSet.Addresses | .[]' <<< ${WAF_CONFIG}))
+
+ #If CIDR is already present in IP set, eject
+ grep -q $RUNNER_CIDRS <<< ${IP_ADDRESSES}
+ [[ $? -ne 0 ]] || ( echo "CIDR is present in IP Set." && exit 0 )
+
+ #Add runner CIDR to array
+ IP_ADDRESSES+=("$RUNNER_CIDRS")
+ else
+ ##If this is used to hard set the IP set, just clobber it
+ IP_ADDRESSES=("$RUNNER_CIDRS")
+ fi
+
+ #Stringify IPs
+ STRINGIFIED=$(echo $(IFS=" " ; echo "${IP_ADDRESSES[*]}"))
+ [[ $DEBUG -ge 2 ]] && echo "Ip Addresses: ${STRINGIFIED}"
+
+ #Parse out optimistic concurrency control token
+ OCC_TOKEN=$(jq -r '.LockToken' <<< ${WAF_CONFIG})
+ [[ $DEBUG -ge 2 ]] && echo "LockToken: ${OCC_TOKEN}"
+
+ #This loop is ONLY for retrying if the retries exceeded exception is thrown
+ for ((k=1; k <= $CIRCUIT_BREAKER; k++)); do
+ #Write updated WAF configuration to AWS
+ OUTPUT=$(aws wafv2 update-ip-set --scope CLOUDFRONT --id ${ID} --name ${NAME} --lock-token ${OCC_TOKEN} --addresses ${STRINGIFIED} 2>&1)
+ CMD_CD=$?
+ [[ $DEBUG -ge 1 ]] && echo "AWS CLI Write Response Code: ${CMD_CD}"
+ [[ $DEBUG -ge 2 ]] && echo "AWS CLI Write Response: ${OUTPUT}"
+
+ #If the retries exceeded error code is returned, try again, otherwise exit the loop
+ [[ $CMD_CD -eq $AWS_RETRY_ERROR ]] || break
+ #If WAFOptimisticLockException error code is returned, exit the loop
+ [[ "$OUTPUT" =~ "WAFOptimisticLockException" ]] && break
+
+ SLEEP_FOR=$(jitter ${k})
+ echo "CLI retries exceed. Waiting for ${SLEEP_FOR} seconds to execute write again...(${k})"
+ sleep ${SLEEP_FOR}
+ done
+
+ [[ $CMD_CD -ne 0 ]] || break
+ #Still not having success, so try again
+
+ echo "Exit Code: ${CMD_CD}"
+
+ SLEEP_FOR=$(jitter ${i})
+ echo "Waiting for ${SLEEP_FOR} seconds to execute main loop again...(${i})"
+ sleep ${SLEEP_FOR}
+done
+
+[[ $DEBUG -ge 1 ]] && echo "Attempts to update ip set: $i"
+
+[[ $i -gt $CIRCUIT_BREAKER ]] && echo “Attempts to update WAF IPSet exceeded, exiting.” && exit 2
+
+echo "Applied the IP Set successfully."
+
+#Things should not have made it this far without being able to successfully write the IP Set
+exit $CMD_CD
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 6eb49a5ee7..5909a66c4c 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -26,7 +26,7 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
diff --git a/.github/workflows/cypress-workflow.yml b/.github/workflows/cypress-workflow.yml
index 6297aae216..23893d3fc2 100644
--- a/.github/workflows/cypress-workflow.yml
+++ b/.github/workflows/cypress-workflow.yml
@@ -35,7 +35,7 @@ jobs:
name: Setup Cypress Test Matrix
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- id: set-test-matrix
run: |
echo "test-matrix=$(ls -1 tests/cypress/cypress/e2e/${{ inputs.test-path }}/* | xargs -n 1 basename | jq -R -s -c 'split("\n")[:-1]')" >> $GITHUB_OUTPUT
@@ -46,14 +46,14 @@ jobs:
runs-on: ubuntu-latest
container:
image: cypress/browsers:node16.16.0-chrome107-ff107
- options: --user 1001
+ options: --user root
needs: setup
strategy:
fail-fast: false
matrix:
containers: ${{ fromJson(needs.setup.outputs.test-matrix) }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: set path
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index c8415d296a..29332636db 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -8,7 +8,7 @@ on:
- "!skipci*"
concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
+ group: ${{ github.workflow }}-${{ github.ref_name }}
permissions:
id-token: write
@@ -19,7 +19,7 @@ jobs:
unit-tests:
name: Unit Tests
uses: ./.github/workflows/unittest-workflow.yml
- if: github.ref == 'refs/heads/master'
+ if: github.ref_name == 'master'
secrets:
CODE_CLIMATE_ID: ${{ secrets.CODE_CLIMATE_ID }}
deploy:
@@ -27,17 +27,21 @@ jobs:
env:
SLS_DEPRECATION_DISABLE: "*" # Turn off deprecation warnings in the pipeline
steps:
+ - uses: actions/checkout@v4
- name: set branch_name # Some integrations (Dependabot & Snyk) build very long branch names. This is a switch to make long branch names shorter.
run: |
- echo "GITHUB_REF=${GITHUB_REF}"
- if [[ "$GITHUB_REF" =~ ^refs/heads/dependabot/.* ]]; then
- echo "branch_name=`echo ${GITHUB_REF##*/*-} | md5sum | head -c 10 | sed 's/^/x/'`" >> $GITHUB_ENV
- elif [[ "$GITHUB_REF" =~ ^refs/.*/snyk-* ]]; then
- echo "branch_name=`echo ${GITHUB_REF##*/*-} | head -c 10 | sed 's/^/s/'`" >> $GITHUB_ENV
- else
- echo "branch_name=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
- fi
- - uses: actions/checkout@v3
+ BRANCH_NAME=$(./.github/setBranchName.sh ${{ github.ref_name }})
+ echo "branch_name=${BRANCH_NAME}" >> $GITHUB_ENV
+ - name: "Setup jq"
+ uses: dcarbone/install-jq-action@v2.1.0
+ with:
+ version: "${{ inputs.version }}"
+ force: "${{ inputs.force }}"
+ - name: "Check jq"
+ # language=sh
+ run: |
+ which jq
+ jq --version
- name: Validate branch name
run: ./.github/branchNameValidation.sh $STAGE_PREFIX$branch_name
- name: set branch specific variable names
@@ -80,10 +84,12 @@ jobs:
working-directory: services
outputs:
application_endpoint: ${{ steps.endpoint.outputs.application_endpoint}}
+ BRANCH_SPECIFIC_VARNAME_AWS_DEFAULT_REGION: ${{ steps.set_names.outputs.BRANCH_SPECIFIC_VARNAME_AWS_DEFAULT_REGION }}
+ BRANCH_SPECIFIC_VARNAME_AWS_OIDC_ROLE_TO_ASSUME: ${{ steps.set_names.outputs.BRANCH_SPECIFIC_VARNAME_AWS_OIDC_ROLE_TO_ASSUME }}
# run e2e tests after deploy completes
e2e-tests-init:
name: Initialize End To End Tests
- if: ${{ github.ref != 'refs/heads/master' && github.ref != 'refs/heads/val' && github.ref != 'refs/heads/prod' }}
+ if: ${{ github.ref_name != 'master' && github.ref_name != 'val' && github.ref_name != 'prod' }}
needs:
- deploy
runs-on: ubuntu-latest
@@ -93,17 +99,16 @@ jobs:
run: |
echo "No endpoint set, Check if the deploy workflow was successful."
exit 1
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: set branch_name
run: |
- if [[ "$GITHUB_REF" =~ ^refs/heads/dependabot/.* ]]; then # Dependabot builds very long branch names. This is a switch to make it shorter.
- echo "branch_name=`echo ${GITHUB_REF#refs/heads/} | md5sum | head -c 10 | sed 's/^/x/'`" >> $GITHUB_ENV
- else
- echo "branch_name=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV
- fi
+ BRANCH_NAME=$(./.github/setBranchName.sh ${{ github.ref_name }})
+ echo "branch_name=${BRANCH_NAME}" >> $GITHUB_ENV
- name: set branch specific variable names
+ id: set_names
run: ./.github/build_vars.sh set_names
- name: set variable values
+ id: set_values
run: ./.github/build_vars.sh set_values
env:
AWS_DEFAULT_REGION: ${{ secrets[env.BRANCH_SPECIFIC_VARNAME_AWS_DEFAULT_REGION] || secrets.AWS_DEFAULT_REGION }}
@@ -123,7 +128,6 @@ jobs:
path: |
services/app-api/node_modules
services/uploads/node_modules
- services/stream-functions/node_modules
services/ui/node_modules
services/ui-auth/node_modules
services/ui-src/node_modules
@@ -139,13 +143,63 @@ jobs:
- name: set path
run: |
echo "PATH=$(pwd)/node_modules/.bin/:$PATH" >> $GITHUB_ENV
+ - name: Get Runner IP
+ id: get-ip
+ run: |
+ #!/bin/bash
+ # Get the IP address of the runner
+ IP_ADDRESS=$(curl https://api.ipify.org)
+ echo "Runner IP address: $IP_ADDRESS"
+ # Store the IP address as an output variable
+ echo "RUNNER_IP=$IP_ADDRESS/32" >> $GITHUB_OUTPUT
+ - name: Get Github Actions CIDR Blocks
+ id: get-gha-cidrs
+ shell: bash
+ run: |
+ #!/bin/bash
+ GHA_RESP=$(curl --header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/meta)
+ echo "Response for GHA runner CIDR blocks: $GHA_RESP"
+ IPV4_CIDR_ARR=($(echo $GHA_RESP | jq -r '.actions | .[]' | grep -v ':'))
+ GHA_CIDRS_IPV4=$(echo $(IFS=" "; echo ${IPV4_CIDR_ARR[*]}))
+ echo "GHA_CIDRS_IPV4=$GHA_CIDRS_IPV4" >> $GITHUB_OUTPUT
+ - name: Generate IP Set Name
+ id: gen-ip-set-name
+ run: |
+ STAGE_GH_IPSET_NAME=$STAGE_PREFIX$branch_name-gh-ipset
+ echo "Github IP Set name: $STAGE_GH_IPSET_NAME"
+ echo "STAGE_GH_IPSET_NAME=$STAGE_GH_IPSET_NAME" >> $GITHUB_OUTPUT
+ - name: Fetch AWS IP set Metadata
+ id: fetch-ip-set-info
+ run: |
+ #!/bin/bash
+ # Fetch AWS IP set ARNs using AWS CLI and store them in a variable
+ AWS_IP_SET_INFO=$(aws wafv2 list-ip-sets --scope=CLOUDFRONT)
+ # Store the IP set ARNs in an output variable using GITHUB_OUTPUT
+ IPSET_NAME=${{ steps.gen-ip-set-name.outputs.STAGE_GH_IPSET_NAME }}
+ IPSET=$(jq '.IPSets | map(select(.Name == "'${IPSET_NAME}'")) | .[]' <<< ${AWS_IP_SET_INFO})
+ [ -z "$IPSET" ] && echo "IP Set with name ${IPSET_NAME} was not located. Exiting..." && exit 1
+ echo "IP Set metadata: ${IPSET}"
+ #Get Values from the IP SET
+ IPSET_ID=$(jq -r '.Id' <<< ${IPSET})
+ echo "IPSET_ARN=$IPSET_ARN" >> $GITHUB_OUTPUT
+ echo "IPSET_NAME=$IPSET_NAME" >> $GITHUB_OUTPUT
+ echo "IPSET_ID=$IPSET_ID" >> $GITHUB_OUTPUT
+ - name: Update IP Set
+ id: update-ip-set
+ run: ./.github/waf-controller.sh set ${{ steps.fetch-ip-set-info.outputs.IPSET_NAME }} ${{ steps.fetch-ip-set-info.outputs.IPSET_ID }} ${{ steps.get-gha-cidrs.outputs.GHA_CIDRS_IPV4 }}
+ env:
+ AWS_RETRY_MODE: adaptive
+ AWS_MAX_ATTEMPTS: 10
outputs:
application_endpoint: ${{ needs.deploy.outputs.application_endpoint }}
+ ipset_name: ${{ steps.fetch-ip-set-info.outputs.IPSET_NAME }}
+ ipset_id: ${{ steps.fetch-ip-set-info.outputs.IPSET_ID }}
setup-tests:
name: "Setup End To End Tests"
uses: ./.github/workflows/cypress-workflow.yml
- needs: e2e-tests-init
+ needs:
+ - e2e-tests-init
with:
test-path: "init"
test-endpoint: "${{ needs.e2e-tests-init.outputs.application_endpoint }}"
@@ -244,3 +298,31 @@ jobs:
cypress-user3: ${{ secrets.CYPRESS_TEST_USER_3 }}
cypress-user4: ${{ secrets.CYPRESS_TEST_USER_4 }}
cypress-password: ${{ secrets.CYPRESS_TEST_PASSWORD_1 }}
+
+ cleanup:
+ name: Deslist GHA Runner CIDR Blocks
+ if: ${{ github.ref != 'refs/heads/master' && github.ref != 'refs/heads/val' && github.ref != 'refs/heads/prod' }}
+ runs-on: ubuntu-latest
+ needs:
+ - e2e-tests-init
+ - e2e-feature-tests
+ - child-e2e-measure-tests
+ - adult-e2e-measure-tests
+ - health-home-e2e-measure-tests
+ - deploy
+ - a11y-tests
+ env:
+ SLS_DEPRECATION_DISABLE: "*" # Turn off deprecation warnings in the pipeline
+ steps:
+ - uses: actions/checkout@v4
+ - name: Configure AWS credentials for GitHub Actions
+ uses: aws-actions/configure-aws-credentials@v4
+ with:
+ role-to-assume: ${{ secrets[env.BRANCH_SPECIFIC_VARNAME_AWS_OIDC_ROLE_TO_ASSUME] || secrets.AWS_OIDC_ROLE_TO_ASSUME }}
+ aws-region: ${{ secrets[env.BRANCH_SPECIFIC_VARNAME_AWS_DEFAULT_REGION] || secrets.AWS_DEFAULT_REGION }}
+ - name: clean-up-iplist
+ id: reset-ip-set
+ run: ./.github/waf-controller.sh set ${{ needs.e2e-tests-init.outputs.ipset_name }} ${{ needs.e2e-tests-init.outputs.ipset_id }} '[]'
+ env:
+ AWS_RETRY_MODE: adaptive
+ AWS_MAX_ATTEMPTS: 10
diff --git a/.github/workflows/destroy.yml b/.github/workflows/destroy.yml
index f9f5ef5066..08ee56abd1 100644
--- a/.github/workflows/destroy.yml
+++ b/.github/workflows/destroy.yml
@@ -1,6 +1,12 @@
name: Destroy
-on: delete
+on:
+ delete:
+ workflow_dispatch:
+ inputs:
+ environment:
+ description: "Name of the environment to destroy:"
+ required: true
permissions:
id-token: write
@@ -14,17 +20,23 @@ jobs:
# This conditional is a backup mechanism to help prevent mistakes from becoming disasters.
# This is a list of branch names that are commonly used for protected branches/environments.
# Add/remove names from this list as appropriate.
- if: github.event.ref_type == 'branch' && !contains(fromJson('["master", "val", "prod"]'), github.event.ref)
+ if: |
+ (
+ github.event.ref_type == 'branch' &&
+ (!startsWith(github.event.ref, 'skipci')) &&
+ (!contains(fromJson('["master", "val", "prod"]'), github.event.ref))
+ ) ||
+ (
+ inputs.environment != '' &&
+ (!contains(fromJson('["master", "val", "prod"]'), inputs.environment))
+ )
runs-on: ubuntu-latest
steps:
+ - uses: actions/checkout@v4
- name: set branch_name
run: |
- if [[ "${{ github.event.ref }}" =~ ^dependabot/.* ]]; then # Dependabot builds very long branch names. This is a switch to make it shorter.
- echo "branch_name=`echo ${{ github.event.ref }} | md5sum | head -c 10 | sed 's/^/x/'`" >> $GITHUB_ENV
- else
- echo "branch_name=${{ github.event.ref }}" >> $GITHUB_ENV
- fi
- - uses: actions/checkout@v3
+ BRANCH_NAME=$(./.github/setBranchName.sh ${{ inputs.environment || github.event.ref }})
+ echo "branch_name=${BRANCH_NAME}" >> $GITHUB_ENV
- name: set branch specific variable names
run: ./.github/build_vars.sh set_names
- name: set variable values
diff --git a/.github/workflows/git-secrets.yaml b/.github/workflows/git-secrets.yaml
index e63711abea..3906e2eef1 100644
--- a/.github/workflows/git-secrets.yaml
+++ b/.github/workflows/git-secrets.yaml
@@ -4,7 +4,7 @@ jobs:
gitleaks-scan:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Run gitleaks docker
uses: docker://zricethezav/gitleaks
with:
diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
index b8c1b44a45..8eb67279b3 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.yml
@@ -15,7 +15,7 @@ jobs:
assignAuthor:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Assign PR to Creator
run: |
if [ "$PR_AUTHOR_TYPE" != "Bot" ]
diff --git a/.github/workflows/pullrequest-precommit-check.yml b/.github/workflows/pullrequest-precommit-check.yml
index b9909c03f8..51c92c7ef4 100644
--- a/.github/workflows/pullrequest-precommit-check.yml
+++ b/.github/workflows/pullrequest-precommit-check.yml
@@ -6,7 +6,7 @@ jobs:
prettier:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: actions/setup-python@v3
- uses: pre-commit/action@v3.0.0
with:
diff --git a/.github/workflows/scan_security-hub-jira-integration.yml b/.github/workflows/scan_security-hub-jira-integration.yml
index 89f872c57c..5f8001001c 100644
--- a/.github/workflows/scan_security-hub-jira-integration.yml
+++ b/.github/workflows/scan_security-hub-jira-integration.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- name: Check out repo
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
diff --git a/.github/workflows/scan_snyk-jira-integration.yml b/.github/workflows/scan_snyk-jira-integration.yml
index 66e5a5f9c4..dcb08e11cc 100644
--- a/.github/workflows/scan_snyk-jira-integration.yml
+++ b/.github/workflows/scan_snyk-jira-integration.yml
@@ -16,7 +16,7 @@ jobs:
if: github.event_name == 'pull_request'
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install Snyk and Run Snyk test
run: |
@@ -31,7 +31,7 @@ jobs:
if: github.event_name == 'schedule'
steps:
- name: Check out repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install Snyk and Run Snyk test
run: |
diff --git a/.github/workflows/unittest-workflow.yml b/.github/workflows/unittest-workflow.yml
index 465ff76879..fbbe523e33 100644
--- a/.github/workflows/unittest-workflow.yml
+++ b/.github/workflows/unittest-workflow.yml
@@ -11,7 +11,7 @@ jobs:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: set variable values
run: ./.github/build_vars.sh set_values
env:
diff --git a/README.md b/README.md
index 27c332c8cf..f5fd2910ed 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,8 @@ QMR is the CMCS MDCT application for collecting state data for related to measur
# Table of Contents
+- [MDCT QMR (Quality Measure Reporting)](#mdct-qmr-quality-measure-reporting)
+- [Table of Contents](#table-of-contents)
- [Getting Started](#getting-started)
- [Local Development Setup](#local-development-setup)
- [Prettier](#prettier)
@@ -40,7 +42,6 @@ QMR is the CMCS MDCT application for collecting state data for related to measur
- [Database](#database)
- [Tables](#tables)
- [How to set up Dynamo endpoint to view local Db](#how-to-set-up-dynamo-endpoint-to-view-local-db)
- - [Stream Functions](#stream-functions)
- [UI](#ui)
- [Dev/Impl/Prod endpoints](#devimplprod-endpoints)
- [Branch Endpoints](#branch-endpoints)
@@ -413,12 +414,6 @@ To run the dynamodb gui, run `DYNAMO_ENDPOINT=http://localhost:8000 dynamodb-adm
From here you can view the tables and perform operations on the local tables.
-### Stream Functions
-
----
-
-The stream functions fire deltas when updates to its table happens. These changes are picked up in the API where these changes are communicated to the kafka streams for the application.
-
## UI
The UI Service creates the URL's associated with the application and the cloudfront logs that monitor traffic.
@@ -539,9 +534,13 @@ to make in order to get that year working.
![After](./.images/afterImportUpdate.png?raw=true)
-5. Copy over the `/globalValidations`, `/CommonQuestions`, and `/Qualifiers` directories to the latest year
+5. Go to the `/services/ui-src/src/utils/testUtils`, create a new directory for the latest year (e.g. 2024), and copy over the `validationHelpers.ts` and `validationHelpers.test.ts` files
+
+ **NOTE:** Remember to update the imports in these files to reflect the latest year.
+
+6. Copy over the `/globalValidations`, `/CommonQuestions`, and `/Qualifiers` directories to the latest year
-6. Similar to Step 4, update import names from the previous year to the most recent year
+7. Similar to Step 4, update import names from the previous year to the most recent year
Before
@@ -551,9 +550,9 @@ to make in order to get that year working.
![After](./.images/afterCommonComponentUpdate.png?raw=true)
-7. In `services/ui-src/src/libs/spaLib.ts`, copy over the prior year's entry into the array.
+8. In `services/ui-src/src/libs/spaLib.ts`, copy over the prior year's entry into the array.
-8. In `services/ui-src/src/measures/measureDescriptions.ts` , copy over the prior year's entry into the array.
+9. In `services/ui-src/src/measures/measureDescriptions.ts` , copy over the prior year's entry into the array.
## Things to Look Out For (Gotchas)
diff --git a/deploy.sh b/deploy.sh
index 71cfaa96cd..7942b5ede7 100755
--- a/deploy.sh
+++ b/deploy.sh
@@ -8,7 +8,6 @@ services=(
'database'
'app-api'
'uploads'
- 'stream-functions'
'ui'
'ui-auth'
'ui-src'
diff --git a/destroy.sh b/destroy.sh
index 6ada321564..615ec57db0 100755
--- a/destroy.sh
+++ b/destroy.sh
@@ -35,6 +35,16 @@ set -e
# Find cloudformation stacks associated with stage
stackList=(`aws cloudformation describe-stacks | jq -r ".Stacks[] | select(.Tags[] | select(.Key==\"STAGE\") | select(.Value==\"$stage\")) | .StackName"`)
+if [ ${#stackList[@]} -eq 0 ]; then
+ echo """
+ ---------------------------------------------------------------------------------------------
+ ERROR: No stacks were identified for destruction
+ ---------------------------------------------------------------------------------------------
+ Please verify the stage name: $stage
+ """
+ exit 1
+fi
+
# Find buckets attached to any of the stages, so we can empty them before removal.
bucketList=()
set +e
diff --git a/package.json b/package.json
index ae7a3006f5..1895093a6d 100644
--- a/package.json
+++ b/package.json
@@ -53,6 +53,7 @@
"yargs": "^16.1.1"
},
"dependencies": {
+ "@enterprise-cmcs/serverless-waf-plugin": "^1.3.2",
"xml2js": "0.6.0"
},
"resolutions": {
diff --git a/services/app-api/handlers/dynamoUtils/measureList.ts b/services/app-api/handlers/dynamoUtils/measureList.ts
index e1c6a0e22c..09b3a3b16f 100644
--- a/services/app-api/handlers/dynamoUtils/measureList.ts
+++ b/services/app-api/handlers/dynamoUtils/measureList.ts
@@ -1034,4 +1034,342 @@ export const measures: Measure = {
placeholder: true,
},
],
+ 2024: [
+ {
+ type: "A",
+ measure: "CSQ",
+ },
+ {
+ type: "C",
+ measure: "CSQ",
+ },
+ {
+ type: "H",
+ measure: "CSQ",
+ },
+ {
+ type: "A",
+ measure: "AAB-AD",
+ },
+ {
+ type: "A",
+ measure: "AMM-AD",
+ },
+ {
+ type: "A",
+ measure: "AMR-AD",
+ },
+ {
+ type: "A",
+ measure: "BCS-AD",
+ },
+ {
+ type: "A",
+ measure: "CBP-AD",
+ },
+ {
+ type: "A",
+ measure: "CCP-AD",
+ },
+ {
+ type: "A",
+ measure: "CCS-AD",
+ },
+ {
+ type: "A",
+ measure: "CCW-AD",
+ },
+ {
+ type: "A",
+ measure: "CDF-AD",
+ },
+ {
+ type: "A",
+ measure: "CHL-AD",
+ },
+ {
+ type: "A",
+ measure: "COB-AD",
+ },
+ {
+ type: "A",
+ measure: "COL-AD",
+ },
+ {
+ type: "A",
+ measure: "CPA-AD",
+ },
+ {
+ type: "A",
+ measure: "CPU-AD",
+ },
+ {
+ type: "A",
+ measure: "FUA-AD",
+ },
+ {
+ type: "A",
+ measure: "FUH-AD",
+ },
+ {
+ type: "A",
+ measure: "FUM-AD",
+ },
+ {
+ type: "A",
+ measure: "FVA-AD",
+ },
+ {
+ type: "A",
+ measure: "HBD-AD",
+ },
+ {
+ type: "A",
+ measure: "HPCMI-AD",
+ },
+ {
+ type: "A",
+ measure: "HVL-AD",
+ },
+ {
+ type: "A",
+ measure: "IET-AD",
+ },
+ {
+ type: "A",
+ measure: "MSC-AD",
+ },
+ {
+ type: "A",
+ measure: "NCIDDS-AD",
+ autocompleteOnCreation: true,
+ },
+ {
+ type: "A",
+ measure: "OHD-AD",
+ },
+ {
+ type: "A",
+ measure: "OUD-AD",
+ },
+ {
+ type: "A",
+ measure: "PCR-AD",
+ },
+ {
+ type: "A",
+ measure: "PPC-AD",
+ },
+ {
+ type: "A",
+ measure: "PQI01-AD",
+ },
+ {
+ type: "A",
+ measure: "PQI05-AD",
+ },
+ {
+ type: "A",
+ measure: "PQI08-AD",
+ },
+ {
+ type: "A",
+ measure: "PQI15-AD",
+ },
+ {
+ type: "A",
+ measure: "SAA-AD",
+ },
+ {
+ type: "A",
+ measure: "SSD-AD",
+ },
+ {
+ type: "C",
+ measure: "AAB-CH",
+ },
+ {
+ type: "C",
+ measure: "ADD-CH",
+ },
+ {
+ type: "C",
+ measure: "AMB-CH",
+ },
+ {
+ type: "C",
+ measure: "AMR-CH",
+ },
+ {
+ type: "C",
+ measure: "APM-CH",
+ },
+ {
+ type: "C",
+ measure: "APP-CH",
+ },
+ {
+ type: "C",
+ measure: "CCP-CH",
+ },
+ {
+ type: "C",
+ measure: "CCW-CH",
+ },
+ {
+ type: "C",
+ measure: "CDF-CH",
+ },
+ {
+ type: "C",
+ measure: "CHL-CH",
+ },
+ {
+ type: "C",
+ measure: "CIS-CH",
+ },
+ {
+ type: "C",
+ measure: "CPC-CH",
+ },
+ {
+ type: "C",
+ measure: "DEV-CH",
+ },
+ {
+ type: "C",
+ measure: "FUA-CH",
+ },
+ {
+ type: "C",
+ measure: "FUH-CH",
+ },
+ {
+ type: "C",
+ measure: "FUM-CH",
+ },
+ {
+ type: "C",
+ measure: "IMA-CH",
+ },
+ {
+ type: "C",
+ measure: "LSC-CH",
+ },
+ {
+ type: "C",
+ measure: "LBW-CH",
+ autocompleteOnCreation: true,
+ },
+ {
+ type: "C",
+ measure: "LRCD-CH",
+ autocompleteOnCreation: true,
+ },
+ {
+ type: "C",
+ measure: "OEV-CH",
+ },
+ {
+ type: "C",
+ measure: "PPC2-CH",
+ },
+ {
+ type: "C",
+ measure: "SFM-CH",
+ },
+ {
+ type: "C",
+ measure: "TFL-CH",
+ },
+ {
+ type: "C",
+ measure: "W30-CH",
+ },
+ {
+ type: "C",
+ measure: "WCC-CH",
+ },
+ {
+ type: "C",
+ measure: "WCV-CH",
+ },
+ {
+ type: "H",
+ measure: "AIF-HH",
+ },
+ {
+ type: "H",
+ measure: "AMB-HH",
+ },
+ {
+ type: "H",
+ measure: "CBP-HH",
+ },
+ {
+ type: "H",
+ measure: "CDF-HH",
+ },
+ {
+ type: "H",
+ measure: "COL-HH",
+ },
+ {
+ type: "H",
+ measure: "FUA-HH",
+ },
+ {
+ type: "H",
+ measure: "FUH-HH",
+ },
+ {
+ type: "H",
+ measure: "FUM-HH",
+ },
+ {
+ type: "H",
+ measure: "IET-HH",
+ },
+ {
+ type: "H",
+ measure: "IU-HH",
+ },
+ {
+ type: "H",
+ measure: "OUD-HH",
+ },
+ {
+ type: "H",
+ measure: "PCR-HH",
+ },
+ {
+ type: "H",
+ measure: "PQI92-HH",
+ },
+ {
+ type: "H",
+ measure: "SS-1-HH",
+ placeholder: true,
+ },
+ {
+ type: "H",
+ measure: "SS-2-HH",
+ placeholder: true,
+ },
+ {
+ type: "H",
+ measure: "SS-3-HH",
+ placeholder: true,
+ },
+ {
+ type: "H",
+ measure: "SS-4-HH",
+ placeholder: true,
+ },
+ {
+ type: "H",
+ measure: "SS-5-HH",
+ placeholder: true,
+ },
+ ],
};
diff --git a/services/app-api/handlers/measures/tests/get.test.ts b/services/app-api/handlers/measures/tests/get.test.ts
index 3312c39019..882864d599 100644
--- a/services/app-api/handlers/measures/tests/get.test.ts
+++ b/services/app-api/handlers/measures/tests/get.test.ts
@@ -150,7 +150,7 @@ describe("Test Get Measure Handlers", () => {
};
const res = await getReportingYears(event, null);
expect(res.statusCode).toBe(StatusCodes.SUCCESS);
- expect(res.body).toBe('["2021","2022","2023"]');
+ expect(res.body).toBe('["2021","2022","2023","2024"]');
});
test("Test getMeasureListInfo works when called with an empty object", async () => {
diff --git a/services/app-api/handlers/prince/pdf.ts b/services/app-api/handlers/prince/pdf.ts
index 94292d7cb4..29c330aee7 100644
--- a/services/app-api/handlers/prince/pdf.ts
+++ b/services/app-api/handlers/prince/pdf.ts
@@ -1,62 +1,143 @@
+import { fetch } from "cross-fetch"; // TODO delete this line and uninstall this package, once QMR is running on Nodejs 18+
+import createDOMPurify from "dompurify";
+import { JSDOM } from "jsdom";
import handler from "../../libs/handler-lib";
import { StatusCodes } from "../../utils/constants/constants";
-import { URL } from "url";
-import { SignatureV4 } from "@smithy/signature-v4";
-import { Sha256 } from "@aws-crypto/sha256-js";
-import { fetch } from "cross-fetch"; // TODO remove this polyfill once QMR is on Node 18+
+
+const windowEmulator: any = new JSDOM("").window;
+const DOMPurify = createDOMPurify(windowEmulator);
export const getPDF = handler(async (event, _context) => {
- const body = event.body; // will be base64-encoded HTML, like "PGh0bWw..."
- if (!body) {
+ const rawBody = event.body; // will be base64-encoded HTML, like "PGh0bWw..."
+ if (!rawBody) {
throw new Error("Missing request body");
}
- const {
- // princeApiHost: hostname, // JUST the host name, no protocol, ex: "my-site.cms.gov"
- // princeApiPath: path, // Needs leading slash, ex: "/doc-conv/508html-to-508pdf"
- princeUrl,
- AWS_ACCESS_KEY_ID: accessKeyId,
- AWS_SECRET_ACCESS_KEY: secretAccessKey,
- AWS_SESSION_TOKEN: sessionToken,
- } = process.env;
-
- if (
- princeUrl === undefined ||
- accessKeyId === undefined ||
- secretAccessKey === undefined ||
- sessionToken === undefined
- ) {
+ let sanitizedBody;
+ if (DOMPurify.isSupported && typeof rawBody === "string") {
+ // decode body from base64, sanitize dangerous html
+ const decodedBody = Buffer.from(rawBody, "base64").toString();
+ sanitizedBody = DOMPurify.sanitize(decodedBody);
+ }
+ if (!sanitizedBody) {
+ throw new Error("Could not process request");
+ }
+
+ const { docraptorApiKey, stage } = process.env;
+ if (!docraptorApiKey) {
throw new Error("No config found to make request to PDF API");
}
- const { hostname, pathname: path } = new URL(princeUrl);
+ const requestBody = {
+ user_credentials: docraptorApiKey,
+ doc: {
+ document_content: sanitizedBody,
+ type: "pdf" as const,
+ // This tag differentiates QMR and CARTS requests in DocRaptor's logs.
+ tag: "QMR",
+ test: stage !== "prod",
+ prince_options: {
+ profile: "PDF/UA-1" as const,
+ },
+ },
+ };
+
+ const arrayBuffer = await sendDocRaptorRequest(requestBody);
+ const base64PdfData = Buffer.from(arrayBuffer).toString("base64");
+ return {
+ status: StatusCodes.SUCCESS,
+ body: base64PdfData,
+ };
+});
- const request = {
+async function sendDocRaptorRequest(request: DocRaptorRequestBody) {
+ const response = await fetch("https://docraptor.com/docs", {
method: "POST",
- protocol: "https",
- hostname,
- path,
headers: {
- host: hostname, // Prince requires this to be signed
+ "content-type": "application/json",
},
- body,
- };
-
- const signer = new SignatureV4({
- service: "execute-api",
- region: "us-east-1",
- credentials: { accessKeyId, secretAccessKey, sessionToken },
- sha256: Sha256,
+ body: JSON.stringify(request),
});
- const signedRequest = await signer.sign(request);
+ await handlePdfStatusCode(response);
- const response = await fetch(`https://${hostname}${path}`, signedRequest);
+ const pdfPageCount = response.headers.get("X-DocRaptor-Num-Pages");
+ console.debug(`Successfully generated a ${pdfPageCount}-page PDF.`);
- const base64EncodedPdfData = await response.json();
+ return response.arrayBuffer();
+}
- return {
- status: StatusCodes.SUCCESS,
- body: base64EncodedPdfData,
+/**
+ * If PDF generation was not successful, log the reason and throw an error.
+ *
+ * For more details see https://docraptor.com/documentation/api/status_codes
+ */
+async function handlePdfStatusCode(response: Response) {
+ if (response.status === 200) {
+ return;
+ }
+
+ const xmlErrorMessage = await response.text();
+ console.warn("DocRaptor Error Message:\n" + xmlErrorMessage);
+
+ switch (response.status) {
+ case 400: // Bad Request
+ case 422: // Unprocessable Entity
+ throw new Error("PDF generation failed - possibly an HTML issue");
+ case 401: // Unauthorized
+ case 403: // Forbidden
+ throw new Error(
+ "PDF generation failed - possibly a configuration or throttle issue"
+ );
+ default:
+ throw new Error(
+ `Received status code ${response.status} from PDF generation service`
+ );
+ }
+}
+
+type DocRaptorRequestBody = {
+ /** Your DocRaptor API key */
+ user_credentials: string;
+ doc: DocRaptorParameters;
+};
+
+/**
+ * Here is some in-band documentation for the more common DocRaptor options.
+ * There also options for JS handling, asset handling, PDF metadata, and more.
+ * Note that we do not use DocRaptor's hosting; we return the PDF directly.
+ * For more details see https://docraptor.com/documentation/api
+ */
+type DocRaptorParameters = {
+ /** Test documents are watermarked, but don't count against API limits. */
+ test?: boolean;
+ /** We only use `pdf`. */
+ type: "pdf" | "xls" | "xlsx";
+ /** The HTML to render. Either this or `document_url` is required. */
+ document_content?: string;
+ /** The URL to fetch and render. Either this or `document_content` is required. */
+ document_url?: string;
+ /** Synchronous calls have a 60s limit. Callbacks are required for longer-running docs. */
+ async?: false;
+ /** This name will show up in the logs: https://docraptor.com/doc_logs */
+ name?: string;
+ /** This tag will also show up in DocRaptor's logs. */
+ tag?: string;
+ /** Should DocRaptor run JS embedded in your HTML? Default is `false`. */
+ javascript?: boolean;
+ prince_options: {
+ /**
+ * In theory we can choose a different PDF version, but UA-1 is the only accessible one.
+ * https://docraptor.com/documentation/article/6637003-accessible-tagged-pdfs
+ */
+ profile: "PDF/UA-1";
+ /** The default is `print`. */
+ media?: "print" | "screen";
+ /** May be needed to load relative urls. Alternatively, use the `` tag. */
+ baseurl?: string;
+ /** The title of your PDF. By default this is the `
` of your HTML. */
+ pdf_title?: string;
+ /** This may be used to override the default DPI of `96`. */
+ css_dpi?: number;
};
-});
+};
diff --git a/services/app-api/handlers/prince/tests/pdf.test.ts b/services/app-api/handlers/prince/tests/pdf.test.ts
new file mode 100644
index 0000000000..44702af492
--- /dev/null
+++ b/services/app-api/handlers/prince/tests/pdf.test.ts
@@ -0,0 +1,113 @@
+import { getPDF } from "../pdf";
+import { fetch } from "cross-fetch";
+import { testEvent } from "../../../test-util/testEvents";
+
+jest.spyOn(console, "error").mockImplementation();
+jest.spyOn(console, "warn").mockImplementation();
+
+jest.mock("../../../libs/authorization", () => ({
+ isAuthenticated: jest.fn().mockReturnValue(true),
+}));
+
+jest.mock("cross-fetch", () => ({
+ fetch: jest.fn().mockResolvedValue({
+ status: 200,
+ headers: {
+ get: jest.fn().mockResolvedValue("3"),
+ },
+ arrayBuffer: jest.fn().mockResolvedValue(
+ // An ArrayBuffer containing `%PDF-1.7`
+ new Uint8Array([37, 80, 68, 70, 45, 49, 46, 55]).buffer
+ ),
+ }),
+}));
+
+const dangerousHtml = "abc
";
+const sanitizedHtml = "abc
";
+const base64EncodedDangerousHtml =
+ Buffer.from(dangerousHtml).toString("base64");
+
+const noBodyEvent = {
+ ...testEvent,
+ body: null,
+};
+
+const dangerousHtmlBodyEvent = {
+ ...testEvent,
+ body: base64EncodedDangerousHtml,
+};
+
+describe("Test GetPDF handler", () => {
+ beforeEach(() => {
+ process.env = {
+ docraptorApiKey: "mock api key", // pragma: allowlist secret
+ };
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it("should throw error when no body provided", async () => {
+ const res = await getPDF(noBodyEvent, null);
+
+ expect(res.statusCode).toBe(500);
+ expect(res.body).toContain("Missing request body");
+ });
+
+ it("should throw error when body is not type string", async () => {
+ const res = await getPDF(testEvent, null);
+
+ expect(res.statusCode).toBe(500);
+ expect(res.body).toContain("Could not process request");
+ });
+
+ it("should throw error when config not defined", async () => {
+ delete process.env.docraptorApiKey;
+
+ const res = await getPDF(dangerousHtmlBodyEvent, null);
+
+ expect(res.statusCode).toBe(500);
+ expect(res.body).toContain("No config found to make request to PDF API");
+ });
+
+ it("should call PDF API with sanitized html", async () => {
+ const res = await getPDF(dangerousHtmlBodyEvent, null);
+
+ expect(res.statusCode).toBe(200);
+
+ expect(fetch).toHaveBeenCalled();
+ const [url, request] = (fetch as jest.Mock).mock.calls[0];
+ const body = JSON.parse(request.body);
+ expect(url).toBe("https://docraptor.com/docs");
+ expect(request).toEqual({
+ method: "POST",
+ headers: { "content-type": "application/json" },
+ body: expect.stringMatching(/^\{.*\}$/),
+ });
+ expect(body).toEqual({
+ user_credentials: "mock api key", // pragma: allowlist secret
+ doc: expect.objectContaining({
+ document_content: sanitizedHtml,
+ type: "pdf",
+ prince_options: expect.objectContaining({
+ profile: "PDF/UA-1",
+ }),
+ }),
+ });
+ });
+
+ it("should handle an error response from the PDF API", async () => {
+ (fetch as jest.Mock).mockResolvedValueOnce({
+ status: 500,
+ text: jest.fn().mockResolvedValue("It broke."),
+ });
+
+ const res = await getPDF(dangerousHtmlBodyEvent, null);
+
+ expect(res.statusCode).toBe(500);
+
+ // eslint-disable-next-line no-console
+ expect(console.warn).toBeCalledWith(expect.stringContaining("It broke."));
+ });
+});
diff --git a/services/app-api/package.json b/services/app-api/package.json
index a3f7f671e4..a59aa8f408 100644
--- a/services/app-api/package.json
+++ b/services/app-api/package.json
@@ -19,7 +19,9 @@
"devDependencies": {
"@types/aws-lambda": "^8.10.88",
"@types/aws4": "^1.11.2",
+ "@types/dompurify": "^3.0.5",
"@types/jest": "^27.4.0",
+ "@types/jsdom": "^21.1.6",
"@types/prompt-sync": "^4.1.1",
"aws-lambda": "^1.0.7",
"jest": "^27.4.7",
@@ -33,11 +35,11 @@
"typescript": "^4.6.4"
},
"dependencies": {
- "@aws-crypto/sha256-js": "^5.2.0",
- "@smithy/signature-v4": "^2.0.18",
"aws-sdk": "^2.1310.0",
"aws4": "^1.11.0",
"cross-fetch": "^4.0.0",
+ "dompurify": "^3.0.9",
+ "jsdom": "^22.1.0",
"jwt-decode": "^3.1.2",
"kafkajs": "^2.2.4",
"prompt-sync": "^4.2.0",
diff --git a/services/app-api/serverless.yml b/services/app-api/serverless.yml
index 04749d6421..a78491cd7a 100644
--- a/services/app-api/serverless.yml
+++ b/services/app-api/serverless.yml
@@ -11,6 +11,7 @@ plugins:
- serverless-plugin-typescript
- serverless-plugin-warmup
- serverless-associate-waf
+ - "@enterprise-cmcs/serverless-waf-plugin"
- serverless-offline-ssm
- serverless-offline
- serverless-stack-termination-protection
@@ -34,6 +35,11 @@ custom:
tsConfigFileLocation: "./tsconfig.json"
stage: ${opt:stage, self:provider.stage}
region: ${opt:region, self:provider.region}
+ wafPlugin:
+ name: ${self:service}-${self:custom.stage}-webacl-waf
+ wafExcludeRules:
+ awsCommon:
+ - "SizeRestrictions_BODY"
serverlessTerminationProtection:
stages:
- master
@@ -41,7 +47,7 @@ custom:
- prod
dotenv:
path: ../../.env
- princeUrl: ${ssm:/configuration/default/princeurl, "https://macpro-platform-dev.cms.gov/doc-conv/508html-to-508pdf"}
+ docraptorApiKey: ${env:docraptorApiKey, ssm:/${self:custom.stage}/pdf/docraptorApiKey, ssm:/default/pdf/docraptorApiKey}
bootstrapBrokerStringTls: ${ssm:/configuration/${self:custom.stage}/qmr/bootstrapBrokerStringTls, ssm:/configuration/default/qmr/bootstrapBrokerStringTls, ''}
coreSetTableName: ${env:coreSetTableName, cf:database-${self:custom.stage}.CoreSetTableName}
coreSetTableArn: ${env:DYNAMO_TABLE_ARN, cf:database-${self:custom.stage}.CoreSetTableArn}
@@ -51,7 +57,7 @@ custom:
measureTableStreamArn: ${env:DYNAMO_TABLE_ARN, cf:database-${self:custom.stage}.MeasureTableStreamArn}
bannerTableName: ${env:bannerTableName, cf:database-${self:custom.stage}.BannerTableName}
bannerTableArn: ${env:DYNAMO_TABLE_ARN, cf:database-${self:custom.stage}.BannerTableArn}
- webAclName: ${self:service}-${self:custom.stage}-webacl
+ webAclName: ${self:service}-${self:custom.stage}-webacl-waf
vpcId: ${ssm:/configuration/${self:custom.stage}/vpc/id, ssm:/configuration/default/vpc/id, ''}
privateSubnets:
- ${ssm:/configuration/${self:custom.stage}/vpc/subnets/private/a/id, ssm:/configuration/default/vpc/subnets/private/a/id, ''}
@@ -114,7 +120,7 @@ provider:
uploadS3BucketName: ${ssm:/s3bucket/uploads, cf:uploads-${self:custom.stage}.AttachmentsBucketName, "local-uploads"}
dynamoSnapshotS3BucketName: ${ssm:/s3bucket/snapshots, cf:uploads-${self:custom.stage}.DynamoSnapshotBucketName, "local-dynamo-snapshots"}
stage: ${opt:stage, self:provider.stage}
- princeUrl: ${self:custom.princeUrl}
+ docraptorApiKey: ${self:custom.docraptorApiKey}
functions:
listMeasures:
@@ -309,35 +315,6 @@ resources:
gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
ResponseType: DEFAULT_5XX
RestApiId: !Ref ApiGatewayRestApi
- ApiGwWebAcl:
- Type: AWS::WAFv2::WebACL
- Properties:
- Name: ${self:custom.webAclName}
- DefaultAction:
- Block: {}
- Rules:
- - Action:
- Allow: {}
- Name: ${self:custom.webAclName}-allow-usa-plus-territories
- Priority: 0
- Statement:
- GeoMatchStatement:
- CountryCodes:
- - GU # Guam
- - PR # Puerto Rico
- - US # USA
- - UM # US Minor Outlying Islands
- - VI # US Virgin Islands
- - MP # Northern Mariana Islands
- VisibilityConfig:
- SampledRequestsEnabled: true
- CloudWatchMetricsEnabled: true
- MetricName: WafWebAcl
- Scope: REGIONAL
- VisibilityConfig:
- CloudWatchMetricsEnabled: true
- SampledRequestsEnabled: true
- MetricName: ${self:custom.stage}-webacl
Outputs:
ApiGatewayRestApiName:
Value: !Ref ApiGatewayRestApi
diff --git a/services/app-api/yarn.lock b/services/app-api/yarn.lock
index 3e3f9e46d6..75987d1177 100644
--- a/services/app-api/yarn.lock
+++ b/services/app-api/yarn.lock
@@ -18,57 +18,6 @@
"@jridgewell/gen-mapping" "^0.1.0"
"@jridgewell/trace-mapping" "^0.3.9"
-"@aws-crypto/crc32@3.0.0":
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-3.0.0.tgz#07300eca214409c33e3ff769cd5697b57fdd38fa"
- integrity sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==
- dependencies:
- "@aws-crypto/util" "^3.0.0"
- "@aws-sdk/types" "^3.222.0"
- tslib "^1.11.1"
-
-"@aws-crypto/sha256-js@^5.2.0":
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz#c4fdb773fdbed9a664fc1a95724e206cf3860042"
- integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==
- dependencies:
- "@aws-crypto/util" "^5.2.0"
- "@aws-sdk/types" "^3.222.0"
- tslib "^2.6.2"
-
-"@aws-crypto/util@^3.0.0":
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-3.0.0.tgz#1c7ca90c29293f0883468ad48117937f0fe5bfb0"
- integrity sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==
- dependencies:
- "@aws-sdk/types" "^3.222.0"
- "@aws-sdk/util-utf8-browser" "^3.0.0"
- tslib "^1.11.1"
-
-"@aws-crypto/util@^5.2.0":
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da"
- integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==
- dependencies:
- "@aws-sdk/types" "^3.222.0"
- "@smithy/util-utf8" "^2.0.0"
- tslib "^2.6.2"
-
-"@aws-sdk/types@^3.222.0":
- version "3.468.0"
- resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.468.0.tgz#f97b34fc92a800d1d8b866f47693ae8f3d46517b"
- integrity sha512-rx/9uHI4inRbp2tw3Y4Ih4PNZkVj32h7WneSg3MVgVjAoVD5Zti9KhS5hkvsBxfgmQmg0AQbE+b1sy5WGAgntA==
- dependencies:
- "@smithy/types" "^2.7.0"
- tslib "^2.5.0"
-
-"@aws-sdk/util-utf8-browser@^3.0.0":
- version "3.55.0"
- resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.55.0.tgz#a045bf1a93f6e0ff9c846631b168ea55bbb37668"
- integrity sha512-ljzqJcyjfJpEVSIAxwtIS8xMRUly84BdjlBXyp6cu4G8TUufgjNS31LWdhyGhgmW5vYBNr+LTz0Kwf6J+ou7Ug==
- dependencies:
- tslib "^2.3.1"
-
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789"
@@ -822,82 +771,6 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
-"@smithy/eventstream-codec@^2.0.15":
- version "2.0.15"
- resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-2.0.15.tgz#733e638fd38e7e264bc0429dbda139bab950bd25"
- integrity sha512-crjvz3j1gGPwA0us6cwS7+5gAn35CTmqu/oIxVbYJo2Qm/sGAye6zGJnMDk3BKhWZw5kcU1G4MxciTkuBpOZPg==
- dependencies:
- "@aws-crypto/crc32" "3.0.0"
- "@smithy/types" "^2.7.0"
- "@smithy/util-hex-encoding" "^2.0.0"
- tslib "^2.5.0"
-
-"@smithy/is-array-buffer@^2.0.0":
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz#8fa9b8040651e7ba0b2f6106e636a91354ff7d34"
- integrity sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==
- dependencies:
- tslib "^2.5.0"
-
-"@smithy/signature-v4@^2.0.18":
- version "2.0.18"
- resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-2.0.18.tgz#53b78b238edaa84cc8d61faf67d2b3c926cdd698"
- integrity sha512-SJRAj9jT/l9ocm8D0GojMbnA1sp7I4JeStOQ4lEXI8A5eHE73vbjlzlqIFB7cLvIgau0oUl4cGVpF9IGCrvjlw==
- dependencies:
- "@smithy/eventstream-codec" "^2.0.15"
- "@smithy/is-array-buffer" "^2.0.0"
- "@smithy/types" "^2.7.0"
- "@smithy/util-hex-encoding" "^2.0.0"
- "@smithy/util-middleware" "^2.0.8"
- "@smithy/util-uri-escape" "^2.0.0"
- "@smithy/util-utf8" "^2.0.2"
- tslib "^2.5.0"
-
-"@smithy/types@^2.7.0":
- version "2.7.0"
- resolved "https://registry.yarnpkg.com/@smithy/types/-/types-2.7.0.tgz#6ed9ba5bff7c4d28c980cff967e6d8456840a4f3"
- integrity sha512-1OIFyhK+vOkMbu4aN2HZz/MomREkrAC/HqY5mlJMUJfGrPRwijJDTeiN8Rnj9zUaB8ogXAfIOtZrrgqZ4w7Wnw==
- dependencies:
- tslib "^2.5.0"
-
-"@smithy/util-buffer-from@^2.0.0":
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz#7eb75d72288b6b3001bc5f75b48b711513091deb"
- integrity sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==
- dependencies:
- "@smithy/is-array-buffer" "^2.0.0"
- tslib "^2.5.0"
-
-"@smithy/util-hex-encoding@^2.0.0":
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz#0aa3515acd2b005c6d55675e377080a7c513b59e"
- integrity sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==
- dependencies:
- tslib "^2.5.0"
-
-"@smithy/util-middleware@^2.0.8":
- version "2.0.8"
- resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-2.0.8.tgz#2ec1da1190d09b69512ce0248ebd5e819e3c8a92"
- integrity sha512-qkvqQjM8fRGGA8P2ydWylMhenCDP8VlkPn8kiNuFEaFz9xnUKC2irfqsBSJrfrOB9Qt6pQsI58r3zvvumhFMkw==
- dependencies:
- "@smithy/types" "^2.7.0"
- tslib "^2.5.0"
-
-"@smithy/util-uri-escape@^2.0.0":
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz#19955b1a0f517a87ae77ac729e0e411963dfda95"
- integrity sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==
- dependencies:
- tslib "^2.5.0"
-
-"@smithy/util-utf8@^2.0.0", "@smithy/util-utf8@^2.0.2":
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.0.2.tgz#626b3e173ad137208e27ed329d6bea70f4a1a7f7"
- integrity sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==
- dependencies:
- "@smithy/util-buffer-from" "^2.0.0"
- tslib "^2.5.0"
-
"@szmarczak/http-timer@^4.0.5":
version "4.0.6"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807"
@@ -915,6 +788,11 @@
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
+"@tootallnate/once@2":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
+ integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
+
"@tsconfig/node10@^1.0.7":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
@@ -990,6 +868,13 @@
"@types/node" "*"
"@types/responselike" "*"
+"@types/dompurify@^3.0.5":
+ version "3.0.5"
+ resolved "https://registry.yarnpkg.com/@types/dompurify/-/dompurify-3.0.5.tgz#02069a2fcb89a163bacf1a788f73cb415dd75cb7"
+ integrity sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==
+ dependencies:
+ "@types/trusted-types" "*"
+
"@types/glob@^7.1.1":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
@@ -1037,6 +922,15 @@
jest-matcher-utils "^27.0.0"
pretty-format "^27.0.0"
+"@types/jsdom@^21.1.6":
+ version "21.1.6"
+ resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-21.1.6.tgz#bcbc7b245787ea863f3da1ef19aa1dcfb9271a1b"
+ integrity sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==
+ dependencies:
+ "@types/node" "*"
+ "@types/tough-cookie" "*"
+ parse5 "^7.0.0"
+
"@types/json-buffer@~3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64"
@@ -1086,6 +980,16 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
+"@types/tough-cookie@*":
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304"
+ integrity sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==
+
+"@types/trusted-types@*":
+ version "2.0.7"
+ resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11"
+ integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==
+
"@types/yargs-parser@*":
version "21.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b"
@@ -1098,7 +1002,7 @@
dependencies:
"@types/yargs-parser" "*"
-abab@^2.0.3, abab@^2.0.5:
+abab@^2.0.3, abab@^2.0.5, abab@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
@@ -1915,6 +1819,13 @@ cssstyle@^2.3.0:
dependencies:
cssom "~0.3.6"
+cssstyle@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a"
+ integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==
+ dependencies:
+ rrweb-cssom "^0.6.0"
+
d@1, d@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"
@@ -1932,6 +1843,15 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
+data-urls@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4"
+ integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==
+ dependencies:
+ abab "^2.0.6"
+ whatwg-mimetype "^3.0.0"
+ whatwg-url "^12.0.0"
+
dayjs@^1.11.7:
version "1.11.7"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2"
@@ -1956,6 +1876,11 @@ decimal.js@^10.2.1:
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783"
integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
+decimal.js@^10.4.3:
+ version "10.4.3"
+ resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
+ integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
+
decompress-response@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
@@ -2093,6 +2018,18 @@ domexception@^2.0.1:
dependencies:
webidl-conversions "^5.0.0"
+domexception@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673"
+ integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==
+ dependencies:
+ webidl-conversions "^7.0.0"
+
+dompurify@^3.0.9:
+ version "3.0.9"
+ resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.9.tgz#b3f362f24b99f53498c75d43ecbd784b0b3ad65e"
+ integrity sha512-uyb4NDIvQ3hRn6NiC+SIFaP4mJ/MdXlvtunaqK9Bn6dD3RuB/1S/gasEjDHD8eiaqdSael2vBv+hOs7Y+jhYOQ==
+
dotenv-expand@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-9.0.0.tgz#1fd37e2cd63ea0b5f7389fb87256efc38b035b26"
@@ -2133,6 +2070,11 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
dependencies:
once "^1.4.0"
+entities@^4.4.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
+ integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
+
error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
@@ -2140,22 +2082,14 @@ error-ex@^1.3.1:
dependencies:
is-arrayish "^0.2.1"
-es5-ext@^0.10.12, es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.47, es5-ext@^0.10.49, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@^0.10.59, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46:
- version "0.10.61"
- resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.61.tgz#311de37949ef86b6b0dcea894d1ffedb909d3269"
- integrity sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA==
- dependencies:
- es6-iterator "^2.0.3"
- es6-symbol "^3.1.3"
- next-tick "^1.1.0"
-
-es5-ext@^0.10.61, es5-ext@^0.10.62:
- version "0.10.62"
- resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5"
- integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==
+es5-ext@^0.10.12, es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.47, es5-ext@^0.10.49, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@^0.10.59, es5-ext@^0.10.61, es5-ext@^0.10.62, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46:
+ version "0.10.63"
+ resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.63.tgz#9c222a63b6a332ac80b1e373b426af723b895bd6"
+ integrity sha512-hUCZd2Byj/mNKjfP9jXrdVZ62B8KuA/VoK7X8nUh5qT+AxDmcbvZz041oDVZdbIN1qW6XY9VDNwzkvKnZvK2TQ==
dependencies:
es6-iterator "^2.0.3"
es6-symbol "^3.1.3"
+ esniff "^2.0.1"
next-tick "^1.1.0"
es6-iterator@^2.0.3, es6-iterator@~2.0.3:
@@ -2232,6 +2166,16 @@ esniff@^1.1.0:
d "1"
es5-ext "^0.10.12"
+esniff@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308"
+ integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==
+ dependencies:
+ d "^1.0.1"
+ es5-ext "^0.10.62"
+ event-emitter "^0.3.5"
+ type "^2.7.2"
+
esprima@^4.0.0, esprima@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
@@ -2505,6 +2449,15 @@ form-data@^3.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
+form-data@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+ integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.8"
+ mime-types "^2.1.12"
+
formidable@^1.2.0:
version "1.2.6"
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.6.tgz#d2a51d60162bbc9b4a055d8457a7c75315d1a168"
@@ -2763,6 +2716,13 @@ html-encoding-sniffer@^2.0.1:
dependencies:
whatwg-encoding "^1.0.5"
+html-encoding-sniffer@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
+ integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==
+ dependencies:
+ whatwg-encoding "^2.0.0"
+
html-escaper@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
@@ -2782,6 +2742,15 @@ http-proxy-agent@^4.0.1:
agent-base "6"
debug "4"
+http-proxy-agent@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
+ integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
+ dependencies:
+ "@tootallnate/once" "2"
+ agent-base "6"
+ debug "4"
+
http2-wrapper@^1.0.0-beta.5.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d"
@@ -2810,6 +2779,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24:
dependencies:
safer-buffer ">= 2.1.2 < 3"
+iconv-lite@0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
+ integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3.0.0"
+
ieee754@1.1.13:
version "1.1.13"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
@@ -3531,6 +3507,35 @@ jsdom@^16.6.0:
ws "^7.4.6"
xml-name-validator "^3.0.0"
+jsdom@^22.1.0:
+ version "22.1.0"
+ resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-22.1.0.tgz#0fca6d1a37fbeb7f4aac93d1090d782c56b611c8"
+ integrity sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==
+ dependencies:
+ abab "^2.0.6"
+ cssstyle "^3.0.0"
+ data-urls "^4.0.0"
+ decimal.js "^10.4.3"
+ domexception "^4.0.0"
+ form-data "^4.0.0"
+ html-encoding-sniffer "^3.0.0"
+ http-proxy-agent "^5.0.0"
+ https-proxy-agent "^5.0.1"
+ is-potential-custom-element-name "^1.0.1"
+ nwsapi "^2.2.4"
+ parse5 "^7.1.2"
+ rrweb-cssom "^0.6.0"
+ saxes "^6.0.0"
+ symbol-tree "^3.2.4"
+ tough-cookie "^4.1.2"
+ w3c-xmlserializer "^4.0.0"
+ webidl-conversions "^7.0.0"
+ whatwg-encoding "^2.0.0"
+ whatwg-mimetype "^3.0.0"
+ whatwg-url "^12.0.1"
+ ws "^8.13.0"
+ xml-name-validator "^4.0.0"
+
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
@@ -4006,6 +4011,11 @@ nwsapi@^2.2.0:
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
+nwsapi@^2.2.4:
+ version "2.2.7"
+ resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30"
+ integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==
+
object-assign@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -4147,6 +4157,13 @@ parse5@6.0.1:
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
+parse5@^7.0.0, parse5@^7.1.2:
+ version "7.1.2"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32"
+ integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==
+ dependencies:
+ entities "^4.4.0"
+
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
@@ -4316,6 +4333,11 @@ punycode@^2.1.0, punycode@^2.1.1:
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+punycode@^2.3.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
+ integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+
qs@^6.5.1:
version "6.10.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
@@ -4469,6 +4491,11 @@ rimraf@^3.0.0:
dependencies:
glob "^7.1.3"
+rrweb-cssom@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1"
+ integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==
+
run-async@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
@@ -4505,7 +4532,7 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
-"safer-buffer@>= 2.1.2 < 3":
+"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -4527,6 +4554,13 @@ saxes@^5.0.1:
dependencies:
xmlchars "^2.2.0"
+saxes@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5"
+ integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==
+ dependencies:
+ xmlchars "^2.2.0"
+
seek-bzip@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.6.tgz#35c4171f55a680916b52a07859ecf3b5857f21c4"
@@ -5021,7 +5055,7 @@ token-types@^4.1.1:
"@tokenizer/token" "^0.3.0"
ieee754 "^1.2.1"
-tough-cookie@^4.0.0:
+tough-cookie@^4.0.0, tough-cookie@^4.1.2:
version "4.1.3"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf"
integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==
@@ -5038,6 +5072,13 @@ tr46@^2.1.0:
dependencies:
punycode "^2.1.1"
+tr46@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469"
+ integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==
+ dependencies:
+ punycode "^2.3.0"
+
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
@@ -5088,26 +5129,11 @@ ts-node@^10.7.0:
v8-compile-cache-lib "^3.0.0"
yn "3.1.1"
-tslib@^1.11.1:
- version "1.14.1"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
- integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-
tslib@^2.1.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
-tslib@^2.3.1:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
- integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
-
-tslib@^2.5.0, tslib@^2.6.2:
- version "2.6.2"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
- integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
-
type-check@~0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
@@ -5286,6 +5312,13 @@ w3c-xmlserializer@^2.0.0:
dependencies:
xml-name-validator "^3.0.0"
+w3c-xmlserializer@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073"
+ integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==
+ dependencies:
+ xml-name-validator "^4.0.0"
+
walker@^1.0.7:
version "1.0.8"
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"
@@ -5323,6 +5356,11 @@ webidl-conversions@^6.1.0:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
+webidl-conversions@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
+ integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
+
whatwg-encoding@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
@@ -5330,11 +5368,31 @@ whatwg-encoding@^1.0.5:
dependencies:
iconv-lite "0.4.24"
+whatwg-encoding@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53"
+ integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==
+ dependencies:
+ iconv-lite "0.6.3"
+
whatwg-mimetype@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
+whatwg-mimetype@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"
+ integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==
+
+whatwg-url@^12.0.0, whatwg-url@^12.0.1:
+ version "12.0.1"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c"
+ integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==
+ dependencies:
+ tr46 "^4.1.1"
+ webidl-conversions "^7.0.0"
+
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
@@ -5420,11 +5478,21 @@ ws@^7.4.6, ws@^7.5.3:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67"
integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==
+ws@^8.13.0:
+ version "8.16.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4"
+ integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==
+
xml-name-validator@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
+xml-name-validator@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
+ integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
+
xml2js@0.4.19:
version "0.4.19"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"
diff --git a/services/stream-functions/.gitignore b/services/stream-functions/.gitignore
deleted file mode 100644
index 546c3da2a7..0000000000
--- a/services/stream-functions/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# package directories
-node_modules
-jspm_packages
-
-# Serverless directories
-.serverless
-.webpack
-.repack
diff --git a/services/stream-functions/README.md b/services/stream-functions/README.md
deleted file mode 100644
index fdb3b12cf9..0000000000
--- a/services/stream-functions/README.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# stream-functions
-
-## Configuration - AWS Systems Manager Parameter Store (SSM)
-
-The following values are used to configure the deployment of this service (see below for more background and context).
-| Parameter | Required? | Accepts a default? | Accepts a branch override? | Purpose |
-| --- | :---: | :---: | :---: | --- |
-| .../iam/path | N | Y | Y | Specifies the [IAM Path](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html#identifiers-friendly-names) at which all IAM objects should be created. The default value is "/". The path variable in IAM is used for grouping related users and groups in a unique namespace, usually for organizational purposes.|
-| .../iam/permissionsBoundaryPolicy | N | Y | Y | Specifies the [IAM Permissions Boundary](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html) that should be attached to all IAM objects. A permissions boundary is an advanced feature for using a managed policy to set the maximum permissions that an identity-based policy can grant to an IAM entity. If set, this parmeter should contain the full ARN to the policy.|
-| sesSourceEmailAddress | N | Y | Y | The email address with which the apllication sends the email. This email address must be verified in SES.|
-| reviewerEmailAddress | N | Y | Y | Email address of the submissions reviewer.|
-
-This project uses [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html), often referred to as simply SSM, to inject environment specific, project specific, and/or sensitive information into the deployment.
-In short, SSM is an AWS service that allows users to store (optionally) encrypted strings in a directory like hierarchy. For example, "/my/first/ssm/param" is a valid path for a parameter. Access to this service and even individual paramters is granted via AWS IAM.
-
-An example of environment specific information is the id of a VPC into which we want to deploy. This VPC id should not be checked in to git, as it may vary from environment to environment, so we would use SSM to store the id information and use the [Serverless Framework's SSM lookup capability](https://www.serverless.com/framework/docs/providers/aws/guide/variables/#reference-variables-using-the-ssm-parameter-store) to fetcn the information at deploy time.
-
-This project has also implemented a pattern for specifying defaults for variables, while allowing for branch (environment specific overrides). That pattern looks like this:
-
-```
-sesSourceEmailAddress: ${ssm:/configuration/${self:custom.stage}/sesSourceEmailAddress~true, ssm:/configuration/default/sesSourceEmailAddress~true}
-```
-
-The above syntax says "look for an ssm parameter at /configuration//sesSourceEmailAddress; if there isn't one, look for a parameter at /configuration/default/sesSourceEmailAddress". With this logic, we can specify a generic value for this variable that would apply to all environments deployed to a given account, but if we wish to set a different value for a specific environment (branch), we can create a parameter at the branch specific path and it will take precedence.
-
-In the above tabular documentation, you will see columns for "Accepts default?" and "Accepts a branch override?". These columns relate to the above convention of searching for a branch specific override but falling back to a default parameter. It's important to note if a parameter can accept a default or can accept an override, because not all can do both. For example, a parameter used to specify Okta App information cannot be set as a default, because Okta can only support one environment (branch) at a time; so, okta_metadata_url is a good example of a parameter that can only be specified on a branch by branch basis, and never as a default.
-
-In the above documentation, you will also see the Parameter value denoted as ".../iam/path", for example. This notation is meant to represent the core of the parameter's expected path. The "..." prefix is meant to be a placeholder for either "/configuration/default" (in the case of a default value) or "/configuration/myfavoritebranch" (in the case of specifying a branch specific override for the myfavoritebranch branch.
diff --git a/services/stream-functions/handlers/emailReviewer.js b/services/stream-functions/handlers/emailReviewer.js
deleted file mode 100644
index b3dfacadee..0000000000
--- a/services/stream-functions/handlers/emailReviewer.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import * as ses from "./../libs/ses-lib";
-
-exports.handler = function (event, context, callback) {
- console.log("Received event:", JSON.stringify(event, null, 2));
- event.Records.forEach(function (record) {
- var params = (function (eventName) {
- switch (eventName) {
- case "INSERT":
- return ses.getSESEmailParams({
- ToAddresses: [process.env.reviewerEmail],
- Source: process.env.emailSource,
- Subject: `New APS Submission - ${record.dynamodb.NewImage.transmittalNumber.S}`,
- Text: getReviewerEmailBody(
- record.dynamodb.NewImage,
- "A new APS submission has been received."
- ),
- });
- case "MODIFY":
- return ses.getSESEmailParams({
- ToAddresses: [process.env.reviewerEmail],
- Source: process.env.emailSource,
- Subject: `Updated APS Submission - ${record.dynamodb.NewImage.transmittalNumber.S}`,
- Text: getReviewerEmailBody(
- record.dynamodb.NewImage,
- "An update to an existing APS submission has been received."
- ),
- });
- case "REMOVE":
- return ses.getSESEmailParams({
- ToAddresses: [process.env.reviewerEmail],
- Source: process.env.emailSource,
- Subject: `Updated APS Submission - ${record.dynamodb.OldImage.transmittalNumber.S}`,
- Text: getReviewerEmailBody(
- record.dynamodb.OldImage,
- "A request to delete the below APS request has been processed."
- ),
- });
- default:
- return 30;
- }
- })(record.eventName);
-
- ses.sendEmail(params);
- });
- callback(null, "message");
-};
-
-function getReviewerEmailBody(image, summary) {
- return `
-Hi,
-
-${summary}
-
-Details:
-- APS ID (Transmittal Number): ${image.transmittalNumber.S}
-State: ${image.territory.S}
-Submitter Name: ${image.firstName.S} ${image.lastName.S}
-Submitter Contact Email: ${image.email.S}
-
-Regards,
-APS Submission App
-
-`;
-}
diff --git a/services/stream-functions/handlers/emailSubmitter.js b/services/stream-functions/handlers/emailSubmitter.js
deleted file mode 100644
index e6ac0f9f63..0000000000
--- a/services/stream-functions/handlers/emailSubmitter.js
+++ /dev/null
@@ -1,79 +0,0 @@
-import * as ses from "./../libs/ses-lib";
-
-exports.handler = function (event, context, callback) {
- console.log("Received event:", JSON.stringify(event, null, 2));
- event.Records.forEach(function (record) {
- var params = (function (eventName) {
- switch (eventName) {
- case "INSERT":
- return ses.getSESEmailParams({
- ToAddresses: [record.dynamodb.NewImage.email.S],
- Source: process.env.emailSource,
- Subject: `New ACME APS submission received! - ${record.dynamodb.NewImage.transmittalNumber.S}`,
- Text: `
-Hi ${record.dynamodb.NewImage.firstName.S},
-
-We are writing to let you know we've received your Amendment to Planned Settlement (APS) submission!
-It is under review.
-No additional action is needed on your part.
-
-APS ID: ${record.dynamodb.NewImage.transmittalNumber.S}
-
-Thank you for using our APS submission system!
-
-Regards,
-APS Team
-
-`,
- });
- case "MODIFY":
- return ses.getSESEmailParams({
- ToAddresses: [record.dynamodb.NewImage.email.S],
- Source: process.env.emailSource,
- Subject: `Updated ACME APS submission received! - ${record.dynamodb.NewImage.transmittalNumber.S}`,
- Text: `
- Hi ${record.dynamodb.NewImage.firstName.S},
-
- We are writing to let you know we've received an update to your Amendment to Planned Settlement (APS) submission!
- It is under review.
- No additional action is needed on your part.
-
- APS ID: ${record.dynamodb.NewImage.transmittalNumber.S}
-
- Thank you for using our APS submission system!
-
- Regards,
- APS Team
-
- `,
- });
- case "REMOVE":
- return ses.getSESEmailParams({
- ToAddresses: [record.dynamodb.OldImage.email.S],
- Source: process.env.emailSource,
- Subject: `Your ACME APS submission has been deleted - ${record.dynamodb.OldImage.transmittalNumber.S}`,
- Text: `
- Hi ${record.dynamodb.OldImage.firstName.S},
-
- We received a request to delete your Amendment to Planned Settlement (APS) submission.
- We are writing to let you know that we have processed that request.
- No additional action is needed on your part.
-
- APS ID: ${record.dynamodb.OldImage.transmittalNumber.S}
-
- Thank you for using our APS submission system!
-
- Regards,
- APS Team
-
- `,
- });
- default:
- return 30;
- }
- })(record.eventName);
-
- ses.sendEmail(params);
- });
- callback(null, "message");
-};
diff --git a/services/stream-functions/libs/ses-lib.js b/services/stream-functions/libs/ses-lib.js
deleted file mode 100644
index 89ddd3c6db..0000000000
--- a/services/stream-functions/libs/ses-lib.js
+++ /dev/null
@@ -1,35 +0,0 @@
-const AWS = require("aws-sdk");
-var ses = new AWS.SES({ region: "us-east-1" });
-
-export function getSESEmailParams(email) {
- let emailParams = {
- Destination: {
- ToAddresses: email.ToAddresses,
- },
- Message: {
- Body: {
- Text: {
- Charset: "UTF-8",
- Data: email.Text,
- },
- },
- Subject: {
- Charset: "UTF-8",
- Data: email.Subject,
- },
- },
- Source: email.Source,
- };
-
- return emailParams;
-}
-
-export function sendEmail(params) {
- ses.sendEmail(params, function (err, data) {
- if (err) {
- console.error(err);
- } else {
- console.log(data);
- }
- });
-}
diff --git a/services/stream-functions/package.json b/services/stream-functions/package.json
deleted file mode 100644
index cf7f782e4d..0000000000
--- a/services/stream-functions/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "name": "stream-functions",
- "description": "",
- "version": "1.0.0",
- "dependencies": {},
- "devDependencies": {
- "serverless-s3-bucket-helper": "Enterprise-CMCS/serverless-s3-bucket-helper#0.1.1"
- }
-}
diff --git a/services/stream-functions/serverless.yml b/services/stream-functions/serverless.yml
deleted file mode 100644
index 09cba88af6..0000000000
--- a/services/stream-functions/serverless.yml
+++ /dev/null
@@ -1,71 +0,0 @@
-# Refer to the README.md file in within this service directory to configure all ssm parameters required for this service.
-service: stream-functions
-
-frameworkVersion: "3"
-
-package:
- individually: true
-
-plugins:
- - serverless-bundle
- - serverless-dotenv-plugin
- - serverless-stack-termination-protection
- - serverless-idempotency-helper
- - serverless-s3-bucket-helper
-
-provider:
- name: aws
- runtime: nodejs16.x
- region: us-east-1
- iam:
- role:
- path: ${ssm:/configuration/${self:custom.stage}/iam/path, ssm:/configuration/default/iam/path, "/"}
- permissionsBoundary: ${ssm:/configuration/${self:custom.stage}/iam/permissionsBoundaryPolicy, ssm:/configuration/default/iam/permissionsBoundaryPolicy, ""}
- statements:
- - Effect: "Allow"
- Action:
- - dynamodb:DescribeStream
- - dynamodb:GetRecords
- - dynamodb:GetShardIterator
- - dynamodb:ListStreams
- Resource: ${self:custom.tableStreamArn}
- - Effect: "Allow"
- Action:
- - ses:SendEmail
- - ses:SendRawEmail
- Resource: "*"
-
-custom:
- stage: ${opt:stage, self:provider.stage}
- region: ${opt:region, self:provider.region}
- serverlessTerminationProtection:
- stages:
- - master
- - val
- - prod
- tableStreamArn: ${cf:database-${self:custom.stage}.CoreSetTableStreamArn}
- sesSourceEmailAddress: ${ssm:/configuration/${self:custom.stage}/sesSourceEmailAddress, ssm:/configuration/default/sesSourceEmailAddress, "admin@example.com"}
- reviewerEmailAddress: ${ssm:/configuration/${self:custom.stage}/reviewerEmailAddress, ssm:/configuration/default/reviewerEmailAddress, "reviewteam@example.com"}
-
-functions:
- emailSubmitter:
- handler: handlers/emailSubmitter.handler
- events:
- - stream:
- arn: ${self:custom.tableStreamArn}
- startingPosition: LATEST
- maximumRetryAttempts: 2
- environment:
- emailSource: ${self:custom.sesSourceEmailAddress}
- maximumRetryAttempts: 2
- emailReviewer:
- handler: handlers/emailReviewer.handler
- events:
- - stream:
- arn: ${self:custom.tableStreamArn}
- startingPosition: LATEST
- maximumRetryAttempts: 2
- environment:
- emailSource: ${self:custom.sesSourceEmailAddress}
- reviewerEmail: ${self:custom.reviewerEmailAddress}
- maximumRetryAttempts: 2
diff --git a/services/stream-functions/yarn.lock b/services/stream-functions/yarn.lock
deleted file mode 100644
index 8886796bb4..0000000000
--- a/services/stream-functions/yarn.lock
+++ /dev/null
@@ -1,7 +0,0 @@
-# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-# yarn lockfile v1
-
-
-serverless-s3-bucket-helper@Enterprise-CMCS/serverless-s3-bucket-helper#0.1.1:
- version "1.0.0"
- resolved "https://codeload.github.com/Enterprise-CMCS/serverless-s3-bucket-helper/tar.gz/f0f6d6a1ffe54e292f0afc93777764bce16a4037"
diff --git a/services/ui-auth/handlers/createUsers.js b/services/ui-auth/handlers/createUsers.js
index 0f176630ab..77c2cadd42 100644
--- a/services/ui-auth/handlers/createUsers.js
+++ b/services/ui-auth/handlers/createUsers.js
@@ -12,6 +12,7 @@ async function myHandler(event, context, callback) {
UserPoolId: userPoolId,
Username: users[i].username,
DesiredDeliveryMediums: ["EMAIL"],
+ MessageAction: "SUPPRESS",
UserAttributes: users[i].attributes,
};
var passwordData = {
diff --git a/services/ui-auth/package.json b/services/ui-auth/package.json
index ddc79cf9be..f5372e61e2 100644
--- a/services/ui-auth/package.json
+++ b/services/ui-auth/package.json
@@ -12,7 +12,7 @@
"serverless-s3-bucket-helper": "Enterprise-CMCS/serverless-s3-bucket-helper#0.1.1"
},
"dependencies": {
- "aws-sdk": "^2.1310.0",
+ "aws-sdk": "^2.1531.0",
"xml2js": "0.6.0"
}
}
diff --git a/services/ui-auth/serverless.yml b/services/ui-auth/serverless.yml
index 108ece315e..1b264622df 100644
--- a/services/ui-auth/serverless.yml
+++ b/services/ui-auth/serverless.yml
@@ -36,7 +36,6 @@ custom:
- master
- val
- prod
- sesSourceEmailAddress: ${ssm:/configuration/${self:custom.stage}/sesSourceEmailAddress, ssm:/configuration/default/sesSourceEmailAddress, ""}
attachments_bucket_arn: ${cf:uploads-${self:custom.stage}.AttachmentsBucketArn}
api_gateway_rest_api_name: ${cf:app-api-${self:custom.stage}.ApiGatewayRestApiName}
okta_metadata_url: ${ssm:/configuration/${self:custom.stage}/okta_metadata_url, ""}
@@ -61,11 +60,6 @@ functions:
resources:
Conditions:
- CreateEmailConfiguration:
- Fn::Not:
- - Fn::Equals:
- - ""
- - ${self:custom.sesSourceEmailAddress}
BackWithOkta:
Fn::Not:
- Fn::Equals:
@@ -80,12 +74,6 @@ resources:
- email
AutoVerifiedAttributes:
- email
- EmailConfiguration:
- Fn::If:
- - CreateEmailConfiguration
- - EmailSendingAccount: DEVELOPER
- SourceArn: !Sub arn:aws:ses:us-east-1:${AWS::AccountId}:identity/${self:custom.sesSourceEmailAddress}
- - !Ref AWS::NoValue
Schema:
- Name: given_name
AttributeDataType: String
diff --git a/services/ui-auth/yarn.lock b/services/ui-auth/yarn.lock
index d4877c03b2..f44e75cdd7 100644
--- a/services/ui-auth/yarn.lock
+++ b/services/ui-auth/yarn.lock
@@ -7,10 +7,10 @@ available-typed-arrays@^1.0.5:
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
-aws-sdk@^2.1310.0:
- version "2.1326.0"
- resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1326.0.tgz#91da60f08d10e4e1db0640c97bc7d2298fde8f17"
- integrity sha512-LSGiO4RSooupHnkvYbPOuOYqwAxmcnYinwIxBz4P1YI8ulhZZ/pypOj/HKqC629UyhY1ndSMtlM1l56U74UclA==
+aws-sdk@^2.1531.0:
+ version "2.1545.0"
+ resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1545.0.tgz#8678ae8117a426d4a6602408e7f47f176356d7ca"
+ integrity sha512-iDUv6ksG7lTA0l/HlOgYdO6vfYFA1D2/JzAEXSdgKY0C901WgJqBtfs2CncOkCgDe2CjmlMuqciBzAfxCIiKFA==
dependencies:
buffer "4.9.2"
events "1.1.1"
@@ -21,7 +21,7 @@ aws-sdk@^2.1310.0:
url "0.10.3"
util "^0.12.4"
uuid "8.0.0"
- xml2js "0.4.19"
+ xml2js "0.6.2"
base64-js@^1.0.2:
version "1.5.1"
@@ -213,14 +213,6 @@ which-typed-array@^1.1.2:
has-tostringtag "^1.0.0"
is-typed-array "^1.1.10"
-xml2js@0.4.19:
- version "0.4.19"
- resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"
- integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==
- dependencies:
- sax ">=0.6.0"
- xmlbuilder "~9.0.1"
-
xml2js@0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.0.tgz#07afc447a97d2bd6507a1f76eeadddb09f7a8282"
@@ -229,12 +221,15 @@ xml2js@0.6.0:
sax ">=0.6.0"
xmlbuilder "~11.0.0"
+xml2js@0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499"
+ integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==
+ dependencies:
+ sax ">=0.6.0"
+ xmlbuilder "~11.0.0"
+
xmlbuilder@~11.0.0:
version "11.0.1"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
-
-xmlbuilder@~9.0.1:
- version "9.0.7"
- resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
- integrity sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==
diff --git a/services/ui-src/package.json b/services/ui-src/package.json
index efe6ca2f53..781d82659d 100644
--- a/services/ui-src/package.json
+++ b/services/ui-src/package.json
@@ -20,7 +20,7 @@
"node-fetch": "3.3.1",
"node-forge": "1.3.1",
"object-path": "^0.11.8",
- "pac-resolver": "6.0.2",
+ "pac-resolver": "7.0.1",
"react": "^17.0.1",
"react-bootstrap": "^2.0.1",
"react-dom": "^17.0.1",
diff --git a/services/ui-src/public/index.html b/services/ui-src/public/index.html
index f5556f0f70..4448c12327 100644
--- a/services/ui-src/public/index.html
+++ b/services/ui-src/public/index.html
@@ -33,13 +33,12 @@
/>
@@ -51,7 +50,7 @@