diff --git a/.env b/.env index 00b58cbde6..447fd3c1b4 100644 --- a/.env +++ b/.env @@ -12,7 +12,7 @@ UI_PORT=3000 # openssl rand -base64 32 AUTH_SECRET="N/c6mnaS5+SWq81+819OrzQZlmx1Vxtp/orjttJSmw8=" # Google Tag Manager ID -NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID="GTM-XXX" +NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID="" #### Prowler API Configuration #### diff --git a/.github/workflows/api-pull-request.yml b/.github/workflows/api-pull-request.yml index 82a9fb8c60..db826d7a46 100644 --- a/.github/workflows/api-pull-request.yml +++ b/.github/workflows/api-pull-request.yml @@ -169,8 +169,9 @@ jobs: - name: Safety working-directory: ./api if: steps.are-non-ignored-files-changed.outputs.any_changed == 'true' + # 76352 and 76353 come from SDK, but they cannot upgrade it yet. It does not affect API run: | - poetry run safety check --ignore 70612,66963,74429 + poetry run safety check --ignore 70612,66963,74429,76352,76353 - name: Vulture working-directory: ./api diff --git a/.github/workflows/pull-request-check-changelog.yml b/.github/workflows/pull-request-check-changelog.yml new file mode 100644 index 0000000000..19237b9c63 --- /dev/null +++ b/.github/workflows/pull-request-check-changelog.yml @@ -0,0 +1,83 @@ +name: Check Changelog + +on: + pull_request: + types: [opened, synchronize, reopened, labeled, unlabeled] + +jobs: + check-changelog: + if: contains(github.event.pull_request.labels.*.name, 'no-changelog') == false + runs-on: ubuntu-latest + permissions: + pull-requests: write + env: + MONITORED_FOLDERS: "api ui prowler" + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + + - name: Get list of changed files + id: changed_files + run: | + git fetch origin ${{ github.base_ref }} + git diff --name-only origin/${{ github.base_ref }}...HEAD > changed_files.txt + cat changed_files.txt + + - name: Check for folder changes and changelog presence + id: check_folders + run: | + missing_changelogs="" + + for folder in $MONITORED_FOLDERS; do + if grep -q "^${folder}/" changed_files.txt; then + echo "Detected changes in ${folder}/" + if ! grep -q "^${folder}/CHANGELOG.md$" changed_files.txt; then + echo "No changelog update found for ${folder}/" + missing_changelogs="${missing_changelogs}- \`${folder}\`\n" + fi + fi + done + + echo "missing_changelogs<> $GITHUB_OUTPUT + echo -e "${missing_changelogs}" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + - name: Find existing changelog comment + id: find_comment + uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e #v3.1.0 + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: '' + + - name: Comment on PR if changelog is missing + if: steps.check_folders.outputs.missing_changelogs != '' + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 + with: + issue-number: ${{ github.event.pull_request.number }} + comment-id: ${{ steps.find_comment.outputs.comment-id }} + body: | + + ⚠️ **Changes detected in the following folders without a corresponding update to the `CHANGELOG.md`:** + + ${{ steps.check_folders.outputs.missing_changelogs }} + + Please add an entry to the corresponding `CHANGELOG.md` file to maintain a clear history of changes. + + - name: Comment on PR if all changelogs are present + if: steps.check_folders.outputs.missing_changelogs == '' + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0 + with: + issue-number: ${{ github.event.pull_request.number }} + comment-id: ${{ steps.find_comment.outputs.comment-id }} + body: | + + ✅ All necessary `CHANGELOG.md` files have been updated. Great job! 🎉 + + - name: Fail if changelog is missing + if: steps.check_folders.outputs.missing_changelogs != '' + run: | + echo "ERROR: Missing changelog updates in some folders." + exit 1 diff --git a/api/.gitignore b/api/.gitignore deleted file mode 100644 index a215af5677..0000000000 --- a/api/.gitignore +++ /dev/null @@ -1,168 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.pyc -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal -/_data/ - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/latest/usage/project/#working-with-version-control -.pdm.toml -.pdm-python -.pdm-build/ - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -*.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -.idea/ - -# VSCode -.vscode/ diff --git a/api/.pre-commit-config.yaml b/api/.pre-commit-config.yaml deleted file mode 100644 index 1cd04529c3..0000000000 --- a/api/.pre-commit-config.yaml +++ /dev/null @@ -1,91 +0,0 @@ -repos: - ## GENERAL - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 - hooks: - - id: check-merge-conflict - - id: check-yaml - args: ["--unsafe"] - - id: check-json - - id: end-of-file-fixer - - id: trailing-whitespace - - id: no-commit-to-branch - - id: pretty-format-json - args: ["--autofix", "--no-sort-keys", "--no-ensure-ascii"] - exclude: 'src/backend/api/fixtures/dev/.*\.json$' - - ## TOML - - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks - rev: v2.13.0 - hooks: - - id: pretty-format-toml - args: [--autofix] - files: pyproject.toml - - ## BASH - - repo: https://github.com/koalaman/shellcheck-precommit - rev: v0.10.0 - hooks: - - id: shellcheck - exclude: contrib - ## PYTHON - - repo: https://github.com/astral-sh/ruff-pre-commit - # Ruff version. - rev: v0.5.0 - hooks: - # Run the linter. - - id: ruff - args: [ --fix ] - # Run the formatter. - - id: ruff-format - - - repo: https://github.com/python-poetry/poetry - rev: 1.8.0 - hooks: - - id: poetry-check - args: ["--directory=src"] - - id: poetry-lock - args: ["--no-update", "--directory=src"] - - - repo: https://github.com/hadolint/hadolint - rev: v2.13.0-beta - hooks: - - id: hadolint - args: ["--ignore=DL3013", "Dockerfile"] - - - repo: local - hooks: - - id: pylint - name: pylint - entry: bash -c 'poetry run pylint --disable=W,C,R,E -j 0 -rn -sn src/' - language: system - files: '.*\.py' - - - id: trufflehog - name: TruffleHog - description: Detect secrets in your data. - entry: bash -c 'trufflehog --no-update git file://. --only-verified --fail' - # For running trufflehog in docker, use the following entry instead: - # entry: bash -c 'docker run -v "$(pwd):/workdir" -i --rm trufflesecurity/trufflehog:latest git file:///workdir --only-verified --fail' - language: system - stages: ["commit", "push"] - - - id: bandit - name: bandit - description: "Bandit is a tool for finding common security issues in Python code" - entry: bash -c 'poetry run bandit -q -lll -x '*_test.py,./contrib/,./.venv/' -r .' - language: system - files: '.*\.py' - - - id: safety - name: safety - description: "Safety is a tool that checks your installed dependencies for known security vulnerabilities" - entry: bash -c 'poetry run safety check --ignore 70612,66963,74429' - language: system - - - id: vulture - name: vulture - description: "Vulture finds unused code in Python programs." - entry: bash -c 'poetry run vulture --exclude "contrib,.venv,tests,conftest.py" --min-confidence 100 .' - language: system - files: '.*\.py' diff --git a/docs/getting-started/requirements.md b/docs/getting-started/requirements.md index f942b38da9..37b8ab5663 100644 --- a/docs/getting-started/requirements.md +++ b/docs/getting-started/requirements.md @@ -70,9 +70,13 @@ The other three cases does not need additional configuration, `--az-cli-auth` an Prowler for Azure needs two types of permission scopes to be set: - **Microsoft Entra ID permissions**: used to retrieve metadata from the identity assumed by Prowler and specific Entra checks (not mandatory to have access to execute the tool). The permissions required by the tool are the following: - - `Domain.Read.All` + - `Directory.Read.All` - `Policy.Read.All` - `UserAuthenticationMethod.Read.All` (used only for the Entra checks related with multifactor authentication) + + ???+ note + You can replace `Directory.Read.All` with `Domain.Read.All` that is a more restrictive permission but you won't be able to run the Entra checks related with DirectoryRoles and GetUsers. + - **Subscription scope permissions**: required to launch the checks against your resources, mandatory to launch the tool. It is required to add the following RBAC builtin roles per subscription to the entity that is going to be assumed by the tool: - `Reader` - `ProwlerRole` (custom role with minimal permissions defined in [prowler-azure-custom-role](https://github.com/prowler-cloud/prowler/blob/master/permissions/prowler-azure-custom-role.json)) @@ -205,12 +209,17 @@ Prowler for M365 requires two types of permission scopes to be set (if you want - **Service Principal Application Permissions**: These are set at the **application** level and are used to retrieve data from the identity being assessed: - `AuditLog.Read.All`: Required for Entra service. - - `Domain.Read.All`: Required for all services. - - `Organization.Read.All`: Required for retrieving tenant information. + - `Directory.Read.All`: Required for all services. - `Policy.Read.All`: Required for all services. - `SharePointTenantSettings.Read.All`: Required for SharePoint service. - `User.Read` (IMPORTANT: this must be set as **delegated**): Required for the sign-in. + ???+ note + You can replace `Directory.Read.All` with `Domain.Read.All` is a more restrictive permission but you won't be able to run the Entra checks related with DirectoryRoles and GetUsers. + + > If you do this you will need to add also the `Organization.Read.All` permission to the service principal application in order to authenticate. + + - **Powershell Modules Permissions**: These are set at the `M365_USER` level, so the user used to run Prowler must have one of the following roles: - `Global Reader` (recommended): this allows you to read all roles needed. diff --git a/docs/tutorials/azure/getting-started-azure.md b/docs/tutorials/azure/getting-started-azure.md index 3fabdcc3a5..94c5d189e9 100644 --- a/docs/tutorials/azure/getting-started-azure.md +++ b/docs/tutorials/azure/getting-started-azure.md @@ -90,11 +90,14 @@ A Service Principal is required to grant Prowler the necessary privileges. Assign the following Microsoft Graph permissions: - - Domain.Read.All +- Directory.Read.All - - Policy.Read.All +- Policy.Read.All - - UserAuthenticationMethod.Read.All (optional, for MFA checks) +- UserAuthenticationMethod.Read.All (optional, for MFA checks) + +???+ note + You can replace `Directory.Read.All` with `Domain.Read.All` that is a more restrictive permission but you won't be able to run the Entra checks related with DirectoryRoles and GetUsers. 1. Go to your App Registration > `API permissions` @@ -107,7 +110,7 @@ Assign the following Microsoft Graph permissions: 3. Search and select: - - `Domain.Read.All` + - `Directory.Read.All` - `Policy.Read.All` - `UserAuthenticationMethod.Read.All` diff --git a/docs/tutorials/azure/img/domain-permission.png b/docs/tutorials/azure/img/domain-permission.png index 467ec8ff36..2d442bac46 100644 Binary files a/docs/tutorials/azure/img/domain-permission.png and b/docs/tutorials/azure/img/domain-permission.png differ diff --git a/docs/tutorials/microsoft365/getting-started-m365.md b/docs/tutorials/microsoft365/getting-started-m365.md index 838df5e542..46184d84e2 100644 --- a/docs/tutorials/microsoft365/getting-started-m365.md +++ b/docs/tutorials/microsoft365/getting-started-m365.md @@ -95,12 +95,18 @@ With this done you will have all the needed keys, summarized in the following ta ### Grant required API permissions Assign the following Microsoft Graph permissions: + - `AuditLog.Read.All`: Required for Entra service. -- `Domain.Read.All`: Required for all services. +- `Directory.Read.All`: Required for all services. - `Policy.Read.All`: Required for all services. - `SharePointTenantSettings.Read.All`: Required for SharePoint service. - `User.Read` (IMPORTANT: this is set as **delegated**): Required for the sign-in. +???+ note + You can replace `Directory.Read.All` with `Domain.Read.All` is a more restrictive permission but you won't be able to run the Entra checks related with DirectoryRoles and GetUsers. + + > If you do this you will need to add also the `Organization.Read.All` permission to the service principal application in order to authenticate. + Follow these steps to assign the permissions: 1. Go to your App Registration > Select your Prowler App created before > click on `API permissions` @@ -113,8 +119,7 @@ Follow these steps to assign the permissions: 3. Search and select every permission below and once all are selected click on `Add permissions`: - `AuditLog.Read.All`: Required for Entra service. - - `Domain.Read.All` - - `Organization.Read.All` + - `Directory.Read.All` - `Policy.Read.All` - `SharePointTenantSettings.Read.All` @@ -134,7 +139,7 @@ Follow these steps to assign the permissions: ![Permission Screenshots](./img/directory-permission-delegated.png) -6. Click `Add permissions`, then **grant admin consent** +6. After adding all the permissions, click on `Grant admin consent` ![Grant Admin Consent](./img/grant-admin-consent.png) diff --git a/docs/tutorials/microsoft365/img/app-permissions.png b/docs/tutorials/microsoft365/img/app-permissions.png index 386447d31f..eb330ee552 100644 Binary files a/docs/tutorials/microsoft365/img/app-permissions.png and b/docs/tutorials/microsoft365/img/app-permissions.png differ diff --git a/docs/tutorials/microsoft365/img/final-permissions-m365.png b/docs/tutorials/microsoft365/img/final-permissions-m365.png index 4d094ee082..601f032a8d 100644 Binary files a/docs/tutorials/microsoft365/img/final-permissions-m365.png and b/docs/tutorials/microsoft365/img/final-permissions-m365.png differ diff --git a/docs/tutorials/microsoft365/img/grant-admin-consent.png b/docs/tutorials/microsoft365/img/grant-admin-consent.png index b5953c6771..0b242308f9 100644 Binary files a/docs/tutorials/microsoft365/img/grant-admin-consent.png and b/docs/tutorials/microsoft365/img/grant-admin-consent.png differ diff --git a/docs/tutorials/reporting.md b/docs/tutorials/reporting.md index 032065052a..fef53a2a41 100644 --- a/docs/tutorials/reporting.md +++ b/docs/tutorials/reporting.md @@ -116,7 +116,7 @@ The following table shows the mapping between the CSV headers and the the provid ### JSON-OCSF -The JSON-OCSF output format implements the [Detection Finding](https://schema.ocsf.io/1.1.0/classes/detection_finding) from the [OCSF v1.1.0](https://schema.ocsf.io/1.1.0) +The JSON-OCSF output format implements the [Detection Finding](https://schema.ocsf.io/classes/detection_finding) from the [OCSF](https://schema.ocsf.io) ```json [{ diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 23d4da2c01..343a9aa566 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -37,6 +37,10 @@ All notable changes to the **Prowler SDK** are documented in this file. - Azure Databricks service integration for Azure provider, including the `databricks_workspace_vnet_injection_enabled` check [(#8008)](https://github.com/prowler-cloud/prowler/pull/8008) - Azure Databricks check `databricks_workspace_cmk_encryption_enabled` to ensure workspaces use customer-managed keys (CMK) for encryption at rest [(#8017)](https://github.com/prowler-cloud/prowler/pull/8017) - Add `storage_account_default_to_entra_authorization_enabled` check for Azure provider. [(#7981)](https://github.com/prowler-cloud/prowler/pull/7981) +- Replace `Domain.Read.All` with `Directory.Read.All` in Azure and M365 docs [(#8075)](https://github.com/prowler-cloud/prowler/pull/8075) + +### Removed +- OCSF version number references to point always to the latest [(#8064)](https://github.com/prowler-cloud/prowler/pull/8064) --- diff --git a/prowler/lib/outputs/ocsf/ocsf.py b/prowler/lib/outputs/ocsf/ocsf.py index 4c47036ef4..7cb2186f38 100644 --- a/prowler/lib/outputs/ocsf/ocsf.py +++ b/prowler/lib/outputs/ocsf/ocsf.py @@ -40,7 +40,7 @@ class OCSF(Output): - get_finding_status_id(muted: bool) -> StatusID: Returns the StatusID based on the muted value. References: - - OCSF: https://schema.ocsf.io/1.2.0/classes/detection_finding + - OCSF: https://schema.ocsf.io/classes/detection_finding - PY-OCSF-Model: https://github.com/prowler-cloud/py-ocsf-models """ diff --git a/prowler/providers/azure/services/entra/entra_service.py b/prowler/providers/azure/services/entra/entra_service.py index 547a9db459..ae6fbb3428 100644 --- a/prowler/providers/azure/services/entra/entra_service.py +++ b/prowler/providers/azure/services/entra/entra_service.py @@ -45,38 +45,45 @@ class Entra(AzureService): for tenant, client in self.clients.items(): users_list = await client.users.get() users.update({tenant: {}}) - for user in users_list.value: - users[tenant].update( - { - user.id: User( - id=user.id, - name=user.display_name, - authentication_methods=[ - AuthMethod( - id=auth_method.id, - type=getattr(auth_method, "odata_type", None), - ) - for auth_method in ( - await client.users.by_user_id( - user.id - ).authentication.methods.get() - ).value - ], - ) - } - ) + try: + for user in users_list.value: + users[tenant].update( + { + user.id: User( + id=user.id, + name=user.display_name, + authentication_methods=[ + AuthMethod( + id=auth_method.id, + type=getattr( + auth_method, "odata_type", None + ), + ) + for auth_method in ( + await client.users.by_user_id( + user.id + ).authentication.methods.get() + ).value + ], + ) + } + ) + except Exception as error: + if ( + error.__class__.__name__ == "ODataError" + and error.__dict__.get("response_status_code", None) == 403 + ): + logger.error( + "You need 'UserAuthenticationMethod.Read.All' permission to access this information. It only can be granted through Service Principal authentication." + ) + else: + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) except Exception as error: - if ( - error.__class__.__name__ == "ODataError" - and error.__dict__.get("response_status_code", None) == 403 - ): - logger.error( - "You need 'UserAuthenticationMethod.Read.All' permission to access this information. It only can be granted through Service Principal authentication." - ) - else: - logger.error( - f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" - ) + logger.error( + f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) return users diff --git a/ui/CHANGELOG.md b/ui/CHANGELOG.md index 2082c0a6df..73fa758d8c 100644 --- a/ui/CHANGELOG.md +++ b/ui/CHANGELOG.md @@ -19,7 +19,8 @@ All notable changes to the **Prowler UI** are documented in this file. - Compliance detail view: Generic (rest of the compliances) [(#7990)](https://github.com/prowler-cloud/prowler/pull/7990) - Compliance detail view: MITRE ATTACK [(#8002)](https://github.com/prowler-cloud/prowler/pull/8002) - Improve `Scan ID` filter by adding more context and enhancing the UI/UX [(#7979)](https://github.com/prowler-cloud/prowler/pull/7979) -- Add Google Tag Manager integration [(#8058)](https://github.com/prowler-cloud/prowler/pull/8058) +- Lighthouse chat interface [(#7878)](https://github.com/prowler-cloud/prowler/pull/7878) +- Google Tag Manager integration [(#8058)](https://github.com/prowler-cloud/prowler/pull/8058) ### 🔄 Changed diff --git a/ui/next.config.js b/ui/next.config.js index 2f71bd229c..af64900d4c 100644 --- a/ui/next.config.js +++ b/ui/next.config.js @@ -3,38 +3,38 @@ // HTTP Security Headers // 'unsafe-eval' is configured under `script-src` because it is required by NextJS for development mode const cspHeader = ` - img-src 'self'; - font-src 'self'; - style-src 'self' 'unsafe-inline'; - script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.stripe.com; - connect-src 'self' https://api.iconify.design https://api.simplesvg.com https://api.unisvg.com https://js.stripe.com; - frame-src 'self' https://js.stripe.com/; - frame-ancestors 'none'; - default-src 'self' -` + default-src 'self'; + script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.stripe.com https://www.googletagmanager.com; + connect-src 'self' https://api.iconify.design https://api.simplesvg.com https://api.unisvg.com https://js.stripe.com https://www.googletagmanager.com; + img-src 'self' https://www.google-analytics.com https://www.googletagmanager.com; + font-src 'self'; + style-src 'self' 'unsafe-inline'; + frame-src 'self' https://js.stripe.com https://www.googletagmanager.com; + frame-ancestors 'none'; +`; module.exports = { - poweredByHeader: false, + poweredByHeader: false, output: "standalone", async headers() { return [ { - source: '/(.*)', + source: "/(.*)", headers: [ { - key: 'Content-Security-Policy', - value: cspHeader.replace(/\n/g, ''), + key: "Content-Security-Policy", + value: cspHeader.replace(/\n/g, ""), }, { - key: 'X-Content-Type-Options', - value: 'nosniff', + key: "X-Content-Type-Options", + value: "nosniff", }, { - key: 'Referrer-Policy', - value: 'strict-origin-when-cross-origin', + key: "Referrer-Policy", + value: "strict-origin-when-cross-origin", }, ], }, - ] - } + ]; + }, };